Skip to content
academia.sh

Lesson 09 / 22

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.

Contents

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.

// 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:

/* 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.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ Esc navigate · open · close