-
When writing a string of multiple utility classes, always do so in an order with meaning. The "Concentric CSS" approach works well with utility classes (i.e,. 1. positioning/visibility 2. box model 3. borders 4. backgrounds 5. typography 6. other visual adjustments). Once you establish a familiar pattern of ordering, parsing through long strings of utility classes will become much, much faster so a little more effort up front goes a long way!
-
Always use fewer utility classes when possible. For example, use
mx-2
instead ofml-2 mr-2
and don't be afraid to use the simplerp-4 lg:pt-8
instead of the longer, more complicatedpt-4 lg:pt-8 pr-4 pb-4 pl-4
. -
Prefix all utility classes that will only apply at a certain breakpoint with that breakpoint's prefix. For example, use
block lg:flex lg:flex-col lg:justify-center
instead ofblock lg:flex flex-col justify-center
to make it very clear that the flexbox utilities are only applicable at thelg:
breakpoint and larger. -
Keep in mind that, by default, all responsive utilities are set to apply at
min-width
. That is, thesm:
prefix does not apply only to small viewports; it applies to small breakpoints and larger. So for example instead of using the longformblock sm:block md:flex lg:flex xl:flex
for a<div>
element you can just writemd:flex
. They both have the same behavior. -
Also note that, by default, the
sm:
breakpoint does not start atmin-width: 0
as you might expect. It starts atmin-width: 640px
. So if you want something to apply only to the smallest of viewports (those smaller than640px
), you must first set it to apply to all breakpoints and then override it for larger breakpoints. For example, useblock sm:inline
if you want something to appear asblock
for only the smallest of screens. -
Use a consistent pattern of margin classes to make it easier to reason about positioning. For example, consistently use only
mt-*
andml-*
utilities, which will position the current element, or consistently use onlymb-*
andmr-*
utilities, which will position adjacent elements. In general it is easier to reason about positioning when using the former pattern because it is effectively scoped to the styled element, but keep in mind that you may need to make exceptions to your selected pattern for elements which only appear under certain conditions, as you wouldn't want to have to also add conditional margins to elements that are adjacent to the conditional element. -
Remember that you can often add a wrapper
<div>
with padding instead of using margins, which may help avoid potential issues with margin collapsing. By using utility-first CSS, you don't need to invent "semantic" class names for single purpose<div>
s so there is no reason to avoid using them when they are helpful.
-
Do not prematurely use
@apply
to abstract component classes where a proper framework/template-based component is more appropriate. However, if the string of utility classes within that component is also used in other components, then there is true duplication. In this case, the creation of a new shared CSS component class with@apply
may be warranted. -
Do not
@apply
component classes in other components. For example, maintain seperate.btn
and.btn-blue
classes rather than using.btn-blue { @apply btn; }
- When CSS classes are selected or generated dynamically, do not use string concatination to combine fragments of the full class. Instead switch between the complete strings. For example, use this
{% set width = maxPerRow == 2 ? 'sm:w-1/2' : 'sm:w-1/3' %}
instead of this{% set width = 'sm:w-1/' ~ maxPerRow %}
. You always want the complete string of each utility class present in the markup to make it easier to reason about and to ensure that it survives the PurgeCSS process, if it is a part of your build tools.