Say Remove for displays, and make the default accent purple (#210050)
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
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
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]>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
d9dafcb0f7
commit
0009f62fbd
@@ -2,6 +2,7 @@
|
||||
|
||||
- Status: Accepted
|
||||
- Date: 2026-10-15
|
||||
- Teal accent and teal-tinted dark palette superseded by: [0066](0066-default-purple-accent.md)
|
||||
- Extends: [0039](0039-shared-design-tokens-and-elements.md),
|
||||
[0040](0040-module-stylesheets-and-artifact-isolation.md),
|
||||
[0058](0058-dashboard-shell-navigation-and-lucide-icons.md),
|
||||
|
||||
+10
-4
@@ -147,11 +147,17 @@ and a `role="listbox"` of options).
|
||||
|
||||
### The normal list shows active displays, current as they change
|
||||
|
||||
The Paired displays list shows **active** displays only. A revoked display leaves it at once — the
|
||||
page asks for the list again right after revoking — and the page says so, with focus on that notice
|
||||
because the display's own card is gone. The revoked device row and its permission rows stay in the
|
||||
The Paired displays list shows **active** displays only. A removed display leaves it at once — the
|
||||
page asks for the list again right after removing it — and the page says so, with focus on that
|
||||
notice because the display's own card is gone. The device row and its permission rows stay in the
|
||||
database, marked revoked, as security history; a history view can show them later.
|
||||
|
||||
**Administrators remove a display; the platform revokes its credential.** The interface says
|
||||
*Remove*, *Remove display?*, and *Display removed*, and never shows "revoked". The domain does not
|
||||
change for presentation: the device and its permissions are revoked, the endpoint is still
|
||||
`POST /api/v1/devices/:id/revoke`, the fields are still `revoked`/`revokedAt`, and security and audit
|
||||
history still describe a credential revocation.
|
||||
|
||||
A newly paired display appears without a reload. Approval does not create the display: the screen
|
||||
does, when it collects its credential on its next poll, a few seconds later (ADR 0036). So after
|
||||
approving, the page shows a "Waiting for the screen to connect…" placeholder and asks for the list
|
||||
@@ -166,7 +172,7 @@ Each active display shows:
|
||||
purpose: last-seen is recorded at most every 15 minutes;
|
||||
- **Showing:** a module, or *Nothing yet*;
|
||||
- last seen and paired dates;
|
||||
- **Change** and **Revoke**.
|
||||
- **Change** and **Remove**.
|
||||
|
||||
### Changing what a display shows: one target, the active display permission
|
||||
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
# 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.
|
||||
@@ -68,9 +68,10 @@ ADRs capture durable choices that affect more than one package or phase.
|
||||
- [0057: Dashboard URLs are real, host-owned routes](0057-dashboard-urls-are-host-owned-routes.md) — Accepted
|
||||
- [0058: The Dashboard shell's navigation, and Lucide as Ekklesia's icon vocabulary](0058-dashboard-shell-navigation-and-lucide-icons.md) — Accepted (device panel and phone menu placement superseded by 0064)
|
||||
- [0059: Shared interaction primitives — confirmation, toasts, errors, loading, Back, and no native dialogs](0059-shared-interaction-primitives.md) — Accepted
|
||||
- [0060: Light, Dark, and System appearance; reduced motion; and one radius scale](0060-light-dark-system-theme-and-motion.md) — Accepted
|
||||
- [0060: Light, Dark, and System appearance; reduced motion; and one radius scale](0060-light-dark-system-theme-and-motion.md) — Accepted (teal accent and palette superseded by 0066)
|
||||
- [0061: Settings is one host-owned hierarchy; module lifecycle is Discover, and a module's settings live at `/settings/<module-id>`](0061-settings-hierarchy-and-module-settings-routes.md) — Accepted (Discover's address and the landing page superseded by 0064)
|
||||
- [0062: The account area, and appearance preferences that belong to the account](0062-account-area-and-synced-appearance-preferences.md) — Accepted
|
||||
- [0063: Module navigation — manifest v9 icons and pages, real module routes, and return-to-origin](0063-module-navigation-contract-manifest-v9.md) — Accepted
|
||||
- [0064: Discover is its own page, the sidebar is navigation only, Settings opens on an overview, and phones get a sticky header](0064-stage-1-shell-review-corrections.md) — Accepted
|
||||
- [0065: Displays pair at the platform's own `/display/setup`; phone forms never zoom; the drawer locks the page](0065-display-setup-phone-forms-and-drawer-scroll-lock.md) — Accepted
|
||||
- [0066: The default accent is a deep purple anchored on `#210050`](0066-default-purple-accent.md) — Accepted
|
||||
|
||||
Reference in New Issue
Block a user