DEV Community

Cover image for The ONLY CSS Guide You Will Need
Technophile
Technophile

Posted on

The ONLY CSS Guide You Will Need

We will cover topics ranging from naming selectors, organizing your code to specificity and which units to pick. I divided this guide into 5 habits that are important for every developer learning or improving their CSS.

Table of Contents

Habit #1: Consistent naming

Can you look at the following selectors in CSS, and answer these 3 questions:

  • WHAT do they do?
  • WHERE can they be used?
  • and are they RELATED to each other?

Unrelated selectors

We know that .title class is used to style the title, but, it doesn't tell us whether it's the main title of the website or the title of some article. And, we don't know if it's related to .card element at all.

And, it's same issue with the other classes, too. Without looking at the HTML code, we can't answer those 3 questions.

Just like other programming languages, CSS has adopted its own naming rules that developers should follow.

These include the following:

  • Names should be written in lowercase
  • Separate the words with hyphens (-)
  • Don't use camelCase, under_score or other naming conventions.
  • And, here's my personal take: don't use abbreviations unless they're commonly used among developers. like bg for background.

And, still, these are not enough to answer those 3 questions, especially when your project starts to grow.

Then, what is that secret sauce?

famous chef adding salt

It's BEM, the most popular naming convention in CSS among developers.

Let's look at our previous example and see how we apply this approach.

Looking at the HTML first, we can see that they're pricing cards. Each card has a class name, called .card. Inside, there’s a title, price and a list of features.

HTMl code

Let's use the BEM approach and change the .title to .card__title. Now, we know that it belongs to the .card element.

Let's apply the same logic for the other ones, too. Change them from .price to .card__price, and from .features to .card__features.

.card {
  ...
}

.bg-primary {
  ...
}

.card__title {
  ...
}

.big-text {
  ...
}

.card__price {
  ...
}

.card__features {
  ...
}
Enter fullscreen mode Exit fullscreen mode

Output:

Final output so far

But, what if one of the cards is a popular option and has a different background color. In that case, we add a new class name next to the .card, called .card--popular. And, then we can add the different background color to that new class name.

Added background color in the popular option

Also, the font size of the price in the popular card is bigger than other ones, so let’s add a new class name to it, called .card__price--big.

Increased font size

HTML code:

HTML Code

As you can see, BEM makes the code so much easier to read. And, most importantly, it answers the 3 fundamental questions:

  • WHAT does it do?
  • WHERE could it be used?
  • and is it RELATED to other elements?

Next time when you are naming selectors, try to cover all these questions.

Also, if you want to dive deeper into BEM, you read this awesome article.

Okay, let's continue.


Habit #2: Use CSS variables

Here’s a question for you. Do you know what color is this?

.class__name {
  background-color: #191970;
}
Enter fullscreen mode Exit fullscreen mode

Well, it’s dark blue. Now, here’s the next question: Is it a primary color, an accent color, or just a random one-off color used in the project?

That’s the problem. Without additional context, like looking at the design file or visiting the website, it’s difficult to know.

Solution?

CSS custom properties (AKA variables)

You might probably have heard the term, DRY (Don't Repeat Yourself) principle. If you find yourself repeating the same values or copy-pasting them across your CSS, you should store them as variables.

Let’s take our color example, and refactor it using variables. A good starting point is naming the variable by the color itself. In our case, it could be:

:root {
  --darkblue: #191970;
}

.class__name {
  background-color: var(--darkblue);
}
Enter fullscreen mode Exit fullscreen mode

Now that we used this variable, there's a potential problem here. What happens if the color itself changes to red or green? Suddenly, the name --darkblue doesn’t make sense anymore.

To solve this, avoid naming variables based on their literal value. Instead, name them according to their purpose. For example, if this color is the primary color of our project, we can name the variable --primary-color:

:root {
  --primary-color: #191970;
}

.card--popular {
  background-color: var(--primary-color);
  ....
}

button {
  background-color: var(--primary-color);
  ....
}
Enter fullscreen mode Exit fullscreen mode

Now, even if the color changes to green or any other color, variable name still makes sense.

Bonus tip: You can also use variables to store some random or complicated value. It will give a better context on what it is.

/* bad */
.element {
  transition: all 0.3s cubic-bezier(0.25, 0.1, 0.25, 1);
}
Enter fullscreen mode Exit fullscreen mode
/* good */
:root {
  --ease-out: cubic-bezier(0.25, 0.1, 0.25, 1);
}

.element {
  transition: all 0.3s var(--ease-out);
}
Enter fullscreen mode Exit fullscreen mode

Habit #3: Comment and organize

Writing comments... Either you like it or hate it.

Cat showing like thumb

But, comments in CSS are not just for giving explanations, they can also be used to organize and structure your code.

But, before that let’s first start with the intended use case: adding explanations.

Take a look the following code. Without the comments, can you tell me what it does?

.auto-grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
}
Enter fullscreen mode Exit fullscreen mode

How about now?

/* this is for auto-grid, lol */
.auto-grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
}
Enter fullscreen mode Exit fullscreen mode

Just kidding, but seriously, how about now?

/** 
 * Responsive gird that adjusts columns automatically
 * Each item has a minimum width of 22rem and a maximum of 100%
 **/
.auto-grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
}
Enter fullscreen mode Exit fullscreen mode

I know that we all hate writing comments in our code. But, if it's the only thing you can understand, try to give at least some explanations. Trust me, otherwise, you will forget it eventually.

You can ask ChatGPT to give a short and clean comment on how your code works.

ChatGPT responded with

