@jpgilldev / steid

steid/plans/decisions/0005-tailwind-and-copied-components.md
3.6 KBRaw
1# 0005 — Tailwind via Topcoat, with components copied rather than depended on
2
3**Status:** accepted · **Date:** 2026-08-11
4
5## Context
6
7Everything was unstyled HTML. Milestone 2 phase 2 adds the first form that needs
8success and failure feedback, so a visual language was needed before more pages
9inherited the absence of one — three pages is the cheapest this decision ever gets.
10
11Attempt #1 had a complete OKLCH design system for Tailwind and DaisyUI. Attempt #2
12dropped Tailwind entirely, for one reason: the npm build step. Topcoat's Tailwind
13integration wraps the standalone Tailwind CLI from a Cargo build script — no Node, no
14PostCSS — which removes that objection.
15
16Topcoat also ships `topcoat ui`, a component registry that **copies source into the
17project** rather than being a library dependency.
18
19## Decision
20
21Tailwind through Topcoat's build-script integration, with `styles.css` as the theme.
22
23Components come from two places:
24
25- **Borrowed primitives**, added with `topcoat ui add` — button, input, label,
26 textarea, card. Boilerplate worth not writing.
27- **Steid's own**, written by hand — starting with `flash`, which the registry has no
28 equivalent for. Anything Steid-specific (profile header, repo row, commit bar) is
29 ours.
30
31The distinction is thin, because copied source is ours the moment it lands. A borrowed
32component that stops fitting gets edited or deleted, not worked around.
33
34**The theme is ours from the start.** Topcoat's `neutral` theme was the scaffold;
35its tokens were retuned to attempt #1's palette — dark-first at hue 260, blue primary
36— and extended with `--surface`, `--success`, `--warning` and a monospace family.
37Success and warning were absent from `neutral` but are needed for flash messages now
38and git diff semantics later.
39
40**The rule that makes this work: components reference tokens, never raw colours.** A
41hardcoded colour does not follow a palette change and does not adapt to the colour
42scheme. This is the one convention worth enforcing in review.
43
44## Alternatives considered
45
46- **Hand-rolled CSS, no Tailwind.** No build script, no download, full control.
47 Rejected: it also means hand-rolling the token structure, the colour-scheme
48 switching, and the focus-ring conventions that the theme already solves.
49- **Tailwind only, without the `ui` feature.** Skips `components.toml` and the
50 registry. Rejected narrowly — the borrowed primitives are genuinely boring code, and
51 their house style is a useful model for ours.
52- **Depending on a component library.** Rejected: Topcoat is weeks old and expects
53 breaking changes. Copied source cannot break underneath us on a version bump, which
54 matters more here than in a mature ecosystem.
55- **DaisyUI, as attempt #1 planned.** Rejected: a second styling dependency on top of
56 Tailwind, and not bundled by Topcoat.
57
58## Consequences
59
60- **Slower builds.** A build script runs Tailwind on every change. The CLI is
61 downloaded once and cached under the target directory.
62- **`topcoat asset bundle` is required for manual builds** before assets resolve;
63 `topcoat dev` does it automatically. A stale bundle serves stale CSS.
64- **Utility classes live in markup.** That is the Tailwind bargain.
65- **`src/components.rs` is rewritten by `topcoat ui add`**, so hand-written entries can
66 be reordered. Keep them alphabetical and expect churn there.
67- **A palette change is a one-file edit**, provided the token rule holds.
68- **The theme is versioned in `components.toml`** by hash. A registry theme update will
69 not silently overwrite local retuning.
70- `plans/ui.md` now describes this rather than attempt #1's DaisyUI plan.