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)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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