The rest of the roadmap: a second ground, real shadows, one table owner, arrow keys, tests, one brand colour - #6
Merged
Conversation
Sixteen sections shared one background. Over roughly ten thousand pixels of scroll the only rhythm device was hairline density, so a reader could not tell from peripheral vision whether they had moved from Architecture to Agents to Mobile — the difference between restrained and monotonous. `--card`, `--secondary` and `--accent` were declared in the stylesheet and used zero times, which is how long this had been the plan. Two grounds, not five. `raised` goes only to the four sections that already read as inset: Safe Mode, the depth index, the prices and the questions. The token is checked, not assumed. Every foreground was measured against the new surface: the tightest is `--muted-foreground-subtle` at 4.61:1, which still clears AA. `[data-tone="raised"]` has to sit after `.dark` in source order — both are specificity (0,1,0) and the tie breaks on position — and it needs only one rule because `--surface-raised` is itself theme-swapped. Pricing is wrapped in `SectionShell` to get there, which it should have been all along. It was the one landing section that rendered its own header, and it had already drifted: extra spacers the shell has no concept of, and a headline missing `text-pretty`. Every future shell change had been silently skipping the highest-intent section on the page.
`shadow-sm` is a 3px blur. On an image rendered 1216px wide that is an order of magnitude too small, which is why the hero read as something laid on the page rather than a window sitting above it. Nine images across three files used it. The replacement is a three-layer ramp, which is the shape every reference Mac-app site converged on independently: a tight contact shadow, a mid lift, and one large soft ambient. Warm-tinted rather than neutral black, so it sits in the page's own light instead of muddying it. `drop-shadow`, not `box-shadow`, because the captures carry real transparent macOS squircle corners — I checked, the shipped PNGs have an alpha channel — so the shadow follows the actual silhouette. A box-shadow would draw a rectangle behind a rounded image. The border and radius come off for the same reason: the image already has its own corners, and `border-radius` is a circular arc tracing a continuous curve, which is visibly wrong where the two meet.
Three sections carry real tabular data and each hand-rolled its own table shell.
That is tolerable where they genuinely differ — the spec result set, the Safe
Mode ladder and the plan comparison have different typography and density on
purpose, and a primitive that parameterised all of it would be harder to read
than the markup it replaced.
What was not tolerable is that they had already disagreed on the thing the
hairline system exists to settle. Columns are stable structure and take the
heavier rule; rows are data and take the hairline. The spec table did that. The
plan comparison drew its column separators with the row weight — so the one
table where the columns carry the entire meaning was the one drawing them
faintest.
`DataTable` owns exactly that: the shell, the two weights by name, and a
required `caption`. Requiring the caption is the only reliable way to keep it
true; a table without one gives a screen reader no idea what it is about to read
cells from.
The interpolation in spec-strip is worth noting because it typechecks either
way: two `className="…${TOKEN}…"` strings were double-quoted rather than
template literals, so they would have shipped the literal text as a class name.
Caught by grepping the compiled stylesheet for the token names.
Twenty-seven tiles, and reaching the one you want meant Tab twenty-seven times. Arrow keys now move between them, up and down by a whole row, Home and End to the ends. The column count is read off `COLS` at the current breakpoint so the two can never disagree. Purely additive: every tile stays in the tab order. The usual composite-widget pattern would take them all out and leave one, but that needs `role="grid"` with `row` and `gridcell` children before assistive technology can make sense of what happened — and this is one flat CSS grid whose borders are computed by index, so row wrappers would break the layout outright. Adding the roles without the structure would describe a widget that is not there, which is worse than describing nothing. Focus clamps at the ends rather than wrapping. Wrapping a two-dimensional grid on Left at the start of a row lands you at the end of the previous one, which reads as focus jumping backwards for no reason. The other two grids the plan named do not get this: `depth-grid` and `faq` contain zero interactive elements, so there is nothing to move between.
Nothing shipped in the last four PRs had a test. That was survivable while the `ssr` job did not run; it is not survivable now that it does, because the job is the only thing that sees the rendered DOM and it was checking the page as it looked before any of this. Nine assertions, all on the real server-rendered output, all on things a typecheck cannot see: Tailwind silently drops an unknown utility, a CSS counter class is just a string, and a section that stops rendering leaves no trace in `tsc`. Each one guards a defect this work actually shipped and had to repair: - Every full-bleed rule carries the counter, because AccentLine did not and the first rule of every section went unnumbered. - `rule-inset-host` is present, because without it half the ordinals rendered 32px off and inside the content column. - At least four routes to /download exist, because the page once offered two with three thousand words between them. - Both blocking objections precede the feature tour, and the AI answer sits between Agents and Safety rather than after both. - The page has more than one ground. - Agents is adjacent to Safety, and Mobile follows Pricing. - `data-row` appears on more than twenty elements, because it once appeared on two that could not take focus. - Plan availability exists as words, because the checkmark is aria-hidden and every included feature announced as an empty cell.
…ser chrome Four values claimed to be the brand colour: `--primary` at #f68001, a `theme-color` meta at #FFAA46, #ffaa46 across three OG templates, and a manifest declaring #000000. None of them agreed and none referenced the others. `theme-color` was wrong in kind, not just in value. It tints the browser's own chrome, so an accent there paints the address bar a colour that appears nowhere on the page — and a single value cannot be right for a site that follows the OS. It is now two media-scoped tags carrying the actual backgrounds, #ffffff and #0a0a0a, both computed from the tokens rather than picked. The manifest follows. The OG templates keep #ffaa46 deliberately. It corresponds to the dark theme's `--primary-strong`, which computes to #ffa65e — 10.4:1 against the card ground versus 10.2:1, a difference nobody can see. Changing it would mean regenerating thirty-six committed PNGs through Chromium for nothing. What those files needed was not a new value but a reference, so the next person knows the literals are tokens that could not reach the stylesheet, and that touching them means re-running `og:generate`.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Everything left that did not need a screenshot, a benchmark or sponsor copy.
Six items, one PR, each its own commit.
1. The page had one background for sixteen sections
This was flagged critical in the very first audit and never touched.
Over roughly ten thousand pixels of scroll the only rhythm device was hairline
density, so a reader could not tell from peripheral vision whether they had
moved from Architecture to Agents to Mobile. That is the difference between
restrained and monotonous.
--card,--secondaryand--accentwere declaredin the stylesheet and used zero times, which is how long this had been the
intention.
Two grounds, not five.
raisedgoes to the four sections that already read asinset: Safe Mode, the depth index, the prices, the questions.
Measured rather than assumed — every foreground token was checked against the
new surface, the tightest being
--muted-foreground-subtleat 4.61:1, whichstill clears AA.
[data-tone="raised"]sits after.darkbecause both arespecificity (0,1,0) and the tie breaks on source order.
Pricing is wrapped in
SectionShellto get there, which it should have beensince #2. It was the one landing section rendering its own header, and it had
already drifted: extra spacers the shell has no concept of, a headline missing
text-pretty. Every shell change had been silently skipping the highest-intentsection on the page.
2.
shadow-smis a 3px blur on a 1216px-wide imageAn order of magnitude too small, which is exactly why the hero read as a sticker
laid on the page rather than a window above it. Nine images across three files.
Replaced with the three-layer ramp every reference Mac-app site converged on
independently: tight contact shadow, mid lift, one large soft ambient.
Warm-tinted rather than neutral black.
drop-shadowrather thanbox-shadowbecause the shipped PNGs have an alphachannel — I checked — so the captures carry real transparent macOS squircle
corners and the shadow follows the actual silhouette. The border and radius come
off for the same reason:
border-radiusis a circular arc tracing a continuouscurve, and the join is visible.
3. The three tables had already disagreed
Not about typography — they differ there on purpose — but about the one thing
the hairline system exists to settle. Columns are structure and take the heavier
rule; rows are data and take the hairline. The spec table did that. The plan
comparison drew its column separators with the row weight, so the one table
where columns carry the entire meaning drew them faintest.
DataTableowns the shell, the two weights by name, and a requiredcaption— the only reliable way to keep every data table announcing what itis before a screen reader starts reading cells.
Worth flagging because it typechecks either way: two
className="…${TOKEN}…"strings in spec-strip were double-quoted rather than template literals, so they
would have shipped the literal text as a class name. Caught by grepping the
compiled stylesheet for the token names.
4. Arrow keys across the database tiles
Twenty-seven tiles; reaching one meant Tab twenty-seven times. Up and down move
a whole row, read off
COLSat the current breakpoint so the two cannotdisagree. Focus clamps rather than wraps — wrapping a 2D grid on Left at the
start of a row lands you at the end of the previous one, which reads as focus
jumping backwards.
Purely additive: every tile stays in the tab order. The usual composite
pattern would leave one, but that needs
role="grid"withrowandgridcellchildren, and this is one flat CSS grid whose borders are computed by index —
row wrappers would break the layout outright. Adding the roles without the
structure would describe a widget that is not there.
depth-gridandfaqdo not get this: they contain zero interactiveelements, so there is nothing to move between. The plan named all three.
5. Nothing from the last four PRs had a test
Survivable while the
ssrjob did not run. Not survivable now that it does,because it is the only thing that sees the rendered DOM and it was still
checking the page as it looked before any of this.
Nine assertions, all on things a typecheck cannot see, each guarding a defect
this work actually shipped and had to repair — every rule numbered (AccentLine
was not),
rule-inset-hostpresent (without it half the ordinals were 32pxoff), four routes to
/download, both blocking objections before the featuretour, the AI answer between Agents and Safety, more than one ground, Agents
adjacent to Safety,
data-rowon more than twenty elements, and planavailability as words.
6. Four values claimed to be the brand colour
--primary#f68001, atheme-colormeta #FFAA46, #ffaa46 across three OGtemplates, a manifest declaring #000000. None agreed, none referenced the others.
theme-colorwas wrong in kind: it tints the browser's own chrome, so an accentthere paints the address bar a colour appearing nowhere on the page — and one
value cannot serve a site that follows the OS. Now two media-scoped tags
carrying the actual backgrounds, both computed from tokens.
The OG templates keep #ffaa46 deliberately. It corresponds to the dark
theme's
--primary-strong, which computes to #ffa65e — 10.4:1 against the cardground versus 10.2:1, a difference nobody can see. Changing it would regenerate
thirty-six committed PNGs through Chromium for nothing. What those files needed
was a reference, not a new value.
Still outstanding, unchanged
The hero recapture, the cold-start measurement, and eight sponsor descriptions.
All three need an artifact or a fact I do not have, and I will not invent any of
them.
Verification
typecheck, build and pint clean. 105 tests / 1849 assertions with
REQUIRE_SSR=1, up from 96. Rendered HTML checked for all six: four raisedsections,
app-plateon hero and both comparison shots, zeroshadow-smlefton any screenshot, three tables through the primitive, two media-scoped
theme-color tags, and one
main/ oneh1/ oneheaderstill.