Reviewing a database migration the way GitHub reviews code: a real diff of real SQL, a lock analysis of every statement attached as evidence, and one required approval standing between the change and 41.2 million rows.
Direction
Archetype. Shell. The rail is the release week, the top bar is persistent, and one migration fills the well. Someone returns to this tab every day of a release week.
Data stance. Prose-first. The review conversation is the spine. The diff, the lock table, and the checks sit under the sentences that cite them.
Density. Compact. 14px body, 12px metadata, 32px controls, and a 44px hit area on a coarse pointer through --control-minTarget-coarse rather than a taller button.
The one bold thing. State is never carried by hue. Open, merged, and closed each get an icon, a word, and a silhouette of their own: a full pill, a 3px rectangle, an outline with no fill. The rail repeats all three six times, which is what teaches the vocabulary before the reader needs it.
Generated
primer_tokens.py --both > tokens.css
primer_tokens.py --scales > scales.css
primer_tokens.py --theme light-tritanopia
--both emits colour only, so the size, spacing, type, and motion scales come from a second run of the same generator rather than from a hand-written number. Nobody edits either file. The third command is the check behind the bold thing.
What the accessible themes settled
--bgColor-open-emphasis is green #1f883d in light, orange #bc4c00 in light-colorblind, and red #cf222e in light-tritanopia. --bgColor-closed-emphasis goes from red to neutral gray in both accessible themes. --bgColor-done-emphasis stays purple #8250df throughout.
Open against merged measures 1.12:1 in light, 1.00:1 in light-colorblind, and 1.06:1 in light-tritanopia. The two fills are one tone in greyscale in every theme Primer ships, so colour could not separate them under any remapping. The icon, the word, and the silhouette carry the state; the fill only confirms it.
Contrast measured
fgColor-muted on bgColor-muted at 12px reaches 5.74:1 light and 5.94:1 dark. fgColor-onEmphasis on bgColor-open-emphasis at 14px reaches 4.52:1 light and 4.63:1 dark. That second pair is Primer's own tightest required pairing, and it is why the pill label sits at 14px medium rather than 12px.
Target size, and why the line numbers are not links
A diff row measures 19.5px at Primer's --text-codeBlock line height. Eight line-number links stacked at that pitch met neither SC 2.5.8's 24px minimum nor its spacing exemption, and moving the anchor to the whole row would not have helped: the row is the same 19.5px tall and the same 19.5px apart. Growing the pitch to 24px was the other way to pass, and it changes what the page is.
So the line number identifies the line and nothing more. The row keeps its id, so #L14 still resolves and tr:target still marks the line; the links that reach a line live in the sentences that cite it, where SC 2.5.8's inline exception applies and where a prose-first page should have put them anyway. Verified with node tools/a11y-probe.mjs github-primer: no findings, light and dark.
Copy-lint exceptions
copy-lint.ignore holds three rules with the reason for each: operator-copy, because this page is the operator's tool and "migration" is their own word; shouty-caps, because every caps run on the page is SQL; and jargon, because Request changes is the name of the control in every review tool this audience uses.
The rail
The rail carried five destinations and four of them resolved to this document. A shell whose rail does nothing is a frame around a smaller product, so the destinations are gone and the rail is the release week instead: six migrations, one of them this page.
The current one is the only link and carries aria-current="page". The other five are rows, not controls, and that is the honest treatment for a build that is one screen — a control that does nothing is worse than a row that never claimed to. #476 and #479 in the rail are the same two the timeline cites, so the rail and the conversation agree about the week.
Below 1012px the queue is a horizontal snap strip above the page head. It reads as cards rather than as tabs, which keeps it distinct from the section nav underneath it; the previous collapse put two scrolling tab strips one above the other.
The greyscale check
.rail rendered with filter: grayscale(1) and read back: open, merged and closed stay apart with no colour at all. Open and merged are one tone — that is Primer's palette and it is measured above — so the separation is the pill against the 3px rectangle, the icon, and the word. Closed is the outline and separates on lightness as well.
The prose measure
The stance is prose-first and the spine had no measure: at 1920px a body paragraph ran 1310px, about 187 characters. .shell is capped at Primer's own --breakpoint-xxlarge (1400px) and centred, which puts the paragraph at 790px and leaves 1920 reading as 1440 does. The spec panel's own measure is capped at 34rem for the same reason.