loading...
Cover image for CSS Best Practices and Advice for beginners

CSS Best Practices and Advice for beginners

joelbonetr profile image JoelBonetR ・15 min read

Welcome back to another styling post, this time I assume you know something about CSS or SCSS and that you are able to build a website, prettier or uglier, better or worse but at the end, able to get the job done.

If you are learning, and you can build a structure and styling, this post is for you. If you are familiar with CSS and/or SCSS development, as part of your projects or as part of your daily job, this post is for you.

If you are in an early stage of the learning curve, then I recommend you to take a comprehensive read and, if you don't catch the concepts, take a time to perform better understanding the following posts and then come back:


I'll try a "learn by doing" methodology here so you can follow it easily. Let's create a use case for the code.

Use case presentation

Imagine we get a job on a startup and we need to build a theme for a new e-commerce (based on prestashop, magento, custom e-commerce... this doesn't matter at all at this point).

We must receive one of both things:

  1. A bunch of designs where we can inspect or read the CSS properties and values such as colors, paddings, margins, font-family, font sizes and so.
  2. Same as point 1 but attached on a design system, which is preferred.

If we didn't receive a design that we can inspect (from a designer. A web designer, preferably) this is a bad practice from the company or from the design team, and there's no much we can do about apart from complain, but complain on our job on the first day seems kinda counterproductive so let's move on assuming we have a design.

On this design we'll be able to discern which HTML tags are shown on it. If we have a graphics designer doing this job we can not expect him/her to set HTML tags properly when exporting into a dev format (I.E. exporting from sketch to abstract) so we need this HTML basics.

Then we need to make a decision about how to build our structure, which is needed before adding colors, fonts, border radius, box shadows and so.

But I read somewhere that building little components is better for productivity and maintainability, is that true?

Yes, but you'll need to start with something. Is better to code all structure of a component (adding basic CSS for positioning and sizing this structure properly) before start adding more CSS on it.

Structure approaches

You can use tones of different approaches for the structure but we can resume all on 3 categories:

  1. Flex Box Layout.
  2. Grid Layout.
  3. Using media queries only.

At the beginning of responsive layouts, when first versions of foundation and bootstrap showed up, there was many frameworks and custom webs that used media queries only as flex and grid has poor support these days. Now we will found flex and grid or both together used on modern frameworks.

Framework

Do we need a framework?

Of course not, I've some concerns about that, let's make a list of PROs and CONs about it:

Pros

  • Increased productivity (as we'll need to build that we need from scratch if not using a FW).
  • Extensive documentation.
  • Community support (asking on SO for example)

Cons

  • The weight will be higher, and so will be on load times.
  • Using a framework will add tones of selectors, properties and browser default overrides (or what is worse, self overrides) that we definitely don't need, and that we'll need to override sooner or later.
  • We are not learning about CSS or SCSS, nor JS using a framework of this kind "as is" as we simply go to the doc and use what its meant to reach what we need.

I could do this list larger but it's enough for purpose. Obviously the pros and cons list for creating your own CSS for the structure would be something equal to this list but reversed.

Recently I created a framework (not published yet) using flexbox and CSS only components, componentized to being able to import only this parts we need on a project and the structure A.K.A. "grid system" took me only a pair of hours to dynamically build all possible structures on our design so it's not something that will take an entire agile sprint to reach (as a senior at least).

I recommend you to build your own as you will learn a lot doing it, but let's move on to the next step as this is a best practices post after all, so I'll add code from my own as example and not from a framework.

CSS / SCSS Best Practices

We finally arrived at the point on where best practices can save our "me" from the future from our "me" from the past.

Best practices - Chapter 1: Browser Defaults

This chapter will be quick. The statement is "Knowing HTML tags and it's default properties makes your code better".

Let's set an example:

span.text {
  display: block;
  margin: 1em 0;
}

It can seem nice at first sight but we've just transmuted a <span> into a default <p> tag. So we just can use a <p> tag instead of a <span class="text"> , avoiding misuse of a tag and extra rendering or re-rendering on execution time and also some bytes on our css. It's also more maintainable not having this useless css declaration.

Disclaimer:

Each browser may render different default properties and values on an html tag, it's your responsibility to check whether you need to implicitly define some property with some value or let it "as is".

Remember than you can use vendor prefixes to establish some property-value relation that will not being rendered by other browsers.

Now for the question do websites need to look exactly the same in every browser? the answer is NO!. Each one of us is used to a browser, if they feel uncomfortable with some browser, people simply move on to another. The browser defaults is one of the reasons why *Chrome is used by more than 80% of people worldwide or more than 70% on a more accurate statistics site, giving more usage to Safari which is bad. Is bad because Safari is the new IE, they don't follow the standards and don't maintain Safari properly, causing some properties to malfunction, non-natural renderings and bugs when applied. Also mention that overriding all browser defaults will cause some (mostly unnecessary) overheat to your webpage.

*This statistics may differ from you company clients, so make sure you use analytics to know which browsers and devices your clients or potential clients are using.

Best practices - Chapter 2: Scopes

On the design of the main page for example we'll see repetitions, like all <p> tags have almost the same properties with almost the same values, so it's logic to isolate this common tag into CSS like this:

 p {
   font-family: 'Roboto', sans-serif;
   font-size: 18px;
   font-weight: 400;
   margin-bottom: 1em;
   line-height: 16px;
 }

Isn't it?

Of course it is but this code is good and bad at the same time, let's see why.

It's OK because:

  1. We set basic styling for a tag.
  2. we don't need to add boilerplate to our html, avoiding class or id usage which raises the productivity while avoiding the need to document another specific class.

It's NOT OK because:

  1. We don't have scope, so we'll need to override this properties with new values wherever a p tag must look different.
  2. We may not have enough information at this point to ensure this will fit the majority of <p> tags on our project.
  3. If a future re-design adds differences between some <p> tags it will be difficult to re-code.
  4. We've override a browser default with the exact same value margin-bottom: 1em;

So what can we do to repair this NOT OK list?

CSS Version:

 div#main p {
   font-family: 'Roboto', sans-serif;
   font-size: 18px;
   font-weight: 400;
   line-height: 16px;
 }

SCSS version:

 div#main {
   p {
     font-family: 'Roboto', sans-serif;
     font-size: 18px;
     font-weight: 400;
     line-height: 16px;
   }
 }

