@hikari/ui-web
Shared web component library for all Next.js frontends in the monorepo. Built on shadcn/ui with Radix primitives, styled via Tailwind CSS v4 and class-variance-authority (CVA).
Storybook: ui-web-storybook.pages.dev
Installation
Already available to all workspace apps. Add the dependency:
{
"dependencies": {
"@hikari/ui-web": "workspace:*"
}
}
Import the stylesheet in your app's root CSS:
@import "@hikari/ui-web/styles.css";
Usage
import {
Button,
Card,
CardHeader,
CardTitle,
CardContent,
} from "@hikari/ui-web";
function MyComponent() {
return (
<Card>
<CardHeader>
<CardTitle>Dashboard</CardTitle>
</CardHeader>
<CardContent>
<Button variant="outline" size="sm">
View Details
</Button>
</CardContent>
</Card>
);
}
Deep imports
For tree-shaking or to avoid pulling in the full barrel export:
import { Button } from "@hikari/ui-web/components/ui/button";
Utility function
import { cn } from "@hikari/ui-web";
<div className={cn("flex gap-2", isActive && "bg-accent")} />;
Components (56)
| Category | Components |
|---|---|
| Layout | AspectRatio, Card, Carousel, Collapsible, Direction, Resizable, ScrollArea, Separator, Sidebar |
| Navigation | Breadcrumb, Menubar, NavigationMenu, Pagination, Tabs |
| Forms | Button, ButtonGroup, Calendar, Checkbox, Combobox, Field, Form, Input, InputGroup, InputOTP, Label, NativeSelect, RadioGroup, Select, Slider, Switch, Textarea, Toggle, ToggleGroup |
| Data Display | Avatar, Badge, Chart, Empty, Item, Kbd, Progress, Skeleton, Spinner, Table |
| Feedback | Alert, Sonner (Toast) |
| Overlays | AlertDialog, Command, ContextMenu, Dialog, Drawer, DropdownMenu, HoverCard, Popover, Sheet, Tooltip |
Theming
The package uses CSS custom properties for theming. All color tokens are defined in src/styles.css using oklch values with light/dark mode support.
Key tokens: --background, --foreground, --primary, --secondary, --muted, --accent, --destructive, --border, --input, --ring, plus chart and sidebar variants.
Dark mode activates via the .dark class on an ancestor element.
Per-app customization
Each app overrides the default theme by redefining CSS custom properties after importing the package stylesheet. The CSS cascade ensures app-specific values win.
Step 1 — Import order in your layout:
// app/layout.tsx
import "@hikari/ui-web/styles.css"; // default tokens + Tailwind v4 setup
import "./globals.css"; // app overrides (loaded second → wins)
The package stylesheet includes @import "tailwindcss", @source "./components", and all shadcn/Tailwind v4 setup. Your app does not need its own @import "tailwindcss" or @source for ui-web components — the package handles both. The @tailwindcss/postcss plugin auto-detects your app's own TSX files for Tailwind class scanning.
If your app uses other packages with Tailwind classes (e.g., @hikari/schema-form-adapter-ui-web), add an @source directive in your app's CSS for those packages:
/* globals.css — only needed for packages NOT covered by styles.css */
@source "../../../../packages/schema-form-adapter-ui-web/src/**/*.tsx";
Step 2 — Override tokens in your app CSS:
/* app/globals.css */
/* Light mode overrides */
:root {
--primary: #f97316; /* orange instead of default gray */
--primary-foreground: #ffffff;
--background: #fafaf9;
--foreground: #1c1917;
--accent: #f5f5f4;
--ring: #f97316;
--radius: 0.5rem;
/* ... override as many or as few tokens as needed */
}
/* Dark mode overrides */
.dark {
--primary: #fb923c;
--primary-foreground: #0c0a09;
--background: #0f0e0d;
--foreground: #ededec;
/* ... */
}
You only need to override the tokens you want to change — any token not redefined falls back to the package default.
Dark mode class: ui-web components use the .dark class on an ancestor element (via @custom-variant dark (&:is(.dark *)) in Tailwind v4). If your app uses data-theme="dark" for theme switching, sync the .dark class with a script:
// layout.tsx — sync .dark class from data-theme attribute
const darkClassSync = `
(function(){
function s(){document.documentElement.classList.toggle("dark",document.documentElement.dataset.theme==="dark")}
s();
new MutationObserver(s).observe(document.documentElement,{attributes:true,attributeFilter:["data-theme"]});
})();
`;
// Add as: <script dangerouslySetInnerHTML={{ __html: darkClassSync }} />
Step 3 — Add app-specific tokens (optional):
Apps can define their own tokens alongside the shadcn ones for custom UI elements:
:root {
/* App-specific tokens (not from ui-web) */
--ws-accent: #f97316;
--ws-accent-soft: #fff7ed;
--page-bg: #fafaf9;
--sidebar-bg: #f5f5f4;
}
Available tokens
| Token | Purpose |
|---|---|
--background / --foreground | Page background and text |
--card / --card-foreground | Card surfaces |
--popover / --popover-foreground | Popover/dropdown surfaces |
--primary / --primary-foreground | Primary buttons and actions |
--secondary / --secondary-foreground | Secondary buttons |
--muted / --muted-foreground | Muted backgrounds and text |
--accent / --accent-foreground | Accent highlights |
--destructive / --destructive-foreground | Error/danger states |
--border | Default border color |
--input | Input field borders |
--ring | Focus ring color |
--radius | Base border radius |
--chart-1 through --chart-5 | Chart color palette |
--sidebar-* | Sidebar-specific variants |
Example: workspace-web
See apps/workspace-web/ for a real example. Its globals.css overrides the default grayscale theme with a warm stone palette and orange accent, while DESIGN-SYSTEM.md documents the full design language. Every app with significant UI should have its own DESIGN-SYSTEM.md.
Adding or upgrading components
# Add a new shadcn component
pnpm --filter @hikari/ui-web ui:add
# Upgrade existing components to latest shadcn
pnpm --filter @hikari/ui-web ui:upgrade
Tech stack
- Primitives: Radix UI (via radix-ui and @base-ui/react)
- Styling: Tailwind CSS v4, class-variance-authority, tailwind-merge
- Charts: Recharts
- Forms: React Hook Form + Zod
- Carousel: Embla Carousel
- Drawer: Vaul
- Toast: Sonner
- Icons: Lucide React
Storybook
The component library is documented and tested via Storybook 10:
- App:
apps/ui-web-storybook - Framework:
@storybook/nextjs-vite - URL: ui-web-storybook.pages.dev
- Coverage: 56 story files, 103 stories covering all variants and sizes
- Tests: Vitest storybook integration tests run per-story in Chromium