Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[For discussion] Type & space scales #14

Closed
colepeters opened this issue Jan 12, 2023 · 5 comments · Fixed by #28
Closed

[For discussion] Type & space scales #14

colepeters opened this issue Jan 12, 2023 · 5 comments · Fixed by #28
Assignees
Labels
enhancement New feature or request

Comments

@colepeters
Copy link
Member

colepeters commented Jan 12, 2023

Something I'm finding a bit finicky is landing on a scale that works equally well for both type sizes and layout spacing, as they both currently use the same scale intervals via styleguide.json. (This isn't a challenge unique to Enhance Styles, I've faced this many times with other parametric design implementations.)

Specifically, I find that things get tough as the scale moves into the negative values (text-1, text-2, etc). The values produced by certain scales at these intervals often results in type sizes that make legibility a challenge; however, those same values can be really useful for fine tuning layouts. Here's an example:

.text5{font-size:3.052rem;}/* 48.828px */ 
.text4{font-size:2.441rem;}/* 39.063px */ 
.text3{font-size:1.953rem;}/* 31.25px */ 
.text2{font-size:1.563rem;}/* 25px */ 
.text1{font-size:1.25rem;}/* 20px */ 
.text0{font-size:1rem;}/* 16px */ 
.text-1{font-size:0.8rem;}/* 12.8px */ 
.text-2{font-size:0.64rem;}/* 10.24px */ 
.text-3{font-size:0.512rem;}/* 8.192px */ 
.text-4{font-size:0.41rem;}/* 6.554px */ 
.text-5{font-size:0.328rem;}/* 5.243px */ 

In this instance, the positive intervals (text1, text2…) work well, but the negative intervals would make most type too hard to read. On the flip side, those same intervals can make precise adjustments to margins, padding, etc where needed, which is nice.

One workaround that I've tried is using a scale with very small intervals (for example, a minor third scale, with a 1.2 scale factor), which produces finer gradations (and thus more usable type sizes at the negative intervals). However, depending on the number of steps specified in the scale, this can produce either too few distinct options for type sizing at positive intervals (since the contrast between heading level sizes is quite minimal), or too many superfluous options for both type and layout spacing (thus reducing the effectiveness of using a parametric bounds in the first place, since many of the values produced will go unused).

One option that comes to mind is the ability to provide explicit intervals for the scale, and possibly the option to differentiate the type and layout scales. The existing scale configuration option could be persisted, but a new set of options could be provided as opt ins for folks who want to get more granular. For example:

{
  "typeScale": [
    "0.875rem",
    "1rem",
    "1.25rem",
    "1.563rem",
    "1.953rem",
    ""
  ],
  "spaceScale": {
    "ratio": "perfectFourth",
    "steps": 16
  }
}

In this example, both typeScale and spaceScale could take either an array of explicit values, or an object to specify a generated scale, as the existing scale option currently works.

This is just one idea off the top of my head, but wanted to spin up an issue to get the wheels turning. All input welcome!

@colepeters colepeters self-assigned this Jan 12, 2023
@colepeters colepeters added enhancement New feature or request question Further information is requested and removed question Further information is requested labels Jan 12, 2023
@colepeters
Copy link
Member Author

Whoops, just realized this is extremely similar to #5 — we may want to close one of these, I'll leave this one open for now though as there's a bit more information here.

@colepeters
Copy link
Member Author

colepeters commented Apr 4, 2023

Recap of possible iterations

Several threads relating to type and spacing scales in Enhance Styles have been ongoing in the background over the past few months. The following is a list of the main considerations that have been discussed:

Distinct type and space scales

As discussed previously, providing an option to create distinct type and space scales would be useful. For example:

export default {
  typeScale: {
    ratio: 'perfectFourth',
    steps: 6,
  },
  spaceScale: {
    ratio: 'minorThird',
    steps: 12,
  },
}

Where one of either typeScale or spaceScale was undefined, the default would be for the undefined scale to echo the defined scale.

Imperative scales

There have been some requests for type and space scales to support an imperative set of values, similar to Theme UI's theme specification. Considering the option to specify distinct type and space scales, this would look something like:

export default {
  typeScale: ['0.85rem', '1rem', '1.125rem', '1.25rem', '1.5rem', '2rem', '4rem'], // string values emitted in CSS as-is
  spaceScale: [0, 2, 4, 8, 16, 32, 64, 128, 256], // integers emitted in CSS as pixels
}

Again, if one of these two was undefined, we would generate the undefined scale to match the defined scale.

Fluid scales

Finally, the ability to create fluid type and space scales (see Utopia) has been suggested. This allows for creating fluid type and space scales without defining breakpoints, which is becoming an increasingly popular and useful way to work with type and layout. This requires a fair bit more in terms of specification, i.e.:

  1. The number of steps in the scale
  2. A set of minimums, comprised of:
    • Viewport width
    • Base size
    • Scale ratio
  3. A set of maximums, comprised of:
    • Viewport width
    • Base size
    • Scale ratio
      These would again need to be specified for type and/or space scales, with the option of filling in either of these if undefined. This could look something like:
export default {
  typeScale: {
    steps: 6,
    viewportMin: 320,
    viewportMax: 1500,
    baseMin: 16,
    baseMax: 22,
    scaleMin: 1.25,
    scaleMax: 1.33,
  },
  spaceScale: {
    steps: 12,
    viewportMin: 320,
    viewportMax: 1500,
    baseMin: 16,
    baseMax: 32,
    scaleMin: 1.5,
    scaleMax: 2,
  },
}

Considerations

Explicit or implicit scale types

Differentiating between imperative and parametric scales is straightforward, in that an imperative scale will always be specified as an array of values, whereas a parametric scale requires an object specifying a configuration.

