Minid.net

August 12, 2019

In Defense of Functional CSS

Functional CSS is still treated by some developers as a messy or undisciplined way to write interfaces. I think that criticism misses the point. Functional CSS can reduce CSS growth, make changes more explicit, and lower the cost of maintaining large front-end projects.

The new line cook opened the walk-in and just stood there. The chef who built the kitchen had quit a month earlier, and he had arranged everything by a private logic only he understood: sauces shelved by the week he invented them, proteins grouped by supplier, herbs in an order that might have been alphabetical in a language nobody had thought to ask about. “Where do we keep the butter?” the cook finally called out. Nobody answered, because nobody knew. The butter had a place. The place had a reason. The reason had left the building. By Friday they gave up and taped a label to every shelf saying exactly what was on it, and for the first time all week, service ran on time.

Functional CSS has always had a branding problem. To some developers, it looks like chaos: too many classes, too much noise in the markup, not enough poetry in the stylesheet. To others, it is a practical answer to a very old problem. CSS architecture sounds great in theory, but in real projects it often becomes a museum of forgotten decisions, abandoned naming conventions, and selectors that nobody wants to touch.

In front-end development, the debate is usually presented as a fight between two worlds. On one side sits traditional CSS architecture: BEM, OOCSS, SMACSS, semantic class names, component-specific selectors, and so on. On the other sits Functional CSS: small, single-purpose classes that apply one job clearly and directly.

Both approaches have trade-offs, and that is not the problem. The problem is that Functional CSS is often judged unfairly. Critics fixate on the HTML looking busy and then ignore the bigger issues that happen every day in real projects. CSS reuse is often exaggerated. Technical debt is usually underestimated. Performance gets discussed only when Lighthouse starts shouting. Naming is treated as if every developer shared the same brain. People leave companies. Projects are paused, resumed, outsourced, inherited, and rewritten. Documentation is usually missing, outdated, or written with the enthusiasm of someone filling out tax forms. And there is a strange fear of adding more classes to the markup, even when the alternative is shipping large, fragile CSS files.

My position is simple. Functional CSS is not perfect, but for many projects it is the better default. It is explicit. It is predictable. It keeps CSS files smaller. It reduces the amount of hidden architecture a developer must understand before making a change. And, yes, I know this position annoys some CSS purists. That is fine. They have survived worse things, including Internet Explorer 6.

Start with the reuse argument, because it is the one people lean on hardest. The claim is that Functional CSS does not support code reuse, and I think it is weaker than it looks. In more than 25 years working as a developer, product person, and manager, I have rarely seen CSS reused at the level people claim when defending traditional architecture. I have worked on many websites and native products, and the pattern is almost always the same: when a redesign happens, teams do not simply update the CSS and keep the HTML structure untouched. They change the markup. They change the components. They change the layout. They change the design system. They change half the product and then act surprised that the old CSS does not fit anymore. Sometimes they import pieces of old CSS, and sometimes they preserve a few utility classes, resets, tokens, or variables. But the romantic idea that developers can redesign a serious product by only touching CSS is mostly a myth.

CSS Zen Garden was a brilliant project. It showed the power of CSS at a time when many people still treated it as a minor styling layer, proving that a single HTML document could support many different visual designs through CSS alone. But it was also a controlled experiment. It was not a SaaS platform with dashboards, modals, data tables, settings pages, permissions, responsive states, user-generated content, A/B tests, and five teams pushing changes before lunch. Using it as proof that real products should avoid changing HTML during redesigns is like using a Formula 1 car to explain why your family car does not need a trunk. Beautiful example, wrong context. Modern web products mutate constantly. Their markup changes, their components change, their interaction models change, their business rules change, and when that happens, CSS architecture usually changes too. Functional CSS accepts this reality. It does not pretend that a perfect semantic layer will survive every redesign; it puts the styling decision close to the component, where the work is actually happening. That is not a lack of discipline. It is honesty.

None of this means Functional CSS eliminates technical debt, because no methodology does. Bad Functional CSS exists. Bad BEM exists. Bad everything exists. Give a team enough pressure, unclear requirements, and no review process, and they can turn any methodology into a crime scene. So the question is not whether Functional CSS prevents all technical debt, because it does not. The better question is which methodology makes technical debt easier to see, easier to limit, and easier to fix. Traditional CSS often hides complexity behind class names. A class like .card--featured looks clean, but the real behavior may live in several selectors, modifiers, media queries, inherited rules, and overrides. Before changing it, a developer needs to understand the context. Where is it used? What does it affect? Does another selector override it? Is it safe to remove? Is it part of an undocumented pattern created by someone who left the company three years ago?

