April 7, 2019
The Utilitarian CSS Methodology
We have been writing CSS in the wrong way for a long time, and it is time to move forward. The Utilitarian CSS Methodology, or UCSS, reduces the size of CSS files, improves loading, rendering, and painting speed, and makes projects easier to maintain. Traditional CSS architectures tend to grow into large, fragile systems, and a utility-first approach can solve many of these problems.
When I started writing CSS in 1996, it felt like a huge step forward. For the first time, the presentation of an HTML document could be defined in a separate file. A single change could affect an entire website. For developers, this was a major improvement. Before that, changing a design often meant editing hundreds or thousands of HTML files by hand. CSS promised to remove that pain.
But there was a problem. At that time, Internet Explorer 3 was the first commercial browser with CSS support, but that support was still incomplete. Many users still used Netscape, and people did not switch browsers as quickly as they do today. So developers still had to rely heavily on HTML for layout and presentation until browser support improved.
There was another important issue: we did not have the tools we have today. We did not have modern content management systems, components, includes, partials, or templating systems. Developers used frames to reuse parts of a page. It was ugly and limited, but it was what we had.
Today, many of those problems are almost gone. Browser support is much better. Connection speeds are much faster. Compatibility issues are smaller than they used to be. We have powerful tools, frameworks, build systems, and component-based architectures.
But we also created new problems, and to understand how CSS became so difficult to manage, it helps to look at the history of web development, which I usually describe in four stages.
The first stage, the Dawn of HTML, ran roughly from 1995 to 2001. During this period, layouts were built with tables. Not simple tables. Tables inside tables, inside more tables. Developers abused the table element because there was no better way to create complex layouts. We also had very slow connections, heavy images, transparent GIF, strange design trends, and homepages that could take 15 to 20 seconds to load. If you were overseas, it could take even longer. At the same time, Macromedia Flash started to grow. Flash appeared in 1996, and Flash 3 arrived in 1998. Still, CSS was not widely used. Only a small group of developers interested in web standards were using it seriously. Most websites were still built with tables.
The second stage was the Empire of Flash. From around 2001 to late 2007, CSS and HTML struggled to become the main way to build rich websites. Flash dominated the market because it made animations, interactions, and complex visual experiences much easier. Flash was proprietary, but it solved real problems at the time. Many developers and managers saw it as the future of the web. That changed with the release of the iPhone in 2007, and especially with Steve Jobs’ decision not to support Flash on Apple mobile devices. From that moment, web standards started moving in a new direction. There was no way back.
The third stage, the Rise of HTML5, arrived around 2008, when everything changed again. Smartphones, tablets, and the decline of Flash forced teams to rethink how they built websites. HTML, CSS, and JavaScript became the main stack for rich user interfaces. Developers started using CSS more heavily, but often without control. CSS files grew quickly. Projects needed to support different browsers, devices, and screen sizes. Developers created more and more classes to cover every possible case.
This chaos pushed larger companies and communities to define CSS methodologies, and we started to see approaches like OOCSS, ACSS, BEM, and SMACSS. These methodologies helped developers organize their work and made CSS more structured and easier to reason about, but they did not really solve the size problem. In many cases, they made CSS files bigger:
Around the end of 2012, we entered the fourth stage, the Dominion of Methodologies and Frameworks. In this period, almost every developer used either a CSS framework or a CSS methodology. This created two common problems.
The first problem comes from frameworks. Many teams import large CSS frameworks even when they only need a small part of them. Then they add more CSS on top to override the default styles. As a result, the final CSS file contains the framework, the unused framework code, and the custom overrides.
The second problem comes from methodologies. Developers often split components into too many blocks, elements, modifiers, and variants. Over time, the number of components and modifiers grows. The system becomes harder to understand. The project accumulates redundant code. This is not always a problem of developer skill. The methodology itself encourages developers to create more components, more modifiers, and more exceptions. This era gave developers tools to work more comfortably. But comfort is not the same as performance.
All of this raises the real question: why does CSS file size matter so much? To understand the impact, I looked for projects that had existed since the early days of the web. Most websites from the 1990s disappeared after a few years. Newspapers were the perfect group to analyze because many of them have been online for decades.
The trend was clear: CSS files kept growing year after year. There were some highs and lows, but the general direction was the same. The size increased significantly, and the pattern repeated across different newspapers.
This happened for several reasons. First, developers moved more and more UI logic into CSS. They expected CSS components to cover all the needs of the website. But as the business grew, the CSS file grew too.
Second, developers stopped thinking about browser wars and started adopting methodologies and frameworks such as BEM, OOCSS, YUI, Bootstrap, and Foundation. These tools added more base code to each project. Many methodologies are verbose, preprocessors make repetition easy, and frameworks introduce constraints that often lead to technical debt.
Third, there was an explosion of UI libraries. Libraries like YUI and jQuery UI allowed developers to quickly add calendars, widgets, and other components. But they also increased the size of CSS files.
By the end of 2018, the same pattern was still visible. The main issue was that teams had moved too much responsibility into CSS architecture. Instead of reducing CSS, many teams kept expanding it. Large CSS files became a kind of historical record of every past decision, every redesign, and every workaround. They became hard to change because changing them required too much refactoring.
For example, in the CSS file of MARCA, one of the most-read newspapers in Spain, the same properties appeared thousands of times:
| display | color | height | font-size | display | padding | margin |
|---|---|---|---|---|---|---|
| 2029 | 1464 | 1416 | 1308 | 1249 | 942 | 902 |
This is a clear warning sign. The problem is not only network transfer. With modern connections, downloading a large file may not look like a serious problem. The real problem is the amount of CSS that has to be created, parsed, maintained, and reused. When I say “created,” I do not mean developers manually write color: #ff0000 thousands of times. In many cases, the repetition comes from preprocessors, mixins, extensions, and framework abstractions.
Most projects end up facing the same dilemma: reuse existing classes and risk unexpected conflicts, or create new classes to avoid conflicts and increase technical debt. In practice, many developers choose the second option. They create more classes, more architecture, and more rules because they believe this will make the system safer. But over time, this makes the system heavier and harder to change.
If you want to redesign a large site like MARCA, you may need to refactor thousands of lines of CSS and then update every related component in the CMS. That type of project becomes enormous. In many cases, it is never fully completed. The backpack is too heavy.
This is the problem I wanted to solve with the Utilitarian CSS Methodology, and UCSS is designed to be useful, not decorative. It is a functional approach to writing, scaling, and maintaining CSS, and it is not a completely new idea. Some people call similar ideas functional CSS or immutable CSS.
The core idea is simple: instead of creating large component classes, we create small utility classes that represent one CSS property and one value.
For example:
.font-weight--bold {
font-weight: bold;
}
.display--block {
display: block;
}
Each utility class has a clear purpose. It does one thing. In theory, every CSS property could be represented in the final CSS file. In practice, we only generate the properties and values that the project actually needs. This gives us strict control over the size of the CSS file.
Let’s compare two approaches. Here is a traditional, component-based version:
<div class="hero hero--homepage">
<h1 class="hero__title hero__title--accent">Our wonderful startup idea</h1>
<img src="/images/image.png" alt="" class="hero__image hero__image--centered" />
</div>
And here is the same markup written with UCSS:
<div class="background-color--1 padding--32 text-align--center">
<h1 class="font-size--64 color--2 margin-bottom--32">Our wonderful startup idea</h1>
<img src="/images/image.png" class="display--block margin--auto" alt="" />
</div>
In the first example, the classes are descriptive, but they describe the component, not the actual CSS behavior. We can guess what .hero or .hero--homepage does, but we do not know for sure. We also do not know whether those classes are overwritten somewhere else. The information is hidden. In the second example, the behavior is visible. The class names tell us exactly what is happening. The CSS is explicit.
This matters because component-based CSS often creates problems as a project grows. A class starts with a clear purpose, but later the project needs a similar variant. Then another variant. Then a special case. Then an exception.
For example, a project may start with a .nav class and a few modifiers such as .nav--main, .nav--aside, or .nav--categories. Later, the team needs navigation patterns that no longer fit those modifiers. Developers then create new classes with different names, such as .buttons, .options, or other synonyms. They do this because overwriting 100% of the original navigation rules does not make sense. They also avoid reusing existing classes because a future change could break other parts of the site. So they create new classes from scratch. Again and again.
This is how CSS grows into technical debt. With UCSS, the behavior is separated into small reusable instructions. Changes happen in the component or template, not inside a growing CSS architecture. Developers can adjust the HTML or JavaScript module without editing the CSS file.
There are no hidden dependencies, no inheritance problems, and fewer overwritten rules. That is utilitarianism applied to CSS.
A common question follows naturally: if UCSS is so explicit, why not just use inline CSS? Inline CSS can be useful in some cases, but it has important limitations. It is harder to build responsive designs, and it does not support selectors and pseudo-selectors properly. It can force the use of !important when combined with external CSS, and it becomes difficult to read and maintain after a few properties. It cannot be preprocessed or post-processed in the same way, and it can be harder to modify safely with JavaScript or other languages. Vendor prefixes are more difficult to manage, and mixing inline CSS with external CSS files can complicate the architecture. UCSS gives you the best of both worlds: the explicit nature of inline styles and the flexibility of CSS classes.
For example:
<div class="sm-display--none md-display--grid c-warning">
<h3>This is a warning</h3>
<p class="color--2">Your profile needs to be updated.</p>
</div>
In this example, sm-display--none applies to small screens, and md-display--grid overrides it on larger screens.
The class c-warning is different. The c- prefix identifies it as a custom class. Custom classes are used when pure UCSS is not enough, usually for behaviors such as hover states or interactions.
For example:
.c-warning:hover {
background: map-get($colors, 1);
}
This makes the custom behavior easy to find and easy to maintain. It lives in one custom file and has a clear purpose.
This approach brings several benefits together. Performance improves because values are defined directly in CSS, and there is less risk of breaking other parts of the project. In most cases you no longer need !important, and class name conflicts stop appearing over time. Only the necessary properties are used, there is less repeated code, and the CSS architecture does not keep expanding. Development of JavaScript or HTML components is faster, maintenance is lighter, and CSS files stay smaller and load faster. Legacy code is also better supported, because the same utility classes can be reused across many modules.
Utility classes are not new. But UCSS is not just a list of helper classes. It is a strict way of translating CSS properties into a controlled class system. The goal is to reduce the amount of CSS required for styling and let developers focus on the real components: JavaScript modules, templates, backend-generated views, or any other system that produces HTML. UCSS can be adopted partially. It can live inside an existing codebase. It can also work together with components such as forms, buttons, and custom interactive elements. There are other projects with a similar philosophy, such as Basscss, Tachyons, and Tailwind CSS. They also use utility classes to simplify development and help teams build interfaces faster.
Performance is where all of this pays off. Every network request that sits on the critical rendering path can delay the first render of a page. In a normal navigation, the main resource is the HTML document. The browser parses that HTML and discovers the critical subresources it needs, including stylesheets, scripts, fonts, and images. Because external stylesheets are render-blocking by default, small amounts of critical CSS placed directly in the head can make the first render faster than waiting for a separate CSS file.
However, inline CSS does not always scale. It increases the size of the HTML document and cannot be cached independently from the page. In many projects, external CSS files are still the right choice. When those files are used, caching becomes important. Without fast delivery and good caching, the browser may delay painting while it downloads and parses render-blocking CSS and builds the CSSOM.
This is why CSS delivery matters. The goal is not always to inline everything. The goal is to keep critical CSS small, deliver it early, and avoid making the browser download and parse CSS that is not needed for the initial view.
UCSS can make CSS delivery faster because the generated files are usually small. In many cases, the critical part of a UCSS file can be small enough to place directly in the head of the document. When served as an external file, a well-configured UCSS file is still usually much smaller than a conventional CSS architecture, and in the projects I tested, most UCSS files were less than 15 KB uncompressed.
Another important topic is browser resource processing. The browser must understand the HTML, CSS, JavaScript, images, fonts, and other resources sent by the server. This work is often measured in milliseconds, but it can still become expensive.
Large CSS files with many selectors and repeated properties can slow this process down. This is not because BEM or any other methodology is automatically slow. The problem appears when a project produces thousands of rules, deep selector chains, repeated declarations, and many unused styles. As mentioned earlier, some real-world CSS files can exceed 9,000 lines or more, and all of that extra CSS must be loaded, parsed, and applied.
In my tests, the UCSS version was significantly faster to process than the traditional compiled CSS file I compared it against. For example, after loading the compiled CSS resources of MARCA 100 times, the difference was clear:
| CSS file | Processing speed |
|---|---|
| Compiled Marca.com CSS | 84.3 ms |
| Compiled UCSS | 8.4 ms |
Rendering time matters just as much. After the browser builds the DOM and CSSOM, it can construct the render tree, run layout, and paint the page. Many factors affect this process: JavaScript, web fonts, CSS effects, box shadows, filters, background images, complex selectors, pseudo-selectors, and the total number of resources.
UCSS can perform well because it is explicit and keeps the active rule set small. There are usually fewer rules in play, fewer overrides, less accidental inheritance, and fewer conflicts to resolve. In tests with projects of different complexity, I saw rendering improvements from an average of 175 ms to around 17 ms. These numbers should be read as results from my test cases, not as a universal guarantee. That is a huge difference.
In some lab tests, pages styled with UCSS were almost as fast as unstyled pages. In some cases, the difference was surprisingly small because browsers also apply their own default stylesheets. The result is simple: less critical CSS usually means less work for the browser before the first render.
UCSS also ships with a framework. It is written in SCSS, so it can be adopted in many types of projects, including ones that already use PostCSS. It offers a configurable normalizer, a configurable set of properties, configurable variables for colors, scales, and values, and support for most CSS 1, CSS 2, and CSS 3 properties.
One thing that has always bothered me is the default use of normalizers. Many teams install a normalizer and never remove the parts they do not need. As a result, every project starts with several kilobytes of CSS that may never be used.
The UCSS framework includes a configurable normalizer. It follows normalizer rules, but each section can be enabled only when needed.
To use it, you open config_normalizer.scss and activate the required sections by setting their variables to true. No unnecessary kilobytes, no hidden cost.
The most important part of the framework is the property configuration file. To produce the closest thing to perfect CSS, you must generate only the properties your project needs.
By default, all properties are disabled. For example, if you want to use color classes, you enable the color property in config_css.scss. Then the preprocessor generates the related classes using the values defined in your variables file. If you later stop using that property, you can disable it again. The idea is simple: generate only the necessary classes. No more, no less.
For example, this website was built entirely with the UCSS framework. The final compressed CSS size was 1,142 bytes. That is small enough to place directly in the head of each document. This removes the extra stylesheet request for that page, although the CSS still has to be parsed by the browser. In that test, the CSS processing time dropped from 55 ms to 5 ms.
Once you know which CSS property you need, such as display or word-break, you configure the values for that property.
For example, in _vars.scss, the display values may look like this:
$displays: (
// none,
block,
// inline,
// inline-block,
// flex,
grid
);
In this example, block and grid are enabled. Other values are commented out.
The framework will generate:
.display--block {
display: block;
}
.display--grid {
display: grid;
}
If you need flex, you remove the comment from flex, and the next build generates:
.display--flex {
display: flex;
}
This is the main idea of the framework. You activate only the values you need. You can also define your own values for colors, spacing scales, margins, padding, and other properties. The framework does not output nice forms, headers, cards, buttons, or hero sections by default. You build those yourself using the utility classes, and that is the point. You do not need to fight a preset design, and you do not need to override dozens of rules. If the system is configured with discipline, you get more control, smaller CSS, and a codebase that is easier to maintain.
Creating a methodology like this requires data, a lot of data. During the last six months, I have used UCSS in client projects and in controlled examples, comparing real CSS files with UCSS equivalents to measure size, loading time, processing time, and rendering time. The results were strong enough to keep developing the methodology.
I would like more developers to try UCSS in real projects, because the more examples we have, the better we can understand its limits and improve the framework. So far I have not found many serious problems with the approach, but every methodology needs real usage, real feedback, and real pressure from production projects, so if you are using UCSS in a project, let me know.
That, in the end, is the whole point: write less CSS, keep it explicit, cut technical debt, and let developers focus on building products instead of feeding a monster that only grows heavier with every release.