Three defects of the same shape: a token that looks right on this library's palette and wrong on a consumer's.
Select trigger
border-main and focus-visible:ring-main/50 resolve to --main, which is the foreground. On the Punktfunk console theme that is a near-white border and a 3px near-white focus ring — reported from the field as "a really bright outline".
data-placeholder:text-secondary and the chevron's text-secondary: --secondary is a surface colour there, so the chevron and placeholder were painted in a background tone at 50% opacity and all but vanished. That is what made the control stop reading as a select at all.
Retuned to InputText's vocabulary — border-input, ring-ring at ring-1, rounded-md, px-3 — because a select sits beside text inputs in every form we ship and the two must not look like different widgets. The chevron now carries its own colour and no opacity knock-down.
InputNumber
The spinner arrows are drawn by the browser. With no declared color-scheme the UA paints them for a light UI: near-black arrows on a near-black field, i.e. invisible. Declared via arbitrary properties rather than Tailwind's scheme-* so it holds whatever Tailwind version a consumer builds with.
🛑 Why none of this was caught here
src/styles/theme.css has no dark palette — 134 lines, zero dark tokens — so every story in this Storybook renders light, while components already ship dark: variants for a palette this workbench cannot show. All three failures need a dark consumer palette to appear.
That is the actual root cause and it is not fixed by this PR. Until the design system can render a dark palette, this class of bug can only be found in a consumer. Worth its own issue.
Also
Adds the stories these three primitives never had (Select, InputNumber, InputText). form/Select → In a form row and form/InputText → With a button make a height or ring mismatch obvious on sight.
Note: Button already has a size="input" variant matching h-input-height, and SelectTrigger is h-input-height at its default size — control-height mismatches in consumers are almost always the consumer passing the h-9 default or size="sm", not missing library API.
Verification
Typecheck clean; biome clean on every touched file (checkbox.stories.tsx / switch.stories.tsx already fail noLabelWithoutControl on main — pre-existing, untouched). Verified against the Punktfunk console palette by building this branch and consuming it in punktfunk-plugin-rom-manager: the Select trigger border goes from oklch(0.985 0 0) (near-white) to rgb(42, 33, 72) (the input token), chevron to muted-foreground, height 48px.
⚠️@unom/ui@0.9.2 is already published to the registry (latest) so the consumer fix could be verified end-to-end. Merging this makes main agree with what is published.
Three defects of the same shape: a token that looks right on **this** library's palette and wrong on a consumer's.
### Select trigger
- `border-main` and `focus-visible:ring-main/50` resolve to `--main`, which is the **foreground**. On the Punktfunk console theme that is a near-white border and a 3px near-white focus ring — reported from the field as *"a really bright outline"*.
- `data-placeholder:text-secondary` and the chevron's `text-secondary`: `--secondary` is a **surface** colour there, so the chevron and placeholder were painted in a background tone at 50% opacity and all but vanished. That is what made the control stop reading as a select at all.
- Retuned to `InputText`'s vocabulary — `border-input`, `ring-ring` at `ring-1`, `rounded-md`, `px-3` — because a select sits beside text inputs in every form we ship and the two must not look like different widgets. The chevron now carries its own colour and no opacity knock-down.
### InputNumber
The spinner arrows are drawn by the **browser**. With no declared `color-scheme` the UA paints them for a light UI: near-black arrows on a near-black field, i.e. invisible. Declared via arbitrary properties rather than Tailwind's `scheme-*` so it holds whatever Tailwind version a consumer builds with.
### 🛑 Why none of this was caught here
`src/styles/theme.css` has **no dark palette** — 134 lines, zero dark tokens — so every story in this Storybook renders light, while components already ship `dark:` variants for a palette this workbench cannot show. All three failures need a dark consumer palette to appear.
**That is the actual root cause and it is not fixed by this PR.** Until the design system can render a dark palette, this class of bug can only be found in a consumer. Worth its own issue.
### Also
Adds the stories these three primitives never had (`Select`, `InputNumber`, `InputText`). `form/Select → In a form row` and `form/InputText → With a button` make a height or ring mismatch obvious on sight.
Note: `Button` already has a `size="input"` variant matching `h-input-height`, and `SelectTrigger` is `h-input-height` at its default size — control-height mismatches in consumers are almost always the consumer passing the `h-9` default or `size="sm"`, not missing library API.
### Verification
Typecheck clean; `biome` clean on every touched file (`checkbox.stories.tsx` / `switch.stories.tsx` already fail `noLabelWithoutControl` on `main` — pre-existing, untouched). Verified against the Punktfunk console palette by building this branch and consuming it in `punktfunk-plugin-rom-manager`: the Select trigger border goes from `oklch(0.985 0 0)` (near-white) to `rgb(42, 33, 72)` (the input token), chevron to muted-foreground, height 48px.
⚠️ **`@unom/ui@0.9.2` is already published to the registry** (`latest`) so the consumer fix could be verified end-to-end. Merging this makes `main` agree with what is published.
Three defects, all of the same shape: a token that happens to look right on THIS
library's own palette and wrong on a consumer's.
Select trigger
- `border-main` and `focus-visible:ring-main/50` resolve to --main, which is the
FOREGROUND. On the Punktfunk console theme that is a near-white border and a 3px
near-white focus ring — reported from the field as "a really bright outline".
- `data-placeholder:text-secondary` and the chevron's `text-secondary`: --secondary is
a SURFACE colour there, so the chevron and placeholder were painted in a background
tone at 50% opacity and all but vanished. That is what made the control stop reading
as a select at all.
- Retuned to InputText's vocabulary — border-input, ring-ring at ring-1, rounded-md,
px-3 — because a select sits beside text inputs in every form we ship and the two
must not look like different widgets. The chevron now carries its own colour and no
opacity knock-down.
InputNumber
- The spinner arrows are drawn by the BROWSER. With no declared color-scheme the UA
paints them for a light UI: near-black arrows on a near-black field, i.e. invisible.
Declared via arbitrary properties rather than Tailwind's scheme-* so it holds
whatever Tailwind version a consumer builds with.
Why none of this was caught here: src/styles/theme.css has NO dark palette — 134 lines,
zero dark tokens — so every story in this Storybook renders light, and all three failures
need a dark consumer palette to appear. Components already use `dark:` variants for a
palette this workbench cannot show. Worth fixing separately; it is the actual root cause.
Also adds the stories these three never had (Select, InputNumber, InputText). form/Select
→ "In a form row" and form/InputText → "With a button" are the ones that make a height or
ring mismatch obvious on sight.
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.
Three defects of the same shape: a token that looks right on this library's palette and wrong on a consumer's.
Select trigger
border-mainandfocus-visible:ring-main/50resolve to--main, which is the foreground. On the Punktfunk console theme that is a near-white border and a 3px near-white focus ring — reported from the field as "a really bright outline".data-placeholder:text-secondaryand the chevron'stext-secondary:--secondaryis a surface colour there, so the chevron and placeholder were painted in a background tone at 50% opacity and all but vanished. That is what made the control stop reading as a select at all.InputText's vocabulary —border-input,ring-ringatring-1,rounded-md,px-3— because a select sits beside text inputs in every form we ship and the two must not look like different widgets. The chevron now carries its own colour and no opacity knock-down.InputNumber
The spinner arrows are drawn by the browser. With no declared
color-schemethe UA paints them for a light UI: near-black arrows on a near-black field, i.e. invisible. Declared via arbitrary properties rather than Tailwind'sscheme-*so it holds whatever Tailwind version a consumer builds with.🛑 Why none of this was caught here
src/styles/theme.csshas no dark palette — 134 lines, zero dark tokens — so every story in this Storybook renders light, while components already shipdark:variants for a palette this workbench cannot show. All three failures need a dark consumer palette to appear.That is the actual root cause and it is not fixed by this PR. Until the design system can render a dark palette, this class of bug can only be found in a consumer. Worth its own issue.
Also
Adds the stories these three primitives never had (
Select,InputNumber,InputText).form/Select → In a form rowandform/InputText → With a buttonmake a height or ring mismatch obvious on sight.Note:
Buttonalready has asize="input"variant matchingh-input-height, andSelectTriggerish-input-heightat its default size — control-height mismatches in consumers are almost always the consumer passing theh-9default orsize="sm", not missing library API.Verification
Typecheck clean;
biomeclean on every touched file (checkbox.stories.tsx/switch.stories.tsxalready failnoLabelWithoutControlonmain— pre-existing, untouched). Verified against the Punktfunk console palette by building this branch and consuming it inpunktfunk-plugin-rom-manager: the Select trigger border goes fromoklch(0.985 0 0)(near-white) torgb(42, 33, 72)(the input token), chevron to muted-foreground, height 48px.⚠️
@unom/ui@0.9.2is already published to the registry (latest) so the consumer fix could be verified end-to-end. Merging this makesmainagree with what is published.Three defects, all of the same shape: a token that happens to look right on THIS library's own palette and wrong on a consumer's. Select trigger - `border-main` and `focus-visible:ring-main/50` resolve to --main, which is the FOREGROUND. On the Punktfunk console theme that is a near-white border and a 3px near-white focus ring — reported from the field as "a really bright outline". - `data-placeholder:text-secondary` and the chevron's `text-secondary`: --secondary is a SURFACE colour there, so the chevron and placeholder were painted in a background tone at 50% opacity and all but vanished. That is what made the control stop reading as a select at all. - Retuned to InputText's vocabulary — border-input, ring-ring at ring-1, rounded-md, px-3 — because a select sits beside text inputs in every form we ship and the two must not look like different widgets. The chevron now carries its own colour and no opacity knock-down. InputNumber - The spinner arrows are drawn by the BROWSER. With no declared color-scheme the UA paints them for a light UI: near-black arrows on a near-black field, i.e. invisible. Declared via arbitrary properties rather than Tailwind's scheme-* so it holds whatever Tailwind version a consumer builds with. Why none of this was caught here: src/styles/theme.css has NO dark palette — 134 lines, zero dark tokens — so every story in this Storybook renders light, and all three failures need a dark consumer palette to appear. Components already use `dark:` variants for a palette this workbench cannot show. Worth fixing separately; it is the actual root cause. Also adds the stories these three never had (Select, InputNumber, InputText). form/Select → "In a form row" and form/InputText → "With a button" are the ones that make a height or ring mismatch obvious on sight.