Tailwind Best Practices

Oscar te Giffel

Oscar te Giffel / July 20, 2020

Last update: 06/01/2021

Utility classes

  1. 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!
  2. Always use fewer utility classes when possible. For example, use mx-2 instead of ml-2 mr-2 and don't be afraid to use the simpler p-4 lg:pt-8 instead of the longer, more complicated pt-4 lg:pt-8 pr-4 pb-4 pl-4.
  3. 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 of block lg:flex flex-col justify-center to make it very clear that the flexbox utilities are only applicable at the lg: breakpoint and larger.
  4. Keep in mind that, by default, all responsive utilities are set to apply at min-width. That is, the sm: prefix does not apply only to small viewports; it applies to small breakpoints and larger. So you can use md:flex instead of md:flex lg:flex xl:flex for example. They both have the same behavior.
  5. Also note that, by default, the sm: breakpoint does not start at min-width: 0; as you might expect. It starts at min-width: 640px;. So if you want something to apply only to the smallest of viewports (those smaller than 640px), you must first set it to apply to all breakpoints and then override it for larger breakpoints. For example, use block sm:inline if you want something to appear as block for only the smallest of screens.
  6. Use a consistent pattern of margin classes to make it easier to reason about positioning. For example, consistently use only mt-* and ml-* utilities, which will position the current element, or consistently use only mb-* and mr-* 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 adjacent to the conditional element.
  7. 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.

Component classes

  1. 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.
  2. Do not @apply component classes in other components. For example, maintain seperate .btn and .btn-blue classes rather than using .btn-blue { @apply btn; }

Dynamic classes

  1. 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.