Skip to content

feat(ci): the UI audit can now see what nav FORGOT, not just what it wrote - #9

Merged
github-actions[bot] merged 2 commits into
mainfrom
feat/nav-contract-detectors
Aug 31, 2026
Merged

feat(ci): the UI audit can now see what nav FORGOT, not just what it wrote#9
github-actions[bot] merged 2 commits into
mainfrom
feat/nav-contract-detectors

Conversation

@catomean

Copy link
Copy Markdown
Contributor

The audit's two detectors both look for something someone put on the page: ink below the contrast floor, rows starting at different x. An entire class of navigation defect is structurally invisible to that shape, because each instance is something someone left out.

A fleet-wide nav audit across 20 repos (~13,300 lines of navigation, nothing shared between them) found three, repeatedly.

The three detectors

1. A current page no link announces. Every repo styles the active item; nine never set aria-current, and four more set it on every surface but one. The highlight exists only for people who can see it.

2. Nav targets below 44px. That's this fleet's own floor, not the WCAG 2.2 AA minimum of 24px — fleetcrown enforces it centrally, kivvi and wild-spirit state it explicitly. So the message says "out of step with the fleet", not "non-compliant".

3. A label that looks like a control but isn't. The orangecat sidebar shipped an <h3> beside a chevron-only button carrying the whole onClick. Visitors aimed at the word and nothing happened.

Rendered, not grepped — and that's the point. This fleet holds Next apps, CSS modules, Tailwind, and one hand-rolled static generator with no framework at all. No source-level lint spans that. The DOM does.

Each detector refuses to fire where the absence is correct

  • aria-current is demanded only when a link in that nav actually points at the page being rendered. A footer of outbound links has no current page to mark; flagging it would fire on every site that has one.
  • A dead label is claimed only when the heading sits outside any control and a sibling control has no text of its own. That pairing is the bug; a heading beside a labelled button is not.
  • Nested controls are measured by their parent, so one small icon inside a large row isn't reported as the row.
  • Small targets print one line per distinct size, not per element — a nav of twelve identical 32px links is one decision, and twelve lines of it buries everything else.

Tests

Seven new self-tests, both sides pinned as the existing ones are. 19/19 pass. The failing side of each is a real bug from a real repo; the passing side is the corrected markup.

Lands while the workflow still defaults to --warn-only, so findings surface in the job summary before anything fails on them.

Also: SHARED.md

Records navigation chrome under "What must NOT be centralized" — with evidence rather than opinion. sitekit is the one shared nav renderer, serves 2 of 20 repos, and shipped ~28px targets and no focus style to both consumers, unfixable downstream. Its flat {path,label} model can't express solon's mega-menu or reparaturbonus-zh's drawer, which is why every repo with real nav complexity reinvented its own.

Plus the six-rule contract that is shareable, and where each rule is enforced (rendered audit for public pages; a repo-local check for anything behind a login — disjoint surfaces, neither substitutes for the other).

One reversal recorded honestly: the audit first recommended putting nav duplication on the ratchet. Writing the "do not centralize" section showed the two can't both be right — the ratchet counts concerns that should converge on one implementation, and every repo is supposed to have its own nav config.

Unrelated pre-existing failure

shared-inventory.sh --check currently exits 1 on health-route: 9 → 10. Not from this PR — the inventory scans remote default branches, this diff touches only local docs and audit scripts, and the baseline file is untouched here. A tenth health route landed somewhere independently and needs either adoption or a deliberate --update.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XeELB8b3N4JrT2asYL9WvE

catomean and others added 2 commits August 31, 2026 09:42
…wrote

The audit's two detectors both look for something someone put on the page: ink
below the contrast floor, rows that start at different x. An entire class of
navigation defect is invisible to that shape, because each instance is
something someone left out.

A fleet-wide nav audit across 20 repos found three, repeatedly:

  1. A current page no link announces. Every repo styles the active item;
     nine of them never set aria-current, and four more set it on every
     surface but one. The highlight exists only for people who can see it.
  2. Nav targets below 44px. That is this fleet's own floor, not the WCAG 2.2
     AA minimum of 24px — fleetcrown enforces it centrally, kivvi and
     wild-spirit state it explicitly — so the message says "out of step with
     the fleet", not "non-compliant".
  3. A label that looks like a control but is not: the orangecat sidebar
     shipped an <h3> beside a chevron-only button carrying the whole onClick.
     Visitors aimed at the word and nothing happened.

Rendered, not grepped, and that is the point. This fleet holds Next apps, CSS
modules, Tailwind, and one hand-rolled static generator with no framework at
all. No source-level lint spans that; the DOM does.

Each detector refuses to fire where the absence is CORRECT:

  - aria-current is only demanded when a link in that nav really does point at
    the page being rendered. A footer of outbound links has no current page to
    mark, and flagging it would fire on every site that has one.
  - A dead label is only claimed when the heading is outside any control AND a
    sibling control has no text of its own. That pairing is the bug; a heading
    next to a labelled button is not.
  - Nested controls are measured by their parent, so one small icon inside a
    large row is not reported as the row.

Small targets print one line per distinct SIZE rather than per element: a nav
of twelve identical 32px links is one decision, and twelve lines of it buries
everything else.

Seven new self-tests, both sides pinned as the existing ones are — 19/19 pass.
The failing side of each is the real bug from a real repo; the passing side is
the corrected markup.

Lands while the workflow still defaults to --warn-only, so the findings surface
in the job summary before anything fails on them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XeELB8b3N4JrT2asYL9WvE
Records the outcome of the 2026-08-29 fleet nav audit (20 repos, ~13,300 lines
of navigation, nothing shared between them).

Adds navigation chrome to "What must NOT be centralized", with the evidence
rather than the opinion: sitekit is the one shared renderer, it serves 2 of 20
repos, and it shipped ~28px targets and no focus style to BOTH consumers —
unfixable downstream, because a consumer cannot patch markup it does not own.
Its flat {path,label} model also cannot express solon's mega-menu or
reparaturbonus-zh's drawer, which is why every repo with real nav complexity
reinvented its own instead of adopting it.

Adds the six-rule contract that IS shareable, and says where each rule is
enforced — the rendered central audit for public pages, a repo-local check for
anything behind a login, because those cover disjoint surfaces and neither
substitutes for the other.

Records one reversal: the audit first recommended putting nav duplication on
the ratchet. Writing the "do not centralize" section is what showed the two
cannot both be right — the ratchet counts concerns that should converge on ONE
implementation, and every repo is SUPPOSED to have its own nav config, so the
number would score the correct outcome as duplication.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XeELB8b3N4JrT2asYL9WvE
@github-actions
github-actions Bot merged commit 8dde0f1 into main Aug 31, 2026
1 check passed
@github-actions
github-actions Bot deleted the feat/nav-contract-detectors branch August 31, 2026 07:45
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