Skip to content

feat(ui-core): the design system moves to the b2iH preset - #778

Merged
YaelAnaya merged 5 commits into
mainfrom
refactor/preset-b2iH
Aug 25, 2026
Merged

feat(ui-core): the design system moves to the b2iH preset#778
YaelAnaya merged 5 commits into
mainfrom
refactor/preset-b2iH

Conversation

@YaelAnaya

Copy link
Copy Markdown
Collaborator

The design system moves from b3bXyyPdWj to b2iH, with --pointer on.

Stacked on #777 (the icon swap) and should merge after it — b2iH decodes
iconLibrary: lucide, which that branch already applied, so the two agree instead
of fighting.

What the preset actually changes

Decoded with shadcn preset decode b2iH, against the preset it replaces:

font         inter   ->  geist
fontHeading  geist   ->  inherit
chartColor   orange  ->  neutral
iconLibrary  tabler  ->  lucide     (already the product's, via #777)

style, base, baseColor, theme, radius, menuAccent, menuColor   unchanged

The values are the CLI's own output. shadcn@latest — 4.19.0, checked rather than
assumed — scaffolded the preset into a throwaway container and the delta was applied
from what it generated, which is how the previous preset's values got in too.

init was not run against this repository

The command as given scaffolds. --template react-router creates a react-router
project, and init rewrites components.json and the stylesheet wholesale — in a
workspace that already is a react-router app with a stylesheet carrying nine
justified extension tokens, that is not a configuration change but a replacement.
So the preset was generated in /tmp inside a container, and this branch carries
the difference between what it produced and what the repository had.

One family

--font-sans is Geist and --font-heading is var(--font-sans), so a heading is
the same face at a different size and weight rather than a second typeface.

The h1h4 rule stays in the base layer even though it now changes nothing. It
keeps the semantic hook, so a later preset that splits the families again lands in
one declaration rather than at every heading — and the styleguide's browser check
still asserts each hook by name rather than against the other, because a heading
that had fallen back to the browser's default would satisfy "the two agree" while
proving the hook was never wired.

@fontsource-variable/inter is gone. The lockfile is resolved from the manifests
and a frozen install runs from an empty tree.

The chart palette, and what the trade is

Five neutral steps, identical in both themes as the orange ones were:

chart-1  oklch(0.87 0 0)     chart-4  oklch(0.371 0 0)
chart-2  oklch(0.556 0 0)    chart-5  oklch(0.269 0 0)
chart-3  oklch(0.439 0 0)

They separate by lightness rather than hue. That reads for somebody who cannot
separate two hues and it survives greyscale, at the cost of holding fewer series
apart before the steps run together. DESIGN.md says so, because a palette that
reads in greyscale and a palette that holds many series apart are not the same
palette, and the next person choosing between them should not have to work it out
from five oklch values.

Nothing in the product draws charts yet. The only surface using those tokens is
the styleguide's swatch row. So this change is real in the tokens and currently
invisible in the app — worth knowing before reading the baselines and wondering
where the colour went.

The pointer option

button:not(:disabled), [role="button"]:not(:disabled) { cursor: pointer; }

Scoped to what is pressable and stopped at :not(:disabled): a hand over a control
that will not respond is the cursor making a promise the control does not keep.

Test plan

Every command ran inside Docker/Linux; no repository validation ran natively on the
host.

install (pnpm install --frozen-lockfile, from an empty tree)  PASS
build (pnpm -r build)                                         PASS
@visionset/ui-core lint (incl. typecheck)                     PASS
@visionset/ui-core test                                       PASS
@visionset/app build                                          PASS
@visionset/app lint (incl. typecheck:e2e)                     PASS
@visionset/annotator test                                     PASS
pnpm test:scripts                                             PASS
pnpm version:check                                            PASS

full browser suite (all projects)                             276 passed
visual project, three consecutive runs                        10 / 10 / 10

Two gates caught the change before anything else did, which is what they are for:
tokens.test.ts on the chart values, the font pair and the import count, and the
styleguide's base-layer check on the two font hooks. Both were updated to the new
contract rather than relaxed.

The ten baselines were regenerated, opened and read. All of them carry type, so a
change of family moves all of them.

Scope

Routes changed:            No
Product IA changed:        No
Backend / wire / domain:   No
Primitive API changed:     No
Annotator behaviour:       No

Tokens and typography change; nothing about what the product does changes.

Note for merging

components.json and the preset agree again on every property. The divergence
#777 had to document — configuration saying lucide while the preset decoded
tabler — is gone, and the note describing it is removed rather than left to
describe a state that no longer exists.

Four decoded properties change against `b3bXyyPdWj`, and the values are the CLI's
own output from a throwaway scratch project rather than anything transcribed by
hand:

  font         inter   -> geist
  fontHeading  geist   -> inherit
  chartColor   orange  -> neutral
  iconLibrary  tabler  -> lucide   (already the product's, from the icon swap)

**One family.** `--font-sans` is Geist and `--font-heading` is `var(--font-sans)`,
so a heading is the same face at a different size and weight. The `h1`-`h4` rule
stays in the base layer even though it now changes nothing: it keeps the semantic
hook, so a later preset that splits the families again lands in one declaration
rather than at every heading. `@fontsource-variable/inter` goes, with the lockfile
resolved from the manifests and a frozen install run from an empty tree.

**The chart palette separates by lightness rather than by hue.** Five neutral
steps, identical in both themes as the orange ones were. It reads for somebody who
cannot separate two hues and it survives greyscale, at the cost of holding fewer
series apart before the steps run together.

**The pointer option is on**, scoped to `button` and `[role="button"]` and stopped
at `:not(:disabled)` - a hand over a control that will not respond is the cursor
making a promise the control does not keep.

`init` was not run against this repository. It scaffolds: it would rewrite
`components.json` and the stylesheet wholesale and bring a react-router template
into a workspace that already is one. The preset was generated in a scratch
container instead and the delta applied here, which is also how the previous
preset's values got in.
`tokens.test.ts` pins the chart steps and the font pair, so the preset move showed
up there first - which is what those assertions are for. Its import-order check
counted five and now counts four: one family means one font import.

The styleguide's base-layer check asserted Inter on the body and Geist on
headings. Both are Geist now, and each is still asserted by name rather than
against the other: a heading that had fallen back to the browser's default would
satisfy "the two agree" while proving the hook was never wired.

`design_tokens.test.mjs` loses the note about `iconLibrary` diverging from the
preset. `b2iH` decodes `lucide`, so the configuration and the preset agree again
and the divergence that comment described no longer exists.
The preset code, the decoded values and the CLI version that generated them. Four
sections were describing a system the repository had stopped having: Typography
named two families, Charts listed the orange steps, Source of Truth quoted the old
code, and the architecture note repeated all of it.

Two additions rather than replacements. *Typography* says why `font-heading`
survives when it no longer changes the face - it is the hook a later split lands
in. *Charts* says what separating by lightness buys and costs, because a palette
that reads in greyscale and a palette that holds many series apart are not the
same palette, and the next person choosing between them should not have to work
that out from five oklch values.

The note about `iconLibrary` diverging from the preset is gone: `b2iH` decodes
`lucide`, so the two agree again and there is nothing left to warn about.
Every reference image carries type, so a change of family moves all ten. They were
regenerated in the canonical container, opened and read rather than accepted from
a green run, and the suite passes three consecutive times against them.

Worth recording: the chart palette changed and no baseline shows it. The only
surface drawing those tokens is the styleguide's swatch row, which is not in the
reference set, so the new steps are held by `tokens.test.ts`'s exact values and by
nothing visual. The first chart the product actually builds is what would make a
picture of them worth taking.
Base automatically changed from refactor/icons-back-to-lucide to main August 25, 2026 03:49
@YaelAnaya
YaelAnaya merged commit a27f271 into main Aug 25, 2026
15 checks passed
@YaelAnaya
YaelAnaya deleted the refactor/preset-b2iH branch August 25, 2026 03:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant