AI Agent Built Themes
Baan supports a second type of theme where you instruct an AI coding agent — Claude Code, Cursor, or Codex — to write TSX components directly. This unlocks any layout or interaction that React can express.
When to Use This Approach
Section titled “When to Use This Approach”| Admin Generated | AI Agent Built | |
|---|---|---|
| How | Admin UI prompt | AI agent writes TSX |
| Structure | config.json + CSS only | Full TSX component set |
| Flexibility | Preset variants + colors | Unlimited |
| Best for | Color/variant changes | Custom layouts, widgets, unique interactions |
Use AI Agent Built when you need something outside the available presets — a reading progress sidebar, a docs-style tree navigation, a magazine layout with custom card logic, or anything with unique interactivity.
How to Prompt Your AI Agent
Section titled “How to Prompt Your AI Agent”The simplest prompt works — the agent reads the theme documentation and builds the scaffolding automatically.
Level 1: Just describe the concept
Section titled “Level 1: Just describe the concept”Create a theme called "ocean". Ocean-like atmosphere.Create a theme called "startup". Modern SaaS landing page style blog.Level 2: Specify layout and structure
Section titled “Level 2: Specify layout and structure”Create a theme called "tech-journal".
- Single column, no sidebar- Compact left-aligned header- Article list as text-only (no images)- Monospace font- Dark background with green accentLevel 3: Specific colors and styles
Section titled “Level 3: Specific colors and styles”Create a theme called "midnight".
Color palette:- Background: #0f172a (dark navy)- Text: #e2e8f0 (light gray)- Accent: #7c3aed (purple)
Cards with large border radius (rounded-xl), shadow intensifies on hover.Tags displayed as hashtags (#React style).Level 4: Customize based on an existing theme
Section titled “Level 4: Customize based on an existing theme”Create a "docs-site" theme based on the the-publish theme.
- Keep the left tree navigation- Add table of contents in the right sidebar- More neutral (gray-based) color schemeLevel 5: Add custom widgets
Section titled “Level 5: Add custom widgets”Create a theme called "monograph".
- Ultra-thin fixed header (44px), site name only- Minimal black & white design- Right sidebar on article pages with a reading progress tracker (auto-extracts h2/h3 for ToC, highlights current section on scroll)
Note: avoid naming a theme `prose` — it collides with Tailwind's`@tailwindcss/typography` utility class.What Gets Created
Section titled “What Gets Created”The agent creates a directory under themes/my-theme/:
themes/my-theme/├── theme.json ← theme metadata (includes `ai` field)├── config.ts ← pagination colors (required — build fails without it)├── prompts/ ← Theme Provenance (required)│ ├── concept.md ← design concept & principles (English)│ └── generation.md ← optional: the generation prompt├── styles/theme.css ← CSS variables├── components/│ ├── layout/│ │ ├── Layout.tsx ← main layout wrapper│ │ ├── SiteHeader.tsx│ │ └── SiteFooter.tsx│ └── post/│ ├── PostCard.tsx ← article card│ ├── PostContent.tsx ← article body│ └── RelatedPosts.tsx└── templates/ └── home-template.tsx ← home page templateThe theme is auto-detected at build time — no manual registration or file edits outside the theme directory are needed. Any directory under themes/ with a theme.json is picked up automatically.
Theme Provenance
Section titled “Theme Provenance”Every theme must bundle its design prompts. theme.json records the generation context, and prompts/concept.md documents the design intent in English:
{ "ai": { "agent": "Claude Code", "skills": ["create-theme"], "generatedAt": "2026-04-18" }}The skills array can include built-in skill names (e.g. "create-theme") and references to custom skill files bundled at prompts/skills/<name>.md. Both forms can coexist.
This is what sets Baan apart from traditional theme systems: consumers receive the theme’s recipe (prompts) alongside the compiled code. They can read the intent, fork by editing the prompt, or feed it to a different AI agent. See Creating Themes → Theme Provenance for the full specification.
CSS Variables
Section titled “CSS Variables”Themes define their color palette as CSS variables in styles/theme.css. Variables use HSL space-separated format — no hsl() wrapper.
:root { --background: 0 0% 100%; --foreground: 0 0% 7%; --card: 0 0% 100%; --primary: 0 0% 7%; --primary-foreground: 0 0% 98%; --muted: 0 0% 96%; --muted-foreground: 0 0% 42%; --accent: 0 0% 94%; --border: 0 0% 90%; --radius: 0.25rem;}Use Tailwind classes that reference these variables — avoid hardcoded colors like bg-white or text-gray-900:
bg-background, text-foreground ← page background and body textbg-card, text-card-foreground ← cardsbg-muted, text-muted-foreground ← subtle elementsbg-primary, text-primary-foreground ← primary accentborder-border ← bordersImplementation Notes
Section titled “Implementation Notes”config.ts is required
Section titled “config.ts is required”Every theme must have a config.ts — the build fails without it:
export const themeConfig = { pagination: { activeColor: "#111111", hoverColor: "#f5f5f5", },};export type ThemeConfig = typeof themeConfig;Handle client-side navigation
Section titled “Handle client-side navigation”In Next.js App Router, the Layout component stays mounted across page navigations. A useEffect with an empty dependency array only runs once, so DOM-dependent logic (like a table of contents) becomes stale after navigating.
Fix: use usePathname() as a dependency:
import { usePathname } from "next/navigation";
const pathname = usePathname();
useEffect(() => { setItems([]); const raf = requestAnimationFrame(() => { // new DOM is available after one frame const article = document.querySelector("article"); // ... DOM scan }); return () => cancelAnimationFrame(raf);}, [pathname]); // re-runs on every route changeUse lg breakpoints, not xl
Section titled “Use lg breakpoints, not xl”The Admin Customizer preview panel takes ~350px of screen width. At 1440px, the preview iframe is ~1090px — below the xl breakpoint (1280px) but above lg (1024px). Using xl: makes sidebars invisible in the preview.
// Avoid: invisible in Customizer preview<aside className="hidden xl:block">
// Use instead<aside className="hidden lg:block">Align header nav with content column
Section titled “Align header nav with content column”To keep navigation links visually aligned with the content below, use the same CSS grid template in both the header and the main layout:
// Same grid in both Layout.tsx and SiteHeader.tsx<div className="grid grid-cols-1 lg:grid-cols-[1fr_640px_200px] gap-0 lg:gap-8">HeaderNavigation left offset
Section titled “HeaderNavigation left offset”When navStyle is passed to HeaderNavigation, each link gets px-3, offsetting the first item. Fix with [&_a]:pl-0:
<HeaderNavigation className="flex items-center gap-6 text-sm [&_a]:pl-0" navStyle={navigationStyle}/>Available Fonts
Section titled “Available Fonts”Use an existing fontKey in theme.json — no code changes required:
| fontKey | Font |
|---|---|
inter | Inter (sans-serif) |
merriweather | Merriweather (serif) |
jetbrains-mono | JetBrains Mono (monospace) |
After Implementation
Section titled “After Implementation”npm run build # must pass before finishingSelect the theme name in Admin → Settings → Theme to apply it immediately.