fix(form): Select and InputNumber were unusable on a dark consumer palette (0.9.2) #1

Merged
enricobuehler merged 2 commits from worktree-form-fixes into main 2026-08-09 14:04:24 +00:00
Owner

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.
enricobuehler added 2 commits 2026-08-09 10:44:22 +00:00
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.
enricobuehler merged commit 10b1eae3de into main 2026-08-09 14:04:24 +00:00
enricobuehler deleted branch worktree-form-fixes 2026-08-09 14:04:25 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: unom/ui#1