StatTile and SoftChip drawn at the shared baseline's own token values.
A decision artifact for DS-5 / PRIM-1, not an approved spec. Each component already
exists twice — hram as a file, and a second build inline inside a page in
fitness-monitor (StatTile) and cockpit
(SoftChip). The promotion trigger is met on evidence, and the
seven divergences between the two builds
were decided on 2026-08-12 — everything drawn
here is the promoted spec.
Every colour, size and radius below is read from ui-core-micha/src/theme/tokens.js.
The chip tints are derived the way the component derives them — alpha(main, .12) fill,
alpha(main, .30) border, darken(main, .3) text and dot — so nothing here
is a hand-picked hex. Contrast ratios are computed in the page against the blended
background, because a ratio taken against the raw rgba() would be meaningless.
A small bordered card: muted label, one bold tabular number, optional caption. Two call sites in hram today — the Calibration KPI row and the Allocation rail.
At 375 px the tiles fill the width and wrap once they hit the 120 px floor — the decided
flex: 1 plus minWidth rule, which is why two fit here where hram's
original minWidth-only version dropped to one per line.
THEME-4 as written. It references tokens throughout —
variant="caption", variant="h5", text.secondary,
divider — and its only raw numbers are theme-relative multipliers
(borderRadius: 1, the spacing steps), not literals. What it lacks is a test:
neither build has one, in either app, and tests are part of promotion.
Soft tinted chips that read as annotation rather than alarm. Two variants, four tones, a 6 px leading dot. Replaced loud saturated fills — a bright-orange pill and a full-width green banner.
| Tone | main | text = darken(main,.3) | effective fill over paper | ratio | WCAG AA (4.5) |
|---|
The fill is alpha(main, .12) over background.paper, so the real
background is a blend. getContrastRatio ignores alpha and would return the ratio
against white — which is close here but is the wrong number for the wrong reason. These are
computed from the blend.
SoftChip set typography as raw numbers, which is exactly what THEME-4's shadowing check
forbids in a kit component. The four cells below are the options as they stood; the
status variant now uses overline, and caveat is deliberately left
unresolved (see the end of this page).
fontSize: 11, fontWeight: 600 — no variantoverline — chosenfontSize: 12, fontWeight: 500captionoverline for status and accept uppercase — which changes how every status
chip reads, from “passed” to
“PASSED”. Or add one variant to the baseline for chip text
and let both variants reference it — which is a baseline change, so an app-level decision that does
not belong inside a component promotion. caveat has the same shape of problem at 12/500
against caption's 12/400: visually a hair bolder, but there is no variant for it.
The second build of each is an inline component inside a page, not a file, which is why a filename search misses it. The promotion rule is met: two independent builds each, three apps consuming.
h6 16/600 · tabular-nums · padding 12/8 fixed ·
minWidth 120 · caption sub-line · children slot
h5 18/600 · no tabular-nums · padding responsive
{xs:1, sm:1.5} · flex: 1 · accent
= 2 px primary.main border (right-hand tile)
Five decisions fall out of this, and none is arbitrary: h6 or h5 for the value;
tabular-nums (hram has it, fm does not — for a KPI row it is what makes digits line
up between tiles, so this one has a right answer); whether accent
survives as a variant; fixed or responsive padding; and
flex: 1 vs minWidth, which is the 375 px wrap behaviour
above expressed as an API choice.
SoftChip.jsx, status variantalpha(main,.30) · fill alpha(main,.12) · auto height
BoardView.jsx:65, lane pilloverline in all but the weight step. So overline is not an imposition on
the shared component; it is what the second consumer independently chose, and hram's lowercase is
the outlier. That turned “changes how every status chip reads” from a cost into a
one-app cost — which is how it was decided.
Operator decisions, 2026-08-12. Everything rendered above the divergence section already reflects them; the two comparison panels keep the original values as the record of what changed.
| Divergence | Decision | Which app changes |
|---|---|---|
| StatTile value size | h5 — 18/600 | hram (16 → 18) |
| StatTile digit alignment | tabular-nums, always — the reason a KPI row reads as a row | fitness-monitor |
| StatTile row behaviour | flex: 1 with a minWidth floor — covers both
existing behaviours with one rule, no prop |
hram (tiles now share the row) |
StatTile accent |
kept — 2 px primary.main; without it fitness-monitor cannot
adopt |
neither |
| StatTile padding | responsive {xs: 1, sm: 1.5} |
hram (was fixed) |
| SoftChip status typography | overline — 11/600, uppercase, .4 px tracking. No new
baseline variant, so THEME-4 is satisfied without a theme change |
hram (passed → PASSED); cockpit drops 700 → 600 |
| SoftChip leading element + border | dot by default, any icon via prop; border optional | neither — both adopt unchanged |
caveat variant, unchanged at 12/500
caveat variant keeps its
raw 12/500, which has no baseline variant (caption is 12/400). It is left as-is
deliberately — resolving it needs either a baseline addition or a visible weight change, and neither
belongs in the same change as the promotion. It will surface as a THEME-4 finding on the
promoted component and should be exempted with that reason, not silently flattened to 400.
Not shown, because it cannot be: motion, focus and hover states, and how the chips read on a real dense results screen rather than a specimen sheet.