twin.macro icon indicating copy to clipboard operation
twin.macro copied to clipboard

"tw" property fails to override variant styling in Stitches

Open idrm opened this issue 1 year ago • 5 comments

The tw property fails to override a (Stitches) component's styling when that styling is inside a variant definition. E.g. the following will render the text in red instead of blue:

const StyledDiv = styled.div({
  variants: {
    xyz: {
      true: tw`text-red-500`,
    },
  }
})

<StyledDiv tw="text-blue-500" xyz>Hello world</StyledDiv>

Oddly enough, it works when I try to override using the css property instead, e.g.

<StyledDiv css={{...tw`text-blue-500`}} xyz>Hello world</StyledDiv>

Here's a replication of the issue in Sandbox (using the the current twin.macro + next.js + Stitches twin.examples repo as a starting point).

I haven't yet investigated the underlying cause, but I suspect it's something to do with CSS precedence rules.

idrm avatar Aug 06 '22 10:08 idrm

Thanks for the great bug replication.

Here's the conversion data - perhaps you can guide me on what needs to be changed to get this feature working in stitches?

// in

import tw, { styled } from "twin.macro";

const StyledDiv = styled.div({
  variants: {
    xyz: {
      true: tw`text-red-500`,
    },
  },
});

const IndexPage = () => (
  <StyledDiv tw="text-blue-500" xyz>
    Hello world
  </StyledDiv>
);
// out

import { styled as _styled } from "stitches.config.js";

const StyledDiv = _styled("div", {
  variants: {
    xyz: {
      true: {
        "--tw-text-opacity": "1",
        "color": "rgba(239, 68, 68, var(--tw-text-opacity))"
      }
    }
  }
});

const _TwComponent = _styled(StyledDiv, {
  "--tw-text-opacity": "1",
  "color": "rgba(59, 130, 246, var(--tw-text-opacity))"
});

const IndexPage = () => /*#__PURE__*/React.createElement(_TwComponent, {
  xyz: true,
  "data-tw": "text-blue-500"
}, "Hello world");

ben-rogerson avatar Aug 08 '22 07:08 ben-rogerson

The generated component code is correct, but it appears that the order of the generated CSS class rules (inside the head element) affects the final result. You can see the xyz=true class definition show up after that of the tw="text-blue-500" class, as well as the xyz=true class taking precedence in the computed styles section of Chrome's dev tools in the screenshot below (taken from the Sandbox demo I linked to previously): image

idrm avatar Aug 09 '22 06:08 idrm

I modified the Sandbox demo to include a default green background styling in the component definition, as well as a yellow background override where the component is rendered. That worked as expected. The override seems to fail only in the case of variant styles.

import tw, { styled } from "twin.macro";

const StyledDiv = styled.div({
  ...tw`bg-green-500`,
  variants: {
    xyz: {
      true: tw`text-red-500`
    }
  }
});

const IndexPage = () => (
  <StyledDiv tw="text-blue-500 bg-yellow-500" xyz>
    Hello world
  </StyledDiv>
);

idrm avatar Aug 09 '22 07:08 idrm

After digging inside the twin.macro source code, I believe the issue stems from how the tw attribute is handled and the resulting order of CSS classes. If I set the moveTwPropToStyled config option to false (and as long as I don't mix the tw and css props inside the same element) I get the proper order of class rules and the desired style override. Here's a CS demonstrating that.

idrm avatar Aug 09 '22 10:08 idrm

It's possible the issue lies with the following bug in Stitches: stitchesjs/stitches#1060

Their reproduction demo shows the same CSS rule order behavior.

idrm avatar Aug 09 '22 15:08 idrm

I'm closing this issue since it lies with how Stitches operates.

idrm avatar Aug 13 '22 09:08 idrm