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.

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:

  1. Traditional CSS architecture: BEM, OOCSS, SMACSS, semantic class names, component-specific selectors, and so on.
  2. Functional CSS: small, single-purpose classes that apply one job clearly and directly.

Both approaches have trade-offs. That is not the problem. The problem is that Functional CSS is often judged unfairly. Critics focus on the HTML looking busy, then ignore the bigger issues that happen every day in real projects:

  • CSS reuse is often exaggerated.
  • Technical debt is usually underestimated.
  • Performance is discussed only when Lighthouse starts shouting.
  • Naming things is treated as if every developer has 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 tax forms.
  • There is a strange fear of adding more classes to HTML, 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. CSS purists have survived worse things, including Internet Explorer 6.

Code reuse is often exaggerated

One common argument against Functional CSS is that it does not support code reuse. I think this argument 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 CSS 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 teams import pieces of old CSS. 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. It proved that a single HTML document could support many different visual designs through CSS alone. But CSS Zen Garden 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 CSS Zen Garden 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 HTML 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.

Functional CSS can reduce technical debt

No CSS methodology eliminates technical debt. 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. The question is not whether Functional CSS prevents all technical debt. 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. This matters.

In real projects, technical debt grows because developers are under pressure. They need to ship. They need to fix a bug. They need to make a layout work. If the existing CSS architecture is hard to understand, they add another selector. Then another. Then a modifier. Then a special case. 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.

That is a serious advantage.

Performance is not a decorative topic

Performance is one of the weakest parts of many anti-Functional CSS arguments.

The usual criticism is: “Functional CSS makes the HTML bigger.”

Yes, it can.

But that statement alone is not useful. 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 CSS before rendering styled content. Large stylesheets and unused rules are not free. They affect load, parsing, CSSOM construction, and rendering.

A few extra class names in HTML are not usually the performance 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 can help because the CSS rule set is limited and reusable. Once the utility classes exist, you keep composing with them. 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.

That is exactly where many teams fail.

Functional CSS moves the system toward a smaller, more stable rule set. It makes reuse the default instead of something developers must remember to do.

That is not just a developer-experience improvement. It can also be a user-experience improvement.

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 HTML-size panic is overblown

Some developers see Functional CSS and immediately complain that the HTML is too verbose.

Fair enough. Functional CSS can make markup look busier.

But readable to whom?

A traditional class like this looks clean:

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

But the simplicity is only visual. To understand what it does, you must inspect the CSS.

A 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. 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”. They are not the same thing. A short class name can hide a lot of complexity. 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 is harder than people admit

Traditional CSS depends heavily on naming. That sounds reasonable until you remember that naming is one of the hardest problems in programming. 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. It is no longer a hero. It is not really a banner. It is reused in an onboarding page, a pricing page, and a marketing modal. Nobody renames it because renaming it may break something. Six months later, a new developer asks why the checkout screen uses .hero-banner. Nobody knows.

This is normal. This is how CSS archaeology begins. Functional CSS avoids much of that. 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. 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”.

Functional CSS removes a large part of the naming debate. That alone can save real time.

Employees leave. CSS remains.

A lot of CSS architecture advice assumes a stable team.

That is not how companies work.

People leave. Contractors rotate. Agencies hand off projects. Teams grow. Teams shrink. A developer who deeply understood the original CSS 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 a project, traditional CSS requires them to learn the local architecture before they can work safely. They need to understand naming conventions, component boundaries, overrides, layout rules, responsive patterns, and undocumented exceptions.

Functional CSS lowers that onboarding cost.

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.

Style guides help, but they are not magic

Someone will say: “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. The composition is visible in the markup. You still need standards. You still need review. You still need design decisions. But you need fewer explanations for what each class means. That is a good thing.

This is not just a communication problem

Some people frame CSS problems as team communication problems. They are partly right. 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 friction. You do not need to ask which modifier should be used. You do not need to check whether .section-card--compact is safe. 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.

Returning to old projects is painful

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. Unfortunately, the answer was: me. This is where Functional CSS is very useful. When the styling is explicit, you do not need to reconstruct the full mental model behind the CSS architecture. You can 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. 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.

Functional CSS is not anti-component

One lazy criticism of Functional CSS is that it ignores components.

It does not.

Functional CSS works especially well with component-based development. React, Vue, Svelte, Angular, and similar tools already encourage us to think in components. 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.

The utility classes become the styling vocabulary.

This is a clean separation:

  • The component defines structure and behavior.
  • The utility classes define visual properties.
  • The CSS file provides a stable set of reusable rules.

That is not a mess. That is a system.

Traditional CSS can be good, but it is fragile

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.
  • 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.

Functional CSS is not always the answer

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.
  • 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.

Why Functional CSS works

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 CSS rule set.
  • More explicit styling decisions.
  • Less dependency on naming conventions.
  • Faster component composition.
  • Lower onboarding cost.
  • Less fear when changing old code.
  • Better control over CSS growth.

That is why I defend it. Not because it looks prettier. It often does not. Not because it feels more “semantic”. It usually does not. Not because it makes CSS architecture discussions more elegant. It absolutely does not. It may even ruin a few conference talks. I defend Functional CSS because it is practical. And in real products, practical usually wins.

Final thought

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 based 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. That is a trade-off worth taking.