Skip to content

fix: #4997 give icon-only controls accessible names - #5739

Merged
Jocs merged 1 commit into
marktext:developfrom
Pranav435:fix/4997-accessible-control-names
Oct 18, 2026
Merged

Jocs merged 1 commit into
marktext:developfrom
Pranav435:fix/4997-accessible-control-names

Conversation

@Pranav435

@Pranav435 Pranav435 commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Closes #4997

Summary

Icon-only controls across the editor and Preferences windows had no accessible name, and most of them could only be used with a mouse. With NVDA or VoiceOver running, the find bar's four buttons were read as "button" or "image button" with no name. Tab skipped the title bar, the side bar icons, the tab strip and the case/word/regex toggles entirely, because they were <div>s, <span>s and <i>s with click handlers. Nearly every switch and select in Preferences was announced as just "switch" or "combobox".

This PR fixes that for every icon-only control in the desktop renderer, following WCAG 2.2 AA:

  • Real buttons with names (1.1.1, 4.1.2, 2.1.1). Clickable <div>/<span>/<i> icons are now <button type="button"> with a translated aria-label. This covers the title bar (app menu, minimize, maximize/restore, close), the side bar icons, Save All / Close All, the close buttons in the tab strip and the opened-files list, New Tab, the find bar, the editor notification bar, the rename dialog, toast notifications, and the Preferences title bar. Close buttons include the file name ("Close notes.md"), so a list of them is not ten identical "Close" buttons.
  • State (4.1.2). The side bar icons and the case/word/regex toggles expose aria-pressed. The find bar's replace chevron exposes aria-expanded, and the app menu button aria-haspopup="menu".
  • Visible focus (2.4.7, 1.4.11). The global :focus { outline: none } stays. The new buttons get a 2px :focus-visible ring instead, drawn in --sideBarTitleColor. I didn't use --focusColor because it is 2.4–2.8:1 against the background in the default, Ayu Light and Everforest Light themes, under the 3:1 a focus indicator needs. The title colour is at least 3.1:1 in all 33 bundled themes, and a unit test checks that.
  • Hover-only buttons are reachable. Save All / Close All and the opened-file close button used display: none until hover, so Tab could never reach them. They are now clipped (clip-path: inset(50%)) instead, and show on hover or keyboard focus. The tab strip's close and New Tab buttons also show on :focus-visible.
  • Tooltips. title attributes on these controls are replaced by a small IconTooltip wrapper around el-tooltip, the same pattern as the media viewer (Add a zoom/pan viewer and image export for rendered diagrams #5554). It fixes two things I ran into: el-tooltip calls preventDefault on Enter/Space keydown of its trigger, which cancels the button's click (so trigger-keys is cleared), and it now closes on Escape (1.4.13).
  • Preferences form controls (4.1.2, 2.5.3). bool, select, range, textBox and fontTextBox pass their visible description as aria-label, so the name matches the label on screen. The "learn more" link icons are buttons named "Learn more about {setting}", and the keybindings docs link is a button instead of an <a> without href.
  • Target size (2.5.8). The keybinding table's edit/reset/unbind buttons and the case/word/regex toggles are now 24×24. Their icons stay where they were.
  • Focus order (2.4.3). The window controls were laid out with row-reverse, so Tab went close → maximize → minimize. They are now in DOM order, left to right.

New strings: titleBar.* (application menu, minimize, maximize, restore, close), editor.tabs.close ("Close {name}") and preferences.learnMore, added to all 12 locales. Minimize and Close reuse each locale's existing translations.

Screen reader results

Same keyboard walk through the window, before and after. The NVDA output is from its speech log, and the VoiceOver output is from its "last phrase".

Control NVDA 2026.2 before NVDA after VoiceOver after
Side bar icons not reachable "Files, toggle button, pressed" "Files, selected, toggle button"
Window controls (Windows) not reachable "Minimize, button" … "Close, button" n/a
Tab strip not reachable "Close readme.md, button", "New Tab, button" "Close readme.md, button", "New Tab, button"
Find previous / next "button", "button" "Find Previous, button", "Find Next, button" "Find Previous, button"
Case sensitive toggle not reachable "Case Sensitive, toggle button, not pressed", then Space: "pressed" "Case Sensitive, toggle button", then "selected"
Replace chevron not reachable "Replace, button, expanded" "Replace, expanded, button"

One design note: the tooltips open on hover only, not on keyboard focus. I tried focus first, but while open el-tooltip points the trigger's aria-describedby at itself, so NVDA read every label twice ("Files, toggle button, pressed … Files"). Keyboard users get the focus ring, and screen reader users get the name.

axe-core 4.13 (WCAG 2.0–2.2 A/AA rules) on the same states: button-name in the find bar goes from 4 to 0, label across the seven Preferences pages goes from 56 to 0 (57 on Windows, which has one extra setting), and target-size on the keybindings page goes from 45 to 0. The remaining findings are pre-existing and out of scope here (see below).

Screenshots

Windows 11, default light theme, reached with Tab. Before this change none of these controls could take keyboard focus, so there was no "before" state to capture.

Search icon in the side bar with a dark 2px focus ring around it Custom title bar with the focus ring on the Minimize button
Side bar icon Window controls
Opened files list with the close button for todo.md visible and focused Tab strip with the focus ring on the New Tab button
Close button revealed on focus New Tab
Find bar with Case Sensitive switched on and the focus ring on Select whole word Tooltip reading Table of Contents next to the side bar icon
Find bar toggles (24px targets, icons unmoved) Hover tooltip

One Dark, to show the ring follows the theme:

Search icon focused in the One Dark theme with a light focus ring Find bar in One Dark with the focus ring on Select whole word Tooltip reading Table of Contents in One Dark

Type of change

  • Bug fix (non-breaking, fixes an issue)
  • New feature (non-breaking, adds functionality)
  • Breaking change (causes existing functionality to change)
  • Documentation update

Test plan

  • New tests added
    • test/unit/specs/icon-button-a11y.spec.ts parses every renderer template and fails if a <button>/<el-button> without text has no aria-label, if an IconTooltip wraps a control labelled differently from the tooltip, or if it wraps a control that needs aria-expanded/aria-haspopup. It also checks the focus ring is at least 3:1 against the side bar, editor and floating surfaces in every theme. On develop the first check lists 12 unnamed buttons.
    • test/e2e/issue-4997-icon-button-names.spec.ts drives the built app: names and pressed state on the side bar, Enter/Space activating a tooltip-wrapped button, hover tooltip + Escape, no tooltip or aria-describedby on keyboard focus, focus ring, Tab reaching New Tab and opening a tab, find bar names/state, window control names and order, and an accessible name for every visible control on all seven Preferences pages.
    • search-prefill.spec.ts gets a stub for the new import.
  • Manually tested on: macOS 27 (VoiceOver) and Windows 11 25H2 (NVDA 2026.2)
  • pnpm run lint, pnpm run typecheck and the unit suite pass on both machines. On Windows the new e2e spec passes in full (16/16), along with find-replace, media-viewer, media-viewer-modal and diagram-viewer. On macOS the window-controls case skips because there are no custom window controls there.
  • Full e2e suite, this branch against unmodified develop on the same machines: Windows fails the same 11 specs on both (353 passed on the branch, 341 on develop; the difference is the new spec). macOS fails 9 on develop and the same 9 plus three hover-tooltip cases on the branch, which pass on Windows (see the VoiceOver note below). On macOS: external-reload-undo (source-mode scrollTop) and issue-5394-image-resize-reload. On Windows: image-relative-path, issue-2245-window-menu-bar, issue-3020-second-instance-args, issue-3148-sidebar-delete-key, issue-5344-image-content-hash, search-prefill-from-selection and sidebar-auto-reveal-528. Hover-tooltip specs (media-viewer, diagram-viewer and two of mine) also fail on my Mac only because VoiceOver is running there with "mouse pointer follows VoiceOver cursor", which moves the real pointer; they pass on Windows. source-toc-scroll and titlebar-selection-word-count were intermittent on my Mac on both branches. I didn't touch those areas.

Notes for reviewers

  • IconTooltip keeps the <button> in the caller's template, so the caller's scoped styles still reach it. Its comment documents the two el-tooltip constraints (Enter/Space and the ARIA attributes it overwrites). The find bar chevron and the app menu button are not wrapped for that reason.
  • .plain-button in index.css only strips the browser's button chrome and adds the focus ring. Each component keeps its own sizing and colours.
  • Out of scope, and happy to open issues for them: keyboard navigation inside the file tree and tab strip (role="tree"/tablist), the side bar resize handle, <html lang>, pre-existing colour-contrast findings, Element Plus's unlabelled autocomplete wrapper, and Muya's floating toolbars (they need a keyboard path before naming them helps).

By submitting this pull request, I confirm that my contribution is made under the terms of the MIT license.

Icon-only controls in the editor and Preferences windows were divs, spans
and icons with click handlers, so screen readers announced them without a
name and Tab skipped them. Make them buttons with translated aria-labels,
expose pressed and expanded state, and add a :focus-visible ring that meets
3:1 against the background in every bundled theme.

Label the Preferences switches, selects, sliders and inputs with their
visible descriptions, and size the keybinding and search toggle buttons to
24px.
@Jocs

Jocs commented Oct 17, 2026 •

Copy link
Copy Markdown
Member

I see that IconTooltip already uses Element Plus's el-tooltip and centralizes the keyboard handling, so the wrapper makes sense.

Could you make the tooltip background, text, and arrow colors follow the current theme, or at least distinguish between light and dark themes? Please also increase the delay before it appears.

@Pranav435

@Jocs
Jocs merged commit 1d9222d into marktext:develop Oct 18, 2026
10 checks passed
@Pranav435

Copy link
Copy Markdown
Contributor Author

Thanks for merging this, and for doing the tooltip theming and delay in #5788. Sorry I didn't get to it before you did. The longer delay should also help VoiceOver users who have the pointer following the VoiceOver cursor, since tooltips will open less often as they move around.

@Pranav435 Pranav435 mentioned this pull request Oct 19, 2026
1 task done
Jocs added a commit that referenced this pull request Oct 21, 2026
…button (#5830)

* fix(desktop): show themed title-bar control icons and space the menu button

The window-control icons (minimize/maximize/close) are only visible in dark themes because each theme recolours their SVG. #5739 changed the icon wrapper from a div to a span, so all 30 theme rules that target .frameless-titlebar-button > div > svg stopped matching and the icons fell back to the base black fill, invisible on a dark title bar.

Point the rules at the element the template renders, and give .left-toolbar a gap so the document statistics no longer sit flush against the application-menu button.

Closes #5828

* chore(desktop): remove titlebar style tests per review

---------

Co-authored-by: Jocs <luoran1988@126.com>
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.

a11y: icon-only buttons lack visible text and persistent accessible names

2 participants