DEV Community

Alex Dimitrov
Alex Dimitrov

Posted on

Two tips to keep your CSS simpler

One of the approaches to write clean code is to also write simple code. Meaning — do your task in a way that is easy to implement, has fewer things that could break, and overall is easy to understand by other developers.

But how does that happen? Well, while achieving greatness might not be what we are going for, trying to simplify things most likely is 🙂

Goal #1 — clean up your code from unneeded rules

One of the easiest simplifications you can use is to not write code. Or if you already wrote it to delete it. Which one? Welp, the one that does nothing in particular. Here is an example:

...some-selector p {
    color: black;
    font-size: 1em;
    margin: 1em;
    line-height: 1.7;
}
Enter fullscreen mode Exit fullscreen mode

Just looking at this snippet without knowing what is going on around it is not enough to decide what to delete. But, for the sake of the example, let's consider the most common use case that you might encounter and see what can we do with the rules:

...some-selector p {
    color: black;  /* Inherittted? */
    font-size: 1em; /* That is default */
    margin: 1em;
    line-height: 1.7; /* for consistency, we might want this set to the parent */
}
Enter fullscreen mode Exit fullscreen mode
  • If we never set color , then it's inherited as black, so there is no point in defining it here.
  • font-size is the same, unless you changed the font size, there is no point in writing its default value. This often happens while people are debugging, trying to visualize, or changing values while testing.
  • line-height is slightly different — if you want visual consistency, often you'd like to have a specific font size for regular content and for low character-per-line content. If you define line-heights in many places, keeping things consistent could be a problem.

So, with cleaning up the above, it's possible to end up with the same result, but code that looks like this:

...some-selector p {
    margin: 1em;
}
Enter fullscreen mode Exit fullscreen mode

We just overwrite the margin sizings. And this could be further simplified if that margin is consistent across the website - if that is the case, we can set it to the top root level just as p { margin: 1em } without the ...some-selector wrapper around it.

Other code that can be cleaned is generally something along the lines of:

  • div with width: 100% that doesn't have any other rules or parents that would redefine it
  • height: 100% for regular content (like a <p> tag inside normal <div> tag). That height does nothing.
  • Complex styles for simple results, but this is harder to showcase or explain — many times there are simpler ways to achieve a design
  • Reduce depth — in vanilla CSS it's deleting the parent, in Sass, it's moving your selectors a level or two up.
  • Rules that do nothing in particular (could be any)

Goal #2 - Style only what is needed

Well, this one sounds obvious, isn't it? But we quickly forget it and just go with it, it's not like there is some price to how much code is written, right? Well, there is — time and money come into play when maintaining a medium to large codebase becomes sluggish and people stay away from it.

One of the ways to reduce this complication is to really think about what has to be styled. How are components connected and which component styles what? Here is an example:

/* Demo component that does one thing only. */
.card { color: red; }
.card .card-title {font-size: 2em}

/* A <section> element right in the website wrapper */
.section-welcome {padding: 5em 0;}
Enter fullscreen mode Exit fullscreen mode

If we need a card to be blue when it's inside the .section-welcome element, we have two realistic ways to do it: One is to follow the requirement 1:1 and write .section-welcome .card {}, which will overwrite the .card styles for us. The other approach is to write a modifier for the .card element like .card--secondary for example.

In this example, we can decide which of the two approaches is correct by looking into the design and the requirements. If the welcome section is very specific in its design (something unique for the website) and the .card element has to look different only there, then it's understandable to nest it inside the .section-welcome.

If the .card element changes are less specific and could be reused elsewhere, then it's best to have a modifier. A modifier is an extra class you can set to the component, which will style it and its children differently.

When you might want to use a Modifier:

  • When the visual change is common enough that it can be used elsewhere in the application or website like color, size, spacing changes etc.
  • When you can find it used elsewhere in the website. Note* if there are minor differences like 4px smaller padding or some opacity to only one element and consistency isn't what your designer needs but instead 1:1 results, then you can mix modifiers with nested selectors.

When you might want to nest your selector:

  • When there is only one change to a component on the whole app/website for a specific wrapper.
  • When you have many wrappers that slightly change a component in a different way. If you were to use modifiers, you would struggle to name them and you'd notice that you can't reuse them.

Some of the tips above are derived from bad practice in design - inconsistency, but in the real world, you will be bombarded with all kinds of scenarios — time limitations, bad designs, unrealistic expectations, difficult team members, lack of testing, change of directions and requirements, browser support that requires hacking, the editing experience (not just static code), backend team changing markup or forgetting a class (modifiers especially) and more. So, it takes experience in all of these problems to come up with the least problematic approach and apply it.

Overall, each situation might require you to think for a minute on the best approach. Don't forget you can refactor code! It's a major reason why big projects get hard to maintain and grow with bad structure — even if you think well of it, put a good architecture, when problems like the ones described above come up, we as developers might often forget things like "If you hack something now, come back to refactor it as soon as possible".

Discussion (0)