Skip to main content
ninna-ui

Theming

Learn how to customize colors, switch themes, and enable dark mode.

Built-in Themes

Ninna UI ships with 5 built-in themes. Each theme defines all 31 CSS variables for light and dark mode.

defaultPurple / Magenta

Vibrant electric purple with cool slate surfaces

oceanBlue / Cyan

Deep ocean blues with cool undertones

sunsetOrange / Rose

Warm sunset tones for bold designs

forestGreen / Amber

Natural greens with earthy accents

minimalMonochrome

Near-black primary, low chroma for minimal UIs

Switching Themes

Change your entire theme by swapping a single CSS import line.

1. Change the CSS import

/* 1. Change the CSS import */
@import "tailwindcss";
@import "@ninna-ui/core/theme/presets/ocean.css";
@variant dark (&:is(.dark *));

2. Set data-theme on your root element

<!-- 2. Set data-theme on your root element -->
<html data-theme="ocean">
<!-- Or import all presets and switch at runtime -->
<html data-theme="ocean"> <!-- change this attribute to switch theme -->

Available themes: default.css, ocean.css, sunset.css, forest.css, minimal.css — all can be imported together for per-section theming.

Dark Mode

Dark mode works automatically via CSS variables. No dark: classes needed anywhere.

How Dark Mode Works

Every built-in theme includes both light and dark color definitions. The dark mode CSS selector uses .dark [data-theme="default"] or [data-theme="default"].dark to redefine all CSS variables — no dark: utility classes needed anywhere. A data-theme attribute is always required on <html> (or any ancestor element).

System Preference (Auto)

All themes include a @media (prefers-color-scheme: dark) block. When no class is set on <html>, dark mode activates automatically based on OS preference. Adding class="light" forces light mode and blocks the media query.

Class-Based Toggle

Add the dark class to your root element for manual control.

<!-- Forced dark mode -->
<html data-theme="default" class="dark">
<!-- Forced light mode (blocks OS preference) -->
<html data-theme="default" class="light">
<!-- System preference (auto) — no class, follows OS -->
<html data-theme="default">
<!-- Note: data-theme is always required -->

JavaScript Toggle

// Three modes: 'dark', 'light', 'system'
function setTheme(mode) {
const root = document.documentElement;
root.classList.remove('dark', 'light');
if (mode === 'dark') {
root.classList.add('dark'); // forces dark via CSS selector
} else if (mode === 'light') {
root.classList.add('light'); // forces light, blocks @media query
}
// 'system': no class — @media (prefers-color-scheme: dark) handles it
localStorage.setItem('theme', mode);
}
// Restore on page load (run before React hydrates to avoid flash)
const saved = localStorage.getItem('theme') || 'system';
setTheme(saved);

CSS Variables

All 31 CSS variables that define a Ninna UI theme.

VariableTailwind ClassUsage
--color-primarybg-primary, text-primaryMain brand color
--color-primary-contenttext-primary-contentText on primary backgrounds
--color-secondarybg-secondary, text-secondarySecondary brand color
--color-accentbg-accent, text-accentAccent/highlight color
--color-neutralbg-neutral, text-neutralNeutral/muted color
--color-successbg-success, text-successSuccess states
--color-dangerbg-danger, text-dangerError/danger states
--color-warningbg-warning, text-warningWarning states
--color-infobg-info, text-infoInformational states
--color-base-50 to -900bg-base-50 to bg-base-900Surface scale (10 steps)
--color-base-contenttext-base-contentDefault text color
--color-borderborder-borderDefault border color

Per-Section Theming

Apply different themes to different sections of your page using the data-theme attribute.

Scope themes to specific sections

<!-- Per-section theming with data-theme attribute -->
<div data-theme="default">
<Button color="primary">Default Theme Button</Button>
</div>
<div data-theme="ocean">
<Button color="primary">Ocean Theme Button</Button>
</div>
<div data-theme="sunset">
<Button color="primary">Sunset Theme Button</Button>
</div>

Note: Import all presets you need and use data-theme to scope each section. Each preset only activates when its matching data-theme value is present, so multiple presets can coexist without conflicts.

Custom Themes

Create your own theme by defining all 31 CSS variables.

Copy any built-in theme and modify the oklch values to match your brand. Each color uses theoklch(lightness chroma hue) format from the CSS Color Level 4 spec.

Custom theme template

