⚡ You're viewing a live demo of ChimerAI. Data resets daily at midnight UTC.Get the CLI →

White-Label Theming

ChimerAI's theming system lets you fully customise the look and feel of your app — logos, colors, fonts, dark mode — all without touching component internals. Per-workspace theming is supported for multi-tenant SaaS.

What you get

  • CSS custom properties — All colors & radii are CSS variables, overridable at runtime
  • Dark modenext-themes integration, system preference detection
  • Logo & brand — Swap logos per workspace from admin panel
  • Font customisation — Google Fonts or self-hosted, set per theme
  • Per-workspace themes — Each workspace can have its own theme stored in the DB
  • Theme preview — Live preview without page reload

Quick setup

npx chimerai add theming

Scaffolds:

app/api/theme/route.ts            ← GET/PUT workspace theme
components/ThemeProvider.tsx      ← Wraps app with CSS vars
lib/theme.ts                      ← Theme helpers & defaults
app/theming/page.tsx              ← Theme editor UI
styles/theme.css                  ← CSS variable definitions

Theme structure

// lib/theme.ts
export interface AppTheme {
  primaryColor: string; // e.g. '#6366f1'
  backgroundColor: string;
  textColor: string;
  borderRadius: string; // e.g. '0.5rem'
  fontFamily: string; // e.g. 'Inter, sans-serif'
  logoUrl?: string;
  darkMode: 'light' | 'dark' | 'system';
}

export const defaultTheme: AppTheme = {
  primaryColor: '#6366f1',
  backgroundColor: '#ffffff',
  textColor: '#111827',
  borderRadius: '0.5rem',
  fontFamily: 'Inter, sans-serif',
  darkMode: 'system',
};

Applying themes via CSS variables

// components/ThemeProvider.tsx
'use client';

import { useEffect } from 'react';

export function ThemeProvider({ theme, children }: { theme: AppTheme; children: React.ReactNode }) {
  useEffect(() => {
    const root = document.documentElement;
    root.style.setProperty('--color-primary', theme.primaryColor);
    root.style.setProperty('--color-bg', theme.backgroundColor);
    root.style.setProperty('--color-text', theme.textColor);
    root.style.setProperty('--radius', theme.borderRadius);
    root.style.setProperty('--font-sans', theme.fontFamily);
  }, [theme]);

  return <>{children}</>;
}

Saving a workspace theme

// app/api/theme/route.ts
import { db } from '@/lib/db';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';

export async function PUT(req: Request) {
  const session = await getServerSession(authOptions);
  if (!session) return new Response('Unauthorized', { status: 401 });

  const theme = await req.json();
  await db.workspace.update({
    where: { id: session.user!.workspaceId },
    data: { theme: JSON.stringify(theme) },
  });

  return Response.json({ ok: true });
}

Dark mode

Dark mode is handled by next-themes. Add ThemeProvider from next-themes in your root layout:

// app/layout.tsx
import { ThemeProvider } from 'next-themes';

export default function RootLayout({ children }) {
  return (
    <html suppressHydrationWarning>
      <body>
        <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
          {children}
        </ThemeProvider>
      </body>
    </html>
  );
}

Further reading

ChimerAI Docs · Back to Demo