Files
Ekklesia/docs/architecture/decisions/0066-default-purple-accent.md
T
ekklesiaandClaude Opus 5.5 0009f62fbd
CI / verify (pull_request) Successful in 1m14s
CI / infrastructure-smoke (pull_request) Successful in 42s
CI / package-release (pull_request) Successful in 49s
CI / migration-smoke (pull_request) Successful in 2m30s
CI / image-build (pull_request) Successful in 1m8s
CI / deploy-smoke (same-origin) (pull_request) Successful in 45s
CI / package-publication (pull_request) Successful in 56s
CI / deploy-smoke (split) (pull_request) Successful in 8m55s
Say Remove for displays, and make the default accent purple (#210050)
Administrators now remove a display: the card's Remove, "Remove display?",
and "Display removed". The platform still revokes the device's credential;
the endpoint, fields, and security history keep revoke/revoked (ADR 0065).

The default accent is a deep purple anchored on #210050, set in the shared
design tokens only (ADR 0066, superseding ADR 0060's teal in part). The
anchor is the light primary action with white on it; the light accent text,
focus ring, and selection, and the dark theme's lavender accent, are
variants on its hue chosen for contrast. The teal-tinted neutrals move to a
faint violet-grey at the same lightness, so every contrast pairing holds.
The contrast test gains six pairings the accent depends on, and pins the
anchor.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 19:14:46 -05:00

113 lines
5.9 KiB
Markdown

# 0066: The default accent is a deep purple anchored on `#210050`
- Status: Accepted
- Date: 2026-10-16
- Supersedes, in part: [0060](0060-light-dark-system-theme-and-motion.md) (the teal accent and the
teal-tinted charcoal of its designed dark theme)
- Extends: [0039](0039-shared-design-tokens-and-elements.md),
[0060](0060-light-dark-system-theme-and-motion.md)
## Context
Ekklesia's default theme was accented in teal. The light primary action was a pale teal (`#4fb3a8`)
with dark text on it. ADR 0060's designed dark theme described "teal-tinted charcoal" backgrounds and
"the same teal accent, slightly lifted". The neutrals carried the same teal tint, so did the
selection and information washes, and the whole product read as teal.
The maintainer chose a purple default anchored on `#210050`. Branding settings will later let an
administrator choose the accent. This decision changes only the **default**.
A single hex cannot serve every place the accent is used. `#210050` is very dark: it is 17.8:1
against white, but about 1.2:1 against a dark-theme page, where it would all but vanish.
## Decision
### The accent is set through the semantic tokens only
The change is made in `@ekklesia/design-tokens`' registry, and nowhere else. No Dashboard component
names a purple. The shared `@ekklesia/ui` elements and the shell already draw from `accent.*`,
`state.selected-*`, and `status.info-*`, so they inherit the new values. Modules read the same
`--ekklesia-*` custom properties (ADR 0040), so their token inheritance is unchanged.
### `#210050` is the light primary action; every other value is a variant on its hue
Each variant keeps the anchor's OKLCH hue (≈291.5°). Lightness and chroma are chosen for contrast
where the variant is used.
| Token | Light | Dark | Used for |
| --- | --- | --- | --- |
| `accent.base` | **`#210050`** | `#ac9bf6` | primary button, tab underline, spinner, brand mark |
| `accent.hover` | `#340e71` | `#bbacff` | hovered primary button |
| `accent.active` | `#120033` | `#9a88e2` | pressed primary button |
| `accent.foreground` | `#ffffff` | **`#210050`** | text on the accent |
| `accent.strong` | `#4d2a99` | `#c8bcff` | accent text and icons; checked box and radio |
| `accent.subtle` | `#f0eeff` | `#2e2849` | selected tab wash |
| `accent.focus` | `#6a40c9` | `#bfb0ff` | focus ring |
| `state.selected-background` | `#eceaff` | `#342b53` | the open page in navigation, selected option |
| `state.selected-foreground` | `#48278e` | `#d7d0ff` | its label |
| `status.info-background` | `#f1f0ff` | `#292440` | information notice and badge |
| `status.info-border` | `#c7c0f2` | `#564b83` | its border |
**The light theme turns around.** A pale accent with dark text becomes the deep anchor with white
text: 17.8:1, or 14.5:1 hovered.
**Light accent text and the focus ring stay off the anchor itself.** In body copy `#210050` is
almost indistinguishable from near-black text, so accent text and icons use a brighter `#4d2a99`
(10.0:1 on a surface). The focus ring uses a vivid `#6a40c9` (6.1:1 on the page). Next to a dark
primary button, a ring in the anchor's own colour would merge into the button; this one is clearly
distinct.
**Dark keeps ADR 0060's principle: lifted, not glowing.** The accent is a lavender (`#ac9bf6`). The
anchor is drawn on it as its foreground, at 7.4:1, and at 5.9:1 pressed. Selected and information
washes are deep purple tints, never saturated fills.
### The neutrals lose their teal tint
Every teal-tinted neutral moves to the anchor's hue at the same OKLab lightness and chroma. This
covers the backgrounds, text, borders, hover and pressed washes, the placeholder, the shadows'
colour, and the dark overlay.
- **Light:** a faint violet-grey (page `#f6f5f9`, text `#201f28`).
- **Dark:** a violet-grey charcoal (page `#141319`, text `#eaeaf1`).
ADR 0060's other rules still hold: never pure black, never pure white, and an elevation ladder.
Because lightness is kept, contrast is kept. The tightest pairings move by less than 0.1. For
example, `border.control` on the light page goes from 3.24 to 3.28, and `text.muted` on the light
page from 5.04 to 5.10. Success, warning, and danger keep their own hues.
### Contrast stays tested
`packages/design-tokens/tests/contrast.test.ts` keeps all 41 pairings it already checked, in both
schemes. It adds six pairings this accent depends on:
- the accent as a non-text indicator on the page, the canvas, and a surface;
- the selected tab's label on its wash;
- accent text in a dialog;
- the mark inside a checked box or radio.
A test also pins the anchor: `accent.base` is `#210050` with white on it in light, and in dark it is
lifted, with the anchor as its foreground.
## Alternatives
- **The same hex everywhere.** Rejected: `#210050` fails as an indicator on dark surfaces at about
1.2:1. As light text, it is too close to the body copy to read as an accent.
- **Purple accent tokens, teal-tinted neutrals.** Rejected: the teal stays visible in every surface
and border, most of all in dark, and clashes with the accent.
- **Hue-rotating every accent token at its old lightness and chroma.** Rejected for the accent
tokens: teal's low chroma gives a dull grey-purple. It is used for the neutrals, where a faint
tint is the point.
- **Waiting for Branding settings.** Rejected by the maintainer: the default should be right before
anyone configures it.
## Consequences
- Every first-party surface turns purple from tokens alone: the Dashboard, the sign-in pages, and
the shared elements. The display host stays light (ADR 0060) and takes the light values.
- Modules that use the shared tokens, such as Signage, follow the new default with no change of
their own. A module that hard-coded the old teal would not, which is why no module should.
- A deployment that overrides tokens at its host root is unaffected.
- When Branding settings arrive, the accent becomes an administrator's choice. That feature must
derive the same set of variants and pass the same contrast tests. This decision is its default.