Skip to content

fix(a11y): sweep focus:ring-primary/50 to full-opacity ring for WCAG AA contrast (#1218)#1265

Merged
Chris0Jeky merged 3 commits into
mainfrom
fix/1218-focus-ring-contrast
Jul 2, 2026
Merged

fix(a11y): sweep focus:ring-primary/50 to full-opacity ring for WCAG AA contrast (#1218)#1265
Chris0Jeky merged 3 commits into
mainfrom
fix/1218-focus-ring-contrast

Conversation

@Chris0Jeky

@Chris0Jeky Chris0Jeky commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Closes #1218.

What

Sweeps every remaining ring-primary/50 usage (18 focus: + 1 selected-state; 0 left repo-wide) to the full-opacity treatment PR #1216 established on BoardSettingsModal, and removes 5 compile-proven dead focus:ring-primary classes from checkboxes (no ring-width utility + no forms plugin ⇒ the class never painted; checkbox focus is compliantly indicated by the global *:focus-visible rule at 4.52:1).

Why

Issue #1218: a 50%-opacity primary ring on the dark surfaces is marginal — adversarial review of this PR computed 3.00–3.49:1 against the actual adjacent surfaces (the issue's '~2.8:1' was overstated, but the treatment was at/near the WCAG 2.1 AA 3:1 floor for UI component indicators). Full opacity lifts the rendered indicators to 8.41–10.07:1 on every adjacent surface, verified across all shipped themes (ring color + surfaces are build-time-static Tailwind hex; theme switches only remap --td-*/--paper vars).

Files (6, all in components/board/)

  • ColumnEditModal.vue (canonical token pattern) — 2 rings full-opacity, 1 dead class removed
  • FilterPanel.vue — 2 rings, 2 dead classes removed
  • LabelManagerModal.vue — 2 rings
  • card-modal/CardModalForm.vue — 4 rings, 1 dead class removed
  • card-modal/CardModalComments.vue — 3 rings
  • card-modal/CardModalLabels.vue — 1 selected-state ring full-opacity, 1 dead class removed

Review

Two independent adversarial reviews (a11y/theme lens + completeness/regression lens) posted below; all findings of every severity fixed in-PR or tracked (#1266).

Verification

  • grep -rn "ring-primary/50" frontend/taskdeck-web/src0 hits (incl. non-focus usages)
  • npm run typecheck → 0 errors; npm run lint → 0 errors (6 pre-existing warnings)
  • Targeted specs for all touched components: 137/137 pass
  • No unit/E2E/visual spec, snapshot baseline, Tailwind safelist, or CI color-audit couples to these class strings (review-verified)

…AA contrast (#1218)

Uniform mechanical sweep of all 18 remaining focus:ring-primary/50 usages
(6 board components incl. the canonical ColumnEditModal) to the full-opacity
focus:ring-primary treatment PR #1216 established on BoardSettingsModal:
50% primary on dark bg-surface-container-high (#2a2a2a) is ~2.8:1, below
the WCAG 2.1 AA 3:1 minimum for UI component indicators.
Copilot AI review requested due to automatic review settings July 2, 2026 15:09

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the focus ring styling across multiple Vue components in the board directory, including ColumnEditModal, FilterPanel, LabelManagerModal, CardModalComments, CardModalForm, and CardModalLabels. Specifically, it replaces the semi-transparent focus ring class focus:ring-primary/50 with the solid focus:ring-primary class on various input fields, checkboxes, textareas, and select elements to ensure a more prominent focus state. There are no review comments, so I have no feedback to provide.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 improves keyboard focus visibility across board-related modals/panels by replacing low-contrast focus:ring-primary/50 focus ring styling with full-opacity focus:ring-primary, aligning focus indicators with WCAG AA contrast expectations on dark surfaces.

Changes:

  • Replaced focus:ring-primary/50 with focus:ring-primary in 6 board components (18 occurrences).
  • Kept changes strictly to class strings (no behavioral/layout changes) to minimize regression risk.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
frontend/taskdeck-web/src/components/board/ColumnEditModal.vue Updates text/number inputs and checkbox focus ring to full-opacity primary.
frontend/taskdeck-web/src/components/board/FilterPanel.vue Updates search/select and checkbox focus ring to full-opacity primary.
frontend/taskdeck-web/src/components/board/LabelManagerModal.vue Updates label name/color inputs focus ring to full-opacity primary.
frontend/taskdeck-web/src/components/board/card-modal/CardModalForm.vue Updates card form inputs/textarea/date/checkbox focus ring to full-opacity primary.
frontend/taskdeck-web/src/components/board/card-modal/CardModalComments.vue Updates comment/reply textareas focus ring to full-opacity primary.
frontend/taskdeck-web/src/components/board/card-modal/CardModalLabels.vue Updates label checkbox focus ring to full-opacity primary.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…5 checkboxes (#1218 review)

Without a ring-width utility (focus:ring-2) and with no forms plugin,
focus:ring-primary only sets --tw-ring-color and paints nothing. Checkbox
keyboard focus is indicated by the global *:focus-visible box-shadow rule
(style.css) at 4.52:1 — already WCAG AA compliant.
@Chris0Jeky

Copy link
Copy Markdown
Owner Author

Adversarial Code Review

Two independent review agents (a11y/theme-correctness lens + sweep-completeness/regression lens), both empirically grounded (compiled the actual Tailwind v4 output; computed WCAG relative-luminance contrast against the real theme tokens).

CRITICAL

  • None

HIGH

  • None

MEDIUM

  • 5 of the 18 swept usages (all checkboxes) are compile-proven dead code. The checkbox controls (w-4 h-4 text-primary border-outline-variant rounded focus:ring-primary) carry no ring-width utility (focus:ring-2) and the project has no @tailwindcss/forms plugin (tailwind.config.js plugins: []), so focus:ring-primary compiles to only --tw-ring-color — no box-shadow is ever emitted. Both the old /50 and the new full-opacity class paint zero pixels there. Checkbox keyboard focus is actually (and compliantly, 4.52:1) indicated by the global *:focus-visible box-shadow rule in style.css.
  • The repo's genuinely-last ring-primary/50 survived in a file this PR touches: CardModalLabels.vue:23 — the always-on selected-label state ring (ring-2 ring-offset-2 ring-primary/50), a rendered ~3.44:1 indicator. WCAG 1.4.11 applies to state indicators, not just focus rings.

LOW

  • The '~2.8:1' premise in issue Sweep focus:ring-primary/50 → full-opacity ring for WCAG AA contrast (repo-wide) #1218 / the PR body does not reproduce. Alpha-compositing rgba(255,179,174,.5) over the actual adjacent surfaces computes to 3.00–3.49:1 (marginal pass), not ~2.8:1. The sweep is still clearly right — full opacity lifts the indicator to 8.41–10.07:1 across every adjacent surface, in ALL shipped themes (ring color and surfaces are build-time-static hex; paper/paper-night only remap --td-*/--paper vars) — but the 'below AA' framing was overstated. PR body corrected.
  • Pre-existing quirk observed (not introduced here): the selected-label ring uses ring-offset-2 without a ring-offset color, so Tailwind v4's default --tw-ring-offset-color: #fff paints a 2px white halo on the dark surface. Tracked as a follow-up issue.

Bot Comments Addressed

  • None yet (no bot reviews had landed at review time; any subsequent round will be addressed before merge).

Verified-sound (could not refute)

  • Sweep completeness: 0 focus:ring-<color>/<opacity> variants remain anywhere in src (any color, any opacity, focus/focus-visible, .vue/.ts).
  • No unit/E2E/visual test, snapshot baseline, Tailwind safelist, or CI color-audit script couples to these class strings (the visual spec that screenshots these modals has no committed baselines and is label-gated).
  • Git history shows the /50 came from mechanical dark-mode tokenization (3116a05f, BUG: Dark mode theme missing on Edit Card modal, Edit Column modal, and Filter panel #514) — no deliberate design rationale.
  • Full opacity is nowhere worse than /50: the only failing combination (#ffb3ae on paper-card #fbf7ee, 1.60:1) never occurs behind these controls.

Summary

0 CRITICAL / 0 HIGH / 2 MEDIUM / 2 LOW. Nothing merge-blocking; both MEDIUMs fixed in-PR (see follow-up comment).

@Chris0Jeky

Copy link
Copy Markdown
Owner Author

Adversarial Review — Fixes Applied

Finding Severity Fix Verified
5 checkbox focus:ring-primary classes are dead code (no ring-width utility, no forms plugin) MEDIUM Removed the no-op class from all 5 checkboxes — 97806d1e + 7eb01c49 (CardModalLabels' checkbox). The compliant global *:focus-visible indicator (4.52:1) remains their focus treatment. grep: 0 rounded focus:ring-primary" left; 137/137 targeted specs; typecheck clean
Last rendered ring-primary/50 (selected-label state ring) survived in CardModalLabels.vue:23 MEDIUM Swept to full ring-primary7eb01c49. grep: 0 ring-primary/50 anywhere in src; specs green
'~2.8:1' premise doesn't reproduce (computed 3.00–3.49:1 marginal) LOW PR body corrected to the computed numbers (marginal→robust framing).
Pre-existing white ring-offset halo on dark surfaces (not introduced here) LOW Tracked: #1266 (out of scope — design decision on the selected-state treatment).

Net effect of the PR after fixes: 13 rendered focus rings lifted 3.0–3.5:1 → 8.4–10.1:1; 1 rendered state ring lifted 3.44:1 → full opacity; 6 dead classes removed; 0 ring-primary/50 remain repo-wide.

All findings addressed. CI status: PENDING (running on 97806d1e).

@Chris0Jeky Chris0Jeky merged commit 1bb3cac into main Jul 2, 2026
20 checks passed
@Chris0Jeky Chris0Jeky deleted the fix/1218-focus-ring-contrast branch July 2, 2026 16:07
@github-project-automation github-project-automation Bot moved this from Pending to Done in Taskdeck Execution Jul 2, 2026
@Chris0Jeky

Copy link
Copy Markdown
Owner Author

Post-merge record correction: the PR body prose said 'removes 6 compile-proven dead focus:ring-primary classes' — the correct count is 5 (ColumnEditModal 1 + FilterPanel 2 + CardModalForm 1 + CardModalLabels 1; the per-file list in the body already summed to 5). Body edited; caught by the independent accuracy review of the docs sync PR #1268.

Chris0Jeky added a commit that referenced this pull request Jul 2, 2026
* docs: STATUS post-merge sync — #1241 (#1263) + #1218 (#1265) shipped

* docs: masterplan delivery log — 2026-07-02 security + a11y slices (#1263, #1265)

* docs: MEDIUM correct dead-class count 6->5 + precise 19-site arithmetic + Codex-coverage phrasing (#1268 review)

* docs: LOW separate shipped vs still-open phrasing per Gemini (#1268)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Sweep focus:ring-primary/50 → full-opacity ring for WCAG AA contrast (repo-wide)

2 participants