Skip to content

fix(a11y): color contrast, skip link, focus rings, and aria attributes#421

Merged
gabitoesmiapodo merged 6 commits intofeat/ai-integrationfrom
fix/a11y
Mar 30, 2026
Merged

fix(a11y): color contrast, skip link, focus rings, and aria attributes#421
gabitoesmiapodo merged 6 commits intofeat/ai-integrationfrom
fix/a11y

Conversation

@gabitoesmiapodo
Copy link
Copy Markdown
Collaborator

Summary

  • Fix warning color contrast: #cc0 fails WCAG AA; #996600 (light) / #e6b800 (dark)
  • Fix danger/ok semantic tokens: add proper dark-mode variants (#ff6666, #66ee66)
  • Add skip-to-main-content link for keyboard navigation
  • Add aria-invalid to BigNumberInput on range validation failure
  • Replace outline:none with _focusVisible focus ring on CopyButton and ExternalLink
  • Add aria-label="Close" to icon-only close buttons in Modal and TokenInput
  • Add aria-label="View on explorer" to icon-only ExternalLink in Hash

Test plan

  • pnpm test passes
  • Verify focus ring visible on CopyButton and ExternalLink with keyboard
  • Verify skip link appears on Tab from address bar

Copilot AI review requested due to automatic review settings March 23, 2026 21:45
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
components.dappbooster Ready Ready Preview, Comment Mar 30, 2026 6:52pm
demo.dappbooster Ready Ready Preview Mar 30, 2026 6:52pm
docs.dappbooster Ready Ready Preview, Comment Mar 30, 2026 6:52pm

Request Review

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR improves accessibility across the app by addressing color-contrast issues, adding keyboard navigation support (skip link + focus rings), and improving screen-reader semantics with ARIA attributes.

Changes:

  • Update semantic color tokens (warning/ok/danger) to improve WCAG contrast in light/dark modes.
  • Add a skip-to-main-content link and restore visible focus styling for icon-only controls.
  • Add/extend ARIA attributes for validation and icon-only buttons/links.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/routes/__root.tsx Adds a skip-to-main-content link and a main-content anchor target.
src/components/ui/provider.tsx Updates semantic token colors for contrast-compliant warning/ok/danger states.
src/components/sharedComponents/ui/Modal/index.tsx Adds aria-label to icon-only close button.
src/components/sharedComponents/ui/ExternalLink/index.tsx Replaces outline="none" with _focusVisible focus ring styling.
src/components/sharedComponents/ui/CopyButton/index.tsx Replaces outline="none" with _focusVisible focus ring styling.
src/components/sharedComponents/TokenInput/index.tsx Adds aria-label to icon-only close button in the token dialog.
src/components/sharedComponents/Hash.tsx Adds aria-label to icon-only explorer external link.
src/components/sharedComponents/BigNumberInput.tsx Introduces aria-invalid wiring based on range validation failures.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/components/sharedComponents/BigNumberInput.tsx:74

  • hasError is only updated inside updateValue, so if the parent updates value programmatically (e.g. reset to 0 / set max) after an out-of-range entry, aria-invalid can remain stuck true even though the new value is valid. Consider deriving/clearing hasError in an effect when value/min/max change, or resetting it whenever value is externally synchronized.
  const [hasError, setHasError] = useState(false)

  // update inputValue when value changes
  useEffect(() => {
    const current = inputRef.current

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…butes

- Fix warning color contrast: #cc0 fails WCAG AA; use #996600 (light) / #e6b800 (dark)
- Fix danger/ok colors: add proper light/dark variants (#ff6666, #66ee66) for dark mode contrast
- Add skip-to-main-content link in root layout for keyboard navigation
- Add aria-invalid to BigNumberInput default input on range validation failure
- Replace outline:none with _focusVisible focus ring on CopyButton and ExternalLink
- Add aria-label="Close" to icon-only close buttons in Modal and TokenInput
- Add aria-label="View on explorer" to icon-only ExternalLink in Hash component
- Fix malformed transition string in CopyButton and ExternalLink (missing
  closing brace on last token)
- Move aria-invalid into inputProps so it applies to both the default input
  and any custom renderInput renderer
- Clear hasError state on empty-string input path (was only cleared on
  valid range path, leaving aria-invalid=true after user clears the field)
- Add tabIndex={-1} to #main-content so skip link activation moves
  keyboard focus into the main region
An intermediate/unparseable input string while the user is typing could
cause the useEffect that syncs the DOM value on external prop changes to
throw. Wrap parseUnits in a try/catch and use a sentinel value to force
the DOM overwrite when the current string is not parseable.
Base automatically changed from test/utils to feat/ai-integration March 30, 2026 18:24
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/components/sharedComponents/BigNumberInput.tsx:153

  • New aria-invalid behavior isn’t covered by existing tests. Since there’s already BigNumberInput.test.tsx, add assertions that aria-invalid is set when range validation fails and cleared again when the value becomes valid / empty (and for other invalid input cases, if you decide to flag them).
  const inputProps = {
    'aria-invalid': (hasError || undefined) as true | undefined,
    disabled,
    onChange: updateValue,
    placeholder,
    type: 'text',
  }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Also exclude .worktrees/ from vitest to prevent cross-worktree test pollution.
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.

3 participants