Installation

Add @noarka/ui to any React 19 project styled with Tailwind CSS v4. Then import the design tokens once and start rendering components. To customise the palette, fonts, or radius, see the Theming page.

1.

Install the packages

The library itself plus its peer dependencies:

npm install @noarka/ui @base-ui/react @solar-icons/react class-variance-authority clsx tailwind-merge
2.

Import the design tokens

Add both stylesheets once in your global CSS file. This is also where Tailwind v4 picks up the theme. The @source line registers the library's classes, which Tailwind would otherwise skip inside node_modules.

css
@import "@noarka/ui/styles/tokens.css";@import "@noarka/ui/styles/base.css";@source "../node_modules/@noarka/ui/dist/**/*.js";
3.

Or paste the tokens directly

Prefer not to import the stylesheets? Copy these two blocks into your own global CSS instead. They are the same tokens the library ships, split into a light default and a dark override, so your app gets both modes with no extra config. Toggle by adding or removing the dark class on <html>.

You still need the @source line from step 2 so Tailwind scans the library's classes.

Light

css
:root {  --font-serif: var(--font-bricolage), ui-serif, Georgia, serif;  --solar-stroke-width: 2;  --background: oklch(1 0 0);  --foreground: oklch(0.148 0.004 228.8);  --card: oklch(1 0 0);  --card-foreground: oklch(0.148 0.004 228.8);  --popover: oklch(1 0 0);  --popover-foreground: oklch(0.148 0.004 228.8);  --primary: #DA7756;  --primary-foreground: oklch(0.97 0.014 254.604);  --secondary: oklch(0.967 0.001 286.375);  --secondary-foreground: oklch(0.21 0.006 285.885);  --muted: oklch(0.963 0.002 197.1);  --muted-foreground: oklch(0.56 0.021 213.5);  --accent: oklch(0.963 0.002 197.1);  --accent-foreground: oklch(0.218 0.008 223.9);  --destructive: oklch(0.577 0.245 27.325);  --destructive-foreground: oklch(0.92 0.06 27.325);  --success: hsl(142 54% 54%);  --success-foreground: hsl(142 80% 95%);  --warning: hsl(36 35% 54%);  --warning-foreground: hsl(36 70% 95%);  --dead: hsl(0 0% 42%);  --dead-foreground: hsl(0 0% 92%);  --border: oklch(0.925 0.005 214.3);  --input: oklch(94.007% 0.00011 271.152);  --ring: oklch(0.723 0.014 214.4);  --chart-1: oklch(0.872 0.007 219.6);  --chart-2: oklch(0.56 0.021 213.5);  --chart-3: oklch(0.45 0.017 213.2);  --chart-4: oklch(0.378 0.015 216);  --chart-5: oklch(0.275 0.011 216.9);  --radius: 0.625rem;  --sidebar: #f1f3f4;  --sidebar-foreground: oklch(0.148 0.004 228.8);  --sidebar-primary: oklch(0.55 0.22 263);  --sidebar-primary-foreground: oklch(0.97 0.014 254.604);  --sidebar-accent: #e2e6ea;  --sidebar-accent-foreground: oklch(0.218 0.008 223.9);  --sidebar-border: oklch(0.925 0.005 214.3);  --sidebar-ring: oklch(0.723 0.014 214.4);  --surface: oklch(100% 0.00011 271.152);} 

Dark

css
.dark {  --background: oklch(16.9% 0.002 286.2);  --foreground: oklch(96% 0.011 286.2);  --card: oklch(20.1% 0.002 286.2);  --card-foreground: oklch(0.987 0.002 197.1);  --popover: oklch(20.1% 0.002 286.2);  --popover-foreground: oklch(0.987 0.002 197.1);  --primary: oklch(67.4% 0.132 39.1);  --primary-foreground: oklch(0.97 0.014 254.604);  --secondary: oklch(23.1% 0.002 286.3);  --secondary-foreground: oklch(0.985 0 0);  --muted: oklch(23.1% 0.002 286.3);  --muted-foreground: oklch(70.811% 0.01569 264.51);  --accent: oklch(25.3% 0.004 286.1);  --accent-foreground: oklch(0.987 0.002 197.1);  --destructive: oklch(58.6% 0.123 21.5);  --success: oklch(74.6% 0.161 152.4);  --warning: oklch(67.9% 0.077 76.8);  --border: oklch(100% 0.00011 271.152 / 0.034);  --input: oklch(23.1% 0.002 286.3);  --ring: oklch(100% 0.00011 271.152 / 0.034);  --chart-1: oklch(0.872 0.007 219.6);  --chart-2: oklch(0.56 0.021 213.5);  --chart-3: oklch(0.45 0.017 213.2);  --chart-4: oklch(0.378 0.015 216);  --chart-5: oklch(0.275 0.011 216.9);  --sidebar: oklch(18.7% 0.002 286.2);  --sidebar-foreground: oklch(0.987 0.002 197.1);  --sidebar-primary: oklch(54.76% 0.22148 263.278 / 0.192);  --sidebar-primary-foreground: oklch(0.97 0.014 254.604);  --sidebar-accent: oklch(23.1% 0.002 286.3);  --sidebar-accent-foreground: oklch(0.987 0.002 197.1);  --sidebar-border: oklch(1 0 0 / 10%);  --sidebar-ring: oklch(53.492% 0.02466 268.236);  --surface: oklch(22.3% 0.002 286.2);}
4.

Use the components

Import primitives directly, no providers or setup required (wrap tooltips in a TooltipProvider once near the root of your app).

tsx
import { Button, Card, CardHeader, CardTitle, Input, Label } from "@noarka/ui";export function Example() {  return (    <Card>      <CardHeader>        <CardTitle>Invite a teammate</CardTitle>      </CardHeader>      <div className="space-y-1.5 px-4">        <Label htmlFor="email">Email</Label>        <Input id="email" placeholder="teammate@company.com" />      </div>      <div className="p-4">        <Button render={<a href="/team">Send invite</a>} />      </div>    </Card>  );}
5.

Render as other elements

Every interactive primitive accepts render to swap the underlying element, this replaces Radix-style asChild. Non-button elements automatically opt out of native button semantics.

6.

Theme per app

Everything is a CSS variable. Scope overrides under any selector:

css
[data-theme="acme"] {  --primary: oklch(0.65 0.2 150);}

The full token list, radius scale, and font slots are covered on the Theming page.