stitchesjs / stitches

[Not Actively Maintained] CSS-in-JS with near-zero runtime, SSR, multi-variant support, and a best-in-class developer experience.
https://stitches.dev
MIT License
7.74k stars 253 forks source link

Support for CSS layers #1117

Open viridia opened 1 year ago

viridia commented 1 year ago

One of the problems with CSS-in-JS frameworks generally is that it's hard to control the cascade order. This often results in styles overriding each other in undesirable ways, for example your 'hover' style taking a higher precedence than your 'disabled' style. There are various workarounds, such as adding extra && to up the specificity, but these are just hacks.

The solution is to support CSS layers. The way I envision it, the list of layers would be passed into the createStitches() constructor. It would take a list of names in camel case, for example:

createStitches({
  layers: ['uiBase', 'uiVariants', 'uiStates'],
  ... etc.
)}

Once defined, the layers can then be used in any call to css():

const myCss = css({
  uiBase: {
    borderColor: 'red',
  },

  uiStates: {
    '&:hover': {
      borderColor: 'blue',
    }
  }

The CSS properties within the layers can use any of the syntax that is normally allowed in the css() call. From an implementation standpoint, this means that they layer names are merged in with the CSS argument keys just like variants and such. It is the responsibility of the caller to ensure that layer names don't conflict with any CSS property names.

CSS properties not wrapped in layers will work just as they do today. Because of the way CSS layers work, classes not wrapped in a layer have a higher precedence than any explicitly-named layer, which is what we want.

When the stylesheet is generated, layers would have the following effects:

@layer ui-base, ui-variants, ui-states;
.xx-1234 {
  border-color: blue;
}

.xx-4567 {
  border-color: red;
}

@layer ui-base {
  .xx-1234 {
    background-color: #ff0000;
  }

  .xx-4567 {
    background-color: #ff0000;
  }
}

@layer ui-states {
  .xx-1234:hover {
    filter: brightness(1.1);
  }

  .xx-4567[disabled] {
    opacity: 0.5;
  }
}

Note: while it is possible to use layers currently by simply treating them as media queries, it is not very efficient, because the @layer definition is treated like a selector expression, and is output separately for each individual CSS class that uses a layer - in other words, the layers are not merged, so the @layer is repeated many times.

iduuck commented 1 year ago

Never heard of those layers before, but interesting concept. However, I think stitches will not get any new features in the meantime (see #1144)