@micha.bigler/ui-core-micha 2.38.0 → 2.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,177 @@
1
+ # PRIM-1 — Promote `StatTile` and `SoftChip` into the shared kit
2
+
3
+ **Target repo:** `ui-core-micha` (branch `main`)
4
+ **Tier:** 3 — shared-core.
5
+
6
+ New register prefix `PRIM-*`: shared dashboard/data primitives — small presentational components that
7
+ are neither chart (`CHART-*`) nor app chrome (`SHELL-*`).
8
+
9
+ ---
10
+
11
+ ## A. Envelope
12
+
13
+ ### Goal
14
+
15
+ Both components already exist **twice**, built independently, and the second build in each case is an
16
+ **inline component inside a page** rather than a file — which is why a filename search misses it:
17
+
18
+ | Component | First build | Second build |
19
+ |---|---|---|
20
+ | `StatTile` | `hram/frontend/src/components/ResearchComponents/StatTile.jsx` (36 lines) | `fitness-monitor/frontend/src/pages/BodyHistoryPage.jsx:554-568` |
21
+ | `SoftChip` | `hram/frontend/src/components/ResearchComponents/SoftChip.jsx` (67 lines) | `cockpit/frontend/src/cockpit/BoardView.jsx:65-73` (the lane pill) |
22
+
23
+ Promote both into the kit as the single implementation. The promotion trigger is met on evidence — two
24
+ independent builds each, and a third app in view for each.
25
+
26
+ ### The visual spec, and the seven decisions in it
27
+
28
+ **Prototype:** [`work-orders/assets/PRIM-1-dashboard-primitives.html`](work-orders/assets/PRIM-1-dashboard-primitives.html).
29
+ It renders the promoted spec at 375 px and 1280 px, keeps both original builds side by side as the
30
+ record of what changed, and computes its contrast ratios in-page against the *blended* fill.
31
+
32
+ The two builds diverged in seven places. All seven were decided by the operator on 2026-08-12, and the
33
+ prototype already reflects them:
34
+
35
+ | | Decision | Which app changes |
36
+ |---|---|---|
37
+ | StatTile value size | **`h5` — 18/600** | hram, 16 → 18 |
38
+ | StatTile digits | **`tabular-nums`, always** — it is what makes a KPI row read as a row | fitness-monitor |
39
+ | StatTile row behaviour | **`flex: 1` with a `minWidth` floor** — one rule covering both existing behaviours, no prop | hram, tiles now share the row |
40
+ | StatTile `accent` | **kept** — 2 px `primary.main`; without it fitness-monitor cannot adopt | neither |
41
+ | StatTile padding | **responsive `{xs: 1, sm: 1.5}`** | hram, was fixed |
42
+ | SoftChip `status` typography | **`overline` — 11/600, uppercase, .4 px tracking** | hram (`passed` → `PASSED`); cockpit drops 700 → 600 |
43
+ | SoftChip leading element + border | **dot by default, any icon via prop; border optional** | neither — both adopt unchanged |
44
+
45
+ **Why `overline` rather than a new variant:** cockpit's build independently arrived at 11-with-uppercase-
46
+ and-.4 px-tracking, which is the baseline's `overline` in all but the weight step. So the shared choice
47
+ is what the second consumer already made, and it satisfies `THEME-4` **without a baseline change**.
48
+
49
+ ### Token delta list
50
+
51
+ **One entry** — the rest is drawn from the baseline's own tokens, so there is nothing else to reconcile.
52
+
53
+ - **`caveat` variant text: 12/500.** The baseline's nearest variant is `caption` at 12/**400**; there is
54
+ no 12/500. **Resolved by the operator as: keep the prototype value, do not add a baseline variant in
55
+ this WO.** It will surface as a `THEME-4` finding on the promoted component and must be **exempted
56
+ with that reason stated** — not silently flattened to 400, and not fixed by extending the baseline
57
+ here. Adding a variant is an app-level decision that does not belong inside a component promotion.
58
+
59
+ ### Scope
60
+
61
+ 1. **`StatTile`** as a kit component, per the decisions above. Props: `label`, `value`, `caption`,
62
+ `accent`, `children`, and a `minWidth` floor with a sensible default.
63
+ 2. **`SoftChip`** as a kit component. Props: `label`, `tone` (`success`/`warning`/`error`/`info`),
64
+ `variant` (`caveat`/`status`), `icon` (defaulting to the 6 px dot), a border toggle, and `title` for
65
+ the tooltip.
66
+ 3. **Colour derivation stays as authored** — fill `alpha(main, .12)`, border `alpha(main, .30)`, text and
67
+ dot `darken(main, .3)`, read from `theme.palette[tone]`. **Do not restate any hex.** This is the
68
+ property that makes the component exemplary and the reason its four tones all pass AA (measured in the
69
+ prototype: 7.11 / 7.33 / 7.67 / 9.20).
70
+ 4. **Keep `enterTouchDelay={0}`** on the tooltip. It is why SoftChip's `title` is reachable on a touch
71
+ pointer — the exact failure `AUTH-4` is blocked on elsewhere. Do not drop it as boilerplate.
72
+ 5. Exports from the package root, and the README line.
73
+
74
+ ### Non-goals / do not touch
75
+
76
+ - **The three app adoptions.** hram, fitness-monitor and cockpit each get their own small follow-up WO.
77
+ This WO ships the kit component and nothing in any app.
78
+ - **No baseline/theme change.** No new typography variant, no palette edit. See the token delta list.
79
+ - **The other primitives in hram's `ResearchComponents/`** — `CardTitle`, `InfoPopover`,
80
+ `ProvisionalChip`, `FieldInfo`. `ProvisionalChip` wraps `SoftChip` and is the obvious next candidate,
81
+ but it has one build and is out of scope here.
82
+ - **`SoftChip`'s `caveat` weight.** Left at 12/500 deliberately.
83
+ - Any chart wrapper, shell component, or existing kit surface.
84
+
85
+ ### Risks
86
+
87
+ - **Two apps' visible appearance changes when they adopt** — hram's status chips become uppercase and its
88
+ KPI values grow 16 → 18; cockpit's pills lose a weight step. All three are decided, not accidental, and
89
+ all three land in the *adoption* WOs rather than here. Worth stating in each so nobody reports them as
90
+ regressions.
91
+ - **Neither `StatTile` build has a test.** The promoted component's tests are written here from scratch,
92
+ not ported. `SoftChip` has `SoftChip.test.jsx` in hram — read it, but do not assume it covers the
93
+ promoted API.
94
+ - **`THEME-4` will flag the `caveat` 12/500.** Expected. Exempt with the reason; an unexplained exemption
95
+ is itself a finding by that check's own design.
96
+ - **Version:** this is a new capability area (two new components), so a **minor** — `2.38.0` → `2.39.0`.
97
+
98
+ ### Required tests to WRITE
99
+
100
+ - **`StatTile`**: renders label/value/caption; omits the value row when `value` is undefined; renders
101
+ `children`; `accent` applies the 2 px border and the default does not; the value carries
102
+ `tabular-nums`.
103
+ - **`SoftChip`**: each of the four tones derives fill/border/text from `theme.palette[tone]` rather than a
104
+ literal (assert against the theme object, not against a hex string); `status` vs `caveat` differ in
105
+ radius and typography; the default leading element is the dot and a passed `icon` replaces it; the
106
+ border toggle works; `title` renders a tooltip and `enterTouchDelay` is 0.
107
+ - **Do not write a contrast assertion using `getContrastRatio`** — it ignores alpha, so on a 12 % tint it
108
+ answers a different question and would pass while meaning nothing. The prototype computes the real
109
+ ratios; if a contrast test is wanted it must blend the fill first.
110
+
111
+ Scope the run to these two new specs plus anything the new exports touch. Not the full suite — no
112
+ dependency change, no theme change.
113
+
114
+ ### Parity guardrail
115
+
116
+ Visual and API only. The promoted components must be capable of rendering **both** existing builds'
117
+ appearances where a decision preserved them, and exactly the decided appearance where it did not. No
118
+ behaviour, permission or data-contract surface exists here.
119
+
120
+ ---
121
+
122
+ ## B. Implementation map
123
+
124
+ *Filled by the Orchestrator on `git pull` — see `AGENTS.md` → "Work Order".*
125
+
126
+ ---
127
+
128
+ ## C. Orchestrator only
129
+
130
+ > **STOP — if you are the implementer reading this work order as your own specification, this section is
131
+ > NOT addressed to you. Skip it entirely.** It tells the Orchestrator how to invoke you, how the result
132
+ > is reviewed, and how it is committed. **You ARE that invocation — do NOT shell out to `codex exec`, do
133
+ > NOT spawn reviewers, do NOT run tests beyond your own new ones, do NOT edit `WORK_ORDERS.md`, and do
134
+ > NOT `git add`/`commit`/`push`.** Leave the diff in the working tree.
135
+
136
+ ### Execution directive
137
+
138
+ Check `.claude/codex-status.md` first. **No line for the current date means use Codex.** A line recording
139
+ `unavailable` for the current date means skip the attempt, implement directly in Claude, and name the
140
+ record and its date — that flips authorship. If the newest confirmation is older than today, make exactly
141
+ one probe and write the outcome back either way.
142
+
143
+ ### Review routing
144
+
145
+ Tier 3: **independent `reviewer`** and **`ui_reviewer`**, concurrent, in one background batch, before the
146
+ commit. **No `sec_reviewer`** — no auth, permission, money or schema surface.
147
+
148
+ Ask `ui_reviewer` specifically whether the `overline` decision reads correctly at both variants, and
149
+ whether the `caveat` exemption is stated rather than assumed.
150
+
151
+ ### Verification
152
+
153
+ **A prototype is in scope, so the rendered side-by-side at 375 px and 1280 px is a commit gate** — see
154
+ `AGENTS.md` → Reviews. Note the known constraint: the Browser pane composites only while displayed and
155
+ screenshots have not been legibly obtainable in this repo. If capture fails, **declare the substitution
156
+ in the register Notiz** and say what was inspected instead (computed styles, DOM) — an undeclared
157
+ substitution is a skipped gate, and that fallback cannot see whether a screen reads heavy or dense.
158
+
159
+ The prototype's own contrast numbers are computed at render time; re-read them rather than restating
160
+ these.
161
+
162
+ ### Register + commit
163
+
164
+ One row, `PRIM-1`, with the `PRIM-*` prefix documented in the register header. `done` only with both
165
+ reviewers and their verdicts named, plus the rendered-gate evidence or its declared substitution.
166
+
167
+ Publishing is a separate step from committing: **a landed commit is not a publish.** If the three
168
+ adoption WOs are to follow, `2.39.0` must actually be on the registry first — verify against npm, not
169
+ against a commit.
170
+
171
+ ### Mini-handover
172
+
173
+ Repo: `ui-core-micha` (`C:\Users\biglmi\Documents\webapps\ui-core-micha`), branch `main`.
174
+ WO: `work-orders/PRIM-1.md`. Promotes two components whose second builds are inline in
175
+ `fitness-monitor/BodyHistoryPage.jsx:554` and `cockpit/BoardView.jsx:65`. Seven divergences already
176
+ decided — the table in Part A is the spec, the prototype renders it. Tier 3, both reviewers, rendered
177
+ gate applies, minor version `2.39.0`. App adoptions are separate WOs. Follow `orchestrate-codex`.