-
Notifications
You must be signed in to change notification settings - Fork 75
Does writing css in js really bring you many advantages? #132
Comments
Hi @mattijsbliek, CSS SpecificityI think BEM is great. But you have to enforce it to really benefit you at all. With aphrodite, I don't care/think about specificity. I don't have to follow a convention. I just use the technology and it works things out for me automatically and in a very explicit and straightforward way. And it's true that if I follow BEM I generally don't need to worry about specificity. But I've been in enough projects that "followed BEM" to know that it's really common for people (myself included) to not use BEM properly and then you start fighting specificity wars. The thing with aphrodite is that I don't have to care/think about it at all. That's what I like. CSS LintersThe biggest bang for your buck on linters is just to make sure that the code will run (no syntax errors to mess up other style rules in the case of CSS). I already use ESLint for my JavaScript and it's great. By moving my CSS to JS, I benefit from my existing linter and don't have to worry about having an extra linter for CSS. Now, right now I don't know of anything that checks that your styles are using legit properties. This might exist though because CSS in JS has been popular for a while now. If you find an ESLint plugin for this, let me know! CSS PreprocessorsI think this tweet from @markdalgleish sums up my feelings pretty well: JavaScript is a super powerful language and has great support for way more than you'd find in any made-up language like Sass, Less, PostCSS, Stylus, etc. Composing styles together with object spread For your specific example with changing the value of variables through media queries, that'd likely require a bit of work with JavaScript, but aphrodite has a really nice API for media queries and I can accomplish a similar thing, in a different way, and it'd still be super awesome. I should probably note here that I'm not a huge fan of JavaScript hacks to make things like media queries and/or pseudo elements/states work. Adding an event handler (even with delegation) is not my idea of a solid solution. But aphrodite creates actual style sheets. Which means that I can get all the benefits of CSS AND JavaScript together. And I can do it at runtime AND/OR build time. This is super awesome. CSS Vendor PrefixingRight. Remember, this wasn't necessarily a rub on any particular pre-processor or pre-processors in general. I'm just glad that we don't have to worry about this anymore! Other thoughtsI should mention something else here. I've been thinking about what it would be like to build something like Bootstrap or Foundation using aphrodite. I can see pros and cons: Pros:
Cons:
I'm mostly concerned with scaling component CSS in an application. So this doesn't concern me as much. I'm migrating JavaScriptAir.com to aphrodite and getting closer to 100% aphrodite. There will be some things that wont make sense with aphrodite (like reset styles). But that stuff isn't where we have scaling problems. It's mostly on reusable components. So maybe aphrodite isn't the right tool for a CSS framework, but I'll bet you can't find something better for a component library (whether that be for React, Angular, or Ember). Providing style overrides is super straightforward too. It's just so simple. I hope that helps answer your questions. If you haven't tried aphrodite yet, I recommend you give it a shot. It's great :-) |
CSS Components, Elements and UtilsCSS Components, Elements and Utils proposal is based on concepts such as ITCSS, SUITCSS, OOCSS (Object-Oriented CSS), SMACSS, BEM and Atomic Design. C, E and U are the prefix used for the CSS classes. Are used as a way to identify and localize the classes.
Utils are abstract, Elements are atomic and Components are complex. Components Classes, has the purpose to give style to components. Most of the time are not necessary because of the use of Utils Classes. Examples of such classes are the following:
Element Classes, has the purpose to give style to the atoms. The double dash (--) is used to define elements or modifiers when OOCSS is used. Examples of such classes are the following:
Utils Classes, are used every time, normally with a single property. Their purpose is reduce the amount of CSS extending objects and components. The double dash (--) is used to define the values. Examples of such classes are the following:
The goal is reduce complexity and repetition. Further class prefixes for child elements of a component should start with the name of the component. For example a child class for the element '.c-tabs' would be '.c-tabs--element' or '.c-tabs--inner'. Never use inheritance, being ideal always a 0010 specificity. Using this paradigm not only affects the CSS. It is the architecture that should lead the way in developing the templating. Using a modular structure, regardless of the language used. Reusing the components as often as necessary, always bearing in mind principles like DRY(Don't Repeat Yourself) or KIS (Keep It Simple). |
@kentcdodds I definitely see were you're coming from, and appreciate the benefits Aphrodite gives you. I don't want to go too much into specificity, there are ways to avoid those issues, but I get your point that not having to worry about is better than working around it. I still have some concerns regarding css in js however, interested to hear your thoughts on these: Styles become harder to trace and modifyCurrently, if I find a styling bug in production I can very quickly trace that back to a certain class and fix it. By transforming your css into abstract classes this becomes impossible to do, and you need a dev environment with sourcemaps to see what's going on. Also the use of Made up programming language and long term dependanceWell PostCSS isn't exactly made up, it's basically Babel for css. So you should be able to use less and less of it over time. Build tools change all the time. I still have to maintain some projects running CachingThis is actually kind of a big one for me, from the looks of it styles won't be cachable ever. Aphrodite renders an inline style tag to the html. Since in most situations it's preferable to not cache the html, or cache it with a very short expiry time, this means no caching for the css. Pro's vs. consIn the end, for me personally, the only big gains are avoiding specificity and tree shaking my css. The linter is nice to have, but given the huge stack of build tools that come with any SPA it's the least of my worries. I think for avoiding specificity, prefixing classes with a unique string, or using the upcoming all property would be better solutions. Which leaves tree-shaking. I'm wondering whether a tool like PurifyCSS wouldn't be sufficient here? |
Hey @mattijsbliek, Styles become harder to trace and modifyThis is true. Especially with regards to the inconvenience of the use of But like everything else, it's a trade-off. When using normal CSS (or a pre/post-processor), there's this implicit dependency between the component and the CSS used to style it. Sure I can ask myself and my team to keep to our standards, but that is really hard to enforce and often doesn't happen. With aphrodite, I'm no longer looking for the CSS used to style a component, I'm simply looking for the component. I now debug CSS situations just like I would debug markup or view logic situations. I find the code responsible for the component I'm looking at. Then I can see the explicit dependencies it has on styles. I feel like this is an improvement in my own workflow. Made up programming language and long term dependance
Good point, and I should apologize for calling these languages "made-up" because it sounds derogatory to the creators and that's not at all my intention. PostCSS is definitely my favorite of the bunch. I should note that there are a slew of plugins for PostCSS that will likely never make it in the language (like the ability to have loops and nesting). But I don't have much room to talk here because I use a few Babel plugins that will never make it into the language because of convenience 😅. This is another trade-off. I trade the flexibility to migrate to another CSS preprocessor for the flexibility to share my components with other applications (and open source them) in a way that's totally different and in many ways better. Providing style overrides now doesn't require people use the preprocessor of my choice or battling specificity. Instead they can simply pass in an object when using my API. This requires absolutely 0 tooling on their part, which I think is a big win. I should note also that even though I can't migrate to another css preprocessor without friction, neither could I when I was using CachingIf this becomes a bottle neck for you application (and not before), then you can use Aphrodite's support for server-side rendering which works really well. If caching is important to you, then as part of your build you could have it generate a CSS file and serve that (and have it cached). Then instruct aphrodite to "rehydrate" your application when it loads (so you can have dynamic pieces) and then you're off to the races. Pro's vs. cons
I've never used PurifyCSS before, but I probably wouldn't trust it if some of my CSS is used only when my application is in certain states (like when the delete modal is open for example). Like I said, I don't have to worry about this. In CSS applications, nobody feels comfortable removing any CSS because tracking down dependencies is a manual process and often you get it wrong. So you wind up with a CSS file that's bloated with a bunch of stuff you don't need/use. With aphrodite, when you delete a component, you delete the CSS with it and don't even have to think about it. I should have put I keep coming back to If you're interested in why I don't just use CSS modules, listen to this. I hope this is helpful! |
This is indeed a big win. We rely on the convention that a css file = js/html module, so if you have It would be great if we could have that explicit coupling without losing the ability to easily tweak styles in the browser.
I can see how this is very convenient. Having to overwrite stuff is often my main gripe when using third party modules that contain both js and css. For the css you often have to overwrite a bunch of styles, and are forced to deal with whatever naming convention the developer of the module decided on. When working on internal code it's a problem I come across less often. I basically do the same, passing an object to a module, which triggers the appropriate css classes. Of course you do have to wire up some logic for this, but a few ternary operators usually suffice.
Unless you're only serving only a couple KB's of CSS per page, why not take advantage of caching? Easier on your users and your servers and great for performance :)
This is a really nice feature to have! I would actually prefer if it were flipped though. So the server inlines only critical css, and Aphrodite rehydrates the rest via an external stylesheet which can be cached.
I briefly looked in to CSS modules, but I feel it only solves the problem of scoping/specificity while requiring you to drastically alter your build flow. Also the way they use
Absolutely! I think this is a very interesting discussion. We should definitely try new approaches and see what works and what doesn't :) |
Yeah, there's a PR to expose an API to specify that you don't want
Definitely a good point. It's a trade-off for sure. But something that should probably be measured. How much CSS are you sending to users that isn't ever being used by them? With lazy-loading of JS (and with aphrodite, CSS) you can avoid sending it altogether. So yeah, definitely differs per use-case.
Yeah, that's not really something I considered. I honestly didn't use many of the features of CSS modules. But as I mention in that 3 minute podcast I referenced, I'm not a fan of how it requires a change in the build flow either. I reeeally like that aphrodite requires 0 build at all. Thanks for chatting! |
@gc-victor, kind of rude of me to ignore you like that. Apologies. The reason that I didn't really address you though is because it appears that you're mostly interested in just pushing your own opinion and you didn't actually respond at all to what I said. And as this is my AMA and the question was directed at me, I wasn't quite sure how to respond to you. I'll just respond by saying that it looks like you've got a cool convention there. But that's something that I don't even have to think about with aphrodite, and I think that's great. |
Great debate you two! 👍 |
@kentcdodds Woah I never have heard of Aphrodite before, it looks awesome. I've been using Polymer for a few weeks and I've been avoiding anything other than vanilla CSS. But this looks like it can work wonders. Thanks |
this was a very constructive discussion that helped me a lot during my CSS in JS exploration. I use some of the topics described here as a reference in a recent blog post about my journey trying to use CSS in JS for component libraries: https://twitter.com/alansouzati/status/763473912793600001 I would appreciate your feedbadk @kentcdodds and @mattijsbliek I just think that component libraries has a big challenge when using CSS in JS that few people are exploring so far. |
Interesting post. Thanks for sharing. I think that the theming solution can be fairly easily solved in JS just using modules. You have a module that has all of your variables and expose a method for updating those. Then everywhere that needs to key off of that theme can just use that module. It's pretty straightforward :) |
Totally agree with that. this is what I did in grommet-simple project, and I even created a theme library that uses that strategy: https://github.com/grommet/grommet-hpe-theme. I have the modules with colors, fonts, and everything. CSS-in-JS seems pretty promising, but the cons listed by @mattijsbliek is a reality for us as well. Aphrodite is on top of my list of things to investigate in evolution of the CSS in JS in Grommet. :) |
I agree with Kent that you can just expose all of your variables for theming. This gives you the added benefit that you’re not dealing with specificity conflicts between your base styles and your theme by overwriting selectors (e.g. Bootstrap specificity hell). While CSS-in-JS is indeed promising, I find that all tools I've come across so far give up too much—in my opinion—on the CSS side to make it easier on the JS side. Either the form of syntax and/or by just applying a subset of CSS (no pseudo elements for instance). This means everybody who touches CSS has to know what the tool does to their CSS before it ends up in the DOM, and how to reverse engineer a selector in the DOM to the corresponding CSS in a source file. Some tools make this easier than others, but it can still be quite tricky people less familiar with JS. One of the strengths of CSS is that it’s so easy to grasp and apply, it would be great if we could lower the complexity of the CSS-in-JS tools to make them less mind boggling for beginners. |
@mattijsbliek, there's definitely some area for improvement on the CSS in JS front, but I think that for the most part the issues are with familiarity, not easiness/simplicity. I think that aphrodite is quite straightforward with how it translates to CSS. There's not much searching around to find where the responsible code is for any given file. I don't know, I could have reached the level of my experience with it that makes it hard for me to empathize with newcomers though. I appreciate the discussion :) |
@kentcdodds Yes that could very well be the case. I was looking into CSJS and it’s actually pretty close to what I'm looking for. However, I'm wondering if it would be possible to get rid of CSS-in-JS altogether and just write CSS files which get transpiled to something JS understands on import. Right now Webpack already converts your styles to JS via the style-loader/css-loader, so wouldn’t the step where you can actually utilise that CSS in your JS be pretty small? That way your editor just sees a regular CSS file—which is great for syntax highlighting and auto-completion—and there’s no learning curve for newcomers. Any thoughts on that? |
I feel like that would eliminate everything that I like about CSS in JS. The thing I like about it is the fact that it is in JavaScript, a real language. You could definitely write a loader like the |
It would explicitly couple CSS to JS, and make specificity issues a thing of the past. Those are the biggest problems I have right now. But I come from a CSS background, so I'm happy to keep writing CSS. It makes sense that different devs prefer different tools :) Thanks for clarifying! |
@kentcdodds I appreciate this thread is closed but I've been investigating CSS in JS and unless I've completely mis-understood I don't see the "no caching con" as a problem. If you're caching your JS with (for example) an MD5 hash query string (or some other approach) then the CSS is...well...cached, just within the JS? Please let me know if I'm being a tool here. Thanks. |
I read your tweet in which you say you don't care anymore about:
At the end you included a link to Aphrodite, which allows you to write css in your js components. I was hoping you could clarify some of your points, because I'm having a hard time seeing how Aphrodite is a better solution than something like Sass or PostCSS.
CSS Specificity
This is (almost) never a problem when using a naming convention such as BEM, and defining styles on a per component basis. Of course there will be some global styles like typography, grid etc. But in my experience these rarely—if ever—conflict.
CSS Linters
Could be I don't get what you mean here, but I don't see how linters are related to whether you use css in js or something else. To me all they do is enforce a consistent writing style, which I feel is important regardless of the technique used.
CSS Preprocessors
It seems to me Sass allows much easier use of variables such as
$primaryColor: #f00
to re-use across components than something like Aphrodite. Am I missing something here?Post-processors like PostCSS even offer you the possibility of doing something like this:
Changing variables depending on the breakpoint is a very powerful feature, and keeps your site more consistent than having to add that breakpoint to all components that use a variable that changes.
CSS Vendor Prefixing
Nobody cares about this anymore right? Whether you use Sass, PostCSS or Aphrodite, it's all auto-prefixed.
The text was updated successfully, but these errors were encountered: