Skip to content

research: adopt a @webjsdev/ui class-helper design system in the scaffold gallery #1058

Description

@vivek7405

Question

Should the scaffold feature gallery use a design system rather than hand-rolled inline Tailwind on every demo, and if so, built on what?

Decision

Yes. Adopt a lightweight design system in the gallery built on @webjsdev/ui's tier-1 class helpers (buttonClass, cardClass, inputClass, badgeClass, etc.), not on hand-rolled per-demo class strings and not on wrapping components. The gallery keeps looking mostly the same (same design tokens, same neutral palette); this is a consistency + variants refactor plus an architecture cleanup, not a redesign.

Why (rationale)

  • The drift is real and measurable. ~18 gallery files hand-roll button styling with slightly different class strings; card panels, inputs, and badges repeat too. That drift is exactly how the cursor-pointer gap (fix: add cursor-pointer to the gallery streaming demo button #1057) happened: one of 18 hand-styled buttons was missed.
  • @webjsdev/ui already solves it, and the gallery is half-adopted. The gallery already uses the shadcn-standard design tokens (--color-primary, --color-card). It just does not use the buttonClass({ variant, size }) / cardClass() / inputClass() / badgeClass() helpers (button variants default/destructive/outline/secondary/ghost/link; sizes xs/sm/lg/icon...).
  • The primitives are CLASS HELPERS, not wrapping components. You spread the classes onto a native element: <button class=${buttonClass({ variant: 'outline', size: 'sm' })} @click=...>. So adopting them:
    • adds NO indirection and keeps each demo's concept (server action, streaming, slots) fully readable, unlike a <ui-button> wrapper;
    • centralizes consistency (variants + sizes) and makes the fix: add cursor-pointer to the gallery streaming demo button #1057 bug class impossible: buttonClass puts cursor-pointer on the base and handles disabled:pointer-events-none;
    • dogfoods @webjsdev/ui in WebJs's PRIMARY teaching surface (a strong signal that the framework uses its own kit) and teaches agents and humans the right pattern for growing the scaffold.

Scope (deliberately bounded)

  • Adopt the HIGH-REPETITION primitives only: button (~18 files), card/panel, input/textarea, badge. That is where the drift lives.
  • Leave one-off LAYOUT (grids, section wrappers, the gallery index cards) as inline Tailwind. Those are not design-system primitives; forcing them into helpers would obscure, not clarify.
  • Shadow-DOM demos (for example reactive-meter) keep their scoped static styles; buttonClass is a light-DOM Tailwind helper, so light-DOM primitives are the system and shadow demos stay a deliberate exception (a couple of them).
  • Ship the tier-1 class helpers under the scaffold's components/ui/ (the scaffold already pre-initializes for webjs ui add), so a scaffolded app STARTS with a design system.
  • Keep it visually "mostly how it looks now": same tokens, same palette. Consistency + variants, not a new look.

Alternatives considered and rejected

  • Full @webjsdev/ui wrapping components for every demo. Rejected: adds an indirection layer that obscures the WebJs concept each demo is meant to teach. The class-helper form gives the design system with none of that cost.
  • A global CSS base rule (button:not(:disabled) { cursor: pointer }). Rejected earlier (see fix: add cursor-pointer to the gallery streaming demo button #1057 discussion): un-Tailwind-idiomatic, inconsistent with a codebase that does per-element utilities, and it only addresses the cursor symptom, not the broader styling drift.
  • A bespoke gallery-only style helper (lib/ui.ts). Rejected in favor of dogfooding @webjsdev/ui, so the gallery teaches the real kit rather than a one-off.

Non-goals / guardrails

  • @webjsdev/ui stays OPT-OUT (the helpers are removable class functions; defaults, not lock-in).
  • Do NOT pull in all 40 registry components, only the handful the gallery repeats.
  • Not a redesign: the visual result should be recognizably the same gallery.

Follow-up

The implementation is separate, actionable tracked work (filed via the webjs-file-issue skill), with the scoped primitive list above as its spec.

Metadata

Metadata

Assignees

No one assigned

    Labels

    researchResearch/design/decision record (no code); filter these to read design history

    Type

    No type

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions