---
title: 'Responsive and Adaptive Layout'
source: 'https://academia.sh/en/courses/interface-fundamentals/responsive-and-adaptive-layout'
course: 'Fundamentals of Interface Design'
language: en
updated: '2026-08-17T18:11:07+00:00'
license: 'CC BY-SA 4.0'
---

# Responsive and Adaptive Layout

The difference between continuous and discrete layout approaches; deriving a breakpoint from content criteria and computing the widths that need testing in a continuous grid.

The calculations in the previous two lessons rested on a single container width. The
Grid Systems lesson already showed the limit of this assumption: at a 768-pixel
container, no column division met the content criteria. The Density Decisions lesson, in
turn, assumed a single fixed viewport height.

Both findings lead to the same question: what does a layout do if the container's width
is not a number the designer chooses but a variable determined at the moment of use?
This lesson distinguishes the two approaches that answer this question differently and
computes where transition points are derived from.

## Continuous Layout and Discrete Layout

**Responsive layout** is the approach that treats container width as a continuous
variable. Measures are given as ratios with minimum and maximum bounds; the layout is
defined at every intermediate width.

**Adaptive layout** is the approach in which separate layouts are designed for a handful
of predetermined widths. Intermediate widths round to the nearest layout; the layout
exists in one of a finite number of states.

The difference is not an implementation detail, it is the design contract itself. In the
responsive approach, the designer designs **rules** and leaves the outcome of every
intermediate width to the rule. In the adaptive approach, the designer designs **states**
and directly controls the appearance of every state.

Two asymmetries follow from this. The responsive approach requires less work but gives
less control: at a width nobody has looked at, the layout is still defined, but whether
it is correct is not guaranteed. The adaptive approach requires more work but keeps
control complete: every state has been designed individually, and there is no undesigned
state.

## The Breakpoint Is Derived from Content

In both approaches, the layout has to change shape at some point. This transition width
is called a **breakpoint**.

The common way breakpoints are chosen is a list of device widths. This method does not
survive five years, and it asks the wrong question to begin with: the issue is not how
wide the device is, but how much width the content needs. The catalog interface's two
criteria were set in the Grid Systems lesson: the filter panel needs at least 240 pixels,
the results list at least 480 pixels.

The program below searches for the smallest container width that satisfies these two
criteria, then derives a continuous card grid's column count from width, and finds the
exact widths at which column count changes.

```js
// 09-responsive.mjs — deriving the breakpoint from content and continuous column count

const MARGIN = 24;
const GUTTER = 24;
const COLUMN_COUNT = 12;

const FILTER_MIN = 240; // filter labels fit without wrapping
const LIST_MIN = 480; // record titles keep a readable length

function columnWidth(container) {
  return (container - 2 * MARGIN - (COLUMN_COUNT - 1) * GUTTER) / COLUMN_COUNT;
}
function span(n, column) {
  return n * column + (n - 1) * GUTTER;
}

// 1) The smallest container width at which a two-column layout can stand.
let smallest = null;
let smallestSplit = null;
for (let k = 600; k <= 1200; k++) {
  const s = columnWidth(k);
  for (let left = 2; left <= 6; left++) {
    if (span(left, s) >= FILTER_MIN && span(COLUMN_COUNT - left, s) >= LIST_MIN) {
      smallest = k;
      smallestSplit = [left, COLUMN_COUNT - left];
      break;
    }
  }
  if (smallest !== null) break;
}
console.log("1) the threshold for a two-column layout");
console.log(`   criteria: filter >= ${FILTER_MIN}px, list >= ${LIST_MIN}px`);
console.log(`   smallest container: ${smallest}px   split: ${smallestSplit.join("+")} columns`);
const s0 = columnWidth(smallest);
console.log(
  `   at this width: filter ${span(smallestSplit[0], s0).toFixed(1)}px, ` +
    `list ${span(smallestSplit[1], s0).toFixed(1)}px`
);
console.log(`   the commonly cited 768px from device lists is ${smallest - 768}px below this threshold`);

// 2) Continuous column count: card grid minimum width 220px, gap 24px.
const CARD_MIN = 220;
const GAP = 24;
function cardCount(container) {
  return Math.max(1, Math.floor((container + GAP) / (CARD_MIN + GAP)));
}
console.log("");
console.log("2) card grid: minimum card width 220px, gap 24px");
console.log("   container  card count  card width");
for (const k of [360, 480, 720, 960, 1200, 1440]) {
  const n = cardCount(k);
  const width = (k - (n - 1) * GAP) / n;
  console.log(
    `   ${String(k).padStart(9)}  ${String(n).padStart(11)}  ${width.toFixed(1).padStart(14)}px`
  );
}

// 3) Exact widths at which column count changes: W + gap = n * (min + gap)
console.log("");
console.log("3) widths at which column count changes (values to test)");
for (let n = 2; n <= 6; n++) {
  const threshold = n * (CARD_MIN + GAP) - GAP;
  console.log(`   transition to ${n} columns: ${threshold}px  (verification: ${cardCount(threshold - 1)} -> ${cardCount(threshold)})`);
}
console.log(`   integer widths between 320 and 1440: ${1440 - 320 + 1}`);
console.log("   widths that need testing: 5 transitions + 2 endpoints = 7");
```

