03 · Utility Basics: Spacing, Sizing & the Scale¶
Most of the classes you'll write in any Tailwind project are spacing and sizing: padding, margin, gaps, widths and heights. They follow one naming pattern and one scale, and once you know both you can guess almost any class name correctly. This lesson decodes the pattern and shows, from the compiler's actual output, what each class becomes.
The naming pattern¶
Most utilities are {property}-{value}, with optional side or axis letters:
| Prefix | Property | Sides / axes |
|---|---|---|
p |
padding | pt pr pb pl (top/right/bottom/left), px py (inline/block axes), ps pe (inline start/end) |
m |
margin | same pattern: mt, mx, my, ms, me… |
gap |
gap | gap-x, gap-y |
w, h |
width, height | min-w, max-w, min-h, max-h |
size |
width and height | — |
inset, top, left… |
position offsets | inset-x, inset-y |
So px-6 is horizontal padding at step 6, mt-2 is top margin at step 2, gap-x-4 is
column gap at step 4.
The spacing scale is one variable¶
In v4, the whole spacing scale is derived from a single theme variable, --spacing, which
defaults to 0.25rem (4px at the default browser font size). A number after a spacing
utility is a multiplier. Here is what the compiler generated for a selection:
.p-4 { padding: calc(var(--spacing) * 4); } /* 1rem */
.p-4\.5 { padding: calc(var(--spacing) * 4.5); } /* 1.125rem */
.p-13 { padding: calc(var(--spacing) * 13); } /* 3.25rem */
.w-128 { width: calc(var(--spacing) * 128); } /* 32rem */
.-mt-2 { margin-top: calc(var(--spacing) * -2); } /* -0.5rem */
.gap-4 { gap: calc(var(--spacing) * 4); }
.size-10 {
width: calc(var(--spacing) * 10);
height: calc(var(--spacing) * 10);
}
Three consequences:
- Any multiple works, including ones that weren't in older fixed scales (
p-13,w-128) and quarter steps likep-4.5. Steps must be multiples of 0.25: in our testp-4.25andp-4.75were generated,p-4.3silently wasn't. - Negative values are a leading minus:
-mt-2,-translate-x-4. - Retuning the scale is one line. Set
--spacing: 0.3remin your theme (Level 2 · 02) and everyp-*,m-*,gap-*,w-*,h-*changes proportionally. We tried it: with--spacing: 0.3rem, an element withp-6computed to28.8pxof padding (6 × 0.3rem × 16px) instead of 24px.
A useful mental conversion: step × 4 = pixels at default settings. p-6 is 24px,
gap-3 is 12px, w-64 is 256px.
Other kinds of values¶
Not every value is a spacing multiple:
.w-1\/2 { width: calc(1 / 2 * 100%); } /* fractions: w-1/3, w-2/5, ... */
.w-screen { width: 100vw; }
.h-dvh { height: 100dvh; } /* also h-svh, h-lvh, h-screen (100vh) */
.max-w-prose { max-width: 65ch; }
.max-w-md { max-width: var(--container-md); } /* --container-md: 28rem */
.mx-auto { margin-inline: auto; }
.p-\[13px\] { padding: 13px; } /* arbitrary value, Level 2 · 04 */
Keywords you'll use constantly: w-full (100%), w-auto, w-fit (fit-content),
min-w-0 (essential inside flex and grid — see
the min-width: auto trap),
h-full, min-h-dvh.
The max-w-* named sizes (max-w-sm, md, lg, xl, 2xl … 7xl) come from the
--container-* theme variables and are the usual way to limit a column's width.
max-w-prose limits text to a comfortable 65 characters.
space-* vs gap¶
To put space between children, there are two tools:
<div class="flex gap-4">…</div> <!-- gap: only works in flex and grid containers -->
<div class="space-y-4">…</div> <!-- margins between children, any layout -->
space-x-4 compiles to a rule on the children:
:where(.space-x-4 > :not(:last-child)) {
--tw-space-x-reverse: 0;
margin-inline-start: calc(calc(var(--spacing) * 4) * var(--tw-space-x-reverse));
margin-inline-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-x-reverse)));
}
It adds an end margin to every child except the last. Prefer gap in flex and grid
layouts: it doesn't depend on children being visible, handles wrapping correctly (margins
break when items wrap to a new line) and doesn't interfere with margins you set on the
children yourself. space-y-* is still handy for vertical rhythm in plain block content,
like paragraphs in an article.
Worked example: a profile card¶
<article class="mx-auto max-w-sm rounded-xl border border-gray-200 p-6">
<img class="size-16 rounded-full" src="avatar.jpg" alt="" width="64" height="64">
<h2 class="mt-4 text-lg font-semibold">Priya Rao</h2>
<p class="mt-1 text-sm text-gray-600">Recipe developer</p>
<div class="mt-6 flex gap-3">
<a class="rounded-md px-4 py-2 text-sm font-medium" href="/priya">Profile</a>
<a class="rounded-md px-4 py-2 text-sm font-medium" href="/priya/recipes">Recipes</a>
</div>
</article>
Reading it as spacing decisions: the card is centred (mx-auto) and never wider than
24rem (max-w-sm); it has 1.5rem padding (p-6); vertical rhythm between blocks is
mt-1, mt-4, mt-6 — small gaps within a group, larger ones between groups; buttons
are separated by a 0.75rem gap-3. Every value comes from one scale, which is why
Tailwind layouts tend to look evenly spaced without a designer checking each margin.
How It Actually Works¶
When the compiler sees p-4.5, it:
- Splits the candidate into a utility root and a value: root
p, value4.5. Roots are matched longest-first, sopx-4is rootpx, notpwith valuex-4, andspace-x-4is rootspace-x. - Resolves the value. For spacing roots, a bare number is valid if it's a multiple of
0.25; it becomes
calc(var(--spacing) * 4.5). Named values (auto,full,screen,px) and fractions are looked up in built-in tables; theme keys likemdresolve to--container-md. - Emits the declaration for that root:
p→padding,px→padding-inline,mt→margin-top.
Notice px-6 generates padding-inline and mx-auto generates margin-inline — logical
properties, which mirror automatically in right-to-left layouts (Level 4 · 03). And
because the result is calc(var(--spacing) * N) rather than a hard-coded length, the
value stays connected to the theme: devtools shows you the variable, and changing the
variable at runtime changes the layout.
Common mistakes¶
- Guessing pixel values:
p-16is 64px, not 16px. - Using
space-*in wrapping flex rows — usegap. - Arbitrary values for things the scale already covers (
p-[16px]is justp-4). w-screenfor full width, which includes the scrollbar and causes horizontal scroll; usew-full.h-screenon mobile, taller than the visible area when browser toolbars show; useh-dvhormin-h-dvh.- Forgetting
min-w-0on flex children that contain long text or code.
Exercise¶
- Without looking anything up, write the classes for: 24px padding on all sides, 8px top margin, a 12px gap between flex items, a 40px square avatar, and a column limited to 42rem and centred. Compile and check.
- Build the profile card. Then set
--spacing: 0.3remin a@themeblock (preview of Level 2 · 01) and watch every spacing value grow proportionally. - Make a flex row of tags with
space-x-2andflex-wrap, narrow the window until it wraps, and compare withgap-2.