Functional CSS reduces that hidden context. A class like display--flex or align-items--center tells you what it does. It does not ask you to understand the emotional journey of the person who named the component, and that matters. In real projects, technical debt grows because developers are under pressure. They need to ship, fix a bug, make a layout work. If the existing architecture is hard to understand, they add another selector, then another, then a modifier, then a special case, and then !important appears, and at that point the stylesheet has entered its villain era. Functional CSS does not make developers smarter, but it gives them fewer places to hide complexity. The class either applies a property or it does not. The rule is reusable by design. The CSS file does not need to grow every time a component needs a slightly different layout, and that is a serious advantage.

Performance is where the anti-Functional CSS arguments get weakest, and it is not a decorative topic. The usual criticism is that Functional CSS makes the HTML bigger. Yes, it can. But that statement alone is not useful, because the real question is: bigger compared to what? Compared to a smaller HTML file that requires a much larger CSS file, more selectors, more overrides, and more unused rules? That trade-off is not automatically better. CSS is render-blocking by default. Browsers need to download and process it before rendering styled content, so large stylesheets and unused rules are not free; they affect load, parsing, CSSOM construction, and rendering. A few extra class names in the markup are not usually the disaster critics imagine. In many cases, the bigger problem is shipping a large CSS file full of rules that are not needed for the current page.

Functional CSS helps here because the rule set is limited and reusable. Once the utility classes exist, you keep composing with them, and the stylesheet does not need to grow every time a new component is created. Traditional CSS can also be optimized, of course. A disciplined team can write small, clean, efficient CSS with semantic classes, but that requires strong architecture, good naming, constant review, and long-term consistency, which is exactly where many teams fail. Functional CSS moves the system toward a smaller, more stable rule set and makes reuse the default instead of something developers must remember to do. That is not just a developer-experience improvement, it can be a user-experience improvement too, because users do not care if your CSS architecture is elegant. They care if the page loads quickly, behaves correctly, and does not feel like it was assembled during a fire drill.

The other reflexive complaint is that the markup is too verbose, and fair enough, Functional CSS can make it look busier. But readable to whom? A traditional class looks clean:

<div class="pricing-card pricing-card--featured">

The simplicity, though, is only visual. To understand what it does, you have to go inspect the CSS. The Functional CSS version may look longer:

<div class="display--flex flex-direction--column padding--large border-radius--medium">

It is more verbose, but it is also more explicit, and you can understand much of the styling without jumping between files. That is a trade-off, not a flaw. The problem is that developers often confuse “short” with “maintainable”, and they are not the same thing. A short class name can hide a lot of complexity, while a longer list of utility classes can be easier to reason about because the behavior is visible at the point of use. Functional CSS is closer to inline styles in terms of explicitness, but with the benefits of classes: caching, consistency, media variants, pseudo-state support, design tokens, and reuse. It gives you the good part of inline styling without fully surrendering to chaos.

Naming deserves its own defense, because traditional CSS depends heavily on it. That sounds reasonable until you remember that naming is one of the hardest problems in programming, and it is even harder in CSS because class names often try to describe visual structure, product meaning, component purpose, and future reuse all at once. A class name may start its life as .hero-banner. Then the design changes, and it is no longer a hero and not really a banner. It gets reused in an onboarding page, a pricing page, and a marketing modal. Nobody renames it, because renaming might break something. Six months later, a new developer asks why the checkout screen uses .hero-banner, and nobody knows. This is normal. This is how CSS archaeology begins. Functional CSS avoids much of that, because it does not ask every developer to invent a new name for every visual decision. It provides a shared vocabulary based on properties and values, and that vocabulary is not glamorous, but it is stable. margin-top--large does not become semantically wrong because the product team changed the feature name. display--grid does not need a meeting. text-align--center does not depend on whether someone thinks the component is a card, panel, box, widget, module, or “that thing on the right”. Removing a large part of the naming debate can save real time.

There is also the uncomfortable fact that a lot of CSS architecture advice assumes a stable team, and that is not how companies work. People leave. Contractors rotate. Agencies hand off projects. Teams grow and shrink. A developer who deeply understood the original architecture may move to another company, another department, or a cabin in the mountains where nobody can ask them why .media-object--inverse exists. When a new developer joins, traditional CSS requires them to learn the local architecture before they can work safely: naming conventions, component boundaries, overrides, layout rules, responsive patterns, and undocumented exceptions. Functional CSS lowers that onboarding cost, because the system is not hidden in custom names, it is expressed through small, predictable classes. A developer still needs to understand the product and the component structure, but they do not need to reverse-engineer a private CSS philosophy before changing padding. That is valuable in real companies, especially the messy ones, which is most of them.

