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

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 is horizontal-tb (top-to-bottom lines, left-to-right characters in LTR languages).
  • Inline elements form line boxes. line-height controls their height; vertical-align controls 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: 11 1 0%. flex: auto1 1 auto. flex: none0 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:

  • gap on 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 — set align-items: flex-start if you want intrinsic height.
  • Don't reach for order for 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; }
body {
  min-height: 100dvh;       /* dynamic viewport — accounts for mobile chrome */
  display: flex;
  flex-direction: column;
}
main { flex: 1; }            /* pushes footer to the bottom */
.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