@stoneforge/ui
React 19 component library for building Stoneforge front-ends. Includes core UI primitives, domain-specific components, real-time communication hooks, and a full design token system.React 19: Built with the latest React features including automatic batching, concurrent rendering, and Server Components support.
Installation
npm install @stoneforge/ui
Peer Dependencies
npm install react react-dom
npm install @tanstack/react-query @tanstack/react-router
npm install @tiptap/react @tiptap/starter-kit @tiptap/extension-link
npm install lowlight marked turndown
Quick Start
import '@stoneforge/ui/styles/tokens.css';
import { Button, Card, CardHeader, CardTitle, CardContent } from '@stoneforge/ui';
import { useTheme } from '@stoneforge/ui/hooks';
function App() {
const { isDark, toggleDarkMode } = useTheme();
return (
<Card>
<CardHeader>
<CardTitle>Welcome to Stoneforge</CardTitle>
</CardHeader>
<CardContent>
<Button variant="primary" onClick={toggleDarkMode}>
{isDark ? 'Light Mode' : 'Dark Mode'}
</Button>
</CardContent>
</Card>
);
}
Components
Core UI
- Button
- Card
- Dialog
- Form Inputs
import { Button } from '@stoneforge/ui';
<Button variant="primary" onClick={() => {}}>
Primary
</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="danger">Danger</Button>
<Button variant="outline">Outline</Button>
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
<Button disabled>Disabled</Button>
<Button loading>Loading...</Button>
import {
Card,
CardHeader,
CardTitle,
CardDescription,
CardContent,
CardFooter
} from '@stoneforge/ui';
<Card>
<CardHeader>
<CardTitle>Title</CardTitle>
<CardDescription>Optional description</CardDescription>
</CardHeader>
<CardContent>
<p>Card content goes here</p>
</CardContent>
<CardFooter>
<Button>Action</Button>
</CardFooter>
</Card>
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
DialogBody,
DialogFooter
} from '@stoneforge/ui';
const [open, setOpen] = useState(false);
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Confirm Action</DialogTitle>
<DialogDescription>
Are you sure you want to proceed?
</DialogDescription>
</DialogHeader>
<DialogBody>
<p>This action cannot be undone.</p>
</DialogBody>
<DialogFooter>
<Button variant="ghost" onClick={() => setOpen(false)}>
Cancel
</Button>
<Button variant="primary" onClick={handleConfirm}>
Confirm
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
import { Input, Textarea, Label, Select } from '@stoneforge/ui';
<div>
<Label htmlFor="title">Title</Label>
<Input
id="title"
placeholder="Enter title"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
</div>
<div>
<Label htmlFor="description">Description</Label>
<Textarea
id="description"
rows={4}
value={description}
onChange={(e) => setDescription(e.target.value)}
/>
</div>
<Select value={status} onValueChange={setStatus}>
<SelectTrigger>
<SelectValue placeholder="Select status" />
</SelectTrigger>
<SelectContent>
<SelectItem value="open">Open</SelectItem>
<SelectItem value="in_progress">In Progress</SelectItem>
<SelectItem value="closed">Closed</SelectItem>
</SelectContent>
</Select>
Layout
import { AppShell, Sidebar, Header } from '@stoneforge/ui/layout';
function App() {
return (
<AppShell>
<Sidebar />
<div className="main-content">
<Header title="Dashboard" />
<main>{children}</main>
</div>
</AppShell>
);
}
Domain Components
- TaskCard
- EntityCard
- Status Badges
- EntityLink
import { TaskCard } from '@stoneforge/ui/domain';
import type { Task } from '@stoneforge/core';
<TaskCard
task={task}
onClick={() => navigate(`/tasks/${task.id}`)}
onAssign={(taskId) => handleAssign(taskId)}
showAssignees
showPriority
showDependencies
/>
import { EntityCard } from '@stoneforge/ui/domain';
import type { Entity } from '@stoneforge/core';
<EntityCard
entity={entity}
onClick={() => navigate(`/entities/${entity.id}`)}
showRole
showEmail
/>
import {
TaskStatusBadge,
TaskPriorityBadge,
TaskTypeBadge,
MergeStatusBadge
} from '@stoneforge/ui/domain';
<TaskStatusBadge status="open" />
<TaskPriorityBadge priority={3} />
<TaskTypeBadge type="feature" />
<MergeStatusBadge status="pending" />
import { EntityLink } from '@stoneforge/ui/domain';
<EntityLink
entityId={entity.id}
entityName={entity.name}
showHoverCard
/>
// Shows entity name with hover preview
Skeleton Loading
import {
Skeleton,
SkeletonText,
SkeletonAvatar,
SkeletonCard
} from '@stoneforge/ui';
<Skeleton className="h-10 w-full" />
<SkeletonText lines={3} />
<SkeletonAvatar size="lg" />
<SkeletonCard />
Hooks
Theme & Responsive
import { useTheme } from '@stoneforge/ui/hooks';
const {
isDark,
isHighContrast,
theme, // 'light' | 'dark' | 'system'
toggleDarkMode,
setTheme,
setHighContrast
} = useTheme();
<Button onClick={toggleDarkMode}>
{isDark ? 'Light' : 'Dark'} Mode
</Button>
Real-time Communication
import { useWebSocket } from '@stoneforge/ui/hooks';
const {
connectionState, // 'connecting' | 'connected' | 'disconnected'
subscribe,
unsubscribe,
send,
close
} = useWebSocket({
url: 'ws://localhost:3456/ws',
channels: ['tasks', 'agents'],
reconnect: true,
reconnectInterval: 5000,
});
useEffect(() => {
const unsubscribe = subscribe('tasks', (event) => {
console.log('Task event:', event);
// Invalidate queries, update state, etc.
});
return unsubscribe;
}, [subscribe]);
Keyboard Shortcuts
import { useKeyboardShortcut } from '@stoneforge/ui/hooks';
useKeyboardShortcut('cmd+k', () => {
openCommandPalette();
});
useKeyboardShortcut('cmd+n', () => {
createNewTask();
});
useKeyboardShortcut('esc', () => {
closeModal();
});
API Clients
import { WebSocketClient } from '@stoneforge/ui/api';
const ws = new WebSocketClient({
url: 'ws://localhost:3456/ws',
reconnect: true,
heartbeatInterval: 30000,
});
ws.subscribe('tasks', (event) => {
console.log('Task event:', event);
});
ws.send({ type: 'subscribe', channels: ['agents'] });
ws.close();
Design Tokens
Import the CSS custom properties in your app entry point:@import '@stoneforge/ui/styles/tokens.css';
Color System
:root {
--color-background: #ffffff;
--color-foreground: #0a0a0a;
--color-primary: #2563eb;
--color-secondary: #64748b;
--color-success: #10b981;
--color-warning: #f59e0b;
--color-danger: #ef4444;
--color-border: #e5e7eb;
}
Spacing & Typography
--spacing-0: 0;
--spacing-1: 0.25rem; /* 4px */
--spacing-2: 0.5rem; /* 8px */
--spacing-3: 0.75rem; /* 12px */
--spacing-4: 1rem; /* 16px */
--spacing-6: 1.5rem; /* 24px */
--spacing-8: 2rem; /* 32px */
--spacing-12: 3rem; /* 48px */
Visualizations
import { LineChart } from '@stoneforge/ui/visualizations';
const data = [
{ date: '2026-01-01', value: 10 },
{ date: '2026-01-02', value: 15 },
{ date: '2026-01-03', value: 12 },
];
<LineChart
data={data}
xKey="date"
yKey="value"
title="Task Completion Rate"
/>
Entry Points
- Main
- Components
- Layout
- Domain
- Hooks
- API
- Visualizations
import { Button, Card, Dialog } from '@stoneforge/ui';
// All components, hooks, and utilities
import { Button, Card, Input } from '@stoneforge/ui/components';
import { AppShell, Sidebar, Header } from '@stoneforge/ui/layout';
import { TaskCard, EntityCard } from '@stoneforge/ui/domain';
import { useTheme, useWebSocket } from '@stoneforge/ui/hooks';
import { WebSocketClient, ApiClient } from '@stoneforge/ui/api';
import { LineChart, BarChart } from '@stoneforge/ui/visualizations';
React 19: This package uses React 19 features. Ensure your project is using React 19 or later.
Next Steps
Quarry API
Connect UI to the data layer
Smithy Orchestration
Build agent management interfaces