See also: docs/ui/ for comprehensive UI documentation including architecture, design system, and component catalog.
This document describes the web UI component library for Shep AI CLI, built with Next.js 16, React 19, Tailwind CSS v4, and shadcn/ui.
The web component library provides a consistent design system for the Shep AI web interface. It uses:
The web UI uses a four-tier component architecture with strict dependency direction. See docs/ui/architecture.md for full details.
src/presentation/web/
βββ app/ # Next.js App Router pages
β βββ globals.css # Global styles with design tokens
β βββ layout.tsx # Root layout (uses AppShell)
β βββ page.tsx # Home page
β βββ version/
β βββ page.tsx # Version info page
βββ components/
β βββ ui/ # Tier 0: shadcn/ui primitives (CLI-managed)
β β βββ button.tsx
β β βββ button.stories.tsx
β β βββ card.tsx
β β βββ dialog.tsx
β β βββ ... # 13 primitives with colocated stories
β βββ common/ # Tier 1: Cross-feature composed components
β β βββ theme-toggle/
β β β βββ theme-toggle.tsx
β β β βββ theme-toggle.stories.tsx
β β β βββ index.ts
β β βββ page-header/
β β βββ empty-state/
β β βββ loading-skeleton/
β βββ layouts/ # Tier 2: Page shells, structural wrappers
β β βββ sidebar/
β β βββ header/
β β βββ dashboard-layout/
β β βββ app-shell/
β βββ features/ # Tier 3: Domain-specific UI bound to routes
β βββ version/
β βββ settings/
βββ docs/ # Design system MDX documentation
β βββ Colors.mdx
β βββ Typography.mdx
β βββ GettingStarted.mdx
βββ hooks/
β βββ useTheme.ts # Theme state management
βββ lib/
β βββ utils.ts # Utility functions (cn)
βββ types/
βββ theme.ts # Theme type definitions
Key rules:
ui/index.ts or common/index.ts)index.ts)button.stories.tsx next to button.tsx)# Component subfolder pattern (Tier 1-3)
components/common/page-header/
βββ page-header.tsx # Component implementation
βββ page-header.stories.tsx # Storybook stories (colocated)
βββ index.ts # Per-component barrel export
Design tokens are defined in globals.css using Tailwind CSS v4βs @theme directive:
@theme {
/* Colors - Light Mode */
--color-background: #ffffff;
--color-foreground: #0a0a0a;
--color-primary: #3b82f6;
--color-primary-foreground: #ffffff;
--color-secondary: #f1f5f9;
--color-secondary-foreground: #0f172a;
--color-muted: #f1f5f9;
--color-muted-foreground: #64748b;
--color-accent: #f1f5f9;
--color-accent-foreground: #0f172a;
--color-destructive: #ef4444;
--color-destructive-foreground: #ffffff;
--color-border: #e2e8f0;
--color-input: #e2e8f0;
--color-ring: #3b82f6;
--color-card: #ffffff;
--color-card-foreground: #0a0a0a;
--color-popover: #ffffff;
--color-popover-foreground: #0a0a0a;
/* Typography */
--font-sans: ui-sans-serif, system-ui, sans-serif;
--font-mono: ui-monospace, SFMono-Regular, monospace;
/* Border Radius */
--radius-sm: 0.25rem;
--radius-md: 0.375rem;
--radius-lg: 0.5rem;
--radius-xl: 0.75rem;
--radius-2xl: 1rem;
/* Shadows */
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1);
}
Dark mode tokens are defined in the .dark class selector, which overrides the light mode values:
.dark {
--color-background: #0a0a0a;
--color-foreground: #fafafa;
--color-secondary: #1e293b;
--color-muted: #1e293b;
--color-border: #1e293b;
/* ... other dark mode overrides */
}
Note: Spacing tokens are intentionally omitted to avoid conflicts with Tailwind v4βs built-in sizing utilities. Use Tailwindβs standard spacing scale (p-4, m-6, gap-2, etc.).
| Tier | Directory | Purpose | Import Rule |
|---|---|---|---|
| 0 | ui/ |
shadcn/ui primitives | External packages only |
| 1 | common/ |
Cross-feature composed components | Can import ui/ |
| 2 | layouts/ |
Page shells, structural wrappers | Can import ui/, common/ |
| 3 | features/ |
Domain-specific UI bound to routes | Can import all lower tiers |
See Component Catalog for the full list of available components.
import { Button } from '@/components/ui/button';
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
import { ThemeToggle } from '@/components/common/theme-toggle';
export function MyComponent() {
return (
<Card>
<CardHeader>
<CardTitle>Welcome</CardTitle>
<ThemeToggle />
</CardHeader>
<CardContent>
<Button variant="default">Get Started</Button>
</CardContent>
</Card>
);
}
Tier 0 (shadcn/ui primitives):
pnpm dlx shadcn@latest add [component-name]
Tier 1-3 (custom components):
components/{tier}/[name]/[name].tsx[name].stories.tsx (MANDATORY)index.tsComponents are configured via components.json at the web package root.
The theme system supports light, dark, and system-preference modes:
import { useTheme } from '@/hooks/useTheme';
function MyComponent() {
const { theme, setTheme, resolvedTheme } = useTheme();
return <button onClick={() => setTheme('dark')}>Current: {resolvedTheme}</button>;
}
Theme preference is persisted to localStorage under the key shep-theme.
pnpm dev:storybook
Opens Storybook at http://localhost:6006 with all component stories and design token documentation.
# Unit tests (React components)
pnpm test:unit
# E2E tests (Web UI with Playwright)
pnpm test:e2e:web
# Watch mode for TDD
pnpm test:watch
# Build Next.js app
pnpm build:web
# Build Storybook
pnpm build:storybook
Located in tests/unit/presentation/web/, unit tests verify:
Example test:
import { render, screen } from '@testing-library/react';
import { Button } from '@/components/ui/button';
describe('Button', () => {
it('renders with default variant', () => {
render(<Button>Click me</Button>);
expect(screen.getByRole('button')).toHaveTextContent('Click me');
});
});
Located in tests/e2e/web/, E2E tests verify:
The following path aliases are configured:
| Alias | Path |
|---|---|
@/components |
src/presentation/web/components |
@/lib |
src/presentation/web/lib |
@/hooks |
src/presentation/web/hooks |
@/types |
src/presentation/web/types |
These are configured in:
tsconfig.json (TypeScript)vitest.config.ts (Tests).storybook/main.ts (Storybook)| File | Purpose |
|---|---|
components.json |
shadcn/ui configuration |
postcss.config.mjs |
PostCSS with Tailwind v4 |
next.config.ts |
Next.js configuration |
.storybook/main.ts |
Storybook configuration |
playwright.config.ts |
Playwright E2E configuration |