Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
608ca0d
feat(web): Tailwind v4, OKLCH semantic tokens, contrast guard, UI pri…
Aug 7, 2026
510f7e8
refactor(web): migrate the whole app onto the semantic tokens
Aug 7, 2026
8e327b5
fix(web): keep the chat row's height so its hover strip stops eating …
Aug 8, 2026
fbd48ab
test(web): cover the primitives, and stop the lint rules reading prose
Aug 8, 2026
eeb5f5a
feat(web): runtime theme + accent picker over all four directions
Aug 8, 2026
ecad993
fix(web): parse the bare accent channel triple, and survive 8-bit rou…
Aug 8, 2026
e76812e
fix(web): dock the picker instead of modalling it, so the preview is …
Aug 8, 2026
6aef4ed
refactor(web): move the picker into /config as a section, drop the dock
Aug 8, 2026
5a18221
fix(web): stop a repaired accent token pinning the colour, and show a…
Aug 8, 2026
bbb78b8
feat(web): make the accent spectrum a draggable slider that updates live
Aug 8, 2026
f3ef651
refactor(web): drop the contrast readout from the picker
Aug 8, 2026
4153ab4
feat(web): add the scifi theme
Aug 8, 2026
def1c28
feat(web): parchment — a sixth runtime theme
Aug 8, 2026
c04d83b
fix(web): retune scifi after looking at it live
Aug 8, 2026
da905bd
feat(web): add the `terminal` theme — phosphor tube and greenbar paper
Aug 8, 2026
06cdbf7
Merge branch 'design/theme-terminal' into design/theme-picker
Aug 8, 2026
221bd2c
Merge branch 'design/theme-scifi' into design/theme-picker
Aug 8, 2026
23aaffc
feat(web): three more themes, a guard that covers all eight, and a wi…
Aug 8, 2026
ba14c05
fix(web): keep a picked hue's chroma, and stop the registry rotting a…
Aug 8, 2026
bd42175
fix(web): repair the accent from the theme's chroma, not the degraded…
Aug 8, 2026
35cdab7
feat(web): parchment round two — build the menu, not the palette
Aug 8, 2026
97e520a
Merge remote-tracking branch 'origin/design/theme-parchment' into des…
Aug 8, 2026
243e4b5
feat(web): parchment — separation, brass, and the nested well
Aug 8, 2026
56cfe4e
Merge remote-tracking branch 'origin/design/theme-parchment' into des…
Aug 8, 2026
9440157
feat(web): parchment — make the parchment actually parchment
Aug 8, 2026
a2f7f4b
Merge remote-tracking branch 'origin/design/theme-parchment' into des…
Aug 8, 2026
ddc6842
fix(web): parchment — the token was never the colour on screen
Aug 8, 2026
7a5ca33
Merge remote-tracking branch 'origin/design/theme-parchment' into des…
Aug 8, 2026
fefeb15
docs(design): a token is not a pixel — verify composited themes by sa…
Aug 8, 2026
2d0a0b5
fix(web): parchment — the rail is a parchment panel, and dark mode is…
Aug 8, 2026
5be3f9f
feat(web): parchment — the texture is back, and the guard understands it
Aug 8, 2026
b1e41f9
feat(web): parchment — the theme's own colour is amber
Aug 8, 2026
f13cfac
Merge remote-tracking branch 'origin/design/theme-parchment' into des…
Aug 8, 2026
c67e47a
docs(design): two routes for a composited theme, not one rule it fails
Aug 8, 2026
49bdda4
feat(web): ship four themes, and fix the hover that lost contrast
Aug 9, 2026
d5cd928
docs(demo): regenerate the reel on the four-theme UI, and fix the dif…
Aug 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .changeset/design-system-foundation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
"@paddock/web": minor
---

Design system foundation: Tailwind v4, semantic OKLCH tokens, and an enforced contrast floor.

The UI had no design document and no token layer — colour was addressed by
palette step in 1017 places, with 722 hand-written `dark:` pairs and 200
arbitrary `text-[Npx]` values. One ramp had been tuned against a dark canvas and
reused unchanged against a light one, so light mode failed WCAG AA at its
most-used tokens.

- **Light mode now passes AA.** Help text and field labels went 3.75:1 → 6.71:1,
muted text and placeholders 2.81:1 → 4.90:1, and the primary button's white
label 4.17:1 → 5.53:1. Light and dark ramps are now derived separately in
OKLCH; the mid-steps lose the high-chroma tan cast that made light mode read
muddy.
- **Contrast is enforced, not asserted.** A new test parses the real stylesheet
and fails the build if any text-on-surface pair drops below 4.5:1 (3:1 for
control boundaries) in either mode, or if a colour falls outside the sRGB
gamut.
- **Shared UI primitives** (`Button`, `Card`, `Section`, `EmptyState`, `Field`,
`Input`, `Toggle`, `Chip`, `Callout`, `Dialog`, `Menu`). Dialogs now trap and
restore focus, and menus support arrow-key navigation — neither worked before.
- Chat messages no longer animate in (a 250 ms fade-with-translate on a
100+/day event), and `prefers-reduced-motion` is honoured throughout.
- Config no longer reflows while you type: a field's width was recomputed from
its live value on every keystroke, so crossing 38 characters jumped it to full
width and re-packed every field after it.
- Per-instance branding (`PADDOCK_BRAND_ACCENT`) is unchanged and covered by a
test.

