Skip to content

Automated design review: coherence and polish passes over index.html - #1

Open
aksaeri wants to merge 15 commits into
masterfrom
automated-design-review
Open

Automated design review: coherence and polish passes over index.html#1
aksaeri wants to merge 15 commits into
masterfrom
automated-design-review

Conversation

@aksaeri

@aksaeri aksaeri commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

Two sequential improvement passes over the site's single index.html, produced by an automated design review (study → spec → implement → independent adversarial review at each stage). Only index.html changes; data.enc/highlights.enc are byte-identical to master, so this deploys with no data or password implications.

Pass 1 — Coherence (8 commits)

  • One design-token block replacing the accumulated patchwork: 26 declared font sizes → one scale, three competing severity-tier color ramps → one, five "selected" idioms → one, one eyebrow recipe, unified radii/shadows/focus states.
  • Muted text darkened to clear WCAG AA (independent contrast sweep: 59 failing text styles → 10, all 10 pre-existing and out of scope).
  • Navigation repaired: the 8 nav items are real focusable anchors (Tab/Enter/ctrl-click/middle-click all behave), and #/faqs resolves instead of falling back to Home.
  • Calmer caveat banner, quiet empty states, tooltip width fix.

Pass 2 — Polish (7 commits)

  • Honesty: cells holding only an upper bound ("<71%") are no longer tinted as if measured — on screen, in the sector share column, and in both PNG exports, which now match the screen exactly (the Home export previously printed "<42%" as "42%").
  • Fixed in production: the sector PNG export threw a ReferenceError on every click; it now works.
  • The Home coach-mark stays anchored to its row while scrolling; dark-OS users no longer get dark pills on the light page; "PDF" ghost letters no longer peek from behind thumbnails; documents view loses ~300px of dead space at phone widths.
  • Deliberate behavior changes (2): dismissing the onboarding callout persists across visits; #/compare/a,b deep links scroll to the comparison they name.
  • Home tier headers calmed to keylines with the export updated in the same commit (this item was dropped in pass 1 by unanimous review vote precisely because the export didn't move with it; it shipped unanimously once it did).

Verification

Each pass was reviewed by two independent adversarial reviewers against live before/after builds: per-view text-content parity checked character-by-character (four accounted deltas, all enumerated), every interaction exercised (sorts, filters, drawers, tour, exports, history/back-forward), zero console errors across all views at 1440px and 390px, and both PNG exports downloaded and compared against the screen. Four contested items were gated behind unanimous reviewer votes; each sits in its own revertable commit (marked [GATED]).

🤖 Generated with Claude Code

aksaeri and others added 15 commits August 18, 2026 21:46
Colour, type, spacing, radius and shadow were spread across 1,600 lines of
style block and two :root declarations. This gathers them into the first
:root as named tokens, keeping every existing name live so nothing has to be
rewritten to keep working. The tier ramp (--lvl-*) and the green share ramp
(--d*) stay where they are declared further down rather than being copied.

One value changes: --muted goes #898A8D -> #6E6E6B, which takes small grey
text from 3.3:1 to 5.1:1 on white and over the AA line. Everything else is
pixel-identical.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The style block had grown three severity ramps, five near-ink greys, fifteen
small-uppercase-label recipes, twenty-six font sizes, fifteen radii, four
tooltips, eleven legends and four ways of drawing "no data". Each of those is
now one pattern, chosen from the site's own dominant convention rather than
invented:

- one tier ramp. --lvl-* takes the values the matrix JS (VTIERS) already
  draws, so the flagship view does not move a pixel, and the hand-mixed
  ramps in the significance panel and the sector bands come onto it. Brand
  red stops being a data fill.
- one text ramp: #333/#444/#4A4A47/#55524C/#5A5A5A all read --body.
- one eyebrow: 10.5px/700/.05em uppercase in --muted, for the twenty-one
  labels that were doing that job in nine different ways.
- one type scale (9.5/10.5/11/12/13/14/15.5/18/22), one page-question size,
  one section-heading size; one radius vocabulary (--r-cell/-ctl/-modal/-pill).
- one selected idiom (ink, not red), one tooltip skin, one legend skin with
  one swatch size, one scrim darkness, one close button, one empty state.
- one prose measure and one wide measure; tabular numerals on numeric cells.

The measure token holds 760px, not the spec's 78ch: 78ch measures 702px at
14px and 624px at 13px, so as a ch value the measure would change with the
font size and rewrap every paragraph. 760px is the site's own dominant cap.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Four places where a view shouted:

- the model-coding caveat in the mitigation drill-down was a full-width
  #FFD43B bar, the loudest element on the site. It now wears the caveat skin
  the FAQ page already uses: pale amber tint, an amber left rule, brown text.
  Same words, same position.
- the sector heatmap's "scroll horizontally" hint was 15px bold brand red, an
  error costume on a navigation hint. Now 12px in --muted.
- the companies table's sector group rows were eight one-off pastels, the
  first of which (pale red with a red bar) read as an alert row. Every group
  row now uses the structural pale-blue band, the same one Compare draws its
  sector bands with. The per-company left rule under a sector goes with it,
  or the band would have stopped at the header. SEC_TINTS stays defined for
  the company tags in a pooled matrix, where sector is data rather than chrome.
- the mitigation domain palette moves into the token block. Pixel-equivalent:
  the mitigations view diffs to zero changed pixels, which also proves var()
  resolves in the inline fills.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two documented defects.

The eight primary nav items were <div>s: no href, no tab stop, nothing a
screen reader could recognise as a control, and no way to middle-click one
into a new tab. They are now anchors carrying the router's own slugs. The
delegated handler keeps driving the SPA for a plain click and stands aside
for a modifier or a non-primary button, so open-in-new-tab does what it says
while the in-page path is unchanged. Verified: Tab from the logo reaches all
eight in order then the tour button, Enter activates, one history entry per
click, ctrl+click loads the right view in a new tab and leaves the current
one where it was, and the tour still drives the bar.

#/faqs fell back to Home while the nav item next to it said "FAQs". One
inbound alias fixes it. Outbound hashes stay #/about, so no shared link
changes and history behaviour is untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A sweep of every text-bearing element across twelve views, comparing the
branch against master: master had 45 distinct text styles below their WCAG AA
threshold, the branch has 6. Three near-misses were worth closing here
because this pass is what put them where they are:

- an inactive nav item measured 4.48:1 on the red bar. At 0.9 alpha it clears.
- the sector count beside a group row now sits on the pale axis band, where
  body grey measures 4.14:1. It takes the band's own ink (--axis-ink), the
  pair the document pills and open-links already use, at 5.4:1.
- the same for the role sub-label in Compare's column headers.

The 6 that remain are pre-existing and outside every item in the spec: the
company-page matrix chips (white on the green share ramp at 4.25, absent-grey
on grey at 2.77) and the deliberately greyed-out "no data" mitigation rows
(2.39). Each is a one-line colour change if the owner wants them, but they
are data-fill and absence-state decisions rather than anything this pass
touched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The five tier headers were the most saturated colour on the site, spent on
the legend dimension: the labels outweighed the numbers they head, which
inverts the hierarchy the matrix is for. Each header now carries its tier's
hue as a 4px keyline over its column, with the label in ink on white — the
pattern the sibling matrix already used on .mx-tierhead. Same colours, same
order, same labels, same tooltips; the data cells go back to being the
darkest thing in the table.

Self-contained on purpose: this commit is the whole of it, so a
non-unanimous review can drop it with one revert.

Reviewer note, and the reason to look twice: the PNG export draws its own
canvas and still fills those headers at full saturation, so with this commit
the screen and the exported figure disagree. Bringing the export across is
about five lines in drawMatrixPng's header loop (white fill, a 4px rect of
the tier hue, ink text), but that is canvas logic the spec did not bless for
this item, so it is left for the gate decision.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two things review found, both real.

