@yetric/ui
v0.6.0
Published
Shared React UI foundation for Yetric applications.
Readme
@yetric/ui
A practical React component library built for modern applications — including AI-native UIs.
@yetric/ui gives you a complete set of accessible, composable components that cover everyday UI needs, advanced data patterns, and the streaming/chat interfaces that AI-powered apps demand. Provider-agnostic by design: bring your own backend, your own geocoding, your own LLM.
Install
npm install @yetric/uiWhat's included
Core components
Button, Input, Textarea, Select, Checkbox, RadioGroup, Switch, Slider, Label, Badge, Avatar, Card, Alert, Dialog, AlertDialog, Popover, Tooltip, DropdownMenu, ContextMenu, Sheet, Drawer, Tabs, Accordion, Collapsible, HoverCard, Menubar, NavigationMenu, Command, Pagination, Breadcrumb, Stepper, Timeline, Table, DataTable, Skeleton, Spinner, Progress, Separator, ScrollArea, ResizablePanel, Carousel, Calendar, InputOTP, TagsInput, MultiSelect, PinInput, Code, Toggle, ToggleGroup, Toolbar
Advanced components (0.5.0+)
DatePicker, DateRangePicker, FileUpload, Image, ColorPicker, Charts (Line/Bar/Area/Pie), EmptyState, ConfirmDialog, Toast API
Power components (0.6.0+)
DataGrid (virtualized), RichTextEditor (Tiptap), MapView (Leaflet/OpenStreetMap), CommandPalette (⌘K), PhoneInput, AddressInput (provider-agnostic), Tour, VideoPlayer, LoginForm, SignupForm
AI components (coming in 0.7.0)
AIChat, StreamingText, PromptInput, AIMessage, ThinkingIndicator, ModelSelector, SuggestionChips
Quick start
import { Button, Card, CardContent, CardHeader, CardTitle } from "@yetric/ui";
import "@yetric/ui/dist/index.css";
export function Example() {
return (
<Card>
<CardHeader>
<CardTitle>Welcome</CardTitle>
</CardHeader>
<CardContent>
<Button>Get started</Button>
</CardContent>
</Card>
);
}AI-native usage (0.7.0)
import { AIChat } from "@yetric/ui";
export function Assistant() {
return (
<AIChat
onSend={async (message) => {
const res = await fetch("/api/chat", {
method: "POST",
body: JSON.stringify({ message }),
});
return res.body; // ReadableStream
}}
/>
);
}Provider-agnostic patterns
Components like AddressInput, DataGrid, and the upcoming AI components take callbacks instead of hardcoded integrations:
// Bring your own geocoding
<AddressInput
onSearch={async (query) => {
const res = await fetch(`/api/geocode?q=${query}`);
return res.json(); // AddressSuggestion[]
}}
/>
// Bring your own LLM
<AIChat onSend={(msg) => streamFromAnyProvider(msg)} />Tech stack
The intended foundation is:
React
TypeScript
Radix UI primitives
Tailwind CSS
class-variance-authority
tailwind-merge
Storybook
Vitest
React Testing Library
tsup or Vite library modeDesign approach
The library should use proven accessible primitives for behavior-heavy components and focus our own work on visual design, API consistency, composition, and documentation.
Build ourselves:
component APIs
styling conventions
variants
design tokens
layout primitives
documentation
business-friendly usage examplesAvoid rebuilding from scratch:
focus trapping
keyboard navigation
ARIA behavior
popover positioning
menu behavior
dialog behavior
combobox behavior
screen reader edge casesComponent principles
- Keep core components domain-free.
- Prefer composition over large configuration objects.
- Use accessible primitives for complex behavior.
- Keep APIs boring and predictable.
- Use TypeScript types as part of the component contract.
- Use Storybook to document real usage.
- Add at least basic tests for every reusable component.
- Do not put app-specific business logic in
@yetric/ui. - Business components should live in a separate package or app layer.
Suggested package structure
packages/
ui/
src/
components/
button/
Button.tsx
Button.test.tsx
Button.stories.tsx
index.ts
card/
Card.tsx
Card.stories.tsx
index.ts
dialog/
Dialog.tsx
Dialog.stories.tsx
index.ts
lib/
cn.ts
styles/
globals.css
index.ts
package.json
tsconfig.json
tailwind.config.ts
tsup.config.tsIf this repository starts as a single-package repo instead of a monorepo, the same structure can live directly under src/.
Initial components
The first version should validate the foundation with a small set of components:
Button
Card
DialogThese are enough to prove:
- styling conventions
- variants
- composition
- Radix wrapping
- exports
- Storybook setup
- testing setup
- package build
Example usage
import { Button, Card, CardContent, CardHeader, CardTitle } from "@yetric/ui";
export function Example() {
return (
<Card>
<CardHeader>
<CardTitle>Welcome</CardTitle>
</CardHeader>
<CardContent>
<Button>Get started</Button>
</CardContent>
</Card>
);
}Styling utility
The library should include a small cn helper for combining conditional classes and resolving Tailwind conflicts.
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}Component variants
Use class-variance-authority for variant-heavy components.
Example:
<Button variant="primary" size="md">
Save
</Button>
<Button variant="secondary" size="sm">
Cancel
</Button>
<Button variant="danger">
Delete
</Button>Scripts
Suggested scripts:
{
"scripts": {
"build": "tsup src/index.ts --format esm --dts",
"dev": "tsup src/index.ts --format esm --dts --watch",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build",
"test": "vitest",
"test:run": "vitest run",
"typecheck": "tsc --noEmit"
}
}Adjust scripts to match the final repository setup.
Development
Install dependencies:
npm installRun Storybook:
npm run storybookRun tests:
npm run testBuild the library:
npm run buildRun type checking:
npm run typecheckPublic exports
All public components should be exported from src/index.ts.
Example:
export * from "./components/button";
export * from "./components/card";
export * from "./components/dialog";
export * from "./lib/cn";Consumers should not import from deep internal paths unless explicitly supported.
Preferred:
import { Button } from "@yetric/ui";Avoid:
import { Button } from "@yetric/ui/src/components/button/Button";Adding a new component
When adding a component:
- Create a folder under
src/components/<component-name>/. - Add the component implementation.
- Add an
index.tsfile for local exports. - Add Storybook stories.
- Add basic tests.
- Export the component from
src/index.ts. - Document variants and important usage notes.
Recommended structure:
src/components/input/
Input.tsx
Input.test.tsx
Input.stories.tsx
index.tsWhat belongs here
Good candidates for @yetric/ui:
Button
Input
Textarea
Checkbox
RadioGroup
Select
Dialog
DropdownMenu
Tabs
Tooltip
Popover
Card
Badge
Alert
Table primitives
Typography helpers
Layout primitivesWhat does not belong here
Avoid adding business-specific components to this package.
Examples that should probably live elsewhere:
AccountSelector
PaymentReceiverSelector
PayAmountField
BookingCalendar
RestaurantTableMap
PropertyValuationForm
LeadCaptureFlowThose components can still use @yetric/ui, but they should live in app-specific packages or a separate business UI package.
Possible future package:
@yetric/ui-businessAccessibility
Accessibility is a core reason for using primitives like Radix UI.
Components should support:
- keyboard navigation
- focus states
- disabled states
- accessible names and labels
- screen reader-friendly structure
- sensible default semantics
Do not remove accessibility behavior from underlying primitives unless there is a very good reason.
Testing expectations
Each reusable component should have at least basic tests.
Minimum expectations:
- renders without crashing
- renders children/content
- supports important variants
- supports disabled state where relevant
- supports basic interaction where relevant
For behavior-heavy components, test the behavior that matters to consumers.
Example:
Dialog opens when trigger is clicked.
Dialog content is visible when open.
Button is disabled when disabled=true.Storybook expectations
Each component should have stories for:
- default state
- variants
- sizes, if applicable
- disabled/loading/error states, if applicable
- realistic usage example
Storybook should be treated as the main component documentation.
Versioning
Use semantic versioning once the package is consumed by real applications.
patch: bug fixes and internal improvements
minor: new components or backwards-compatible additions
major: breaking API or styling contract changesFuture direction
Possible future additions:
@yetric/ui-icons
@yetric/ui-theme
@yetric/ui-forms
@yetric/ui-business
internal component registry
visual regression testing
theme switching
React Hook Form adapters
application shell componentsDo not add these until there is a real need.
Guiding idea
Start small. Keep it useful. Avoid building a museum of abstractions.
The best version of this library is boring, predictable, accessible, and easy to use.
