Skip to content

Enforce design-system consistency across all monorepo apps #1064

Description

@vivek7405

Problem

The scaffold gallery was recently made internally consistent on the design system (a single section-spacing rhythm, themed tokens, and one shared focus ring; see PR #1060). The other in-repo WebJs apps (website/, examples/blog/, docs/, packages/ui/packages/website/) were NOT swept the same way and have drifted.

The concrete trigger was the focus ring. An interactive element that does NOT go through the design system's global focus rule falls back to the raw browser outline: auto (thick, and light-coloured on a dark theme), which shows on window-refocus (:focus-visible re-fires when the OS window regains focus). Observed on the website's live like-button and the copy-command button.

Design / approach

Follow what the gallery now does:

  • The design system supplies ONE themed, keyboard-only (:focus-visible), never-removed focus ring globally, the shadcn convention @layer base { * { @apply border-border outline-ring/50 } } in the theme CSS.
  • Light-DOM elements need NO per-element focus style (rely on the global rule).
  • A shadow-DOM component is the one exception: a document rule cannot cross the shadow boundary, so it styles its own :focus-visible in static styles.

Best-practice research (Next.js create-next-app ships NO focus rules and relies on the browser default; removing focus fails WCAG 2.4.7): the themed global outline is the right model. A refinement worth evaluating is making the outline EXPLICIT so the WIDTH is consistent too (see notes), not just the colour.

Implementation notes (for the implementing agent)

  • Where to edit (per app, each has its own layout + theme):
    • website/: components/like-button.ts is a light-DOM <button> styled by a bare like-button button rule in app/page.ts (around L145-158) with a :hover but no :focus-visible. components/copy-cmd.ts similarly. Ensure the website's Tailwind base carries the global outline-ring/50 (or add a themed :focus-visible).
    • examples/blog/, docs/: audit each app's root layout / base CSS for the global focus rule and any shadow components.
    • packages/ui/packages/website/app/_components/preview-tabs.ts is a shadow-DOM component that currently does .tab:focus-visible { outline: none } (L54-55), an a11y regression, give it a real themed :focus-visible outline instead.
  • Shadow-DOM components set their own: button:focus-visible { outline: 2px solid var(--color-ring); outline-offset: 2px } in static styles (see the gallery's reactive-meter.ts for the pattern).
  • Best-practice width refinement (evaluate, do NOT rush): outline-ring/50 themes only the COLOUR and leaves the browser-default outline WIDTH, which varies (~3px on a <button>, ~1px on an <a>). An explicit :focus-visible { outline: 2px solid var(--color-ring); outline-offset: 2px } fixes the width too. LANDMINE: a hand-written var(--color-ring) / var(--ring) in a raw @layer base rule resolved to currentColor in testing (Tailwind v4 @theme token resolution quirk), while the outline-ring/50 UTILITY resolves it fine. So the explicit form needs careful token wiring (confirm --color-ring is emitted as a runtime custom property, not @theme inline-inlined) before adopting. If it lands, apply it in the ui registry theme packages/ui/packages/registry/themes/index.css (the single source the scaffold + webjs ui add pull from) so it propagates.
  • Broader consistency: while in each app, audit section-spacing rhythm, token usage, and ui class-helper usage, matching the gallery sweep.
  • Invariants: shadcn API parity for @webjsdev/ui (AGENTS invariant 5); the prose-punctuation invariant (11) for any doc edits; no vendor pins in monorepo apps.
  • Docs surface: the focus convention is already documented in .agents/skills/webjs/references/styling.md ("Focus rings"); keep it in sync if the model changes.

Acceptance criteria

  • Every interactive element in website/, examples/blog/, docs/, and packages/ui/packages/website/ shows a themed --ring focus ring on keyboard focus / window-refocus, never the raw browser outline: auto.
  • No app uses outline: none without a themed replacement (a11y).
  • Every shadow-DOM component styles its own :focus-visible.
  • The explicit-width refinement is either adopted (with the token quirk resolved) or a note records why it was deferred.
  • Section spacing, tokens, and ui helpers audited for consistency in each app.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

Status
Todo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions