Skip to content
Noksha UIv0.1
Colour theme

Getting started

Installation

One package and one CSS import. No provider is required, and no configuration file.

Two

Required steps

None

Config files to write

None

Providers to wrap

1

Install the package

pnpm add @noksha-ui/react

react and react-dom (>=18) are peer dependencies. Nothing else is required.

2

Import the stylesheet

This one file carries the tokens, the theme, the animations and the tone rules. It is generated from the token engine, so it cannot drift from what the components expect.

Two imports and nothing else. The stylesheet registers its own sources, so you never touch a config file.

/* app.css — the only stylesheet you import */
@import 'tailwindcss';
@import '@noksha-ui/react/styles.css';
3

Stop the dark-mode flashOptional — Skip it if your app is light-only.

themeScript() returns a small string that runs before anything paints and stamps the theme onto <html>. Without it, a dark-mode visitor sees a white frame on every load, because React has not started yet.

It ships from an entry with no "use client" directive, so a Server Component can call it during its own render.

// app/layout.tsx
import { themeScript } from '@noksha-ui/react/theme-script';

export default function RootLayout({ children }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <head>
        <script dangerouslySetInnerHTML={{ __html: themeScript() }} />
      </head>
      <body>{children}</body>
    </html>
  );
}
4

Add a theme toggleOptional — Only if users switch themes.

Theming itself needs no provider — the tokens are CSS variables. ThemeProvider exists solely to switch modes and keep tabs in sync.

// app/providers.tsx
'use client';

import { ThemeProvider } from '@noksha-ui/react';

export function Providers({ children }: { children: React.ReactNode }) {
  return <ThemeProvider>{children}</ThemeProvider>;
}
// components/theme-toggle.tsx
'use client';

import { Button, useTheme } from '@noksha-ui/react';

export function ThemeToggle() {
  const { resolvedTheme, toggle } = useTheme();

  return (
    <Button variant="ghost" tone="neutral" onClick={toggle}>
      {resolvedTheme === 'dark' ? 'Light mode' : 'Dark mode'}
    </Button>
  );
}
5

Check it worked

Drop this on any page. You should get a filled button in the brand colour, with a visible focus ring when you press Tab.

import { Button } from '@noksha-ui/react';

export default function Page() {
  // A filled button in your brand colour, with a visible focus ring on Tab.
  // If it is unstyled, Tailwind is not seeing the package — see Troubleshooting.
  return <Button>It works</Button>;
}

Using it from a Server Component

Every component is importable from a server file — it simply becomes a client component, which it is. One rule: use the flat exports.

Works in a server file

// app/page.tsx — a Server Component
import { CardRoot, CardHeader, CardTitle, CardContent } from '@noksha-ui/react';

export default function Page() {
  return (
    <CardRoot>
      <CardHeader><CardTitle>Works</CardTitle></CardHeader>
      <CardContent>Flat exports cross the server/client boundary.</CardContent>
    </CardRoot>
  );
}

Does not

// app/page.tsx — a Server Component
import { Card } from '@noksha-ui/react';

export default function Page() {
  // Card.Root is undefined here: a "use client" module's exports arrive as
  // opaque client references, and a server file cannot read through one.
  return <Card.Root>…</Card.Root>;
}

The Card.Root namespace form is a convenience for client components, where it works fine. This is a constraint of React Server Components, not of Noksha — it applies to any library that exports a namespace object from a client module. Every component ships both forms, so the flat one is always available.

Troubleshooting

Three things account for nearly every report. Each has a one-line fix.

Components render, but with no styling at all

Tailwind is not seeing the package. v4 skips node_modules when it scans for classes; the shipped stylesheet registers itself, but a pnpm or monorepo layout can still defeat it.

/* app.css */
@import 'tailwindcss';
@import '@noksha-ui/react/styles.css';

/* Tailwind v4 skips node_modules when it scans for classes. The shipped
   stylesheet registers itself, but a strict setup may still need this. */
@source '../node_modules/@noksha-ui/react/dist';

Dark mode works on the page, but dialogs, drawers and tooltips stay light

Overlays render into document.body to escape clipping and stacking contexts, so a theme declared on a wrapper below <html> used to be left behind. It no longer is — the overlay reads the mode where it logically sits and carries it through the portal.

// The marker can sit anywhere, not only on <html>.
// Overlays portal to <body>, and they carry this scope with them.
<div className="dark">
  <Drawer.Root>…</Drawer.Root>
</div>

If you are pinned to an older version, the fix is to move the dark class onto <html>, which is what themeScript() does for you in step 3.

Card.Root is undefined, or the build says a component is not a function

A Server Component is reading through a namespace object. Import the flat export instead — CardRoot rather than Card.Root.

Next