```
1) the threshold for a two-column layout
   criteria: filter >= 240px, list >= 480px
   smallest container: 816px   split: 4+8 columns
   at this width: filter 240.0px, list 504.0px
   the commonly cited 768px from device lists is 48px below this threshold

2) card grid: minimum card width 220px, gap 24px
   container  card count  card width
         360            1           360.0px
         480            2           228.0px
         720            3           224.0px
         960            4           222.0px
        1200            5           220.8px
        1440            6           220.0px

3) widths at which column count changes (values to test)
   transition to 2 columns: 464px  (verification: 1 -> 2)
   transition to 3 columns: 708px  (verification: 2 -> 3)
   transition to 4 columns: 952px  (verification: 3 -> 4)
   transition to 5 columns: 1196px  (verification: 4 -> 5)
   transition to 6 columns: 1440px  (verification: 5 -> 6)
   integer widths between 320 and 1440: 1121
   widths that need testing: 5 transitions + 2 endpoints = 7
```

The first block gives the breakpoint: 816 pixels. This number was not chosen; it came out
of two content criteria and the grid arithmetic. The 768 pixels that recurs in device
lists is 48 pixels below this threshold; if a two-column layout were built at that width,
either the filter panel or the results list would drop below its own minimum width.

A derived breakpoint has one more advantage: when content changes, the breakpoint changes
with it. If filter labels grow longer, the minimum width grows, the calculation is
rerun, and a new threshold is found. A number taken from a device list silently becomes
wrong when content changes.

Once the breakpoint is found, the layout can be expressed with a single rule:

```css
/* Catalog results page: the two-column layout is built only above the threshold. */
.catalog {
  display: grid;
  gap: 24px;
  padding: 0 24px;
  grid-template-columns: 1fr;
}

@media (min-width: 816px) {
  .catalog {
    grid-template-columns: minmax(240px, 1fr) minmax(480px, 2fr);
  }
}
```

The 240 and 480 values in the declaration are exactly the criteria that produced the
breakpoint; the `1fr` and `2fr` ratio comes from the 4+8 column division. Below the
threshold, a single column is built and the filter panel drops above the list.

## A Continuous Layout May Not Need a Breakpoint

The second block shows another route. In a card-shaped results list, column count can be
derived directly from width instead of through breakpoints. Under the rule, the maximum
number of cards that fit the container is computed, and the remaining space is
distributed among the cards.

In the output's second block, card widths are 360, 228, 224, 222, 220.8, and 220 pixels.
As width grows, card count grows, and card width approaches its minimum value. This is
the characteristic behavior of a continuous layout: it stays undefined at no width, and
it violates the criterion at no width.

The rule here depends on three numbers — minimum card width, gap, and container — and
the designer chooses only the first two. The breakpoint decision disappears. This is not
always possible: it works in layouts like a card grid, where **elements of the same kind
repeat**, but not in layouts like the filter panel and results list, where **elements of
different kinds stand side by side**, because the two have separate minimum widths and
neither can substitute for the other.

## A Continuous Layout Does Not Require Infinite Testing

The common objection raised against continuous layout is testing cost: a layout defined
at every width appears to need testing at every width. There are 1121 integer widths
between 320 and 1440 pixels.

The third block shows this objection is wrong. Column count changes at only five widths:
464, 708, 952, 1196, and 1440 pixels. These values are exact by rule; the program
verifies each transition by computing column count one pixel below and exactly at it. At
widths between two transitions, the layout's structure stays the same, and only card
width changes continuously.

The set of widths that needs testing therefore has seven elements: five transition points
and two endpoint values. A continuous layout defines infinite states with a finite number
of transitions; testing is done at those transitions. In the adaptive approach, by
contrast, the number of states to test equals the number of designed layouts, and the
testing burden grows linearly as the number of layouts grows.

## Which One, When

The two approaches are not competitors; the criterion is what changes when width changes.

If what changes is only the **arrangement** — the same elements, in a different order —
the responsive approach is enough, and it is cheaper. The card grid dropping from three
columns to two is like this; no element is added or removed.

If what changes is the **content**, the adaptive approach is needed. At a narrow
viewport, the filter panel does not drop above the list — it moves into a dropdown panel;
the borrow action is removed from the record row and left to the detail view. These
cannot be expressed with a ratio, because the element set is different.

In practice, the two are used together: two or three discrete states are defined, and
within each state, measures are computed continuously. The catalog interface takes this
form — a discrete transition at the 816-pixel threshold, with continuous behavior on both
sides of it.

## Summary

- Responsive layout treats width as a continuous variable and designs rules; adaptive
  layout defines a finite number of states and designs each one directly.
- A breakpoint is derived from the content's minimum width requirements, not from device
  widths; the catalog interface's threshold computes to 816 pixels, and the common value
  of 768 is 48 pixels below this threshold.
- A derived breakpoint can be recomputed when content changes; a breakpoint taken from a
  list silently becomes wrong when content changes.
- In grids where elements of the same kind repeat, column count can be derived directly
  from width, and the breakpoint decision disappears.
- A continuous layout does not require infinite testing: structure changes only at the
  widths where column count changes, and in the measured example, 7 values are tested
  instead of 1121 widths.
- If only the arrangement changes when width changes, the responsive approach is enough;
  if the element set changes, the adaptive approach is needed, and the two are most often
  used together.

## Next Step

This lesson treated width as the only variable. But a narrow viewport is not only
narrow: input type often changes with it too, touch takes the place of the pointer, and
target size gets redefined. The Density Decisions lesson found that a 33-pixel row
passed the lowest threshold but fell short of the enhanced one. The next lesson computes
these thresholds by criterion number, tests the icon buttons in the catalog toolbar
against target size and spacing exception criteria, and shows how pointer-dependent
interactions go unanswered under touch.
