Skip to main content
A Singapore Government Agency Website
LAY-4L2 · recommendedhybridLayout

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

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.

Also available as Markdown · catalog.yaml