facebook / create-react-app

Set up a modern web app by running one command.
https://create-react-app.dev
MIT License
102.5k stars 26.77k forks source link

Support scoped CSS in the same file #5224

Open gaearon opened 5 years ago

gaearon commented 5 years ago

This is kinda vague but I'd like to have a built-in option to write CSS that:

Basically I want "CSS Modules" but without the "Modules" part. Just put it in the same file if that's the only place I use it anyway.

https://github.com/4Catalyzer/astroturf looks related. cc @jquense @markdalgleish

satya164 commented 5 years ago

So we recently released Linaria 1.0, and reviewing the main requirements, it satisfies all of them and more. Updated my original comment https://github.com/facebook/create-react-app/issues/5224#issuecomment-426398292

jednano commented 5 years ago

@ranyefet it looks like neither astroturf or linaria support CRA without ejecting.

I'm hoping CRA implements linaria, because it has TypeScript support!

See also: https://github.com/callstack/linaria/issues/308#issuecomment-462164541

o-t-w commented 5 years ago

I’d like to see support for Styled-JSX from CRA.

jednano commented 5 years ago

@o-t-w I don't see that happening, because CRA already has TypeScript support and it would appear that styled-jsx doesn't.

ivancuric commented 5 years ago

@gaearon what were you guys using for the new FB? Any plans on open sourcing that?

nhooyr commented 5 years ago

https://www.styled-components.com has much greater adoption than astroturf/linaria and as such is more stable, widely used and better supported. It has a runtime but it's small, it's likely it'd be a better fit for most React apps.

ai commented 5 years ago

Styled Components is popular but not the best CSS-in-JS solution in terms of performance.

  1. It will add 44 KB (minified) JS runtime to your bundle, which is pretty big (in terms how long it will be compiled and executed on slow phones). Linaria and Astroturf can have 0 bytes runtime (or just add few hundred bytes for syntax sugar).
  2. Styled Components compile CSS on client side. And it does it again on props changes. Astroturf and Linaria process CSS during deploy build. You will have fast static CSS files with them.
  3. Styled Components dynamicly add/change <styled> tags. It forces repaint for the whole screen.

Hope @lttb will correct me and show his benchmarks.

nhooyr commented 5 years ago

Another problem Linaria has is the lack of IE 11 support for the react style API due to CSS variables not being supported. IE 11 is in the list of browsers targeted by CRA.

ai commented 5 years ago

@nhooyr you do not need CSS Custom Properties always for Astroturf:

const Button = styled.button`
  background: blue;
  &.isRemove {
    background: red;
  }
`

<Button isRemove={true} />

If you do not have styles related to user input it will work pretty great.

satya164 commented 5 years ago

Regarding styled components, it already works fine with CRA, this thread is about solutions that produce CSS files which need custom tooling and hence need additional integration.

Linaria has is the lack of IE 11 support for the react style API due to CSS variables not being supported

Well, dynamic prop based styles are not supported, but that doesn't mean Linaria doesn't work in IE11. There are many other things Linaria does besides dynamic styles.

If you need IE 11 support, you can use several approaches to have similar functionality like you would do with vanilla CSS.

jquense commented 5 years ago

@satya164 we should get together and build some sort of ultimate runtime free amalgam of astroturf and linaria :P

satya164 commented 5 years ago

@jquense haha why not, I just need to find time :D

jednano commented 5 years ago

Is there something missing from Linaria that Astroturf has? Perhaps a side by side feature comparison would be useful in this thread?

nhooyr commented 5 years ago

@ai it's only 16kb gzipped.

ai commented 5 years ago

We should care about JS size not because of download time, but because of the time for processing and execution. On slow phones this time could be bigger than download time. And execution time can't be cached as we can cache file from downloading.

Here is more theory about cost of JavaScript: https://v8.dev/blog/cost-of-javascript-2019

Since we are talking about processing time, minified size is more representative. Gzip size can hide some important repeats which we need to parse and execute.

nhooyr commented 5 years ago

We should care about JS size not because of download time, but because of the time for processing and execution. On slow phones this time could be bigger than download time. And execution time can't be cached as we can cache file from downloading.

I agree it's important but I think in this case the gains outweigh the costs. react-dom itself is 100kb minified along with react at 6.5kb but still very widely used as it makes development much easier.

Likewise, I'm arguing that dynamic css in js is worth the cost as well as it makes it much easier to theme your app, use props to control styles and your code reads much better.

Linaria is clever in that it lets you do pretty much the same for most use cases with css variables but it won't work on IE 11 which is very unfortunate. And astroturf won't let users dynamically adjust css at all.

