Layout

Modular & adaptive UI

Build a page once out of reusable modules, then let it adapt to the screen and the context it's viewed in. Modular keeps things consistent and fast to build; adaptive and contextual UI make it feel right on every device, for every person. Every example here is live.

Start here

Three ideas, one goal

These three terms get mixed up constantly. Here's the plain-English difference — and how they work together.

Modular

The page is assembled from reusable blocks — cards, sections, media objects — built on a shared grid and spacing scale. Compose, don't hand-craft.

Adaptive / responsive

The layout responds to the available space — reflowing columns, resizing type — so one build looks right from phone to desktop.

Contextual

The UI changes based on who's looking and how — input type, dark mode, motion preference, connection, even returning vs new users.

Modular

Modular layouts — build from blocks

A module is a self-contained block you reuse anywhere. Define them once and the page becomes a set of Lego pieces. The key tools are CSS Grid for the arrangement and a fixed scale for spacing and type.

Auto-fitting card modules

One rule lays out any number of cards and wraps them automatically — no media queries needed.

Module
Module
Module
Module
Module
Module
CSS
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 12px;                 /* fits as many 150px+ columns as space allows */
}

A bento grid — modules of different sizes

Give some modules span classes to break the uniform grid into a lively, magazine-like arrangement.

Featured
A
B
Wide
Tall
C
D
HTML
<div class="bento">
  <div class="b-big">Featured</div>
  <div>A</div><div>B</div>
  <div class="b-wide">Wide</div>
  <div class="b-tall">Tall</div>
  <div>C</div><div>D</div>
</div>
CSS
.bento { display: grid; grid-template-columns: repeat(4, 1fr); grid-auto-rows: 70px; gap: 10px; }
.b-wide { grid-column: span 2; }
.b-tall { grid-row: span 2; }
.b-big  { grid-column: span 2; grid-row: span 2; }

A modular scale keeps it consistent

Pick spacing and type from a fixed set of steps — not random pixels. Every module then lines up.

step 0 · 1remThe quick brown fox
step 1 · 1.25remThe quick brown fox
step 2 · 1.563remThe quick brown
step 3 · 1.953remThe quick
CSS
:root {
  --ratio: 1.25;                         /* a "major third" scale */
  --step-0: 1rem;
  --step-1: calc(var(--step-0) * var(--ratio));   /* 1.25rem  */
  --step-2: calc(var(--step-1) * var(--ratio));   /* 1.563rem */
  --step-3: calc(var(--step-2) * var(--ratio));   /* 1.953rem */
}
h3 { font-size: var(--step-1); }   /* every size comes from the scale */
Related: see Grid Layout Examples for module patterns and Design Tokens / Design Systems for turning a scale into reusable variables.
Adaptive

Adaptive UI — respond to the space

Responsive layouts react to the viewport. Modern CSS goes further: components can react to their own container, and type can scale smoothly instead of jumping at breakpoints.

Container queries — components adapt to their box

Drag the resize handle at the bottom-right of the box. The same card flips from stacked to side-by-side based on the container's width — not the screen's.

I adapt to my container

Narrow = stacked. Wide enough = image beside text. Resize the dashed box →

Drag the bottom-right corner of the dashed box
CSS
.wrap { container-type: inline-size; }   /* make .wrap a query container */

.card { display: grid; gap: 12px; }       /* stacked by default */

@container (min-width: 420px) {           /* when the CONTAINER is wide enough */
  .card { grid-template-columns: 160px 1fr; align-items: center; }
}

Fluid type with clamp()

Type that scales smoothly between a min and max as the window resizes — no breakpoint jumps. Resize your browser to see it move.

This headline scales with the viewport
CSS
h1 { font-size: clamp(1.4rem, 5vw, 3rem); }
/*                 ↑min   ↑grows  ↑max
   never smaller than 1.4rem, never bigger than 3rem,
   fluid in between (5% of viewport width) */

Progressive disclosure

Show the essentials; reveal detail on demand. Adapts the amount of UI to what the user needs right now. (Click to expand.)

What's included in the plan?

Unlimited projects, a custom domain, and priority support. Native <details> needs zero JavaScript — the browser handles the toggle and it's keyboard-accessible by default.

HTML
<details>
  <summary>What's included in the plan?</summary>
  <p>Unlimited projects, a custom domain, and priority support.</p>