Someone will say all of this can be solved with a good style guide. Yes, in theory. In theory, documentation is updated, developers read it, reviewers enforce it, product changes respect it, and nobody ever creates a one-off class at 18:47 before a release. In practice, CSS documentation is often missing, outdated, incomplete, or ignored. A style guide is useful only if the team maintains it and enforces it. Otherwise it becomes a decorative PDF, like a speed-limit sign in the middle of nowhere: technically correct, widely ignored. Functional CSS reduces the amount of documentation needed because the system is more mechanical. The class names map directly to styling intent, the rules are reusable, and the composition is visible in the markup. You still need standards, review, and design decisions, but you need fewer explanations for what each class means, and that is a good thing.

Some people reframe all of this as a team communication problem, and they are partly right, because good communication helps. But communication does not scale well when every styling decision requires context, agreement, or investigation. Modern teams already have Slack, Git, pull requests, design tools, sprint planning, documentation, and code review. The issue is not that developers cannot talk to each other. The issue is that they should not need a conversation every time they want to align an element or reuse spacing. Functional CSS removes that friction: you do not need to ask which modifier to use, you do not need to check whether .section-card--compact is safe, and you do not need to wonder whether changing one selector will break four pages you forgot existed. You compose the component with explicit classes. That does not remove the need for design review or engineering standards; it simply removes a lot of unnecessary ceremony, and ceremony is expensive.

Then there is the version of yourself you will meet again later. Developers like to think they will remember their own code. They will not. After enough time, your old code becomes code written by someone who had your name, your keyboard, and worse judgment. I have returned to old projects and asked myself what kind of person made those decisions, and the answer, unfortunately, was me. This is where Functional CSS earns its keep. When the styling is explicit, you do not need to reconstruct the full mental model behind the architecture; you open the component and see what is happening. I once rewrote a large personal project from BEM to Functional CSS. The original uncompressed CSS file was 42.9 KB, and the final Functional CSS file was 12.6 KB. That result will not happen in every project, but the experience was clear: I spent less time interpreting old decisions and more time making the interface work. With BEM, I had to remember why I created certain blocks, elements, and modifiers. With Functional CSS, I mostly had to read the component. That difference matters.

A lazier criticism is that Functional CSS ignores components. It does not. It works especially well with component-based development. React, Vue, Svelte, Angular, and similar tools already encourage us to think in components, and the styling can live close to that component’s structure instead of being spread across a large stylesheet with naming rules that may or may not still make sense. The component becomes the unit of meaning, and the utility classes become the styling vocabulary. It is a clean separation: the component defines structure and behavior, the utility classes define visual properties, and the CSS file provides a stable set of reusable rules. That is not a mess. That is a system.

To be clear, traditional CSS methodologies are not useless. BEM helped many teams write more structured CSS. OOCSS pushed developers toward reuse. SMACSS gave people a way to think about categories and organization. These methodologies were important, and they solved real problems. But they also depend heavily on discipline. They work best when the team understands the methodology, naming conventions are clear, code review is strict, components are stable, documentation exists, dead CSS is removed, and developers do not bypass the system under pressure. That is a long list of conditions. Functional CSS also requires discipline, but the discipline is simpler: use the existing utility system, avoid unnecessary custom CSS, and compose explicitly. That is easier to enforce.

I should also be honest about the limits, because Functional CSS is not perfect for every case. Sometimes a component has complex visual behavior that deserves custom CSS. Sometimes a highly interactive module needs carefully scoped rules. Sometimes a design system needs semantic abstractions on top of utilities. Sometimes utility classes alone make the markup too hard to scan. That is fine. Functional CSS does not mean “never write custom CSS”. It means custom CSS should be the exception, not the default reaction to every design requirement. A healthy system can have functional utility classes for common styling, components for reusable structure, design tokens for consistency, and custom CSS for complex, repeated, or highly interactive patterns. The point is not religious purity. The point is reducing unnecessary CSS growth and hidden complexity.

Functional CSS works because it accepts how front-end development actually happens. Projects change. Teams change. Designs change. Requirements change. People forget things. Documentation decays. CSS grows. Dead rules survive because nobody is sure whether removing them will break production. Functional CSS does not solve all of that, but it reduces the surface area of the problem. It gives teams a smaller and more stable rule set, more explicit styling decisions, less dependency on naming conventions, faster component composition, lower onboarding cost, less fear when changing old code, and better control over CSS growth. That is why I defend it. Not because it looks prettier, because it often does not. Not because it feels more “semantic”, because it usually does not. Not because it makes CSS architecture discussions more elegant, because it absolutely does not, and it may even ruin a few conference talks. I defend Functional CSS because it is practical, and in real products, practical usually wins.

The web is not a museum for perfect CSS architecture. It is a living environment where products change constantly, teams move fast, and users do not care how poetic your class names are. Functional CSS is not a hack. It is a serious methodology built on explicit composition, reuse, and constraint. It makes some things uglier at the markup level, yes, but it can make the system simpler, faster, and easier to maintain, and that is a trade-off worth taking.