The term tooltips did collapse. C8 replaced `width:240px` with `max-width:320px`,
but the tooltip is an absolutely positioned pseudo-element, so with no width it
shrink-to-fits against its containing block rather than against its max-width —
inside a table cell that came out at 68-138px and three times as tall.
`width:max-content` alongside the max-width restores the behaviour: measured on
all six live call sites (the sector title, the sector matrix's share column in
both population modes, and the risk page's four role definitions) they now render
at 320px and 35-70px tall, against master's 240px and 31-62px.

Reviewer A was right that these render and were broken; B's reading that the
`.tip` call sites are unreachable was wrong. They are emitted from three places
(mxLegend, the sector title, the sector matrix column header) and four of the six
are the role tooltips on the risk page, exactly where A measured them.

.cpm-chip goes back to 10px, the one size deliberately off the type scale. The
comment above the grid records that the column width was measured so the longest
risk name fits a chip unclipped, and it was measured at 10px; at 10.5 the two
longest lost their last characters. Every alternative had a side effect: trimming
the chip and cell padding together still leaves it a pixel short, wrapping the
chip changes every row's height, and widening the matrix ~40px moves a view that
review did not ask to move. The geometry is load-bearing, the half-pixel is not.

Verified after both fixes: no clipped chip on the Microsoft profile (was two),
the other three tooltip skins unchanged in behaviour (#mx-tip 320px fixed, .mit-tip
320px, both shrink-to-fitting correctly all along), 58 scene-viewport screenshots
with zero console errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Six repairs where the page or its exported image said something the data does
not support.

D1. In Top-1000 mode a rule-of-three result is a ceiling: "<42%" means
"somewhere between nothing and 42%". Both Home cell builders fed it into the
shared tint ramp anyway, so a ceiling carried the colour of a real 42% share
while the caption promised colour = share. Ceilings now leave the ramp — white
cell, muted figure, the treatment a non-reportable estimate already gets —
keeping their text, tooltip and footnote. Same in the sector matrix's share
column, and ceilings no longer set the range the ramp scales to, where one
bound could stretch the scale for every real figure beside it.

The two canvas exports came along, as pass 1 taught. Both were worse than the
screen to begin with: exportHomePng printed v.pct+'%' for every cell, so a
ceiling exported as "42%" on a dark tint and a non-reportable estimate as "0%"
where the screen shows an em dash; exportSectorPng printed cov+'%'
unconditionally. Both now print the cell's own text.

While verifying that, found exportSectorPng has never produced a file: its
download line reads `sn`, which is not in scope there, so every sector export
threw. It reaches for the sector's title, which is `sp.title`. One word.

D2. The teaching callout was position:fixed with a scroll listener commented
"the card is fixed, so it must track the page" — it did not: scrolling left it
hovering over rows it was not describing. It is now absolute inside .mxv-wrap,
the containment the spotlight box already uses, positioned in wrap coordinates
and clamped to the wrap so .center's overflow cannot clip it. The scroll
listener is gone rather than fixed. Measured: the card sits exactly level with
its highlighted row and stays there through a 900px scroll.

D3. One declaration. Stacked at 390px the Documents filter fields turned their
340px flex-basis into a 340px height; measured field height drops from ~340 to
56 against 52px of content.

D4. The dark-scheme block whose own comment called it dormant was firing on the
reader's OS preference and painting the seven document pills near-black on a
page that is light either way. Deleted, with the comment corrected to say what
happened. It can return with a real theme toggle.

D5. The "PDF" fallback label sat in flow behind the thumbnails, so a present
image covered only part of it and left fragments at the card's left edge. Both
rules take the absolute positioning the company-page thumbnail already used.

D6. The exports' hardcoded #898A8D greys and the select chevron's data-URI
predate pass 1's --muted; six literals and one URI now read #6E6E6B.

Verified: text parity against v1 across 13 view-states at 1440 and 390, all
identical; 58 scene-viewport screenshots with zero console errors; Home export
diffed against v1's (Top-100 differs only in the grey literals); the sector
export produces a file for the first time and its coverage column matches the
screen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two deliberate behavior changes, one each.

D7. Dismissing the coach mark lasted until the next page load: a returning
reader met the four-step walkthrough and a 32%-dimmed table every visit. It
now persists. The key is new — orgrev_explorer_callout_done_v2 — because the
line this replaces was not a leftover: its comment says it clears a flag an
earlier build persisted so anyone who dismissed it then still sees it now. That
migration has been running on every load since, so its work is done, and
reusing the old key would resurrect flags of unknown vintage. Nothing is lost:
"How to read this" still reopens the walkthrough regardless of the flag.
Verified: shows once, stays dismissed across a reload, reopens on request,
walking to the last step persists too, clearing storage brings it back, and
with localStorage throwing (private windows) it degrades to exactly today's
behavior.

D8. Arriving at #/compare/microsoft,novartis showed a wall of ~200 picker chips
with the answer 2,684px below the fold. applyHash() now scrolls the result
region into view, and only applyHash() — chip clicks re-render and write the URL
directly without passing through it, so hand-picking companies never yanks the
page. Verified at both viewports: the table lands at the top of the viewport on
arrival, plain #/compare does not scroll, two successive hand-picks do not
scroll, hashchange arrivals do, back still works, and no other tab moves.
'auto' rather than 'smooth', so there is no animation to suppress under
prefers-reduced-motion.

Text parity against v1 unchanged (13 view-states x 2 viewports, all identical);
58 scene-viewport screenshots with zero console errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-D11)

