Diving into Component and Atomic Styling Methodologies

An Article By Ben Stankich // 3.13.2020

Styling applications at scale is a classic web development problem. Scope, consistency, and optimization are all issues that seem to permeate any honest examination or implementation of CSS. A history of approaches to these challenges has evolved into two distinct methodologies that teams gravitate towards: component and atomic.

Component-based CSS breaks down an application into isolated units, while atomic CSS (also called utility-first or functional) strings together single-purpose classes to style markup. This reveals an underlying distinction: component methodology places CSS complexity in stylesheets, whereas atomic brings it alongside markup.

Neither methodology is perfect. In this article, you will learn about two approaches and see where they shine, fall short, and which situations they’re best suited for.

Component styles: a tried and true strategy

The philosophy of component styling centers around scope. It contends that the global, cascading nature of CSS is dangerous, so structure must be added to limit scope as much as possible. This is accomplished by building a system of reusable CSS components.

Just as a web application is broken into units of code, CSS can be separated into units of style. Each component has its own properties and doesn’t share classes with other components. This self-contained design means that we can reliably style and re-style a component without affecting other parts of a website.

This view is materialized through rules around how to structure and name classes, such as the BEM architecture. Teams can also make use of various frameworks and libraries (such as BulmaBootstrap, and Foundation) to formalize requirements in how CSS is written and applied.

Components can also be nested inside each other and have subcomponents of their own. Below is an example of a typical card design implemented using BEM-like component styles and its associated markup.

See the Pen Component styles example: BEMish by Ben Stankich (@benstankich) on CodePen.

The anatomy of component CSS follows how modern applications arrange markup and functionality. We already think of websites as collections of buttons, headings, inputs, etc. Organizing CSS classes as components is a natural extension of this concept. And with a growing focus on building design systems, the path to writing styles as components is more clear-cut.

Drawbacks to component styles

As with most highly organized structures, it takes time to create a component system. The biggest factor in this is defining scope. When creating a new component, one has to ask questions to determine its boundaries.

One example is the heading inside the card sample above. Is the heading unique to the card and thus should it be a subcomponent of the card? Or is the heading style something that will be used in other contexts, and therefore should exist as its own component?

Further complicating the matter is that a component’s scope might change over time. Changes to both style and markup can dictate CSS alterations and force another look at the component’s usage.

Filling in the gaps

While the component approach has much to say about structure and scope, it remains mute on how to address a few smaller concerns. The first gap to fill in is management of frequently repeated styles across isolated components.

Typically, a single repeated style is defined as a variable and a set of repeated styles becomes either a mixin or a component of its own. In some cases, copy-pasting shared styles across a few components is manageable and won’t notably affect the file size once compressed.

A second gap of component systems is the lack of a foolproof method for making adjustments to a specific component instance (such as spacing tweaks). The answer is usually to add a modifier class unique to that component as needed, but this can become tedious over time. Sometimes these modifications might even require a new component, such as a spacing component, to handle these subtle differences.

Atomic styles: relentless reusability

The goal of atomic CSS (not to be confused with atomic design) is to abstract styles down to their most useful unit, usually a single CSS property. Elements are given a class for each property they require, which means most end up with at least a few classes.

These classes are typically generated by a framework. A configuration file assigns the values each property can have, so classes can be generated for each respective style. In effect, this whitelists which properties can be applied. These constraints can’t be bypassed without changing the configuration file or creating a class outside the atomic structure.

While component systems often utilize a configuration file, with atomic systems these style properties are necessarily defined ahead of time. This step is built in to the implementation and can’t be skipped.

Below is the same card example previously shown, but implemented using a popular atomic framework called Tailwind (with classes already generated). Notice the difference in how styles are applied to the same structure and content.

See the Pen Component styles example: BEMish by Ben Stankich (@benstankich) on CodePen.

Abstracting design properties

Adam Wathan, creator of Tailwind, describes utility-first CSS as preventing “premature abstraction”. Instead of defining components from the outset based on intuition, classes are added to markup as needed. Then code is abstracted as patterns emerge.

Repeated markup is moved to a template/component/partial, and repeated class pairings are consolidated to a single class (effectively a CSS component). Not all atomic frameworks advise using atomic and component classes together, but most do for the sake of usability.

Editing styles

A big benefit of using atomic is that altering an element with existing styles is low-risk, since it is simply adding and removing classes. These local changes affect only that element, whereas editing component styles affects every instance of that component.

Additionally, specificity issues are low since atomic classes set properties unique to that group. For example, an atomic framework is ideally set to only use .color-{name} classes to manipulate the color property. No other classes can.

Tradeoffs with the atomic approach

Managing style properties

To avoid generating unnecessary classes, lists of values and selectors to be used must be maintained for every CSS property. In practice, the config file often becomes a mix of default and one-off properties and the two become difficult to distinguish. Beyond the config file, additional tooling (such as PurgeCSS) is highly recommended to remove unused classes, since many generated classes aren’t actually used.

While atomic CSS is strict about which properties can be applied, it does nothing to enforce which combinations are used. Elements can combine approved colors, spacing, font sizes, etc. in ways that are inconsistent with other elements.

For instance, just because you can create a text heading 120px, bold, orange, and underlined (all approved on their own), doesn’t mean this combination follows the designer’s intentions. Some sort of componentization of text styles is probably a good start in limiting variety.

Handling complex CSS

