The console was pinned to @unom/ui@^0.8.16 while the design system had moved to 0.9.2. This brings it current.
What it picks up
0.9.x adds the Badge, Spinner, Skeleton, Switch, Table, EmptyState and CodeBlock primitives, and 0.9.2 carries two form fixes that were live in this console:
Select used border-main and focus-visible:ring-main/50. --main is the foreground here (--main: var(--foreground) in web/src/styles.css), so the trigger wore a near-white border and a 3px near-white focus ring. Its chevron and placeholder were painted --secondary, which is a surface colour on this palette, so they all but vanished — which is what stopped it reading as a select. Now on --input/--ring, the same tokens InputText already used.
InputNumber declares a color-scheme, so the browser-drawn spinner arrows stop being near-black on a near-black field.
Both were found while overhauling the rom-manager plugin UI, but the console shares the palette that exposes them.
Verification
codegen + vite build clean, tsc --noEmit clean, bun test server/ 9/9
Storybook builds, 31/31 screenshots captured
A probe across all 61 stories reports zero page errors
The two stories containing a Select now render it at h-input-height with border: rgb(42, 33, 72) (the input token) and a muted-foreground chevron
Unchanged on purpose
The web/src/components/ui/ wrapper layer stays as-is. In particular dialog.tsx is still required: @unom/ui's DialogContent remains a surface with no Portal and no placement, and that wrapper is what supplies them. (The rom-manager plugin hit exactly this and had to add the same wrapper.)
Not included
@unom/app-ui — the console does not currently depend on it at all. See the discussion on scope; adopting it is a refactor rather than a version bump.
The console was pinned to `@unom/ui@^0.8.16` while the design system had moved to 0.9.2. This brings it current.
### What it picks up
0.9.x adds the Badge, Spinner, Skeleton, Switch, Table, EmptyState and CodeBlock primitives, and **0.9.2 carries two form fixes that were live in this console**:
- **`Select`** used `border-main` and `focus-visible:ring-main/50`. `--main` is the **foreground** here (`--main: var(--foreground)` in `web/src/styles.css`), so the trigger wore a near-white border and a 3px near-white focus ring. Its chevron and placeholder were painted `--secondary`, which is a **surface** colour on this palette, so they all but vanished — which is what stopped it reading as a select. Now on `--input`/`--ring`, the same tokens `InputText` already used.
- **`InputNumber`** declares a `color-scheme`, so the browser-drawn spinner arrows stop being near-black on a near-black field.
Both were found while overhauling the rom-manager plugin UI, but the console shares the palette that exposes them.
### Verification
- `codegen` + `vite build` clean, `tsc --noEmit` clean, `bun test server/` 9/9
- Storybook builds, 31/31 screenshots captured
- A probe across **all 61 stories** reports **zero page errors**
- The two stories containing a `Select` now render it at `h-input-height` with `border: rgb(42, 33, 72)` (the input token) and a muted-foreground chevron
### Unchanged on purpose
The `web/src/components/ui/` wrapper layer stays as-is. In particular `dialog.tsx` is still required: `@unom/ui`'s `DialogContent` remains a **surface** with no Portal and no placement, and that wrapper is what supplies them. (The rom-manager plugin hit exactly this and had to add the same wrapper.)
### Not included
`@unom/app-ui` — the console does not currently depend on it at all. See the discussion on scope; adopting it is a refactor rather than a version bump.
Brings the console onto the current design system. 0.9.x adds the Badge, Spinner,
Skeleton, Switch, Table, EmptyState and CodeBlock primitives, and 0.9.2 carries the
form fixes found while overhauling the rom-manager plugin UI:
- Select's border and focus ring resolved to `--main`, which is the FOREGROUND here
(`--main: var(--foreground)` in web/src/styles.css), so the trigger wore a near-white
border and a 3px near-white focus ring. Its chevron and placeholder were painted
`--secondary`, a SURFACE colour, and all but vanished. Now on `--input`/`--ring`, the
same tokens InputText already used.
- InputNumber declares a color-scheme, so the browser-drawn spinner arrows stop being
near-black on a near-black field.
Both defects were live in this console too — the console palette is what exposes them.
Verified: codegen + vite build clean, `tsc --noEmit` clean, `bun test server/` 9/9,
Storybook builds, 31/31 screenshots. A probe over all 61 stories reports ZERO page
errors, and the two stories containing a Select now render it at h-input-height with
`border: rgb(42, 33, 72)` (the input token) and a muted-foreground chevron.
Note: the console's components/ui/ wrapper layer is unchanged and still required —
@unom/ui's DialogContent remains a surface with no Portal or placement, which is
exactly what web/src/components/ui/dialog.tsx supplies.
Same pill strip the plugin UIs use, via @unom/ui's Tabs: Configuration | Live displays.
The page was two stacked cards, and the configuration card ALONE is taller than the
viewport — the existing comment on the unsaved badge says as much, because that height
is how pending edits went unnoticed. The live-display list sat below all of it, so in
practice it was off screen.
Two details that are not cosmetic:
- The dirty marker moved from the card header onto the Configuration TRIGGER. Behind a
tab the old badge would vanish entirely while Live was open — a strictly worse version
of the problem it was added to solve. On the trigger it survives both tabs, and the
Custom block keeps its own inline badge for when the tab IS open.
- The strip is extracted as a presentational `DisplayTabs` rather than inlined in
`DisplaySection`. The container calls `useBlocker`, which needs a router, so it cannot
render in Storybook — and this page's story exists specifically to pin the MOTION
NESTING of the preset grid (a card sets no delayChildren, so tiles nested one level
deeper stop staggering). Inserting tabs changes that ancestor chain, so the story has
to render the real one or it passes for the wrong reason.
Adds Pages/Displays → "Unsaved on other tab", which switches to Live with a dirty draft:
if the marker ever goes silent there, the warning is gone exactly when it matters.
Verified: tsc clean, biome clean, `bun test server/` 9/9, vite build + i18n check clean,
Storybook builds, 32/32 screenshots.
The broken inset on the Displays configuration card was the symptom. The cause is
structural, and it had already been diagnosed at least twice in-tree without being fixed.
Two faults, both in components/ui/card.tsx:
1. The padding was a RESPONSIVE COMPOUND: `p-4 pt-0 sm:p-6 sm:pt-0`. tailwind-merge
resolves conflicts only within a variant, so any call-site override won at the base
and lost at `sm:` — correct on a phone, wrong on every desktop. Measured on the
Displays card before this change: padding-top 24px at 500px, 0px at 1440px.
2. `pt-0` encoded an assumption about a SIBLING that nothing enforced — "a CardHeader is
above me and supplies the top inset". Delete the header, which is exactly what tabbing
a page does since the tab label replaces the card title, and the top inset silently
vanishes at ≥640px.
Fix:
- One single-variant utility, `p-padding-card` — the same `--spacing-padding-card` token
@unom/ui's own Card uses, so nested cards finally agree on their inset. A single
variant cannot half-lose an override.
- Top inset is now self-correcting: `[&:not(:first-child)]:pt-0`. Ask the DOM instead of
the author. A headerless CardContent keeps its inset with nothing to remember.
Seven call sites had grown their own compensation in five dialects — `p-6`,
`p-card pt-card sm:pt-card` (×3), `p-4 sm:pt-6` (×3), `pt-4 sm:pt-6`, and my own `pt-6`
from the tabs commit. All removed; they are the symptom-fixes this replaces. LogsCard
even carried a six-line comment correctly describing the trap and working around it
locally — that comment is now three lines saying it no longer needs saying.
`flush` stays: full-bleed content is a real intent, expressed as a prop the component
honours rather than a utility that has to out-argue the one already there.
Guarded by UI/Card → "Inset with and without header", a headered/headerless pair that has
to look identical on every side. It must be checked at BOTH widths — a single width
cannot show this class of bug, which is why it kept surviving.
Verified by measuring computed padding at 500px and 1440px: first child 20px on all four
sides, after-a-header 0px top and 20px elsewhere, identical at both widths. tsc clean,
biome clean on every touched file, 9/9 server tests, build + i18n clean, 32/32 screenshots.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The console was pinned to
@unom/ui@^0.8.16while the design system had moved to 0.9.2. This brings it current.What it picks up
0.9.x adds the Badge, Spinner, Skeleton, Switch, Table, EmptyState and CodeBlock primitives, and 0.9.2 carries two form fixes that were live in this console:
Selectusedborder-mainandfocus-visible:ring-main/50.--mainis the foreground here (--main: var(--foreground)inweb/src/styles.css), so the trigger wore a near-white border and a 3px near-white focus ring. Its chevron and placeholder were painted--secondary, which is a surface colour on this palette, so they all but vanished — which is what stopped it reading as a select. Now on--input/--ring, the same tokensInputTextalready used.InputNumberdeclares acolor-scheme, so the browser-drawn spinner arrows stop being near-black on a near-black field.Both were found while overhauling the rom-manager plugin UI, but the console shares the palette that exposes them.
Verification
codegen+vite buildclean,tsc --noEmitclean,bun test server/9/9Selectnow render it ath-input-heightwithborder: rgb(42, 33, 72)(the input token) and a muted-foreground chevronUnchanged on purpose
The
web/src/components/ui/wrapper layer stays as-is. In particulardialog.tsxis still required:@unom/ui'sDialogContentremains a surface with no Portal and no placement, and that wrapper is what supplies them. (The rom-manager plugin hit exactly this and had to add the same wrapper.)Not included
@unom/app-ui— the console does not currently depend on it at all. See the discussion on scope; adopting it is a refactor rather than a version bump.