diff --git a/.claude/skills/origin-ui-builder/SKILL.md b/.claude/skills/origin-ui-builder/SKILL.md
new file mode 100644
index 0000000..6c86e82
--- /dev/null
+++ b/.claude/skills/origin-ui-builder/SKILL.md
@@ -0,0 +1,305 @@
+---
+name: "origin-ui-builder"
+description: "This skill should be used when the user wants to build UIs with Origin design system components, create forms with Field and Input, add buttons or interactive controls, use Origin components, style with design tokens, create layouts, implement form validation, build navigation menus, create data tables, use compound components like Select or Accordion, apply typography mixins, style with CSS Modules, or work with Base UI wrappers. Triggers include: \"add a button\", \"create a form\", \"build a table\", \"use Origin\", \"design system component\", \"spacing tokens\", \"style with tokens\", \"Field component\", \"Select component\", \"form validation\", \"navigation layout\", \"compound component\", \"CSS Modules\", \"typography mixin\"."
+---
+
+# Origin UI Builder
+
+## Overview
+
+Origin is a design system built on Base UI with a Figma-first approach. It provides consistent, accessible components with design tokens that map directly from Figma to code.
+
+**Key Principles:**
+- Components wrap Base UI for behavior and accessibility
+- Figma provides tokenized CSS that maps directly to code
+- No hardcoded values - use design tokens for all styling
+- Components use namespace exports for compound patterns
+
+## Quick Start
+
+### Basic Import Pattern
+
+```tsx
+import { Button, Field, Input, Select } from '@grid/origin';
+```
+
+### Simple Component Usage
+
+```tsx
+// Button with variants
+
+
+
+
+
+// Button with icons
+}>Add Item
+
+```
+
+### Compound Component Pattern
+
+Origin uses namespace exports for multi-part components:
+
+```tsx
+// Select with compound pattern
+
+
+
+
+
+
+
+
+
+ Option 1
+ Option 2
+
+
+
+
+
+```
+
+## Component Categories
+
+### Simple Components
+Direct wrappers with prop-based customization:
+- **Button** - Primary actions with variants, icons, loading states
+- **Input** - Text input fields
+- **Badge** - Status indicators with color variants
+- **Alert** - Info/critical messages
+- **Avatar** - User/entity images with fallbacks
+- **Loader** - Loading spinners
+- **Chip** - Dismissible tags
+- **Switch** - Toggle controls
+
+### Compound Components
+Multi-part components with namespace exports:
+- **Field** - Form field container (Root, Label, Description, Error)
+- **Select** - Dropdown selection
+- **Menu** - Action menus
+- **Tabs** - Tab navigation
+- **Accordion** - Collapsible sections
+- **Checkbox** - Checkbox groups
+- **Radio** - Radio button groups
+- **Table** - Data tables
+- **NavigationMenu** - Site navigation
+- **Tooltip** - Contextual hints
+- **AlertDialog** - Confirmation dialogs
+
+## Form Patterns
+
+### Field + Input Pattern
+
+Always wrap inputs with Field for proper labeling and error handling:
+
+```tsx
+
+ Email Address
+
+ We'll never share your email.
+ Please enter a valid email address.
+
+```
+
+### Validation States
+
+Use `data-invalid` attribute for error states:
+
+```tsx
+
+ Username
+
+ Username is required.
+
+```
+
+### Radio and Checkbox Groups
+
+```tsx
+// Radio group
+
+
+
+
+
+// Checkbox group
+
+
+
+
+```
+
+## Styling Guidelines
+
+### Token Usage
+
+Always use design tokens - never hardcode values:
+
+```scss
+// CORRECT
+.container {
+ padding: var(--spacing-md);
+ color: var(--text-primary);
+ background: var(--surface-primary);
+ border-radius: var(--corner-radius-md);
+}
+
+// INCORRECT
+.container {
+ padding: 16px;
+ color: #1A1A1A;
+ background: #F8F8F7;
+ border-radius: 8px;
+}
+```
+
+### Typography Mixins
+
+Use typography mixins for consistent text styles:
+
+```scss
+@use '@grid/origin/tokens' as *;
+
+.heading {
+ @include headline;
+}
+
+.bodyText {
+ @include body;
+}
+
+.buttonText {
+ @include btn;
+}
+
+.labelText {
+ @include label;
+}
+```
+
+### State Styling
+
+Use Base UI's data attributes for states:
+
+```scss
+.input {
+ border-color: var(--border-primary);
+
+ &:hover {
+ border-color: var(--border-hover);
+ }
+
+ &:focus-visible {
+ box-shadow: var(--input-focus);
+ }
+
+ &[data-disabled] {
+ background: var(--surface-disabled);
+ cursor: not-allowed;
+ }
+
+ &[data-invalid] {
+ border-color: var(--border-critical);
+
+ &:focus-visible {
+ box-shadow: var(--input-focus-critical);
+ }
+ }
+}
+```
+
+## Layout Patterns
+
+### Spacing with Tokens
+
+```tsx
+
+
+
+
+```
+
+### Common Layout Classes
+
+```scss
+.stack {
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-md);
+}
+
+.row {
+ display: flex;
+ align-items: center;
+ gap: var(--spacing-sm);
+}
+
+.grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: var(--spacing-lg);
+}
+```
+
+## Component Sizes
+
+Most components support size variants:
+
+| Component | Sizes |
+|-----------|-------|
+| Button | `default`, `compact` |
+| Avatar | `16`, `20`, `24`, `32`, `40`, `48` |
+| Switch | `sm`, `md` |
+| Chip | `sm`, `md` |
+| Loader | `sm`, `md`, `lg` |
+
+## Icons
+
+Use CentralIcon for all icons:
+
+```tsx
+import { CentralIcon } from '@grid/origin';
+
+
+
+
+```
+
+Icon names have TypeScript autocomplete via the `CentralIconName` type.
+
+## Accessibility
+
+Origin components inherit Base UI's accessibility features:
+- Proper ARIA attributes
+- Keyboard navigation
+- Focus management
+- Screen reader support
+
+Always include:
+- Labels for form inputs (via Field.Label)
+- Descriptive text for loaders (via `label` prop)
+- Clear focus indicators (built-in)
+
+## References
+
+For detailed documentation, see:
+- `references/components.md` - Complete component API reference
+- `references/tokens.md` - Design token system
+- `references/patterns.md` - Common UI patterns
+- `references/styling.md` - SCSS patterns and mixins
+- `examples/` - Working code examples
+
+## Verification
+
+After building a UI, verify it renders correctly:
+1. Run Storybook (`npm run storybook`) or dev server (`npm run dev`)
+2. Check visual rendering matches expected design
+3. Test responsive behavior at different viewport sizes
+4. Verify interactive states (hover, focus, disabled)
+5. Ensure accessibility with keyboard navigation
diff --git a/.claude/skills/origin-ui-builder/examples/data-table.module.scss b/.claude/skills/origin-ui-builder/examples/data-table.module.scss
new file mode 100644
index 0000000..bd572e9
--- /dev/null
+++ b/.claude/skills/origin-ui-builder/examples/data-table.module.scss
@@ -0,0 +1,86 @@
+@use '@grid/origin/tokens' as *;
+
+.container {
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-md);
+}
+
+.toolbar {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: var(--spacing-md);
+ flex-wrap: wrap;
+}
+
+.search {
+ flex: 1;
+ max-width: 320px;
+}
+
+.filters {
+ display: flex;
+ align-items: center;
+ gap: var(--spacing-sm);
+}
+
+.checkboxCell {
+ width: 48px;
+}
+
+.nameCell {
+ min-width: 160px;
+}
+
+.name {
+ @include label;
+ color: var(--text-primary);
+}
+
+.emailCell {
+ color: var(--text-secondary);
+}
+
+.actionsCell {
+ width: 80px;
+ text-align: right;
+}
+
+.sortButton {
+ display: inline-flex;
+ align-items: center;
+ gap: var(--spacing-2xs);
+ background: none;
+ border: none;
+ padding: 0;
+ font: inherit;
+ color: inherit;
+ cursor: pointer;
+
+ &:hover {
+ color: var(--text-primary);
+ }
+}
+
+.emptyState {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: var(--spacing-4xl);
+ color: var(--text-secondary);
+ text-align: center;
+}
+
+.pagination {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding-top: var(--spacing-md);
+ border-top: 1px solid var(--border-tertiary);
+}
+
+.paginationInfo {
+ @include body-sm;
+ color: var(--text-secondary);
+}
diff --git a/.claude/skills/origin-ui-builder/examples/data-table.tsx b/.claude/skills/origin-ui-builder/examples/data-table.tsx
new file mode 100644
index 0000000..4276cdd
--- /dev/null
+++ b/.claude/skills/origin-ui-builder/examples/data-table.tsx
@@ -0,0 +1,345 @@
+import React, { useState, useMemo } from 'react';
+import {
+ Table,
+ Button,
+ Badge,
+ Input,
+ Select,
+ Pagination,
+ Menu,
+ Checkbox,
+ CentralIcon,
+} from '@grid/origin';
+import styles from './data-table.module.scss';
+
+interface User {
+ id: string;
+ name: string;
+ email: string;
+ role: 'admin' | 'editor' | 'viewer';
+ status: 'active' | 'inactive' | 'pending';
+ createdAt: string;
+}
+
+const MOCK_USERS: User[] = [
+ {
+ id: '1',
+ name: 'Alice Johnson',
+ email: 'alice@example.com',
+ role: 'admin',
+ status: 'active',
+ createdAt: '2024-01-15',
+ },
+ {
+ id: '2',
+ name: 'Bob Smith',
+ email: 'bob@example.com',
+ role: 'editor',
+ status: 'active',
+ createdAt: '2024-02-20',
+ },
+ {
+ id: '3',
+ name: 'Carol Williams',
+ email: 'carol@example.com',
+ role: 'viewer',
+ status: 'inactive',
+ createdAt: '2024-03-10',
+ },
+ {
+ id: '4',
+ name: 'David Brown',
+ email: 'david@example.com',
+ role: 'editor',
+ status: 'pending',
+ createdAt: '2024-04-05',
+ },
+ {
+ id: '5',
+ name: 'Eva Martinez',
+ email: 'eva@example.com',
+ role: 'viewer',
+ status: 'active',
+ createdAt: '2024-05-12',
+ },
+];
+
+type SortField = 'name' | 'email' | 'role' | 'status' | 'createdAt';
+type SortDirection = 'asc' | 'desc';
+
+const STATUS_COLORS: Record = {
+ active: 'green',
+ inactive: 'gray',
+ pending: 'yellow',
+};
+
+const ROLE_LABELS: Record = {
+ admin: 'Administrator',
+ editor: 'Editor',
+ viewer: 'Viewer',
+};
+
+const PAGE_SIZE = 10;
+
+export function DataTable() {
+ const [users] = useState(MOCK_USERS);
+ const [searchQuery, setSearchQuery] = useState('');
+ const [statusFilter, setStatusFilter] = useState('all');
+ const [sortField, setSortField] = useState('name');
+ const [sortDirection, setSortDirection] = useState('asc');
+ const [currentPage, setCurrentPage] = useState(1);
+ const [selectedIds, setSelectedIds] = useState([]);
+
+ const filteredUsers = useMemo(() => {
+ return users.filter((user) => {
+ const matchesSearch =
+ user.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ user.email.toLowerCase().includes(searchQuery.toLowerCase());
+
+ const matchesStatus =
+ statusFilter === 'all' || user.status === statusFilter;
+
+ return matchesSearch && matchesStatus;
+ });
+ }, [users, searchQuery, statusFilter]);
+
+ const sortedUsers = useMemo(() => {
+ return [...filteredUsers].sort((a, b) => {
+ const aValue = a[sortField];
+ const bValue = b[sortField];
+
+ if (aValue < bValue) return sortDirection === 'asc' ? -1 : 1;
+ if (aValue > bValue) return sortDirection === 'asc' ? 1 : -1;
+ return 0;
+ });
+ }, [filteredUsers, sortField, sortDirection]);
+
+ const paginatedUsers = useMemo(() => {
+ const start = (currentPage - 1) * PAGE_SIZE;
+ return sortedUsers.slice(start, start + PAGE_SIZE);
+ }, [sortedUsers, currentPage]);
+
+ const totalPages = Math.ceil(sortedUsers.length / PAGE_SIZE);
+
+ const handleSort = (field: SortField) => {
+ if (sortField === field) {
+ setSortDirection((prev) => (prev === 'asc' ? 'desc' : 'asc'));
+ } else {
+ setSortField(field);
+ setSortDirection('asc');
+ }
+ };
+
+ const handleSelectAll = (checked: boolean) => {
+ if (checked) {
+ setSelectedIds(paginatedUsers.map((u) => u.id));
+ } else {
+ setSelectedIds([]);
+ }
+ };
+
+ const handleSelectOne = (id: string, checked: boolean) => {
+ if (checked) {
+ setSelectedIds((prev) => [...prev, id]);
+ } else {
+ setSelectedIds((prev) => prev.filter((i) => i !== id));
+ }
+ };
+
+ const allSelected =
+ paginatedUsers.length > 0 &&
+ paginatedUsers.every((u) => selectedIds.includes(u.id));
+
+ const someSelected =
+ paginatedUsers.some((u) => selectedIds.includes(u.id)) && !allSelected;
+
+ const SortIcon = ({ field }: { field: SortField }) => {
+ if (sortField !== field) return null;
+ return (
+
+ );
+ };
+
+ return (
+
+
+
+ setSearchQuery(e.target.value)}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+ All Statuses
+ Active
+ Inactive
+ Pending
+
+
+
+
+
+
+ {selectedIds.length > 0 && (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Actions
+
+
+
+
+ {paginatedUsers.map((user) => (
+
+
+
+ handleSelectOne(user.id, checked === true)
+ }
+ />
+
+
+ {user.name}
+
+ {user.email}
+ {ROLE_LABELS[user.role]}
+
+
+ {user.status.charAt(0).toUpperCase() + user.status.slice(1)}
+
+
+
+ {new Date(user.createdAt).toLocaleDateString()}
+
+
+
+
+
+
+
+
+
+ {}}>
+
+ Edit
+
+ {}}>
+
+ Duplicate
+
+
+ {}}>
+
+ Delete
+
+
+
+
+
+
+
+ ))}
+
+
+
+ {paginatedUsers.length === 0 && (
+
+
No users found matching your criteria.
+
+ )}
+
+ {totalPages > 1 && (
+
+
+ Showing {(currentPage - 1) * PAGE_SIZE + 1} to{' '}
+ {Math.min(currentPage * PAGE_SIZE, sortedUsers.length)} of{' '}
+ {sortedUsers.length} users
+
+
+
+
+
+
+
+ )}
+
+ );
+}
+
+export default DataTable;
diff --git a/.claude/skills/origin-ui-builder/examples/form-with-validation.module.scss b/.claude/skills/origin-ui-builder/examples/form-with-validation.module.scss
new file mode 100644
index 0000000..d33e440
--- /dev/null
+++ b/.claude/skills/origin-ui-builder/examples/form-with-validation.module.scss
@@ -0,0 +1,68 @@
+@use '@grid/origin/tokens' as *;
+
+.form {
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-xl);
+ max-width: var(--max-width-lg);
+}
+
+.row {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: var(--spacing-md);
+
+ @media (max-width: 640px) {
+ grid-template-columns: 1fr;
+ }
+}
+
+.terms {
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-xs);
+}
+
+.termsError {
+ @include body-sm;
+ color: var(--text-critical);
+ margin-left: var(--spacing-lg);
+}
+
+.actions {
+ display: flex;
+ justify-content: flex-end;
+ gap: var(--spacing-sm);
+ padding-top: var(--spacing-md);
+ border-top: 1px solid var(--border-tertiary);
+
+ @media (max-width: 640px) {
+ flex-direction: column-reverse;
+
+ button {
+ width: 100%;
+ }
+ }
+}
+
+.successMessage {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: var(--spacing-md);
+ padding: var(--spacing-4xl);
+ text-align: center;
+
+ h2 {
+ @include headline;
+ color: var(--text-primary);
+ margin: 0;
+ }
+
+ p {
+ @include body;
+ color: var(--text-secondary);
+ margin: 0;
+ }
+}
diff --git a/.claude/skills/origin-ui-builder/examples/form-with-validation.tsx b/.claude/skills/origin-ui-builder/examples/form-with-validation.tsx
new file mode 100644
index 0000000..5b0fd62
--- /dev/null
+++ b/.claude/skills/origin-ui-builder/examples/form-with-validation.tsx
@@ -0,0 +1,288 @@
+import React, { useState } from 'react';
+import {
+ Button,
+ Field,
+ Fieldset,
+ Input,
+ Select,
+ Checkbox,
+ Radio,
+} from '@grid/origin';
+import styles from './form-with-validation.module.scss';
+
+interface FormData {
+ firstName: string;
+ lastName: string;
+ email: string;
+ phone: string;
+ country: string;
+ plan: string;
+ notifications: string[];
+ agreeToTerms: boolean;
+}
+
+interface FormErrors {
+ firstName?: string;
+ lastName?: string;
+ email?: string;
+ phone?: string;
+ country?: string;
+ plan?: string;
+ agreeToTerms?: string;
+}
+
+const COUNTRIES = [
+ { value: 'us', label: 'United States' },
+ { value: 'uk', label: 'United Kingdom' },
+ { value: 'ca', label: 'Canada' },
+ { value: 'au', label: 'Australia' },
+ { value: 'de', label: 'Germany' },
+];
+
+const PLANS = [
+ { value: 'basic', label: 'Basic', description: '$9/month - For individuals' },
+ { value: 'pro', label: 'Pro', description: '$29/month - For small teams' },
+ { value: 'enterprise', label: 'Enterprise', description: 'Custom pricing' },
+];
+
+const NOTIFICATION_OPTIONS = [
+ { value: 'email', label: 'Email notifications' },
+ { value: 'sms', label: 'SMS notifications' },
+ { value: 'push', label: 'Push notifications' },
+];
+
+function validateEmail(email: string): string | undefined {
+ if (!email) return 'Email is required';
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
+ return 'Please enter a valid email address';
+ }
+}
+
+function validateRequired(value: string, fieldName: string): string | undefined {
+ if (!value || !value.trim()) {
+ return `${fieldName} is required`;
+ }
+}
+
+export function FormWithValidation() {
+ const [formData, setFormData] = useState({
+ firstName: '',
+ lastName: '',
+ email: '',
+ phone: '',
+ country: '',
+ plan: 'basic',
+ notifications: ['email'],
+ agreeToTerms: false,
+ });
+
+ const [errors, setErrors] = useState({});
+ const [isSubmitting, setIsSubmitting] = useState(false);
+ const [isSubmitted, setIsSubmitted] = useState(false);
+
+ const updateField = (
+ field: K,
+ value: FormData[K]
+ ) => {
+ setFormData((prev) => ({ ...prev, [field]: value }));
+ if (errors[field as keyof FormErrors]) {
+ setErrors((prev) => ({ ...prev, [field]: undefined }));
+ }
+ };
+
+ const validateForm = (): boolean => {
+ const newErrors: FormErrors = {};
+
+ newErrors.firstName = validateRequired(formData.firstName, 'First name');
+ newErrors.lastName = validateRequired(formData.lastName, 'Last name');
+ newErrors.email = validateEmail(formData.email);
+ newErrors.country = validateRequired(formData.country, 'Country');
+
+ if (!formData.agreeToTerms) {
+ newErrors.agreeToTerms = 'You must agree to the terms';
+ }
+
+ const filteredErrors = Object.fromEntries(
+ Object.entries(newErrors).filter(([, v]) => v !== undefined)
+ ) as FormErrors;
+
+ setErrors(filteredErrors);
+ return Object.keys(filteredErrors).length === 0;
+ };
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+
+ if (!validateForm()) return;
+
+ setIsSubmitting(true);
+
+ await new Promise((resolve) => setTimeout(resolve, 1500));
+
+ setIsSubmitting(false);
+ setIsSubmitted(true);
+ };
+
+ if (isSubmitted) {
+ return (
+
+
Registration Complete
+
Thank you for registering, {formData.firstName}!
+
+
+ );
+ }
+
+ return (
+
+ );
+}
+
+export default FormWithValidation;
diff --git a/.claude/skills/origin-ui-builder/examples/navigation-layout.module.scss b/.claude/skills/origin-ui-builder/examples/navigation-layout.module.scss
new file mode 100644
index 0000000..569f333
--- /dev/null
+++ b/.claude/skills/origin-ui-builder/examples/navigation-layout.module.scss
@@ -0,0 +1,170 @@
+@use '@grid/origin/tokens' as *;
+
+.layout {
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ background: var(--surface-base);
+}
+
+.header {
+ position: sticky;
+ top: 0;
+ z-index: 100;
+ background: var(--surface-base);
+ border-bottom: 1px solid var(--border-tertiary);
+}
+
+.headerContent {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: var(--spacing-sm) var(--spacing-xl);
+ max-width: var(--max-width-7xl);
+ margin: 0 auto;
+ width: 100%;
+}
+
+.headerLeft {
+ display: flex;
+ align-items: center;
+ gap: var(--spacing-xl);
+}
+
+.headerRight {
+ display: flex;
+ align-items: center;
+ gap: var(--spacing-xs);
+}
+
+.avatarButton {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: none;
+ border: none;
+ padding: var(--spacing-2xs);
+ border-radius: var(--corner-radius-round);
+ cursor: pointer;
+ transition: background-color 150ms ease;
+
+ &:hover {
+ background: var(--surface-hover);
+ }
+}
+
+.popupItemLabel {
+ @include label;
+ display: block;
+ color: var(--text-primary);
+}
+
+.popupItemDescription {
+ @include body-sm;
+ display: block;
+ color: var(--text-secondary);
+ margin-top: var(--spacing-4xs);
+}
+
+.subHeader {
+ padding: var(--spacing-sm) var(--spacing-xl);
+ border-bottom: 1px solid var(--border-tertiary);
+ background: var(--surface-primary);
+}
+
+.tabsContainer {
+ padding: 0 var(--spacing-xl);
+ border-bottom: 1px solid var(--border-tertiary);
+ background: var(--surface-base);
+}
+
+.main {
+ flex: 1;
+ padding: var(--spacing-xl);
+ max-width: var(--max-width-7xl);
+ margin: 0 auto;
+ width: 100%;
+}
+
+.pageHeader {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: var(--spacing-lg);
+ margin-bottom: var(--spacing-xl);
+
+ @media (max-width: 640px) {
+ flex-direction: column;
+ }
+}
+
+.pageTitle {
+ @include headline;
+ color: var(--text-primary);
+ margin: 0 0 var(--spacing-2xs);
+}
+
+.pageDescription {
+ @include body;
+ color: var(--text-secondary);
+ margin: 0;
+}
+
+.pageActions {
+ display: flex;
+ align-items: center;
+ gap: var(--spacing-sm);
+ flex-shrink: 0;
+}
+
+.content {
+ background: var(--surface-elevated);
+ border: 1px solid var(--border-tertiary);
+ border-radius: var(--corner-radius-lg);
+ padding: var(--spacing-xl);
+ min-height: 400px;
+}
+
+.footer {
+ border-top: 1px solid var(--border-tertiary);
+ background: var(--surface-primary);
+}
+
+.footerContent {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: var(--spacing-lg) var(--spacing-xl);
+ max-width: var(--max-width-7xl);
+ margin: 0 auto;
+ width: 100%;
+
+ @media (max-width: 640px) {
+ flex-direction: column;
+ gap: var(--spacing-md);
+ text-align: center;
+ }
+}
+
+.footerText {
+ @include body-sm;
+ color: var(--text-secondary);
+ margin: 0;
+}
+
+.footerLinks {
+ display: flex;
+ align-items: center;
+ gap: var(--spacing-lg);
+
+ a {
+ @include body-sm;
+ color: var(--text-secondary);
+ text-decoration: none;
+ transition: color 150ms ease;
+
+ &:hover {
+ color: var(--text-primary);
+ }
+ }
+}
diff --git a/.claude/skills/origin-ui-builder/examples/navigation-layout.tsx b/.claude/skills/origin-ui-builder/examples/navigation-layout.tsx
new file mode 100644
index 0000000..2e0fd0d
--- /dev/null
+++ b/.claude/skills/origin-ui-builder/examples/navigation-layout.tsx
@@ -0,0 +1,314 @@
+import React, { useState } from 'react';
+import {
+ NavigationMenu,
+ Tabs,
+ Button,
+ Logo,
+ Avatar,
+ Menu,
+ Breadcrumb,
+ CentralIcon,
+} from '@grid/origin';
+import styles from './navigation-layout.module.scss';
+
+interface NavItem {
+ label: string;
+ href?: string;
+ children?: {
+ label: string;
+ href: string;
+ description?: string;
+ }[];
+}
+
+const NAV_ITEMS: NavItem[] = [
+ {
+ label: 'Products',
+ children: [
+ {
+ label: 'Analytics',
+ href: '/products/analytics',
+ description: 'Track and analyze your data',
+ },
+ {
+ label: 'Automation',
+ href: '/products/automation',
+ description: 'Automate your workflows',
+ },
+ {
+ label: 'Integrations',
+ href: '/products/integrations',
+ description: 'Connect with your tools',
+ },
+ ],
+ },
+ {
+ label: 'Solutions',
+ children: [
+ {
+ label: 'For Startups',
+ href: '/solutions/startups',
+ description: 'Scale your business faster',
+ },
+ {
+ label: 'For Enterprise',
+ href: '/solutions/enterprise',
+ description: 'Enterprise-grade solutions',
+ },
+ {
+ label: 'For Teams',
+ href: '/solutions/teams',
+ description: 'Collaborate effectively',
+ },
+ ],
+ },
+ {
+ label: 'Pricing',
+ href: '/pricing',
+ },
+ {
+ label: 'Resources',
+ children: [
+ {
+ label: 'Documentation',
+ href: '/docs',
+ description: 'Guides and API reference',
+ },
+ {
+ label: 'Blog',
+ href: '/blog',
+ description: 'Latest news and updates',
+ },
+ {
+ label: 'Community',
+ href: '/community',
+ description: 'Join our community',
+ },
+ ],
+ },
+];
+
+const TABS = [
+ { value: 'overview', label: 'Overview' },
+ { value: 'analytics', label: 'Analytics' },
+ { value: 'reports', label: 'Reports' },
+ { value: 'settings', label: 'Settings' },
+];
+
+const BREADCRUMB_ITEMS = [
+ { label: 'Home', href: '/' },
+ { label: 'Dashboard', href: '/dashboard' },
+ { label: 'Analytics', href: '/dashboard/analytics' },
+];
+
+export function NavigationLayout() {
+ const [activeTab, setActiveTab] = useState('overview');
+
+ return (
+
+
+
+
+
+
+ {BREADCRUMB_ITEMS.map((item, index) => (
+
+ {index > 0 && }
+
+ {item.label}
+
+
+ ))}
+
+
+
+
+
+
+
+ {TABS.map((tab) => (
+
+ {tab.label}
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
Overview
+
+ Welcome to your dashboard overview.
+
+
+
+
+ }>
+ New Report
+
+
+
+
+
Overview content goes here...
+
+
+
+
+
+
+
Analytics
+
+ View detailed analytics and metrics.
+
+
+
+
+
Analytics content goes here...
+
+
+
+
+
+
+
Reports
+
+ Generate and view reports.
+
+
+
+
+
Reports content goes here...
+
+
+
+
+
+
+
Settings
+
+ Configure your dashboard settings.
+
+
+
+
+
Settings content goes here...
+
+
+
+
+
+
+
+ );
+}
+
+export default NavigationLayout;
diff --git a/.claude/skills/origin-ui-builder/references/components.md b/.claude/skills/origin-ui-builder/references/components.md
new file mode 100644
index 0000000..64004fa
--- /dev/null
+++ b/.claude/skills/origin-ui-builder/references/components.md
@@ -0,0 +1,713 @@
+# Origin Components Reference
+
+Complete API reference for all Origin design system components.
+
+## Simple Components
+
+### Button
+
+Primary action component with multiple variants.
+
+```tsx
+import { Button } from '@grid/origin';
+```
+
+| Prop | Type | Default | Description |
+|------|------|---------|-------------|
+| `variant` | `'filled' \| 'outline' \| 'ghost' \| 'critical' \| 'link'` | `'filled'` | Visual style |
+| `size` | `'default' \| 'compact'` | `'default'` | Button size |
+| `loading` | `boolean` | `false` | Shows spinner, disables button |
+| `leadingIcon` | `ReactNode` | - | Icon before text |
+| `trailingIcon` | `ReactNode` | - | Icon after text |
+| `iconOnly` | `boolean` | `false` | For icon-only buttons |
+| `disabled` | `boolean` | `false` | Disabled state |
+
+```tsx
+
+
+
+
+
+
+
+}>Add
+
+```
+
+### Input
+
+Text input field wrapping Base UI Input.
+
+```tsx
+import { Input } from '@grid/origin';
+```
+
+Standard HTML input attributes plus Base UI enhancements. Use with Field for labels/errors.
+
+```tsx
+
+
+
+
+
+```
+
+### Badge
+
+Compact status/category labels.
+
+```tsx
+import { Badge } from '@grid/origin';
+```
+
+| Prop | Type | Default | Description |
+|------|------|---------|-------------|
+| `variant` | `'gray' \| 'purple' \| 'blue' \| 'sky' \| 'pink' \| 'green' \| 'yellow' \| 'red'` | `'gray'` | Color variant |
+| `vibrant` | `boolean` | `false` | High contrast version |
+
+```tsx
+Default
+Success
+Error
+New
+```
+
+### Alert
+
+Info or critical message display.
+
+```tsx
+import { Alert } from '@grid/origin';
+```
+
+| Prop | Type | Default | Description |
+|------|------|---------|-------------|
+| `variant` | `'default' \| 'critical'` | `'default'` | Alert type |
+| `title` | `string` | **required** | Alert heading |
+| `description` | `string` | - | Additional text |
+| `icon` | `boolean \| ReactNode` | `true` | Show/custom icon |
+
+```tsx
+
+
+} />
+```
+
+### Loader
+
+Loading spinner with 3x3 dot grid animation.
+
+```tsx
+import { Loader } from '@grid/origin';
+```
+
+| Prop | Type | Default | Description |
+|------|------|---------|-------------|
+| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Spinner size |
+| `label` | `string` | `'Loading'` | Accessibility label |
+
+```tsx
+
+
+
+```
+
+### Chip
+
+Dismissible tag/filter component.
+
+```tsx
+import { Chip } from '@grid/origin';
+```
+
+| Prop | Type | Default | Description |
+|------|------|---------|-------------|
+| `variant` | `'default' \| 'filter'` | `'default'` | Visual style |
+| `size` | `'sm' \| 'md'` | `'md'` | Chip size |
+| `onDismiss` | `(details: EventDetails) => void` | - | Dismiss callback |
+| `dismissIcon` | `ReactNode` | - | Custom dismiss icon |
+
+```tsx
+Tag
+ {}}>Removable
+Filter
+```
+
+### Switch
+
+Toggle switch control.
+
+```tsx
+import { Switch } from '@grid/origin';
+```
+
+| Prop | Type | Default | Description |
+|------|------|---------|-------------|
+| `size` | `'sm' \| 'md'` | `'md'` | Switch size |
+| `checked` | `boolean` | - | Controlled state |
+| `defaultChecked` | `boolean` | - | Uncontrolled default |
+| `onCheckedChange` | `(checked: boolean) => void` | - | Change handler |
+| `disabled` | `boolean` | `false` | Disabled state |
+
+```tsx
+
+
+
+
+```
+
+### CentralIcon
+
+Icon component with 100+ Figma icons.
+
+```tsx
+import { CentralIcon } from '@grid/origin';
+```
+
+| Prop | Type | Default | Description |
+|------|------|---------|-------------|
+| `name` | `CentralIconName` | **required** | Icon name (autocomplete) |
+| `size` | `number` | `20` | Icon size in px |
+| `color` | `string` | `'currentColor'` | Icon color |
+
+```tsx
+
+
+
+```
+
+Icon names are available via TypeScript autocomplete using the `CentralIconName` type.
+
+### Logo
+
+Lightspark logo component.
+
+```tsx
+import { Logo } from '@grid/origin';
+```
+
+| Prop | Type | Default | Description |
+|------|------|---------|-------------|
+| `variant` | `'logo' \| 'logomark' \| 'wordmark'` | `'logo'` | Logo type |
+| `weight` | `'regular' \| 'light'` | `'regular'` | Font weight |
+| `height` | `number` | `24` | Height in px |
+| `color` | `string` | `'currentColor'` | Logo color |
+
+---
+
+## Compound Components
+
+### Avatar
+
+User/entity image with fallback.
+
+```tsx
+import { Avatar } from '@grid/origin';
+```
+
+**Parts:** `Avatar.Root`, `Avatar.Image`, `Avatar.Fallback`
+
+| Prop (Root) | Type | Default | Description |
+|-------------|------|---------|-------------|
+| `size` | `'16' \| '20' \| '24' \| '32' \| '40' \| '48'` | `'32'` | Avatar size |
+| `variant` | `'squircle' \| 'circle'` | `'squircle'` | Shape |
+| `color` | `'blue' \| 'purple' \| 'sky' \| 'pink' \| 'green' \| 'yellow' \| 'red' \| 'gray'` | - | Fallback color |
+
+```tsx
+
+
+ JD
+
+
+
+ AB
+
+```
+
+### Field
+
+Form field wrapper with label, description, and error.
+
+```tsx
+import { Field } from '@grid/origin';
+```
+
+**Parts:** `Field.Root`, `Field.Label`, `Field.Description`, `Field.Error`
+
+```tsx
+
+ Email
+
+ We'll never share your email.
+
+
+
+ Username
+
+ Username is required.
+
+```
+
+### Fieldset
+
+Grouping wrapper for related form fields.
+
+```tsx
+import { Fieldset } from '@grid/origin';
+```
+
+**Parts:** `Fieldset.Root`, `Fieldset.Legend`, `Fieldset.Description`, `Fieldset.Error`
+
+```tsx
+
+ Contact Information
+ Enter your contact details below.
+ {/* Field components */}
+
+```
+
+### Select
+
+Dropdown selection component.
+
+```tsx
+import { Select } from '@grid/origin';
+```
+
+**Parts:** `Select.Root`, `Select.Trigger`, `Select.Value`, `Select.Icon`, `Select.Portal`, `Select.Positioner`, `Select.Popup`, `Select.List`, `Select.Item`, `Select.Group`, `Select.GroupLabel`, `Select.Separator`
+
+| Prop (Trigger) | Type | Default | Description |
+|----------------|------|---------|-------------|
+| `variant` | `'default' \| 'ghost'` | `'default'` | Trigger style |
+
+| Prop (Value) | Type | Default | Description |
+|--------------|------|---------|-------------|
+| `placeholder` | `string` | - | Placeholder text |
+
+```tsx
+
+
+
+
+
+
+
+
+
+ Option 1
+ Option 2
+
+
+ Group
+ Option 3
+
+
+
+
+
+
+```
+
+### Checkbox
+
+Checkbox with group support.
+
+```tsx
+import { Checkbox } from '@grid/origin';
+```
+
+**Parts:** `Checkbox.Group`, `Checkbox.Item`, `Checkbox.Field`, `Checkbox.Legend`, `Checkbox.Description`, `Checkbox.Error`, `Checkbox.Indicator`
+
+| Prop (Group) | Type | Default | Description |
+|--------------|------|---------|-------------|
+| `value` | `string[]` | - | Selected values (controlled) |
+| `defaultValue` | `string[]` | `[]` | Default selected (uncontrolled) |
+| `onValueChange` | `(value: string[]) => void` | - | Change handler |
+| `variant` | `'default' \| 'card'` | `'default'` | Visual style |
+
+| Prop (Item) | Type | Default | Description |
+|-------------|------|---------|-------------|
+| `value` | `string` | **required** | Item value |
+| `checked` | `boolean` | - | Controlled state |
+| `indeterminate` | `boolean` | `false` | Indeterminate state |
+| `label` | `ReactNode` | - | Item label |
+| `description` | `ReactNode` | - | Item description |
+| `disabled` | `boolean` | `false` | Disabled state |
+
+```tsx
+
+
+
+
+
+
+// Single checkbox
+
+```
+
+### Radio
+
+Radio button group.
+
+```tsx
+import { Radio } from '@grid/origin';
+```
+
+**Parts:** `Radio.Group`, `Radio.Item`, `Radio.Field`, `Radio.Legend`, `Radio.Description`, `Radio.Error`
+
+| Prop (Group) | Type | Default | Description |
+|--------------|------|---------|-------------|
+| `value` | `string` | - | Selected value (controlled) |
+| `defaultValue` | `string` | - | Default selected (uncontrolled) |
+| `onValueChange` | `(value: string) => void` | - | Change handler |
+| `variant` | `'default' \| 'card'` | `'default'` | Visual style |
+
+| Prop (Item) | Type | Default | Description |
+|-------------|------|---------|-------------|
+| `value` | `string` | **required** | Item value |
+| `label` | `ReactNode` | - | Item label |
+| `description` | `ReactNode` | - | Item description |
+| `disabled` | `boolean` | `false` | Disabled state |
+
+```tsx
+
+
+
+
+
+```
+
+### Tabs
+
+Tabbed navigation component.
+
+```tsx
+import { Tabs } from '@grid/origin';
+```
+
+**Parts:** `Tabs.Root`, `Tabs.List`, `Tabs.Tab`, `Tabs.Panel`, `Tabs.Indicator`
+
+| Prop (List) | Type | Default | Description |
+|-------------|------|---------|-------------|
+| `variant` | `'default' \| 'minimal'` | `'default'` | Tab style |
+
+```tsx
+
+
+ Tab 1
+ Tab 2
+ Tab 3
+
+
+ Content 1
+ Content 2
+ Content 3
+
+```
+
+### Accordion
+
+Collapsible content sections.
+
+```tsx
+import { Accordion } from '@grid/origin';
+```
+
+**Parts:** `Accordion.Root`, `Accordion.Item`, `Accordion.Header`, `Accordion.Trigger`, `Accordion.Panel`
+
+| Prop (Root) | Type | Default | Description |
+|-------------|------|---------|-------------|
+| `type` | `'single' \| 'multiple'` | `'single'` | Expansion mode |
+| `value` | `string \| string[]` | - | Controlled expanded items |
+| `defaultValue` | `string \| string[]` | - | Default expanded |
+| `onValueChange` | `(value) => void` | - | Change handler |
+| `collapsible` | `boolean` | `true` | Allow all closed (single mode) |
+
+```tsx
+
+
+
+ Section 1
+
+ Content 1
+
+
+
+ Section 2
+
+ Content 2
+
+
+```
+
+### Menu
+
+Dropdown action menu.
+
+```tsx
+import { Menu } from '@grid/origin';
+```
+
+**Parts:** `Menu.Root`, `Menu.Trigger`, `Menu.Portal`, `Menu.Positioner`, `Menu.Popup`, `Menu.Item`, `Menu.Separator`, `Menu.Group`, `Menu.GroupLabel`, `Menu.Submenu`, `Menu.SubmenuTrigger`
+
+```tsx
+
+
+
+
+
+
+
+ {}}>Edit
+ {}}>Duplicate
+
+ {}} disabled>Delete
+
+
+
+
+```
+
+### Tooltip
+
+Contextual hint on hover/focus.
+
+```tsx
+import { Tooltip } from '@grid/origin';
+```
+
+**Parts:** `Tooltip.Provider`, `Tooltip.Root`, `Tooltip.Trigger`, `Tooltip.Portal`, `Tooltip.Positioner`, `Tooltip.Popup`, `Tooltip.Arrow`
+
+```tsx
+
+
+
+
+
+
+
+
+
+ Helpful tooltip text
+
+
+
+
+
+```
+
+### AlertDialog
+
+Confirmation/alert dialog.
+
+```tsx
+import { AlertDialog } from '@grid/origin';
+```
+
+**Parts:** `AlertDialog.Root`, `AlertDialog.Trigger`, `AlertDialog.Portal`, `AlertDialog.Backdrop`, `AlertDialog.Popup`, `AlertDialog.Title`, `AlertDialog.Description`, `AlertDialog.Close`, `AlertDialog.Actions`
+
+```tsx
+
+
+
+
+
+
+
+ Confirm Deletion
+
+ Are you sure you want to delete this item? This action cannot be undone.
+
+
+
+
+
+
+
+
+
+
+```
+
+### Table
+
+Data table component.
+
+```tsx
+import { Table } from '@grid/origin';
+```
+
+**Parts:** `Table.Root`, `Table.Header`, `Table.HeaderCell`, `Table.Body`, `Table.Row`, `Table.Cell`
+
+```tsx
+
+
+
+ Name
+ Email
+ Status
+
+
+
+
+ John Doe
+ john@example.com
+ Active
+
+
+
+```
+
+### NavigationMenu
+
+Site navigation component.
+
+```tsx
+import { NavigationMenu } from '@grid/origin';
+```
+
+**Parts:** `NavigationMenu.Root`, `NavigationMenu.List`, `NavigationMenu.Item`, `NavigationMenu.Trigger`, `NavigationMenu.Icon`, `NavigationMenu.Link`, `NavigationMenu.Content`, `NavigationMenu.Portal`, `NavigationMenu.Positioner`, `NavigationMenu.Popup`, `NavigationMenu.Viewport`, `NavigationMenu.Arrow`, `NavigationMenu.Backdrop`, `NavigationMenu.PopupItem`, `NavigationMenu.SubmenuTrigger`, `NavigationMenu.Group`, `NavigationMenu.GroupLabel`, `NavigationMenu.Separator`
+
+```tsx
+
+
+
+
+ Products
+
+
+
+
+
+ Product 1
+ Product 2
+
+
+
+
+
+ About
+
+
+
+```
+
+### Breadcrumb
+
+Breadcrumb navigation.
+
+```tsx
+import { Breadcrumb } from '@grid/origin';
+```
+
+**Parts:** `Breadcrumb.Root`, `Breadcrumb.List`, `Breadcrumb.Item`, `Breadcrumb.Link`, `Breadcrumb.Separator`
+
+```tsx
+
+
+
+ Home
+
+
+
+ Products
+
+
+
+ Current Item
+
+
+
+```
+
+### Combobox
+
+Searchable dropdown (autocomplete).
+
+```tsx
+import { Combobox } from '@grid/origin';
+```
+
+**Parts:** `Combobox.Root`, `Combobox.Control`, `Combobox.Input`, `Combobox.Trigger`, `Combobox.Portal`, `Combobox.Positioner`, `Combobox.Popup`, `Combobox.List`, `Combobox.Item`, `Combobox.Group`, `Combobox.GroupLabel`, `Combobox.Separator`
+
+```tsx
+
+
+
+
+
+
+
+
+
+ {filteredItems.map(item => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+
+```
+
+### Pagination
+
+Page navigation component.
+
+```tsx
+import { Pagination } from '@grid/origin';
+```
+
+**Parts:** `Pagination.Root`, `Pagination.Link`, `Pagination.Next`, `Pagination.Prev`, `Pagination.Pages`
+
+```tsx
+
+
+
+
+
+```
+
+### Toast
+
+Toast notification component.
+
+```tsx
+import { Toast } from '@grid/origin';
+```
+
+**Parts:** `Toast.Root`, `Toast.Title`, `Toast.Description`, `Toast.Close`
+
+### Progress
+
+Progress bar component.
+
+```tsx
+import { Progress } from '@grid/origin';
+```
+
+**Parts:** `Progress.Root`, `Progress.Indicator`
+
+```tsx
+
+
+
+```
+
+### Meter
+
+Gauge/meter display.
+
+```tsx
+import { Meter } from '@grid/origin';
+```
+
+**Parts:** `Meter.Root`, `Meter.Indicator`
+
+```tsx
+
+
+
+```
diff --git a/.claude/skills/origin-ui-builder/references/patterns.md b/.claude/skills/origin-ui-builder/references/patterns.md
new file mode 100644
index 0000000..afa2c9d
--- /dev/null
+++ b/.claude/skills/origin-ui-builder/references/patterns.md
@@ -0,0 +1,695 @@
+# Origin UI Patterns Reference
+
+Common patterns and best practices for building UIs with Origin components.
+
+## Form Patterns
+
+### Basic Form Field
+
+Always wrap inputs with Field for proper labeling and accessibility:
+
+```tsx
+import { Field, Input, Button } from '@grid/origin';
+
+function BasicForm() {
+ return (
+
+ );
+}
+```
+
+### Form with Validation
+
+```tsx
+import { Field, Input, Button } from '@grid/origin';
+import { useState } from 'react';
+
+function ValidatedForm() {
+ const [email, setEmail] = useState('');
+ const [errors, setErrors] = useState>({});
+
+ const validateEmail = (value: string) => {
+ if (!value) return 'Email is required';
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
+ return 'Please enter a valid email';
+ }
+ return '';
+ };
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ const emailError = validateEmail(email);
+
+ if (emailError) {
+ setErrors({ email: emailError });
+ return;
+ }
+
+ // Submit form
+ };
+
+ return (
+
+ );
+}
+```
+
+### Form with Multiple Fields
+
+```tsx
+import { Field, Fieldset, Input, Select, Checkbox, Button } from '@grid/origin';
+
+function CompleteForm() {
+ return (
+
+ );
+}
+```
+
+### Radio Group Form
+
+```tsx
+import { Radio, Field, Button } from '@grid/origin';
+
+function PlanSelector() {
+ const [plan, setPlan] = useState('basic');
+
+ return (
+
+ );
+}
+```
+
+## Layout Patterns
+
+### Stack Layout (Vertical)
+
+```tsx
+// Using inline styles
+
+
+
+
+
+```
+
+```scss
+// Using CSS module
+.stack {
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-md);
+}
+
+.stackCompact {
+ gap: var(--spacing-sm);
+}
+
+.stackLoose {
+ gap: var(--spacing-xl);
+}
+```
+
+### Row Layout (Horizontal)
+
+```scss
+.row {
+ display: flex;
+ align-items: center;
+ gap: var(--spacing-sm);
+}
+
+.rowSpaceBetween {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.rowEnd {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ gap: var(--spacing-sm);
+}
+```
+
+### Card Layout
+
+```tsx
+import styles from './Card.module.scss';
+
+function Card({ title, children }) {
+ return (
+
+
+
{title}
+
+
+ {children}
+
+
+ );
+}
+```
+
+```scss
+.card {
+ background: var(--surface-elevated);
+ border: 1px solid var(--border-tertiary);
+ border-radius: var(--corner-radius-lg);
+ box-shadow: var(--shadow-sm);
+}
+
+.header {
+ padding: var(--spacing-md) var(--spacing-lg);
+ border-bottom: 1px solid var(--border-tertiary);
+}
+
+.title {
+ @include label-lg;
+ color: var(--text-primary);
+ margin: 0;
+}
+
+.content {
+ padding: var(--spacing-lg);
+}
+```
+
+### Page Layout
+
+```tsx
+function PageLayout({ children }) {
+ return (
+
+
+
+
+ {/* Navigation items */}
+
+
+
+
+ {children}
+
+
+
+
+ );
+}
+```
+
+```scss
+.page {
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+}
+
+.header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: var(--spacing-md) var(--spacing-xl);
+ border-bottom: 1px solid var(--border-tertiary);
+}
+
+.main {
+ flex: 1;
+ padding: var(--spacing-xl);
+ max-width: var(--max-width-7xl);
+ margin: 0 auto;
+ width: 100%;
+}
+
+.footer {
+ padding: var(--spacing-lg) var(--spacing-xl);
+ border-top: 1px solid var(--border-tertiary);
+}
+```
+
+### Grid Layout
+
+```scss
+.grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+ gap: var(--spacing-lg);
+}
+
+.gridFixed3 {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: var(--spacing-lg);
+
+ @media (max-width: 768px) {
+ grid-template-columns: 1fr;
+ }
+}
+```
+
+## State Patterns
+
+### Loading State
+
+```tsx
+import { Button, Loader } from '@grid/origin';
+
+function LoadingButton() {
+ const [loading, setLoading] = useState(false);
+
+ return (
+
+ );
+}
+
+function LoadingContent() {
+ const { data, isLoading } = useQuery();
+
+ if (isLoading) {
+ return (
+
+
+
+ );
+ }
+
+ return ;
+}
+```
+
+### Empty State
+
+```tsx
+import { Button, CentralIcon } from '@grid/origin';
+
+function EmptyState({ title, description, action }) {
+ return (
+
+
+
{title}
+
{description}
+ {action && (
+
+ )}
+
+ );
+}
+```
+
+```scss
+.emptyState {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: var(--spacing-4xl);
+ text-align: center;
+}
+
+.title {
+ @include label-lg;
+ color: var(--text-primary);
+ margin: var(--spacing-md) 0 var(--spacing-xs);
+}
+
+.description {
+ @include body;
+ color: var(--text-secondary);
+ margin: 0 0 var(--spacing-lg);
+ max-width: 300px;
+}
+```
+
+### Error State
+
+```tsx
+import { Alert, Button } from '@grid/origin';
+
+function ErrorState({ error, onRetry }) {
+ return (
+
+ );
+}
+
+function ErrorBoundaryFallback({ error, resetErrorBoundary }) {
+ return (
+
+ );
+}
+```
+
+### Disabled State
+
+```tsx
+// Disable based on form validity
+
+
+// Disable during loading
+
+
+// Disabled input
+
+
+// Disabled select
+
+
+ Locked
+
+
+```
+
+## Navigation Patterns
+
+### Tabbed Navigation
+
+```tsx
+import { Tabs } from '@grid/origin';
+
+function TabbedContent() {
+ return (
+
+
+ Overview
+ Settings
+ Billing
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+```
+
+### Breadcrumb Navigation
+
+```tsx
+import { Breadcrumb } from '@grid/origin';
+
+function PageBreadcrumb({ items }) {
+ return (
+
+
+ {items.map((item, index) => (
+
+ {index > 0 && }
+
+
+ {item.label}
+
+
+
+ ))}
+
+
+ );
+}
+```
+
+## Dialog Patterns
+
+### Confirmation Dialog
+
+```tsx
+import { AlertDialog, Button } from '@grid/origin';
+
+function DeleteConfirmation({ onDelete }) {
+ return (
+
+
+
+
+
+
+
+ Delete Item?
+
+ This action cannot be undone. This will permanently delete the item
+ and remove all associated data.
+
+
+
+
+
+
+
+
+
+
+ );
+}
+```
+
+### Actions Menu
+
+```tsx
+import { Menu, Button, CentralIcon } from '@grid/origin';
+
+function ActionsMenu({ onEdit, onDuplicate, onDelete }) {
+ return (
+
+
+
+
+
+
+
+
+
+ Edit
+
+
+
+ Duplicate
+
+
+
+
+ Delete
+
+
+
+
+
+ );
+}
+```
+
+## Responsive Patterns
+
+### Responsive Layout
+
+```scss
+.container {
+ padding: var(--spacing-md);
+
+ @media (min-width: 768px) {
+ padding: var(--spacing-xl);
+ }
+
+ @media (min-width: 1024px) {
+ padding: var(--spacing-2xl);
+ }
+}
+
+.grid {
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: var(--spacing-md);
+
+ @media (min-width: 768px) {
+ grid-template-columns: repeat(2, 1fr);
+ gap: var(--spacing-lg);
+ }
+
+ @media (min-width: 1024px) {
+ grid-template-columns: repeat(3, 1fr);
+ }
+}
+```
+
+### Mobile-First Button Group
+
+```tsx
+function ResponsiveActions() {
+ return (
+
+
+
+
+ );
+}
+```
+
+```scss
+.actions {
+ display: flex;
+ flex-direction: column-reverse;
+ gap: var(--spacing-sm);
+
+ @media (min-width: 640px) {
+ flex-direction: row;
+ justify-content: flex-end;
+ }
+}
+
+.cancelBtn {
+ @media (max-width: 639px) {
+ width: 100%;
+ }
+}
+
+.submitBtn {
+ @media (max-width: 639px) {
+ width: 100%;
+ }
+}
+```
diff --git a/.claude/skills/origin-ui-builder/references/styling.md b/.claude/skills/origin-ui-builder/references/styling.md
new file mode 100644
index 0000000..b7cbece
--- /dev/null
+++ b/.claude/skills/origin-ui-builder/references/styling.md
@@ -0,0 +1,685 @@
+# Origin Styling Reference
+
+SCSS patterns, mixins, and styling conventions for Origin components.
+
+## CSS Modules Setup
+
+All Origin components use CSS Modules with SCSS:
+
+```tsx
+// Component.tsx
+import styles from './Component.module.scss';
+
+function Component({ className }) {
+ return (
+
+ {/* content */}
+
+ );
+}
+```
+
+```scss
+// Component.module.scss
+@use '@grid/origin/tokens' as *;
+
+.root {
+ // styles
+}
+```
+
+## Token Usage
+
+### Always Use Tokens
+
+Never hardcode values - always use design tokens:
+
+```scss
+// CORRECT
+.container {
+ padding: var(--spacing-md);
+ color: var(--text-primary);
+ background: var(--surface-primary);
+ border: 1px solid var(--border-secondary);
+ border-radius: var(--corner-radius-md);
+ box-shadow: var(--shadow-sm);
+}
+
+// INCORRECT - hardcoded values
+.container {
+ padding: 16px;
+ color: #1A1A1A;
+ background: #F8F8F7;
+ border: 1px solid #C1C0B8;
+ border-radius: 8px;
+ box-shadow: 0 1px 2px rgba(0,0,0,0.08);
+}
+```
+
+### Common Token Patterns
+
+```scss
+// Spacing
+.element {
+ padding: var(--spacing-md);
+ margin-bottom: var(--spacing-lg);
+ gap: var(--spacing-sm);
+}
+
+// Colors
+.text {
+ color: var(--text-primary);
+
+ &.secondary {
+ color: var(--text-secondary);
+ }
+
+ &.muted {
+ color: var(--text-muted);
+ }
+}
+
+// Surfaces
+.card {
+ background: var(--surface-elevated);
+
+ &:hover {
+ background: var(--surface-hover);
+ }
+}
+
+// Borders
+.bordered {
+ border: 1px solid var(--border-secondary);
+
+ &:focus-visible {
+ border-color: var(--border-primary);
+ }
+}
+```
+
+## Typography Mixins
+
+### Available Mixins
+
+```scss
+@use '@grid/origin/tokens' as *;
+
+// Body text
+.bodyText {
+ @include body; // 14px, regular weight, 20px line-height
+}
+
+.bodyLarge {
+ @include body-lg; // 16px, regular, 24px
+}
+
+.bodySmall {
+ @include body-sm; // 12px, regular, 18px
+}
+
+.bodyXSmall {
+ @include body-xs; // 12px, regular, 12px line-height
+}
+
+// Headings
+.headline {
+ @include headline; // 24px, medium weight, 32px, tight tracking
+}
+
+.headlineSmall {
+ @include headline-sm; // 18px, medium, 24px
+}
+
+// Labels
+.label {
+ @include label; // 14px, book weight (450), 20px
+}
+
+.labelLarge {
+ @include label-lg; // 16px, book, 24px
+}
+
+.labelSmall {
+ @include label-sm; // 12px, book, 18px
+}
+
+.labelXLarge {
+ @include label-xl; // 24px, book, 32px
+}
+
+.labelXSmall {
+ @include label-xs; // 10px, book, 16px
+}
+
+// Buttons
+.buttonText {
+ @include btn; // 14px, book, 20px
+}
+
+.buttonLarge {
+ @include btn-lg; // 16px, book, 24px
+}
+
+.buttonSmall {
+ @include btn-sm; // 12px, book, 12px
+}
+
+// Code
+.codeText {
+ @include code; // 13px mono font, regular, 18px
+}
+```
+
+### Custom Typography
+
+When mixins don't fit, use tokens directly:
+
+```scss
+.customText {
+ font-family: var(--font-family-sans);
+ font-size: var(--font-size-lg);
+ font-weight: var(--font-weight-medium);
+ line-height: var(--font-leading-lg);
+ letter-spacing: var(--font-tracking-tight);
+}
+```
+
+## State Styling
+
+### Base UI Data Attributes
+
+Style states using Base UI's data attributes:
+
+```scss
+.input {
+ border: 1px solid var(--border-secondary);
+ background: var(--surface-base);
+
+ // Hover
+ &:hover:not([data-disabled]) {
+ border-color: var(--border-hover);
+ }
+
+ // Focus
+ &:focus-visible {
+ border-color: var(--border-primary);
+ box-shadow: var(--input-focus);
+ }
+
+ // Disabled
+ &[data-disabled] {
+ background: var(--surface-disabled);
+ color: var(--text-disabled);
+ cursor: not-allowed;
+ }
+
+ // Invalid/Error
+ &[data-invalid] {
+ border-color: var(--border-critical);
+
+ &:focus-visible {
+ box-shadow: var(--input-focus-critical);
+ }
+ }
+}
+```
+
+### Common Data Attributes
+
+| Attribute | Description |
+|-----------|-------------|
+| `[data-disabled]` | Disabled state |
+| `[data-invalid]` | Invalid/error state |
+| `[data-focused]` | Focused state |
+| `[data-selected]` | Selected state |
+| `[data-highlighted]` | Highlighted (keyboard focus) |
+| `[data-pressed]` | Active/pressed state |
+| `[data-open]` | Open state (dropdowns, menus) |
+| `[data-checked]` | Checked state (checkbox, radio) |
+| `[data-indeterminate]` | Indeterminate checkbox |
+
+### Interactive States Pattern
+
+```scss
+.button {
+ background: var(--surface-primary);
+ color: var(--text-primary);
+ border: 1px solid var(--border-secondary);
+ transition: all 150ms ease;
+
+ &:hover:not([data-disabled]) {
+ background: var(--surface-hover);
+ }
+
+ &:active:not([data-disabled]),
+ &[data-pressed] {
+ background: var(--surface-pressed);
+ }
+
+ &[data-disabled] {
+ opacity: 0.5;
+ cursor: not-allowed;
+ }
+}
+```
+
+## Utility Mixins
+
+### Button Reset
+
+Reset native button styles:
+
+```scss
+@mixin button-reset {
+ appearance: none;
+ background: none;
+ border: none;
+ padding: 0;
+ margin: 0;
+ font: inherit;
+ color: inherit;
+ cursor: pointer;
+
+ &:focus {
+ outline: none;
+ }
+}
+
+.iconButton {
+ @include button-reset;
+ // custom styles
+}
+```
+
+### Visually Hidden
+
+Hide visually but keep accessible to screen readers:
+
+```scss
+@mixin visually-hidden {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border: 0;
+}
+
+.srOnly {
+ @include visually-hidden;
+}
+```
+
+### Smooth Corners (Squircle)
+
+iOS-style smooth corners:
+
+```scss
+@mixin smooth-corners($radius) {
+ border-radius: $radius;
+
+ @supports (mask-image: paint(squircle)) {
+ mask-image: paint(squircle);
+ --squircle-radius: $radius;
+ --squircle-smooth: 1;
+ }
+}
+
+.avatar {
+ @include smooth-corners(var(--corner-radius-lg));
+}
+```
+
+### Focus Ring
+
+Standard focus indicator:
+
+```scss
+@mixin focus-ring {
+ &:focus-visible {
+ outline: 2px solid var(--border-primary);
+ outline-offset: 2px;
+ }
+}
+
+// Or use the input focus pattern
+@mixin input-focus {
+ &:focus-visible {
+ box-shadow: var(--input-focus);
+ border-color: var(--border-primary);
+ }
+}
+```
+
+## Animation Patterns
+
+### Reduced Motion
+
+Always include reduced motion support:
+
+```scss
+.animated {
+ transition: transform 200ms ease, opacity 200ms ease;
+
+ @media (prefers-reduced-motion: reduce) {
+ transition: none;
+ }
+}
+
+@keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+
+.fadeIn {
+ animation: fadeIn 200ms ease;
+
+ @media (prefers-reduced-motion: reduce) {
+ animation: none;
+ }
+}
+```
+
+### Common Transitions
+
+```scss
+// Fast (hover states)
+transition: all 100ms ease;
+
+// Normal (most interactions)
+transition: all 150ms ease;
+
+// Slow (page transitions)
+transition: all 200ms ease;
+
+// Specific properties
+transition: background-color 150ms ease, border-color 150ms ease;
+```
+
+### Common Animations
+
+```scss
+// Fade in
+@keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+
+// Scale in (for dropdowns, modals)
+@keyframes scaleIn {
+ from {
+ opacity: 0;
+ transform: scale(0.95);
+ }
+ to {
+ opacity: 1;
+ transform: scale(1);
+ }
+}
+
+// Slide down (for dropdowns)
+@keyframes slideDown {
+ from {
+ opacity: 0;
+ transform: translateY(-8px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+```
+
+## Layout Utilities
+
+### Flexbox Patterns
+
+```scss
+// Center content
+.center {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+// Stack (vertical)
+.stack {
+ display: flex;
+ flex-direction: column;
+ gap: var(--spacing-md);
+}
+
+// Row (horizontal)
+.row {
+ display: flex;
+ align-items: center;
+ gap: var(--spacing-sm);
+}
+
+// Space between
+.spaceBetween {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+```
+
+### Grid Patterns
+
+```scss
+// Auto-fit grid
+.autoGrid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+ gap: var(--spacing-lg);
+}
+
+// Fixed columns
+.grid3 {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: var(--spacing-md);
+}
+
+// Two column form
+.formGrid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: var(--spacing-md) var(--spacing-lg);
+
+ @media (max-width: 640px) {
+ grid-template-columns: 1fr;
+ }
+}
+```
+
+## Responsive Design
+
+### Breakpoint Usage
+
+```scss
+// Mobile first approach
+.element {
+ padding: var(--spacing-sm);
+
+ // Tablet
+ @media (min-width: 768px) {
+ padding: var(--spacing-md);
+ }
+
+ // Desktop
+ @media (min-width: 1024px) {
+ padding: var(--spacing-lg);
+ }
+
+ // Large desktop
+ @media (min-width: 1280px) {
+ padding: var(--spacing-xl);
+ }
+}
+```
+
+### Hide/Show Utilities
+
+```scss
+.hideOnMobile {
+ @media (max-width: 767px) {
+ display: none;
+ }
+}
+
+.hideOnDesktop {
+ @media (min-width: 768px) {
+ display: none;
+ }
+}
+
+.mobileOnly {
+ display: block;
+
+ @media (min-width: 768px) {
+ display: none;
+ }
+}
+
+.desktopOnly {
+ display: none;
+
+ @media (min-width: 768px) {
+ display: block;
+ }
+}
+```
+
+## Dark Mode
+
+### Manual Dark Mode
+
+```scss
+.element {
+ background: var(--surface-primary);
+ color: var(--text-primary);
+
+ // Dark mode via class or attribute
+ :global([data-theme="dark"]) &,
+ :global(.dark) & {
+ // Tokens auto-adapt, but can override if needed
+ }
+}
+```
+
+### System Preference
+
+```scss
+@media (prefers-color-scheme: dark) {
+ // Dark mode styles if not using token system
+}
+```
+
+Note: Origin's semantic tokens automatically adapt to dark mode, so manual overrides are rarely needed.
+
+## Component-Specific Patterns
+
+### Input Styling
+
+```scss
+.input {
+ @include body;
+ width: 100%;
+ padding: var(--spacing-xs) var(--spacing-sm);
+ background: var(--surface-base);
+ border: 1px solid var(--border-secondary);
+ border-radius: var(--corner-radius-sm);
+ color: var(--text-primary);
+ transition: border-color 150ms ease, box-shadow 150ms ease;
+
+ &::placeholder {
+ color: var(--text-muted);
+ }
+
+ &:hover:not([data-disabled]) {
+ border-color: var(--border-hover);
+ }
+
+ &:focus-visible {
+ border-color: var(--border-primary);
+ box-shadow: var(--input-focus);
+ outline: none;
+ }
+
+ &[data-invalid] {
+ border-color: var(--border-critical);
+
+ &:focus-visible {
+ box-shadow: var(--input-focus-critical);
+ }
+ }
+
+ &[data-disabled] {
+ background: var(--surface-disabled);
+ color: var(--text-disabled);
+ cursor: not-allowed;
+ }
+}
+```
+
+### Card Styling
+
+```scss
+.card {
+ background: var(--surface-elevated);
+ border: 1px solid var(--border-tertiary);
+ border-radius: var(--corner-radius-lg);
+ box-shadow: var(--shadow-sm);
+ overflow: hidden;
+
+ &.interactive {
+ cursor: pointer;
+ transition: box-shadow 150ms ease, border-color 150ms ease;
+
+ &:hover {
+ box-shadow: var(--shadow-md);
+ border-color: var(--border-secondary);
+ }
+
+ &:active {
+ box-shadow: var(--shadow-sm);
+ }
+ }
+}
+```
+
+### List Item Styling
+
+```scss
+.listItem {
+ display: flex;
+ align-items: center;
+ gap: var(--spacing-sm);
+ padding: var(--spacing-sm) var(--spacing-md);
+ border-radius: var(--corner-radius-sm);
+ cursor: pointer;
+ transition: background-color 100ms ease;
+
+ &:hover {
+ background: var(--surface-hover);
+ }
+
+ &[data-highlighted] {
+ background: var(--surface-hover);
+ }
+
+ &[data-selected] {
+ background: var(--surface-active);
+ }
+
+ &[data-disabled] {
+ opacity: 0.5;
+ cursor: not-allowed;
+ }
+}
+```
diff --git a/.claude/skills/origin-ui-builder/references/tokens.md b/.claude/skills/origin-ui-builder/references/tokens.md
new file mode 100644
index 0000000..4e296e0
--- /dev/null
+++ b/.claude/skills/origin-ui-builder/references/tokens.md
@@ -0,0 +1,365 @@
+# Origin Design Tokens Reference
+
+Complete reference for all design tokens in the Origin design system.
+
+## Spacing Scale
+
+Spacing tokens follow a consistent scale for margins, padding, and gaps.
+
+| Token | Value | Common Use |
+|-------|-------|------------|
+| `--spacing-4xs` | 2px | Minimal spacing |
+| `--spacing-3xs` | 4px | Tight spacing |
+| `--spacing-2xs` | 6px | Compact spacing |
+| `--spacing-xs` | 8px | Small spacing |
+| `--spacing-sm` | 12px | Default compact |
+| `--spacing-md` | 16px | Default spacing |
+| `--spacing-lg` | 20px | Comfortable spacing |
+| `--spacing-xl` | 24px | Section spacing |
+| `--spacing-2xl` | 32px | Large sections |
+| `--spacing-3xl` | 40px | Page sections |
+| `--spacing-4xl` | 48px | Major sections |
+| `--spacing-5xl` | 56px | Large gaps |
+| `--spacing-6xl` | 64px | Extra large |
+| `--spacing-7xl` | 72px | Header spacing |
+| `--spacing-8xl` | 80px | Hero sections |
+
+Extended scale continues: `--spacing-9xl` (96px) through `--spacing-21xl` (320px)
+
+Negative values available: `--spacing-neg-4xs` through `--spacing-neg-21xl`
+
+### Usage
+
+```scss
+.container {
+ padding: var(--spacing-md);
+ gap: var(--spacing-sm);
+ margin-bottom: var(--spacing-xl);
+}
+
+.compact {
+ padding: var(--spacing-xs) var(--spacing-sm);
+}
+```
+
+## Color System
+
+### Base Colors
+
+| Token | Value | Use |
+|-------|-------|-----|
+| `--color-base-white` | #FFFFFF | Pure white |
+| `--color-base-black` | #000000 | Pure black |
+
+### Gray Scale
+
+| Token | Value |
+|-------|-------|
+| `--color-gray-025` | Lightest gray |
+| `--color-gray-050` | Very light |
+| `--color-gray-100` | Light gray |
+| `--color-gray-200` | Light-medium |
+| `--color-gray-300` | Medium-light |
+| `--color-gray-400` | Medium |
+| `--color-gray-500` | Mid gray |
+| `--color-gray-600` | Medium-dark |
+| `--color-gray-700` | Dark |
+| `--color-gray-800` | Very dark |
+| `--color-gray-900` | Near black |
+| `--color-gray-950` | Darkest gray |
+
+### Semantic Colors
+
+Each color family has shades from 050 to 950:
+
+**Purple:** `--color-purple-050` through `--color-purple-950`
+**Blue:** `--color-blue-050` through `--color-blue-950`
+**Sky:** `--color-sky-050` through `--color-sky-950`
+**Pink:** `--color-pink-050` through `--color-pink-950`
+**Green:** `--color-green-050` through `--color-green-950`
+**Yellow:** `--color-yellow-050` through `--color-yellow-950`
+**Red:** `--color-red-050` through `--color-red-950`
+
+### Alpha Colors
+
+Transparent variants for overlays:
+
+```scss
+// White with opacity
+--color-alpha-white-02 // 2% opacity
+--color-alpha-white-04
+--color-alpha-white-10
+--color-alpha-white-20
+--color-alpha-white-50
+
+// Black with opacity
+--color-alpha-black-02
+--color-alpha-black-04
+--color-alpha-black-10
+--color-alpha-black-20
+--color-alpha-black-50
+```
+
+## Semantic Tokens
+
+### Text Colors
+
+| Token | Use |
+|-------|-----|
+| `--text-primary` | Primary text (#1A1A1A) |
+| `--text-secondary` | Secondary text (#7C7C7C) |
+| `--text-tertiary` | Tertiary/hint text |
+| `--text-muted` | Muted/placeholder |
+| `--text-disabled` | Disabled state |
+| `--text-surface` | Text on colored surfaces |
+| `--text-critical` | Error text |
+| `--text-purple` | Purple accent text |
+| `--text-blue` | Blue accent text |
+| `--text-green` | Success text |
+| `--text-yellow` | Warning text |
+
+### Icon Colors
+
+| Token | Use |
+|-------|-----|
+| `--icon-primary` | Primary icons |
+| `--icon-secondary` | Secondary icons |
+| `--icon-tertiary` | Tertiary icons |
+| `--icon-surface` | Icons on colored surfaces |
+| `--icon-info` | Info icons |
+| `--icon-success` | Success icons |
+| `--icon-warning` | Warning icons |
+| `--icon-critical` | Error icons |
+| `--icon-inverted` | Icons on dark backgrounds |
+
+### Border Colors
+
+| Token | Use |
+|-------|-----|
+| `--border-primary` | Primary borders (#1A1A1A) |
+| `--border-secondary` | Secondary borders (#C1C0B8) |
+| `--border-tertiary` | Subtle borders (10% black) |
+| `--border-hover` | Hover state borders |
+| `--border-inverted` | Borders on dark backgrounds |
+| `--border-critical` | Error state borders |
+
+### Surface Colors
+
+| Token | Use |
+|-------|-----|
+| `--surface-base` | Page background (#FFFFFF) |
+| `--surface-primary` | Primary surface (#F8F8F7) |
+| `--surface-secondary` | Secondary surface |
+| `--surface-tertiary` | Tertiary surface |
+| `--surface-elevated` | Elevated elements (cards) |
+| `--surface-sunken` | Recessed areas |
+| `--surface-hover` | Hover state (4% black) |
+| `--surface-pressed` | Pressed state |
+| `--surface-active` | Active/selected state |
+| `--surface-inverted` | Dark surface (#1A1A1A) |
+| `--surface-disabled` | Disabled state |
+| `--surface-scrim` | Modal overlays |
+
+Color-specific surfaces:
+- `--surface-purple`, `--surface-purple-strong`
+- `--surface-blue`, `--surface-blue-strong`
+- `--surface-green`, `--surface-green-strong`
+- `--surface-red`, `--surface-red-strong`
+- `--surface-yellow`, `--surface-yellow-strong`
+
+### Focus States
+
+| Token | Use |
+|-------|-----|
+| `--input-focus` | Input focus ring |
+| `--input-focus-critical` | Error state focus ring |
+
+```scss
+&:focus-visible {
+ box-shadow: var(--input-focus);
+}
+
+&[data-invalid]:focus-visible {
+ box-shadow: var(--input-focus-critical);
+}
+```
+
+## Typography
+
+### Font Families
+
+| Token | Value |
+|-------|-------|
+| `--font-family-sans` | "Suisse Intl" |
+| `--font-family-mono` | "Suisse Int'l Mono" |
+
+### Font Sizes
+
+| Token | Value |
+|-------|-------|
+| `--font-size-2xs` | 10px |
+| `--font-size-xs` | 12px |
+| `--font-size-sm` | 13px |
+| `--font-size-base` | 14px |
+| `--font-size-lg` | 16px |
+| `--font-size-xl` | 18px |
+| `--font-size-2xl` | 24px |
+
+### Font Weights
+
+| Token | Value |
+|-------|-------|
+| `--font-weight-hairline` | 100 |
+| `--font-weight-thin` | 250 |
+| `--font-weight-light` | 300 |
+| `--font-weight-regular` | 400 |
+| `--font-weight-book` | 450 |
+| `--font-weight-medium` | 500 |
+| `--font-weight-semibold` | 600 |
+| `--font-weight-bold` | 700 |
+| `--font-weight-black` | 900 |
+
+### Line Heights
+
+| Token | Value |
+|-------|-------|
+| `--font-leading-2xs` | 12px |
+| `--font-leading-xs` | 16px |
+| `--font-leading-sm` | 18px |
+| `--font-leading-base` | 20px |
+| `--font-leading-lg` | 24px |
+| `--font-leading-xl` | 32px |
+
+### Letter Spacing
+
+| Token | Value |
+|-------|-------|
+| `--font-tracking-tighter` | -0.7px |
+| `--font-tracking-tight` | -0.2px |
+| `--font-tracking-normal` | 0 |
+| `--font-tracking-wide` | 0.2px |
+| `--font-tracking-wider` | 0.7px |
+
+### Typography Mixins
+
+Use these SCSS mixins for consistent typography:
+
+```scss
+@use '@grid/origin/tokens' as *;
+
+// Body text
+@include body; // 14px, regular, 20px line-height
+@include body-lg; // 16px, regular, 24px
+@include body-sm; // 12px, regular, 18px
+@include body-xs; // 12px, regular, 12px
+
+// Headings
+@include headline; // 24px, medium, 32px, tight tracking
+@include headline-sm; // 18px, medium, 24px
+
+// Labels
+@include label; // 14px, book (450), 20px
+@include label-lg; // 16px, book, 24px
+@include label-sm; // 12px, book, 18px
+@include label-xl; // 24px, book, 32px
+@include label-xs; // 10px, book, 16px
+
+// Buttons
+@include btn; // 14px, book, 20px
+@include btn-lg; // 16px, book, 24px
+@include btn-sm; // 12px, book, 12px
+
+// Code
+@include code; // 13px mono, regular, 18px
+```
+
+## Border Radius
+
+| Token | Value | Use |
+|-------|-------|-----|
+| `--corner-radius-2xs` | 2px | Minimal rounding |
+| `--corner-radius-xs` | 4px | Small elements |
+| `--corner-radius-sm` | 6px | Compact elements |
+| `--corner-radius-md` | 8px | Default rounding |
+| `--corner-radius-lg` | 12px | Cards |
+| `--corner-radius-xl` | 16px | Large cards |
+| `--corner-radius-2xl` | 24px | Modals |
+| `--corner-radius-3xl` | 32px | Large modals |
+| `--corner-radius-4xl` | 40px | Extra large |
+| `--corner-radius-round` | 999px | Pills/circles |
+| `--corner-radius-square` | 0px | No rounding |
+
+## Shadows
+
+| Token | Value | Use |
+|-------|-------|-----|
+| `--shadow-sm` | `0px 1px 2px rgba(0,0,0,0.08)` | Subtle elevation |
+| `--shadow-md` | Multi-layer shadow | Cards |
+| `--shadow-lg` | Multi-layer shadow | Modals, popovers |
+
+```scss
+.card {
+ box-shadow: var(--shadow-md);
+}
+
+.modal {
+ box-shadow: var(--shadow-lg);
+}
+```
+
+## Strokes (Border Widths)
+
+| Token | Value |
+|-------|-------|
+| `--stroke-none` | 0px |
+| `--stroke-1xs` | 0.33px |
+| `--stroke-xs` | 0.5px |
+| `--stroke-sm` | 1px |
+| `--stroke-md` | 1.5px |
+| `--stroke-lg` | 2px |
+| `--stroke-xl` | 4px |
+
+## Breakpoints
+
+| Token | Value |
+|-------|-------|
+| `--screens-sm` | 640px |
+| `--screens-md` | 768px |
+| `--screens-lg` | 1024px |
+| `--screens-xl` | 1280px |
+| `--screens-2xl` | 1536px |
+
+```scss
+@media (min-width: 768px) {
+ // Tablet and up
+}
+
+@media (min-width: 1024px) {
+ // Desktop
+}
+```
+
+## Max Widths
+
+| Token | Value |
+|-------|-------|
+| `--max-width-xs` | 320px |
+| `--max-width-sm` | 384px |
+| `--max-width-md` | 448px |
+| `--max-width-lg` | 512px |
+| `--max-width-xl` | 576px |
+| `--max-width-2xl` | 672px |
+| `--max-width-3xl` | 768px |
+| `--max-width-4xl` | 896px |
+| `--max-width-5xl` | 1024px |
+| `--max-width-6xl` | 1152px |
+| `--max-width-7xl` | 1280px |
+
+## Dark Mode
+
+Semantic tokens automatically adapt in dark mode via:
+- `[data-theme="dark"]` attribute
+- `.dark` class
+- `@media (prefers-color-scheme: dark)`
+
+All semantic tokens (text, surfaces, borders, icons) flip appropriately.
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..6a4f33c
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,120 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## Project Overview
+
+**Origin** is a design system built on Base UI with a Figma-first approach. It serves as the foundational package (`@grid/origin`) for consuming products. Base UI handles component behavior and accessibility; Figma provides tokenized CSS that maps directly to code.
+
+## Commands
+
+```bash
+npm install --legacy-peer-deps # Install dependencies
+npm run dev # Next.js dev server (localhost:3000)
+npm run storybook # Storybook (localhost:6006)
+npm run build # Production build (catches type errors)
+npm run lint # ESLint
+npm run test # Playwright component tests
+npm run test:unit # Vitest unit tests
+npm run test:all # Both test suites
+npm run tokens:build # Transform Figma tokens to SCSS
+npm run figma:styles # Sync text styles + effects from Figma API
+npm run check:baseui # Check Base UI version sync
+```
+
+## Architecture
+
+### Key Directories
+
+| Path | Purpose |
+|------|---------|
+| `src/components/` | React components (Base UI wrappers) |
+| `src/tokens/` | Design tokens (SCSS variables, mixins) |
+| `src/lib/` | Utilities (base-ui-utils.ts, dev-warn.ts) |
+| `tools/figma-styles/` | Figma REST API style fetchers |
+| `tools/base-ui-lint/` | Figma plugin for Base UI validation |
+
+### Token System
+
+Tokens are generated from Figma Variables and should never be edited manually:
+
+| File | Content | Source |
+|------|---------|--------|
+| `_variables.scss` | Colors, spacing, sizing | Figma Variables export |
+| `_text-styles.scss` | Typography mixins | Figma Text Styles API |
+| `_effects.scss` | Shadow variables | Figma Effect Styles API |
+
+### Component Structure
+
+Components follow a namespace export pattern for compound components:
+
+```
+src/components/{Name}/
+├── {Name}.test-stories.tsx # Test fixtures
+├── {Name}.test.tsx # Playwright component tests
+├── {Name}.tsx or parts.tsx # Implementation
+├── {Name}.module.scss # Styles (CSS Modules)
+├── {Name}.stories.tsx # Storybook
+└── index.ts # Exports
+```
+
+## Component Implementation
+
+### Decision Tree
+
+1. **Does Base UI have it?** → Wrap it (read Base UI docs first)
+2. **No Base UI equivalent?** → Build custom (check WAI-ARIA APG)
+
+### Research Order
+
+1. Check `https://base-ui.com/llms.txt` for component availability
+2. Read docs at `https://base-ui.com/react/components/{name}`
+3. Note all `data-*` attributes Base UI provides (don't duplicate them)
+4. Apply project styling with tokens
+
+### Required Patterns
+
+- Use `forwardRef` with named function (not arrow)
+- Use `clsx` for className merging
+- Use `var(--token)` for all values (no hardcoded colors/spacing)
+- Use `@include` mixins for typography
+- Props spread last (consumer wins)
+
+### Wrapper Simplicity
+
+Before adding any custom prop or data attribute:
+1. Check if Base UI already provides it (`data-disabled`, `data-invalid`, `data-focused`, etc.)
+2. Start minimal: forward ref, merge className, spread props
+3. Only add custom props when Base UI lacks the feature
+
+### Context Provider Components
+
+Some Base UI parts are context providers that don't render elements. Check type definitions:
+
+```bash
+cat node_modules/@base-ui/react/{component}/{part}/{Part}.d.ts
+```
+
+If it's a context provider (no `ForwardRefExoticComponent`), wrap in your own element to enable ref/className support.
+
+## Styling
+
+- CSS Modules with SCSS (`Component.module.scss`)
+- Mirror Figma exactly: use tokens where Figma uses tokens, raw values where Figma uses raw values
+- State styling via Base UI's `data-*` attributes
+- Reduced motion: always include `@media (prefers-reduced-motion: reduce)`
+
+## Testing
+
+- Write tests first (TDD approach)
+- Use `page.getByRole()` or `page.getByPlaceholder()` for queries
+- Test for Base UI's data attributes, don't invent custom ones
+- Run `npm run build` before committing (production build catches type errors dev mode misses)
+
+## Conventions
+
+- No hardcoded values (use tokens)
+- No emojis in code or console output
+- No decorative comment dividers
+- No editing generated files (`_text-styles.scss`, `_effects.scss`)
+- This is a design system: keep components generic and product-agnostic
diff --git a/package-lock.json b/package-lock.json
index 1d0de71..9472453 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2034,16 +2034,6 @@
"keyv": "^5.5.5"
}
},
- "node_modules/@cacheable/memory/node_modules/keyv": {
- "version": "5.6.0",
- "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz",
- "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@keyv/serialize": "^1.1.1"
- }
- },
"node_modules/@cacheable/utils": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.3.3.tgz",
@@ -2055,16 +2045,6 @@
"keyv": "^5.5.5"
}
},
- "node_modules/@cacheable/utils/node_modules/keyv": {
- "version": "5.6.0",
- "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz",
- "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@keyv/serialize": "^1.1.1"
- }
- },
"node_modules/@central-icons-react/round-filled-radius-3-stroke-1.5": {
"version": "1.1.89",
"resolved": "https://registry.npmjs.org/@central-icons-react/round-filled-radius-3-stroke-1.5/-/round-filled-radius-3-stroke-1.5-1.1.89.tgz",
@@ -4152,20 +4132,6 @@
"url": "https://opencollective.com/parcel"
}
},
- "node_modules/@parcel/watcher/node_modules/picomatch": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
- "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
"node_modules/@playwright/experimental-ct-core": {
"version": "1.57.0",
"resolved": "https://registry.npmjs.org/@playwright/experimental-ct-core/-/experimental-ct-core-1.57.0.tgz",
@@ -5294,7 +5260,7 @@
"version": "19.2.8",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.8.tgz",
"integrity": "sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"csstype": "^3.2.2"
@@ -7249,16 +7215,6 @@
"qified": "^0.6.0"
}
},
- "node_modules/cacheable/node_modules/keyv": {
- "version": "5.6.0",
- "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz",
- "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@keyv/serialize": "^1.1.1"
- }
- },
"node_modules/call-bind": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
@@ -7866,7 +7822,7 @@
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/damerau-levenshtein": {
@@ -9333,6 +9289,16 @@
"node": ">=16"
}
},
+ "node_modules/file-entry-cache/node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@@ -9406,6 +9372,16 @@
"node": "^10.12.0 || >=12.0.0"
}
},
+ "node_modules/flat-cache/node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
"node_modules/flatted": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
@@ -11160,13 +11136,13 @@
}
},
"node_modules/keyv": {
- "version": "4.5.4",
- "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
- "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz",
+ "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "json-buffer": "3.0.1"
+ "@keyv/serialize": "^1.1.1"
}
},
"node_modules/kind-of": {
@@ -11488,6 +11464,19 @@
"node": ">=8.6"
}
},
+ "node_modules/micromatch/node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
"node_modules/miller-rabin": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
@@ -12352,13 +12341,13 @@
"license": "ISC"
},
"node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">=8.6"
+ "node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
@@ -12926,7 +12915,6 @@
"version": "19.2.3",
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -12981,7 +12969,6 @@
"version": "19.2.3",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
"integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"scheduler": "^0.27.0"
@@ -13678,7 +13665,6 @@
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
- "dev": true,
"license": "MIT"
},
"node_modules/schema-utils": {
@@ -15016,19 +15002,6 @@
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
- "node_modules/tinyglobby/node_modules/picomatch": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
- "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
"node_modules/tinyrainbow": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz",
@@ -16133,19 +16106,6 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
- "node_modules/vite/node_modules/picomatch": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
- "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
"node_modules/vitest": {
"version": "4.0.17",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.17.tgz",
@@ -16316,19 +16276,6 @@
"node": ">=18"
}
},
- "node_modules/vitest/node_modules/picomatch": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
- "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
"node_modules/vitest/node_modules/tinyrainbow": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz",
diff --git a/src/components/Form/PaymentDestinationForm.stories.tsx b/src/components/Form/PaymentDestinationForm.stories.tsx
new file mode 100644
index 0000000..3230753
--- /dev/null
+++ b/src/components/Form/PaymentDestinationForm.stories.tsx
@@ -0,0 +1,341 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import * as React from 'react';
+import { Form } from './Form';
+import { Field } from '@/components/Field';
+import { Fieldset } from '@/components/Fieldset';
+import { Input } from '@/components/Input';
+import { Select } from '@/components/Select';
+import { Button } from '@/components/Button';
+
+const meta = {
+ title: 'Examples/Payment Destination Form',
+ component: Form,
+ parameters: {
+ layout: 'padded',
+ },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+const accountTypes = [
+ { value: 'clabe', label: 'CLABE', region: 'Mexico' },
+ { value: 'iban', label: 'IBAN', region: 'Europe' },
+ { value: 'pix', label: 'PIX', region: 'Brazil' },
+ { value: 'ach', label: 'ACH', region: 'United States' },
+ { value: 'swift', label: 'SWIFT/BIC', region: 'International' },
+];
+
+const balanceHistory = [
+ { month: 'Aug', value: 12400 },
+ { month: 'Sep', value: 15200 },
+ { month: 'Oct', value: 14100 },
+ { month: 'Nov', value: 18900 },
+ { month: 'Dec', value: 22100 },
+ { month: 'Jan', value: 24850 },
+];
+
+function BalanceChart() {
+ const width = 320;
+ const height = 120;
+ const padding = { top: 16, right: 16, bottom: 24, left: 16 };
+ const chartWidth = width - padding.left - padding.right;
+ const chartHeight = height - padding.top - padding.bottom;
+
+ const maxValue = Math.max(...balanceHistory.map((d) => d.value));
+ const minValue = Math.min(...balanceHistory.map((d) => d.value));
+ const valueRange = maxValue - minValue;
+
+ const points = balanceHistory.map((d, i) => {
+ const x = padding.left + (i / (balanceHistory.length - 1)) * chartWidth;
+ const y = padding.top + chartHeight - ((d.value - minValue) / valueRange) * chartHeight;
+ return { x, y, ...d };
+ });
+
+ const linePath = points.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p.x} ${p.y}`).join(' ');
+
+ const areaPath = `
+ ${linePath}
+ L ${points[points.length - 1].x} ${padding.top + chartHeight}
+ L ${points[0].x} ${padding.top + chartHeight}
+ Z
+ `;
+
+ return (
+
+ );
+}
+
+function BalanceCard() {
+ const currentBalance = balanceHistory[balanceHistory.length - 1].value;
+ const previousBalance = balanceHistory[balanceHistory.length - 2].value;
+ const change = currentBalance - previousBalance;
+ const changePercent = ((change / previousBalance) * 100).toFixed(1);
+
+ return (
+
+
+
+
+ Available Balance
+
+
+ ${currentBalance.toLocaleString()}.00
+
+
+
+
+ +{changePercent}%
+
+
+
+
+ );
+}
+
+export const PaymentDestinationForm: Story = {
+ render: function PaymentDestinationFormStory() {
+ const [accountType, setAccountType] = React.useState('');
+ const [errors, setErrors] = React.useState>({});
+ const [submitted, setSubmitted] = React.useState(false);
+
+ const getAccountPlaceholder = () => {
+ switch (accountType) {
+ case 'clabe':
+ return '18 digits (e.g., 012345678901234567)';
+ case 'iban':
+ return 'e.g., DE89370400440532013000';
+ case 'pix':
+ return 'CPF, CNPJ, email, phone, or random key';
+ case 'ach':
+ return 'Routing + Account number';
+ case 'swift':
+ return '8-11 characters (e.g., DEUTDEFF)';
+ default:
+ return 'Enter account identifier';
+ }
+ };
+
+ const handleSubmit = (event: React.FormEvent) => {
+ event.preventDefault();
+ const formData = new FormData(event.currentTarget);
+ const newErrors: Record = {};
+
+ if (!accountType) newErrors.accountType = 'Please select an account type';
+ if (!formData.get('accountNumber')) newErrors.accountNumber = 'Account number is required';
+ if (!formData.get('receiverName')) newErrors.receiverName = 'Receiver name is required';
+ if (!formData.get('addressLine1')) newErrors.addressLine1 = 'Address is required';
+ if (!formData.get('city')) newErrors.city = 'City is required';
+ if (!formData.get('country')) newErrors.country = 'Country is required';
+
+ setErrors(newErrors);
+ if (Object.keys(newErrors).length === 0) {
+ setSubmitted(true);
+ }
+ };
+
+ if (submitted) {
+ return (
+
+
+
+ Payment destination saved
+
+
+ You can now send funds to this recipient.
+
+
+ );
+ }
+
+ return (
+
+ );
+ },
+};
diff --git a/src/components/Logo/Logo.stories.tsx b/src/components/Logo/Logo.stories.tsx
index 4ee9f8c..3659f2a 100644
--- a/src/components/Logo/Logo.stories.tsx
+++ b/src/components/Logo/Logo.stories.tsx
@@ -35,7 +35,7 @@ export const Default: Story = {
},
};
-export const Logo: Story = {
+export const LogoRegular: Story = {
args: {
variant: 'logo',
weight: 'regular',