Another concern is that writing complex atomic styles can be unwieldy. The class syntax for applying properties at certain sizes (responsive) or in certain states (e.g. hover and focus) differs by framework, but easily grows long and hard to parse.

Note how long the class string grows just to apply simple hover/focus styles in Tailwind

For elements that require greater complexity in styling — inputs, buttons, pseudo-elements, anything highly interactive — styling must be done outside markup. Additionally, using traditional normalize and base stylesheets outside the atomic system can’t be bypassed. These factors don’t negate the usefulness of a utility-first approach, but reveal that there is no practical full-atomic implementation.

Abstracting to components

Even in atomic land, abstracting to component classes is important for limiting inconsistencies. While this is often done at the beginning with component styles, with atomic this auditing overhead looms over styling decisions. And if you forget to abstract out, you’ll likely end up with styling differences.

Perhaps the atomic way is to not worry about abstracting classes as much as applying them well, one at a time. However, a healthy distaste for inconsistency will lead to a feeling of obligation to mentally track slight differences and regularly consider when to consolidate similar class pairings.

Reading properties in markup

One of the first criticisms people make about atomic classes is that they’re lengthy, tiresome to read, and hard to scan. This gut reaction is not an unreasonable assessment. When many see atomic markup for the first time, it feels unintuitive and undesirable compared to traditional classes. Where is this knee-jerk reaction coming from?

While atomic abstracts usefully for writing styles, that abstraction makes reading styles more difficult. This is because its classes refer to elements by description rather than name. It’s analogous to referring to someone as tall, blue-eyed, and 25-years-old instead of simply “Jim”.

This naming scheme isn’t inherently wrong, but it does display a notable tradeoff. Team members need to both understand CSS and the framework’s syntax in order to understand what the classes are doing. With components, the class name serves as more of a black box that can be used to achieve a result without requiring an understanding of how to implement it.

Sustainability of styles

Of course, websites usually aren’t just built and left alone. Many will change over time, and the transition from old to new is impacted by styling decisions.

Both methodologies handle visual refreshes fairly well. Depending on the changes involved, adjusting atomic styles is likely the more efficient of the two, since definitions for every property are exposed at a high-level. Updating component styles might take more effort, since styles are mostly set individually across components.

Ease of redesign

Redesigns that include more foundational design and UX changes are a different beast. This is where component encapsulation provides a really clean process. Altering one component at a time without affecting other components is a vastly helpful technique to have available, especially in large undertakings. Though nested components can cause some bleeding of styles, dealing with these instances is usually trivial.

Wide-scale atomic changes are more complicated. Since styles are heavily abstracted, style relationships among elements are less apparent and elements tend to be more isolated. This can necessitate additional time and effort before redesigns to recognize structures and usage patterns that are spelled out in component-based sites.

Additionally, the heavy reuse of classes can make it difficult to redesign one part of a site without breaking the other parts. Care must be taken in managing the configuration file during this transition, as it can become a confusing mix of old, new, default and one-off properties.

The general caveat for redesigns is that the ease of the process depends greatly on how well each methodology was implemented. Optimally, an atomic site should have heavily componentized markup, and a component-based site should make use of shared styles.

Performance: comparing file size

There are many factors when considering the effect of CSS on website performance. With a slew of frameworks, CSS-in-JS considerations, tools like PurgeCSS, and more at play, it’s difficult to compare the two methodologies.

Atomic advocates are quick to praise it for smaller file sizes, thanks to its focus on removing duplicates styles. That’s true, but only part of the story.

Compression

Most sites deliver CSS files that are Gzipped: compressed with an algorithm that optimizes files based on repeated code. This means the size introduced by repeated styles in a component architecture is greatly reduced after compression. Furthermore CSS files have the benefit of being cached, so any performance hit likely happens once.

On the flip side, uncompressed atomic HTML files will be larger than their component counterparts due to more classnames. But once compressed, this difference is also negligible for most pages.

Writing componentized styles also opens the door to using CSS modules and CSS-in-JS frameworks, which are best suited for the component approach. These paradigms bring opportunities for further optimizations and automations.

In any case, minor file size differences are not the best factor in choosing one methodology over the other. File size is one small aspect of optimizing CSS, which is only one part of optimizing front end delivery.

Choosing the Right Approach for each Project

When to Build a Component System

Rarely will either methodology be the perfect fit for a given application. Component-based styles are not without drawbacks, but the system has remained the go-to methodology for many teams. Using this approach is a great option, especially for sites that:

  • share styles with other sites or design libraries
  • require high complexity in styles
  • have existing (non-atomic) styles to be augmented
  • are highly-segmented

When Atomic is a Good Fit

This newer way of styling web apps might draw many polarized opinions, but its approach is being proven more and more. Thanks to its composability and less opinionated concepts, an atomic framework is an ideal candidate for:

  • prototypes and browser mockups
  • plug-and-play framework needs

In the first case, atomic can be used to style markup quickly and without requiring long-term maintenance. It can also be a practical way to test the waters and see if it’s a good fit for the team and future projects.

In the latter case, an atomic framework is a great option for implementing styles without writing any CSS. (Note that you still need to know how to properly use CSS even if you aren’t writing it). Using an atomic framework (like Tailwind or Tachyons) will entail a definite syntax learning curve. However, it won’t involve fighting the existing concepts found in opinionated component frameworks.

Regardless of which approach you choose, a team that is familiar with both options is a team that has a richer understanding of styling websites and all its intricacies.