Foundations

Grid

The layout is a Mondrian: CSS Grid on a black page, and the gaps between the panels are the black lines. Nothing draws a line; the grid leaves it.

This frame:

How a block is made

  1. The block is a grid on --black, with gap: var(--frame) and the same width of black around it (padding: 0 var(--frame) var(--frame): the block above provides the top edge).
  2. Its layout is named, in grid-template-areas; each tier changes the areas and tracks, never the markup.
  3. Each cell is a panel coloured by one bg-* class and named by data-cell: the block’s prefix plus the area it fills.
  4. Side columns are 14cqi; decorative rows stay short (108–130px on desktop, 56–76px on mobile) so the content panel dominates; every block is at least one screen tall.
  5. Many blocks then move their tracks with the scroll (a view() timeline animating grid-template-rows or -columns), so the lines shift as you read; under reduced motion they hold still.
<section class="sec sec-a">
  <div class="p bg-blue" data-cell="sec01-panel">…</div>
  <div class="s1 bg-paper" data-cell="sec01-s1"></div>
  <div class="s2 bg-paper" data-cell="sec01-s2"></div>
  <div class="s3 bg-yellow" data-cell="sec01-s3"></div>
</section>

.sec {
  display: grid;
  gap: var(--frame);
  padding: 0 var(--frame) var(--frame);
  background: var(--black);
  min-height: var(--screen);
}
.sec-a {
  grid-template-columns: 1fr 14cqi;
  grid-template-rows: 1fr 1fr 108px;
  grid-template-areas: "panel s1" "panel s2" "panel s3";
}

Cell maps

Every block’s desktop grid, drawn from its areas and named by its cells (in proportion, not to scale). Mobile layouts and each block’s scroll motion are in the cell map of DESIGN.md.

Hero .hero: columns 1fr 14cqi, rows --bar 1fr 130px
hero-yellow hero-yellow
hero-title hero-light
hero-sub hero-blue
About .about: columns 14cqi 1fr 14cqi, rows 108px 1fr 92px
about-s1 about-text about-text
about-s2 about-text about-text
about-base about-base about-base
01 Accessibility .sec-a: columns 1fr 14cqi, rows 1fr 1fr 108px
sec01-panel sec01-s1
sec01-panel sec01-s2
sec01-panel sec01-s3
02 System Continuity .sec-b: columns 14cqi 1fr, rows 108px 1fr 1fr
sec02-s1 sec02-panel
sec02-s2 sec02-panel
sec02-s3 sec02-panel
03 Performance .sec-c: columns 1fr 1fr 14cqi, rows 1fr 120px
sec03-panel sec03-panel sec03-panel
sec03-s1 sec03-s2 sec03-s3
04 UX/UI Cohesion .sec-d: columns 1fr, rows 1fr
sec04-panel
05 Writing .sec-e: columns 2fr 1fr 1fr 14cqi, rows auto 1fr 1fr
sec05-panel sec05-panel sec05-panel sec05-s1
sec05-b1 sec05-b2 sec05-b3 sec05-s2
sec05-b1 sec05-b4 sec05-b4 sec05-s3
Footer .foot: columns 1fr 14cqi, rows 1fr 56px
foot-main foot-r
foot-main foot-b
About page head .ap-head: columns 14cqi 1fr 9cqi 5cqi, rows 18% 1fr 1fr 18% of a screen
ap-head-s1 ap-head-s2   ap-head-s3 ap-head-s3
ap-head-s4 ap-head-main ap-head-s5 ap-head-s6
ap-head-s4 ap-head-main ap-head-s7 ap-head-s7
ap-head-s8 ap-head-s9   ap-head-s9 ap-head-s10
Accessibility statement .as: columns 14cqi 1fr 1fr 6cqi, rows 60%, 40%, 40% of a screen (at least)
as-s1 as-head   as-head     as-s4
as-s2 as-commit as-testing  as-s5
as-s3 as-issues as-feedback as-s5

Breakpoints

Every breakpoint is a container query on the page (container-type: inline-size on <body class="page">), so widths are the page’s, not the window’s. The one viewport media query is short, for phones in landscape.

TierWidthReferenceGrids
mobile≤ 760iPhone 13, 390 × 844mobile
tablet760–1024iPad portrait, 820 × 1180mobile, for now (the hero, i1 and i2 desktop)
tablet-l1024–1280iPad landscape, 1180 × 820desktop
desktop1280–1440Laptops, 1280 × 800desktop
desktop-l1440–19201440 × 900, the design referencedesktop
desktop-xl≥ 19201920 × 1080 and updesktop, larger type
shortheight ≤ 500, landscapeiPhone 17 landscape, 874 × 402its width tier’s, sized to the height
@container (max-width: 760px) { … }          /* mobile */
@container (760px < width < 1024px) { … }    /* tablet only */
@container (width >= 1024px) { … }           /* tablet-l and up */
@container (width >= 1280px) { … }           /* desktop and up */

Bound a rule on both ends when it must stop at a tier: an open-ended query leaks into every tier beyond it.