D9 mitigation bar labels carry their unit ("71" -> "71%") under an axis
already labelled 0%-100%. D10 Home's leading severity figure names its
scale in title/aria-label (SEV_LABEL), and Compare's result table says
its rows run most-to-least severe, so the taxonomy IDs beside them stop
reading as a shuffled order. D11 the nav chip hover tint darkens instead
of lightening: white on #861A2B measures 9.56:1, was ~3.4:1.

Deviation from the spec's letter: aria-label carries the value as well
as the scale ("3.49 - Expert-rated mean severity, ..."), because
aria-label alone would have replaced the number for screen readers.

Verified: 14 views x 1440/390, 0 console errors; text parity vs D1-D8
shows only the two enumerated deltas (mitigation labels, one Compare
line); .mit-val does not clip at 390 (0 labels past the viewport, no
document overflow); Compare note sits inside the 300px Risk column
(1 line at 1440, 2 at 390, no overflow).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…— screen and export together (D12)

The five tier headers were the most saturated colour on the site, spent
on the legend dimension: the labels outweighed the numbers they head.
Each header now carries its tier's hue as a 4px keyline over its column
with the label in ink on white — the .mx-tierhead pattern — and this time
exportHomePng's header loop draws the same thing (white fill, keyline at
the screen's 9px padding offset, ink label nudged below it). Legend
swatches stay fully saturated in both. Same colours, order, labels,
tooltips.

