Installation
How to consume @cajon-ui/react inside the monorepo.
@cajon-ui/react resolves through the pnpm workspace, so there is no install step.
1. Import the stylesheet once
At your app root (for example, the App Router root layout), import the single design-system stylesheet. It ships the tokens, all three themes, dark mode, and component styles:
import '@cajon-ui/react/styles.css';2. Wrap your tree in the provider
import { ThemeProvider } from '@cajon-ui/react';
export default function RootLayout({ children }) {
return <ThemeProvider>{children}</ThemeProvider>;
}3. Use components
import { Button, Card, Badge, Input } from '@cajon-ui/react';
export function Example() {
return (
<Card header="Profile">
<Badge tone="success">Active</Badge>
<Input label="Name" placeholder="Ada Lovelace" />
<Button>Save</Button>
</Card>
);
}Fonts
The brand and default font families — Inter (sans), Plus Jakarta Sans
(default display), Roboto (default body), and Geist Mono — are
self-hosted: their woff2 ship inside the package and styles.css declares
them via @font-face. Production needs no runtime connection to Google Fonts,
so it works under a strict CSP and offline.
The 14 public "mood" themes' fonts still load from the Google Fonts CDN (also
via styles.css). If you use a mood theme, warm the connection in your <head>:
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />If you use only the tokens (@cajon-ui/react/styles/tokens-only.css) and skip
the component stylesheet, add the self-hosted faces explicitly:
import '@cajon-ui/react/styles/tokens-only.css';
import '@cajon-ui/react/styles/fonts.css';next/font opt-in (Next.js)
For zero layout shift, a Next app can load the same woff2 through
next/font/local and point a --cajon-font-* token at the generated family.
This replaces the package's @font-face for that family — use the full
styles.css for the mood themes, or tokens-only.css and skip fonts.css if
you self-host every brand family this way. Copy the woff2 from the package
(@cajon-ui/react/src/styles/fonts/) into your app's fonts/ dir so the src
path is stable under pnpm:
import localFont from 'next/font/local';
const sans = localFont({
src: [
{ path: './fonts/inter-latin-400-normal.woff2', weight: '400', style: 'normal' },
{ path: './fonts/inter-latin-600-normal.woff2', weight: '600', style: 'normal' },
],
variable: '--cajon-font-sans',
display: 'swap',
});
// add `sans.variable` to your <html> className; the token now resolves to InterThis is exactly what apps/harmonica already does with its own copies — the
opt-in just lets it (and new apps) share the design system's font files.
Next.js note
@cajon-ui/react ships TypeScript and CSS from source, so a Next.js consumer must list
it under transpilePackages:
// next.config.mjs
const config = {
transpilePackages: ['@cajon-ui/react'],
};React and react-dom are peer dependencies (range >=18), so the consuming app
supplies the single React copy — there is never a second copy in the bundle.