mrmartineau / design-system-utils

👩‍🎨 Access your design tokens with ease
https://zander.wtf
MIT License
539 stars 13 forks source link

Allow for type sizes to be specified by screen size #2

Closed oyeanuj closed 6 years ago

oyeanuj commented 7 years ago

Hi @mrmartineau, thank you for putting out this library! I've been migrating my default global variables to this helpful system. While doing this, I've run into a pattern with my font sizes.

Essentially, the design system enforce font size on one dimension (which is size), whereas I notice that a lot of the code wrt font sizes tends to be in two dimension (screen and size). For example, I have a different default baseFontSize for smaller screens vs larger screens.

So, to work around that, my sizes area is looking like the following:

type: {

        baseFontSize:   20,

        /*
        *   Size scale for fonts:
        *   Micro < Mini < X-Small < Small < Medium < Big
        */

        sizes: {

            /*
            *   Small Screen Micro
            *   11.099px, 0.555em @ 20
            */
            smallScreenMicro:   -5,

            /*
            *   Regular Screen Micro
            *   Small Screen Mini
            *   12.486px, 0.624em @ 20
            */
            regularScreenMicro: -4,
            smallScreenMini:    -4,

It feels intuitively to be a two dimensional array where I want to specify the scale for different screens that I am condensing into one. Does that make sense?

Curious about your thoughts on this, and if you've noticed a similar pattern as well?

Thanks!

mrmartineau commented 7 years ago

Hi @oyeanuj, thanks for getting in touch. Responsive type is hard to do, and what's currently there obviously doesn't cover some use cases.

Your solution is good, but I wonder if a more robust responsive typography system could be baked in. Of course, I'm not sure what that could be right now, but I think facepaint might be useful to you.

mrmartineau commented 7 years ago

@oyeanuj, so baseFontSize is a number/string:

baseFontSize: 20,

But it could be an object, like so:

baseFontSize: {
  narrow: 16,
  wide: 20,
}

Or even an array, like so:

baseFontSize: [16, 20],

Then it could be consumed in a number of different ways.

Still not sure about the sizes object though... any thoughts?

oyeanuj commented 7 years ago

@mrmartineau Thanks for the quick response. The solution I outlined above was just a workaround to make it work with the current version of this library. Ideally, I feel like a solution like below might be most extensible - let me know what you think?

Option A

//For the given breakpoints
breakpoints: {
    smallScreen: 300,
    mediumScreen: 500,
    bigScreen: 800,
},

type: {
  baseFontSize: [12, 16, 20],
  sizes: [
  {
    xs: -2,
    s: -1,
    base: 0, // [default] p, h5, h6
    m: 1, // h4
    l: 2, // h3
  }, 
  {
    xs: -3,
    s: -2,
    base: 0, // [default] p, h5, h6
    m: 2, // h4
    l: 4, // h3
  } ...
  ]
};

//Access using the following API:
ds.fontSize('bigScreen', 'xl')

The first solution is along the lines of facepaint where the breakpoints are defined upfront, and then both baseFontSize can take a number or an array (which we match to the breakpoints) and sizes can take an object (like today) or an array as well (which we match to the breakpoints). The advantage here is that breakpoints are defined once, and are consistent across.

Option B

type: {
  baseFontSize: {
    smallScreen: 12,
    mediumScreen: 16,
    bigScreen: 20
  },
  sizes: {
    smallScreen: {
      xs: -2,
      s: -1,
      base: 0, // [default] p, h5, h6
      m: 1, // h4
      l: 2, // h3
   },
   mediumScreen: {
     xs: -3,
     s: -2,
     base: 0, // [default] p, h5, h6
     m: 2, // h4
     l: 4, // h3
   } ...
  }
};

//Access using the following API:
ds.fontSize('bigScreen', 'xl')

In this second solution, there is no mapping to breakpoints object, instead allowing users to create own mapping for each solution. The advantage here is the flexibility where not everything might be divided per breakpoints.

Option C

responsive: {
  smallScreen: {
    width: 400,
    type:  {
      baseFontSize: 12,
      sizes: {
        // ... Similar object to what exists today
     }
  },
  mediumScreen: { 
    // ... so on and so forth
  },
}

This solution embraces responsiveness as a core way of creating your design system, and all sizes live in their own responsive system. If lets say smallScreen doesn't define baseFontSize, we can fallback to a default (either as part of responsive object or like how we define it today).

Thoughts? Do any of these solutions appeal to you more than others?

oyeanuj commented 7 years ago

@mrmartineau Curious if you've had a chance to think about the options above?

mrmartineau commented 7 years ago

@oyeanuj The API for options A & B are nice, it is very similar to how ds.color('primary', 'dark') works, but I think I would want to switch the arguments so that it would work nicely with only one like the color function does, e.g. ds.fontSize('xl', 'bigScreen').

Out of the first two options, I'm not sure which is best to be honest. I like A because the values are bound to the breakpoints, but I think that may be restrictive as well. Option B is more explicit and easier to understand.

Option C is very interesting, but I think that there would be a lot of repetition with it. I don't think it could be implemented without adding much more to the codebase. I like the small size of it at the moment and am reluctant to increase it much more.

I currently set type using media queries and this is acceptable. Although I think this feature is useful, it isn't a priority for me right now. However, if you are able to add it yourself, I would appreciate it.