|
| 1 | +--- |
| 2 | +title: "Tokens landed, components didn't — the UI needs an overhaul, and the drift linter can't see the problem" |
| 3 | +lede: "19 of 20 apps consume the theme package; shared-ui ships exactly two components. Every remote hand-rolls its own buttons, pills, and empty states against shared colours." |
| 4 | +date_created: 2026-08-21 |
| 5 | +date_modified: 2026-08-21 |
| 6 | +authors: |
| 7 | + - Michael Staton |
| 8 | +augmented_with: |
| 9 | + - Claude Code on Claude Opus 5 |
| 10 | +semantic_version: 0.0.0.1 |
| 11 | +tags: |
| 12 | + - Issue |
| 13 | + - Augment-It |
| 14 | + - Design-System |
| 15 | + - Component-Library |
| 16 | + - Usability |
| 17 | + - Microfrontends |
| 18 | + - Org-Workbench |
| 19 | + - Theme-System |
| 20 | +status: Open · Diagnosed · Scoped from a live prod screenshot + drift audit |
| 21 | +site_uuid: e862574a-3e21-4ee3-b3f7-bf99ce83f28f |
| 22 | +hex_code: 03hrzx |
| 23 | +date_authored_initial_draft: 2026-08-21 |
| 24 | +date_authored_current_draft: 2026-08-21 |
| 25 | +publish: true |
| 26 | +--- |
| 27 | + |
| 28 | +# Tokens landed, components didn't |
| 29 | + |
| 30 | +## Why Care? |
| 31 | + |
| 32 | +[[No-Component-Library-UI-Improvised-Not-Component-Based]] was jotted on |
| 33 | +2026-07-24 and still reads `Open · Jotted`. This issue is not a restatement of |
| 34 | +it — it is the **measurement** that closes the diagnosis, taken a month later |
| 35 | +against the live prod surface, plus the finding that our automated design |
| 36 | +guardrail is structurally blind to the actual defect. |
| 37 | + |
| 38 | +The short version: **the token half of the design system shipped and the |
| 39 | +component half did not.** We now have the worst configuration of the two — |
| 40 | +enough shared infrastructure to believe the problem is handled, not enough to |
| 41 | +make any two surfaces look related. |
| 42 | + |
| 43 | +## The measurement |
| 44 | + |
| 45 | +| Signal | Value | |
| 46 | +|---|---| |
| 47 | +| Apps depending on `@augment-it/theme` | **19 of 20** | |
| 48 | +| Components exported by `packages/shared-ui/src` | **2** — `ConfidencePill.svelte`, `ToggleHeader__PromptOrPackage--Icons.svelte` | |
| 49 | +| Apps importing anything from `shared-ui` | **3** (`pack-runner`, `prompt-template-manager`, `response-reviewer`) + `shell` | |
| 50 | +| `node scripts/design-drift.mjs` | **99 fail · 0 warn**, across **16 apps** | |
| 51 | +| Contrast pairs | **30/30 pass** | |
| 52 | + |
| 53 | +Drift failures by rule: |
| 54 | + |
| 55 | +| Count | Rule | |
| 56 | +|---|---| |
| 57 | +| 53 | `F8` hardcoded hex colour outside `packages/theme` | |
| 58 | +| 17 | `F6` no `DESIGN.md` at member root | |
| 59 | +| 22 | `F4` raw `z-index` (values 1, 2, 5, 10, 15, 20, 50, 90, 100, 200) | |
| 60 | +| 6 | `F8` hardcoded `box-shadow` outside `packages/theme` | |
| 61 | +| 1 | `P2` tier-2 token `--font-mono` missing in light vibrant | |
| 62 | + |
| 63 | +The `z-index` spread is its own small horror: ten distinct raw values competing |
| 64 | +across remotes with no shared stacking contract. That is a layering bug waiting |
| 65 | +for the first overlay that needs to sit above a `200`. |
| 66 | + |
| 67 | +## The finding that matters most: the linter is blind to this |
| 68 | + |
| 69 | +**`org-workbench` produces zero drift failures.** It is token-clean — no |
| 70 | +hardcoded hex, no raw z-index, nothing. And it is the exact surface in the |
| 71 | +screenshot that reads as improvised. |
| 72 | + |
| 73 | +That is the whole problem in one data point. `design-drift.mjs` checks |
| 74 | +*values* — is this colour a token, is this z-index a token, is there a |
| 75 | +`DESIGN.md`. It cannot check *form*: whether a button is the same shape, |
| 76 | +height, radius, and weight as the button on the surface next to it. A remote |
| 77 | +can pass every rule we have and still invent its own visual dialect, because |
| 78 | +nothing in the toolchain has an opinion about components. |
| 79 | + |
| 80 | +So the 99 failures are real and worth fixing, but closing all 99 would **not** |
| 81 | +fix the screenshot. We would have 16 apps hand-rolling divergent components out |
| 82 | +of perfectly compliant tokens. |
| 83 | + |
| 84 | +## What the prod screenshot actually shows |
| 85 | + |
| 86 | +From Org Workbench on `augment.didi.sh`, reach-edu workspace: |
| 87 | + |
| 88 | +- **Three button dialects in one 900px row** — `+ New organization` (flat, dark, |
| 89 | + square-ish), `📋 Relevance brief` (lighter fill, different radius, emoji |
| 90 | + glyph), `◀ orgs` (third fill, third radius, arrow glyph). |
| 91 | +- **A `closed` status pill** floating unanchored in the top-right, overlapping |
| 92 | + the header's baseline rather than sitting in a defined status slot. |
| 93 | +- **A dead 900px void** below the intro copy — no empty state, no skeleton, no |
| 94 | + error surface. (The *reason* it is empty is |
| 95 | + [[Every-Remote-Hardcodes-The-Workspace-WS-To-Localhost-So-Prod-Loads-No-Data]]; |
| 96 | + the fact that emptiness renders as an unstyled void is this issue.) |
| 97 | +- **A crowded, mixed-metaphor header** — monospace `augment-it · shell`, |
| 98 | + underlined `FLOW`, a numbered pill, chat/queue/Developers/account/Dark/Reach |
| 99 | + Edu controls in at least four different shapes and three different border |
| 100 | + treatments. |
| 101 | +- **Chat rail content vertically centred** in a tall column, so the prompt |
| 102 | + hint floats mid-void with no visual anchor. |
| 103 | + |
| 104 | +Notably the *colours* are fine — dark ground, purple accent, readable text, |
| 105 | +30/30 contrast pairs passing. It is the **shapes, spacing, and states** that |
| 106 | +have no shared grammar. Which is precisely what tokens-without-components |
| 107 | +predicts. |
| 108 | + |
| 109 | +## Why it went this way |
| 110 | + |
| 111 | +The honest account is in [[No-Component-Library-UI-Improvised-Not-Component-Based]] |
| 112 | +and holds up: remotes were built fast, independently, each solving its own UI |
| 113 | +in isolation, and Module Federation made that independence frictionless. The |
| 114 | +theme package was the cheap win — a CSS import and a dependency line — so it |
| 115 | +propagated to 19 apps. A component library is the expensive win, because it |
| 116 | +requires agreeing on an API and then *migrating* sixteen call sites. It stalled |
| 117 | +at two components. |
| 118 | + |
| 119 | +This is also the tail of the same pressure recorded in |
| 120 | +[[Refactoring-for-API-Speed]] and [[No-Test-Coverage-TDD-Deferred-Despite-Agentic-Fit]]: |
| 121 | +infrastructure that is one import away lands; infrastructure that requires |
| 122 | +coordinated migration does not. |
| 123 | + |
| 124 | +## What an overhaul should actually do |
| 125 | + |
| 126 | +Sequenced so each step is shippable on its own: |
| 127 | + |
| 128 | +1. **Name the primitives.** From an audit of what the 16 remotes already |
| 129 | + hand-roll, the recurring set is roughly: `Button` (primary/secondary/ghost), |
| 130 | + `Input`, `Pill` / `Badge` (incl. connection status), `Card`, `EmptyState`, |
| 131 | + `ErrorState`, `Skeleton`, `Toolbar`. Ratify that list before writing any of |
| 132 | + it. |
| 133 | +2. **Fix the stacking contract first** — it is the cheapest high-leverage fix. |
| 134 | + Define `--z-*` tokens covering the ten values in use and convert all 22 |
| 135 | + raw `z-index` sites. |
| 136 | +3. **Build the primitives in `packages/shared-ui`**, matching the two existing |
| 137 | + components' conventions so `ConfidencePill` does not become an orphan |
| 138 | + dialect. |
| 139 | +4. **Migrate surface by surface, most-visible first** — Org Workbench, then |
| 140 | + `search-and-add` / `search-results` (the rest of the Augment-from-DB flow), |
| 141 | + then corpora-curator and chat. Each migration is one PR and one changelog |
| 142 | + entry. |
| 143 | +5. **Standardise the connection-status slot** as part of step 4 — every remote |
| 144 | + has the same `'connecting' | 'open' | 'closed' | 'error' | 'auth_required'` |
| 145 | + state and each renders it differently, or (per the sibling issue) not |
| 146 | + meaningfully at all. |
| 147 | +6. **Retire the 53 hardcoded hex values** as a by-product of migration rather |
| 148 | + than as a separate sweep — most of them live in components that are about to |
| 149 | + be replaced. |
| 150 | +7. **Teach the linter about form.** Add a rule that flags a remote defining its |
| 151 | + own `button` / `input` / pill styling when a `shared-ui` primitive exists. |
| 152 | + Without this, step 4 decays exactly the way the theme rollout did. |
| 153 | +8. **`DESIGN.md` per member** — 17 apps lack one. Cheap, and the |
| 154 | + `maintain-design-md` skill already specifies the shape. Do it last; it |
| 155 | + documents the outcome rather than driving it. |
| 156 | + |
| 157 | +## Open questions for the operator |
| 158 | + |
| 159 | +- **Is this an overhaul or a rebuild?** The steps above are incremental and |
| 160 | + preserve every surface. A genuine visual redesign — new layout language, new |
| 161 | + header, new information density — is a different and larger piece of work. |
| 162 | + The screenshot's header crowding hints you may want the latter. |
| 163 | +- **Does the shell header get redesigned separately?** It is the one surface |
| 164 | + every flow inherits, and [[Header-Polish-Flow-Label-Chat-Toggle-Placement-Shell-Suffix]] |
| 165 | + already has scope on it. |
| 166 | +- **Should this supersede [[No-Component-Library-UI-Improvised-Not-Component-Based]]**, |
| 167 | + or sit under it as the measured follow-up? Recommend the latter — that issue |
| 168 | + holds the origin story, this one holds the numbers and the plan. |
| 169 | + |
| 170 | +## Related |
| 171 | + |
| 172 | +- [[No-Component-Library-UI-Improvised-Not-Component-Based]] — the 2026-07-24 admission this measures |
| 173 | +- [[Every-Remote-Hardcodes-The-Workspace-WS-To-Localhost-So-Prod-Loads-No-Data]] — why the screenshot's main pane is empty |
| 174 | +- [[Org-Workbench-Narrow-Layout-Roster-Doesnt-Collapse-Card-Contents-Spill]] — a layout symptom of the same absence |
| 175 | +- [[Header-Polish-Flow-Label-Chat-Toggle-Placement-Shell-Suffix]] |
| 176 | +- [[Live-Not-Live-Indicator-Tooling-And-Cross-Service-Error-Surfacing]] — the status-slot half |
0 commit comments