Design Systems, Pt. 4: Generating Utility Classes
This article outlines how to generate utility classes from the design tokens defined in part 1 of this series.
While I don’t think a utility-first approach to writing CSS styles is the right way to go, utility classes can be useful if used responsibly. Let’s see how we can generate some classes with SASS.
Colors and font sizes
We’ll start with the colors.
@each $key, $value in theme(colors) {
#{".color-" + $key} {
color: var(#{"--color-" + $key}) !important;
}
}
@each $key, $value in theme(colors) {
#{".bg-" + $key} {
background-color: var(#{"--color-" + $key}) !important;
}
}
This generates classes for the text color (e.g. .color-primary
) and for the
background color (e.g. .bg-primary
). The values are not the actual values from
the color map, but references to the custom properties we defined in part
3.
We can do the same with font sizes:
@each $key, $value in theme(sizes) {
#{".size-" + $key} {
font-size: var(#{"--size-" + $key}) !important;
}
}
Now we have font size classes like .size-m
, .size-l
etc. You can use the
same approach for font families, letter spacing, font weights, radii and border
widths, depending on your needs.
Paddings and margins
We have two maps in our configuration relevant for paddings and margins:
$spacer-factors
and $lines
.
In this example, we want to enforce the base line grid by requiring vertical paddings and margins to be defined in lines, while the spacers are only used for horizontal margins. Also, it is often easier to keep margins consistent if you either only use top margins or only use bottom margins, but not mix both of them. That’s why we will only define utility classes for bottom margins, but not for top margins.
Your needs might be different, so you can modify the example to generate utility classes for vertical margins based on spacers and for top margins.
To make explicit which spacing base we are using, our class names will include
-l-
for distances based on lines and -s-
for distances based on spacers.
To generate the line-based classes:
@each $key, $value in theme(lines) {
#{".pt-l-" + $key} {
padding-top: var(#{"--lines-" + $key}) !important;
}
#{".pb-l-" + $key} {
padding-bottom: var(#{"--lines-" + $key}) !important;
}
#{".py-l-" + $key} {
padding-top: var(#{"--lines-" + $key}) !important;
padding-bottom: var(#{"--lines-" + $key}) !important;
}
#{".mb-l-" + $key} {
margin-bottom: var(#{"--lines-" + $key}) !important;
}
}
This will generate the classes .pt-l-1
, .pb-l-1
, .py-l-1
and .mb-l-1
for
paddings and margins with a width of one line, and similar classes for all the
other values in $lines
.
Similarly, we can generate the utility classes for spacer-based paddings and margins with:
@each $key, $value in theme(spacers) {
#{".pl-s-" + $key} {
padding-left: var(#{"--spacer-" + $key}) !important;
}
#{".pr-s-" + $key} {
padding-right: var(#{"--spacer-" + $key}) !important;
}
#{".px-s-" + $key} {
padding-right: var(#{"--spacer-" + $key}) !important;
padding-left: var(#{"--spacer-" + $key}) !important;
}
#{".ml-s-" + $key} {
margin-left: var(#{"--spacer-" + $key}) !important;
}
#{".mr-s-" + $key} {
margin-right: var(#{"--spacer-" + $key}) !important;
}
#{".mx-s-" + $key} {
margin-right: var(#{"--spacer-" + $key}) !important;
margin-left: var(#{"--spacer-" + $key}) !important;
}
}
With this, we’ll get the classes .pl-s-1
, .pr-s-1
, .px-s-1
, .ml-s-1
,
.mr-s-1
and .mx-s-1
for single spacer distances, and similar classes for all
other values in $spacer-factors
.
Next part: Design Systems, Pt. 5: Themes