However, if we're going to consider offering both static and fluid parametric scales, we have two options to differentiate between them when generating those scales:

  1. Detect the scale type implicitly by comparing the provided configuration object's shape and the possible shapes (static or fluid), or
  2. Require an explicit key within a scale's configuration object to declare the desired scale type. This could be a string, e.g. type: 'fluid' | 'static', although the term type is a bit awkward given the name typeScale. This could also be a binary flag, e.g. fluid: true. A binary value would close the door on any future variations on these scales, though at present I can't think of any form these would take other than 'static' or 'fluid' (although life does sometimes, uh, find a way).

The implicit route is simpler for consumers as it saves them having to write (and remember) another configuration parameter.

One benefit to relying on an explicit key to specify the variety of scale, however, would leave the door open for allowing users to request either a static or fluid scale without specifying the parameters themselves (which we could then fill in with a set of sane defaults). For example:

export default {
  typeScale: {
    fluid: true,  // generates a fluid type scale with our default parameters
  },
  spaceScale: {
    fluid: true,
    configuration: {
      steps: 6,
      viewportMin: 320,
      viewportMax: 1500,
      baseMin: 16,
      baseMax: 32,
      scaleMin: 1.5,
      scaleMax: 2,
    },
  },
}

Existing configuration

The existing configuration for the unified type and space scale is defined as:

export default {
  base: 18,
  scale: {
    ratio: 'perfectFourth',
    steps: 12,
  },
}

A few questions regarding this:

  • Is there a way to introduce the new scale options additively, leaving these existing configurations as backwards compatible? I suspect this might easily cause collisions if both these configurations and one of the new configurations is provided, or at the very least add complexity to managing that case.
  • Since the fluid scales require a min and a max base value, does it make sense for base to exist at the root of the configuration file anyway? For static scales, it could move within the scale's configuration object, i.e.:
    export default {
      typeScale: {
        fluid: false,
        configuration: {
          base: 16,
          ratio: 'perfectFourth',
          steps: 12,
        },
      },
      spaceScale: {
        fluid: true,
        configuration: {
          baseMin: 16,
          baseMax: 22,},
      },
    }
  • Should we still be able to specify a scale to create a unified type and space scale? I feel like this would be useful, though it does add some complexity in terms of control flow when generating the scales.

Generated classes

Generated utility classes would need to follow different patterns for different types of scales, i.e.:

  • Imperative scales would generate ordinal classes without negative indexes:
    typeScale: [14, 16, 18, 24, 32, 64] ---> .text0, .text1, .text2, …
    
  • Parametric scales could continue in the current fashion, where steps is used to generate the requested number of steps in both positive and negative directions:
    typeScale: {
      fluid: true,
      configuration: {
        steps: 6,
        …
      },
    }
    ---> .text-6, .text-5, … .text0, .text1, .text2, …
    

@kristoferjoseph
Copy link
Contributor

We should leave the existing scale functionality alone and add in the typeScale and spaceScale with fluid options in an additive way.
This means we would need new generated class names for these added scales.
I feel that even imperative scales would need a base type setting to work correctly as rems so we would need to incoroprate that into the generated class names.

@colepeters
Copy link
Member Author

@kristoferjoseph Sounds good to me. I think we'll have some edge cases to sort out — for example, what happens if a user specifies scale, typeScale, and spaceScale — but I think that'll be fine.

I think there's potentially more complexity to examine with the root base property — for example:

{
  base: 18,
  typeScale: {
    fluid: false,
    configuration: {
      base: 16,
      ratio: 1.25,
    },
  },
}

…or:

{
  base: 18,
  typeScale: {
    fluid: true,
    configuration: {
      baseMin: 16,
      baseMax: 22,
      …
    },
  },
}

In these cases I guess we could either ignore the root base if a higher specificity one exists, or throw an error, but just wanted to flag this as it crossed my mind when thinking about all this last week!

@colepeters
Copy link
Member Author

Follow up - 2023/04/18

The following summarizes some discussions over the past week:

Imperative scale values

Imperative scales defined with integers would need to be interpreted and emitted as rem units in order to preserve typographic accessibility. E.g. a scale of [14, 16, 18, 24], if we choose to interpret the integers as pixel values, would need to be computed to ['0.875rem', '1rem', '1.125rem', '1.5rem'], and so forth.

However, encouraging the use of pixel values may not be something we want to do. This also creates a delta between styleguide input and output, which could be confusing and create unexpected behaviour for end users.

To this end, I'm wondering if we want to avoid the complexity and ambiguity of imperative scales with Enhance Styles. If a user prefers to use an imperative, pixel based scale, going with a different styling library may be a better option for them.

Multiple scales vs a larger/finer grained single scale

We discussed the question of whether we really need/want the option to create dedicated font/spacing scales, or whether we just need a way to generate larger scales with more fine grained steps, which would be useful for both type and spacing.

The tradeoff here is that we could end up with a ton of classes, some of which may never be used, and we're somewhat averse to post processing (i.e. getting into static analysis and tree shaking), which means this could end up really bloating the generated CSS. Also, at the end of the day, the user can themselves choose to use a single scale with many steps and small ratios, so this could end up just being a user decision (providing we offer the option of creating distinct type and spacing scales).

This was referenced May 2, 2023
@colepeters colepeters linked a pull request May 5, 2023 that will close this issue
@colepeters colepeters moved this to In Progress in 💅🏽 Enhance Styles 5.0 May 5, 2023
@github-project-automation github-project-automation bot moved this from In Progress to Done in 💅🏽 Enhance Styles 5.0 May 9, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
No open projects
Development

Successfully merging a pull request may close this issue.

2 participants