Skip to content

fix(a11y): give the audit filter links and the payments toggle their hit target - #141

Merged
guarzo merged 1 commit into
mainfrom
design-sweep/2026-08-06-7
Aug 6, 2026
Merged

fix(a11y): give the audit filter links and the payments toggle their hit target#141
guarzo merged 1 commit into
mainfrom
design-sweep/2026-08-06-7

Conversation

@guarzo

@guarzo guarzo commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Round 7 of the autonomous design-sweep-loop run, from a re-review pass after rounds 1–6. Branched off main, not stacked — see Why this one isn't stacked below.

One sentence: two in-cell controls never got the hit-target fix their sibling got, and this applies it.

What this round resolved

globals.css:2476-2489 already contains the argument, written out and measured, for exactly this problem:

The Raw disclosure has a control on every run row and .json gives it type but no box, which measured 18.6px tall — under WCAG 2.5.8's 24px and under both sizes DESIGN.md commits to. It is a control in a table cell, not a link in a sentence, so the inline-target exception does not cover it.

That fix was applied to .json > summary alone, because it was the only one anybody had put a ruler against. Two controls with the identical shape were left at the bare line box.

.cell-link — the audit log's actor, action and target filters

Every resolved name in the audit log is a filter control: pressing it narrows the log to that actor or target. Each is the sole content of its <td>, so the inline-link exception doesn't reach it any more than it reached .json > summary. At --t-data the target was the text line, around 19px.

Cost to a user: audit rows sit edge to edge with no gap between .log td borders. A mistap doesn't land on nothing — it lands on the neighbouring row and silently applies the wrong filter. That's on the one page in the app whose entire job is identifying precisely who did what to whom.

.log summaryPaymentHistory's "payments (N)" toggle

Styled by .log summary, not by .json, so it never picked up the sibling fix. Same bare line box.

Fixed with padding rather than align-items: center, because the rule directly above it already documents why it uses flex-start — a long character name wraps to two lines and a centred marker floats away from the first. Centring to reach the height would have undone a decision the file explains three lines earlier.

.json > summary still overrides both properties and is unchanged.

What was skipped

Nothing in this round. Both items had the fix already written and justified one selector away, which is why they were cut together.

Findings this re-review raised that turned out to be stale

Worth recording, because they were filed against the tree as it stood before #128 and #139 merged:

  • .btn--micro hardcoding 0.6875rem/0.08em instead of the tokens — already tokenized on current main. Only .copy-result's letter-spacing still hardcodes; that's round 8.
  • The type scale violating DESIGN.md's stated 1.25 minimum ratio — DESIGN.md now says the opposite, at length: "'ratio 1.25 minimum between adjacent steps' is what this section used to claim and it has never been true of the shipped scale." The doc was fixed before the finding was written.

What needs a human

The announcement-and-focus family on /payouts/[id]. Five controls unmount or rename themselves on success with nothing announcing the result: delete a pool, remove a participant, mark paid, revert, and the exclude/include toggle (which changes its own accessible name while focused — confirm-submit.tsx:154-156 states as settled fact for this codebase that a name change on an already-focused control is not reliably re-announced).

This was out of reach when rounds 1–6 ran. #128 changed that: ConfirmNotice is now a shared primitive with two consumers and a documented ?done=&at= contract. Extending it to these five means touching server actions to redirect with params and writing the payouts confirmation copy — bounded work with precedent, but it's one coherent round for all five, not a patch on one. Fixing the exclude/include toggle alone would leave four controls inconsistent with it, which is worse than leaving all five.

Gate results

Gate Result
scripts/check-node-version.sh pass
npm run typecheck pass
npm run lint pass
npm run format:check All matched files use Prettier code style!
npm test 77 files, 1134 tests passed
npm run test:e2e -- admin.spec.ts payouts.spec.ts 108 passed (5.7m)
docker build . not run (slow, environment-bound)
full npm run test:e2e not run — the two specs covering the changed surfaces were run instead

The browser specs were run for this round specifically rather than skipped, because round 4 of this run shipped a label change that broke 22 payouts specs and none of the per-round gates caught it. This round changes no copy, but the same class of surface is involved.

Tree held only src/app/globals.css after every gate.

Why this one isn't stacked

Rounds 1–6 were a GitHub native stack. That bought conflict-freedom and cost automated review: CodeRabbit disables auto-review on any PR whose base isn't the default branch, so five of the six were never reviewed until they were triggered by hand — and the base branch can't be retargeted afterwards, because the API refuses to move a stacked PR's base.

This round branches off main instead. It carries no dependency on #133/#134/#136/#137/#138, and it gets reviewed.

…hit target

`.json > summary` was measured at 18.6px and fixed with a 28px floor when it
was the only in-cell disclosure anybody had put a ruler against. Two siblings
with the identical shape were left behind:

`.cell-link` — every actor, action and target in the audit log is a filter
control and the whole content of its `<td>`, so WCAG 2.5.8's inline-target
exception does not cover it. An admin tapping a name to filter by it aims at a
~19px box in a table whose rows sit edge to edge with no gap, so a mistap does
not land on nothing: it lands on the neighbouring row and silently applies the
wrong filter, on the one page whose whole job is identifying precisely who did
what.

`.log summary` — `PaymentHistory`'s "payments (N)" toggle is styled by this
rule, not by `.json`, and inherited the same bare line box. Fixed with padding
rather than `align-items: center`, because the wrapped-long-name case the rule
already documents still has to hold.

DESIGN.md:227-232 commits to 28px for in-row controls and both of these are
that. Found by a design-sweep re-review; no test pinned either, both are
CSS-only.
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 30 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 73f2373f-e728-4d9b-a4e0-789411983e87

📥 Commits

Reviewing files that changed from the base of the PR and between ea65e2c and 265aa0d.

📒 Files selected for processing (1)
  • src/app/globals.css

Comment @coderabbitai help to get the list of available commands.

guarzo added a commit that referenced this pull request Aug 6, 2026
…correctly (#143)

The Corp share row read `12.5% (4,318,206.71 ISK + remainder)`, which reads as
"this much, and then some rounding on top". It is the opposite: corpAmount is
totalValue minus every participant's amount (services/payout-view.ts:215-218),
so the remainder is already inside the figure — e2e/payouts.spec.ts:609-621
asserts exactly that, to the cent. The reader is an FC checking a split before
finalizing, and this row is the only place the corp's cut appears as ISK; the
old string invited them to add a fudge factor to an exact number, so a split
that reconciles looks like it does not. Now ", remainder included". The word
`remainder` is kept because payouts.spec.ts:619 locates the cell by it.

The duplicate-name and roster-clash notices ended with "remove one before
finalizing" and "Check before finalizing" outside any editability gate, so a
finalized or payment-frozen operation instructed an action that assertEditable
rejects, with the control already removed from the page. Someone auditing a
completed payout after a dispute either hunts for a button that was
deliberately taken away or concludes the operation is still a draft. The
notices themselves are worth keeping after finalizing — they explain why one
pilot appears twice in a split that already paid out — so only the imperative
changes, on a new `amendable` rather than on `canEdit`: `canEdit` folds in the
viewer's role, which is right for showing controls and wrong for copy, since a
member reading a draft cannot remove a duplicate but the roster is not settled.

Skipped: the exclude/include toggle's missing announcement, filed alongside
these as a copy fix. It is not one — those controls are server actions with
revalidation, so a fix needs #128's `?done=&at=` contract, and it is one of
five controls on this page in the same state. Fixing one leaves four
inconsistent with it. See #141.
@guarzo
guarzo merged commit b555c54 into main Aug 6, 2026
7 checks passed
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