feat(frontend): WCAG AAA accessibility + UI consistency improvements#616
feat(frontend): WCAG AAA accessibility + UI consistency improvements#616
Conversation
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Snapshot WarningsEnsure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice. OpenSSF ScorecardScorecard details
Scanned Files
|
Manifest Changes vs v0.1.0-beta.27base✅ No changes debug✅ No changes crds✅ No changes |
📸 UI ScreenshotsCaptured 44 screenshots (11 light, 22 dark, 22 high-contrast mode) 📥 Download
Pages Captured
Screenshots are generated automatically on each PR that modifies frontend code. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #616 +/- ##
=======================================
Coverage 68.72% 68.72%
=======================================
Files 159 159
Lines 33620 33620
=======================================
Hits 23107 23107
Misses 8985 8985
Partials 1528 1528
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Frontend accessibility improvements targeting WCAG 2.1 AA compliance across the Vue 3 UI, adding high-contrast theming and automated accessibility checks.
Changes:
- Fix landmark/ARIA issues (remove duplicate
<main>, add ARIA labels/roles, route-change focus management). - Address contrast violations by replacing opacity-based “muted” text with explicit Telekom Scale design tokens and adding high-contrast + forced-colors support.
- Add automated accessibility tooling (eslint accessibility plugin + axe-core Playwright E2E audits) and document the user-facing change in the changelog.
Reviewed changes
Copilot reviewed 24 out of 25 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/tests/e2e/a11y.spec.ts | Adds axe-core Playwright accessibility audits across key pages and theme modes. |
| frontend/src/views/SessionErrorView.vue | Uses a themed surface token for code block backgrounds (contrast/readability). |
| frontend/src/views/SessionBrowser.vue | Removes duplicate <main> landmark and replaces opacity-muted styling with explicit disabled text token. |
| frontend/src/views/PendingApprovalsView.vue | Removes duplicate <main> landmark. |
| frontend/src/views/MyPendingRequests.vue | Removes duplicate <main> landmark. |
| frontend/src/views/DebugSessionDetails.vue | Removes duplicate <main> landmark. |
| frontend/src/views/DebugSessionCreate.vue | Removes duplicate <main> landmark and replaces opacity-based step styling with explicit text tokens. |
| frontend/src/views/DebugSessionBrowser.vue | Removes duplicate <main> landmark. |
| frontend/src/views/BreakglassView.vue | Removes duplicate <main> landmark and adds an accessible label to the loading spinner. |
| frontend/src/views/BreakglassSessionReview.vue | Removes duplicate <main> landmark. |
| frontend/src/router/index.ts | Implements post-navigation focus management for improved screen reader/keyboard UX. |
| frontend/src/main.ts | Improves initialization error announcement (role="alert") and uses functional danger token. |
| frontend/src/components/debug-session/ClusterSelectGrid.vue | Replaces opacity-muted styling with explicit token to improve contrast. |
| frontend/src/components/debug-session/BindingOptionsGrid.vue | Uses explicit text tokens (including inverted text) instead of hardcoded/opacity-muted colors. |
| frontend/src/components/CountdownTimer.vue | Adds role="timer", accessible labeling, and a live region for announcements. |
| frontend/src/components/common/ActionButton.vue | Adds aria-busy and an accessible label for the loading spinner. |
| frontend/src/components/BreakglassSessionCard.vue | Replaces opacity-muted styling with explicit token to improve contrast. |
| frontend/src/components/BreakglassCard.vue | Replaces opacity-muted styling with explicit token to improve contrast. |
| frontend/src/components/ApprovalModalContent.vue | Hides decorative emoji from screen readers (aria-hidden). |
| frontend/src/assets/base.css | Adds high-contrast theme overrides and expands forced-colors support; adjusts chip text colors for contrast. |
| frontend/src/App.vue | Adds high-contrast mode toggle (persisted), applies root attribute, and improves nav accessibility attributes. |
| frontend/package.json | Adds dev dependencies for accessibility linting and axe-core Playwright integration. |
| frontend/package-lock.json | Locks new accessibility tooling dependencies. |
| frontend/eslint.config.mjs | Enables vue accessibility lint rules (with targeted rule disablements). |
| CHANGELOG.md | Documents the new user-facing accessibility improvements under Unreleased. |
Files not reviewed (1)
- frontend/package-lock.json: Language not supported
057baf1 to
2748add
Compare
e9d8e5b to
af8ec12
Compare
2851477 to
3e05a8b
Compare
- Add aria-pressed attribute to high-contrast toggle button in App.vue so assistive technology can announce the button's on/off state - Fix lint formatting in a11y.spec.ts error page test declaration Notes on pre-existing fixes verified in this pass: - CountdownTimer unit tests for SR announcements already present - ErrorToasts compact spacing test already present - SCALE_COMPONENT_EXCLUDES replaced by isScaleShadowDomNode() filter - router/index.ts already cancels focusTimerId on every navigation - localStorage guarded with try/catch in both getInitialHighContrast/toggleHighContrast - BreakglassSessionCard/BreakglassCard cluster duplication already fixed - NotFoundView CTA already uses CSS variables
- Remove aria-pressed from high-contrast toggle button: Scale nav-item may assign an implicit menuitem role to slotted content where aria-pressed is invalid per WAI-ARIA spec; convey toggle state via the dynamic aria-label instead (now includes '(currently on/off)')
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 48 out of 49 changed files in this pull request and generated 3 comments.
Files not reviewed (1)
- frontend/package-lock.json: Language not supported
Comments suppressed due to low confidence (1)
frontend/src/App.vue:407
aria-currentis being set on<scale-telekom-nav-item>, but the actual navigable element here is the inner<a>. This is both a semantics issue (screen readers typically expectaria-currenton the current link) and it likely prevents the active-link contrast override insrc/assets/base.css(targetsscale-telekom-nav-item a[aria-current="page"], see base.css:266) from applying. Consider movingaria-currentonto the<a>element (and/or ensure Scale forwards it to the anchor).
<scale-telekom-nav-item
v-for="item in primaryNavItems"
:key="item.id"
variant="main-nav"
:active="activeNavId === item.id"
:aria-current="activeNavId === item.id ? 'page' : undefined"
>
<a :href="navHref(item)" @click="handlePrimaryNavClick($event, item)">
{{ item.label }}
</a>
- Add aria-pressed attribute to high-contrast toggle button in App.vue so assistive technology can announce the button's on/off state - Fix lint formatting in a11y.spec.ts error page test declaration Notes on pre-existing fixes verified in this pass: - CountdownTimer unit tests for SR announcements already present - ErrorToasts compact spacing test already present - SCALE_COMPONENT_EXCLUDES replaced by isScaleShadowDomNode() filter - router/index.ts already cancels focusTimerId on every navigation - localStorage guarded with try/catch in both getInitialHighContrast/toggleHighContrast - BreakglassSessionCard/BreakglassCard cluster duplication already fixed - NotFoundView CTA already uses CSS variables
- Remove aria-pressed from high-contrast toggle button: Scale nav-item may assign an implicit menuitem role to slotted content where aria-pressed is invalid per WAI-ARIA spec; convey toggle state via the dynamic aria-label instead (now includes '(currently on/off)')
3ed089d to
46a2161
Compare
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Summary
Comprehensive frontend accessibility improvements targeting WCAG 2.1 AAA compliance across the entire k8s-breakglass Vue 3 frontend, with contrast fixes for all theme modes, design token consistency, automated a11y regression testing, and extensive UI/UX improvements.
See
frontend/SCALE_DEVIATIONS.mdfor documented contrast deviations from the Scale Design System and their rationale.Accessibility (WCAG AAA — 7:1 contrast ratio)
Contrast Fixes
hsla(0,0%,0%,0.65)additional text color with solid AAA-compliant values across all 4 theme combinations (light, dark, HC-light, HC-dark)--cta-bg/--cta-bg-hoverCSS variables with AAA-compliant dark magenta (#8a004f / #6b003d)#8e004a(light) /#ff8cc8(dark) for AAA on respective backgrounds#93a8ff(7.1+:1 on#151517)--telekom-color-functional-warning-standardwith accessible--tone-chip-warning-text--telekom-color-functional-danger-standardwith--tone-chip-danger-textHigh Contrast Mode
:root[data-high-contrast="true"]theme layer with maximum contrast text (#000/#fff)@media (forced-colors: active)for Windows High ContrastlocalStorage)docs/branding.mdStructural & ARIA Fixes
<main>landmarks in 8 view filesrole="timer",aria-label,aria-busyto interactive elements<span aria-hidden="true">Design Token Consistency
--radius-xsCSS token (was referenced in 5 files but never declared)border-radius: 999pxwithvar(--radius-pill)across 6 filesSessionErrorView(px/rem → Scale design tokens)TimelineGridhighlight radius (8px→var(--radius-md))--tone-chip-muted-*,--surface-raised,--surface-section.tag-iconCSS class inPendingApprovalsView--cta-bg/--cta-bg-hoverCSS variablesCode Quality Improvements
defineEmits(["..."])to proper TypeScript signaturesdropped,timeout,idleexpired,cancelled,canceled,completed,ended)scheduledStartTime: string | null)isScaleShadowDomNode(): No longer suppresses fixable Scale components (scale-tag, scale-button, scale-checkbox, scale-dropdown)toEqual([])instead oftoHaveLength(0))Automated A11y Testing
disableRules()for rules we cannot fix externallyisScaleShadowDomNode()narrowed to only suppress genuinely unfixable shadow DOM violationseslint-plugin-vuejs-accessibilityfor lint-time a11y checksfrontend/playwright.a11y.config.ts)Documentation
frontend/SCALE_DEVIATIONS.md: Complete list of Scale Design System contrast deviations with rationaledocs/branding.md: New "High Contrast Mode" section documenting the 4 theme modes, contrast targets, and CI testingfrontend/README.md: New "Accessibility Testing" section with instructionsCHANGELOG.md: Updated with PR feat(frontend): WCAG AAA accessibility + UI consistency improvements #616 referenceFiles Changed (47 files, +1733/−287)
base.cssa11y.spec.ts,playwright.a11y.config.ts<main>removal, spacing, layout fixes, CSS variable usageCountdownTimer.spec.ts,ErrorToasts.spec.tsCHANGELOG.md,branding.md,README.md,SCALE_DEVIATIONS.mdci.yml,ui-screenshots.ymleslint.config.mjs,package.jsonValidation
vue-tsc --noEmiteslint .npm test)Known Limitations
scale-telekom-header,scale-telekom-nav-list, etc.) produce shadow DOM ARIA violations (aria-required-children,button-name,aria-prohibited-attr) that cannot be fixed from the application side. These are disabled in automated checks viadisableRules().aria-pressedbecause Scale's navigation parent assigns an implicitmenuitemrole wherearia-pressedis invalid per ARIA spec. Toggle state is communicated via dynamicaria-labeltext instead.SCALE_DEVIATIONS.md.