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.75k stars 253 forks source link

CSS injection order can be incorrect if a set of styles is reused #1039

Open azuline opened 2 years ago

azuline commented 2 years ago

Bug report

Describe the bug

Let us take three components: A, B, and C. All three are Stitches styled components.

const baseStyles = { ... };
const overrideStyles = { ... };

// A is a simple styled component with some styles.
const A = styled("div", baseStyles);
// B composes A and overrides some of A's styles.
const B = styled(A, overrideStyles);
// C uses the same styles as B, but doesn't compose any component.
const C = styled("div", overrideStyles);

Because B and C share the same styles, the styles are hashed+injected once and reused for both.

If we render A or B first, then the order of injection is:

  1. baseStyles
  2. overrideStyles

and we get the intended result since overrideStyles has higher specificity than baseStyles.

However, if we render C first, then the order of injection is:

  1. overrideStyles
  2. baseStyles

and we have a bug, since baseStyles has higher specificity than overrideStyles.

To Reproduce

https://codesandbox.io/s/modest-bartik-plv94n?file=/src/App.js

Expected behavior

Despite ANormalStyledButton defining the same set of styles as the styles used Composed to override Base, the styles in Composed should be injected after those used in Base.

Screenshots

image

System information

Additional context

I found this bug when investigating a problem with style injection order when composing functional components. However, in the bug I'm experiencing, I can't seem to find the styles duplicated elsewhere. I will be continuing to investigate.

I originally wrote a comment in #671, but realized that this is a different bug that happens at the intersection of injection order and reused styles.

axelson commented 2 years ago

Does anyone have a workaround for this? I've been bitten by this multiple times now.

azuline commented 2 years ago

there is a workaround in the original post, see the screenshot

on more systematic workarounds. one might be to wrap styled with something along the lines of:

const myStyled = (tag, ...styles) => styled(tag, { "--uniq-flag": randomIDGenerator() }, ...styles);

i haven't tested this, but i think it could work.