Now we have scope; It will apply this paragraph properties only when being a child of <div id="main"> which in this example will be the main or homepage wrapper. On the products page we must have a <div id="products-page"> as main wrapper for example, so we can set specific styles for each layout on our e-commerce or web application.

We also avoid the usage of margin-bottom if it's the same as browser default, which avoids an unnecessary re-render (so it's more efficient, fast and clean).

Now, we are able to modify or override this properties adding a class.

CSS Version

 div#main p {
   font-family: 'Roboto', sans-serif;
   font-size: 18px;
   font-weight: 400;
   line-height: 16px;
 }
 div#main p.big-text {
   font-family: 'Roboto', sans-serif;
   font-size: 24px;
   font-weight: bold;
   line-height: 26px;
 }

SCSS Version

 div#main {
   p {
     font-family: 'Roboto', sans-serif;
     font-size: 18px;
     font-weight: 400;
     line-height: 16px;
     &.big-text {
       font-family: 'Roboto', sans-serif;
       font-size: 24px;
       font-weight: bold;
       line-height: 26px;
     }
   }
 }

We are overriding the font-size,font-weight, and line-height properties. It would be better to define a generic tag with only these properties and values that are common to all tags of this kind on the same scope, but we can also set negative selectors for this.

CSS Version

 div#main p { 
   font-family: 'Roboto', sans-serif;
 }
 div#main p:not(.big-text) {
   font-size: 18px;
   font-weight: 400;
   line-height: 16px;
 }
 div#main p.big-text {
   font-size: 24px;
   font-weight: bold;
   line-height: 26px;
 }

SCSS Version

 div#main {
   p {
     font-family: 'Roboto', sans-serif;
     &:not(.big-text) {
       font-size: 18px;
       font-weight: 400;
       line-height: 16px;
     }
     &.big-text {
       font-size: 24px;
       font-weight: bold;
       line-height: 26px;
     }
   }
 }

*As long as we dive into properties we can see the benefits of SCSS about maintainability and readability, don't you?

Now we have a common value for the font-family property on p tags which are childs of #main div, and specific values for changing properties depending on a specific class (or the absence of a specific class). No overrides, easy to read, effective on execution, all OK, we nailed it!

