@kb2b/ui
v0.1.0
Published
Shared UI primitives + design tokens for kb2b-web and kb2b-desktop. CSS-only consumers (Cloudflare Pages, no bundler) import tokens.css/theme.css directly. Tailwind v4 + React consumers import components from the package root.
Maintainers
Readme
@kb2b/ui
Shared UI primitives + design tokens for kb2b-web and kb2b-desktop.
What's in the box
tokens.css— CSS variables for light + dark modes (OKLCH).theme.css— Tailwind v4@theme inlinemapping (Tailwind classes resolve to tokens).tokens.json— Machine-readable token manifest. Use for autocomplete tooling or stylelint validation.- 10 React components — Portable shadcn-style primitives built on
@base-ui-components/react:Button,Card,Badge,Input,Sheet,Dialog,DropdownMenu,Tooltip,Tabs,Separator.
Quickstart by stack
Vite + React + Tailwind v4 (kb2b-desktop)
npm install @kb2b/ui tailwindcss @tailwindcss/vite @base-ui-components/react lucide-reactvite.config.ts (or electron.vite.config.ts renderer block):
import tailwindcss from "@tailwindcss/vite";
export default {
plugins: [tailwindcss()],
};src/styles.css:
@import "tailwindcss";
@import "@kb2b/ui/tokens.css";
@import "@kb2b/ui/theme.css";Use components:
import { Button } from "@kb2b/ui/components/button";
<Button variant="default">Save</Button>;Toggle dark mode:
document.documentElement.classList.toggle("dark");Next.js + Tailwind v4 (kb2b-web)
npm install @kb2b/uiapp/globals.css:
@import "tailwindcss";
@import "@kb2b/ui/tokens.css";
@import "@kb2b/ui/theme.css";The rest works the same as the Vite quickstart.
CSS-only / no bundler (e.g. Cloudflare Pages, scipot-landing)
<link rel="stylesheet" href="https://cdn/path/to/@kb2b/ui/tokens.css">You get the CSS variables. No Tailwind, no components — just consistent colors and radii.
Order matters
Always import in this order. Reversing it breaks the cascade:
tailwindcss@kb2b/ui/tokens.css@kb2b/ui/theme.css
Reason: theme.css references the --background, --brand, etc. variables defined in tokens.css. Tailwind needs both before it can resolve bg-background, text-brand-foreground, etc.
Override recipe (per-tenant or local custom)
/* Your app's own CSS, after the @kb2b/ui imports */
:root {
--brand: oklch(0.65 0.15 280); /* override brand to purple */
}For dark-mode override, scope to .dark:
.dark {
--brand: oklch(0.75 0.18 280);
}If you need a fully isolated theme island (e.g., one widget on one route), use a data attribute selector:
[data-theme="alt"] {
--brand: oklch(0.65 0.15 50);
}Design system sourcing
The tokens reflect the decisions documented in kb2b/DESIGN.md. Any change to those decisions starts there, then propagates here. We do not invent tokens in this package — we mirror.
Component opinions (the 10 primitives)
| Component | Built on | What it does |
|---|---|---|
| Button | @base-ui-components/react/button | Variants: default, outline, secondary, ghost, destructive, link. Sizes: xs/sm/default/lg + icon variants. |
| Card | native <div> | Layout container with CardHeader, CardTitle, CardDescription, CardContent, CardFooter, CardAction sub-parts. |
| Badge | native <span> | Variants: default, secondary, destructive, outline, brand. |
| Input | native <input> | Themed text input. Honors aria-invalid. |
| Sheet | @base-ui-components/react/dialog | Side panel (left/right/top/bottom). Use for mobile nav, settings drawers. |
| Dialog | @base-ui-components/react/dialog | Modal dialog. Includes overlay, content, header, footer, close button. |
| DropdownMenu | @base-ui-components/react/menu | Menu with items, sub-menus, separators, shortcuts. |
| Tooltip | @base-ui-components/react/tooltip | Hover tooltip with arrow. |
| Tabs | @base-ui-components/react/tabs | Horizontal/vertical tabs. |
| Separator | native <div> | Horizontal or vertical divider. |
Versioning
0.x— public API may change without semver bumps. Pin tightly ("@kb2b/ui": "0.1.0").1.0— when the API is stable across all 3 active consumers (kb2b-web, kb2b-desktop, scipot-landing).
Distribution
- v0.x:
file:../kb2b-uilink from sibling repos in thescipot/workspace. - v0.5+: GitHub Packages (or npm private scope) once a third consumer arrives.
