CSS: The bad bits (and how to avoid them)

The more I use CSS, the more I think it’s an abso­lute wild wild west. It’s mature, ubi­quit­ous, yet lacks basic, com­mon-sense fea­tures that come as a giv­en in oth­er sim­il­ar tools. After work­ing with sev­er­al mod­ern MVC web apps that have well archi­tec­ted back-ends, extens­ive test cov­er­age and good doc­u­ment­a­tion, I often find the stylesheets to be giant steam­ing piles of spa­ghetti garbage.

I’m prob­ably being unfair. It was born in a time when the web was still devel­op­ing, ideas were being thrown about and pop­u­lar ones were start­ing to stick.

As dif­fi­cult as I believe CSS is, every web­site in the world needs it and couldn’t sur­vive without it. So here’s some of my thoughts on what’s bad and how to keep sane when writ­ing it. It’s going to be a work­ing list, so any­thing else that bugs me in the future will get tacked onto the end.

1 . Glob­al scope #

My first and biggest qualm with CSS, is that any style declar­a­tion can change any aspect of any ele­ment on a page. This is super power­ful and great if you’ve just star­ted dip­ping your toes into web, but for big­ger sites, more often than not, it’s very dan­ger­ous. Almost all front-end developers at one point in their careers will add/​update/​remove a style, only to find it acci­dently leaks into some oth­er sec­tion of anoth­er page.

I think this is the most sig­ni­fic­ant issue with CSS. It makes any concept of mod­u­lar­ity very dif­fi­cult. Tomor­row a developer could just stick in a few !important tags and scrap your nicely encap­su­lated, reusable widget.

.blog-post { .title { font-size: 2rem; } } # Grrrr, styles leaking everywhere h1 { font-size: 5rem !important; }

SASS/SCSS helps us here, but not com­pletely. If you’re a good little dev, then leaky styles can be sig­ni­fic­antly reduced by nest­ing rules in a par­ent class.

.blog-post { .title { font-size: 2rem; } } # A bit better .home { h1 { font-size: 5rem; } }

This is bet­ter, but it isn’t a fool proof solu­tion. It only takes one care­less style to take you back to leak hell. One tight dead­line which forces you to make some ques­tion­able com­prom­ises (i.e. hacks).

If you told any back-end developer that they had to use a pro­gram­ming lan­guage that gave all vari­ables glob­al scope, made every object’s intern­al state vis­ible, let any oth­er developer over­ride their code, they’d prob­ably resign on the spot. But that’s the insane real­ity of CSS devel­op­ment. Everything’s up for grabs. Immut­ab­il­ity? Pfffshh.

The best way around glob­al scope that I’ve come across is to adopt the CSS nam­ing sys­tem, BEM, which expects developers to con­sist­ently write their code in a mod­u­lar way. It shuns dan­ger­ous leaky habits like styl­ing using tags (e.g. h1 , p , etc) and instead prefers styl­ing on class names. Class names which are delib­er­ately namespaced to the mod­ule (or Block in BEM ter­min­o­logy) that they live in, as to avoid acci­dent­al col­li­sions with sim­il­ar class names.

Often there’s an argu­ment to use tags to cre­ate a default set of styles. For most sites, it’s usu­ally a good idea. How­ever, if you’re just over­rid­ing those styles pretty much every­where, I’d say don’t both­er. Put them in gen­er­ic util­ity classes (e.g. .paragraph , .heading-1 , etc) and use them as you need.

BEM is by no means per­fect. It can be oner­ous writ­ing big, long, namespaced classes every­where. You’re still writ­ing CSS. You can still write dodgy styles that screw everything up. You still need dis­cip­line. But what it does give is struc­ture and a set of rules that will help you write bet­ter and more main­tain­able styles.

Spe­cificity is the mech­an­ism that CSS uses to decide which styles are more import­ant than oth­ers. In my exper­i­ence, it nearly always ends up in a race to the bot­tom (or the top in this con­text). A strew of !important tags, styles on arbit­rary IDs and unneeded div wrap­pers whose whole pur­pose is to nudge the spe­cificity just a bit high­er than trouble­some exist­ing styles. True stories.

If you’re mak­ing a new UI ele­ment, but anoth­er very spe­cif­ic style writ­ten by your col­league is stand­ing in your way, would you:

Invest time under­stand­ing your colleague’s UI wid­get, fig­ure out the impact of a change to the style to oth­er parts of the site, refact­or accordingly. Or bodge it with an !important .

Depend­ing on the kind of envir­on­ment you work in and the size of your code­base, your choice may vary.

