Skip to content

Commit 88aa609

Browse files
authored
refactor(a11y): comprehensive WCAG 2.2 AA accessibility improvements (#924)
* refactor(a11y): comprehensive WCAG 2.2 AA accessibility improvements Add skip-to-content link, landmark regions, ARIA attributes, keyboard navigation, focus styles, reduced-motion support, and i18n keys across all layouts, partials, shortcodes, and JS components. - Add skip nav link in baseof.html and id="content" on all <main> tags - Fix 404 page lang/dir attributes and add <main> landmark - Add aria-label to banner close, PDF iframe, search input/results - Remove aria-hidden from back-to-top button - Add aria-hidden to decorative external link icon - Add role="tablist" to tabs, aria-expanded to filetree/dropdowns - Wrap mermaid diagrams in role="img", asciinema in role="region" - Change theme toggle <p> items to <button role="menuitem"> with full keyboard navigation (Arrow/Home/End/Escape) - Add arrow-key keyboard navigation to tabs component - Separate sidebar collapsible button from link for independent keyboard access with aria-expanded sync - Sync aria-expanded on all dropdown toggles (theme, lang, navbar, hamburger, page context menu) - Add aria-live search status announcements - Add 13 new i18n keys, replace hardcoded aria-label strings - Add prefers-reduced-motion CSS override and focus-visible base styles - Add aria-label swap on code copy ("Copied!" feedback for AT) - Add aria-current to active TOC links - Wrap filetree in <ul> container for proper list semantics - Add unique aria-label to blog "Read more" links - Document accessibility guidelines in AGENTS.md * feat(a11y): enhance focus styles and accessibility for various components - Add focus-visible styles to badges, buttons, and links for improved keyboard navigation. - Update breadcrumb, sidebar, and TOC components to include focus-visible outlines. - Introduce new classes for focus states in the badge and tabs shortcodes. - Ensure consistent focus styles across all interactive elements to meet WCAG 2.2 AA standards. * feat(a11y): implement new focus-visible utilities and enhance accessibility styles - Introduce new utility classes for focus-visible states to improve keyboard navigation. - Update various components including badges, buttons, and search inputs to utilize new focus-visible styles. - Refactor existing focus styles to ensure consistency and compliance with accessibility standards. - Enhance breadcrumb, sidebar, and TOC components with updated focus-visible classes for better user experience. * chore: add .gitattributes to collapse generated files in PR diffs * fix: enhance accessibility and improve documentation - Added alt attributes to images in multiple language documentation files for better accessibility. - Updated the navbar title partial to remove unnecessary title attribute. - Improved search input accessibility by adding autocomplete="off". - Enhanced search partials in both navbar and sidebar with location context. - Updated SVG icons in various components to include aria-hidden and focusable attributes for improved accessibility compliance. * fix: improve giscus theme toggle functionality - Updated the theme toggle options selector to use a data attribute for better specificity. - Modified the event listener to use a setTimeout for the theme update, ensuring smoother transitions when the theme switcher is clicked. * fix: resolve axe-core WCAG AA violations across docs pages Add aria-labels to Hugo task list checkboxes, fix asciinema player timer accessible names, make Jupyter output cells keyboard-focusable, and add missing heading hierarchy in shortcodes docs for fa/ja/zh-cn. * feat: integrate accessibility testing with Playwright and enhance CI workflow - Added Playwright configuration for accessibility testing. - Implemented accessibility tests using axe-core for all English pages. - Created a GitHub Actions workflow to automate accessibility tests on pull requests. - Updated package dependencies to include @axe-core/playwright and @playwright/test. - Enhanced sidebar component with data attributes for improved accessibility styling. * fix: update base URL and improve accessibility labels across multiple languages - Changed the base URL in Playwright configuration and CI workflow from localhost:3000 to localhost:1313. - Added accessibility labels for screen readers in various language files, enhancing user experience for visually impaired users. - Updated the Asciinema script to dynamically set the playback time label for better accessibility compliance. * refactor: reorganize accessibility tests and update test directory structure - Moved accessibility tests from the e2e directory to a new tests directory for better organization. - Updated the test directory path in Playwright configuration. - Refactored the accessibility test implementation to improve code clarity and maintainability. * chore: update .gitignore to include Playwright test output directories - Added entries for 'playwright-report/' and 'test-results/' to the .gitignore file to prevent cluttering the repository with test artifacts. * refactor: enhance accessibility and improve focus styles across components - Removed unused utility for focus visibility in CSS and consolidated focus-visible styles for better maintainability. - Updated various components to use `role` attributes for improved accessibility, including menu items and buttons. - Enhanced theme toggle and language switch components with appropriate ARIA roles and attributes for better screen reader support. - Improved the handling of focus states in the navigation and context menus to ensure a consistent user experience. * chore: update dependencies and enhance accessibility features - Updated the 'serve' package version in package.json and package-lock.json for improved performance. - Removed unused 'xml2js' dependency to streamline the project. - Enhanced the Playwright configuration to better manage the web server setup for testing. - Improved accessibility in the language switcher and navigation menu by refining focus management and keyboard interactions. - Updated the back-to-top button to manage tabindex for better accessibility compliance. * feat: enhance mobile menu accessibility and keyboard interactions - Added ARIA attributes to manage visibility of the sidebar on mobile devices. - Implemented focus management for the sidebar when the menu is toggled. - Introduced keyboard support to close the menu with the Escape key. - Improved overall accessibility for the hamburger menu and sidebar interactions. * fix: refine mobile menu keyboard interaction and enhance navbar accessibility - Updated the Escape key functionality to close the menu only on mobile devices. - Added a new ARIA attribute to the hamburger menu button for improved accessibility.
1 parent 04803c4 commit 88aa609

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+2610
-245
lines changed

.gitattributes

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Mark generated files so they are collapsed by default in GitHub diffs
2+
assets/css/compiled/main.css linguist-generated=true
3+
docs/hugo_stats.json linguist-generated=true
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
name: Accessibility Tests
2+
3+
on:
4+
pull_request:
5+
branches: [main]
6+
7+
concurrency:
8+
group: accessibility-${{ github.head_ref || github.ref_name }}
9+
cancel-in-progress: true
10+
11+
defaults:
12+
run:
13+
shell: bash
14+
15+
jobs:
16+
a11y:
17+
runs-on: ubuntu-latest
18+
environment: accessibility
19+
env:
20+
HUGO_VERSION: 0.147.7
21+
steps:
22+
- name: Checkout
23+
uses: actions/checkout@v4
24+
with:
25+
fetch-depth: 0
26+
submodules: recursive
27+
28+
- name: Setup Go
29+
uses: actions/setup-go@v5
30+
with:
31+
go-version: "1.24"
32+
33+
- name: Setup Node
34+
uses: actions/setup-node@v4
35+
with:
36+
node-version: "22"
37+
cache: npm
38+
39+
- name: Setup Hugo
40+
run: |
41+
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
42+
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb
43+
44+
- name: Install dependencies
45+
run: npm ci
46+
47+
- name: Install Playwright Chromium
48+
run: npx playwright install chromium
49+
50+
- name: Build site
51+
run: npm run build
52+
53+
- name: Run accessibility tests
54+
run: npm run test:a11y
55+
56+
- name: Upload report
57+
if: always()
58+
uses: actions/upload-artifact@v4
59+
with:
60+
name: accessibility-report
61+
path: playwright-report/
62+
retention-days: 14

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@ public/
33
resources/
44

55
.hugo_build.lock
6+
7+
# Playwright
8+
playwright-report/
9+
test-results/

AGENTS.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,28 @@ The `docs/` directory serves as both documentation and testing ground:
9494
- Chroma syntax highlighting themes in `assets/css/chroma/`
9595
- CSS compilation requires Node.js dependencies (PostCSS, Tailwind CSS v4+)
9696

97+
#### Rebuilding CSS after template changes
98+
99+
Tailwind CSS relies on `docs/hugo_stats.json` to know which HTML tags, classes, and IDs are actually used in the built site, so it can tree-shake unused styles. When you modify layouts, partials, or shortcodes you must **regenerate `hugo_stats.json` first**, then rebuild the CSS:
100+
101+
1. **Generate `docs/hugo_stats.json`** — Run Hugo with the `dev.toml` config (which sets `build.buildStats.enable = true`):
102+
103+
```bash
104+
# Using npm (starts a dev server that writes hugo_stats.json on every rebuild):
105+
npm run dev:theme
106+
107+
# Or a one-shot build using the raw Hugo command:
108+
hugo --config=hugo.yaml,../dev.toml --themesDir=../.. --source=docs
109+
```
110+
111+
2. **Build the CSS** — With an up-to-date `hugo_stats.json` in place, compile the stylesheet:
112+
113+
```bash
114+
npm run build:css
115+
```
116+
117+
> **Why two steps?** `dev.toml` mounts `docs/hugo_stats.json` into the Hugo asset pipeline (`assets/notwatching/hugo_stats.json`) and configures a cache-buster so that changes to the stats file trigger a CSS recompile during `dev:theme`. When running outside the dev server you need to perform these steps manually in order.
118+
97119
### Customization Points
98120

99121
- Custom partials: `layouts/_partials/custom/`
@@ -147,6 +169,22 @@ The `docs/` directory serves as both documentation and testing ground:
147169
- Compiled output goes to `assets/css/compiled/main.css`
148170
- Prettier formatting for Go templates and code consistency
149171

172+
### Accessibility (WCAG Compliance)
173+
174+
All new features and UI changes must follow the [Web Content Accessibility Guidelines (WCAG) 2.2](https://www.w3.org/TR/WCAG22/) at the **AA** conformance level. Key requirements:
175+
176+
- **Semantic HTML**: Use appropriate elements (`<nav>`, `<main>`, `<aside>`, `<button>`, `<ul>`, etc.) instead of generic `<div>`/`<span>` where applicable.
177+
- **ARIA attributes**: Add `aria-label`, `aria-expanded`, `aria-controls`, `aria-current`, `role`, and other ARIA attributes to interactive components (menus, toggles, dropdowns, modals) so screen readers can interpret them.
178+
- **Keyboard navigation**: All interactive elements must be reachable and operable via keyboard (`Tab`, `Enter`, `Escape`, arrow keys). Manage focus appropriately when opening/closing menus, modals, and drawers.
179+
- **Focus indicators**: Never remove visible focus outlines. Use the existing `hextra-focus` utility or equivalent visible focus ring styles.
180+
- **Color contrast**: Text and interactive elements must meet WCAG AA contrast ratios (4.5:1 for normal text, 3:1 for large text). Verify in both light and dark modes.
181+
- **Images and icons**: Decorative SVGs/icons should have `aria-hidden="true"`. Meaningful images need descriptive `alt` text.
182+
- **Skip links and landmarks**: Preserve existing skip-navigation links and ARIA landmark roles (`role="navigation"`, `role="search"`, etc.).
183+
- **Live regions**: Use `aria-live` for dynamic content updates (e.g., search results, status messages) so assistive technology announces changes.
184+
- **Form controls**: Associate `<label>` elements with inputs. Provide accessible names for buttons that contain only icons.
185+
186+
When introducing a new component or modifying an existing one, verify it works with keyboard-only navigation and review the rendered HTML for proper semantics and ARIA usage.
187+
150188
### Testing & Quality Assurance
151189

152190
- Test all changes in `docs/` before releasing

assets/css/compiled/main.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/css/components/archives.css

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
11
.hextra-archive-timeline {
2-
border-left: 2px solid rgba(0, 0, 0, 0.15);
3-
}
4-
5-
.dark .hextra-archive-timeline {
6-
border-left-color: rgba(255, 255, 255, 0.15);
2+
@apply hx:border-l-2 hx:border-black/15 hx:dark:border-white/15;
73
}

assets/css/components/badge.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
.hextra-badge {
22
@apply hx:inline-flex hx:items-center;
3-
}
3+
}

assets/css/components/search.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
li {
33
@apply hx:mx-2.5 hx:wrap-break-word hx:rounded-md hx:contrast-more:border hx:text-gray-800 hx:contrast-more:border-transparent hx:dark:text-gray-300;
44
a {
5-
@apply hx:focus-visible:outline-none hx:focus:outline-none hx:block hx:scroll-m-12 hx:px-2.5 hx:py-2;
5+
@apply hx:focus-visible:outline-none hx:block hx:scroll-m-12 hx:px-2.5 hx:py-2;
66
}
77

88
.hextra-search-title {

assets/css/components/sidebar.css

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@
99
}
1010

1111
.hextra-sidebar-container {
12-
li > div {
12+
li > .hextra-sidebar-children {
1313
@apply hx:h-0;
1414
}
15-
li.open > div {
15+
li.open > .hextra-sidebar-children {
1616
@apply hx:h-auto hx:pt-1;
1717
}
18-
li.open > a > span > svg > path {
18+
li.open > .hextra-sidebar-item > .hextra-sidebar-collapsible-button > svg > path {
1919
@apply hx:rotate-90;
2020
}
2121
}

assets/css/styles.css

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,37 @@ body {
4343
@apply hx:outline-none hx:ring-2 hx:ring-primary-200 hx:ring-offset-1 hx:ring-offset-primary-300 hx:dark:ring-primary-800 hx:dark:ring-offset-primary-700;
4444
}
4545

46+
@utility hextra-focus-visible {
47+
@apply hx:focus-visible:outline-none hx:focus-visible:ring-2 hx:focus-visible:ring-primary-200 hx:focus-visible:ring-offset-1 hx:focus-visible:ring-offset-primary-300 hx:dark:focus-visible:ring-primary-800 hx:dark:focus-visible:ring-offset-primary-700;
48+
}
49+
50+
@utility hextra-focus-visible-inset {
51+
@apply hx:focus-visible:outline-none hx:focus-visible:ring-inset hx:focus-visible:ring-2 hx:focus-visible:ring-primary-200 hx:dark:focus-visible:ring-primary-800 hx:focus-visible:ring-offset-0;
52+
}
53+
4654
@layer base {
4755
abbr:where([title]) {
4856
cursor: help;
4957
}
5058
}
5159

60+
@media (prefers-reduced-motion: reduce) {
61+
*, *::before, *::after {
62+
animation-duration: 0.01ms !important;
63+
animation-iteration-count: 1 !important;
64+
transition-duration: 0.01ms !important;
65+
scroll-behavior: auto !important;
66+
}
67+
}
68+
69+
@layer base {
70+
:where(a, button, [role="tab"], [role="menuitem"], [role="menuitemradio"], input, select, textarea, [tabindex="0"]):not(
71+
[class*="hextra-focus-visible"]
72+
):focus-visible {
73+
@apply hx:hextra-focus;
74+
}
75+
}
76+
5277
@import "./typography.css";
5378
@import "./highlight.css";
5479
@import "./components/cards.css";

0 commit comments

Comments
 (0)