aesthetic-suite / framework

🎨 Aesthetic is an end-to-end multi-platform styling framework that offers a strict design system, robust atomic CSS-in-JS engine, a structural style sheet specification (SSS), a low-runtime solution, and much more!
https://aestheticsuite.dev
MIT License
203 stars 5 forks source link

breaking: Refactor variants to support compound variants. #146

Closed milesj closed 3 years ago

milesj commented 3 years ago

Summary

Aesthetic has supported variants since basically the beginning with the @variants at-rule. While this has worked wonderfully, it did not support compound variants (activate based on a combination of variants). Compound variants are very important as it allows for complex component styles to be built. For example, Braid buttons use compound for the color + background/border treatment. This kind of styling is currently very difficult with Aesthetic (would require multiple style sheets).

However, supporting compound variants was non-trivial as the syntax required was either too verbose, or not desirable, or just not a good experience. Before this PR, variants were defined using doubly-nested objects, like so:

const styleSheet = createComponentStyles((css) => ({
  button: {
    '@variants': {
      size: {
        sm: { fontSize: 14 },
        df: { fontSize: 16 },
        lg: { fontSize: 18 },
      },
    },
  },
}));

Because of this nesting, there's not an easy way to insert syntax for compound variants... so I looked towards other projects. Stitches supports compound through a secondary property called compoundVariants, but when applied to Aesthetic, it's just far too verbose. It's also hard to scan/read at a glance.

const styleSheet = createComponentStyles((css) => ({
  button: {
    '@variants': {
      size: {
        sm: { fontSize: 14 },
        df: { fontSize: 16 },
        lg: { fontSize: 18 },
      },
    },
    '@compoundVariants': [
      {
        size: 'df',
        color: 'red',
        css: {
          borderColor: 'red',
        },
      },
    ],
  },
}));

So instead I opted to break and collapse the syntax to support both normal and compound variants within @variants. While this is a little more verbose for the name (duplicating size each time), it was far less verbose for the styles, and took up far less vertical space (which was hard to follow in complex style sheets). This new syntax looks like the following, where compounds use + for combinations.

const styleSheet = createComponentStyles((css) => ({
  button: {
    '@variants': {
      'size:sm': { fontSize: 14 },
      'size:df': { fontSize: 16 },
      'size:lg': { fontSize: 18 },

      'size:df + color:red': {
        borderColor: 'red',
      },
    },
  },
}));

This has the added benefit of looking more like CSS and the @selectors at-rule, which is nice.

Screenshots

Checklist