Now let’s move to the second use case: organizing and grouping your code with comments.

If you're working with a single CSS file, you might already know that it gets messy real quick. And, it becomes a nightmare especially when you're trying to find that one selector. So, grouping similar or related blocks makes your file and life much easier.

Let's take our previous example, and apply this approach.

In our case, we can separate the global styles and group them in one place, ideally at the top of our file. (I will explain later why) Next, we can separate some of the components that we used in our project and group them together. Now, finally, our main cards. Since, we organized them by their name, we can now easily group them into one place and make it easy to separate from the rest of the code.

If you're not sure about how to actually structure your CSS, you can use the 7-1 approach (https://sass-guidelin.es/#the-7-1-pattern) to understand where each piece of your code belongs to. Although it's designed for Sass folders, you can apply the architecture itself into your single CSS file.

/*------------------------------------*\
  #GLOBAL
\*------------------------------------*/
*,
*::before,
*::after {
  ....
}

body {
  ....
}

h1,
h2,
h3,
p,
ul,
li {
  ....
}

/*------------------------------------*\
  #COMPONENTS
\*------------------------------------*/
button {
  ....
}

button:active {
  ....
}

/*------------------------------------*\
  #CARDS
\*------------------------------------*/
.cards {
  ....
}

.card {
  ....
}

.card--popular {
  ....
}

.card__title {
  ....
}

.card__price {
  ....
}

.card__price--big {
  ....
}

.card__features {
  ....
}

.card__features li::before {
  ....
}
Enter fullscreen mode Exit fullscreen mode

Also, when you're separating the blocks, make it easy for your eyes to spot them. I use this commenting approach by Harry Roberts in his CSS Guidelines. And, they look clean, too.

/*------------------------------------*\
  #FOO
\*------------------------------------*/
Enter fullscreen mode Exit fullscreen mode

Habit #4: Always use classes

Don’t use HTML elements as selectors, like h1, p, and button. You're only allowed to use them when you are applying global styles. But, if it’s something specific, always use classes. Even if it feels like extra step, you will thank yourself in the future.

/* bad */
button { .... }

button:active { .... }


/* good */
.button { .... }

.button:active { .... }
Enter fullscreen mode Exit fullscreen mode

And, speaking of HTML elements, don’t use ID selectors. You might have already heard the term “specificity”. If you haven’t, basically, every selector has its own specificity or “importance” score. You can view it in VS Code by hovering over the selector. The higher the score, the more “important” it is, which means it becomes much difficult to override its style with other lower scored selectors.

Specificity score in VS Code

You might think it’s a good thing. But, it’s not often the case. If you look at habit #3, we put the general styles at the top of the file. And, components, utilities and other specific styles are placed at the bottom. It’s because general styles are later overridden by specific styles.

/* lose */
.card {
  background-color: red;
}

/* win */
.card {
  background-color: blue;
}
Enter fullscreen mode Exit fullscreen mode

In CSS, whichever style is applied last will be used. And, in our case, if we use an ID selector somewhere in CSS, and later down the page, try to override its style with a class name, it doesn’t work, because the “specificity” of an id selector is higher than of a class selector.

/* win */
#card {
  background-color: red;
}

/* many lines later... */

/* lose */
.card {
  background-color: blue;
}
Enter fullscreen mode Exit fullscreen mode

Also, !important falls in the same category. Don’t use it unless absolutely necessary, such as for debugging why your style is not being applied to your element.

.card {
  border: 1px solid red !important; /* only for testing */ 
}
Enter fullscreen mode Exit fullscreen mode

On a similar note, don’t nest or chain selectors. It also increases the specificity score and makes it harder to maintain. Instead give it a separate class name. If you are unsure about what to name it, refer to habit #1.

/* bad */
.card__features li::before {
  ....
}


/* good */
.card__feature::before {
  ....
}
Enter fullscreen mode Exit fullscreen mode

Habit #5: Use appropriate unit

If you’re only using pixels (px) in your CSS, either you’re a beginner or living under the rock. Because you are missing out on so many cool features that other units can offer. And, it’s important to understand where to use them effectively.

Living under the rock

Here’s my general approach on using units in CSS for different cases:

For font sizes

  • Use rem. Because it's proven to work better than other units, including px. You can read it here, here and here.

For width and height

It's quite tricky.

  • If it is really specific number, use rem.

  • But, if it is not the case, you can use %, because they work really well most of the time.

  • vw and vh can be also good depending on the use case. But, I often face overflow issue with them. So, be careful.

  • But, if you're controlling the width of a paragraph or text, ch is often a good option. It stands for characters, which means you can specify how many characters you want per line.

For paddings, margins and gaps

  • If you want consistent output that doesn't change and fail, use rem.

  • If you want them to change based on font size, em works really well. em unit depends on the font size of the nearby content. You can leverage it to create responsive spacing with fluid font sizes as well.

For other cases

  • If you are dealing with very small sizes, like 4px or 8px where the difference is not that noticeable, you can use px. Because 0.125rem or 0.375rem might be difficult to understand at first sight compared to 2px or 6px.

All these units can be overwhelming especially if you want consistent approach in your CSS. But, learning to adopt to different solutions can be a game changer when you understand where to use them.


Outro

And, that's pretty much it. Hope you find this article helpful. If you ever struggle with how to write CSS, you can always come back to this article.

And, of course, there are so many ways of writing CSS, and only limit is your imagination.

Thanks to all developers and their awesome articles. I just gathered them all from years of learning into one hopefully useful article for you.

And, as always, thanks for reading and I’ll see you in the next one.

Top comments (0)