4.5 KiB
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 +ToastProvidercontext) 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:
- Create
web/src/components/ui/directory. - Button:
- Props:
variant,size,disabled,loading,onClick,children,class(merge with base) - Primary:
.gradient-primarybg, 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
- Props:
- Card:
- Props:
children,class,header?,footer? - Uses
.gradient-card,.border-border/50, rounded-xl
- Props:
- 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
- Props:
- Badge:
- Props:
variant,children - Small rounded pill with semantic color backgrounds
- Props:
- Modal:
- Props:
isOpen,onClose,title,children,size? - Uses SolidJS
Portalfor rendering outside DOM tree - Backdrop click closes; Escape key closes
- Focus trap inside modal content
- Enter/exit CSS transitions
- Props:
- Toast:
- Create
ToastContextwithshowToast({ message, variant, duration })API ToastProviderrenders a fixed-position container- Individual toasts auto-dismiss after duration (default 4s)
- Variants: success, error, warning, info
- Create
- Write unit tests for each component using Vitest + Testing Library.
- 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 testruns and passes all UI tests- Manual inspection: create a temporary
/ui-testroute 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
classprop should always merge with base classes using a utility likeclsxortailwind-merge. Install if not present. - Do NOT add business logic to primitives — they are purely presentational.