@yaip/yads-ui
v0.1.6
Published
A React component library built on [shadcn/ui](https://ui.shadcn.com) with layout primitives, Tailwind CSS v4, and React Server Component support.
Readme
yads-ui
A React component library built on shadcn/ui with layout primitives, Tailwind CSS v4, and React Server Component support.
Installation
pnpm add @yaip/yads-ui @phosphor-icons/reactPublish
pnpm changeset # create a changeset pnpm changeset version # bump version + changelog pnpm publish # build + publish to npm
Peer dependencies: react >= 19, react-dom >= 19, tailwindcss >= 4, @phosphor-icons/react >= 2.
Setup
In your main CSS file (e.g. globals.css, app.css):
@import "tailwindcss";
@import "@yaip/yads-ui/theme.css";
@source "../node_modules/@yaip/yads-ui/dist";@import "@yaip/yads-ui/theme.css"— Loads the full theme: CSS variables, Tailwind theme mappings (@theme inline), dark mode variant, base styles, and fonts.@source— Tells Tailwind v4 to scan the library's dist for utility classes used by components. This is required because Tailwind v4 ignoresnode_modulesby default.
Note: The
@sourcepath is relative to your CSS file. Adjust it based on where your CSS file lives in your project (e.g.../../node_modules/@yaip/yads-ui/distif your CSS is nested deeper).
Theming
ThemeProvider manages runtime theme state for light, dark, and system. First-paint theme selection is still an app-shell concern, so if you want to avoid a flash on initial load you should apply the theme before your React app hydrates.
Use the provider in your client app:
import { ThemeProvider } from "@yaip/yads-ui/client";
export function Providers({ children }: { children: React.ReactNode }) {
return <ThemeProvider defaultTheme="system">{children}</ThemeProvider>;
}Theme API:
theme— the saved user preference:light,dark, orsystemresolvedTheme— the active theme after resolvingsystemsetTheme(theme)— updates the preference and persists it to local storageforcedTheme— optionally lock the resolved theme tolightordarkfor previews, docs, or embedded demos
Preventing First-Paint Flash
The provider applies the theme after React starts. To prevent an initial flash, bootstrap the theme in your app shell:
- SSR / document-controlled apps — inject
getThemeScript()into<head>or renderThemeScriptin a document-level head/layout slot - Vite / static
index.htmlapps — place an equivalent inline script directly inindex.htmlbefore your app entry script
Framework example:
import { getThemeScript, ThemeProvider } from "@yaip/yads-ui/client";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<head>
<script
dangerouslySetInnerHTML={{ __html: getThemeScript() }}
suppressHydrationWarning
/>
</head>
<body>
<ThemeProvider>{children}</ThemeProvider>
</body>
</html>
);
}Vite example:
<!doctype html>
<html lang="en">
<head>
<script>
(() => {
const storageKey = "theme";
const defaultTheme = "system";
const root = document.documentElement;
const getSystemTheme = () =>
window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light";
let theme = defaultTheme;
try {
const stored = window.localStorage.getItem(storageKey);
if (stored === "light" || stored === "dark" || stored === "system") {
theme = stored;
}
} catch {}
const resolvedTheme = theme === "system" ? getSystemTheme() : theme;
root.style.colorScheme = resolvedTheme;
root.classList.remove("light", "dark");
root.classList.add(resolvedTheme);
})();
</script>
</head>
</html>Note: Mounting
ThemeScriptinsidecreateRoot(...)is too late to prevent first-paint flash in a plain SPA. In that case, useindex.htmlinstead.
Entry Points
yads-ui ships two entry points:
@yaip/yads-ui— Server-safe components (no client-side JS required)@yaip/yads-ui/client— Client components (require"use client"boundary in RSC frameworks). Also re-exports everything from@yaip/yads-ui.
Usage
import { Stack, Card, CardHeader, CardTitle, CardContent } from "@yaip/yads-ui"
import { Button, Dialog, DialogTrigger, DialogContent } from "@yaip/yads-ui/client"
function Example() {
return (
<Stack gap="4">
<Card>
<CardHeader>
<CardTitle>Welcome</CardTitle>
</CardHeader>
<CardContent>
<Dialog>
<DialogTrigger asChild>
<Button>Open</Button>
</DialogTrigger>
<DialogContent>Hello!</DialogContent>
</Dialog>
</CardContent>
</Card>
</Stack>
)
}Server-Safe Components (@yaip/yads-ui)
These components have zero client-side JS and work in React Server Components without a "use client" boundary.
Alert
import { Alert, AlertTitle, AlertDescription } from "@yaip/yads-ui"
<Alert>
<AlertTitle>Heads up!</AlertTitle>
<AlertDescription>This is an alert.</AlertDescription>
</Alert>AspectRatio
import { AspectRatio } from "@yaip/yads-ui"
<AspectRatio ratio={16 / 9}>
<img src="..." alt="..." />
</AspectRatio>Card
import { Card, CardHeader, CardTitle, CardContent } from "@yaip/yads-ui"
<Card>
<CardHeader>
<CardTitle>Title</CardTitle>
</CardHeader>
<CardContent>Content</CardContent>
</Card>Empty
import { Empty, EmptyTitle, EmptyDescription } from "@yaip/yads-ui"
<Empty>
<EmptyTitle>No results</EmptyTitle>
<EmptyDescription>Try a different search.</EmptyDescription>
</Empty>Group
import { Group } from "@yaip/yads-ui"
<Group gap="2">
<span>Left</span>
<span>Right</span>
</Group>Kbd
import { Kbd } from "@yaip/yads-ui"
<Kbd>⌘K</Kbd>Label
import { Label } from "@yaip/yads-ui"
<Label htmlFor="email">Email</Label>NativeSelect
import { NativeSelect, NativeSelectOption } from "@yaip/yads-ui"
<NativeSelect>
<NativeSelectOption value="a">Option A</NativeSelectOption>
<NativeSelectOption value="b">Option B</NativeSelectOption>
</NativeSelect>Pagination
import { Pagination, PaginationContent, PaginationItem, PaginationLink } from "@yaip/yads-ui"
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationLink href="#">1</PaginationLink>
</PaginationItem>
</PaginationContent>
</Pagination>Skeleton
import { Skeleton } from "@yaip/yads-ui"
<Skeleton className="h-4 w-[200px]" />Spinner
import { Spinner } from "@yaip/yads-ui"
<Spinner />Stack
import { Stack } from "@yaip/yads-ui"
<Stack gap="4">
<div>First</div>
<div>Second</div>
</Stack>Table
import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from "@yaip/yads-ui"
<Table>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell>Alice</TableCell>
</TableRow>
</TableBody>
</Table>Textarea
import { Textarea } from "@yaip/yads-ui"
<Textarea placeholder="Type here..." />Client Components (@yaip/yads-ui/client)
These components use client-side JavaScript. In RSC frameworks (Next.js, TanStack Start), import from @yaip/yads-ui/client.
Accordion
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@yaip/yads-ui/client"
<Accordion type="single" collapsible>
<AccordionItem value="item-1">
<AccordionTrigger>Is it accessible?</AccordionTrigger>
<AccordionContent>Yes.</AccordionContent>
</AccordionItem>
</Accordion>AlertDialog
import { AlertDialog, AlertDialogTrigger, AlertDialogContent, AlertDialogAction } from "@yaip/yads-ui/client"
<AlertDialog>
<AlertDialogTrigger>Open</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogAction>Continue</AlertDialogAction>
</AlertDialogContent>
</AlertDialog>Avatar
import { Avatar, AvatarImage, AvatarFallback } from "@yaip/yads-ui/client"
<Avatar>
<AvatarImage src="https://example.com/avatar.png" />
<AvatarFallback>AB</AvatarFallback>
</Avatar>Badge
import { Badge } from "@yaip/yads-ui/client"
<Badge variant="secondary">New</Badge>Breadcrumb
import { Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink } from "@yaip/yads-ui/client"
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink href="/">Home</BreadcrumbLink>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>Button
import { Button } from "@yaip/yads-ui/client"
<Button variant="outline">Click me</Button>ButtonGroup
import { ButtonGroup } from "@yaip/yads-ui/client"
<ButtonGroup>
<Button>One</Button>
<Button>Two</Button>
</ButtonGroup>Calendar
import { Calendar } from "@yaip/yads-ui/client"
<Calendar mode="single" />Carousel
import { Carousel, CarouselContent, CarouselItem } from "@yaip/yads-ui/client"
<Carousel>
<CarouselContent>
<CarouselItem>Slide 1</CarouselItem>
</CarouselContent>
</Carousel>Checkbox
import { Checkbox } from "@yaip/yads-ui/client"
<Checkbox id="terms" />Collapsible
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "@yaip/yads-ui/client"
<Collapsible>
<CollapsibleTrigger>Toggle</CollapsibleTrigger>
<CollapsibleContent>Content</CollapsibleContent>
</Collapsible>Combobox
import { Combobox, ComboboxInput, ComboboxContent, ComboboxItem } from "@yaip/yads-ui/client"
<Combobox>
<ComboboxInput placeholder="Search..." />
<ComboboxContent>
<ComboboxItem value="option">Option</ComboboxItem>
</ComboboxContent>
</Combobox>Command
import { Command, CommandInput, CommandList, CommandItem } from "@yaip/yads-ui/client"
<Command>
<CommandInput placeholder="Search..." />
<CommandList>
<CommandItem>Item</CommandItem>
</CommandList>
</Command>ContextMenu
import { ContextMenu, ContextMenuTrigger, ContextMenuContent, ContextMenuItem } from "@yaip/yads-ui/client"
<ContextMenu>
<ContextMenuTrigger>Right click</ContextMenuTrigger>
<ContextMenuContent>
<ContextMenuItem>Action</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>Dialog
import { Dialog, DialogTrigger, DialogContent, DialogTitle } from "@yaip/yads-ui/client"
<Dialog>
<DialogTrigger>Open</DialogTrigger>
<DialogContent>
<DialogTitle>Title</DialogTitle>
</DialogContent>
</Dialog>Drawer
import { Drawer, DrawerTrigger, DrawerContent } from "@yaip/yads-ui/client"
<Drawer>
<DrawerTrigger>Open</DrawerTrigger>
<DrawerContent>Content</DrawerContent>
</Drawer>DropdownMenu
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from "@yaip/yads-ui/client"
<DropdownMenu>
<DropdownMenuTrigger>Open</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>Action</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>Field
import { Field, FieldLabel, FieldDescription } from "@yaip/yads-ui/client"
<Field>
<FieldLabel>Email</FieldLabel>
<FieldDescription>Enter your email address.</FieldDescription>
</Field>HoverCard
import { HoverCard, HoverCardTrigger, HoverCardContent } from "@yaip/yads-ui/client"
<HoverCard>
<HoverCardTrigger>Hover me</HoverCardTrigger>
<HoverCardContent>Info</HoverCardContent>
</HoverCard>Input
import { Input } from "@yaip/yads-ui/client"
<Input type="email" placeholder="Email" />InputGroup
import { InputGroup, InputGroupInput, InputGroupText } from "@yaip/yads-ui/client"
<InputGroup>
<InputGroupText>@</InputGroupText>
<InputGroupInput placeholder="username" />
</InputGroup>InputOTP
import { InputOTP, InputOTPGroup, InputOTPSlot } from "@yaip/yads-ui/client"
<InputOTP maxLength={6}>
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
</InputOTPGroup>
</InputOTP>Item
import { Item, ItemTitle, ItemDescription } from "@yaip/yads-ui/client"
<Item>
<ItemTitle>Title</ItemTitle>
<ItemDescription>Description</ItemDescription>
</Item>Menubar
import { Menubar, MenubarMenu, MenubarTrigger, MenubarContent, MenubarItem } from "@yaip/yads-ui/client"
<Menubar>
<MenubarMenu>
<MenubarTrigger>File</MenubarTrigger>
<MenubarContent>
<MenubarItem>New</MenubarItem>
</MenubarContent>
</MenubarMenu>
</Menubar>NavigationMenu
import { NavigationMenu, NavigationMenuList, NavigationMenuItem, NavigationMenuLink } from "@yaip/yads-ui/client"
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuLink href="/">Home</NavigationMenuLink>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>Popover
import { Popover, PopoverTrigger, PopoverContent } from "@yaip/yads-ui/client"
<Popover>
<PopoverTrigger>Open</PopoverTrigger>
<PopoverContent>Content</PopoverContent>
</Popover>Progress
import { Progress } from "@yaip/yads-ui/client"
<Progress value={50} />RadioGroup
import { RadioGroup, RadioGroupItem } from "@yaip/yads-ui/client"
<RadioGroup defaultValue="a">
<RadioGroupItem value="a" />
<RadioGroupItem value="b" />
</RadioGroup>Resizable
import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from "@yaip/yads-ui/client"
<ResizablePanelGroup direction="horizontal">
<ResizablePanel>Left</ResizablePanel>
<ResizableHandle />
<ResizablePanel>Right</ResizablePanel>
</ResizablePanelGroup>ScrollArea
import { ScrollArea } from "@yaip/yads-ui/client"
<ScrollArea className="h-[200px]">
<div>Scrollable content</div>
</ScrollArea>Select
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from "@yaip/yads-ui/client"
<Select>
<SelectTrigger>
<SelectValue placeholder="Pick one" />
</SelectTrigger>
<SelectContent>
<SelectItem value="a">Option A</SelectItem>
</SelectContent>
</Select>Separator
import { Separator } from "@yaip/yads-ui/client"
<Separator />Sheet
import { Sheet, SheetTrigger, SheetContent } from "@yaip/yads-ui/client"
<Sheet>
<SheetTrigger>Open</SheetTrigger>
<SheetContent>Content</SheetContent>
</Sheet>Sidebar
import { SidebarProvider, Sidebar, SidebarContent } from "@yaip/yads-ui/client"
<SidebarProvider>
<Sidebar>
<SidebarContent>Nav items</SidebarContent>
</Sidebar>
</SidebarProvider>Slider
import { Slider } from "@yaip/yads-ui/client"
<Slider defaultValue={[50]} max={100} step={1} />Switch
import { Switch } from "@yaip/yads-ui/client"
<Switch />Tabs
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@yaip/yads-ui/client"
<Tabs defaultValue="tab1">
<TabsList>
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
</TabsList>
<TabsContent value="tab1">Content</TabsContent>
</Tabs>Toaster (Sonner)
import { Toaster } from "@yaip/yads-ui/client"
<Toaster />Toggle
import { Toggle } from "@yaip/yads-ui/client"
<Toggle>Bold</Toggle>ToggleGroup
import { ToggleGroup, ToggleGroupItem } from "@yaip/yads-ui/client"
<ToggleGroup type="single">
<ToggleGroupItem value="a">A</ToggleGroupItem>
<ToggleGroupItem value="b">B</ToggleGroupItem>
</ToggleGroup>Tooltip
import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from "@yaip/yads-ui/client"
<TooltipProvider>
<Tooltip>
<TooltipTrigger>Hover</TooltipTrigger>
<TooltipContent>Tooltip text</TooltipContent>
</Tooltip>
</TooltipProvider>Utilities
import { cn } from "@yaip/yads-ui"
<div className={cn("base-class", conditional && "extra-class")} />License
MIT
