3 key aspects that will change your vision of CSS

Eugenio Monforte

CSS is a language vilified by a large part of the web development community. If you are in that group, I’m sure you will change your mind if you focus on only 3 issues of this great declarative language:

  1. Cascade inheritance
  2. Overspecification
  3. Local prioritization

I could start every post on this blog with this well-known meme:

Animated gif of Peter Griffin from Family Guy messing around with a blind while the text CSS appears.

But it would be really unfair: CSS is a solid language that can solve the vast majority of problems with ease.

And in recent years this capability has been enhanced with a multitude of very powerful features: flexbox, grid, custom properties (variables), clamp, etc.

I think the fundamental problem is that CSS works differently from the programming languages commonly used by backend programmers, and even from the working environments of interface designers.

Let’s put it another way, CSS is different because it has to deal with different problems: while the backend (server side) only works in controlled contexts, the frontend (client side) serves multiple platforms, situations and environments so it must adapt.

And that resilience that CSS has is what ends up frustrating those who come from environments where control is the main goal.

Now, after many years of working with CSS, I believe that having in mind only some aspects we can work effectively and learn to enjoy CSS :)

Let’s go there.

The 3 key aspects

1. Cascade inheritance


The great feature of Cascade Style Sheets (CSS) is obviously its cascading logic: styles that can be inherited indefinitely from parent to childs.

The paradox is that, instead of taking it as the great tool it is, a lot of developers suffer and hate it in equal parts.

In my experience, in most cases these are professionals who do a little bit of everything: from working with databases to writing content, whether they are the “old” Webmasters or the “modern” Full Stack Devs.

And of course, when you have to do everything and work under pressure, anyone gets desperate and begins to pull libraries and frameworks that have everything under control… but no.

It isn’t a problem of those libraries or frameworks themselves. They are generally a compendium of good (and bad too) practices from which you can learn a lot.

The point is that these products have a paradigm behind them, and that which allows them to be solid in their proposals also makes them inflexible.

And the cascade is the big victim: once global styles are defined for the components and elements in the library, it’s difficult to adjust them to our needs without breaking everything.

Recommendations for working with the cascade

  1. Don’t use frameworks or libraries.

    Yes, just as it’s written.

    As much as it feels comfortable and fast at the beginning, using frameworks for something other than our needs will end up blocking and slowing us down. This happens all the time.

    The only two exceptions to this recommendation are when we know the framework / library thoroughly and: 1) It really fits the needs of the project, 2) We need to make a quick prototype to prove / show a concept.

  2. Use a minimum reset.

    When having to support multiple devices, it’s a good idea to have properties that “normalize” the basic styles of each.

    The idea is to have a good base to start shaping it according to our project. That’s why it’s good to be careful and stick to global styles that don’t compromise the styles of nested elements.

    If you haven’t heard of them, you can look for them like CSS reset, Normalize, Reboot (Bootstrap), although there are a lot of them.

  3. Use Tokens and apply them only at the appropriate level.

    It’s about saving in variables every data that can be used globally, such as colors or sizes of certain elements. I’ll write about Tokens in next articles.

    With this, we avoid a fight of styles because each level of elements will have a particular value.

2. Overspecification

The other big problem.

Once the cascade starts to change what we want, we desperately look for the desired styles to gain the lost priority by making them even more specific.

The selectors start to multiply, either by attributes, IDs, classes, elements (parents and children), even the universal selector (*)!

Here too the war begins, and it is known that when there is confrontation there are losers.

But it isn’t only that there are styles that will not be applied, a bigger problem appears, which is that the system will continue to grow (it’s what always happens), but we’ll not be able to reuse much of what we have developed, precisely because of the over-specificity that we defined.

And here the consequences exceed the work with CSS: repetition of code to style similar elements, decrease of development speed, increase of the resources associated with the project (new libraries, etc.).

The solution to overspecification? A mini process. Basically, steps 3, 4 and 5 of my 5-step strategy for writing CSS without going crazy:

  • Start with utility classes

    In order not to accumulate styles that later have to be replicated everywhere, it is better to isolate each style in its own class. In this way, you only have to add the necessary classes in the HTML.

  • Isolate patterns as components

    When HTML with the same classes start repeating, it means that they can be defined as components. With that we will have isolated styles for certain elements.

  • Define variants for similar but different components

    Instead of overwriting the base elements, we create variants either using utility classes or components with modifier classes.

3. Local prioritization (!important all the things)

The last resort.

When overspecification isn’t enough, we use the wildcard. By now ubiquitous: it’s common to find files full of !important trying to force the specification.

It happens because if not structured correctly, working with CSS becomes a war of priorities whose last tool is the !important, but it’s also frustrating because, by general logic: “when everything is important, nothing is important”.

The solution? If CSS isn’t worked in a systemic way, the prioritization by specification is a lost war.

If it comes to this point, the best thing to do is a general audit of user interfaces. As this can be very costly for the team, I suggest starting by focusing on the critical elements that appear most frequently.

In fact you would have to refactor the CSS files for each component, so have a strategic planning of the task.

It wasn’t that complex, was it?

Well here’s a little strategy for dealing with CSS at the architecture level :)