Every element is drawn as nested rectangles: the content, then padding (space inside the border, painted with the background), then the border, then margin (transparent space outside that pushes neighbours away). With box-sizing: border-box, the width you set includes padding and border, which makes layouts far easier to predict. The display property decides how boxes flow.
| Layer | What it does | Gets the background? |
|---|---|---|
| Content | Holds text, images and child elements | Yes |
| Padding | Breathing room between content and border | Yes |
| Border | A visible line (or nothing) around the padding | Its own border colour |
| Margin | Gap between this box and its neighbours | No, always transparent |
Updated
Move the four sliders and the diagram, the measured total size and the CSS all change together. Below the visualiser you can flip box-sizing, watch two margins collapse into one, and compare block, inline, inline-block, flex, grid and none side by side on the same three items.
| You want | Use |
|---|---|
| The background colour to cover the space | padding |
| A bigger tap area on a button | padding |
| Space between two separate cards | margin, or gap on the parent |
| One item pushed to the far end of a flex row | margin-left: auto |
| A fixed-width block centred | margin-inline: auto |
| Space that can never collapse with a neighbour | padding, or gap |
The first pair shows two siblings whose 24px and 40px margins meet: the gap is 40px, not 64px. The second pair sits in a flow-root container, which stops the parent and child margins merging, and the third uses flex, where nothing collapses.
Edit the margins and watch the gaps. Adding padding-top: 1px to the outer box also stops the parent and child merging.
<style>
.a { margin-bottom: 24px; background: #dbeafe }
.b { margin-top: 40px; background: #fde68a }
.outer { background: #f1f5f9 }
.inner { margin-top: 30px; background: #bbf7d0 }
.safe { display: flow-root; background: #f1f5f9 }
div { padding: 6px 10px }
</style>
<div class="a">margin-bottom: 24px</div>
<div class="b">margin-top: 40px (gap is 40px)</div>
<div class="outer"><div class="inner">child margin leaks out of the grey parent</div></div>
<div class="safe"><div class="inner">flow-root keeps it inside</div></div>groove, ridge, inset and outset shade the border to fake depth. They look best at 4px or wider.
<style>
.grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px }
.grid div { border: 5px solid #4F98FB; padding: 10px 4px; text-align: center; font-size: 13px }
</style>
<div class="grid">
<div style="border-style: solid">solid</div><div style="border-style: dashed">dashed</div>
<div style="border-style: dotted">dotted</div><div style="border-style: double">double</div>
<div style="border-style: groove">groove</div><div style="border-style: ridge">ridge</div>
<div style="border-style: inset">inset</div><div style="border-style: outset">outset</div>
</div>A huge radius such as 999px makes a pill of any length. 50% on a square gives a circle.
<style>
.row { display: flex; gap: 14px; align-items: center; flex-wrap: wrap }
.row div { background: #0f172a; color: #fff; padding: 10px 16px; font-size: 13px }
</style>
<div class="row">
<div style="border-radius: 10px">10px</div>
<div style="border-radius: 999px">pill</div>
<div style="border-radius: 50%; width: 70px; height: 70px; display: grid; place-items: center; padding: 0">circle</div>
<div style="border-radius: 40px / 16px">40px / 16px</div>
<div style="border-radius: 18px 0">18px 0</div>
</div>| Value | New line? | Width and height work? | Typical use | Tailwind |
|---|---|---|---|---|
| block | Yes | Yes | Sections, paragraphs, cards | block |
| inline | No | No | Words inside a sentence | inline |
| inline-block | No | Yes | Badges, buttons in a line of text | inline-block |
| flex | Yes | Yes | Rows or columns of items | flex |
| inline-flex | No | Yes | An icon and label inside text | inline-flex |
| grid | Yes | Yes | Two-dimensional layouts | grid |
| inline-grid | No | Yes | A small grid that sits in a line | inline-grid |
| flow-root | Yes | Yes | Contains floats and child margins | flow-root |
| contents | Box removed | No box to size | Let children join the parent layout | contents |
| none | Removed from layout | Not rendered | Hide completely | hidden |
Each row holds the same three spans with width: 80px and padding. Only inline ignores the width.
<style>
.demo span { width: 80px; padding: 6px; margin: 3px; background: #dbeafe; border: 1px solid #4F98FB; font-size: 13px }
.demo { border: 1px dashed #94a3b8; margin-bottom: 8px; padding: 4px }
h4 { margin: 4px 0; font: 600 12px monospace }
</style>
<h4>display: block</h4><div class="demo"><span style="display:block">One</span><span style="display:block">Two</span><span style="display:block">Three</span></div>
<h4>display: inline</h4><div class="demo"><span>One</span><span>Two</span><span>Three</span></div>
<h4>display: inline-block</h4><div class="demo"><span style="display:inline-block">One</span><span style="display:inline-block">Two</span><span style="display:inline-block">Three</span></div>
<h4>parent display: flex</h4><div class="demo" style="display:flex"><span>One</span><span>Two</span><span>Three</span></div>
<h4>parent display: grid (2 columns)</h4><div class="demo" style="display:grid;grid-template-columns:1fr 1fr"><span>One</span><span>Two</span><span>Three</span></div>| Property | Keeps its space? | Clickable or focusable? | Read by screen readers? | Can transition? |
|---|---|---|---|---|
| display: none | No | No | No | Only with allow-discrete in newer browsers |
| visibility: hidden | Yes | No | No | Yes, switches at the end or start |
| opacity: 0 | Yes | Yes | Yes | Yes, smoothly |
Notice the gap left behind by visibility and opacity, and the closed gap with display: none.
<style>
.row { display: flex; gap: 6px; margin-bottom: 8px }
.row div { flex: 0 0 70px; padding: 8px; background: #fde68a; text-align: center; font-size: 13px }
</style>
<div class="row"><div>A</div><div style="display:none">B</div><div>C</div> display: none</div>
<div class="row"><div>A</div><div style="visibility:hidden">B</div><div>C</div> visibility: hidden</div>
<div class="row"><div>A</div><div style="opacity:0">B</div><div>C</div> opacity: 0</div>The pseudo-elements are included because they are boxes too. Tailwind's Preflight ships this rule already.
*, *::before, *::after {
box-sizing: border-box;
}| CSS | Tailwind |
|---|---|
| padding: 1rem | p-4 |
| padding-inline: 1.5rem | px-6 |
| margin-top: 2rem | mt-8 |
| margin-inline: auto | mx-auto |
| border-width: 1px / 2px | border / border-2 |
| border-style: dashed | border-dashed |
| border-radius: 0.5rem | rounded-lg |
| border-radius: 9999px | rounded-full |
| box-sizing: border-box / content-box | box-border / box-content |
| visibility: hidden | invisible |
| opacity: 0 | opacity-0 |
| Keyword |
|---|
| solid |
| dashed |
| dotted |
| double |
| groove |
| ridge |
| inset |
| outset |
With content-box, total width equals width plus left and right padding plus left and right border. The margin then adds outside that. This was the original CSS behaviour, and it is still the browser default for most elements.
With border-box, the width you write is measured to the outer edge of the border. Padding and border eat into the content area instead of growing the box, so a two-column layout of 50% plus 50% stays on one line however much padding you add.
The border shorthand takes a width, a style and a colour in any order. The styles are solid, dashed, dotted, double, groove, ridge, inset, outset, none and hidden. A border with style none has zero width whatever width you wrote, which surprises people who set only border-width.
border-radius rounds the corners of the border, background and outline in current browsers. One value rounds all four corners; a slash sets separate horizontal and vertical radii for elliptical corners, and 50% on a square turns it into a circle.
block boxes start on a new line and stretch to fill their container. inline boxes flow inside a line of text and ignore width and height. inline-block flows like text but accepts sizes and vertical margins, which makes it handy for badges and buttons.
flex and grid make the element a block on the outside and a layout container on the inside. Their children stop behaving as block or inline and follow the flex or grid rules instead, with gap available for spacing. contents removes the box itself while keeping its children, but it has a history of accessibility bugs, so test it with a screen reader before relying on it.
Padding sits inside the border, so it is filled by the element's background and is part of its clickable area. Margin sits outside the border, is always transparent and only separates the element from others. Vertical margins between blocks can also collapse together, while padding never collapses.
Under the default box-sizing: content-box, width applies to the content only, and padding plus border are added on top. A 300px box with 20px padding and a 2px border renders 344px wide. Switch to box-sizing: border-box and it stays 300px, with the content shrinking to fit.
Top and bottom margins of block boxes in normal flow merge when they touch: between siblings, and between a parent and its first or last child when no border, padding or inline content separates them. The larger margin wins. Margins never collapse inside flex or grid containers, or horizontally.
display: none removes the element from layout entirely, so the space closes up and screen readers skip it. visibility: hidden keeps the space reserved but paints nothing, and the content is also hidden from assistive technology. opacity: 0 keeps both the space and the ability to be clicked or focused.
No. Width, height and vertical margins are ignored on a plain inline box such as a span. Vertical padding is painted but does not push lines apart. Change it to inline-block, or make the parent a flex container, and those sizes start to apply.
Related: Flexbox and grid cheat sheet, CSS calc, min, max and clamp, How to centre a div, CSS position, CSS shadow generator.
Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.
We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.