vercel / styled-jsx

Full CSS support for JSX without compromises
http://npmjs.com/styled-jsx
MIT License
7.72k stars 261 forks source link

Classname not compiled/passed-through for wrapper components #713

Open JohnRoseDev opened 3 years ago

JohnRoseDev commented 3 years ago

Do you want to request a feature or report a bug?

Not sure if bug or feature

What is the current behavior?

When using a native div element like <div className='bla' /> things work as expected and a "jsx-1234 bla" class name is generated, however, if set to a wrapper component like <Container className='bla' />, which under the hood passes className to a div, the classname is not compiled but simply stays "bla" on the div element, consequently the styles are not applied.

What is the expected behavior?

I would not expect styled-jsx to be limited to native elements since building wrappers and abstractions is a very common pattern in react. I would think the "jsx-1234" should be passed down to the div element inside the wrapper component.

Environment (include versions)

Did this work in previous versions?

Not sure

EduardoRicardez commented 3 years ago

I'm having the same issue, i can't style my formik forms :(

ThetripGr commented 3 years ago

same here and following the example for using css.resolve does not work either

JohnRoseDev commented 3 years ago

@vercel, are you aware of this? This is a major problem for this library to be used in realistic react use cases. Everybody creates (or uses existing) wrappers and ui component libraries for their react projects, and for all these components the className is not compiled correctly by styled-jsx.

jaspersorrio commented 3 years ago

Due to this behaviour, we are forced to create wrapper components around 3rd party components causing an excessive DOM size.

https://developers.google.com/speed/pagespeed/insights/?url=https%3A%2F%2Feezee.sg%2Fbrands%2Faccsafe&tab=desktop

@vercel, if possible please look into this as it has become antithesis to what next.js is trying to achieve.

errnesto commented 3 years ago

Right now if I have a wrapper component that looks like this:

const Wrapper = (props) => {
  return <div className={props.className}></div>
}

And I use it in a container:

const Container = (props) => {
  return <section className='container'><Wrapper className='my-wrapper' /></section>
}

When parsed with styled-jsx the html would look something like this:

<section className='jsx-1 container'>
  <div className='jsx-2 my-wrapper'></div>
</section>

So what we all are missing is the jsx-1 in the div. I would be happy if I was able to add it manually like:

const Container = (props) => {
  const cssScope = useCssScope()

  return (
    <section className='container'>
      <Wrapper className=`${cssScope} my-wrapper` />
      <style jsx>{`
        .my-wrapper {
          color: red;
        }
     `}</style>
    </section>
  )
}

I could do something similar with resolve but that means I can't write my style definitions in the style tag which means they are kind of all over the place…


Right now my workaround is to use :global():

const Container = (props) => {
  return (
    <section className='container'>
      <Wrapper className='my-wrapper' />
      <style jsx>{`
        .container :global(.my-wrapper) {
          color: red;
        }
     `}</style>
    </section>
  )
}
errnesto commented 3 years ago

Oh also this is not the first time this is discussed… https://github.com/vercel/styled-jsx/issues/573

tobyleye commented 1 year ago

Here's a simple workaround for those still struggling with this issue. Essentially we apply a classname on a wrapper element in the parent and use it in combination with the :global() selector to target the child and style from it parent. it's also a good way to override child styles from the parent.

function Child({ className='' }: { className?: string }) {
  return (
    <div className={`child ${className}`}>
      child..
      <style jsx>{`
        .child {
          border: 1px solid red;
          width: 40px;
          height: 40px;
        }
      `}</style>
    </div>
  );
}

function Parent() {
  return (
    <div className="parent">
      <Child className="parent-child" />

      <style jsx>{`
        .parent :global(.parent-child) {
          border: 1px solid green;
        }
      `}</style>
    </div>
  );
}