`docs/DESIGN.md` documents the system, and Tailwind is configured in CSS —
`tailwind.config.js` and `postcss.config.js` are gone.
43 changes: 43 additions & 0 deletions .changeset/runtime-themes-and-accent-picker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
"@paddock/web": minor
---

Four runtime themes and a colour picker, in Config → Appearance.

The design system shipped one palette. This adds a theme switcher and three
further themes on top of the neutral base, plus an accent picker — all
per-browser, applied instantly, no save and no restart.

- **Four themes.** `foundation` (the neutral base), `parchment` (a 90s RPG menu
— wine chrome, brass fittings, corner brackets, an old-style serif),
`terminal` (green phosphor and ANSI in the dark; greenbar and ribbon ink in
the light) and `scifi` (deep-space ground and luminous cyan). Both light and
dark are designed for each, not inverted from one another.
- **Pick any colour for the accent.** The picker takes a colour and nothing
else: the theme supplies its own saturation and a target contrast, and the
*lightness is solved* to hit it. An unreadable accent is therefore
inexpressible rather than merely warned about — it is not possible to choose
one that fails AA in either mode. Optionally the same colour can tint the
page ground (None / A little / More). No colour theory is exposed anywhere in
the UI.
- **Per-instance branding still composes.** With no colour picked, the solver
reads the hue of whatever `PADDOCK_BRAND_ACCENT` produced, so an operator's
brand colour is re-solved against the active theme instead of being used at
whatever lightness it happened to have.
- **Every theme is contrast-guarded.** The build-time guard previously read only
the base palette, so a theme could ship uncertified. It now walks the theme
registry and applies the full contract to each one in both modes, and fails
both ways — a theme registered with no stylesheet, or a stylesheet no one
registered.

**Fixed: hovering the primary button made its label harder to read.** In dark
mode the fill lightened on hover, taking its white label from 5.53:1 to
**4.17:1** — below AA, on hover, on the most-clicked control in the app. Hover
now raises contrast in both modes. The guard had never paired a foreground with
a hover fill, which is why this was invisible; that pairing is now asserted, and
adding it immediately caught a second instance in `parchment` (6.31:1 → 5.20:1)
where the correct direction is the opposite one, because its fill carries dark
ink rather than white.

The "Appearance" entry in the sidebar is gone — it was a second door onto the
Config section that already holds these settings.
33 changes: 33 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,39 @@ derived, not read); nothing may ever log or serialise a declared server's values
puts the whole `mcp_servers` record in one `--mcp-config` argv element, so an `env`
value or an `Authorization` header is visible in `/proc/<pid>/cmdline` to the same user.

## UI conventions

**Read [`docs/DESIGN.md`](docs/DESIGN.md) before touching anything visual.** It is
the repo's only document about how Paddock *looks* (every other `DESIGN-*.md` is
architecture, and unlike the rest of `docs/` it is current, not the stale fork).
It covers the token architecture, the type/space/radius/elevation/motion scales,
the shared primitives, and — aimed squarely at a coding agent — a "Reject this"
section and a step-by-step "How to add a direction".

The five rules it exists to protect, all enforced by
`packages/web/src/styles/tokens.test.ts`, which fails the build:

- **Colour lives only in `packages/web/src/styles/tokens.css`**, as semantic
tokens (`--surface-raised`, `--text-muted`, `--danger-soft`) declared twice —
`:root` for light, `.dark` for dark, ramps derived **separately** in OKLCH.
- **Never a literal hex, `rgb()` or raw palette step in component code.** Write
`text-fg-muted`, not `text-paddock-500` or `text-[#8f7c54]`. The one exception
is `src/lib/brand.ts`.
- **Never a `dark:` variant for a colour** — the token swaps itself. Never an
arbitrary `text-[Npx]` — use a rung (`text-3xs` … `text-3xl`). Never a bare
`outline` focus ring (use `box-shadow`), never `transition-all`.
- **Reach for a primitive** from `packages/web/src/components/ui/` (`Button`,
`Card`, `Section`, `EmptyState`, `Field`, `Input`, `Toggle`, `Chip`,
`Callout`, `Dialog`, `Menu`) before hand-rolling markup. Structural changes go
in the primitive, where one edit reaches every call site.
- **`--accent` / `--accent-600` / `--accent-700` are the branding seam** (#34):
space-separated sRGB channels a running server overwrites for
`PADDOCK_BRAND_ACCENT`. Keep that format and keep every other accent token
derived from them.

Styling is **Tailwind v4** — configuration is CSS (`packages/web/src/index.css`),
there is no `tailwind.config.js` and no PostCSS config; do not reintroduce them.

## Dev conventions

Full guide: [`CONTRIBUTING.md`](CONTRIBUTING.md); run modes: [`DEV.md`](DEV.md).
Expand Down
Loading