Codapult's UI is built on Tailwind CSS v4, shadcn/ui (New York style), and Radix UI primitives. All theming is driven by CSS custom properties in OKLCH color space, making it easy to customize the entire look from a single file.
Color System
Theme colors are defined as CSS custom properties in src/app/globals.css using OKLCH — a perceptually uniform color space that produces more consistent palettes than HSL.
Changing Your Brand Color
Edit the --primary variable in both :root (light) and .dark (dark) sections:
:root {
--primary: oklch(0.546 0.245 262.881); /* blue — default */
--primary-foreground: oklch(0.985 0 0);
/* ... */
}
.dark {
--primary: oklch(0.623 0.214 259.815);
--primary-foreground: oklch(0.985 0 0);
/* ... */
}
To switch to a green brand, for example:
:root {
--primary: oklch(0.55 0.2 145);
--ring: oklch(0.55 0.2 145);
}
.dark {
--primary: oklch(0.65 0.18 145);
--ring: oklch(0.65 0.18 145);
}
Tip: Keep
--ringin sync with--primaryso focus rings match your brand.
Available Tokens
| Token | Purpose |
| ---------------------------------------- | ----------------------------------- |
| --background / --foreground | Page background and text |
| --primary / --primary-foreground | Brand color and text on brand |
| --secondary / --secondary-foreground | Secondary surfaces |
| --muted / --muted-foreground | Muted backgrounds and subdued text |
| --accent / --accent-foreground | Accent highlights |
| --destructive | Error / danger actions |
| --border / --input / --ring | Borders, inputs, focus rings |
| --card / --popover | Card and popover surfaces |
| --sidebar-* | Sidebar-specific overrides |
| --chart-1 through --chart-5 | Chart and data visualization colors |
Tailwind Integration
Tailwind v4 maps CSS variables to utility classes via the @theme inline directive — no tailwind.config file needed:
@theme inline {
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
/* ... */
}
Use standard Tailwind classes like bg-primary, text-muted-foreground, border-border in your components.
Fonts
Default fonts are Geist Sans and Geist Mono, loaded via next/font/google in src/app/layout.tsx.
To replace them:
import { Inter, JetBrains_Mono } from 'next/font/google';
const sans = Inter({ subsets: ['latin'], variable: '--font-geist-sans' });
const mono = JetBrains_Mono({ subsets: ['latin'], variable: '--font-geist-mono' });
The CSS variable names (--font-geist-sans, --font-geist-mono) are referenced by the @theme inline block, so Tailwind classes like font-sans and font-mono update automatically.
Dark Mode
Dark mode uses next-themes with the .dark CSS class strategy.
- ThemeToggle component in the dashboard and marketing pages lets users switch between light, dark, and system.
- Color tokens in
.dark { ... }override the:rootvalues automatically. - All shadcn/ui and Radix UI components respect dark mode out of the box.
Icons
Codapult uses lucide-react for all icons:
import { Settings, Bell, ChevronRight } from 'lucide-react';
<Settings className="h-4 w-4" />;
The cn() Utility
The cn() helper from @/lib/utils combines clsx (conditional classes) and tailwind-merge (deduplication):
import { cn } from '@/lib/utils';
<div className={cn(
'rounded-lg border p-4',
isActive && 'border-primary bg-primary/10',
className,
)} />
Always use cn() when merging Tailwind classes to avoid conflicting utilities.
White-Label Branding
The BrandingProvider component enables per-organization color overrides. Organizations can customize:
- Primary color — brand color used throughout the dashboard
- Sidebar color — sidebar background and highlights
- Accent color — secondary accent for buttons and links
These overrides are stored in the database and injected as CSS variables at runtime, so each organization sees their own branding without a rebuild.
Design Tokens API
Export your theme tokens programmatically via GET /api/design-tokens:
| Format | URL | Description |
| ---------------- | -------------------------------------------- | ------------------------------------------------------------------------------ |
| JSON | /api/design-tokens?format=json | Token name/value pairs |
| CSS | /api/design-tokens?format=css | CSS custom properties block |
| Tailwind | /api/design-tokens?format=tailwind | Tailwind theme config object |
| Style Dictionary | /api/design-tokens?format=style-dictionary | Style Dictionary compatible format |
Token definitions live in src/lib/design-tokens/.
Landing Page
Marketing page sections are modular components in src/components/marketing/:
| Component | Description |
| -------------- | ------------------------------------------ |
| Hero | Above-the-fold headline with CTA |
| HowItWorks | Step-by-step feature walkthrough |
| Pricing | Interactive pricing cards with toggle |
| FAQ | Accordion-style frequently asked questions |
| VideoEmbed | Product demo video embed |
| Testimonials | Customer quotes carousel |
Customizing Sections
Enable, disable, or reorder sections in src/app/(marketing)/page.tsx:
export default function LandingPage() {
return (
<>
<Hero />
<HowItWorks />
<Pricing />
<FAQ />
</>
);
}
Marketing content — pricing tiers, testimonials, stats, and feature lists — is configured in src/config/marketing.ts. Edit that file to update copy without touching components.
UI Components
All base UI components live in src/components/ui/ and follow the shadcn/ui conventions (New York style). They are built on Radix UI primitives for accessibility and composability.
To add a new shadcn/ui component:
npx shadcn@latest add dialog