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

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

Open benash opened this issue 1 year ago โ€ข 3 comments

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`),
}))

benash avatar Aug 18 '23 18:08 benash

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" } />

ben-rogerson avatar Aug 18 '23 23:08 ben-rogerson

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>

benash avatar Aug 21 '23 14:08 benash

mark. same problem.

chaozwn avatar Nov 02 '23 16:11 chaozwn