# LAY-4 — Body-text columns cap at a comfortable measure — target ~66ch, never above 80ch (WCAG 1.4.8); full-bleed running text is a finding

L2 · hybrid · Layout

**Fails when:** body-text column has no max-width and spans the full viewport at 1280 · measure exceeds 80ch

## Requirement

Prose and body-text containers must carry an explicit `max-width`. The line measure must
not exceed 80ch (characters). The target is approximately 66ch, grounded in Baymard
Institute research (comfortable range: 50–75 characters per line). Full-bleed running
text — body copy that spans the full viewport width at 1280 px — is always a finding.

This is a *layout* property, complementary to typography (TYP-1..3). A correctly typed
body that runs full-bleed still violates LAY-4.

## Rationale

Long line measures increase return-sweep errors and fatigue readers — critical for
people reading detailed records or lengthy reports. WCAG SC 1.4.8 (Visual
Presentation, Level AAA) recommends ≤80 characters; the harness adopts this ceiling as
a strong default (L2) because it is straightforwardly checkable and because Baymard's
research grounds the 66ch target in empirical reading data.

**Product anchor**: `app/globals.css` in the DX-DS website sets `.prose { max-width: 56ch }`.
A `ch` is the width of the font's zero, and Inter's zero is wider than its average
character, so this cap renders about 70 characters a line, near the 66ch target. The
site's earlier 70ch cap read as inside the ceiling but rendered about 90 characters a
line. Judge the rendered line, not only the declared cap.

## How to verify

**Deterministic half** — the `checks/type-scan.py` measure rule, built in #161. It judges
`ch` caps that are already present against the 80ch ceiling; it never flags a missing cap,
because a container with no declared cap may still render inside the ceiling. It reads a
`max-w-[Nch]` class, a `[max-width:Nch]` arbitrary property, a CSS `max-width: Nch`, a JSX
`style={{ maxWidth: 'Nch' }}`, and a `var()` defined in the same file. A cap in any other
unit gets a NOTE and never an ERROR, because converting it needs the rendered font size.

Where no cap is written, or the cap is not in `ch`: inspect prose containers in the
component source, check for a `max-width` rule, and estimate the rendered measure at
1280 px. Label findings "verified manually."

**Judgment half** — the evaluator:

- Confirms that body-text passages are not full-bleed at 1280 px.
- Flags containers where the rendered measure visibly exceeds 80ch.

## Evaluator guidance

**Flag**:
- Body text that spans the full viewport width at 1280 px (no `max-width`, or
  `max-width: 100%` / `max-width: none` on a prose container).
- Any `max-width` on a prose container that exceeds 80ch in the rendered output.

**Do not flag**:
- Short UI labels, captions, error messages, data-table cells, or button text.
- Full-width layout containers that are structural (grid wrappers, section backgrounds) —
  only flag the inner prose container if it lacks a measure constraint.
- Deliberately wide non-text elements (images, data visualisations, code blocks) that
  are not running prose.
- A 70–80ch measure that is within the acceptable ceiling even if above the 66ch ideal.