Object-ori­ented lan­guages get round this prob­lem via inher­it­ance and sub­classing. The least​“import­ant” and unspe­cif­ic meth­ods are at the top of the inher­it­ance tree, most spe­cif­ic at the bot­tom. In my opin­ion, applic­a­tion of these OO prin­ciples in the realm of UI work much bet­ter than CSS’s spe­cificity. Any­one famil­i­ar with iOS’s UIKit for example will be famil­i­ar with mak­ing gen­er­ic UIView wid­gets along­side more spe­cif­ic sub­classes that over­ride cer­tain attrib­utes. On the oth­er hand, you’ve usu­ally got to write a bit more code and spend a little more thought on this kind of over­ride behaviour.

Don’t par­ti­cip­ate in the race to the bot­tom. In gen­er­al, make sure your styles aren’t too specific.

Try not to use !important (some­times you have to when work­ing with 3 rd party embeds).

(some­times you have to when work­ing with party embeds). Don’t style on IDs (very specific).

Use class names (unspecific).

Avoid including descendents in style select­ors as these will bump up spe­cificity. * With SASS / SCSS , nes­ted styles com­pile down to des­cend­ent select­ors, so don’t nest.

/ , nes­ted styles com­pile down to des­cend­ent select­ors, so don’t nest. If you’re using BEM , you’re already fol­low­ing these rules. 👍

3 . Implicit percentage rules #

Any­one who has used per­cent­ages in pad­ding rules before will be aware of how con­fus­ing things can get.

.sidebar { padding: 10%; }

If you’re new to CSS, the above style may give you pause for thought. It’s not clear what­so­ever how the 10% is going to act. 10% of what? It could be of the…

Viewport width

Viewport height

Width of the .sidebar

Height of the .sidebar

Parent width of the .sidebar

Parent height of the . sidebar

Totally unclear on the face of it. In actu­al fact, it will cal­cu­late the per­cent­age using the width of the par­ent ele­ment. This gives some unin­tu­it­ive res­ults, such as the ver­tic­al pad­ding chan­ging when the browser’s width changes instead of the height (incid­ent­ally, this weird­ness is often used to hack responsive aspect ratios). It also means that if you wanted your ver­tic­al pad­ding to respond to the height of your con­tain­er, you just can’t. 😞

In an ideal world, whenev­er you use a per­cent­age you should have to expli­citly spe­cify what value it should cor­res­pond to. I think that’s reas­on­able to expect.

Best workaround #

There’s not a lot you can do to get around this unfortunately.

If you’re look­ing to size things rel­at­ive to the view­port width and view­port height, then I’d recom­mend using the vw and vh units. The support is pretty good and you don’t have to faff around with per­cent­ages. Just watch out for pecu­li­ar­it­ies on mobile as the view­port width and height can change depend­ing on if the browser chrome is showing.

In terms of tack­ling the impli­cit per­cent­age issue, I think the approach of adding units like vw and vh is a good solu­tion. I just wish they took it a bit further.

When z‑index is caus­ing head­aches, a com­mon solu­tion, the nuc­le­ar one, is: z-index: 999999 . Once this hap­pens, chaos ensues. Any­thing that needs to appear above it in the future will likely include an extra 9.

I think it all starts to go down hill due to a lack of con­text. Because z‑index rules are spe­cified indi­vidu­ally across all stylesheets, the rela­tion­ships between them isn’t clear.

If we’re dis­cip­lined, SASS/SCSS can help us out here. Defin­ing all our z‑indexes as vari­ables togeth­er gives us the con­text we need.

$z-index-page: 100; $z-index-navigation: 200; $z-index-newsletter-modal: 300;

We can clearly see our news­let­ter mod­al goes above our nav­ig­a­tion, which goes over our page content.

I also tend to work in hun­dreds so we can slot oth­er z‑indexes in between if we need. This doesn’t mat­ter as much if every z‑index is stored like above, since you can just eas­ily update all the val­ues just in the one place.

5 . You have to use it #

Like JavaS­cript, CSS is the de facto lan­guage of the browser. It’s the only option for styl­ing web pages, so has to be accep­ted, warts and all. Hope­fully, some day browsers will become lan­guage agnost­ic. Someday.

On the oth­er hand, CSS is used (fairly) con­sist­ently across mul­tiple dif­fer­ent plat­forms and isn’t the walled garden that iOS, Android and the rest of the tech eco-sys­tem is becoming.

Best workaround #

Quit front end web devel­op­ment. If that’s not an option, tough, you’ve got to use CSS.