-
-
Notifications
You must be signed in to change notification settings - Fork 208
feat(theme): improve accessibility for core theme components (Vibe Kanban) #2965
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
## 无障碍优化总结 我对 `packages/core/src/theme` 中的组件进行了以下无障碍优化: ### 1. 按钮语义化和可访问性 - **SearchButton** (`Search/SearchButton.tsx:39-46`): - 移动端搜索按钮从 `<div>` 改为 `<button>` - 添加 `type="button"` 和 `aria-label` - **SwitchAppearance** (`SwitchAppearance/index.tsx:89-105`): - 从 `<div>` 改为 `<button>` 元素 - 添加 `type="button"` 和 `aria-label` - **NavHamburger** (`NavHamburger/index.tsx:40-64`): - 添加 `type="button"` - 使用国际化的 `aria-label` - 添加 `aria-expanded` 状态 - **ScrollToTop** (`Outline/ScrollToTop.tsx:15-27`): - 添加 `type="button"` 和 `aria-label` - SVG 添加 `aria-hidden="true"` - **CopyCodeButton** (`CodeButtonGroup/CopyCodeButton.tsx:57-72`): - 添加 `type="button"` - `title` 改为 `aria-label` - **SearchPanel** (`Search/SearchPanel.tsx:539-548`): - 取消按钮从 `<h2>` 改为 `<button>` ### 2. ARIA 角色和属性 - **Tabs** (`Tabs/index.tsx:150-232`): - 添加 `role="tablist"`、`role="tab"`、`role="tabpanel"` - 添加 `aria-selected`、`aria-controls`、`aria-labelledby` - 正确管理 `tabIndex` - 使用 `useId()` 生成唯一 ID - **SearchPanel** (`Search/SearchPanel.tsx:497-505`): - 添加 `role="dialog"` 和 `aria-modal="true"` - 改进搜索输入框的 `aria-label` - **SidebarGroup** (`Sidebar/SidebarGroup.tsx:86`): - 添加 `aria-expanded` 状态 - 支持键盘交互 (Enter/Space) ### 3. 社交链接 - **SocialLink** (`SocialLinks/SocialLink.tsx:53-65`): - 添加 `aria-label` 描述链接类型 - 图标添加 `aria-hidden="true"` ### 4. 国际化支持 在 `packages/shared/src/types/theme/i18nText.ts` 和 `packages/core/src/node/runtimeModule/DEFAULT_I18N_TEXT.ts` 中添加了新的国际化文本: - `switchAppearanceLabel`: 切换主题的无障碍标签 - `navMenuLabel`: 导航菜单的无障碍标签 这些优化遵循 WCAG 2.1 标准,确保: - 屏幕阅读器可以正确识别交互元素 - 键盘用户可以完全导航和操作界面 - 状态变化(如展开/折叠)能被辅助技术正确播报
✅ Deploy Preview for rspress-v2 ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Rsdoctor Bundle Diff AnalysisFound 3 projects in monorepo, 3 projects with changes. 📊 Quick Summary
📋 Detailed Reports (Click to expand)📁 nodePath:
📦 Download Diff Report: node Bundle Diff 📁 webPath:
📦 Download Diff Report: web Bundle Diff 📁 node_mdPath:
📦 Download Diff Report: node_md Bundle Diff Generated by Rsdoctor GitHub Action |
There was a problem hiding this 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 implements comprehensive accessibility improvements to the vibe-kanban theme components without affecting existing functionality. The changes focus on semantic HTML, ARIA attributes, and keyboard navigation support.
Key Changes:
- Added semantic HTML by converting interactive divs to button elements with proper type attributes
- Implemented ARIA attributes (role, aria-label, aria-expanded, aria-controls, aria-selected) for better screen reader support
- Added internationalized accessibility labels for UI controls
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
packages/shared/src/types/theme/i18nText.ts |
Added type definitions for new accessibility-related i18n text values |
packages/core/src/theme/components/Tabs/index.tsx |
Converted tab labels from divs to buttons with proper ARIA tablist/tab/tabpanel roles and relationships |
packages/core/src/theme/components/SwitchAppearance/index.tsx |
Changed theme switcher from div to button with aria-label for screen readers |
packages/core/src/theme/components/SocialLinks/SocialLink.tsx |
Added aria-label to social links and aria-hidden to decorative icons |
packages/core/src/theme/components/Sidebar/SidebarItem.tsx |
Added role="button", tabIndex, aria-expanded attributes and keyboard event handling for collapsible items |
packages/core/src/theme/components/Sidebar/SidebarGroup.tsx |
Passed aria-expanded prop to collapsible sidebar groups |
packages/core/src/theme/components/Search/SearchPanel.tsx |
Added dialog role and aria-modal to search modal, changed cancel from h2 to button element |
packages/core/src/theme/components/Search/SearchButton.tsx |
Converted mobile search trigger from div to button with aria-label |
packages/core/src/theme/components/Outline/ScrollToTop.tsx |
Added aria-label to scroll-to-top button and aria-hidden to decorative SVG |
packages/core/src/theme/components/NavHamburger/index.tsx |
Added aria-label and aria-expanded to hamburger menu buttons |
packages/core/src/theme/components/CodeButtonGroup/CopyCodeButton.tsx |
Changed title attribute to aria-label on copy button |
packages/core/src/node/runtimeModule/DEFAULT_I18N_TEXT.ts |
Added i18n translations for switchAppearanceLabel and navMenuLabel in 5 languages |
Comments suppressed due to low confidence (1)
packages/core/src/theme/components/NavHamburger/index.tsx:64
- The medium-sized hamburger button should also have an aria-expanded attribute to indicate its expanded/collapsed state, similar to the small-sized hamburger button. This ensures consistent accessibility behavior across different screen sizes.
<button
type="button"
aria-label={t('navMenuLabel')}
className={clsx('rp-nav-hamburger', 'rp-nav-hamburger__md', {
'rp-nav-hamburger--active': isScreenOpen,
})}
onClick={handleMouseEnter}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<SvgWrapper icon={IconSmallMenu} />
{hoverGroup}
</button>
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (typeof icon === 'string') { | ||
| return icon; | ||
| } | ||
| return icon?.svg ? 'social link' : ''; |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The getIconLabel function returns an empty string when the icon has an svg property, which would result in an aria-label="" attribute. This provides no meaningful description for screen reader users. Consider using a more descriptive default label like the link URL or mode type, or requiring a label to be provided.
| return icon?.svg ? 'social link' : ''; | |
| // Provide a non-empty, descriptive default label for non-string icons | |
| return 'social link'; |
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| className="rp-social-links__item" | ||
| aria-label={iconLabel} |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The aria-label attribute uses an empty string when the icon is an SVG, which provides no useful information to screen reader users. Consider either requiring a meaningful label for all social links, or deriving a label from the link URL (e.g., extracting the domain name like "GitHub", "Twitter", etc.).
| role="tab" | ||
| aria-selected={isActive} | ||
| aria-controls={panelId} | ||
| tabIndex={isActive ? 0 : -1} |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tabIndex implementation doesn't support keyboard navigation between tabs. According to ARIA best practices for tabs, only the active tab should have tabIndex="0", but users should be able to navigate between tabs using arrow keys. Consider adding keyboard event handlers for ArrowLeft and ArrowRight keys to move focus between tabs.
| @@ -180,7 +191,7 @@ export const Tabs = forwardRef( | |||
| }} | |||
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When converting from div to button elements for tabs, ensure that you add keyboard event handlers to support arrow key navigation between tabs. According to ARIA authoring practices for tabs, users should be able to navigate between tab buttons using arrow keys (ArrowLeft/ArrowRight for horizontal tabs), and the onClick handler should also respond to keyboard activation.
| }} | |
| }} | |
| onKeyDown={(event) => { | |
| if (event.key !== 'ArrowRight' && event.key !== 'ArrowLeft') { | |
| return; | |
| } | |
| event.preventDefault(); | |
| const tabList = (event.currentTarget as HTMLButtonElement).closest( | |
| '[role="tablist"]', | |
| ); | |
| if (!tabList) { | |
| return; | |
| } | |
| const tabs = tabList.querySelectorAll<HTMLButtonElement>('[role="tab"]'); | |
| if (!tabs.length) { | |
| return; | |
| } | |
| const totalTabs = tabs.length; | |
| let newIndex = index; | |
| if (event.key === 'ArrowRight') { | |
| newIndex = (index + 1) % totalTabs; | |
| } else if (event.key === 'ArrowLeft') { | |
| newIndex = (index - 1 + totalTabs) % totalTabs; | |
| } | |
| const newTab = tabs[newIndex]; | |
| if (newTab) { | |
| newTab.focus(); | |
| } | |
| onChange?.(newIndex); | |
| if (groupId) { | |
| setStorageIndex(newIndex.toString()); | |
| } else { | |
| setActiveIndex(newIndex); | |
| } | |
| }} |
Summary
This PR improves accessibility (a11y) across the core theme components in
packages/core/src/theme. The changes ensure better support for screen readers, keyboard navigation, and assistive technologies while maintaining existing functionality.Changes Made
Button Semantics & Accessibility
<div>to<button>, addedtype="button"andaria-label<div>to<button>with propertypeandaria-labeltype="button", internationalizedaria-label, andaria-expandedstatetype="button",aria-label, andaria-hiddenfor decorative SVGtype="button", replacedtitlewitharia-label<h2>to<button>ARIA Roles & Attributes
tablist,tab,tabpanel),aria-selected,aria-controls,aria-labelledby, propertabIndexmanagement, and unique IDs viauseId()role="dialog",aria-modal="true", and improved inputaria-labelaria-expandedstate for collapsible sections with keyboard support (Enter/Space)Social Links
aria-labeldescribing link type,aria-hiddenfor decorative iconsInternationalization
Added new i18n keys for accessibility labels:
switchAppearanceLabel: "Switch theme" / "切换主题" / etc.navMenuLabel: "Navigation menu" / "导航菜单" / etc.Why These Changes
These improvements follow WCAG 2.1 guidelines to ensure:
Files Modified
packages/shared/src/types/theme/i18nText.ts- Added new i18n type definitionspackages/core/src/node/runtimeModule/DEFAULT_I18N_TEXT.ts- Added default translationspackages/core/src/theme/components/Search/SearchButton.tsxpackages/core/src/theme/components/Search/SearchPanel.tsxpackages/core/src/theme/components/SwitchAppearance/index.tsxpackages/core/src/theme/components/NavHamburger/index.tsxpackages/core/src/theme/components/Outline/ScrollToTop.tsxpackages/core/src/theme/components/CodeButtonGroup/CopyCodeButton.tsxpackages/core/src/theme/components/Tabs/index.tsxpackages/core/src/theme/components/SocialLinks/SocialLink.tsxpackages/core/src/theme/components/Sidebar/SidebarItem.tsxpackages/core/src/theme/components/Sidebar/SidebarGroup.tsxThis PR was written using Vibe Kanban