When it comes to structuring CSS, there is no shortage of different naming conventions, methodologies, and architectures. Be it BEM, OOCSS, SMACSS, ITCSS, or CUBE CSS – over the last years, many different approaches to managing modular CSS have emerged. Some are offering strategies on how to split CSS into smaller, more manageable pieces, while others focus more on naming conventions for classes. Sometimes, it can be hard to grasp where the differences or advantages of certain methodologies lie, yet in the end, they all aim at the same: Providing structure and consistency, also known as “avoiding a mess”, when you are working in a team or with your present and future self.
No wonder there isn’t a new project where I don’t start to think about the structure of my CSS a bit and, over time, the way I organize and write CSS has changed a lot. The biggest change came when we all started to write CSS for components. But also preprocessors like Sass have clearly left their mark.
In this post, I will share my current take on CSS structure. It does not religiously follow any particular methodology, although people familiar with Harry Roberts’ ITCSS (“Inverted Triangle CSS”) will definitely recognize parts of his methodology. If you haven’t yet looked at ITCSS, I highly recommend it. What I like most, is the pragmatic, real-life approach and the principle of structuring your CSS in a way that gets ever more specific and explicit the farther down you go in the structure. This allows you to focus on the high-level styles first and makes it easier to deal with the cascade, inheritance, and selector specificity while keeping the number of classes – and the specificity! – as low as possible. There are, however, a few differences, and this is also what I’d suggest to anyone setting up their own structure: Take any methodology with a grain of salt and freely adjust it to your needs and the way you work.
The Folder Structure
This is how my folder structure currently looks like:
/scss/
├── 1-settings
├── 2-design-tokens
├── 3-tools
├── 4-generic
├── 5-elements
├── 6-skeleton
├── 7-components
├── 8-utilities
├── _shame.scss
└── main.scss
Let’s break it down a bit.
Settings
The first folder, 1-settings
, is for all general project settings, so for the most basic high-level configuration. This might be a collection of global variables – either as Sass variables or custom properties.
├── 1-settings
│ └── _global.scss
Design Tokens
The second folder is for all styles regarding the visual vocabulary of the site. At this level, we are still not generating any CSS output. It is where we define variables for the typography, colors, spacing, media-queries, or any other attributes which you will use throughout the design. For these visual design attributes, the term design tokens has taken hold. Those design tokens could even be coming from your design system as a single source of truth.
├── 2-design-tokens
│ ├── _colors.scss
│ ├── _fonts.scss
│ ├── _media-queries.scss
│ ├── _spacing.scss
│ └── _typography.scss
Tools
The tools folder is where your global Sass mixins and functions live. Maybe you want to manipulate colors with blend modes or set the aspect ratio for a video container? Or clear your float, for example. I am not a heavy user of mixins myself, but I know many people who love them, so this is where to put them.
├── 3-tools
│ ├── _aspect-ratio.scss
│ ├── _blend-modes.scss
│ ├── _center.scss
│ ├── _clearfix.scss
│ └── _gradients.scss
Generic
Just like in ITCSS, the generic folder is the first one that actually produces CSS. It contains global box-sizing rules, CSS resets, or print styles – anything that should be set right at the beginning of your CSS but isn’t yet project-specific.
├── 4-generic
│ ├── _box-sizing.scss
│ ├── _normalize.scss
│ └── _print.scss
Elements
Now that the most basic things are set up, we can start to style the building blocks of our front-end: Raw HTML Elements. Mostly without classes, we are now redefining the basic browser styles of headlines, buttons, links, lists, etc. and can make sure that all components in our design are using the same consistent base.
├── 5-elements
│ ├── _forms.scss
│ ├── _headings.scss
│ ├── _images.scss
│ ├── _links.scss
│ └── _lists.scss
│ ├── ...
Skeleton
Any modern web project that is built with components also comes with the need for a higher-level structure in which all the components can live: Wrappers, containers, grids, and all kinds of reusable objects that provide layout patterns. This is the skeleton of your site.
├── 6-skeleton
│ ├── _grid.scss
│ ├── _layouts.scss
│ └── _objects.scss
Components
The beating heart of the project. This is where we design the components of the UI. In a few recent projects, I sometimes distinguished between larger modules and smaller components, but you can also nest components into each other and do without the additional distinction. Use prefixes, if you like, and also a naming convention like BEM can make a lot of sense. I have recently settled on a BEM-like but more simplified naming convention: Just use the simplest but most descriptive class name possible and separate elements within other elements with a simple dash, like .card
and .card-content
. Sometimes – for example, when I work with Fractal – the CSS for individual components might also live in another folder, together with the HTML and JavaScript code. In this case, the components folder might be empty, or contain references via @import
.
├── 7-components
│ ├── _accordion.scss
│ ├── _card.scss
│ ├── _hero.scss
│ ├── _pan-galactic-gargle-blaster.scss
│ └── ...
Utilities
Another folder? Yes, but this is definitely the last one. The utilities folder contains utility and helper classes and, most importantly, states and modifiers like .is-active
or .visually-hidden
. Those styles override the styles in the previous layers and are often set via JavaScript. I really like the suggestion by Andy Bell in his CUBE CSS methodology to use data-attributes to change the state of components, which is also useful in terms of the higher specificity.
├── 8-utilities
│ ├── _modifiers.scss
│ └── _states.scss
_shame.scss
This file, which is another idea by Harry Roberts, is a place for all the shameful CSS solutions like quick fixes and hacky things that might solve a problem for the time being but should be solved properly later. Make sure to document all those nasty hacks with comments, though: Why did you solve it this way? Do you already have an idea on how to solve it better? What is needed to solve it? And so on…
Putting it all together
Finally, the main.scss
file is where all the individual files are combined. I prefer to explicitly import each file in a new line instead of importing whole folders because I have more control over the source order. But this is only my personal preference, of course.
@charset "UTF-8";
// 1. Settings
@import
"1-settings/global";
// 2. Design Tokens
@import
"2-design-tokens/colors",
"2-design-tokens/fonts",
"2-design-tokens/media-queries",
"2-design-tokens/spacing",
"2-design-tokens/typography";
...
And that’s it. A structure like this has served me well in recent projects because it keeps everything tidy. The resulting CSS is also much cleaner and it is easier to find the right piece of code when you have to make changes or do bugfixes.
I asked on Twitter the other day which CSS methodology you all prefer and the results were, as was to be expected, mixed:
Web developers and designers of the interwebs! What is your favorite…
— Matthias Ott (@m_ott) August 10, 2020
✨CSS methodology✨?
—and why?
People all like to use their own flavor of CSS, which is great. If you use a methodology or folder structure that you would like to share, write a post about it and I’ll happily link to it here. It would be interesting to see how you structure your CSS.
For future reference, here’s the whole folder structure again:
/scss/
├── 1-settings
│ └── _global.scss
├── 2-design-tokens
│ ├── _colors.scss
│ ├── _fonts.scss
│ ├── _media-queries.scss
│ ├── _spacing.scss
│ └── _typography.scss
├── 3-tools
│ ├── _aspect-ratio.scss
│ ├── _blend-modes.scss
│ ├── _center.scss
│ ├── _clearfix.scss
│ └── _gradients.scss
├── 4-generic
│ ├── _box-sizing.scss
│ ├── _font-face.scss
│ ├── _normalize.scss
│ └── _print.scss
├── 5-elements
│ ├── _forms.scss
│ ├── _headings.scss
│ ├── _images.scss
│ ├── _links.scss
│ ├── _lists.scss
│ └── ...
├── 6-skeleton
│ ├── _grid.scss
│ ├── _layouts.scss
│ └── _objects.scss
├── 7-components
│ ├── _accordion.scss
│ ├── _card.scss
│ ├── _hero.scss
│ ├── _pan-galactic-gargle-blaster.scss
│ └── ...
├── 8-utilities
│ ├── _modifiers.scss
│ └── _states.scss
├── _shame.scss
└── main.scss
-
This is the 51st post of my 100 days of writing series. You can find a list of all posts here.
~