Files
coder/site
christinandAndrew Aquino 4e4603eae9 fix(site/src/pages/UserSettingsPage/AppearancePage): raise theme swatch ring contrast to 3:1 (#30539)
The ring around each theme swatch in the Appearance settings (Theme mode
set to "Sync with system") used the decorative border color: `#27272A`
on the `#09090B` page, 1.33:1. The Dark default swatch is filled with
the page color, so that ring is its only visible boundary, which fails
WCAG 1.4.11 Non-text Contrast.

The ring now uses the existing `surface-invert-secondary` token, which
the Slider handle already uses for its border: `#A1A1AA` on the dark
page (7.8:1) and `#3F3F46` on white (10.5:1). The blue selected and
focus outline is unchanged.

Fixes DEQ-597

## Before and after

Appearance > Theme mode "Sync with system", resting state. Dark default
is the first swatch.

| | Before | After |
|---|---|---|
| Dark page | <img width="360"
src="https://raw.githubusercontent.com/coder/coder/assets/a11y-theme-swatch-contrast/dark-before.png"
/> | <img width="360"
src="https://raw.githubusercontent.com/coder/coder/3e8883931834186feab055d104b7c62fb352240d/dark-after.png"
/> |
| Light page | <img width="360"
src="https://raw.githubusercontent.com/coder/coder/assets/a11y-theme-swatch-contrast/light-before.png"
/> | <img width="360"
src="https://raw.githubusercontent.com/coder/coder/3e8883931834186feab055d104b7c62fb352240d/light-after.png"
/> |

<details>
<summary>Decision log</summary>

- **Resting border, not a focus state.** 1.4.11 requires the control's
boundary to be visible without interaction. The swatches already show a
blue `content-link` outline when focused or selected, and the audit
flagged the unselected, unfocused swatches.
- **Existing token instead of a new one.** `surface-invert-secondary`
already exists on `main` and is used as a control border by the Slider
handle. It passes 3:1 in both modes. None of the existing `border-*`
tokens do on the dark page (`border-secondary` is 1.9:1).
- **Before images** were captured on the same dev server with the
previous border value (`hsl(var(--border))`) applied through an injected
style. Light-page images force the `light` class on `<html>`.

</details>

<details>
<summary>Verification</summary>

- Playwright on the dev server at `/settings/appearance`: the Dark
default swatch's computed border is `rgb(161, 161, 170)` on the dark
page (`rgb(9, 9, 11)`) and `rgb(63, 63, 70)` on the light page
(`rgb(255, 255, 255)`).
- `pnpm check` and `pnpm lint` pass.
- vitest unit (`src/pages/UserSettingsPage`, `src/theme`): 13 files, 399
tests pass.

</details>

---

_Generated by Coder Agents on behalf of @chrifro._

---------

Co-authored-by: Andrew Aquino <dawneraq@gmail.com>
2026-10-13 10:02:22 +02:00
..
…
…
…
…
…