CSS Layout
- Description: How elements arrange on the page — display, normal flow, positioning, flexbox, grid, and modern layout patterns.
- My Notion Note ID: K2A-F2-2
- Created: 2018-03-23
- Updated: 2026-05-17
- License: Reuse is very welcome. Please credit Yu Zhang and link back to the original on yuzhang.io
Table of Contents
- 1. Display Types
- 2. Normal Flow
- 3. Positioning
- 4. Flexbox
- 5. Grid
- 6. Logical Properties
- 7. Container Queries
- 8. Common Patterns
- 9. References
1. Display Types
display decides which formatting context an element creates and how it lays out its children.
| Value | Behaviour |
|---|---|
block |
Full-width box, breaks line before/after. Default for <div>, <p>, <section>, <h1>. |
inline |
Flows with text. Ignores width/height and vertical margin/padding. Default for <span>, <a>, <em>. |
inline-block |
Inline placement, block-style sizing (width/height work). |
flex |
Container with flex layout. Children are flex items. |
inline-flex |
Same, but the container itself flows inline. |
grid |
Container with grid layout. |
inline-grid |
Same, but the container flows inline. |
none |
Removed from layout and accessibility tree. Reserve no space. |
contents |
Element disappears; children promoted to its parent. |
flow-root |
Block that establishes a new block formatting context — modern way to contain floats and prevent margin collapse. |
Note: <div> and <span> are semantically empty; the layout meaning comes from display. Tailwind utility classes like flex, grid, block, hidden map directly to these values.
2. Normal Flow
Default layout if you set nothing: block elements stack vertically, inline elements flow left-to-right within a line.
- Writing mode (
writing-mode) decides what "vertical" and "horizontal" mean. Default ishorizontal-tb(top-to-bottom lines, left-to-right characters in LTR languages). - Inline elements form line boxes.
line-heightcontrols their height;vertical-aligncontrols baseline alignment. - Block elements collapse vertical margins with adjacent siblings — see box model in css-selectors-and-cascade.
3. Positioning
position removes elements from (or anchors them within) normal flow.
| Value | Effect |
|---|---|
static (default) |
Normal flow. top/left/etc. ignored. |
relative |
Stays in flow; can be nudged with top/right/bottom/left. Establishes a positioning context for absolute descendants. |
absolute |
Removed from flow. Positioned relative to nearest positioned ancestor (or initial containing block if none). |
fixed |
Removed from flow. Positioned relative to the viewport. Pinned during scroll. |
sticky |
Stays in flow until scroll crosses a threshold (top: 0), then sticks. Scoped to its scroll container. |
.tooltip-parent { position: relative; }
.tooltip {
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
}
.sticky-header {
position: sticky;
top: 0;
z-index: 10;
}
z-index controls stacking — only works on positioned elements (and flex/grid items). Higher number = closer to the viewer. Negative values allowed.
Stacking context — a self-contained z-index scope. Created by position + z-index non-auto, opacity < 1, transform, filter, will-change, isolation: isolate. Once a stacking context forms, descendants' z-index only compete within it — they can't punch through.
Pitfall: position: absolute looks for the nearest positioned ancestor — if there's no positioned ancestor, it anchors to the viewport, which is rarely what you want. Set position: relative on the intended container.
4. Flexbox
1-dimensional layout — arranges items along a single axis (row or column). Flow-aware: start/end instead of left/right so it works in any writing mode.
.container {
display: flex;
flex-direction: row; /* row | row-reverse | column | column-reverse */
flex-wrap: wrap; /* nowrap (default) | wrap | wrap-reverse */
justify-content: space-between; /* main axis alignment */
align-items: center; /* cross axis alignment */
gap: 1rem; /* gutter between items */
}
4.1 Two Axes
- Main axis = direction set by
flex-direction. - Cross axis = perpendicular.
justify-*operates on the main axis;align-*on the cross axis.
4.2 Container Properties
| Property | Values |
|---|---|
flex-direction |
row, row-reverse, column, column-reverse |
flex-wrap |
nowrap, wrap, wrap-reverse |
justify-content |
flex-start, flex-end, center, space-between, space-around, space-evenly |
align-items |
stretch (default), flex-start, flex-end, center, baseline |
align-content |
Alignment of wrapped lines (only when flex-wrap: wrap). |
gap, row-gap, column-gap |
Gutters. |
4.3 Item Properties
| Property | Meaning |
|---|---|
flex-grow |
How much to expand to fill extra space (0 = don't grow). |
flex-shrink |
How much to shrink when space is tight (1 = default). |
flex-basis |
Starting size before grow/shrink. auto = use width/height. |
flex: G S B |
Shorthand. flex: 1 ≡ 1 1 0%. flex: auto ≡ 1 1 auto. flex: none ≡ 0 0 auto. |
align-self |
Override container's align-items for this one item. |
order |
Reorder items (default 0; lower = earlier). Visual only; tab order still follows source. |
/* Classic header: logo left, nav right */
.header {
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
}
/* Sidebar + main content */
.layout {
display: flex;
gap: 1rem;
}
.sidebar { flex: 0 0 240px; } /* fixed 240px */
.main { flex: 1; } /* fills the rest */
Pitfalls:
gapon flex requires modern browsers — use margin on items for very old support (now rarely needed).align-items: stretch(default) means items take container's full cross-axis size — setalign-items: flex-startif you want intrinsic height.- Don't reach for
orderfor major layout; it harms accessibility (keyboard tab order doesn't follow visual order).
5. Grid
2-dimensional layout — rows and columns. Overkill for 1D row-of-buttons (use flex), unbeatable for page-level layout.
.container {
display: grid;
grid-template-columns: 200px 1fr 200px; /* fixed | flexible | fixed */
grid-template-rows: auto 1fr auto;
gap: 1rem;
}
5.1 Track Sizing
| Unit/value | Meaning |
|---|---|
100px |
Fixed length. |
1fr, 2fr |
Fractional unit — distribute remaining space proportionally. |
auto |
Sized by content. |
min-content, max-content |
Smallest / largest content size. |
minmax(MIN, MAX) |
Bounded track. minmax(200px, 1fr) = at least 200px, grow if space allows. |
fit-content(LEN) |
auto capped at LEN. |
repeat(N, TRACK) |
Shorthand. repeat(12, 1fr) = 12-column grid. |
repeat(auto-fit, minmax(200px, 1fr)) |
Responsive — fits as many 200px-min columns as the container allows. |
/* Classic 12-column grid */
.grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: 1rem; }
/* Responsive cards: as many 240px columns as fit, each expands to share space */
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1rem; }
5.2 Placing Items
.item {
grid-column: 1 / 4; /* span columns 1, 2, 3 */
grid-column: span 3; /* span 3 columns from auto-placed start */
grid-row: 2 / 4;
}
Named lines and named areas:
.container {
display: grid;
grid-template-columns: 200px 1fr 200px;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header header"
"sidebar main aside"
"footer footer footer";
gap: 1rem;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.aside { grid-area: aside; }
.footer { grid-area: footer; }
Named areas are excellent for prototypes — the CSS doubles as ASCII art of the layout.
5.3 Alignment
Same vocabulary as flexbox, but with two-axis variants:
| Property | Axis | Acts on |
|---|---|---|
justify-content |
Inline (typically horizontal) | All tracks together — when total grid is smaller than container. |
align-content |
Block (vertical) | Same, for rows. |
justify-items |
Inline | Each item within its cell. |
align-items |
Block | Each item within its cell. |
justify-self, align-self |
per item | Override on one item. |
place-* (e.g. place-items: center) |
Shorthand for align + justify. |
5.4 Subgrid
grid-template-rows: subgrid — child grids inherit tracks from the parent. Lets nested components align with the outer grid. Solid browser support since 2023.
6. Logical Properties
Old (physical) → new (logical) — direction-agnostic, work with writing-mode and direction:
| Physical | Logical |
|---|---|
margin-top |
margin-block-start |
margin-bottom |
margin-block-end |
margin-left |
margin-inline-start |
margin-right |
margin-inline-end |
width |
inline-size |
height |
block-size |
top |
inset-block-start |
left |
inset-inline-start |
Shorthands: margin-block (top+bottom), margin-inline (left+right), inset-block, inset-inline.
Why care: a Hebrew/Arabic translation flips left/right; logical properties flip with it for free. Even for English-only sites they're worth adopting — easier to read (margin-inline: auto vs margin-left: auto; margin-right: auto).
7. Container Queries
Style an element based on its container's size, not the viewport — finally available since 2023.
.card-container {
container-type: inline-size;
container-name: card;
}
@container card (min-width: 400px) {
.card { display: grid; grid-template-columns: 120px 1fr; }
}
Use cases:
- Component library — a card looks different in a sidebar than in a wide column, without knowing where it'll be placed.
- Reusable widgets that adapt to whatever slot they're dropped into.
Style queries (@container style(--theme: dark)) and scroll-state queries (@container scroll-state(stuck: top)) are the next wave — partial support as of 2026.
8. Common Patterns
8.1 Centring
/* Modern, any direction */
.center { display: grid; place-items: center; }
/* Flexbox */
.center { display: flex; justify-content: center; align-items: center; }
/* Horizontal centring of a fixed-width block */
.center { margin-inline: auto; max-width: 60ch; }
8.2 Sticky Footer
body {
min-height: 100dvh; /* dynamic viewport — accounts for mobile chrome */
display: flex;
flex-direction: column;
}
main { flex: 1; } /* pushes footer to the bottom */
8.3 Holy Grail (header / sidebar+main+aside / footer)
.layout {
display: grid;
min-height: 100dvh;
grid-template-columns: 200px 1fr 200px;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header header"
"side main aside"
"footer footer footer";
}
8.4 Responsive Card Grid
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 1.5rem;
}
No media queries needed — auto-fit + minmax adapt automatically.
8.5 Aspect-Ratio Boxes
.video { aspect-ratio: 16 / 9; }
.square { aspect-ratio: 1; }
Pre-2021 pattern (padding-top: 56.25% hack) is obsolete.
9. References
- MDN Layout overview — https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout
- A Complete Guide to Flexbox (CSS-Tricks) — https://css-tricks.com/snippets/css/a-guide-to-flexbox/
- A Complete Guide to Grid (CSS-Tricks) — https://css-tricks.com/snippets/css/complete-guide-grid/
- CSS Grid Level 2 — https://www.w3.org/TR/css-grid-2/
- CSS Containment Module — https://www.w3.org/TR/css-contain-3/