Skip to content

fix: improve ARIA roles, semantic HTML, and touch targets#599

Open
rickstaa wants to merge 1 commit intomainfrom
fix/aria-semantic-touch-targets
Open

fix: improve ARIA roles, semantic HTML, and touch targets#599
rickstaa wants to merge 1 commit intomainfrom
fix/aria-semantic-touch-targets

Conversation

@rickstaa
Copy link
Member

Summary

  • Add role="button" to sortable table headers and interactive badges in OrchestratorList
  • Add role="group" to related UI clusters in ExplorerChart and RoundStatus
  • Replace <Flex> content wrapper with semantic <main> landmark in layout
  • Fix Drawer "Get LPT" trigger to use proper <button> element
  • Refactor PopoverLink to properly handle external vs internal links with aria-hidden on decorative chevrons
  • Increase touch targets to 44×44px minimum on home page buttons
  • Add proper spacing/gaps between action buttons

Extracted from #509 by @Roaring30s — Lighthouse accessibility and touch target improvements.

Partially addresses #433.

Test plan

  • Verify sortable table headers announce as buttons to screen readers
  • Test semantic <main> landmark is detected by assistive technology
  • Confirm "Get LPT" in drawer works as a proper button
  • Check PopoverLink works for both internal navigation and external links
  • Verify touch targets meet 44px minimum on mobile viewport
  • Test "Performance Leaderboard" and "View All" buttons are easily tappable

🤖 Generated with Claude Code

Add role="button" to sortable table headers and action badges.
Add role="group" to related UI element clusters.
Replace non-semantic elements with proper button/main landmarks.
Refactor PopoverLink to separate internal/external link handling.
Increase touch targets to 44px minimum for mobile accessibility.

Extracted from #509.

Co-Authored-By: Sebastian <115311276+Roaring30s@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 25, 2026 11:05
@rickstaa rickstaa requested a review from ECWireless as a code owner March 25, 2026 11:05
@vercel
Copy link
Contributor

vercel bot commented Mar 25, 2026

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

Project Deployment Actions Updated (UTC)
explorer-arbitrum-one Ready Ready Preview, Comment Mar 25, 2026 11:06am

Request Review

Copy link
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

Improves accessibility and mobile usability across the Explorer UI by adding semantic landmarks, refining ARIA usage, and increasing touch target sizes to better satisfy Lighthouse accessibility audits.

Changes:

  • Adjusts home page action button sizing/spacing to meet 44×44px touch target guidance and improves icon accessibility.
  • Introduces semantic <main> landmark in the main layout container.
  • Updates multiple components (OrchestratorList, ExplorerChart, RoundStatus, Drawer, PopoverLink) with ARIA/interaction refinements and link-handling behavior.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
pages/index.tsx Increases touch targets and adds spacing/gaps; hides decorative arrow icons from AT.
layouts/main.tsx Replaces a generic wrapper with a semantic <main> landmark.
components/RoundStatus/index.tsx Adds ARIA adjustments (including tooltip/grouping-related changes) and hides decorative status icons.
components/PopoverLink/index.tsx Refactors internal vs external link rendering and hides decorative chevrons from AT.
components/OrchestratorList/index.tsx Adds ARIA roles to headers/badges and refactors action trigger structure.
components/ExplorerChart/index.tsx Adds role="group" wrappers around chart header clusters.
components/Drawer/index.tsx Updates “Get LPT” trigger to use a button element instead of a text/link construct.
Comments suppressed due to low confidence (5)

components/OrchestratorList/index.tsx:1063

  • DropdownMenuTrigger uses asChild, but the child Badge is only given role="button". This won’t be keyboard-focusable unless it’s a real button (or you add tabIndex=0 + key handlers). Prefer Badge as="button" type="button" (and style resets) so the trigger is natively interactive.
              <DropdownMenuTrigger
                onClick={(e) => {
                  e.stopPropagation();
                }}
                asChild
              >
                <Badge
                  role="button"
                  size="2"
                  css={{
                    cursor: "pointer",
                    color: "$white",
                    fontSize: "$2",
                  }}

components/OrchestratorList/index.tsx:1162

  • Same keyboard accessibility issue: PopoverTrigger asChild wraps a Badge with role="button", but it isn’t a native control. Use a real button element for the trigger (e.g., as="button" type="button") or add the necessary focus/keyboard support.
                <PopoverTrigger
                  onClick={(e) => {
                    e.stopPropagation();
                  }}
                  asChild
                >
                  <Badge
                    role="button"
                    size="2"
                    css={{
                      cursor: "pointer",
                      color: "$white",
                      fontSize: "$2",
                    }}
                  >

components/OrchestratorList/index.tsx:1249

  • Same pattern here: DropdownMenuTrigger asChild + Badge role="button" leaves the trigger non-focusable for keyboard users. Prefer a native <button> (via as="button" type="button") for the trigger element.
                <DropdownMenuTrigger
                  onClick={(e) => {
                    e.stopPropagation();
                  }}
                  asChild
                >
                  <Badge
                    role="button"
                    size="2"
                    css={{
                      cursor: "pointer",
                      color: "$white",
                      fontSize: "$2",
                    }}

components/OrchestratorList/index.tsx:1332

  • Same issue again: DropdownMenuTrigger asChild expects an interactive child, but Badge only gets role="button". Use a native button element (or add tabIndex + keyboard handlers) so this control is accessible.
                <DropdownMenuTrigger
                  onClick={(e) => {
                    e.stopPropagation();
                  }}
                  asChild
                >
                  <Badge
                    role="button"
                    size="2"
                    css={{
                      cursor: "pointer",
                      color: "$white",
                      fontSize: "$2",
                    }}

components/OrchestratorList/index.tsx:386

  • Badge is being used as the PopoverTrigger child with asChild, but only role="button" is added. A non-interactive element with role="button" is not focusable by default, so keyboard users may not be able to open the popover. Prefer rendering the trigger as a real <button type="button"> (e.g., as="button") and resetting button styles as needed, rather than relying on role alone.
            <Popover>
              <PopoverTrigger disabled={isNewlyActive} asChild>
                <Badge
                  role="button"
                  size="2"
                  css={{
                    cursor: !isNewlyActive ? "pointer" : "default",
                    color: "$white",
                    fontSize: "$2",
                  }}
                >

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

}
>
<Box>Orchestrator</Box>
<Box role="button">Orchestrator</Box>
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

Adding role="button" to the header label doesn’t make the column sortable header keyboard-accessible (the sort handlers are attached to the <Th> via getSortByToggleProps, and the <Box> isn’t focusable). Consider fixing this at the table header level by rendering an actual <button type="button"> inside the <Th>, moving the sort toggle props to that button, and setting appropriate aria-sort.

Suggested change
<Box role="button">Orchestrator</Box>
<Box>Orchestrator</Box>

Copilot uses AI. Check for mistakes.
Comment on lines 248 to 252
</Text>
</Box>
<ExplorerTooltip
role="group"
multiline
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

role="group" is being passed to ExplorerTooltip, which forwards props onto Radix Tooltip.Content. This overrides the content’s default role="tooltip", breaking tooltip semantics for assistive tech. Remove the role prop from ExplorerTooltip and, if grouping is needed, apply role="group" (and an accessible name if appropriate) on a wrapper around the trigger content instead.

Copilot uses AI. Check for mistakes.
Comment on lines 307 to 311
</Flex>
</ExplorerTooltip>
<ExplorerTooltip
role="group"
multiline
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

Same issue here: role="group" on ExplorerTooltip will be applied to the tooltip content element and can override the required role="tooltip". Drop the role prop on ExplorerTooltip and put grouping semantics on a separate wrapper if needed.

Copilot uses AI. Check for mistakes.
Comment on lines 164 to 172
@@ -171,7 +172,7 @@ const Index = ({
}}
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

This Box is now rendered as a <button>, but its styles don’t reset default button UI (background, border, padding), which can cause inconsistent appearance across browsers. In this codebase, other Box as="button" usages explicitly set background: "none" and border: "none" (e.g. components/Table/Pagination.tsx:35-45). Consider applying the same reset here.

Copilot uses AI. Check for mistakes.
}
>
<Box>Orchestrator</Box>
<Box role="button">Orchestrator</Box>
Copy link
Contributor

Choose a reason for hiding this comment

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

Box elements inside ExplorerTooltip were given role="button" but lack keyboard handlers and onClick listeners, making the role semantically incorrect and potentially confusing to screen readers.

Fix on Vercel

};

// For external links, use regular anchor tag to avoid Next.js Link issues
if (newWindow || href.startsWith("http")) {
Copy link
Contributor

Choose a reason for hiding this comment

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

PopoverLink component incorrectly uses regular anchor tag for internal links when newWindow=true, breaking Next.js routing

Fix on Vercel

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Triage

Development

Successfully merging this pull request may close these issues.

2 participants