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.

Production Readiness

Use this checklist when moving NDPRThemeProvider from demo mode into a customer-facing workflow. Pair the UI package with the source templates in @tantainnovative/ndpr-recipes so validation, persistence, and audit behavior live in your own backend.

Import path
Use
Production role
@tantainnovative/ndpr-toolkit
NDPRThemeProvider, NDPRTheme
Typed theme object for scoped CSS-variable branding.
@tantainnovative/ndpr-toolkit/styles
global stylesheet
Base token definitions and component styles consumed by the provider.
@tantainnovative/ndpr-toolkit/unstyled
unstyled entry points
Alternative when your design system owns all component styling.
@tantainnovative/ndpr-recipes
src/nextjs/app-router/layout-example.tsx
Reference root layout for combining provider configuration and theme tokens.

Implementation checklist

  • Confirm brand colors meet contrast requirements in consent, DSR, breach, and dashboard states.
  • Use RGB triplets consistently and convert design-system hex values before passing them to the theme.
  • Test light mode, dark mode, high-contrast OS settings, and mobile breakpoints.
  • Keep legal/action states visually distinct: success, warning, destructive, disabled, and focus ring.
  • Decide whether the provider or raw CSS variables owns production theming, then document that ownership.

Backend notes

  • Theme values are client-rendered UI configuration; keep secrets and tenant-private data out of theme objects.
  • For multi-tenant apps, load approved theme tokens from tenant configuration and validate values before rendering.
  • Use a root layout or shell component so theme tokens wrap all toolkit surfaces consistently.
  • Pair theme releases with visual regression checks because legal forms and banners are user-facing controls.

Testing notes

  • Inspect consent banners, forms, tables, modals, and dashboard score colors under the final theme.
  • Run keyboard focus checks so custom ring colors remain visible on every interactive control.
  • Check dark-mode text, borders, warning states, and disabled states for readability.
  • Verify exported or printed policy views are not dependent on unreadable screen-only colors.

Common mistakes

  • Passing hex colors directly instead of RGB triplets.
  • Changing primary color without checking focus, warning, destructive, and success states.
  • Scoping the provider too low so modals or banners render outside the themed subtree.
  • Using the theme provider when raw CSS variables or unstyled components would be simpler for the host design system.

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

PropTypeDescription
themeNDPRThemeOptional partial theme. Only set fields produce CSS variables; unset fields cascade from the stylesheet.
classNamestringOptional class on the wrapping <div>. Useful for Tailwind utilities (e.g. "min-h-screen bg-background").
childrenReact.ReactNodeApp 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 /unstyled entry and skip CSS variables entirely.