The box model and logical axes
The CSS box model and the logical inline/block axis, sized and spaced with Tailwind utilities, as the foundation for the layouts in this chapter.
You give a card a fixed width, padding, and a border: w-64 p-4 border. It renders exactly 256 pixels wide, not 256 plus the padding, not 256 plus the border, just 256. If you wrote CSS before this course, that looks wrong, because there padding adds to width. It doesn’t here because Tailwind set box-sizing: border-box on every element before you typed a thing. This lesson shows what that one line bought you: the four boxes every element is made of, the spacing scale behind every p-* and m-*, and the inline/block axis that gives you right-to-left layouts for free.
By the end you’ll read any mix of p-*, m-*, and border and predict the rendered size in your head.
The four boxes
Section titled “The four boxes”Every element the browser lays out is not one rectangle but four, nested one inside the next, working outward from your content. Which one your width targets is the detail that trips people up later, so start with the names.
The content box is the innermost rectangle, where your text and child elements render. Under the older sizing model, this is the box your width and height target, the source of a surprise this lesson will undo.
The padding box wraps the content with space on the inside, between your content and the element’s edge, and it takes the element’s background color. Set bg-white p-4 and the white fills the content and the padding.
The border box is the visible edge. Every border-* utility draws here, on the ring between the padding and the outside world.
The margin box is the outermost band: transparent space outside the border that pushes neighboring elements away. Margin is the odd one out. It is the only box that takes no background, so it is always see-through, and the only one subject to margin collapse, a quirk you will meet shortly.
Why w-64 p-4 border is still 256px wide
Section titled “Why w-64 p-4 border is still 256px wide”Here is the surprise promised at the top of the lesson, and the rule behind it. A browser can read the width you set in two ways, and the two give different arithmetic.
Under the old default, content-box, width sizes only the content box; padding and border are added on top. That w-64 p-4 border card, 256px wide with 16px of padding per side and a 1px border, renders at 256 + 32 + 2 = 290px. You asked for 256 and got 290. Most AI-generated CSS still reaches for this model, having learned from a decade of pages written before the better default arrived, so when a pasted snippet comes out mysteriously too wide, suspect content-box first.
Under border-box, the default Preflight sets for you, width sizes the border box: content, padding, and border all fit inside the number you declared. w-64 p-4 border is 256px, full stop. The content box shrinks to absorb the 32px of padding and 2px of border; you name the outer size and the browser does the subtraction.
The payoff is that widths compose by addition, and one of the most common legacy layout bugs disappears. Picture a child set to w-full (100% of its parent) with p-6 of padding. Under content-box it measures 100% + 48px, spills past its parent, and triggers a horizontal scrollbar. Under border-box it stays exactly 100%, padding folded inside, no overflow.
To see it, drag the padding slider and watch the two readouts. Under border-box they stay locked together however much padding you add; flip to content-box and they diverge the instant padding is nonzero.
content
You never wrote the box-sizing rule yourself: you inherited it from Preflight, on every element, and that is the state worth protecting.
The spacing scale and the 4px grid
Section titled “The spacing scale and the 4px grid”The number in p-4 or m-2 is not a pixel count, it’s a multiplier. Every spacing utility in Tailwind is arithmetic over a single variable.
p-*, m-*, gap-*, space-*, and the inset utilities all compile to calc(var(--spacing) * n), where n is the number in the class name. In Tailwind v4 the default --spacing is 0.25rem (4px), so p-4 resolves to calc(0.25rem * 4) = 1rem = 16px, and p-2 to 0.5rem = 8px.
Because every utility routes through one variable, you can rescale the entire spacing system by editing a single line in @theme, the same block where you defined your color tokens in Custom properties & tokens.
@theme { --spacing: 0.25rem;}Change 0.25rem to 0.2rem and every p-*, m-*, and gap-* tightens at once, in proportion, with no find-and-replace. That one source of truth keeps spacing consistent, and it encodes a convention you’ll see everywhere: the 4px grid. Material, Carbon, Tailwind, and shadcn all space things in multiples of 4, which is what makes a layout look intentional.
Tailwind does let you escape the scale with an arbitrary value like p-[17px], but reach for it sparingly. A codebase peppered with p-[17px], mt-[13px], and gap-[5px] has no system. If the scale genuinely doesn’t fit your design, change its base rather than scatter magic numbers through your code.
To check that the formula has landed: given the default --spacing: 0.25rem, resolve each utility to its pixel value.
With the default --spacing of 0.25rem (4px), pick the rendered pixel value for each utility. Pick the right option from each dropdown, then press Check.
p-4 resolves to of padding on every side, px-2 puts of padding on the left and right, and m-6 is of margin.
Margin collapse, and why to use gap instead
Section titled “Margin collapse, and why to use gap instead”Margin has one behavior that surprises everyone the first time, and it works against you, not for you. Learn it so you can recognize it in someone else’s CSS, not so you can reach for it.
The rule shows up in two shapes. The first is adjacent siblings: when two block elements stack vertically, the bottom margin of the upper one and the top margin of the lower one don’t add together. They collapse to the larger of the two. Put an mb-8 element above an mt-4 element and they sit 32px apart, not 48px; the browser keeps the bigger margin and discards the smaller.
The second shape is parent and first or last child: a block parent’s top margin can collapse with its first child’s top margin, and its bottom with its last child’s bottom. The child’s margin appears to escape the parent and push the whole parent down. This is how you get a phantom gap above a section that you can’t find on the parent, because the margin causing it lives on the child.
Two facts scope the trap tightly. Margin collapse happens only between block-level elements in normal flow , and only on the vertical (block) axis. Never between flex items, never between grid items, never horizontally.
So don’t fix margin collapse, sidestep it. The moment a parent becomes a flex or grid container, its children stop collapsing, and you space them with gap instead of margins, leaving no margins to collapse. The habit you’ll build this chapter is flex flex-col gap-4, not a stack of mb-* utilities; gap gets its full treatment in a later lesson. Legacy code stacks blocks with margins and pays the collapse cost, while your code uses gap and never sees it.
Logical properties and the inline/block axes
Section titled “Logical properties and the inline/block axes”CSS lets you name directions two ways. The physical names are the ones you know: left, right, top, bottom. The logical names follow how text flows, and choosing them is what lets a layout mirror correctly in Arabic or Hebrew with no second stylesheet.
CSS gives the box model two axes. The inline axis is the direction text flows, which in English runs left-to-right, so horizontally. The block axis is perpendicular, the direction block elements stack, which in English runs top-to-bottom.
These names follow the writing system rather than the screen. In an RTL language the inline axis runs right-to-left, so “inline start” is the right edge instead of the left. The physical edge changes with the language; the logical concept does not.
This is why CSS has logical properties: padding-inline-start instead of padding-left, margin-inline-end instead of margin-right. They resolve against the writing direction, so when a document flips to dir="rtl", a layout authored in logical properties mirrors itself. Your “start” padding moves from the left edge to the right with no code changes.
Tailwind ships these as utilities, and these are the ones you’ll type:
ps-*/pe-*: padding inline-start / inline-end, the logical twins ofpl-*/pr-*.ms-*/me-*: margin inline-start / inline-end.pbs-*/pbe-*andmbs-*/mbe-*: the block-axis forms, for the rarer vertical-writing cases.
The same system has a logical inset family, inset-s-*, inset-e-*, inset-bs-*, and inset-be-*, for positioned elements; the position-and-inset lesson later in this chapter covers it.
To see the payoff, watch one utility produce two mirrored results. These tabs render the same box with the same ps-8 class; only the document’s writing direction differs.
So adopt this default: use logical utilities from day one in any project that might be localized to a right-to-left language. They cost nothing over physical utilities and hand you RTL support the moment you set a dir attribute. For a project that will only ever be left-to-right, physical utilities are fine, and switching to logical later is mechanical. This is what the course convention “logical properties over physical so RTL works for free” buys you.
Centering a block with mx-auto
Section titled “Centering a block with mx-auto”Notice how little margin you’ve needed: padding for space inside an element, gap for space between siblings, and margin almost never. So where does margin still earn its place? In one job: centering.
mx-auto compiles to margin-inline: auto, which centers a block element of a defined width inside its parent. When both side margins are auto, the browser takes the horizontal space left over after the element’s width and splits it equally between the two sides, so the element sits dead center.
The defined width is essential. A full-width block leaves no space to distribute, so mx-auto alone does nothing; you have to constrain the width first. The standard pattern pairs the two:
<article className="mx-auto max-w-3xl"> <h1>Pricing</h1> <p>Pick the plan that fits your team.</p></article>max-w-3xl caps the article’s width and mx-auto centers what’s left. You’ll reach for this pairing constantly for centered content columns and reading surfaces. (The sizing utilities like max-w-3xl get their own lesson later in this chapter.)
Mind the boundary, since mx-auto is easy to overuse: it’s for a standalone block in normal flow, with no flex or grid parent. Once the parent is a flex or grid container, centering is that container’s job through justify-center or place-items-center, covered in the flexbox and grid lessons later in this chapter. The rule of thumb: mx-auto for a lone block in flow, flex or grid centering for anything inside a flex or grid parent.
The card below is capped to a width but pinned to the left. Add the one class that centers it.
The card is capped with max-w-md but stuck to the left. Add one utility class so it centers horizontally in its parent.
Inspecting the box model in DevTools
Section titled “Inspecting the box model in DevTools”The next time a layout is off by a few pixels, skip the guesswork: open DevTools and let the browser report the resolved numbers.
Hover any element in the Elements panel and the browser overlays the page in the box model’s four colors: content in blue, padding in green, border in yellow, margin in tan-orange.
For exact figures, the Computed panel draws the same diagram with measured values on every side, which is where you confirm what border-box resolved to. Inspect your w-64 p-4 border card and you’ll see 256 in the center with 16 of padding and 2 of border folded inside it.
External resources
Section titled “External resources”When you want to go deeper than the lesson covered, these four are worth bookmarking: two interactive explainers and two canonical references.
Josh Comeau's interactive walkthrough of every margin-collapse rule, with live editable visualizations.
The inline/block axis and the full logical-property family, with diagrams and RTL examples.
The four boxes, content-box vs border-box, and margin collapse, with interactive examples.
The property Preflight sets for you, and exactly how border-box changes the width calculation.