This bundles the change that answers pass 1's sole DROP reason: screen
and export no longer disagree. Verified by exporting the Home PNG and
reading the header band — white cells, tier keylines, ink labels,
matching the live matrix in both population modes.

Text parity: the strings are unchanged; because the keyline is a block
span, innerText now breaks Home's header row into one line per tier
label instead of one tab-joined line. No words added or removed.

Verified: 14 views x 1440/390, 0 console errors; export renders without
error at 2360x2880.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ed pills, screen and export (D13)

Nineteen saturated badges down one column outshouted the coverage numbers
beside them, and set red badges against green tick cells. Each badge keeps
its tier identity as a pale tier tint, the tier's own dark value for the
label, and a 1px tier-coloured border — applied unconditionally, so mixed
columns need no conditional logic and no badge is edited away.
exportSectorPng's chip adopts the identical treatment in this commit.

Measured on the technology sector, all four tiers present: Extremely
6.63:1, Moderately 6.06:1, Minimally 7.98:1, Not at all 6.92:1 — all AA.
data-vtip tooltips and focus outlines untouched. Sector PNG re-exported
and read back: chips match the screen. Text parity vs D12: identical.
14 views x 1440/390, 0 console errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n hear, labels that clear the edge (D14)

The scroll hint keeps its honest live count and still disappears at the
end of the scroll, but at 12px muted readers missed that dozens of
companies were offscreen: it takes --ink at 13px (still not red), and the
right-edge fade widens 72px->96px as a second affordance. The 45-degree
company labels no longer clip: .shm gains 60px of right padding, and the
hint updater subtracts the same constant (SHM_LABEL_PAD) so the gutter is
not counted as two more companies.

Verified at 1440 and 390 on a 50-company sector: hint 13px #1A1A1A;
scrolled to the end, hint and fade both hide and zero labels clip
(last label "Toyota" fully inside the box). Sector PNG export is
unaffected — it draws every company and no hint.

Text delta beyond the spec's two enumerated ones, and local to this item:
the hint's count drops by 2 (83->81 at 1440, 108->106 at 390) because the
new padding is excluded from it. Reverting this commit removes it.

14 views x 1440/390, 0 console errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ip type joins the scale (D15)

The company-profile risk chips were the one deliberate off-scale size
(10px), kept in the pass-1 fix round because the 980px grid was measured
so the longest risk name fits at 10px. 40px more grid buys the scale's
eyebrow step: max-width 980->1020px, chip 10px->10.5px.

Measured at 1024, 1280, 1440, 390 on the Microsoft profile: zero
truncated chips at 1280 and 1440 (grid 1020px), no document overflow at
any width. At 1024 the grid is capped at 924px by the page container, not
by max-width, and two chips truncate there — verified pre-existing and
identical under all four font/width combinations (10/980, 10/1020,
10.5/1020, 10.5/980 all truncate the same two). 390 clips as it did
before, which the rule's own comment describes as intended.

Text parity vs D14: identical. 14 views x 1440/390, 0 console errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant