Skip to main content

Tailwind CSS basics: utility-first

What you learn in this chapter

You understand the basic idea of Tailwind CSS and what the Button classes you saw in the chapter on the finished form do. We leave the topic of color (design tokens) to the next chapter; here we cover the foundational utilities and state modifiers.

What is utility-first?

In traditional CSS, you create a class for each component and write the styles into it.

/* The traditional approach */
.button {
display: inline-flex;
align-items: center;
padding: 0.5rem 1rem;
border-radius: 0.375rem;
}

In Tailwind CSS, you build styles by combining small utility classes.

<button class="inline-flex items-center px-4 py-2 rounded-md">Click me</button>

Benefits

BenefitDescription
No naming neededYou do not have to come up with class names
No name collisionsGlobal CSS class-name collisions do not happen
Changes are localYou only fix that one element
Consistency emergesYou pick from a fixed scale (described later), so things line up

The weakness of "classes get long" is solved by bundling them into a component. That is exactly what the Button in this book is.

A long class list "inline-flex items-center justify-center px-4 ..."
↓ enclose it in a component
On the usage side <Button>Click me</Button>

Breaking down the Button's base classes

Let's look at the base classes of the Button from the chapter on the finished form.

"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50"

Layout

ClassMeaning
inline-flexMake it an inline Flexbox
items-centerCenter vertically
justify-centerCenter horizontally
gap-2Add spacing between the contents (such as an icon and text)
The button's contents: [icon] + button text
- gap-2 … spacing between the icon and the text
- items-center … center vertically
- justify-center … center horizontally

Text

ClassMeaning
whitespace-nowrapDo not wrap the text
text-smSmall font size (0.875rem)
font-mediumSlightly bold (font-weight: 500)

Decoration

ClassMeaning
rounded-mdRounded corners (medium)
transition-colorsSmoothly animate color changes

Focus and disabled states (accessibility)

ClassMeaning
outline-noneRemove the browser's default outline
focus-visible:ring-2Show a ring on keyboard focus
focus-visible:ring-ringThe ring's color (the ring token, the chapter on design tokens)
disabled:pointer-events-noneDo not accept clicks when disabled
disabled:opacity-50Make it semi-transparent when disabled
info

focus-visible is a setting that shows the focus ring only during keyboard operation. It does not appear on mouse clicks. This is an accessibility best practice: "clear for keyboard users, unobtrusive for mouse users."

State modifiers

Adding a prefix like hover: or disabled: lets you specify styles based on state.

<button class="bg-primary hover:bg-primary/90 disabled:opacity-50">Button</button>
ModifierWhen it applies
hover:When you hover the mouse over it
focus:On focus (both mouse and keyboard)
focus-visible:On keyboard focus only
active:While it is being pressed
disabled:When the disabled attribute is present
dark:In dark mode

The classes used for sizes

size: {
default: "h-9 px-4 py-2",
sm: "h-8 px-3",
lg: "h-10 px-6",
icon: "size-9",
}
ClassMeaning
h-9 / h-8 / h-10Height 36px / 32px / 40px
px-4 / px-3 / px-6Horizontal padding 16px / 12px / 24px
py-2Vertical padding 8px
size-9Width and height both 36px at once (a square; for icon buttons)

The spacing scale

Tailwind's spacing and sizes are picked from a scale with a fixed ratio.

0 → 0px
1 → 4px
2 → 8px
3 → 12px
4 → 16px
6 → 24px
8 → 32px
9 → 36px
10 → 40px

The basic unit is 4px per number. Because you pick from this fixed scale, spacing and sizes naturally line up across the whole screen, giving the design a sense of unity. This too is part of a design system.

Specifying opacity

The /90 in hover:bg-primary/90 means "90% opacity."

<div class="bg-primary/50">50%</div>
<div class="bg-primary/90">90%</div>

You can make a hover color by lightening the base color slightly, so you do not need to prepare a separate color.

Specify color with "design tokens"

The Button's bg-primary and text-primary-foreground are not colors that come built into Tailwind. These are design tokens: "meaningful colors" defined for the project.

variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
}

In Tailwind v4, you define these with CSS's @theme and CSS variables. Instead of specifying a color directly like bg-blue-500, you specify by meaning with bg-primary (the lead color), which enables theme switching and consistency.

We learn that mechanism in depth in the next chapter.

Summary of this chapter

  • Utility-first: build styles by combining small classes
  • The Button's base classes can be read split into layout, text, decoration, focus, and disabled states
  • State modifiers (hover: / focus-visible: / disabled: / dark:) specify styles per state
  • The spacing scale creates consistency
  • Specify color not with bg-blue-500 but with the design token bg-primary (next chapter)

In the next chapter, we learn about that color (design tokens) and the idea of a design system.