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
| Benefit | Description |
|---|---|
| No naming needed | You do not have to come up with class names |
| No name collisions | Global CSS class-name collisions do not happen |
| Changes are local | You only fix that one element |
| Consistency emerges | You 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
| Class | Meaning |
|---|---|
inline-flex | Make it an inline Flexbox |
items-center | Center vertically |
justify-center | Center horizontally |
gap-2 | Add 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
| Class | Meaning |
|---|---|
whitespace-nowrap | Do not wrap the text |
text-sm | Small font size (0.875rem) |
font-medium | Slightly bold (font-weight: 500) |
Decoration
| Class | Meaning |
|---|---|
rounded-md | Rounded corners (medium) |
transition-colors | Smoothly animate color changes |
Focus and disabled states (accessibility)
| Class | Meaning |
|---|---|
outline-none | Remove the browser's default outline |
focus-visible:ring-2 | Show a ring on keyboard focus |
focus-visible:ring-ring | The ring's color (the ring token, the chapter on design tokens) |
disabled:pointer-events-none | Do not accept clicks when disabled |
disabled:opacity-50 | Make it semi-transparent when disabled |
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>
| Modifier | When 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",
}
| Class | Meaning |
|---|---|
h-9 / h-8 / h-10 | Height 36px / 32px / 40px |
px-4 / px-3 / px-6 | Horizontal padding 16px / 12px / 24px |
py-2 | Vertical padding 8px |
size-9 | Width 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-500but with the design tokenbg-primary(next chapter)
In the next chapter, we learn about that color (design tokens) and the idea of a design system.