</details>
<!-- collapsible, accessible, no JS required -->
Contextual

Contextual UI — respond to the person

The most thoughtful interfaces adjust to the situation, not just the screen size. CSS media queries can read a surprising amount of context — and you respond to it with the same tools you already know.

Color scheme

prefers-color-scheme tells you if the user wants dark mode — honor it automatically.

Motion

prefers-reduced-motion lets people who get dizzy turn off your animations.

Input type

pointer: coarse means a finger — make tap targets bigger; fine means a mouse.

Data & connection

prefers-reduced-data & the Network API let you serve lighter pages on slow or metered connections.

The user

New vs returning, signed-in vs guest, their saved preferences — show the UI that fits where they are.

Locale & time

Language, currency, date format, even a light/dark greeting by time of day — small touches that feel personal.

Live: this card respects your OS color scheme

If your device is set to dark mode, the card below is dark; in light mode it's light — with no toggle and no JavaScript.

I match your system theme automatically
CSS
.card { background: #f8fafc; color: #1d1d1f; }

@media (prefers-color-scheme: dark) {
  .card { background: #1e293b; color: #e2e8f0; }
}

/* bigger touch targets for finger input */
@media (pointer: coarse) {
  .btn { min-height: 48px; padding: 14px 22px; }
}
Go deeper: for a full light/dark toggle (with a saved preference) see Dark / Light Mode, and for motion preferences see Web Page Motion.
Put it together

How to build this way

Define your scale & grid

Lock in a spacing scale, a type scale, and a base grid. Everything else snaps to these.

Build modules, not pages

Create reusable blocks — card, media object, section, hero — styled only from your tokens.

Compose the page

Assemble screens from modules on the grid. New layouts become rearranging blocks, not rewriting CSS.

Make modules adapt

Use auto-fit/auto-fill, container queries, and clamp() so each block flexes on its own.

Layer in context

Honor color scheme, motion, and input preferences. Add personalization where it genuinely helps.

Reference

Characteristics & components

The signals of a modular, adaptive build:

Responsive layouts Reusable sections Mobile-first

Components to build

A responsive grid, an adaptive card, a flexible nav, and a mobile menu — live first, then the code.

1
2
3
4
Adaptive card

Image beside text when there's room; stacks when narrow.

CSS
/* responsive grid — wraps with no media queries */
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); gap: 8px; }

/* adaptive card — flexes image beside text, stacks when narrow */
.card { display: flex; flex-wrap: wrap; gap: 12px; }
.card .text { flex: 1; min-width: 120px; }

/* flexible nav + mobile menu button */
.nav { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; }
.nav .links { display: flex; gap: 14px; }
.nav .burger { display: none; }
@media (max-width: 600px) {
  .nav .links { display: none; }      /* hide links on small screens */
  .nav .burger { display: block; }    /* show the hamburger */
}
HTML
<!-- responsive grid -->
<div class="grid">
  <div>1</div><div>2</div><div>3</div><div>4</div>
</div>

<!-- adaptive card -->
<div class="card">
  <div class="thumb"></div>
  <div class="text">
    <strong>Adaptive card</strong>
    <p>Image beside text when there's room; stacks when narrow.</p>
  </div>
</div>

<!-- flexible nav + mobile menu -->
<nav class="nav">
  <strong>Flex</strong>
  <div class="links"><a>Home</a><a>Docs</a></div>
  <button class="burger" aria-label="Menu">☰</button>
</nav>
In practice

Do & don't

Do

  • Build reusable modules on a shared grid
  • Size everything from a spacing & type scale
  • Use auto-fit + container queries to skip breakpoints
  • Honor prefers-color-scheme & reduced-motion
  • Make touch targets bigger on pointer: coarse
  • Reveal detail progressively

Don't

  • Hand-craft every page from scratch
  • Use random one-off pixel sizes
  • Pile up dozens of fragile breakpoints
  • Force one theme on everyone
  • Ship desktop-only tap targets to phones
  • Dump every option on screen at once
Keep going: pair this with Grid, Flexbox, Responsive Web Design, and Design Systems.
Recap

The takeaway

Modular = build once from reusable blocks on a shared scale and grid. Adaptive = let those blocks respond to the space with auto-fit, container queries, and clamp(). Contextual = respond to the person with prefers-color-scheme, reduced-motion, pointer, and smart personalization. Together they turn one careful build into an interface that feels right everywhere, for everyone.