Skip to content

refactor(dashboard): component library — consolidate 9 button styles, 3 modals, 6 status indicators into <crowclaw-button>/<crowclaw-modal>/<crowclaw-status-dot> (v0.8.1) #244

Description

@subinium

Summary

The dashboard has accumulated 9 distinct button styles across views, 3 separate modal implementations, and 6 different status indicators that mean the same thing. The audit found:

  • Buttons: .btn, .btn-p, .btn-danger, .ops-btn, .steer-sticky-btn, .tp-stop-btn, .cp-restore, .send-btn, .sess-toggle-btn, .banner-btn, .ft-btn, .cta — at least 9 styles, all hand-rolled per view.
  • Modals: pairing modal in app.ts (own classes), <crowclaw-fork-modal> (own implementation), <crowclaw-modal> component exists at components/modal.ts:185 but isn't used by the other two.
  • Status dots: .led, .tag.ok/.er/.wn/.ac, .sf-dot, .sess-active-dot, .tp-tool-dot, .indicator — 6+ status indicator styles.
  • Form inputs: formStyles.form-input defined once but redefined inline in 6 places (auth-box, sess-hdr, search-overlay, steer-overlay, rename-overlay, etc.)

Build a real component library. Target milestone: v0.8.1.

Why this matters (no shortcut)

  • Visual inconsistency reads as amateur hour. A user clicking three buttons in a row that all look slightly different concludes nobody owned the design.
  • Every new feature today requires "and then style the button". With a component library, every new feature gets the right button by default.
  • Shortcut: leave the existing styles, just add a coat of CSS variables. Don't take it — the divergence is in markup structure (different classnames, different DOM hierarchies), not just colors. CSS variables can't bridge that.

Source / reference points

  • shadcn/ui — the component-library philosophy, even if we don't use React.
  • components/modal.ts — already exists, just unused everywhere.
  • Vercel UI primitives, Linear's design system docs.

Scope

Files to create

File Component Variants
packages/web/ui/src/components/button.ts <crowclaw-button> variant: 'primary' | 'secondary' | 'ghost' | 'danger', size: 'sm' | 'md' | 'lg', loading?: boolean, disabled?: boolean, slot for icon, slot for label
packages/web/ui/src/components/status-dot.ts <crowclaw-status-dot> status: 'running' | 'ok' | 'warn' | 'error' | 'idle' | 'paused', optional pulse for active states
packages/web/ui/src/components/input.ts <crowclaw-input> label slot, error slot, hint slot, all standard input props, integrates with form a11y
packages/web/ui/src/components/icon.ts <crowclaw-icon> name: string, size: number = 16, ships a sprite-based or import-on-demand icon set (Lucide icons recommended — MIT, ~1KB per icon, tree-shakeable)

Files to refactor

File Change
packages/web/ui/src/views/chat-view.ts Replace ~9 distinct button classes with <crowclaw-button>. Replace inline status indicators with <crowclaw-status-dot>. Replace inline SVGs and Unicode glyphs (..., &#10005;, &#9776;) with <crowclaw-icon>.
packages/web/ui/src/views/connect-view.ts Same refactor.
packages/web/ui/src/views/automate-view.ts Same.
packages/web/ui/src/views/settings-view.ts Same.
packages/web/ui/src/views/agent-view.ts Same.
packages/web/ui/src/app.ts Replace pairing-modal bespoke classes with <crowclaw-modal> slot pattern. Replace icon-button glyphs with <crowclaw-icon>.
packages/web/ui/src/components/fork-modal.ts Refactor to use <crowclaw-modal> as its outer shell rather than its own DOM.
packages/web/ui/src/lib/shared-styles.ts Remove .btn, .btn-p, .btn-danger, formStyles.form-input — they're superseded by components. Keep only generic utility classes.

What must NOT change

  • The visual identity at the per-element level — buttons should look approximately the same as the current .btn-p after migration. The component library is a refactor, not a redesign. (The redesign happens in dashboard issue feat: CrowClaw v0.2.0 — infrastructure, agent loop, gateway #5 below.)
  • Existing keyboard shortcuts, form-submit semantics, escape-to-close on modals.
  • Component lifecycle and accessibility — every new component must have proper ARIA roles, focus management, keyboard support.

Acceptance criteria

  • All 9 distinct button styles in the codebase are replaced by <crowclaw-button> calls. Verified by grep -rE "\.btn|\.btn-|\.ops-btn|\.send-btn" returning only the component definition itself.
  • All 3 modal implementations (app.ts pairing, fork-modal, <crowclaw-modal>) consolidate into one — <crowclaw-modal> with slots.
  • All status indicator usages migrate to <crowclaw-status-dot>. The 6+ inline status styles are removed.
  • All inline <svg> icon definitions and Unicode glyph hacks (..., &#9776;, &#10005;) replaced with <crowclaw-icon>. Verified count: zero raw SVG elements remaining outside components/icon.ts.
  • No regression in keyboard / focus / a11y behavior. Tab order in modals matches current. Escape closes.
  • CSS lint rule (custom Vitest test) prevents new files from defining .btn-* or .dot-* classes.

Performance target

  • Bundle size after consolidation: ≤ current size + 5KB gzipped. Component overhead is offset by removing duplicated styles.
  • Component render: < 1ms for typical button/dot/icon.

Out of scope

  • Visual redesign (separate issue — 04-visual-system-reset).
  • Adding new component types (cards, tabs, dropdowns) — only existing patterns get formalized here.
  • Theme switching — handled by a separate issue.

Labels: enhancement, priority/warning, architecture, ux, polish, source/audit

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestpolishProfessional finish / accessibility / i18npriority/warningReal cost / risk; fix when scheduledsource/auditInternal audit findinguxUser experience / interaction design

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions