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…
- Classless HTML for the core styles.
- Utility classes to nudge and tweak elements when needed.
- 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…
- Classless HTML for the core styles.
- Utility classes to nudge and tweak elements when needed.
- Group classes to simplify styling collections of elements that may have more complex needs.
That’s HTML, Utility, Group, or HUG CSS.