@tekton/ui
Tekton UI Reference Component Library - High-quality, accessible React components with CSS Variables theming.
Overview
@tekton/ui provides 20 production-ready components + 13 screen templates built with:
- Screen Templates (NEW): 13 full-page layouts for rapid development
- React 19 - Latest React features with Server Components support
- TypeScript 5.7+ - Full type safety with strict mode
- Radix UI v1.0+ - Accessible headless primitives
- CSS Variables - 3-layer architecture (Atomic → Semantic → Component)
- WCAG 2.1 AA - Fully accessible
Installation
pnpm add @tekton/ui
yarn add @tekton/ui
Quick Start
Import Components
import { Button, Input, Card } from '@tekton/ui';
import '@tekton/ui/styles';
export default function App() {
return (
<Card>
<CardHeader>
<CardTitle>Welcome to Tekton UI</CardTitle>
<CardDescription>Build accessible UIs faster</CardDescription>
</CardHeader>
<CardContent>
<Input placeholder="Enter your email..." />
</CardContent>
<CardFooter>
<Button variant="primary">Get Started</Button>
</CardFooter>
</Card>
);
}
CSS Variables Theming
Import the CSS Variables template and customize:
@import '@tekton/ui/styles';
:root {
--button-primary-background: #3b82f6;
--button-primary-foreground: white;
--input-border: #d1d5db;
--input-background: white;
--card-background: white;
--card-border: #e5e7eb;
}
Components
Primitive Components (14)
Core UI building blocks with single responsibility:
- Button - Interactive button with loading states and variants (default, primary, secondary, destructive, outline, ghost, link)
- Input - Text input with error states and type support (text, email, password, number)
- Checkbox - Checkbox with indeterminate support and form integration
- RadioGroup - Radio button groups with keyboard navigation
- Switch - Toggle switch with checked/unchecked states
- Slider - Range slider with min/max/step configuration
- Text - Polymorphic text component with size variants
- Heading - Hierarchical headings (h1-h6) with level prop
- Badge - Status badges (default, primary, secondary, destructive, outline)
- Avatar - User avatars with image/fallback support
- Progress - Progress bar with value 0-100
- Link - Navigation links (default, muted, underline variants)
- Image - Image component with alt text and optional object-fit
- List - Polymorphic list component (ul/ol)
Composed Components (6)
Complex components built from primitives:
- Card - Card container with header/content/footer structure
- Form - Form with validation context and error handling
- Modal - Dialog/modal overlay with Portal rendering
- Dropdown - Dropdown menu with keyboard navigation
- Tabs - Tabbed navigation with automatic ARIA labels
- Table - Data table with semantic HTML structure
Component Examples
Button
import { Button } from '@tekton/ui';
<Button variant="default">Default</Button>
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="destructive">Delete</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
<Button loading>Loading...</Button>
<Button disabled>Disabled</Button>
Props:
variant: "default" | "primary" | "secondary" | "destructive" | "outline" | "ghost" | "link"
size: "sm" | "md" | "lg"
loading: boolean
- All native
<button> props
Input
import { Input } from '@tekton/ui';
<Input type="text" placeholder="Enter text..." />
<Input type="email" placeholder="Email address" />
<Input type="password" placeholder="Password" />
<Input type="text" aria-invalid="true" />
<Input disabled placeholder="Disabled input" />
Props:
type: "text" | "email" | "password" | "number" | "search" | "tel" | "url"
- All native
<input> props
Card
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from '@tekton/ui';
<Card>
<CardHeader>
<CardTitle>Card Title</CardTitle>
<CardDescription>Card description or subtitle</CardDescription>
</CardHeader>
<CardContent>
<p>Main content goes here</p>
</CardContent>
<CardFooter>
<Button>Action</Button>
</CardFooter>
</Card>;
Form
import { Form, FormField, FormLabel, FormControl, FormDescription, FormMessage } from '@tekton/ui';
import { Input } from '@tekton/ui';
<Form
onSubmit={e => {
e.preventDefault(); /* handle submit */
}}
>
<FormField name="email">
<FormLabel htmlFor="email">Email</FormLabel>
<FormControl>
<Input id="email" type="email" />
</FormControl>
<FormDescription>We'll never share your email.</FormDescription>
<FormMessage name="email">Invalid email address</FormMessage>
</FormField>
</Form>;
Modal (Dialog)
import {
Modal,
ModalTrigger,
ModalContent,
ModalHeader,
ModalTitle,
ModalDescription,
ModalFooter,
ModalClose,
} from '@tekton/ui';
import { Button } from '@tekton/ui';
<Modal>
<ModalTrigger asChild>
<Button>Open Modal</Button>
</ModalTrigger>
<ModalContent>
<ModalHeader>
<ModalTitle>Modal Title</ModalTitle>
<ModalDescription>Modal description text</ModalDescription>
</ModalHeader>
<div>Modal body content</div>
<ModalFooter>
<ModalClose asChild>
<Button variant="outline">Cancel</Button>
</ModalClose>
<Button variant="primary">Confirm</Button>
</ModalFooter>
</ModalContent>
</Modal>;
import {
Dropdown,
DropdownTrigger,
DropdownContent,
DropdownItem,
DropdownSeparator,
} from '@tekton/ui';
import { Button } from '@tekton/ui';
<Dropdown>
<DropdownTrigger asChild>
<Button>Open Menu</Button>
</DropdownTrigger>
<DropdownContent>
<DropdownItem onClick={() => console.log('Edit')}>Edit</DropdownItem>
<DropdownItem onClick={() => console.log('Duplicate')}>Duplicate</DropdownItem>
<DropdownSeparator />
<DropdownItem disabled>Disabled Item</DropdownItem>
<DropdownItem onClick={() => console.log('Delete')}>Delete</DropdownItem>
</DropdownContent>
</Dropdown>;
Tabs
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@tekton/ui';
<Tabs defaultValue="tab1">
<TabsList>
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
<TabsTrigger value="tab2">Tab 2</TabsTrigger>
<TabsTrigger value="tab3">Tab 3</TabsTrigger>
</TabsList>
<TabsContent value="tab1">
<p>Content for Tab 1</p>
</TabsContent>
<TabsContent value="tab2">
<p>Content for Tab 2</p>
</TabsContent>
<TabsContent value="tab3">
<p>Content for Tab 3</p>
</TabsContent>
</Tabs>;
Checkbox
import { Checkbox } from '@tekton/ui';
<label>
<Checkbox id="terms" />
Accept terms and conditions
</label>
<Checkbox checked="indeterminate" />
<Checkbox disabled />
Props:
checked: boolean | "indeterminate"
onCheckedChange: (checked: boolean | "indeterminate") => void
- All Radix UI Checkbox props
RadioGroup
import { RadioGroup, RadioGroupItem } from '@tekton/ui';
<RadioGroup defaultValue="option1">
<div>
<RadioGroupItem value="option1" id="opt1" />
<label htmlFor="opt1">Option 1</label>
</div>
<div>
<RadioGroupItem value="option2" id="opt2" />
<label htmlFor="opt2">Option 2</label>
</div>
<div>
<RadioGroupItem value="option3" id="opt3" />
<label htmlFor="opt3">Option 3</label>
</div>
</RadioGroup>;
Switch
import { Switch } from '@tekton/ui';
<label>
<Switch id="notifications" />
Enable notifications
</label>
<Switch checked onCheckedChange={(checked) => console.log(checked)} />
Props:
checked: boolean
onCheckedChange: (checked: boolean) => void
- All Radix UI Switch props
Slider
import { Slider } from '@tekton/ui';
<label htmlFor="volume" id="volume-label">Volume</label>
<Slider
id="volume"
defaultValue={[50]}
min={0}
max={100}
step={1}
aria-labelledby="volume-label"
/>
<Slider defaultValue={[25, 75]} min={0} max={100} />
Props:
defaultValue: number[]
min: number
max: number
step: number
- All Radix UI Slider props
Progress
import { Progress } from '@tekton/ui';
<Progress value={50} aria-label="Upload progress" />
<Progress value={75} aria-label="Loading" />
<Progress value={100} aria-label="Complete" />
Props:
value: number (0-100)
- All Radix UI Progress props
Avatar
import { Avatar, AvatarImage, AvatarFallback } from '@tekton/ui';
<Avatar>
<AvatarImage src="https://example.com/avatar.jpg" alt="John Doe" />
<AvatarFallback>JD</AvatarFallback>
</Avatar>;
Badge
import { Badge } from '@tekton/ui';
<Badge variant="default">Default</Badge>
<Badge variant="primary">Primary</Badge>
<Badge variant="secondary">Secondary</Badge>
<Badge variant="destructive">Error</Badge>
<Badge variant="outline">Outline</Badge>
Props:
variant: "default" | "primary" | "secondary" | "destructive" | "outline"
Text
import { Text } from '@tekton/ui';
<Text size="xs">Extra small text</Text>
<Text size="sm">Small text</Text>
<Text size="md">Medium text</Text>
<Text size="lg">Large text</Text>
<Text as="p">Paragraph</Text>
<Text as="span">Inline text</Text>
<Text as="div">Block text</Text>
Props:
as: "p" | "span" | "div" | "label"
size: "xs" | "sm" | "md" | "lg"
variant: "default" | "muted"
Heading
import { Heading } from '@tekton/ui';
<Heading level={1}>Heading 1</Heading>
<Heading level={2}>Heading 2</Heading>
<Heading level={3}>Heading 3</Heading>
<Heading level={4}>Heading 4</Heading>
<Heading level={5}>Heading 5</Heading>
<Heading level={6}>Heading 6</Heading>
<Heading level={1} size="sm">Small H1</Heading>
<Heading level={2} size="lg">Large H2</Heading>
Props:
level: 1 | 2 | 3 | 4 | 5 | 6
size: "sm" | "md" | "lg"
Link
import { Link } from '@tekton/ui';
<Link href="/home" variant="default">Default Link</Link>
<Link href="/about" variant="muted">Muted Link</Link>
<Link href="/contact" variant="underline">Underlined Link</Link>
Props:
href: string
variant: "default" | "muted" | "underline"
- All native
<a> props
List
import { List, ListItem } from '@tekton/ui';
<List as="ul">
<ListItem>Item 1</ListItem>
<ListItem>Item 2</ListItem>
<ListItem>Item 3</ListItem>
</List>
<List as="ol">
<ListItem>First item</ListItem>
<ListItem>Second item</ListItem>
<ListItem>Third item</ListItem>
</List>
Props:
Image
import { Image } from '@tekton/ui';
<Image
src="https://example.com/image.jpg"
alt="Description of image"
/>
<Image
src="https://example.com/image.jpg"
alt="Cover image"
className="object-cover w-full h-64"
/>
Props:
src: string
alt: string (required for accessibility)
- All native
<img> props
Table
import {
Table,
TableHeader,
TableBody,
TableFooter,
TableRow,
TableHead,
TableCell,
TableCaption,
} from '@tekton/ui';
<Table>
<TableCaption>List of users</TableCaption>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Email</TableHead>
<TableHead>Role</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell>John Doe</TableCell>
<TableCell>john@example.com</TableCell>
<TableCell>Admin</TableCell>
</TableRow>
<TableRow>
<TableCell>Jane Smith</TableCell>
<TableCell>jane@example.com</TableCell>
<TableCell>User</TableCell>
</TableRow>
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={3}>Total: 2 users</TableCell>
</TableRow>
</TableFooter>
</Table>;
Features
CSS Variables First
All themeable properties use CSS Variables:
<Button variant="primary">
{}
</Button>
Type-Safe Variants
Powered by class-variance-authority:
<Button variant="secondary" size="lg" loading>
Click me
</Button>
Accessibility Built-In
- WCAG 2.1 AA compliant - All components pass axe-core validation
- Proper ARIA attributes - Screen reader friendly
- Keyboard navigation - Full keyboard support (Tab, Arrow keys, Enter, Escape)
- Focus management - Visible focus indicators and proper focus trapping
Zero Config Setup
Works out of the box with sensible defaults:
import { Button } from '@tekton/ui';
<Button>Just works</Button>;
Screen Templates (Phase 3)
New in Phase 3: 13 Screen Templates for rapid full-screen composition.
Overview
Screen Templates provide pre-built, theme-aware full-screen layouts that combine multiple components into cohesive user experiences. Each template is registered in a centralized TemplateRegistry for easy discovery and reuse.
Features:
- 13 Pre-built Layouts: Auth (4), Core (3), Feedback (5), Dashboard (1)
- Theme-Aware: Automatically adapts to active theme via CSS Variables
- AI-Friendly: Clear customization boundaries for AI agents
- Type-Safe: Full TypeScript support with ScreenTemplate interface
- Responsive: Desktop, Tablet, Mobile layouts
Quick Start
import { templateRegistry } from '@tekton/ui/templates';
const allTemplates = templateRegistry.getAll();
const authTemplates = templateRegistry.getByCategory('auth');
const loginTemplate = templateRegistry.get('auth.login');
Available Templates (13)
Auth Templates (4)
import {
LoginTemplate,
SignupTemplate,
ForgotPasswordTemplate,
VerificationTemplate,
} from '@tekton/ui/templates/auth';
- LoginTemplate (
auth.login) - Centered login card with email/password
- SignupTemplate (
auth.signup) - Registration form with terms acceptance
- ForgotPasswordTemplate (
auth.forgot-password) - Password reset flow
- VerificationTemplate (
auth.verification) - Email verification confirmation
Core Templates (3)
import { LandingTemplate, PreferencesTemplate, ProfileTemplate } from '@tekton/ui/templates/core';
- LandingTemplate (
home.landing) - Full-width landing page with CTA
- PreferencesTemplate (
settings.preferences) - Sidebar settings layout
- ProfileTemplate (
account.profile) - User profile with avatar editing
Feedback Templates (5)
import {
LoadingTemplate,
ErrorTemplate,
EmptyTemplate,
ConfirmationTemplate,
SuccessTemplate,
} from '@tekton/ui/templates/feedback';
- LoadingTemplate (
feedback.loading) - Loading skeleton state
- ErrorTemplate (
feedback.error) - Error state with retry button
- EmptyTemplate (
feedback.empty) - Empty state with CTA
- ConfirmationTemplate (
feedback.confirmation) - Dialog confirmation
- SuccessTemplate (
feedback.success) - Success message with next steps
Dashboard Template (1)
import { DashboardTemplate } from '@tekton/ui/templates/dashboard';
- DashboardTemplate (
dashboard.overview) - Dashboard layout with sidebar
Template Features
- Type-Safe: Full TypeScript support with
ScreenTemplate interface
- AI-Friendly: Clear customization boundaries (
slots, texts, options)
- Theme-Aware: CSS Variables for automatic theme adaptation
- Responsive: Breakpoint-based layouts (Desktop 1024px+, Tablet 768-1024px, Mobile <768px)
- Accessible: WCAG 2.1 AA compliance (validation in progress)
TemplateRegistry API
The TemplateRegistry provides centralized template management:
import { templateRegistry } from '@tekton/ui/templates/registry';
const allTemplates = templateRegistry.getAll();
const authTemplates = templateRegistry.getByCategory('auth');
const loginTemplate = templateRegistry.get('auth.login');
const formsTemplates = templateRegistry.findByRequiredComponents(['Button', 'Input', 'Form']);
Design Reference: All templates follow Claude.ai Design Philosophy - clarity, accessibility, universality.
Phase 3 Status
Completed (2026-02-01):
- ✅ Template type system (
ScreenTemplate, TemplateRegistry)
- ✅ 13 templates implemented (Auth 4, Core 3, Feedback 5, Dashboard 1)
- ✅ Template registry with category filtering
- ✅ Storybook stories for all templates
- ✅ SPEC TAG annotations (TAG-UI002-001 ~ 035)
- ⚠️ Test coverage: 17.26% (improvement planned)
Next Steps:
- Test coverage to 85%+
- Accessibility validation (axe-core, WCAG 2.1 AA)
- Additional template variants
- Template preview gallery in Storybook
CSS Variables Reference
@tekton/ui uses a 3-layer CSS Variables architecture:
Layer 1: Atomic Tokens
--color-primary: #3b82f6;
--color-destructive: #ef4444;
--spacing-sm: 0.5rem;
--radius-md: 0.375rem;
Layer 2: Semantic Tokens
--text-primary: var(--color-primary);
--bg-destructive: var(--color-destructive);
Layer 3: Component Tokens
Button Tokens:
--button-default-background
--button-default-foreground
--button-primary-background
--button-primary-foreground
--button-secondary-background
--button-secondary-foreground
--button-destructive-background
--button-destructive-foreground
--button-outline-border
--button-outline-foreground
--button-ghost-hover-background
--button-link-foreground
--button-disabled-opacity
--button-focus-ring
Input Tokens:
--input-background
--input-foreground
--input-border
--input-placeholder
--input-focus-ring
--input-disabled-background
--input-disabled-foreground
Card Tokens:
--card-background
--card-foreground
--card-border
Modal Tokens:
--modal-background
--modal-border
--modal-overlay-background
--modal-title-foreground
--modal-description-foreground
Form Tokens:
--form-label-foreground
--form-message-destructive
--form-description-foreground
Other Component Tokens:
- Badge:
--badge-*-background, --badge-*-foreground, --badge-*-border
- Dropdown:
--dropdown-*-background, --dropdown-item-*
- Tabs:
--tabs-*-background, --tabs-trigger-*
- Table:
--table-*-background, --table-border
- Progress:
--progress-background, --progress-indicator-background
- Slider:
--slider-track-background, --slider-thumb-*
- Avatar:
--avatar-background, --avatar-foreground
Custom Theme Example
@import '@tekton/ui/styles';
:root {
--color-primary: #60a5fa;
--color-background: #1f2937;
--color-foreground: #f9fafb;
--button-primary-background: var(--color-primary);
--button-primary-foreground: #000000;
--button-default-background: #374151;
--button-default-foreground: var(--color-foreground);
--input-background: #374151;
--input-foreground: var(--color-foreground);
--input-border: #4b5563;
--card-background: #374151;
--card-foreground: var(--color-foreground);
--card-border: #4b5563;
}
Development
Setup
git clone https://github.com/your-org/tekton.git
cd tekton/packages/ui
pnpm install
Scripts
pnpm test
pnpm test:coverage
pnpm test:watch
pnpm type-check
pnpm lint
pnpm build
pnpm clean
Project Structure
packages/ui/
├── src/
│ ├── primitives/ # 14 primitive components
│ │ ├── button.tsx
│ │ ├── input.tsx
│ │ ├── checkbox.tsx
│ │ └── ...
│ ├── components/ # 6 composed components
│ │ ├── card.tsx
│ │ ├── form.tsx
│ │ ├── modal.tsx
│ │ └── ...
│ ├── lib/
│ │ └── utils.ts # cn() utility
│ └── index.ts # Main export
├── __tests__/
│ ├── primitives/ # Primitive tests
│ ├── components/ # Component tests
│ ├── lib/ # Utility tests
│ └── accessibility.test.tsx # WCAG compliance
├── vitest.config.ts
├── tsconfig.json
└── package.json
Testing
All 20 components include comprehensive test coverage:
Test Coverage (98.93%)
- Statement Coverage: 98.93%
- Branch Coverage: 95.52%
- Function Coverage: 100%
- Line Coverage: 98.93%
Test Categories
Unit Tests (Vitest + Testing Library)
- Component rendering
- Props validation
- Variant behavior
- State management
- Event handling
Accessibility Tests (vitest-axe)
- WCAG 2.1 AA compliance
- ARIA attributes
- Semantic HTML
- Color contrast (disabled in jsdom)
Keyboard Navigation Tests
- Tab navigation
- Arrow key navigation (RadioGroup, Tabs, Slider)
- Enter/Space activation
- Escape key handling (Modal, Dropdown)
User Interaction Tests (userEvent)
- Click events
- Form submissions
- Input changes
- Focus management
Running Tests
pnpm test
pnpm vitest run __tests__/primitives/button.test.tsx
pnpm test:watch
pnpm test:coverage
Browser Support
- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
Note: Internet Explorer is not supported.
TypeScript Support
All components are fully typed with TypeScript 5.7+:
import { Button, ButtonProps } from '@tekton/ui';
const MyButton: React.FC<ButtonProps> = props => {
return <Button {...props} />;
};
<Button
variant="primary" // ✓ Valid
variant="invalid" // ✗ TypeScript error
size="lg" // ✓ Valid
onClick={e => {
// e is typed as MouseEvent<HTMLButtonElement>
console.log(e.currentTarget);
}}
/>;
Contributing
Contributions are welcome! Please read our contributing guidelines before submitting a PR.
Development Workflow
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature)
- Make your changes
- Run tests (
pnpm test)
- Run type check (
pnpm type-check)
- Run linter (
pnpm lint)
- Commit changes (
git commit -m 'Add amazing feature')
- Push to branch (
git push origin feature/amazing-feature)
- Open a Pull Request
Code Quality Standards
- Test Coverage: ≥90% (currently 98.93%)
- TypeScript: Strict mode, no
any types
- Accessibility: WCAG 2.1 AA compliant
- ESLint: Zero warnings/errors
- Documentation: All public APIs documented
License
MIT License - see LICENSE for details
Links
Acknowledgments
Built with:
Version: 1.0.0
Last Updated: 2026-01-26
Maintained by: Tekton Team