We can also use emotion which is pretty much identical and only 27kb minified. [https://bundlephobia.com/result?p=@emotion/core@10.0.14) and @emotion/styled.

ai commented 5 years ago

Size is not the only problem. Parsing CSS string on any props changes and re-render the whole page on any dynamic styles changes is also more important.

I think that these 3 problems (size, render, repaint) is a very big cost for lack of user-defined styles in IE (you still can use dynamic styles in IE without Custom Properties with predefined options) and lack of themes in IE.

nhooyr commented 5 years ago

Size is not the only problem. Parsing CSS string on any props changes and re-render the whole page on any dynamic styles changes is also more important.

Are you sure about that? Seems like a massive bug. I can't seem to find anything on github about it except for this old issue: https://github.com/styled-components/styled-components/issues/356

I agree dynamic css in js is not the move if this is a real, reproducible issue.

ai commented 5 years ago

@nhooyr SC uses template string and put props inside this string. So SC JS runtime must to parse CSS again on any props changes.

Browser doesn't know how new styles will change different parts of the page. It needs to recalculate CSSOM again and reapply it to all competents.

BTW, I forgot forth problem. SC is not compatible with old CSS tools. You can use legacy Sass mixins in Astroturf. Or you can use Autoprefixer to polyfill Grid. SC doesn't support any PostCSS plugins.

nhooyr commented 5 years ago

@nhooyr SC uses template string and put props inside this string. So SC JS runtime must to parse CSS again on any props changes. Browser doesn't know how new styles will change different parts of the page. It needs to recalculate CSSOM again and reapply it to all competents.

According to https://medium.com/styled-components/announcing-styled-components-v4-better-faster-stronger-3fe1aba1a112

It's only milliseconds slower than css modules for a deep tree.

SC doesn't support any PostCSS plugins.

Thats because it handles prefixing itself and aside from that. The other plugins of postcss are cool but not critical.

I've opened https://github.com/callstack/linaria/issues/445 to add a minimal runtime to Linaria for IE 11. Would be the perfect solution.

satya164 commented 5 years ago

You can check comparison of Linaria with SC here https://github.com/callstack/linaria/blob/master/docs/BENEFITS.md#advantages-over-other-css-in-js-solutions

Yea, the CSS parsing time by itself is fast, but there are several other considerations.

giuseppeg commented 5 years ago

One can already use styled-components with CRA right?

Linaria and Astroturf are the only more traditional libraries that can extract to static and do code splitting. They offer a styled-like API and such. IMHO dynamic styles based on props are a huge mistake that we all made (I am author of a couple of CSS in JS libs including ZEIT's styled-jsx). Toggling class names based on props or using inline styles is far better and more efficient.

Has anybody ever made a comparison table between Linaria and Astroturf? I'd go for one of the two.

@gaearon I feel like that this might be a never ending convo, I'd suggest that you either close the issue or come up with a resolution :)

satya164 commented 5 years ago

@giuseppeg

Toggling class names based on props or using inline styles is far better and more efficient.

Why is that more efficient than CSS variables?

Has anybody ever made a comparison table between Linaria and Astroturf

This only lists advantages of Linaria, but it's a start:

https://github.com/4Catalyzer/astroturf/issues/69#issuecomment-454122709

giuseppeg commented 5 years ago

@satya164 yeah for theming custom properties are the best. Not sure how I feel about conditional styles and more complex things. Some use props to define sets of declarations (within a rule) conditionally or do crazy things with them.

nhooyr commented 5 years ago

I've got a WIP for getting Linaria dynamic props working on IE.

See https://github.com/callstack/linaria/pull/446

giuseppeg commented 5 years ago

@satya164 indeed I would lean towards Linaria (I guess mainly for the composition capabilities).

The only thing I dislike about it is the fact that it uses Stylis for preprocessing. I love Stylis but PostCSS is superior and less flacky. For build time solutions I'd pick PostCSS also because it supports plugins.

giuseppeg commented 5 years ago

FWIW if you only use props/custom properties for theming I wouldn't bother supporting IE11. I would just add a fallback and provide a single default theme (that's how I do at work).

giuseppeg commented 5 years ago

FWIW(2) https://github.com/threepointone/glam tried to implement a polyfill for IE11.

nhooyr commented 5 years ago

FWIW if you only use props/custom properties for theming I wouldn't bother supporting IE11. I would just add a fallback and provide a single default theme (that's how I do at work).

How would you add a fallback cleanly?

giuseppeg commented 5 years ago

with this plugin https://github.com/postcss/postcss-custom-properties

satya164 commented 5 years ago

@giuseppeg

The only thing I dislike about it is the fact that it uses Stylis for preprocessing. I love Stylis but PostCSS is superior and less flacky.

It does use stylis by default. But the preprocessor is customizable: https://github.com/callstack/linaria/blob/master/docs/BUNDLERS_INTEGRATION.md#options

jednano commented 5 years ago

Some use props to define sets of declarations (within a rule) conditionally.

@giuseppeg can you please explain why this is a bad thing or how it's less efficient? @satya164 what will Linaria do "under the hood" if one attempts to do something like this? Will a class be created for the condition?

const Foo = styled.div`
  color: black;
  ${props => props.error && {
    color: red;
    border: 1px solid red;
  }}
`

There's probably something wrong with my syntax.

It does use stylis by default. But the preprocessor is customizable: https://github.com/callstack/linaria/blob/master/docs/BUNDLERS_INTEGRATION.md#options

To be clear, @satya164, this means you can configure Linaria to use PostCSS as a preprocessor?

giuseppeg commented 5 years ago

@jedmao every time the prop evaluates to a different value the css in js library has to generate a new css rule and inject it in the stylesheet. So even though part of that rule is static (in your example color: black) you end up not reusing it.

And to clarify if you have a highly dynamic application (RTL, theming etc) probably doing these at runtime is way better, my point was that the more you can reuse (compose) the better but of course libraries cache aggressively and for many this is not a big deal!

jednano commented 5 years ago

That color: black declaration, should it be duplicated, would not add any real footprint after gzip, FWIW. But I see your point, if that's indeed how it (Linaria) works under the hood.

I don't see the composition issue though. I've used BEM successfully before with ultimate composition, but I don't think I would lose composition with CSS in JS.

satya164 commented 5 years ago

@jedmao currently Linaria only supports interpolations for property values.

const Foo = styled.div`
  color: ${props => props.primary  ? 'blue' : 'black'}
`;

Internally it converts interpolations to CSS variables whose values are updated with the style property on the component.

this means you can configure Linaria to use PostCSS as a preprocessor

Yes

jednano commented 5 years ago

I just published a craco plugin for Linaria, for those who don't want to eject from CRA, but still want CSS in JS.

darthdeus commented 4 years ago

Is there any news on using astroturf with CRA without ejecting at this point? (other than the above mentioned craco plugin for Linaria)