ben-rogerson / twin.macro

๐Ÿฆนโ€โ™‚๏ธ Twin blends the magic of Tailwind with the flexibility of css-in-js (emotion, styled-components, solid-styled-components, stitches and goober) at build time.
MIT License
7.92k stars 183 forks source link

Error when passing props to the css attribute in solid/stitches #829

Open benash opened 1 year ago

benash commented 1 year ago

When using the SolidJS integration, the following results in Uncaught ReferenceError: props is not defined in the browser:

const BrokenInput = (props: InputProps) => (
  <input
    css={{
      ...tw`border`,
      ...(props.hasHover && tw`hover:border-red-600`),
    }}
  />
)

On the other hand, this works fine:

const WorkingInput = styled.input((props: InputProps) => ({
  ...tw`border`,
  ...(props.hasHover && tw`hover:border-red-600`),
}))
ben-rogerson commented 1 year ago

Thanks for posting this bug, I've seen the issue too and I haven't settled on a good solution yet.

Here's the broken transformation:

const BrokenInput = (props) => (
  <input css={{ ...(props.hasHover && tw`block`) }} /> // < Any conditionals like `props` here will lose their reference
)

// โ†“ โ†“ โ†“ โ†“ โ†“ โ†“

import { styled } from "solid-styled-components";

const TwComponent = styled("input")({
  ...(props.hasHover && { "display": "block" }) // < `props` reference is lost
});

const BrokenInput = props => <TwComponent />; // < `props` isn't passed to the component

The css prop doesn't exist in solid-styled-components so twin converts the element into a styled component. The problem is that during that process all the variables used in the conditional are severed and rather than chasing a complex solution in babel I'm looking into adding a new conversion type instead.

The new conversion for the css prop would use the css import from solid-styled-components:

const BrokenInput = (props) => (
  <input css={{ ...(props.hasHover && tw`block`) }} />
)

// โ†“ โ†“ โ†“ โ†“ โ†“ โ†“

import { css } from "solid-styled-components";

const BrokenInput = props => (
  <input class={css({ ...(props.hasHover && { display: "block" }) })} />
)

This option would simplify the conversion and the conditionals wouldn't be affected.

One issue is that if there's already a class on the element then a merge needs to happen - perhaps something as simple as this would work:

<input class="box" css={{ ... }} />
// โ†“ โ†“ โ†“ โ†“ โ†“ โ†“
<input class={"box " + css({ ... }) } />

and if css is defined before the class attribute:

<input css={{ ... }} class="box" />
// โ†“ โ†“ โ†“ โ†“ โ†“ โ†“
<input class={css({ ... }) + " box" } />
benash commented 1 year ago

Thanks for the explanation. Would Solid's classList help with that merge?

<div classList={{ ...{ [css`color: rebeccapurple;`]: true }, ...{ myClass: true, otherClass: false } }}>My text</div>
chaozwn commented 11 months ago

mark. same problem.