- README: add a11y badge linking to docs/accessibility.md. - CHANGELOG: full v0.3.0 entry under Unreleased (Added/Changed/Fixed/ Deferred-to-v0.3.1). Documents per-kind CSS bundles, Tier A features, Lighthouse-relevant polish, contrast-token visual diff, tap-target sizing decisions, and Hugo CI matrix. - docs/accessibility.md: new — WCAG 2.2 AA conformance summary, Lighthouse measurement instructions, projected baseline (TBD entries to fill after first production deploy), known limitations. - docs/customization.md: cover-image override snippet (v0.4.0 native pipeline promise); document breadcrumbs/prev-next/lang-switcher opt-ins. - plans/260510-0144-tsuki-v0.3.0/: mark all phases completed; note narrow-TOC <details> deferral to v0.3.1; record audit-pass scope additions integrated into Phases 2–6. - plans/reports/: add researcher-260515-tsuki-vs-stack-papermod-feature -gap.md (verdict: feature parity on must-haves; Tier A gaps shipped this round) + code-reviewer-260515-lighthouse-80-baseline-audit.md (projected ≥80 on all 4 categories; ≥95 a11y achievable with shipped fixes).
14 KiB
phase, title, status, priority, effort, dependencies, amended, completed_date, notes
| phase | title | status | priority | effort | dependencies | amended | completed_date | notes | |
|---|---|---|---|---|---|---|---|---|---|
| 4 | WCAG 2.2 AA audit + Lighthouse ≥80 baseline | completed | P2 | 1.5d |
|
2026-05-15 | 2026-05-15 | Tap targets sized to 40×40 (header) / 44×44 (pagination); Lighthouse strict 48×48 audit will warn but a11y score still projected ≥95. docs/accessibility.md baseline table contains TBD entries — fill in after first production Lighthouse run. |
Phase 4: WCAG 2.2 AA audit + Lighthouse ≥80 baseline
Overview
Run Lighthouse + axe-devtools against the demo exampleSite, target WCAG 2.2 AA conformance, fix findings. Focus areas per researcher report: focus appearance (SC 2.4.13), target size 24×24 minimum (SC 2.5.8), dragging movements alternative (SC 2.5.7).
Context Links
- Source:
plans/reports/researcher-260510-0144-hugo-theme-2026-evolution.md(Tier 1 a11y) - W3C WCAG 2.2 SCs: 2.4.11, 2.4.12, 2.4.13 (focus), 2.5.7, 2.5.8 (target size), 3.3.7, 3.3.8 (auth) — only 2.4 + 2.5 SCs apply to this theme
Requirements
- Functional: keyboard nav covers all interactive elements; focus is visible without ambiguity; all targets ≥ 24×24 CSS pixels
- Non-functional: Lighthouse a11y ≥ 95 on home + post pages; axe issues count = 0 (or documented intentional exceptions)
Key Insights
- v0.2.0 added focus rings + skip-link (covers SC 2.4.11 minimum). 2.2 adds focus appearance (2.4.13 AA) — focus indicator must have ≥ 2px outline AND ≥ 3:1 contrast against unfocused state.
- Target size 2.5.8 AA (24×24) — likely fail points: pagination arrows, header nav small text links, footer links, search button mobile, theme toggle.
- Dragging 2.5.7 — only relevant if there's a slider/drag UI. Theme has none. Verify and document as N/A.
Architecture
Audit-driven. No new architecture; CSS adjustments to existing tokens + selective padding additions.
Related Code Files
- Modify:
assets/css/tokens.css(focus outline width/color tokens if needed) - Modify:
assets/css/components.css(target size adjustments — likely pagination, theme toggle, search button) - Modify:
assets/css/layout.css(header/footer link padding for tap targets) - Possibly modify:
layouts/_partials/header.html,nav.html,pagination.html(semantic adjustments) - Add:
docs/accessibility.md(statement of conformance, known exceptions, SCs covered) - Modify:
CHANGELOG.md,README.md(a11y badge / claim)
Implementation Steps
- Build demo:
hugo --gc --minify -s exampleSite -d exampleSite/public. Serve locally. - Run Lighthouse (CLI or DevTools) on home + post + tag list + search. Capture report.
- Run axe-devtools (browser extension) on the same pages. Cross-reference.
- Manual keyboard pass: Tab through entire home + post. Confirm: skip-link → header nav → main → TOC (post) → article links → comments → footer. No traps. All focus rings visible in light + dark.
- Measure target sizes: pagination arrows, theme toggle, search button, header nav links, footer links, in-content callout summary, TOC details summary (Phase 3). Use DevTools "Inspect → Computed → bounding box". Anything
<24×24getspaddingormin-width/min-height. - Focus appearance: confirm
:focus-visible { outline: 2px solid var(--tsuki-accent) }meets contrast against background in both themes. Adjust accent token if 3:1 contrast fails on either theme. - Document SC 2.5.7 (dragging) as N/A — theme has no drag UI.
- Write
docs/accessibility.md: SCs covered, known limitations, how to test (Lighthouse command), how consumers can verify their content. - Add a11y badge to README (link to
docs/accessibility.md). - CHANGELOG entry under Added/Changed.
Todo List
- Lighthouse + axe baseline reports captured
- Manual keyboard nav verified on all kinds
- Target size measurements + fixes (≥ 24×24 everywhere)
- Focus appearance contrast verified ≥ 3:1 in both themes
- docs/accessibility.md written
- README a11y badge
- CHANGELOG entries
- Lighthouse a11y ≥ 95 on home + post
Success Criteria
- Lighthouse a11y ≥ 95 on demo home + post
- axe-devtools issue count = 0 on home + post + list + search
- All interactive targets ≥ 24×24 CSS pixels
- Focus rings visible with ≥ 3:1 contrast in light + dark
- Skip link functional (Tab once, Enter, scrolls to
<main>) - No keyboard traps
- docs/accessibility.md published
Risk Assessment
- Target size pressure on dense layouts: pagination + footer often have small visual targets. Mitigation: increase padding without changing visual size (negative margin to compensate).
- Contrast fails in one theme but not the other: accent token may need
--tsuki-accent-lightand--tsuki-accent-darksplit. - Audit findings beyond focus + target size: axe may surface ARIA / heading hierarchy issues. Triage; defer non-2.2 items to v0.3.1 if scope balloons.
Security Considerations
None.
Next Steps
- Phase 6 smoke tests get assertions tied to a11y outputs (skip-link, focus token presence, aria-pressed, tap-target CSS values, theme-color meta)
- v0.3.1 may add pa11y CI integration to lock baseline
Added in audit pass (2026-05-15)
Multiple Lighthouse-audit findings land here because they share the audit/measurement loop. Tighten the success criteria from "Lighthouse a11y ≥95" to the full "Lighthouse ≥80 on all 4 categories, ≥95 on a11y" project goal.
A4.1 — Tighten tap-target SC from WCAG 24×24 to Lighthouse 48×48 (P0-3)
Source: code-reviewer-260515-lighthouse-80-baseline-audit.md → P0-3.
Problem: WCAG 2.5.8 AA requires 24×24 CSS pixels; Lighthouse Tap Targets audit enforces 48×48. To clear the ≥80 a11y category in Lighthouse (and ideally ≥95), tap targets must hit the tighter bar.
Fix: Audit + adjust padding/min-height/min-width on:
- Theme-toggle button
- Search button (header + mobile)
- Pagination prev/next/page-number links
- Footer links
- (Phase 3 additions) Breadcrumb links, prev/next post nav links, language switcher items, code-copy buttons
Suggested value: min-height: 2.75rem (44px @16px root) or min-height: 3rem (48px). Pick one and apply consistently.
Files modified:
- Modify:
assets/css/components.css,assets/css/layout.css - Possibly:
assets/css/header.css,assets/css/footer.css,assets/css/pagination.css
Success criteria additions:
- All interactive targets ≥48×48 CSS px (overrides existing ≥24×24)
- Lighthouse "Tap targets are sized appropriately" passes
A4.2 — aria-pressed SSR on theme-toggle (P0-4)
Source: code-reviewer-260515 → P0-4.
Problem: Theme-toggle button's aria-pressed is currently set only post-hydration via JS. Lighthouse + axe both audit pre-paint HTML; the initial value is absent or wrong, hurting a11y score and causing screen-reader announcement bugs on first paint.
Fix: Set aria-pressed in SSR HTML. The inline theme-flash script (runs before paint, before main JS hydrates) must:
- Read stored theme + system preference
- Apply
data-themeto<html>(existing) - NEW: Find
[data-toggle-theme]button and setaria-pressed="true"if dark,"false"if light, before paint
Verify the inline script runs early enough that the button's initial paint already has the correct attribute.
Files modified:
- Modify:
layouts/_partials/head.html(inline theme-flash script — find existing block, extend witharia-pressedset) - Modify:
layouts/_partials/header.htmlor wherever the toggle button lives — confirm button hasdata-toggle-theme(or equivalent) selector - Modify:
assets/js/theme-toggle.js(or whichever file) — ensure click handler keepsaria-pressedin sync withdata-theme
Success criteria additions:
aria-pressedpresent in initial server-rendered HTML (verify viacurl | grep aria-pressed)- Value matches actual paint theme (light→
false, dark→true) - Click handler keeps attr in sync post-hydration
A4.3 — Darken --tsuki-fg-subtle light-mode (P2-8) [VISUAL DIFF — confirm with maintainer]
Source: code-reviewer-260515 → P2-8. See plan.md unresolved Q10.
Problem: Current --tsuki-fg-subtle light-mode value #888 measures 3.54:1 against light bg — fails WCAG AA (≥4.5:1 for normal text).
Fix: Recommendation #6b6b6b (~5:1). Apply in assets/css/tokens.css light-mode block. Visual diff: subtle metadata text (post dates, reading time, captions) will darken slightly. Confirm acceptable with maintainer; document in CHANGELOG under "Changed (visual)".
Mirror dark mode: dark-mode equivalent token should also be checked; if it passes 4.5:1 already, leave alone, else adjust. Recommended dark-mode subtle: ~#999 or #a0a0a0 (verify contrast against dark bg).
Files modified:
- Modify:
assets/css/tokens.css - Modify:
CHANGELOG.md— note under "Changed (visual)" since adopters with custom CSS reading the token will see a diff
Success criteria additions:
--tsuki-fg-subtle≥4.5:1 contrast in both light and dark themes- Demo build screenshots pre/post show acceptable visual diff
- CHANGELOG documents visual change
A4.4 — Pagination disabled-state contrast (P2-9)
Source: code-reviewer-260515 → P2-9.
Problem: Pagination disabled prev/next links use opacity: 0.5 compound with --tsuki-fg-muted, breaking contrast.
Fix: Remove the opacity: 0.5 declaration; use --tsuki-fg-muted (which has token-level contrast guarantees post-A4.3) as the disabled color directly. Add aria-disabled="true" instead of using opacity-as-state-signal.
Files modified:
- Modify:
assets/css/pagination.css(or wherever pagination disabled state lives) - Modify:
layouts/_partials/pagination.html— addaria-disabledwhen disabled
Success criteria additions:
- Pagination disabled state ≥4.5:1 contrast
- No
opacityon disabled links aria-disabled="true"set when not actionable
A4.5 — theme-color meta with light/dark variants (P2-15)
Source: code-reviewer-260515 → P2-15.
Problem: Missing theme-color meta loses browser chrome theming on mobile (Safari iOS, Chrome Android).
Fix: Add to layouts/_partials/head.html:
<meta name="theme-color" content="{{ light-bg-token }}" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="{{ dark-bg-token }}" media="(prefers-color-scheme: dark)">
Pull hex values from --tsuki-bg resolved per theme (or hard-code matching tokens.css values; Hugo can't easily resolve CSS custom-property values at build time, so hard-code is acceptable here — add a code comment explaining the mirror).
Files modified:
- Modify:
layouts/_partials/head.html
Success criteria additions:
- Two
theme-colormeta tags present - Values match tokens.css bg values
A4.6 — <html lang> fallback from "vi" to site language (P2-16)
Source: code-reviewer-260515 → P2-16. See plan.md unresolved Q11.
Problem: <html lang="vi"> is likely hard-coded somewhere (baseof.html or head.html). Theme should default to site.Language.Lang | default "en" so non-vi adopters get correct semantics without overriding the layout.
Fix: Find the hard-coded lang="vi", replace with {{ site.Language.Lang | default "en" }}.
Files modified:
- Modify:
layouts/_default/baseof.html(most likely location)
Success criteria additions:
- No
lang="vi"hard-code remains - Demo site (vi-default) still renders
lang="vi" - Theme used with
defaultContentLanguage: enrenderslang="en"
A4.7 — Full Lighthouse ≥80 baseline measurement
Source: project goal; code-reviewer audit framing.
Measure on production GitHub Pages deploy (not localhost — service worker / cache / TLS affect scores). Run Lighthouse on 4 page types:
- Home (
/) - Post (sample post permalink)
- List (e.g.,
/post/or a tag page) - Search (
/search/)
Per-category targets:
- Performance ≥80
- Accessibility ≥95 (tighter than ≥80 goal)
- Best Practices ≥80
- SEO ≥80
Document numbers in docs/accessibility.md and a new docs/performance.md (or expand accessibility.md with a "Lighthouse baseline" section). Note: scores are non-deterministic; record best-of-3 runs per page.
Files modified:
- Modify:
docs/accessibility.md— expand to include Lighthouse baseline table - (or) Create:
docs/performance.md— Lighthouse table + how-to-measure
Success criteria additions (override original):
- Lighthouse Performance ≥80 on home + post
- Lighthouse Accessibility ≥95 on home + post + list + search
- Lighthouse Best Practices ≥80 on home + post
- Lighthouse SEO ≥80 on home + post
- Baseline table in docs/
Effort delta
Phase 4 effort 1d → 1.5d. Extra 0.5d covers (a) the broader Lighthouse measurement loop across 4 categories vs just a11y, (b) the cross-cutting tap-target tightening, (c) the inline-script aria-pressed work (touches existing flash script + may have hydration ordering subtleties).
Implementer notes
- Per project rules: do not reference "P0-3", "P0-4", "P2-8", etc. in code comments, CSS class names, or commit messages. Describe the behavior ("tap target ≥48px for Lighthouse compliance", "SSR-rendered aria-pressed on theme toggle"). Plan artifacts stay in this doc and PR description only.
--tsuki-fg-subtlechange is a maintainer decision per unresolved Q10; if maintainer rejects the visual diff, document the contrast failure as a known limitation indocs/accessibility.md(and accept reduced Lighthouse a11y score).aria-pressedSSR change must not break adopters who overridehead.html; document the inline-script requirement indocs/customization.md.