Best practices - Chapter 3: Class Names

Use semantically correct language for your class names.

This means that the following code is garbage:

div.div-1 {
  background: lightblue;
}
div.div-2 {
  background: goldenrod;
}

You may know what div-1 means NOW, but when you came back to this project after three or four months to develop an evolutive, you'll need to inspect each item to know the hell is happening when adding class="div-1" to a div.

Moreover this could lead you to create a new class for doing the same as div-1 and div-2 because you don't remember the div-1 and div-2 declarations and the intellisense of your IDE does not give you any hint about setting a lightblue or goldenrod background colors.

This doesn't need further explanation, read the following cleaned code by your own:

.lightblue-background {
  background: lightblue;
}
.goldenrod-background {
  background: goldenrod;
}

The same applies to abstract class names like class="brown-sugar" or class="pretty-text", if you can remember what this classes are about after some months, congrats, but some coworkers can talk bad about you (and you can't blame them for that).

Best practices - Chapter 4: Wrappers

Don't wrap everything without reason.

I saw many times things like

 <div class="header-wrapper"> <h1> HOMEPAGE </h1> </div>

The question here is why did you do that? You can simply add a class to the h1 tag and it's all ok. If you need to wrap something because the layout type (flex or grid) needs some wrapper with some specific property to reach the expected design it's ok, but wrapping things like way of life adds node elements into your HTML structure, which makes it heavier, less maintainable and even throws a pagespeed warning (lowering your SEO).

Best practices - Chapter 5: Resets

Don't use them unless it's extremely necessary

If you don't know what "Reset" means on CSS context, it will be the action of setting N properties to value 0 or "none", on one or more elements. For example:

 * { 
    margin: 0; 
    padding: 0; 
 }

This will avoid items for being rendered with browser defaults if any so you can set your own margins and paddings for each element (in this example).

It can also being used to resetting values given by a framework (usually frameworks have a built-in reset).

If you know how CSS is being rendered (and re-rendered) by the browser you can expect this to cause overhead. You're right and that's why I told you not to use them unless necessary, specially if you want a good pagespeed performance.

The only "reset" I recommend to use is:

* { 
   box-sizing: border-box; 
}

This will prevent browsers to calculate the size of different objects different from others, which is helpful and sometimes needed.

You can find more information about this property here

As a better practice you can add box-sizing: border-box; only to the relevant elements or use it when you see extrange behavior on an element when testing on different browsers.

Best practices - Chapter 6: Selector Specificity

Avoid extra selectors

If you already read my post Complete CSS guide for beginners and not so beginners you know how to override properties properly (adding specificity). So if you add extra-specificity by default it will be difficult to override these properties in the future.

Of course, the better situation could be a greenfield where overrides are not needed, but in the real world the things grow and the override necessity appear as water in the desert. By the fact, if you are on a project where overrides are not needed, why you want to add extra specificity for anyway?

 body > div#main > .container p.bold {
   font-weight: bold;
 }

This extra selectors that gives an exclusive specificity will be bad for both things, one explained above (you will have to add more specificity to override it, which at this point is kinda difficult), and rendering. The render will search for one and fall to the next one on the CSSOM (CSS Object Model), so the best solution will be:

 .bold {
   font-weight: bold;
 }

Now you can even use it on a span, or h tag for example, not only on p tags.

Best practices - Chapter 7: Concatenate classes

Yup! Some simply fact, add classes which applies little portion of the styling, instead on setting a bunch of things on a single class.

 <div class="d-flex justify-center align-items-center lightgray-background">
  <p class="primary-color bold"> Hello world </p>
 </div>

Look how easy you can interpret this code, like reading a book. Aren't you agree?

The CSS code will look like

.d-flex { display: flex; }
.justify-center { justify-content: center; }
.align-items-center { align-items: center; }
.lightgray-background { background: lightgray; }
.primary-color { color: #4570DE; }
.bold{ font-weight: bold; }

No brainer for all people who put them hands on this project (including yourself on a future iteration of the project).

Best practices - Chapter 8: Use SCSS

If possible, of course.

Why?

Among other things SCSS is more readable, lets you create mixins, selectors tree, @use statements instead @imports and will always being compressed/minified when transpiled into CSS (if not, use another transpiler/compiler :D). And if that was not enough, some bundlers like Parcel.js will add automagically the compiler out of the box so you don't need to search a compiler by your own and configure it. Moreover using a bundler usually adds an extra feature; if you use SCSS @use statements properly and segment what you need where you need, the bundler compiler package will not generate unused CSS on your view (that's a thing to detail on another post, but now you know that this exists so you can search some info about).

