Open gaearon opened 5 years ago
Would this be like opening a style tag in .vue single file components? I think it'd be a great addition
Also relevant: https://github.com/callstack/linaria
Would this be like opening a style tag in .vue single file components?
Yeah. Except I don't want to partition file into sections like Vue does. Keep it JS but add the ability to add CSS styles inside.
This sounds a bit like styled-jsx
.
TBH I think astroturf covers all these points, at it's core it's co-locating CSS files in JS. It's actually implemented as that; the tag content is extracted to a CSS file (not written to disk via webpack magic). The really nice thing about this approach is you get ALL the benefits of writing separate files without the work. For instance mini-extraxt-css-plugin and html-plugin both automatically works without any additional setup.
We've got all the relevant standalone loaders, plugins and Babel plugins in repo, you could integrate with a global css
tag with all the library details hidden.
We've been using this for all our large projects with Sass and its really hit a nice sweet spot for convenient component API and playing nice with the tons of existing CSS tooling
I will say css-modules can be wonky and weird but if you're using them, I think the inline approach is really an ergonomic improvement. Cc @ai too
an astroturf
macro would be nice to start playing with this in CRA right now. it appears astroturf has a babel plugin already, so making a macro version shouldn't be too hard?
It would be great to treat CSS as a first-class citizen in ReactJS!
linaria
is a nice project, but right now I prefer astroturf
, because of more high order API:
styled
to created React component like in styled-component.&.primary
then can be used as <Button primary>
.styled
declaration and they still will be scoped.@jquense just a question, looking at astroturf repo I didn't find that. That's an way to create a local class, like :local
from css-modules does?
@threepointone
The macro should be easy enough since the core is a babel-plugin, tho i'm not sure how'd deal we the extracted css files, they'd need to be written to disk somewhere. In a webpack env tho the loader avoids this by using a virtual filesystem plugin, exposes the files only to webpack's fs
@renatoagds astroturf is css-modules, so the behavior is the same, we have :local
by default on since that's what css-loader does by default, so all classes are scoped. you can use :global
to create global ones instead
Yup – so the contrast here is that astroturf just handles pulling out the CSS into a separate virtual file, without imposing any of its own opinions on how to do CSS processing. It fully uses the existing CSS processing pipeline, which means that if you have Sass set up, then it will run Sass on your styling code – and this is exactly how we use it.
Do we have anyone that use astroturf
in production ? To share the pros and cons using it.
We use astroturf in production right now.
For the part of the API as described above, there's not really any "experience". The whole point is that it doesn't do anything at runtime – it just pulls the CSS out into a separate file, and lets the rest of your pipeline take over.
The experience analogous to defining a CSS module next to my JavaScript, just without having to go bother with having a separate file.
@renatoagds I am using it in production. I use both css
and styled
function in big React. I converted the project from Emotion.js to astroturf in a few days (mostly because of our auto-import hacks). Animation, many custom PostCSS plugins, webpack CSS minification — everything work. Compare to Emotion we even fix few issues and reduce code size (thanks to &.modifier
and @keyframes
syntax sugar).
The only problem is that Stylelint doesn’t support it right now. But I created an issue and fix is very simple.
Linaria covers all of the points,
@ai Linaria supports similar features, has higher order styled
API with CSS variables integration and syntax sugar for scoped keyframes. It doesn't support syntax sugar modifiers because we prefer it to keep as close to vanilla CSS as possible and these modifiers can be implemented in userland with a HOC.
In addition to these, Linaria has:
styled
(needs evaluate: true
)Regarding the Babel plugin writing files to disk, we used to do that with Linaria but ran into all sorts of problems. For example, if the Babel plugin is co-located with other Babel plugins, you need to have different configs for server, testing, linting etc. to not write files to disk. And if the Babel plugin is just used inside the webpack loader, then there no reason for it to write CSS to disk anyways.
@satya164 yeap, I like all types of zero runtime CSS-in-JS =^_^=
I wonder if it is possible to use the current toolchain and write a small babel macro to compile styles with postcss-modules
and rewrite the declaration to be compatible with the mini-css-extract-plugin
format that in a nutshell is like this https://github.com/giuseppeg/dss/blob/1f183805e790e1721242255cc7d03cfff56d20bd/webpack/loader.js#L19-L24
export.locals
is your { class: hashedClass }
map. The fact that mini-css-extract-plugin
expects the module export to be an array could be problematic though. Maybe @bebraw and @ai can help with validating the idea.
Usage would be like this
import cssModule from 'css-modules/macro'
export default () => <div className={styles.root} />
const styles = cssModules`
.root { display: block }
`
@giuseppeg it is completely what astroturf does 😄 Just a CSS Modules (postcss-modules is the same CSS Modules but for webpack) for Babel with few syntax sugar.
hah cool then! FWIW all the solutions suggested so far (css modules included) have a problem in common that is the cascade. When extracted the css is concatenated in some order so if you import some styles from another module the result is not necessarily predictable.
@giuseppeg in Linaria, if you use the styled
helper instead of passing class names around, it'll ensure that a higher specificity is applied when needed and the result is predictable.
@satya164 cool I guess that with the styled-components like approach it is doable (assuming you are not allowing composition).
@gaearon
Basically I want "CSS Modules" but without the "Modules" part
Could you please elaborate on that? "Modules" mean that the class names get "scoped", which is what you mention as a desired feature.
I also want to mention that it's great what you're doing here. I fully support you endeavor to implement a fully static css solution!
I just mean that I don't want two files.
Oh ok. I can relate to that (kinda 🙂)
I actually wanted to mention something else.
In my opinion, a perfect solution would not only be zero-runtime, but it would also not have any cute syntax shortcuts. E.g. things like this
const styles = css`
.button {
color: black;
&.primary {
color: blue;
}
}
`
seem "nice", but it's not valid css. What if I wanted to use real css nesting from the CSS specification? Granted, nesting is not often needed with "scoped components" model, but let's discuss it for the sake of argument.
It seems to me that I would not be able to do that because it would conflict with the custom syntax of the library. But if the library only supported bare css, then adding nesting would be as easy as adding a postcss plugin. And it would be very similar to how we use babel
plugins to enable js features from the spec.
Same goes for other css features as well.
Writing css in a javascript file might feel awkward for those who are not used it and I believe it's important to be able to say the them "well, you can write any valid css here and it will work". That's how CSS modules work. And just being able to have computed values with js template strings is a great addition which is quite easy to grasp.
@everdimension yeap, astroturf
doesn’t have CSS syntax extensions. It is just PostCSS plugins
You technically don't even need to pull in the PostCSS bits for astroturf – it's sufficient to just pull in astroturf/loader
and set up css-loader
normally, though you probably want to enable modules for things to work the way you want.
I think the delta we want here on astroturf would be something like:
css
template literal to a Foo.module.css
file (instead of just Foo.css
)sass
template literal (or should it be scss
?) template string to a Foo.module.scss
file@taion not sure if you can generate and enqueue files in webpack from a loader (babel-loader or astroturf/loader in this case). You'd have to run the pipeline again. If I am right, the only option is to rewrite the file with babel so that a plugin can extract the compiled (scoped) styles to file.
@giuseppeg You can do that – it's exactly what astroturf does.
not sure if you can generate and enqueue files in webpack from a loader
You can and that's what it's doing. The only requirement for using astroturf's css tag is that you have a webpack' css-loader with the modules enabled for the inline styles. You don't need Babel or any other compilation. The styled() component sugar is extra
@giuseppeg in Linaria, we just add require('/path/to/file.css')
at the end of the file in the loader and it works great! afaik that's the only way to do it. webpack doesn't support returning multiple files from a loader.
Yeah we change
let styles = css`
.foo {}
`
To
let styles = require('./foo.module.css')
@jquense is foo.modules.css
a virtual file?
indeed it is! nice :) I've always been told to avoid side effects in loaders so I never looked into that. But it obviously makes things easier
Parcel supports a nice way of doing this where you can return multiple files from the plugin. Unfortunately no such luck in case of webpack :(
https://www.styled-components.com/ seems to fit the bill?
@Billy- styled-components has 15 KB runtime and compile styled every time in the browser. Zero-runtime solutions like astroturf and linaria are much better.
@Billy- styled-components applies your styles during runtime using the StyleSheet
API, linaria and astroturf generate CSS files when you build your app.
This project was deprecated, but what do you think about this idea? https://github.com/chrisdavies/stylextract-loader
import React from 'react'
export default function ({name}) {
return (
<h1 className="hello-header">{name}</h1>
)
}
<style>
.hello-header {
background: steelblue;
color: white;
}
</style>
import React from 'react'
export default function ({name}) {
return (
<h1 className={css.helloHeader}>{name}</h1>
)
}
const css = <style>
.helloHeader {
background: steelblue;
color: white;
}
</style>
I'm up for either linaria or astroturf. Both fit the bill of "it's just JS", are compatible with the postCSS ecosystems, don't ship any runtimes, and don't try to hijack the CSS engine.
I like astroturf's conception of extracting content of css literal as simple CSS module, so you can integrate it in already configured ecosystem that uses css modules. But it would be nice to support dynamic css properties in styled components, like linaria does.
Will be some sort of "extract dynamic properties to style attribute" useful? Or maybe there's some way to pass properties into styles with CSS variables?
What is the status currently? Is it possible to use astroturf with CRA without eject?
Yeap, astroturf approach is more simple. As result, it could work with different syntaxes like Sass. Because astroturf doesn’t parse it.
Linaria parses styles, so you can use it with more CSS-like syntax. But at least right now, Linaria has more features: source maps and out-of-box Stylelint support. Theoretically, astroturf can do it in future too, but not right now.
This change will vastly simplify one of React pain: files organization.
current files structure looks like this:
components
|-- user
|-- user.tsx
|-- user.module.css
|-- index
I do the above so that I can conveniently import the component from the folder as just:
import User from './components/user';
If this feature get built, we will be able to get ride of folders in components, forever.
I wish @jquense would champion this as he has knowledge about React and build-time CSS-in-JS.
Along with Hooks, it would be awosome to see this built into the next CRA 3.0
Any progress on this? Would love to be able to use Astroturf in CRA since none of the other solutions are ideal.
i am interested in working on a babel macro for this, however i am quite thrown by this requirement: “Extracted via the same pipeline as CSS Modules”. if i understand it right, that is impossible while keeping it in the same file, or adding some thing early in the build process to generate a css modules file. is that something y’all are open to doing? sounds like a webpack plugin then?
@sw-yx both solutions noted above already have simple Babel plugins and webpack loaders. You can use them as a reference but I think part of the discussion is why bother reinventing the wheel, the devil is in the details
i see. thanks Jason, only just understood that after rereading again. so basically we're at an impasse of just needing to pick one and implement it? sounds like astroturf fits Dan's original requirement of "potentially use Sass"
Sound like similar to Zeith static folder?
I've being playing with a mix of proper CSS – you know for the "Cascade Style" part – and scoped embedded styles in component file for both minimal isolated styling and narrow layout tweaking. I've been very happy with this approach – although admittedly I haven't used that into any production code yet.
I've set my preference on styled-jsx as it's well maintained, used extensively and basically all you want for this kind of usage.
There's a plugin for a Sass but although I've used Sass extensively in other projects I'd advocate against it in embedded style. If the embedded scoped styles get so complicate to the point that Sass feels "needed" I'd argue there's another underlying issue and those styles should probably not be scoped, but instead be some normal global cascading style or maybe a CSS module.
I also think it would be better if FE developers would starts to embrace CSS by the specs with the staged features and the process that goes with it (i.e via cssnext) rather than pushing some sugared syntax that newcomers will need to learn on top of the CSS standard. But it seems that this ship already sailed https://github.com/facebook/create-react-app/issues/78 🤷🏽♂️😟.
TL;DR; I think I'd welcome styled-jsx support but without Sass and the added completion (maintenance) and futile abstraction for scoped styling which would come with it.
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