Skip to content

polish(console): a11y, finality lifecycle, loading, disabled-state, responsive#54

Merged
hitakshiA merged 2 commits into
mainfrom
codex/40-a11y
Jul 10, 2026
Merged

polish(console): a11y, finality lifecycle, loading, disabled-state, responsive#54
hitakshiA merged 2 commits into
mainfrom
codex/40-a11y

Conversation

@hitakshiA

@hitakshiA hitakshiA commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Closes #40. A focused accessibility + polish sweep across the console — reuses the merged primitives, no screen redesigns. Audited every screen (Dashboard, Approvals, Contractors, Invoices, Pay, Payroll, Treasury, Grants, AuditLog, Settings, InviteClaim) plus the shell chrome; most bullets were already satisfied by the merged foundation, so this closes the remaining concrete gaps.

Keyboard-operable row actions — the shared Tr primitive now makes any clickable row focusable (tabindex), activatable with Enter/Space, and shows an inset focus ring; non-interactive rows stay out of the tab order (covers the Audit-log detail-drawer rows and any future clickable table).

Visible focus rings on the remaining raw controls that only had hover: styles: the shared onchain.tsx proof drill-down (trigger, close, verifier + transaction links — ripples to every proof receipt), Shell workspace/notification menu items, NetworkMenu explorer link, and the explorer/receipt links + Payslips/Export buttons in Pay, Payroll, Contractors, Treasury, and AuditLog.

Non-color status signal — the notification bell's pending-approvals state was a color-only dot; the button now carries a textual aria-label/title with the count ("Notifications — N awaiting approval").

Prevent double-submit while a tx is pending — the two full-screen ceremony triggers (Grants "Generate attestation", AuditLog "Generate auditor packet") now pass loading tied to their ceremony phase so they can't be re-fired while a proof is in flight (all other submit/proof buttons already gate on loading).

Target size — the inline CopyButton is bumped to a 24×24 hit area (WCAG 2.5.8 minimum); primary buttons are already 40/44px.

Already in place and verified during the audit (left as-is): prefers-reduced-motion honored globally via MotionConfig reducedMotion=\"user\" + a CSS @media (prefers-reduced-motion) reset; the Submitting → Confirming → Completed / Failed → Retry on-chain lifecycle via the shared SendCeremony + StatusPill; testnet/mainnet shown textually (never color-only) in the NetworkMenu chip; darkened --color-muted for subtitle/helper contrast; loading skeletons + empty states on every async list; icon-only controls already labelled; wide tables already wrapped in overflow-x-auto (no horizontal body scroll).

Gates: build ✓ · typecheck ✓ · lint ✓ (exit 0) · test ✓ (68 passing, no test changes needed). Part of tracker #41.

Summary by CodeRabbit

  • Accessibility Improvements

    • Added clearer keyboard focus indicators across navigation, dialogs, tables, buttons, and explorer links.
    • Interactive table rows can now be reached and activated using the keyboard.
    • Improved notification labels to announce pending approval counts.
  • Bug Fixes

    • Added loading states to auditor packet and attestation generation buttons.
    • Improved copy button sizing and alignment.

Greptile Summary

