Why writing your vanilla CSS is a bad idea?
The method used for writing CSS has not changed for years now.
Most developers still use the old and well-know way of styling their stylesheets. Adding all the styling in one huge file. Adding and adding until the file becomes a huge maintenance mess.
At the beginning of the project, you think to yourself, I will create one file for the styles. And, later on, I will expand as needed.
But in no time you find out that you need to change the styling of an element in any particular part of the page. And now, the fun starts. Looking through hundreds, thousands of lines of pretty much the same code.
Of course, you can search for that particular element or class in all of the well-known code editors. Change the styling in that, one place, and move on.
But what if you need to change it in several places?
Looking through your HTML to find what element and class you used on that particular part of the page. Then, searching again through the stylesheet and updating it. You can easily lose time here.
You see where I am going with this?
That’s when CSS preprocessors come in to play.
What? What is a preprocessor?
Well, according to MDN Web Docs, a CSS preprocessor is a program that lets you generate CSS from the preprocessor’s own unique syntax.
Preprocessors allow writing of regular CSS, just as you would write it anywhere else. But they also expand these possibilities, giving us many more options to write the code.
There are plenty of preprocessors in use today. The popular ones are Sass, Less, and Stylus.
The preprocessors themselves are very similar. The main differences are in the language syntax, and the feature set.
In this article, I will not go in-depth for each one of them. This is just a quick introduction to what can be achieved with them. And, how they can benefit your project, regardless of the size of it.
For this article, I will be using Sass as an example.
Variables are a way of saving reusable styles. Ones that can be used throughout your stylesheet. Any CSS value that you think you can reuse can be saved as a variable. Fonts, colors, whatever you need can work.
The $ symbol is used in Sass to declare a variable.
Here is an example of variables being used for storing color values. This can be extremely helpful when working with brand colors. And, overall keeping consistency over the whole website.
$primary-color: #333;
body {
color: $primary-color;
}
We declare the variable $primary-color and set a value to it. Afterward, we can use the declared variable wherever we need it in the codebase.
And, this is what we get out in the CSS output.
body {
color: #333;
}
This is a very simple example. But imagine $primary-color being used in multiple places throughout the stylesheet. Especially when changing the theme color of a website. We declare the value of the color in one place, reuse it, and whenever we want to change it. We just, update the value in the variable at the beginning.
Wouldn’t it be awesome if we could write plain CSS the same way we write our HTML files?
HTML has a clear nested and visual hierarchy. On the other hand, CSS does not.
With Sass, we can write our styling in the same manner. Sass allows nesting of selectors. The only thing that we need to look out for here, is that we avoid nesting too deeply. With every next nesting, we are adding more complexity to our selectors in the CSS output. That can be hard for future maintenance, so it is considered a bad practice.
nav {
ul {
margin: 0;
padding: 0;
list-style: none;
}
li {
display: inline-block;
}
a {
display: block;
padding: 6px 12px;
text-decoration: none;
}
}
In this example, we are styling a typical navigation bar. Using the nesting capabilities of Sass saves us time, and it improves the readability of the code.
The CSS output.
nav ul {
margin: 0;
padding: 0;
list-style: none;
}
nav li {
display: inline-block;
}
nav a {
display: block;
padding: 6px 12px;
text-decoration: none;
}
Partials are a great way to modularize your code into smaller reusable parts. They are partial Sass files that contain snippets of CSS code. Using partials is a great way to avoid huge stylesheets.
A partial is named with a leading underscore. Something like _partial.scss.
When Sass encounters an underscore, it knows not to generate the partial in the CSS output. The partial is only used when it is referenced in a file with the @use rule.
To further simplify our stylesheets, to shorten them, we can use modules.
We write multiple Sass files, which improves maintenance and code readability. Modules are loaded into a file using the @use rule, just as partials.
This rule loads them as modules, which means that we can use all of their variables, mixins, and functions.
We just refer to the module in our Sass file with a namespace based on the filename. And, using a file will use the CSS it generates, in the CSS output.
// _base.scss
$font-stack: Helvetica, sans-serif;
$primary-color: #333;
body {
font: 100% $font-stack;
color: $primary-color;
}
// styles.scss
@use 'base';
.inverse {
background-color: base.$primary-color;
color: white;
}
When we refer to a module in our file with the @use rule, we omit the file extension. So we just write @use 'base';
body {
font: 100% Helvetica, sans-serif;
color: #333;
}
.inverse {
background-color: #333;
color: white;
}
Writing plain CSS can become tedious and overwhelming at some points. Adding multiple vendor prefixes every time we decide to use CSS3 gets boring after a couple of hundred lines of code.
With mixins, we can make groups of CSS declarations. Which can be reused throughout the stylesheet.
It is even possible to pass in values to our mixins, which makes them more flexible.
For creating mixins we use the @mixin directive.
//Sass
@mixin transform($property) {
-webkit-transform: $property;
-ms-transform: $property;
transform: $property;
}
.box { @include transform(rotate(30deg)); }
//CSS Output
.box {
-webkit-transform: rotate(30deg);
-ms-transform: rotate(30deg);
transform: rotate(30deg);
}
For this example, we named our mixin transform. Inside the mixin, we add all the needed vendor prefixes. In this case, we are using the transform() property in CSS3. And, we have also named the value it receives $property.
For using our mixins we just start our CSS declaration with @include followed by the name of the mixin.
Sharing CSS properties between selectors is possible when using @extend .
This part of Sass is one of the most useful features. It helps keep our Sass files very DRY.
In the next example, we will be styling simple messages. The messages will have most of the same styling, except for one or two properties.
We will also use placeholder classes that go hand in hand with extend. They are unique classes that are only printed when they are extended.
/* This CSS will print because %message-shared is extended. */
%message-shared {
border: 1px solid #ccc;
padding: 10px;
color: #333;
}
// This CSS won't print because %equal-heights is never extended.
%equal-heights {
display: flex;
flex-wrap: wrap;
}
.message {
@extend %message-shared;
}
.success {
@extend %message-shared;
border-color: green;
}
.error {
@extend %message-shared;
border-color: red;
}
.warning {
@extend %message-shared;
border-color: yellow;
}
In the CSS output, you will see how the extend directive is being used, and how it arranges the selectors later on.
.message, .success, .error, .warning {
border: 1px solid #ccc;
padding: 10px;
color: #333;
}
.success {
border-color: green;
}
.error {
border-color: red;
}
.warning {
border-color: yellow;
}
You will also see that the %equal-heights placeholder class was never used. So it is omitted from the output.
Operators bring us to another level of code writing. No more calculating on your own, guessing the percentages and messing up your layout.
With Sass, we get the standard mathematical operators (+, -, *, / and %). Which are pretty much straightforward.
In the next example, we will calculate the widths of two elements in a website.
.container {
width: 100%;
}
article[role="main"] {
float: left;
width: 600px / 960px * 100%;
}
aside[role="complementary"] {
float: right;
width: 300px / 960px * 100%;
}
In the input, we add the desired widths in pixels that we want to change to percentages. Which will be used in a simple fluid 960px grid.
.container {
width: 100%;
}
article[role="main"] {
float: left;
width: 62.5%;
}
aside[role="complementary"] {
float: right;
width: 31.25%;
}
Many of the modern alternatives to writing vanilla CSS are at the tips of our fingers. There are so many solutions to these problems, all with their pros and cons. But it’s always up to the developers to choose among them what works best for them, and the project in hand.
Not knowing that better ways of writing code exist, and just staying with vanilla CSS. Is what makes so many developers suffer to maintain their codebase.
What matters is not to be bound by these limitations. But to find the best available option that helps you work around it.