Skip to content

feat(accessibility): add ARIA semantics and landmark roles - #594

Open
baich110 wants to merge 15 commits into
OpenListTeam:mainfrom
baich110:accessibility-improvements
Open

feat(accessibility): add ARIA semantics and landmark roles#594
baich110 wants to merge 15 commits into
OpenListTeam:mainfrom
baich110:accessibility-improvements

Conversation

@baich110

Copy link
Copy Markdown

Summary

This PR implements comprehensive accessibility (a11y) improvements for the OpenList frontend, based on a file-by-file source code audit. The changes address critical barriers that prevent TalkBack (Android screen reader) users from using the app's core features.

Related issues: #2800 (Chinese) / #2801 (English)

Problem

OpenList's Android app renders its UI through a WebView. While the Web interface works functionally, interactive elements lacked ARIA semantic annotations, making the app nearly unusable for visually impaired users who rely on screen readers. A full-project audit found only ~25 aria-label instances, concentrated in image preview controls, with zero coverage in core interaction areas (file list, context menu, toolbar).

Changes

P0 - Critical (File List & Context Menu)

src/pages/home/folder/ListItem.tsx

  • Added role="listitem" + aria-label to list item container (merges filename, size, type, and modified date into one announceable unit)
  • Added tabIndex={0} for keyboard focus
  • Added aria-label to selection checkbox

src/pages/home/folder/GridItem.tsx

  • Added role="listitem" + aria-label to grid item container
  • Added tabIndex={0} for keyboard focus
  • Added alt attribute to thumbnail images
  • Added aria-label to selection checkbox

src/pages/home/folder/context-menu.tsx

  • Added aria-label to menu container
  • Added aria-label to all menu items (rename, move, copy, delete, share, decompress)

P1 - High (Semantic Structure & Toolbar)

src/pages/home/folder/List.tsx

  • Added role="list" to list container
  • Added role="button", tabIndex={0}, aria-label to sort column headers
  • Added aria-label to select-all checkbox

src/pages/home/Layout.tsx

  • Wrapped main content in <Box as="main" role="main"> for landmark navigation

src/pages/home/header/Header.tsx

  • Added role="banner" to header

src/pages/home/Nav.tsx

  • Added role="navigation" + aria-label to breadcrumb navigation

src/pages/home/toolbar/Icon.tsx

  • Added aria-label + tabIndex={0} to CenterIcon (rename, move, copy, delete, decompress, share, cancel select, download)
  • Added aria-label + tabIndex={0} to RightIcon (refresh, new file, mkdir, upload, offline download, settings, etc.)

src/pages/home/toolbar/BackTop.tsx

  • Added aria-label + tabIndex={0} to back-to-top button

P2 - Medium (Dynamic Content & UI Controls)

src/utils/notify.tsx

  • Added role="status" + aria-live="polite" to notification container (Toast notifications now announced by screen readers)

src/components/FullLoading.tsx

  • Added role="status" + aria-busy="true" + aria-label to loading spinners

src/components/SwitchColorMode.tsx

  • Added aria-label + tabIndex={0} to color mode toggle

src/pages/home/Password.tsx

  • Added aria-label to password input

P3 - Low (HTML Structure)

index.html

  • Added skip-to-main-content link for keyboard navigation

src/app/App.tsx

  • Added aria-hidden="true" to routing progress bar (decorative)

Impact

  • Before: ~25 aria-label instances, core interaction areas completely blank
  • After: ~88 accessibility attributes across 15 files, covering file list, context menu, toolbar, page landmarks, notifications, and loading states
  • TalkBack users can now: browse file lists with announced filenames/sizes/dates, understand toolbar button functions, receive notification feedback, and use landmark navigation

Testing

Unable to build locally in this environment. The changes are purely additive ARIA attributes and semantic HTML -- no logic changes. Recommend testing with:

  • TalkBack on Android (primary target)
  • NVDA on desktop browser
  • axe DevTools for automated validation

Background

We are from the Chinese visually impaired community (20,000+ visually impaired developers, 200,000+ users). We conducted a full source code audit of both the backend and frontend, with all issues documented in #2800/#2801. This PR implements the fixes for the documented issues.

@jyxjjj jyxjjj changed the title [Accessibility] Add ARIA semantics and landmark roles for TalkBack/screen reader support feat(accessibility): add ARIA semantics and landmark roles Jul 17, 2026
@PIKACHUIM
PIKACHUIM requested review from KirCute and xrgzs and a lite review from Copilot August 7, 2026 15:32
@PIKACHUIM