This PR is a focused accessibility and polish sweep across the console shell and all major screens — no redesigns or new features. It closes concrete a11y gaps identified in the audit by reusing existing primitives.

  • Focus rings: Adds focus-visible:ring-2 to every raw <a> and <button> control that previously only had hover: styles — explorer/receipt links, dialog triggers, Shell menu items, and the OnChainDetail open/close pair. The ring color is adapted for dark-background contexts (ring-white/50 in Treasury's FundReceipt).
  • Keyboard-operable rows: The shared Tr primitive gains tabIndex, role="button", an onKeyDown handler for Enter/Space (guarded to prevent nested-control hijacking), and an inset focus ring. Non-interactive rows stay out of the tab order.
  • Non-color status + double-submit guard: The notification bell's aria-label now includes the pending count; the Grants "Generate attestation" and AuditLog "Generate auditor packet" buttons are gated on loading={phase !== "idle"} to prevent re-fire while a ceremony is in flight. CopyButton hit area is bumped to 28 × 28 px (WCAG 2.5.8).

Confidence Score: 5/5

Safe to merge — purely additive accessibility improvements with no logic changes outside the two loading guards and the Tr keyboard enhancement.

All changes are CSS class additions (focus rings, hit-area sizing) or small, self-contained behavioral guards (loading prop tied to ceremony phase, Enter/Space key handler on Tr). The loading guard logic was verified against the paymentReducer state machine: the button is correctly disabled for every non-idle phase and re-enabled on RESET. No data paths, API calls, or business logic were altered.

apps/console/src/ui/controls.tsx — the Tr keyboard enhancement is the most structurally significant change; the role=button concern on tr was already flagged in a prior review thread.

Important Files Changed

is non-conformant per HTML-AAM (covered in prior thread).
Filename Overview
apps/console/src/ui/controls.tsx Tr gains keyboard interactivity (tabIndex, role="button", Enter/Space handler, inset focus ring) and a nested-control click guard; CopyButton is bumped to 28×28 px hit area. The role="button" on
apps/console/src/app/Shell.tsx Notification bell aria-label now includes the pending count; inset focus rings added to the workspace/notification menu items.
apps/console/src/screens/AuditLog.tsx Explorer link gains focus ring; "Generate auditor packet" button is now gated with loading={packetState.phase !== "idle"}, correctly preventing double-submit across the full ceremony lifecycle.
apps/console/src/screens/Grants.tsx "Generate attestation" button gated with loading={attState.phase !== "idle"}, preventing re-fire while the ORGSUM proof ceremony is in flight.
apps/console/src/ui/onchain.tsx Focus rings added to the drill-down trigger, close button, verifier contract link, and transaction link — ripples correctly to every on-chain proof receipt across the codebase.
apps/console/src/app/NetworkMenu.tsx Explorer link in InfoRow gets focus-visible ring; change is a one-liner addition to the className.
apps/console/src/screens/Pay.tsx Focus rings added to the "Approvals" and "Contractors" navigation buttons in the payment result banner.
apps/console/src/screens/Payroll.tsx Focus rings added to Payslips download, Export CSV, and on-chain receipt links in the batch drawer.
apps/console/src/screens/Contractors.tsx Focus ring added to the on-chain receipt link in the payment history rows.
apps/console/src/screens/Treasury.tsx Focus rings added to two explorer links; the FundReceipt dark-background link correctly uses ring-white/50 for contrast.

Reviews (2): Last reviewed commit: "fix(console): address Greptile/CodeRabbi..." | Re-trigger Greptile

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The console updates interactive controls with visible keyboard focus, adds keyboard activation for clickable table rows, improves notification labels, and exposes loading states for auditor packet and attestation generation.

Changes

Console interaction polish

Layer / File(s) Summary
Keyboard-operable shared controls
apps/console/src/ui/controls.tsx
Clickable table rows support focus, Enter/Space activation, and focus styling; CopyButton uses fixed inline-flex sizing.
Navigation and transaction focus treatment
apps/console/src/app/*, apps/console/src/screens/*, apps/console/src/ui/onchain.tsx
Navigation buttons and explorer links gain rounded outlines and visible focus rings while preserving existing actions and destinations.
Notification labels and generation loading states
apps/console/src/app/Shell.tsx, apps/console/src/screens/AuditLog.tsx, apps/console/src/screens/Grants.tsx
Notification labels include pending approval counts, and generation buttons show loading when their phase is non-idle.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes address the issue's core goals here with focus rings, keyboard-accessible rows, and loading-state improvements.
Out of Scope Changes check ✅ Passed All edits stay within console accessibility, focus, and loading polish; no unrelated feature work is introduced.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the PR’s main accessibility and UI-polish focus, including loading and disabled-state updates.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/40-a11y

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

Comment thread apps/console/src/ui/controls.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/console/src/ui/controls.tsx`:
- Line 233: The copy control’s 24px hit area is below the required minimum. In
the copy button near the `className` using `h-6 w-6`, change the button
dimensions to at least `h-10 w-10` while preserving the icon’s smaller size.
- Around line 143-154: Update the Tr keyboard handling to preserve its row
behavior: destructure the caller-provided onKeyDown and tabIndex from rest,
invoke the caller handler while retaining the row handler, spread remaining
props before controlled values, and set the computed tabIndex afterward. In the
row handler, only process Enter or Space when event.target ===
event.currentTarget so bubbled events from nested controls are ignored.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2440f0c3-5374-4794-b2fb-98030122e9a2

📥 Commits

Reviewing files that changed from the base of the PR and between ac9a065 and 98c253e.

📒 Files selected for processing (10)
  • apps/console/src/app/NetworkMenu.tsx
  • apps/console/src/app/Shell.tsx
  • apps/console/src/screens/AuditLog.tsx
  • apps/console/src/screens/Contractors.tsx
  • apps/console/src/screens/Grants.tsx
  • apps/console/src/screens/Pay.tsx
  • apps/console/src/screens/Payroll.tsx
  • apps/console/src/screens/Treasury.tsx
  • apps/console/src/ui/controls.tsx
  • apps/console/src/ui/onchain.tsx

Comment on lines +143 to +154
onKeyDown={
onClick
? (e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
onClick();
}
}
: undefined
}
className={`${onClick ? "cursor-pointer outline-none hover:bg-border/30 focus-visible:bg-border/30 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary/40" : ""} ${className}`}
{...rest}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Preserve the row keyboard contract and ignore nested-control events.

Because {...rest} is spread after this handler, a caller-provided onKeyDown or tabIndex can overwrite Tr’s computed behavior. AuditLog.tsx supplies both, and its row handler also receives bubbled keys from nested copy and receipt controls. Destructure/combine the caller handler, keep controlled props after the spread, and only activate the row when event.target === event.currentTarget.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/console/src/ui/controls.tsx` around lines 143 - 154, Update the Tr
keyboard handling to preserve its row behavior: destructure the caller-provided
onKeyDown and tabIndex from rest, invoke the caller handler while retaining the
row handler, spread remaining props before controlled values, and set the
computed tabIndex afterward. In the row handler, only process Enter or Space
when event.target === event.currentTarget so bubbled events from nested controls
are ignored.

Comment thread apps/console/src/ui/controls.tsx Outdated
setTimeout(() => setDone("idle"), 1200);
}}
className="rounded p-1 text-muted outline-none transition hover:bg-border/50 focus-visible:ring-2 focus-visible:ring-primary/40"
className="inline-flex h-6 w-6 items-center justify-center rounded text-muted outline-none transition hover:bg-border/50 focus-visible:ring-2 focus-visible:ring-primary/40"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Increase the copy control hit area to the required minimum.

h-6 w-6 renders a 24×24px target, conflicting with the PR objective requiring interactive targets of at least 40px. Keep the icon small, but make the button itself at least h-10 w-10.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/console/src/ui/controls.tsx` at line 233, The copy control’s 24px hit
area is below the required minimum. In the copy button near the `className`
using `h-6 w-6`, change the button dimensions to at least `h-10 w-10` while
preserving the icon’s smaller size.

…Tr gets role=button (accessible name from cell text) and ignores clicks/keys from nested controls (buttons/links/fields keep their own action); bump inline copy control to a 28px target
@hitakshiA

Copy link
Copy Markdown
Contributor Author

Greptile/CodeRabbit triage (fixed): the clickable Tr now carries role=button so AT gets an accessible name from the row's cell text (callers can still override via aria-label); both its click and Enter/Space handlers ignore events that originated on a nested control (a cell's button/link/field keeps its own action, and only the row itself activates on keyboard); bumped the inline copy control from 24→28px to clear the min touch-target comfortably.

@hitakshiA
hitakshiA merged commit 7c273c3 into main Jul 10, 2026
3 checks passed
@hitakshiA
hitakshiA deleted the codex/40-a11y branch July 10, 2026 11:10
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.

polish(console): accessibility, finality lifecycle, loading, disabled-state, responsive

1 participant