Best practices - Chapter 9: Divide et impera

Another simply-to-follow rule following the chapter 7. Instead of type chunks of styles for a single purpose, try to abstract it on a logic form for future usages.

If you are about to write a CSS component, you may want to isolate it, for not depending on other CSS rules to work.

Think on a collapsible element as a component, and an accordion as a set of collapsible elements. Now, if you are going to code an accordion, don't do it. Code a collapsible element instead. Now you can add a collapsible after another and "et voilà" you get an accordion.

You can see an example of a stand alone collapsible/accordion code here which is intended to work both as single collapsible element or as accordion built of N collapsible elements.

Now you have to put this code somewhere on your stylesheets, but where? If you follow the rules on chapter 10 (which is not necessary but it's fine to know them) the question doesn't exist, but if you have this question, here's the answer: it depends on your environment. If you use SCSS and you import only what you need on each view then the answer is clearly "add a new collapsible.scss to your project" as a stand-alone component, as a partial or whatever, depending on your approach.

If it needs some variable, like branding color, simply make sure you add the @use you need on this file.

Best practices - Chapter 10: CSS Methodologies

This is a more extensive matter that I'm not detailing here (I'm getting relevant information and structuring it for adding it on a new block on Complete CSS guide for beginners and not so beginners post), but if you are on an advanced point of the learning curve, and want to know more right now, check the following concepts:

Paradigm/Methodology description
OOCSS

OOCSS stands for Object Oriented CSS, so it's best understood in the context of Object Oriented programming.

Focuses on flexible, modular, swappable components that do One Thing Well. OOCSS focuses on the single responsibility principle, separation of concerns, and much more of the foundational concepts of Object Oriented Programming.
BEM BEM is a specific concrete application of OOCSS. BEM stands for Block Element Modifier, and it describes the pattern of each CSS object's class name. Essentially, each BEM class starts with a block, which is an object name. Let's start with .example. Then, for children of that block, you add an element, separating it with two underscores: .example__name. Finally, you can modify any class (block or element) by adding a modifier, separated with two hyphens: .example--expanded.
SMACSS SMACSS stands for Scalable and Modular Architecture for CSS. It's a book and a methodology for writing CSS (created by Jonathan Snook), but its most significant and influential aspect is its organizational system, which is designed to provide a set of buckets into which CSS should be organized.

Since OOCSS is an abstract coding methodology, BEM is a concrete application of OOCSS, and SMACSS is an OOCSS-focused organizational structure, they actually play together very nicely, specially using Sass / SCSS.


Footnotes

There's more best practices out there, and some people could contradict or disagree with some things that I wrote. Long time ago I read posts like this and then I tried using it by myself to see which fits better for me, if some advice fit only for projects with some specific specs or if there's something I can take to a more modern usage of a specific tech.

If you know some advice I left or you want to add something based on your experience please, add it into the comments section so we can learn from your experiences too.

If you want to know more about compiling Sass / SCSS into CSS on development time, for production or how to install and start using a bundler, you can read this tutorial which covers all development process you can find on a modern company, simplified and easy to follow for learning purposes.


As always thanks for reading, hope it helps you in some way and I'll appreciate your support through reactions and comments.

Best regards,

Joel

Posted on by:

joelbonetr profile

JoelBonetR

@joelbonetr

Senior Web Developer- Intermediate Grade on Computer Systems- High Grade on Web Application Development- Coursed a non-official MBA with Marketing and HR

Discussion

markdown guide
 
[deleted]
 
[deleted]
 
[deleted]

First of all thanks for explaining yourself, I simply feel angry when people add negative comments without arguing it properly.

Even now, you didn't explained why you think these points are incorrect, you are only pointing things that you think they are. I'll explain your concerns point by point so you can learn a bit more about each.

