You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The scaffold feature gallery hand-rolls inline Tailwind on every demo. ~18 files repeat button styling with slightly different class strings, and card panels / inputs / badges repeat too. That drift is how the cursor-pointer gap (#1057) happened (one of 18 hand-styled buttons was missed). The gallery is WebJs's primary teaching surface, so it should model a small design system instead of copy-pasted class strings. Decision recorded in research #1058.
Design / approach
Adopt @webjsdev/ui's tier-1 CLASS HELPERS (not wrapping components) for the high-repetition primitives, so the demo markup stays native and readable while consistency and variants come from one place. Ship the helper files under the scaffold's components/ui/ and have the gallery demos import them.
Keep it visually mostly the same: same tokens (--color-primary, --color-card), same neutral palette. This is a consistency + variants refactor plus an architecture cleanup, NOT a redesign.
Leave one-off LAYOUT (grids, section wrappers, the gallery index cards) as inline Tailwind. Shadow-DOM demos keep their scoped static styles.
The ui tier-1 helpers already exist and export class functions: packages/ui/packages/registry/components/{button,card,input,textarea,badge}.ts export buttonClass / cardClass (+ cardHeaderClass / cardTitleClass) / inputClass / textareaClass / badgeClass. They depend on cn from lib/utils/cn.ts, which the scaffold ALREADY writes (writeUiBootstrap in packages/cli/lib/create.js writes lib/utils/cn.ts + lib/utils/dom.ts). So the helper files can be dropped in with their cn dep satisfied.
Where to ship the helpers: put the tier-1 helper files under the scaffold's packages/cli/templates/gallery/components/ui/{button,card,input,textarea,badge}.ts (or wire writeUiBootstrap in create.js to emit them by default). A consumer imports with the # alias, for example import { buttonClass } from '#components/ui/button.ts'. Confirm the alias resolves in the generated app (the scaffold ships the catch-all #* imports map).
Where to edit the demos: the ~18 button sites live across packages/cli/templates/gallery/** (grep bg-primary text-primary-foreground for the button class strings, and the shared btn const in modules/stream/components/stream-demo.ts). Swap inline strings for the helper calls, choosing the closest variant/size so the look is preserved. Card panels: grep rounded-2xl border border-border bg-card. Inputs/textarea/badge similarly.
Landmines:
Do NOT convert the shadow-DOM demos (e.g. modules/components/components/reactive-meter.ts, static shadow = true): buttonClass is a light-DOM Tailwind helper and shadow trees do not get document Tailwind. Those keep their static styles.
Do NOT over-abstract: only button / card / input / textarea / badge. Leave layout (grids, section wrappers, gallery index cards) inline. Pulling all 40 registry components in is out of scope.
Light-DOM tag-prefix invariant (7) does not apply here (Tailwind utilities, not custom CSS class selectors).
Keep @webjsdev/ui OPT-OUT in spirit: these are removable class functions, defaults not lock-in.
The gallery ships in every UI template, so the change propagates to every scaffolded app.
Invariants + workflow: this is scaffold work, so invoke the webjs-scaffold-sync skill to walk every scaffold surface. MANDATORY verification: generate an app (webjs create), boot it, run webjs check, and confirm the gallery renders and looks the same (the generators emit strings, so an escaping bug only shows in a freshly generated app). Prose in any touched markdown follows invariant 11.
Tests + docs: scaffold tests under test/scaffolds/** must still pass (a generated app boots + webjs check clean). No framework API changed, so no docs-site API page. If a per-agent scaffold rule file mentions styling conventions, keep it consistent (the AGENTS.md styling guidance already says extract repeated class bundles into a helper, which this satisfies).
Acceptance criteria
The gallery uses buttonClass / cardClass / inputClass / textareaClass / badgeClass for the high-repetition primitives instead of hand-rolled inline strings
The tier-1 helper files ship under the scaffold's components/ui/ and resolve via the # alias in a generated app
Shadow-DOM demos are untouched (still use static styles)
The gallery looks recognizably the same (same tokens/palette; a consistency + variants refactor, not a redesign)
A freshly generated app boots and webjs check passes (the generate + boot + check gate); test/scaffolds/** green
Layout (grids, section wrappers, gallery index cards) left as inline Tailwind (no over-abstraction)
Problem
The scaffold feature gallery hand-rolls inline Tailwind on every demo. ~18 files repeat button styling with slightly different class strings, and card panels / inputs / badges repeat too. That drift is how the
cursor-pointergap (#1057) happened (one of 18 hand-styled buttons was missed). The gallery is WebJs's primary teaching surface, so it should model a small design system instead of copy-pasted class strings. Decision recorded in research #1058.Design / approach
Adopt
@webjsdev/ui's tier-1 CLASS HELPERS (not wrapping components) for the high-repetition primitives, so the demo markup stays native and readable while consistency and variants come from one place. Ship the helper files under the scaffold'scomponents/ui/and have the gallery demos import them.buttonClass({ variant, size })(variants default/destructive/outline/secondary/ghost/link; sizes default/xs/sm/lg/icon...),cardClass(+cardHeaderClass/cardTitleClass),inputClass,textareaClass,badgeClass.<button class=${buttonClass({ variant: 'outline', size: 'sm' })} @click=${...}>. Native element, classes spread on.--color-primary,--color-card), same neutral palette. This is a consistency + variants refactor plus an architecture cleanup, NOT a redesign.static styles.Implementation notes (for the implementing agent)
packages/ui/packages/registry/components/{button,card,input,textarea,badge}.tsexportbuttonClass/cardClass(+cardHeaderClass/cardTitleClass) /inputClass/textareaClass/badgeClass. They depend oncnfromlib/utils/cn.ts, which the scaffold ALREADY writes (writeUiBootstrapinpackages/cli/lib/create.jswriteslib/utils/cn.ts+lib/utils/dom.ts). So the helper files can be dropped in with theircndep satisfied.packages/cli/templates/gallery/components/ui/{button,card,input,textarea,badge}.ts(or wirewriteUiBootstrapincreate.jsto emit them by default). A consumer imports with the#alias, for exampleimport { buttonClass } from '#components/ui/button.ts'. Confirm the alias resolves in the generated app (the scaffold ships the catch-all#*imports map).packages/cli/templates/gallery/**(grepbg-primary text-primary-foregroundfor the button class strings, and the sharedbtnconst inmodules/stream/components/stream-demo.ts). Swap inline strings for the helper calls, choosing the closest variant/size so the look is preserved. Card panels: greprounded-2xl border border-border bg-card. Inputs/textarea/badge similarly.modules/components/components/reactive-meter.ts,static shadow = true):buttonClassis a light-DOM Tailwind helper and shadow trees do not get document Tailwind. Those keep theirstatic styles.@webjsdev/uiOPT-OUT in spirit: these are removable class functions, defaults not lock-in.webjs-scaffold-syncskill to walk every scaffold surface. MANDATORY verification: generate an app (webjs create), boot it, runwebjs check, and confirm the gallery renders and looks the same (the generators emit strings, so an escaping bug only shows in a freshly generated app). Prose in any touched markdown follows invariant 11.test/scaffolds/**must still pass (a generated app boots +webjs checkclean). No framework API changed, so no docs-site API page. If a per-agent scaffold rule file mentions styling conventions, keep it consistent (the AGENTS.md styling guidance already says extract repeated class bundles into a helper, which this satisfies).Acceptance criteria
buttonClass/cardClass/inputClass/textareaClass/badgeClassfor the high-repetition primitives instead of hand-rolled inline stringscomponents/ui/and resolve via the#alias in a generated appstatic styles)webjs checkpasses (the generate + boot + check gate);test/scaffolds/**green