-
Notifications
You must be signed in to change notification settings - Fork 288
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
"Extract CSS File" seems misleading #86
Comments
A few minutes after writing this, I discovered that you have an example of CSS file generation in each of the example directories. My bad for not seeing that sooner. 😁 However, I think using the word "extract" here is misleading, because (at least to me) it implies something that statically extracts all the generated CSS into a file at build time (a la extract-text-webpack-plugin), whereas your examples generate a CSS file dynamically based on what was rendered by React. Maybe this is just my own personal bias showing (I've always used extract-text-webpack-plugin with LESS/CSS modules to generate static CSS files at build time), but the wording in the table seems misleading, because this method doesn't actually allow you to extract a full, reusable CSS file that you could use for the whole app, since it depends on what was rendered. An example of a CSS-in-JS library that does support the kind of file extraction I'm talking about is emotion (though admittedly, it's very limited in how you are allowed to use it). Do you have any thoughts on how to make this more explicit? |
Out of interest, why do you want to extract CSS into an external |
Mostly performance reasons for server-side rendering. I'm working on a small project that has to support clients with JS disabled, so the app is "universal" (or isomorphic or whatever term you prefer 😉). Generating a static CSS file is a requirement because then it gets cached in the browser forever, rather than having to serve different CSS with every page load (which is what I would have to resort to with most CSS-in-JS libraries). I otherwise prefer the CSS-in-JS approach from a developer experience standpoint, so I was curious as to whether any libraries gave you the best of both worlds, and stumbled upon this repo in my search. I understand my use case is probably not very common, so feel free to ignore me. 😁 I have just always seen the word "extract" used in the context of things like extract-text-webpack-plugin that extract the CSS statically at build time, so this threw me off a little bit. |
the word you are looking for is graceful degradation e.g. with SSR and extracted CSS browser will be able to render page even if JS is disabled Extracting CSS and prerendering HTML (with SSR) improves First Paint significantly.
It is pretty common - I mean requirement to generate CSS as text upfront would it be separate file or inlined in HTML in header. |
I was asking because I am currently working on You could actually serve that string on a server as a separate file, too. A bit more problematic would be to extract all styles at build time. I am thinking, one could simply at build time require all |
@stereobooster To clarify, when I said it's not common, I meant it's likely not common for people using CSS-in-JS solutions (at least, that seems to be my experience). This works just fine if you use plain CSS or a preprocessor like LESS or Sass, but I think most people who go the CSS-in-JS route are aware of the drawbacks and don't care about having all the CSS up front. In fact, you could argue that is one of the advantages of doing CSS-in-JS: it only renders the minimal CSS necessary for any given render in most cases. @streamich I think simply requiring the components and pulling out |
There is linaria - CSS is extracted at build time, no runtime is included |
@stereobooster Awesome, that's exactly what I was looking for! Eventually I would have gone through the whole list, but that saves me a lot of time. :) Thank you! |
To go back to the topic of this issue for a second, I just checked out your feature definitions here, and "zero runtime dependency" fits what I was looking for, I just hadn't thought of it that way. So if that page is representative of how the features of these libraries will ultimately be described, I think that solves this issue. |
that was the idea, but we are not quite there yet |
Just wanted to add here, that one of the main reasons to use CSS-in-JS is to be able to use component or render method variables in your CSS, like props. In that case I don't see how CSS can be extracted, or are there libraries that do that? |
@streamich Agreed, I think that's the main reason people use these solutions, so it makes sense that static file extraction isn't a top priority for (or even supported by) a majority of CSS-in-JS libraries. Having said that, stereobooster pointed out that linaria is one library that does everything at build time, so there is at least one example. Edit: To clarify, you can't have both simultaneously—either you have dynamic styles that are based on props and other runtime values, or you have static styles that can be extracted ahead of time. Although, a solution that let you mix and match the two approaches would be interesting. :) |
Yes, but most of the time this is not random value, but rather limited set of values which represent all possible states of application, like in Redux. So you can compile CSS-in-JS down to static CSS with limited number of classes which corresponds to all states |
Has anyone done that? Or, at least, maybe just the default value could be extracted as external CSS. |
@stereobooster But to do that you would have to somehow know every possible state of your application, which isn't possible (at least not generally—you could just render the app and grab the generated CSS, but that would only work assuming none of the variables ever change). Even Redux doesn't magically know all the possible states of the store, it just helps you be more explicit about state changes. That's why you're limited in what you can do if you want support for static CSS extraction. @streamich I would be interested in a solution that combines the static and dynamic approaches, where it would statically compile as much CSS as it can, and dynamically load the rest. But I'm not aware of a library that does this, and it seems like that would take a lot of work to implement. |
@RussianCow I thought about it a bit and I think it is kinda doable.
For Then And here is little summary grouping interfaces by generation:
Tell me if I'm missing something. |
@streamich This is what Linaria does (the 3rd generation part at least), so I think you're right, that will work. However, I personally don't think evaluating modules to get their style info is a good strategy because it means your code cannot depend on any preprocessing other than babel (such as webpack loaders, as noted in the issue here). Myself, I make heavy use of Is there no reason you couldn't just extract all of that info with a babel plugin, without needing to evaluate the file? What do you gain by doing that? PS: I like that separation of libraries by generation; it's very helpful. |
Actually, I just realized why Linaria does it that way. The key is in this comment and the one after it:
So evaluating the modules is necessary in order to properly parse styles that use references from other files, like this: import colors from './colors'
const style = css`
color: ${colors.blue};
` Then the solution I want wouldn't be possible with babel alone; you'd need a plugin for the bundler (e.g. webpack) to then follow and process all of the references after the bundle has been built. So, it's possible, but definitely not easy, and the solution would be tied to webpack or whatever bundler(s) you choose to write a plugin for. |
One big reason for using CSS-in-JS is the power JavaScript gives you. Like variables const className = rule({
color: require('../my-theme').mainColor
}); Mixins: const baseButtonStyles = {
border: 0,
background: 'grey',
};
const styles = sheet({
button: baseButtonStyles,
primary: {
...baseButtonStyles,
background: 'blue'
}
}); Environment vars: const className = rule({
outline: process.env.DEBUG ? '1px solid red' : '0'
}); And lots of other things can imaging...
Yes, but everyone uses Webpack :) Also, this problem arises only if you use Webpack loaders (but then again, everyone uses those :( ) Yeah, I guess it would have to be Webpack plugin. |
@streamich Right, I was asking about whether you had to actually evaluate the file in order to compile that info. Looks like you do unless you write a webpack plugin that follows all the references after the fact. So yeah, it would have to be either a babel plugin, and just not support loaders or any other kind of preprocessors, or a webpack plugin, which sounds harder to implement (I don't know) and would only work with webpack, but would work with no limitations on your code or build process. I guess you could write the plugin such that it would try to compile whatever styling it can statically, and if there is anything dynamic that it depends on (for instance the return value of a function call), it would leave that to be "compiled" at runtime. |
I've create an Here I created a demo: |
One more example of "extraction" - http://css-blocks.com/ |
First of all, thank you so much for your work in compiling this table! It's an awesome reference.
Now, my question: How do you determine whether a library supports CSS file extraction (under the "Extract CSS File" column)? As an example, aphrodite has a checkmark under that column, but this issue suggests that this is explicitly not a feature of the library. Other libraries that have that checkmark in the table, such as glamorous, don't suggest any way of accomplishing this.
Either I'm misunderstanding what that column means, in which case it might be a good idea to describe it in the readme, or some of these values are incorrect. If it's the latter, I would be more than happy to go through them and compile a list of which of these libraries actually advertise this as a feature.
Thanks!
The text was updated successfully, but these errors were encountered: