We built our website without CSS: the highs and the lows
There was a Smashing Magazine article about CSS in JavaScript recently. It explained the concept of not using CSS files; you instead define all of your styling as inline-styles in JavaScript.
The article itself was mildly interesting, but the votes on the comments were telling. The first two really sum it up:
Sounds like Bob could have saved himself some time and listed the things he doesn’t think are terrible ideas. Clearly abandoning the warm embrace of CSS files is not for everyone.
So follow the below little chart and see if this article might apply to you.
The psychology of change
Naturally plenty of people will resist the idea. If this is you, I ask you attempt one thing when reading this article: separate the technology from annoyance that comes with having to learn a new thing and the love affair web developers seem to have with doing things differently to what they did yesterday. One trick is to imagine it all the opposite way around. What if you had always used inline styles and then were told you should actually use these things called CSS files.
Setting the scene
The year was 2016. The month was May. I was starting a new project and I could afford to get a little crazy. It’s now July and the project is finished-ish, so I thought I’d report in with my findings (I’ve just realised that the word ‘finish’ has ‘ish’ at the end, surely there’s something witty I could say about that).
Spoiler alert, after two months of ups and downs, I don’t have a strong opinion either way, but by the end of this post I hope you might be inspired to give it a go, or at least have a good idea of the reasons why it wouldn’t work for your project.
Note: from here on in all examples will be React/ES2015.
The gist of it
I’ve said we’re not using CSS, but really we’re only getting rid of two parts of the whole CSS show, i.e.
- SASS/CSS files
- Class names in the HTML
We’re still going to use ‘CSS declarations’ which are the property/value pairs. E.g. color: #222.
The way we were
In the olden days, people would have CSS files with selectors and declarations. Then they would have HTML elements with class names. The styles get applied to an element because the class names in the HTML match the selector in the CSS file.
The way you go about things if you’re not using CSS files, is to do this:
Go find a developer that doesn’t know about CSS and ask them which of these two makes more sense.
Lists!
The good
- Set up/build pipeline is simpler
- No SASS (if you’re using SASS)
- Styling is more integrated with the logic of the application
- One less language
- Images as backgroundImage
- Style variables can be used in JavaScript
The bad
- Not having :before/:after has been a pain in one scenario
- Having a child element change on hover of a parent is messy
- It’s harder to tell what’s what in the dev tools DOM tree
- It’s harder to experiment with styles in dev tools and see the overall effect
- Anywhere you’d target something by class (e.g. tests) won’t work
- Hot reloading doesn’t work for separate style files
- Hover/media queries requires a library
An example
So what does it look like?
No additional libraries, this is just out-of-the-box React.
It’s a simple example but shows something quite powerful, the composition of styles (using the spread operator). Sure, you can combine styles with CSS, but the order of class names doesn’t matter. No no, the way the styles are merged depends on the order of the declarations in CSS files. Bonkers.
Now lets move onto something more complex. But first…
A bit about Malla
The code in these examples is from Malla, the project I have been building for the last two months. Malla is an app for storing and updating the text for your website. It’s like a lightweight CMS, but rather than adding entries to a database, you ‘draw’ text boxes onto the screen. What you wind up with is something that looks like a wireframe, but there’s an API to access the text. All this means that there’s some quite complex rules in place for the styles as users add, edit and arrange items on the page.
In Malla, there is the concept of a ‘box’ that holds some text. A box has two types and can be in one of three states.
Keep in mind that a ‘box’ is made up of a dozen or so elements. If I were using CSS, this would probably be:
- a .box class on the top-level <div>
- four modifier classes (.box--label, .box--moving, etc.)
- selectors like .box.box--moving .handle { display: ‘block’; }
- some logic to set the top/left based on props
- some logic to add/remove the modifier classes
In short, we have four things going on here:
- Base CSS classes
- Conditional CSS classes (the M in BEM)
- JavaScript to add/remove classes based on some state
- JavaScript to set left/top on style attribute of the element.
This probably seems pretty normal. If you’re like me, you never thought about how convoluted this is.
But when I stopped using classes, I saw how nice and clean this is with just JavaScript. It’s no longer some amalgamation of two different syntaxes bound by matching strings. It’s all just good ‘ol JavaScript.
Here’s a simple example, a button that should be grayed out when disabled:
If you haven’t seen the spread operator before (…props.style), just imagine that it ‘blows up’ the object — it gets replaced with the value of the object; it makes a nice way to clone or merge one object into another. You could achieve the same thing with Object.assign().
This basically replaces using something like classnames to conditionally add a modifier class, but is a lot more direct.
Here’s a more complex example of a box that changes its appearance based on a few different things. Note the if statements; each of these applies some styles to various components of the box (by modifying various properties in the style object).
Notice here that I start by cloning baseStyles which means my styles object can be further up the page or imported from another file.
A list of good and bad things
The good bits of SASS are JavaScript (good)
I’ve found that it’s easier to throw together a ‘mixin’ in JavaScript. Sass mixins aren’t hard, but neither is JavaScript. Three examples:
- The pos mixin takes four arguments and returns four CSS declerations for left, top, width and height (using ES2015 shorthand property names in the returned object)
- The shadow mixin returns a box-shadow of a particular size, defaulting to ‘medium’ (that argument default is ES2015 syntax)
- The inputStyle object is just a set of declarations that I use often throughout the app. This is like a SASS ‘placeholder’
You get the idea: whatever you want to do, it’s all just JavaScript so you already know how to do it.
Inheritance (bad)
Oh how I miss the C in CSS. I want all my <p> elements to have a line-height of 1.6, except in this markdown I’m parsing, I want all the <p> elements to have line-height: 1.
Nope, no dice. I can use a library like Radium and insert a style tag that has these rules, then set a class name on the markdown container. Sure, I can do that, but I don’t have to like it. This one thing alone may well be the thing that makes me go back to CSS on my next project. Although it’s only really been a pain when parsing markdown.
Setup is simpler (good)
Webpack is like a four year old child that’s amazing at the piano but is always screaming and throwing up. You’ve gotta respect the cleverness of webpack, but the less time I spend with it the better. Having zero complexity added to the pipeline for my styling is a good thing.
Messy pseudo (bad)
There’s only one place in my code where I actually want to use the pseudo selector :before. But not being able to use it was a pain in the ass. Also, using :hover requires a library (I’ve enjoyed using Radium which enables media queries too) and the item you’re hovering over must have a unique key or id. Also the hover triggers a render of the component, which might mess with other things you have going on. However there’s a lot to be said for ‘touch-first’ design which means never relying on hover for user-feedback.
Integrated logic (good)
The logic is all right there. I’m not adding/removing classes that exist somewhere else and depend on their order. I am explicitly saying if this thing is selected make its background blue.
No hot reloading of imported styles (bad)
Maybe it’s possible, but I can’t for the life of me work out how to get hot reloading to work if I change a JavaScript file that contains the styles for a component. This has meant that all of my styles reside in the same .jsx file as the rest of the component, which just adds lines. (I could probably word all that better. Meh, just read it twice.)
Images as background images (good)
CSS has some clever properties for dealing with images (size, repeat, etc.), but if you’re setting an image URL (e.g. profile photo) via JavaScript and using old-school CSS, you might feel forced to use an <img> tag which misses those features. When it’s all just styles, changing a background-image property is no different to changing the value of an <img> “src” attribute.
Classes do other things too (bad)
When you have developer tools open and are looking at a deeply nested DOM tree, it can be handy to see clearly that some div is the ‘article-container’ or ‘picture-frame’ or whatever. Occasionally I have found that it’s just a bit harder to get my bearings. Also when you’re using classes and you tweak, say, the padding on a .button class. That will affect all buttons on the page. If you’re using inline-styles, it will affect only that one button. Not a huge issue, but again, it’s been pesky at times. (If I wasn’t conducting a zero-testing experiment I would probably find having no class names a pain there, too.)
Variables are in JavaScript (good)
It’s been a problem I’ve had on every project I’ve worked on. I have variables (really I should be calling them ‘constants’) in SASS for things like ‘small-padding: 12px’ or ‘animation-duration: 170ms’. That’s great, then it comes time to do some animation in JavaScript and you want it to match. You either build a whole lotta complexity to share those variables, or you just have them in two files with a comment to make sure it matches the other file. Having everything in JavaScript removes the need for this fancy footwork.
No SASS (good)
If you don’t use SASS you can skip this bit.
Once set up, SASS is great. But it’s a big heavy beast. If you’re using *NIX and don’t want any pull requests from windows users you can skip this bit. But for windows it’s all a bit ridiculous. Of the one million packages I have in my project, all of them will run, out of the box, anywhere I clone the repo. Except SASS. Here you’ll need Visual Studio (!), C++ developer tools and python. God help you if you have an existing version of any of those. This past week the version of npm out didn’t work with the newest version of visual studio when it came time to do something with node-gyp. I don’t understand how node-gyp works and really I don’t want to have to (and I certainly don’t people that might contribute to my project to have to worry). So doing without SASS does away with that complexity.
Conclusion
There’s been too many pain points for me to solemnly swear that you should abandon CSS files and do all your styling in JavaScript. But the idea of matching class names to CSS selectors to apply styles just seems wrong now.
It’s like growing up in a household that only drinks low-fat milk. One day you go to a friend’s house and experience full-fat milk for the first time; your mind is blown and your eyes opened. Sure you can go back to low-fat milk, but you’ll always know that there’s something not quite right about it.
If I started a personal project today, I would do my styling in JavaScript. If I started a project for an employer with a team of developers, I would use good ‘ol CSS.
My recommendation to you? Do whatever you want.