Files
Kordant/tasks/shieldai-unified-restructure/03-ui-primitive-library.md
2026-05-25 12:23:23 -04:00

4.5 KiB

03. UI Primitive Library — Button, Card, Input, Badge, Modal, Toast

meta: id: shieldai-unified-restructure-03 feature: shieldai-unified-restructure priority: P0 depends_on: [shieldai-unified-restructure-01, shieldai-unified-restructure-02] tags: [frontend, components, design-system, solidjs]

objective:

  • Build a reusable set of UI primitive components in SolidJS that match the ShieldAI theme and are used across all pages and features. These should feel cohesive with the Lendair component style but adapted for ShieldAI's security-focused brand.

deliverables:

  • web/src/components/ui/Button.tsx — Variants: primary, secondary, ghost, danger. Sizes: sm, md, lg. States: disabled, loading.
  • web/src/components/ui/Card.tsx — Container with gradient-card background, border, padding, and optional header/footer slots.
  • web/src/components/ui/Input.tsx — Text input with label, error state, helper text, and focus ring. Types: text, email, password, number.
  • web/src/components/ui/Badge.tsx — Status badges with variants: default, success, warning, error, info.
  • web/src/components/ui/Modal.tsx — Accessible dialog with overlay, close button, focus trap, and animation.
  • web/src/components/ui/Toast.tsx — Toast notification system (single component + ToastProvider context) with auto-dismiss, positions, and variants.
  • web/src/components/ui/index.ts — Barrel export for all primitives.
  • web/src/components/ui/ui.test.ts — Unit tests for each primitive.

steps:

  1. Create web/src/components/ui/ directory.
  2. Button:
    • Props: variant, size, disabled, loading, onClick, children, class (merge with base)
    • Primary: .gradient-primary bg, white text, .shadow-glow-primary
    • Secondary: transparent bg, border, primary text
    • Ghost: transparent, no border, text only
    • Danger: red gradient bg
    • Loading state shows spinner SVG and disables interaction
  3. Card:
    • Props: children, class, header?, footer?
    • Uses .gradient-card, .border-border/50, rounded-xl
  4. Input:
    • Props: label, type, value, onInput, error, helperText, placeholder
    • Styled with bg-transparent, border, focus ring using --color-focus-ring
    • Error state adds red border and error text
  5. Badge:
    • Props: variant, children
    • Small rounded pill with semantic color backgrounds
  6. Modal:
    • Props: isOpen, onClose, title, children, size?
    • Uses SolidJS Portal for rendering outside DOM tree
    • Backdrop click closes; Escape key closes
    • Focus trap inside modal content
    • Enter/exit CSS transitions
  7. Toast:
    • Create ToastContext with showToast({ message, variant, duration }) API
    • ToastProvider renders a fixed-position container
    • Individual toasts auto-dismiss after duration (default 4s)
    • Variants: success, error, warning, info
  8. Write unit tests for each component using Vitest + Testing Library.
  9. Create barrel export index.ts.

steps:

  • Unit: Each component renders correctly with default props
  • Unit: Button variants produce correct CSS classes
  • Unit: Input error state renders error message
  • Unit: Modal open/close toggles visibility and calls onClose
  • Unit: Toast context show/dismiss works and auto-dismiss fires
  • Visual: Inspect all components in Storybook-style page (optional) or directly in landing page

acceptance_criteria:

  • All 6 primitives exist in web/src/components/ui/ with barrel export
  • Each primitive has full TypeScript types
  • Button supports all 4 variants, 3 sizes, disabled, and loading states
  • Input supports error states and focus rings
  • Modal is accessible (focus trap, ESC to close, backdrop click)
  • Toast system can queue multiple toasts and auto-dismiss
  • All components use theme tokens (no hardcoded colors)
  • Unit tests pass with >80% coverage

validation:

  • cd web && pnpm test runs and passes all UI tests
  • Manual inspection: create a temporary /ui-test route importing all primitives and verify visual appearance in both light/dark modes

notes:

  • Reference Lendair components: ~/code/Lendair/web/src/components/ui/Button.tsx, Card.tsx, Input.tsx, Modal.tsx, Toast.tsx
  • Keep components uncontrolled where possible (SolidJS signals for state) to avoid prop drilling.
  • The class prop should always merge with base classes using a utility like clsx or tailwind-merge. Install if not present.
  • Do NOT add business logic to primitives — they are purely presentational.