Copy link
Copy Markdown
Member

cv before v4.3

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 semantics across the OpenList frontend to make core UI flows usable with screen readers (notably Android TalkBack in a WebView) by adding ARIA labeling, landmark roles, and live-region behavior.

Changes:

  • Added ARIA semantics (labels/roles/tabIndex) across file list/grid items, toolbars, context menus, and navigation/breadcrumbs.
  • Added landmark roles (banner, main, navigation) and a skip-to-content link to support better structure and keyboard navigation.
  • Improved announcement of dynamic UI (toast notifications, loading spinners) for assistive technologies.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
index.html Adds skip-to-main-content link for keyboard navigation.
src/app/App.tsx Marks routing progress bar as decorative (aria-hidden).
src/components/FullLoading.tsx Adds loading status semantics (role="status", aria-busy).
src/components/SwitchColorMode.tsx Adds accessible labeling/focusability to color mode toggle.
src/pages/home/Layout.tsx Wraps main content with a <main> landmark and id target for skip link.
src/pages/home/Nav.tsx Adds navigation landmark semantics to breadcrumb container.
src/pages/home/Password.tsx Adds accessible label to password input.
src/pages/home/folder/GridItem.tsx Adds list semantics + labels to grid items, selection checkbox, and thumbnail alt text.
src/pages/home/folder/List.tsx Adds list semantics and accessible attributes to sortable headers and select-all checkbox.
src/pages/home/folder/ListItem.tsx Adds listitem semantics + combined aria-label and selection checkbox labeling.
src/pages/home/folder/context-menu.tsx Adds aria-labels for context menu container and items.
src/pages/home/header/Header.tsx Adds banner landmark semantics to the header.
src/pages/home/toolbar/BackTop.tsx Adds labeling/focusability to back-to-top control.
src/pages/home/toolbar/Icon.tsx Adds labeling/focusability to toolbar icon components.
src/utils/notify.tsx Makes toast notifications announceable via live-region semantics.
Suppressed comments (3)

src/pages/home/toolbar/Icon.tsx:28

  • CenterIcon is made focusable via tabIndex, but it removes focus outlines (_focus sets outline: none) and doesn't provide a keyboard activation handler. This makes keyboard focus hard to track and prevents Enter/Space from triggering the click action.

This issue also appears on line 53 of the same file.

      <Icon
        class={`toolbar-${props.name}`}
        aria-label={t(`home.toolbar.${props.name}`)}
        tabIndex={0}
        _hover={{
          bgColor: hoverColor(),
        }}
        _focus={{
          outline: "none",
        }}

src/pages/home/toolbar/Icon.tsx:64

  • RightIcon is focusable (tabIndex={0}) even when tips is omitted, which can produce an interactive element without an accessible name (current code sets aria-label to undefined). It also removes focus outlines and lacks keyboard activation for Enter/Space.
    <Tooltip
      disabled={!props.tips}
      placement="left"
      withArrow
      label={t(`home.toolbar.${props.tips}`)}
    >
      <Icon
        // bgColor="$info4"
        color={getMainColor()}
        aria-label={props.tips ? t(`home.toolbar.${props.tips}`) : undefined}
        tabIndex={0}
        _hover={{

src/pages/home/toolbar/BackTop.tsx:56

  • The back-to-top control is now focusable, but it removes focus outlines and doesn't handle keyboard activation. For a non-button icon element, Enter/Space won't trigger onClick automatically.
        <Icon
          _focus={{
            outline: "none",
          }}
          cursor="pointer"
          boxSize="$7"
          p="$1"
          rounded="$lg"
          as={FiArrowUp}
          aria-label="Back to top"
          tabIndex={0}
          onClick={() => {
            window.scrollTo({ top: 0, behavior: "smooth" })
          }}
        />

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

Comment on lines +81 to 85
role: "button",
tabIndex: 0,
"aria-label": `${t(`home.obj.${col.name}`)}, ${reverse() ? t("global.descending") : t("global.ascending")}`,
onClick: () => {
if (col.name === orderBy()) {
Comment on lines 21 to 29
<Icon
cursor="pointer"
boxSize={icon().size}
as={icon().component}
onClick={toggleColorMode}
aria-label="Toggle color mode"
tabIndex={0}
p={icon().p}
/>
Comment thread index.html
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<a href="#main-content" class="skip-link" style="position:absolute;left:-9999px;top:0;z-index:9999;">Skip to main content</a>
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.

[Feature] 希望前端可以对无障碍方面做优化

3 participants