NDPRThemeProvider
Typed React Context wrapper that injects --ndpr-* CSS custom properties from a JavaScript theme object. Pair it with the toolkit stylesheet to brand every component in one place.
Overview
NDPRThemeProvideris a thin wrapper around the toolkit's existing CSS-variable theming model. It renders a single<div> with--ndpr-* custom properties set inline, so every nested toolkit component picks them up automatically.
The provider is syntactic sugar — it is not a runtime requirement. Unset fields cascade from the stylesheet defaults at @tantainnovative/ndpr-toolkit/styles, so you can set just the few tokens you care about (brand colour, radius) and leave the rest alone.
Quickstart
import { NDPRThemeProvider, type NDPRTheme } from '@tantainnovative/ndpr-toolkit';
import '@tantainnovative/ndpr-toolkit/styles';
const theme: NDPRTheme = {
colors: {
primary: '22 163 74', // RGB triplet — green-600
primaryHover: '21 128 61', // green-700
},
radius: { base: '0.75rem' },
font: { sans: '"Inter", system-ui, sans-serif' },
};
export default function App() {
return (
<NDPRThemeProvider theme={theme}>
<YourApp />
</NDPRThemeProvider>
);
}Colour values are RGB triplets
Every colour token takes the format 'R G B' — three space-separated channels, no rgb() wrapper. The stylesheet wraps them internally as rgb(var(--ndpr-primary)), which lets the same token power solid backgrounds and transparent tints like rgb(var(--ndpr-primary) / 0.12).
// If your design system stores hex, add a one-liner converter:
const hexToRgbTriplet = (hex: string) => {
const n = parseInt(hex.replace('#', ''), 16);
return `${(n >> 16) & 255} ${(n >> 8) & 255} ${n & 255}`;
};
const theme: NDPRTheme = {
colors: { primary: hexToRgbTriplet('#16a34a') },
};Props
| Prop | Type | Description |
|---|---|---|
theme | NDPRTheme | Optional partial theme. Only set fields produce CSS variables; unset fields cascade from the stylesheet. |
className | string | Optional class on the wrapping <div>. Useful for Tailwind utilities (e.g. "min-h-screen bg-background"). |
children | React.ReactNode | App tree. Anything below the provider inherits the theme. |
NDPRTheme shape
Every field is optional. The full type:
interface NDPRTheme {
mode?: 'light' | 'dark';
colors?: {
primary?: string; // --ndpr-primary
primaryHover?: string; // --ndpr-primary-hover
primaryForeground?: string; // --ndpr-primary-foreground
background?: string; // --ndpr-background
surface?: string; // --ndpr-surface
foreground?: string; // --ndpr-foreground
muted?: string; // --ndpr-muted
mutedForeground?: string; // --ndpr-muted-foreground
border?: string; // --ndpr-border
input?: string; // --ndpr-input
ring?: string; // --ndpr-ring
success?: string; // --ndpr-success
destructive?: string; // --ndpr-destructive
warning?: string; // --ndpr-warning
};
radius?: { sm?: string; base?: string; lg?: string; full?: string };
spacing?: { 1?: string; 2?: string; 3?: string; 4?: string; 5?: string; 6?: string; 8?: string };
shadow?: { sm?: string; base?: string; lg?: string };
font?: {
sans?: string;
sizeXs?: string; sizeSm?: string; sizeBase?: string;
sizeLg?: string; sizeXl?: string;
lineHeight?: string; lineHeightTight?: string;
};
transition?: { base?: string; slow?: string };
z?: { banner?: number | string; modal?: number | string };
}Dark mode
Setting mode: 'dark' stamps data-theme="dark" on the wrapper, which activates the dark-mode block in the stylesheet. The toolkit also auto-switches via prefers-color-scheme, so most apps don't need to set mode explicitly.
const [mode, setMode] = useState<'light' | 'dark'>('light');
<NDPRThemeProvider theme={{ mode }}>
<button onClick={() => setMode(m => m === 'dark' ? 'light' : 'dark')}>
Toggle theme
</button>
<App />
</NDPRThemeProvider>When NOT to use the provider
- You manage design tokens in CSS — write
:root { --ndpr-primary: 22 163 74; }directly. - You need to scope tokens to a subtree without an extra DOM node — apply
--ndpr-*overrides on your existing ancestor. - You're bringing your own design system — use the
/unstyledentry and skip CSS variables entirely.