/* ===== Light Theme ===== */
[data-theme="my-brand"] {
color-scheme: light;
/* Primary — your brand color */
--color-primary: oklch(0.49 0.27 250);
--color-primary-content: oklch(0.93 0.04 250);
/* Secondary */
--color-secondary: oklch(0.50 0.24 300);
--color-secondary-content: oklch(0.93 0.04 300);
/* Accent */
--color-accent: oklch(0.75 0.18 183);
--color-accent-content: oklch(0.16 0.04 183);
/* Neutral */
--color-neutral: oklch(0.21 0.03 265);
--color-neutral-content: oklch(0.93 0.01 265);
/* Base surfaces — light (L: 0.98 → 0.20) */
--color-base-50: oklch(0.985 0.006 260);
--color-base-100: oklch(0.970 0.008 260);
--color-base-200: oklch(0.940 0.010 260);
--color-base-300: oklch(0.900 0.014 260);
--color-base-content: oklch(0.200 0.014 260);
/* ... base-400 through base-900, border colors */
}
/* ===== Dark Theme (explicit .dark class) ===== */
/* Applies when <html class="dark" data-theme="my-brand"> */
.dark [data-theme="my-brand"],
[data-theme="my-brand"].dark {
color-scheme: dark;
/* Colors lighten in dark mode for contrast */
--color-primary: oklch(0.74 0.24 250);
--color-primary-content: oklch(0.15 0.03 250);
--color-neutral: oklch(0.70 0.03 265);
--color-neutral-content: oklch(0.15 0.01 265);
/* Base surfaces — dark (L: 0.14 → 0.93) */
--color-base-50: oklch(0.140 0.012 260);
--color-base-100: oklch(0.180 0.014 260);
--color-base-200: oklch(0.230 0.016 260);
--color-base-300: oklch(0.310 0.018 260);
--color-base-content: oklch(0.930 0.012 260);
/* ... rest of dark overrides */
}
/* ===== Dark Theme (system preference) ===== */
/* Applies when no .dark/.light class and OS prefers dark */
@media (prefers-color-scheme: dark) {
[data-theme="my-brand"]:not(.light):not(.dark) {
color-scheme: dark;
/* ... same dark overrides as above ... */
}
}

Tip: Use the Tailwind CSS oklch palette as a reference. Light mode typically uses -600 shades and dark mode uses -400 shades for semantic colors.

CodeBlock Theming

Customize syntax highlighting colors using CSS custom properties or control the color scheme per instance via prop.

Color Scheme Prop

Each CodeBlock can independently control its light/dark syntax colors via thecolorScheme prop — useful when embedding code inside a dark hero section on an otherwise light page, or vice versa.

import { CodeBlock } from "@ninna-ui/code-block";
{/* Always dark, even inside a light-mode page */}
<CodeBlock code={myCode} colorScheme="dark" />
{/* Always light, even inside a dark-mode page */}
<CodeBlock code={myCode} colorScheme="light" />
{/* Follows the nearest .dark ancestor (default) */}
<CodeBlock code={myCode} colorScheme="auto" />

Token Color Overrides

All syntax token colors are exposed as CSS custom properties prefixed with --ninna-cb-. Override them globally in your CSS or scope overrides to a specific section.

Override token colors in CSS

/* Override individual token colors globally */
[data-slot="code-block"] {
--ninna-cb-keyword: oklch(0.50 0.22 250); /* blue keywords */
--ninna-cb-string: oklch(0.50 0.18 145); /* green strings */
--ninna-cb-comment: oklch(0.55 0.04 264); /* muted comments */
--ninna-cb-component: oklch(0.52 0.20 300); /* purple components */
--ninna-cb-tag: oklch(0.50 0.20 20); /* red/orange tags */
--ninna-cb-attr: oklch(0.52 0.16 55); /* amber attributes */
--ninna-cb-boolean: oklch(0.50 0.18 25); /* orange booleans */
--ninna-cb-number: oklch(0.52 0.18 80); /* yellow numbers */
--ninna-cb-punctuation: oklch(0.50 0.04 264); /* muted punctuation */
}
/* Scope overrides to a specific wrapper — leave other blocks unchanged */
.my-docs-section [data-slot="code-block"] {
--ninna-cb-keyword: oklch(0.55 0.28 160); /* green theme for this section */
}
VariableToken
--ninna-cb-keywordKeywords (import, const, return, …)
--ninna-cb-stringString and template literals
--ninna-cb-commentLine and block comments
--ninna-cb-componentReact component names (PascalCase)
--ninna-cb-tagHTML/JSX tag names (lowercase)
--ninna-cb-attrJSX / HTML attribute names
--ninna-cb-booleantrue, false, null, undefined
--ninna-cb-numberNumeric literals
--ninna-cb-punctuationBrackets, operators, punctuation

CSS Import: Add @import "@ninna-ui/code-block/styles"; to your CSS entry point (or it is picked up automatically via @source if you already source @ninna-ui/**).