Skip to main content

@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)

CategoryComponents
LayoutAspectRatio, Card, Carousel, Collapsible, Direction, Resizable, ScrollArea, Separator, Sidebar
NavigationBreadcrumb, Menubar, NavigationMenu, Pagination, Tabs
FormsButton, ButtonGroup, Calendar, Checkbox, Combobox, Field, Form, Input, InputGroup, InputOTP, Label, NativeSelect, RadioGroup, Select, Slider, Switch, Textarea, Toggle, ToggleGroup
Data DisplayAvatar, Badge, Chart, Empty, Item, Kbd, Progress, Skeleton, Spinner, Table
FeedbackAlert, Sonner (Toast)
OverlaysAlertDialog, 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

TokenPurpose
--background / --foregroundPage background and text
--card / --card-foregroundCard surfaces
--popover / --popover-foregroundPopover/dropdown surfaces
--primary / --primary-foregroundPrimary buttons and actions
--secondary / --secondary-foregroundSecondary buttons
--muted / --muted-foregroundMuted backgrounds and text
--accent / --accent-foregroundAccent highlights
--destructive / --destructive-foregroundError/danger states
--borderDefault border color
--inputInput field borders
--ringFocus ring color
--radiusBase border radius
--chart-1 through --chart-5Chart 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