First of all the div#main selector is not for copy paste into a project (but if you do it won't hurt anybody), the correct selector would be #main, without html tag. This is correct but the tag here is for giving more information to the reader, meaning #main will be a container or content wrapper.
You throw three statements: There is no reason to use a div (only), there is never a reason to use an id for styling and there is never a reason to use a div and an adjacent id.
Sadly you only matched 1 of 3. There is never a reason to use a div and adjacent ID - this is correct, as I explained.

Let me explain the other two statements quickly:

Imagine you are templating a products component on an e-commerce (product boxes with thumbnail/s, excerpt, price and so, and a product details view) and you use a <p> tag for excerpts (depending on the design they must be <span> tags, but let's suppose we get a little paragraph on the design here).

Then you want this paragraph tags for being formatted on a way, that are (should be) different from the rest of <p> tags on your project or even on your component (the excerpt should and must be different from the full detailed product view <p> tags as the message you transmit to your customers is different, on the excerpt you want them to click on the product card and on the product page you want it to buy the product, but this is a marketing -> design reason and the developer has nothing to choose here after all).
How you deal with it on a semantically correct point of view? there's a simplified example:

 #product-cards {
   background: $product-cards-background;
   p {
     font-weight: bold;
     font-size: 18px;
   }
 }
 #product-details {
   p {
     font-size: 16px;
   }
 }

I used an ID for styling, and for getting context, and a tag for giving styles to different usages of the same html element.

Obviously the #product-cards and the #product-details are meant to be div (or section or article or... add here your HTML5 element you prefer that renders internally into a <div> element but looks fancy when used on the template) html elements with, apart from the id (added for the reasons I explained) will have one or more classes, for example:

<div id="product-cards" class="container d-flex justify-center">

If this view will have an specific background there's no point on adding a global class with this background.

I repeat, this was a simplified example.
When applying on a new company you usually found stylesheets with more than 300 lines with luck and more than 1000 without it for components that could be coded with less than a half, but there's deadlines, non-planified updates "on the fly", programmers that are not properly qualified, others that simply don't give af and many reasons for it to happen.

When you have your hands dirty on a re-design on big projects you definitely appreciate the presence of ids (unless they are repeated and used indiscriminately on the project).

So when you start a new project, at this point seems useless to add ids and getting context to your styles, but when this project grows, and grows again, they become the most useful thing for find and edit styles.

Last thing I want to answer something that I feel funny:
Line height would be relative to font size
I found this line really funny because there's no point on adding this information, you could go to w3c or mdn and I'm sure you will found this same information if someone wants to know the admitted values for the line-height property, don't you?

There's nothing incorrect using px, cm, % or whatever unit you want here. You can set it using units or without them, which would be the same than using percentile values, for example line-height: 1.2; is exactly the same than line-height: 120%; but this fits more to my CSS complete guide post instead on this one.

Your words, without examples, without properly constructed arguments and without denoting an experience background makes me think you studied a lot of stuff about CSS but worked less on real projects with it. I mean, if you work on your own projects it's nice, but if you are convinced about something you will make the same mistakes for years. If you put your hands on a third party project (even contributing on some public repo) will let you see different code styles, and you can ask to this person why he coded this like he did, and you will learn by the experience of others which has incalculable value.

Best regards

[deleted]
[deleted]
[deleted]

Of course, because the practices you are referring to (using ids) are good for:

  • Maintainability
  • Readability
  • Scalability
  • Javascript usage (using vanilla script for adding events is much more efficient when looking for a scoped DOM subtree than searching for classnames, and of course, when js find an ID stop looking if another element have another id because id are unique, which does not happen with classes; even searching a class as child of an id is much more efficient).

And don't cause negative effects on:

  • Speed (loading time, time to first interaction and so).
  • Weight (will stay the same using an id or a classname).
  • DOM size (also will stay the same, an id does no generate any additional DOM element).

If you can't explain something easy and with few words, you may not know enough about that.

[deleted]
[deleted]

We've decided that this thread of conversation does nothing positive for anyone - and will be removing ourselves from it. We suggest that you do the same, Joel. BTW nice 100/100/100/100 on your site score! That's awesome! See you around.

It could be nice to keep the conversation because it would be nice for readers, now theres plenty of clarifications that I spent time on without context but whatever... it's your decision.

You can add them to your article. They'll still be good time spent. : )