
I’m not going to deny it, I’m a bit of a SASS refusenik. Although SASS has established itself as a popular technology – simple to to understand and powerful when deployed well. But I’ve seen it built badly before and seen some proper horror shows. In the end SASS still just publishes CSS. So CSS or SASS, which is better?
Back Up A Minute … What is CSS?
CSS stands for Cascading Style Sheets … just another internet TLA. When you load a web page into your browser, it will request a number of other files too (referred to as assets or collateral). These include images, scripts, CSS files and such like. The CSS files contain a number of ‘rules’ which dictate how elements on the page are displayed. Thus:
p {
color: red;
}Code language: CSS (css)will tell the browser that all paragraphs on the page should be displayed in red. Kids, do not try this at home.
The Cascading bit of the name refers to the means by which rules can be made more specific. Thus:
footer {
background-color: black;
}
footer p {
color: silver;
}Code language: CSS (css)Will make the footer of the page black and all the paragraphs within it light grey. Much nicer. For the rest of the web page, the paragraphs are still red. Ho hum.
So What is SASS?
SASS stands for Syntactically Awesome StyleSheets. Developers write the CSS and processes on the developer’s machine publish the SASS as CSS. There are newer variants, SCSS and LESS.
SASS was devised to enable features that didn’t exist in CSS itself, like variables, nesting, mixins, inheritance and other such functionalities that make writing CSS more maintainable and extendable. SASS allows developers to write cleaner and more efficient code by reducing repetition and automating tasks.
So Why the Anti?
I believe in rules. I just don’t believe in too many of them – as my kids used to say ”Dad has fewer rules than Mum, but they matter”.
Although SASS is quite easy to read, once loops extend beyond a page view it is easy to drop a rule into the wrong place and have it published multiple times. When a nest extends beyond a page view it is easy to drop a rule into the wrong place and get into some specificity race. Because SASS is powerful, it frequently amplifies errors and inelegant code design.
Besides, I quite like to know exactly what is getting served to the client’s browser. Modern web pages and refined modern website designs require complicated rules. Without real skill, applying rules to create the design required is very difficult.
So What‘s Changed?
Well … a lot. Here are just some of the features now supported in native CSS which make a developer’s life a lot easier.
CSS Variables
:root {
/* Define your variables inside :root to make them globally available */
--primary-color: #4CAF50;
--secondary-color: #ff6347;
--font-stack: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
--base-font-size: 16px;
}
body {
font-family: var(--font-stack);
font-size: var(--base-font-size);
}
.button {
background-color: var(--primary-color);
color: white;
padding: 10px;
border: none;
text-align: center;
display: inline-block;
font-size: var(--base-font-size);
margin: 4px 2px;
cursor: pointer;
}
.button:hover {
background-color: var(--secondary-color);
}
Code language: CSS (css)In this example, :root is used to define a set of global variables like --primary-color, --secondary-color, --font-stack, and --base-font-size. These variables are then used throughout the stylesheet by calling var(--variable-name).
Variables were the most important feature which set SASS apart. By reusing single values for different aspects of the site design, it becomes possible to keep the design harmonious and quickly apply changes across the design.
Now CSS has variables too … only better. Because the variables are supplied as part of the website collateral (therefore client-side as opposed to server-side), they can be manipulated by JavaScript and accessibility tools on the client’s browser.
CSS Nesting
Intuitively, it makes sense to ‘nest‘ CSS rules. Looking at our earlier example:
footer {
/* define rules for the footer */
background-color: black;
}
footer p {
/* define rules for the tag p in the context of the footer */
color: silver;
}Code language: CSS (css)This would be expressed in SASS as:
footer {
/* define rules for the footer */
background-color: black;
/* define rules for the tag p inside the footer */
p {
color: silver;
}
}
Code language: JavaScript (javascript)Scoping CSS rules inside other rules and mimicking the HTML structure can make stylesheets better organised and easier to maintain. Because this isn’t preprocessed back into a flat rule structure, native, nested CSS avoids the risk of specificity issues and bloated CSS output.
CSS Nesting is now supported all major browsers. This technique just about safe to use (as of April 2024).
Pseudo Classes
Pseudo classes have been around for years in CSS. A pseudo-class is a keyword added to a selector that specifies a special state of the selected element(s), such as :hover to style an element when it’s hovered over by a cursor.
CSS now boasts two new mind-bendingly useful pseudo-classes:
:has()
Oh, how long have I waited for the :has() selector:
#user-menu:has(.cart-icon-full) {
background-color: /* apply styles here */;
}Code language: CSS (css)Rather than using scripts or server-side code to affect a parent item with context-dependent children, this pseudo-class allows you to affect the appearance of your site much more simply. In this case highlighting the user menu when the cart receives content.
No real equivalent to this rule exists in SASS. SASS can adopt it and use it, but it is a property of CSS.
:is()
.is() has been around in jQuery for years and is an exceptionally useful shorthand for identifying items by an arbitrary mix of attributes anywhere in the web page.
This new pseudo-class does the same.
:is( match1, match2, match3, match4 ) {
/* apply styles here */
}Code language: CSS (css)Easy to read and extend. Used judiciously, this selector offers a highly readable and maintainable means of styling disparate elements.
As above, there is no real equivalent to this rule exists in SASS.
CSS Container Queries
Contaner queries will revolutionise responsive design. Container queries, part of these more recent advancements in CSS, allow you to apply styles based on the size of a containing element rather than the viewport, providing a more flexible and responsive design approach. As of April 2024 implementation is not quite there in all major browsers but it is coming … and soon.
First, you need to define a container. This is done by setting a container type in CSS:
<code>.container {
container-type: inline-size;
}
</code>Code language: HTML, XML (xml)Then, you can write a container query to apply styles based on the size of this container:
<code>.container {
container-type: inline-size;
}
@container (min-width: 500px) {
.card {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
}
</code>Code language: HTML, XML (xml)In this example, .container is designated as a container where container queries can be applied. The @container rule checks the width of the .container element, and if it’s at least 500 pixels wide, the .card elements inside it are arranged in a two-column grid.
This approach allows for component-specific breakpoints, offering more precise control over the layout and design as the container’s size changes, rather than the entire viewport’s size. Rather than guessing what an element will look like on an obscure tablet screen, you can now ensure each element looks like right when constrained by the larger layout.
Cascade layers
Modern web sites are complicated and CSS can get big. Because an element can be addressed in more than one way or more than once:
footer p {
/* this paragraph is in the footer */
color: silver;
}
p.address {
/* but it also has the CSS class address */
color: gold;
}
/* some time later - maybe even in a different file */
p.footer {
color: bronze;
}Code language: CSS (css)It is easy to get conflicting instructions. There are rules for resolving this but, in a large code base, these conflicts be hard to identify.
Typically CSS rules are enforced by increasing specificity:
footer p.address {
/* make the paragraph gold when it's in the footer */
color: gold;
}Code language: CSS (css)But that means more rules and more technical debt.
Worse yet, is the resort to the !important directive.
p.address {
/* really, really make the paragraph gold */
color: gold !important;
}Code language: CSS (css)What happens when two !important directives clash? I dread to think.
So …
CSS Cascade Layers, introduced in CSS Cascade Level 5, offer a way to manage and organise the cascade without relying on specificity, !important directives or source order alone. By defining explicit layers, developers can control which styles take precedence in a more structured manner. This feature enhances the predictability and maintainability of large codebases.
In CSS, the cascade layers are defined using the @layer rule. Styles within a layer follow the standard cascade rules but are prioritized based on their layer. If two styles conflict, the one in the higher (later defined) layer takes precedence. Layers can also be nested, allowing for refined control over the cascade.
/* Define layers */
@layer reset, base, components, utilities;
/* Assign styles to the reset layer */
@layer reset {
.body {
margin: 0;
padding: 0;
}
}
/* Assign styles to the base layer */
@layer base {
body {
font-family: sans-serif;
line-height: 1.6;
}
}
/* Assign styles to the components layer */
@layer components {
.button {
padding: 10px 20px;
border: none;
background-color: blue;
color: white;
}
}
/* Assign styles to the utilities layer */
@layer utilities {
.visually-hidden {
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}
}Code language: CSS (css)In this example, four layers are defined: reset, base, components, and utilities. Each layer contains specific styles that serve different purposes, from browser style resetting (reset) to foundational styles (base), UI components (components), and utility classes (utilities). By organising CSS in this manner, developers can easily manage which styles are applied first and how conflicts are resolved, making the stylesheet more scalable and easier to debug.
So CSS or SASS, which is better?
As is so often the answer, it’s not a great question: ”Which are better, eggs or omelettes?” SASS is not going to become obsolete overnight. It still has mixins and good few other elements which can help with large projects. But it is a lot less necessary. As container queries come online, it will become even less so. If a project does not need SASS, it is likely that you will get cleaner, leaner, better code with CSS alone.
Omelettes clearly.
