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:
- Complete CSS guide for beginners and not so beginners
- Building CSS only interactive components from scratch part 1
- Building CSS only interactive components from scratch part 2
- Building CSS only interactive components from scratch part 3
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:
- 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.
- 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:
- Flex Box Layout.
- Grid Layout.
- 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.
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:
- We set basic styling for a tag.
- 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:
- We don't have scope, so we'll need to override this properties with new values wherever a p tag must look different.
- We may not have enough information at this point to ensure this will fit the majority of <p> tags on our project.
- If a future re-design adds differences between some <p> tags it will be difficult to re-code.
- 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