Skip to main content Accessibility Feedback

HUG CSS, how I approach CSS architecture

Yesterday, I wrote about classless vs. class-based CSS design systems. In the article, I noted…

There’s a middle ground between classless and class-based design systems.

My favorite CSS boilerplates/frameworks/whatever include base styles for various HTML elements, some component styles for commonly used components (like nav menus and things), and some utility classes for nudging and tweaking the UI.

I don’t know if you’d call them classless+ or Class-based-lite or what, but they sit somewhere in the middle.

Today, I wanted to talk about how I approach CSS architecture in my own projects. Let’s dig in!

The Approach

I use the following approach with all of the CSS projects I work on…

  1. Classless HTML for the core styles.
  2. Utility classes to nudge and tweak elements when needed.
  3. Group classes to simplify styling collections of elements that may have more complex needs.

To recap, that’s HTML, Utility, Group, or HUG CSS.

HTML core styles

I apply my default styles directly to HTML elements.

Here’s a simplified example…

/* Lists */
ul,
ol {
	margin: 0 0 1.5em 2em;
	padding: 0;
}

li {
	margin-bottom: 0.5em;
}

/* Headings */
h1, h2, h3, h4, h5, h6 {
	font-family: "Comic Sans", sans;
	line-height: 1.4;
	margin: 0 0 1em;
	padding: 0;
	word-wrap: break-word;
}

/* Buttons */
button {
	background-color: #0088cc;
	border: 0.125em solid #0088cc;
	border-radius: 0.25em;
	color: #ffffff;
	display: inline-block;
	font-size: 0.9375em;
	font-weight: normal;
	line-height: 1.2;
	margin-right: 0.3125em;
	margin-bottom: 0.3125em;
	padding: 0.5em 0.6875em;
}

button:hover,
button:active {
	background-color: #f7f7f7;
	border-color: #808080;
	color: #272727;
}

This let’s me author my HTML without the need to drop classes on everything…

<h2>Awesome wizards</h2>

<ul>
	<li>Merlin</li>
	<li>Yennefer</li>
	<li>Radagast</li>
</ul>

<button>Add a Wizard</button>

Utility classes

The default HTML styles work great a majority of the time. But designs often call for deviations from the standard presentation.

I really like utility classes for this. A utility class is a class that typically modifies just one or two properties on an element.

For example, imagine that by default my headings to have a good amount of bottom margin, but part of my design requires a heading and paragraph with no space between them.

I can write a .no-margin-bottom utility class that overrides any default margin-bottom property.

.no-margin-bottom {
	margin-bottom: 0;
}
<h2 class="no-margin-bottom">Awesome Wizards</h2>
<p>Which one do you like best?</p>

I have a few dozen little classes I can use to adjust the font, padding, margin, and color of various elements in my UI.

/* Text sizes */
.text-small {
	font-size: 0.9375em;
}

.text-large {
	font-size: 1.1875em;
	line-height: 1.4;
}

/* Text colors */
.text-muted {
	color: #808080;
}

Group classes

Sometimes, you have a collection of elements that requires slightly more complex styling.

You could use a collection of utility classes for that, but I find creating a group class is just more practical.

For example, let’s imagine we want to convert a standard unordered list (ul) to display the items horizontally and without bullets. We could use a collection of utility functions for that.

<!-- An inline list? -->
<ul class="list-style-none display-flex flex-gap flex-center flex-wrap">
	<li>Merlin</li>
	<li>Yennefer</li>
	<li>Radagast</li>
</ul>

This is what a lot folks refer to as the “Tailwind-style” of authoring CSS. It works, and can be nice for quickly prototyping things, but it’s really clunky to work with in the long term.

For something like this, I have a .list-inline group class that does the same thing with a single class.

.list-inline {
	list-style: none;
	display: flex;
	align-items: center;
	column-gap: 1em;
	flex-wrap: wrap;
}
<!-- An inline list -->
<ul class="list-inline">
	<li>Merlin</li>
	<li>Yennefer</li>
	<li>Radagast</li>
</ul>

You may notice that having a group class also makes it a lot more clear what’s actually going on with that particular piece of HTML.

You can also create utility classes to nudge and tweak your group classes if needed.

For example, I have a few .list-inline-* classes that adjust the amount of spacing between list items.

.list-inline-spaced {
	column-gap: 2.875em;
}

.list-inline-compact {
	column-gap: 0.5em;
}
<!-- An inline list with a bit more space between items -->
<ul class="list-inline list-inline-spaced">
	<li>Merlin</li>
	<li>Yennefer</li>
	<li>Radagast</li>
</ul>

Utility “classes” don’t always have to be classes

Interactive elements in a UI often have attributes that change based on the element’s state.

For example, a button with active and inactivate states should have an [aria-pressed] attribute with a value of true or false, respectively.

<button aria-pressed="true">Save to Favorites</button>

I will often use the attribute rather than a separate class to control styling.

[aria-pressed="true"] {
	background-color: #ff4136;
	border-color: #ff4136;
	color: #ffffff;
}

This removes the need for JavaScript to manage a state attribute and a class, reducing the likelihood of an expected bug.

Quick recap

To summarize…

  1. Classless HTML for the core styles.
  2. Utility classes to nudge and tweak elements when needed.
  3. Group classes to simplify styling collections of elements that may have more complex needs.

That’s HTML, Utility, Group, or HUG CSS.