Skip to content

Refactor the scaffold gallery onto a @webjsdev/ui class-helper design system #1059

Description

@vivek7405

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-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.

  • Primitives to adopt: buttonClass({ variant, size }) (variants default/destructive/outline/secondary/ghost/link; sizes default/xs/sm/lg/icon...), cardClass (+ cardHeaderClass / cardTitleClass), inputClass, textareaClass, badgeClass.
  • Usage form: <button class=${buttonClass({ variant: 'outline', size: 'sm' })} @click=${...}>. Native element, classes spread on.
  • 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.

Implementation notes (for the implementing agent)

  • Read research research: adopt a @webjsdev/ui class-helper design system in the scaffold gallery #1058 first (the full decision, scope, and rejected alternatives).
  • 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)

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions