# Responsive

> The four breakpoints, how the 12-column grid collapses, touch-target sizes, and the mobile navigation pattern.


The rest of the foundations describe values that do not change with viewport
width. This page describes the four points at which the layout does.

## Breakpoints

Four breakpoints, declared in
the synchronized design-system token sheet and
normative here:

| Name | Min width | The layout it describes |
|---|---|---|
| `sm` | 640px | Large phone, landscape phone |
| `md` | 768px | Small tablet — the first two-column layout |
| `lg` | 1024px | Tablet landscape, small laptop — sidebars appear |
| `xl` | 1280px | Desktop — the 1100px measure is fully margined |

Below `sm` is not a breakpoint; it is the **base**. Write the narrow layout
first and add width with `min-width` queries, so an unstyled or unsupported
viewport gets the single-column layout rather than a clipped desktop one.

```css
/* Base — single column, 360px and up. */
.panel-row { display: grid; gap: var(--space-4); }

@media (min-width: 768px) {
  .panel-row { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
  .panel-row { grid-template-columns: 1.5fr 1fr; }
}
```

{{< demo label="Breakpoint ruler — base · 640 · 768 · 1024 · 1280" variant="stack" >}}
<div style="display:flex;align-items:stretch;gap:2px;font-size:.6875rem;line-height:1.4">
  <div style="flex:64;background:var(--pj-midnight-2);color:var(--pj-body-fg);padding:.5rem .375rem;border-radius:3px 0 0 3px">base<br><span>1 col</span></div>
  <div style="flex:13;background:var(--pj-midnight-3);color:var(--pj-body-fg);padding:.5rem .375rem">640<br><span>1 col</span></div>
  <div style="flex:26;background:var(--pj-midnight-6);color:var(--pj-body-fg);padding:.5rem .375rem">768<br><span>2 col</span></div>
  <div style="flex:26;background:var(--pj-midnight-9);color:#fff;padding:.5rem .375rem">1024<br><span style="color:#c3d1e3">+ sidebar</span></div>
  <div style="flex:20;background:var(--pj-midnight-9);color:#fff;padding:.5rem .375rem;border-left:3px solid var(--pj-orange-9);border-radius:0 3px 3px 0">1280<br><span style="color:#c3d1e3">margined</span></div>
</div>
{{< /demo >}}

## How the grid collapses

The grid is 12 columns inside the
[1100px measure](/docs/foundations/space-shape-motion/). It does
not stay twelve columns all the way down — it resolves to four column counts:

| From | Columns | Gutter | Page padding |
|---|---|---|---|
| base | 4 | 16px | 16px |
| `md` 768px | 8 | 16px | 24px |
| `lg` 1024px | 12 | 24px | 32px |
| `xl` 1280px | 12 | 24px | auto — the measure caps at 1100px |

**Below `md`, every multi-column region becomes one column.** There is no
two-column layout on a phone: two 160px columns are two unreadable columns.
Regions stack in **source order**, so the DOM must already be in reading order
— do not rely on `order` or `grid-area` to fix a sequence that is wrong in the
markup, because that breaks the [focus and reading
order](/docs/interface/accessibility/).

Spacing steps **down** one rung when the grid collapses: a `--space-6` (32px)
section gap on desktop becomes `--space-5` (24px) below `md`. Radius, type
scale, and border weights do **not** change — a card is a 9px card at every
width.

{{% callout title="The 1100px measure is a maximum, not a target" type="info" %}}
Wider viewports gain margin, not line length. At `xl` the content stays 1100px
wide and centres; it does not grow to fill a 1920px display. This is the same
rule the spacing page states, restated here because it is the most common thing
a responsive rewrite breaks.
{{% /callout %}}

## Touch targets

**44×44px minimum** for anything tappable, at every viewport — the floor is a
finger, not a breakpoint.

The default control heights are 32 / 40 / 48px, so the `sm` and `md` sizes are
**below** the floor on their own. Two ways to resolve it, in order of
preference:

1. **Grow the hit area, not the control.** Keep the 40px visual button and give
   it a transparent 2px vertical extension, or wrap it in a 44px-tall row. The
   measurement stays normative; the target clears the floor.
2. **Use `lg` (48px) on touch-primary surfaces.** Correct for the primary action
   on a phone, where a 40px button next to a 48px one reads as demoted.

Never shrink to `sm` (32px) for a touch target. Spacing between adjacent targets
is at least `--space-2` (8px), so a mis-tap does not fire the neighbour.

```css
/* Preferred: 40px control, 44px target. */
.pj-btn--md {
  min-height: 40px;
  position: relative;
}
.pj-btn--md::after {
  content: "";
  position: absolute;
  inset: -2px 0;   /* 40 + 2 + 2 = 44 */
}
```

## Mobile navigation

**Use a bottom tab bar for application surfaces. Use a top drawer for
documentation and marketing surfaces.** The choice follows the shape of the
navigation, not the shape of the device:

- An **application** has a small, flat set of destinations — the
  dashboard mockup has five.
  Five or fewer flat destinations fit a tab bar, which keeps the current
  location permanently visible, sits in the thumb arc, and costs no taps to
  reach. That is worth the 56px of permanent screen it occupies.
- **Documentation** has a deep, nested tree. A tab bar cannot express it, and
  flattening the tree to fit one is worse than a drawer. A drawer behind a
  labelled disclosure control costs one tap and can show the whole hierarchy.

The failure mode is a tab bar with six or more items, or one that hides the
primary destination behind "More". If the destinations do not fit, the surface
is a drawer surface.

Both patterns carry the same obligations: the current destination is marked with
**an accent underline or fill plus the label**, never colour alone; the drawer
traps focus while open, closes on `Esc`, and returns focus to its trigger — the
same contract as a [modal](/docs/interface/components/).

{{< demo label="Bottom tab bar — 5 destinations, current marked with fill and label" variant="stack" >}}
<div style="max-width:340px;border:1px solid var(--pj-border);border-radius:9px;overflow:hidden;background:var(--pj-surface)">
  <div style="height:88px;background:var(--pj-surface-subtle);display:flex;align-items:center;justify-content:center;color:var(--pj-text-muted);font-size:.75rem">screen content</div>
  <div style="display:flex;border-top:1px solid var(--pj-border)">
    <span style="flex:1;min-height:44px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;font-size:.625rem;color:var(--pj-orange-11);border-top:2px solid var(--pj-orange-9);font-weight:600">Overview</span>
    <span style="flex:1;min-height:44px;display:flex;align-items:center;justify-content:center;font-size:.625rem;color:var(--pj-text-muted)">Agents</span>
    <span style="flex:1;min-height:44px;display:flex;align-items:center;justify-content:center;font-size:.625rem;color:var(--pj-text-muted)">Alerts</span>
    <span style="flex:1;min-height:44px;display:flex;align-items:center;justify-content:center;font-size:.625rem;color:var(--pj-text-muted)">Account</span>
  </div>
</div>
{{< /demo >}}

## Tables, code, and diagrams

Three content types cannot reflow, and each has one answer:

- **Tables** scroll horizontally inside their own container with the first
  column pinned — never the page. See [wide
  tables](/docs/interface/components/).
- **Code blocks** scroll horizontally. Do not soft-wrap code: a wrapped line
  changes what the code appears to say.
- **Diagrams** get a scrollable container or a re-drawn narrow variant. Do not
  scale a diagram down until its labels are unreadable — an 8px label is not a
  responsive diagram, it is a broken one.

## Worked example

The supplied **mobile mockup** shows
three screens in a device frame: an onboarding panel on midnight, an engagement
list of stacked cards, and a finding-review screen with two side-by-side
actions. It is the dashboard
below `md`: same tokens, same components, one column, tab bar instead of
sidebar.

{{< rules >}}
{{% do %}}
Write the base layout first and add width with `min-width` queries. Put regions
in the DOM in reading order. Give every tappable thing a 44px target.
{{% /do %}}
{{% dont %}}
Keep two columns below 768px, reorder regions with CSS to fix source order,
scale a diagram until its labels are illegible, or let a wide table scroll the
whole page.
{{% /dont %}}
{{< /rules >}}


---
Source: https://projectious-work.github.io/brand/v3.0.0/docs/foundations/responsive/index.md
