Theming
Colors, fonts, dark mode, and Tailwind token mapping.
Theming
All components use neutral Tailwind gray classes (bg-gray-50, text-gray-900, border-gray-200). No CSS is shipped — everything is Tailwind inline. Override by mapping your brand colors to the gray scale.
Color Mapping
Map Tailwind tokens to your CSS variables in globals.css:
@theme inline {
--color-gray-50: var(--my-bg); /* backgrounds, input surface */
--color-gray-100: var(--my-subtle); /* hover states, cards */
--color-gray-200: var(--my-border); /* borders */
--color-gray-400: var(--my-muted); /* muted text */
--color-gray-600: var(--my-secondary); /* secondary text */
--color-gray-900: var(--my-foreground); /* primary text */
--color-blue-500: var(--my-accent); /* accent, focus rings */
--color-green-600: var(--my-success); /* success states */
}Use var() inside @theme inline — this makes values resolve at runtime, required for dark mode.
Fonts
Components inherit fonts from the parent. Set your fonts on <body>:
body {
font-family: 'Inter', sans-serif;
}Agent names in messages use text-[13px] font-semibold with the inherited font.
Dark Mode
Define CSS variables in :root (light) and override them for dark mode. There are two common approaches:
Class-based (Tailwind / Fumadocs standard)
This is the standard approach used by Tailwind CSS and Fumadocs. The dark mode class is toggled on <html>:
:root {
--my-bg: #FAFAF7;
--my-border: #E5E1DA;
--my-foreground: #111111;
}
.dark {
--my-bg: #0F0F0F;
--my-border: #2A2A2A;
--my-foreground: #EEEEEE;
}Attribute-based
Uses a data-theme attribute instead of a class. Useful when you want to support more than two themes:
:root {
--my-bg: #FAFAF7;
--my-border: #E5E1DA;
--my-foreground: #111111;
}
[data-theme="dark"] {
--my-bg: #0F0F0F;
--my-border: #2A2A2A;
--my-foreground: #EEEEEE;
}All components use tokenized classes (bg-gray-50 not bg-white), so dark mode works automatically when you map the tokens — regardless of which approach you use.
Anti-flash
To prevent the light-to-dark flash on page load, add an inline script in <head>. Choose the version that matches your approach:
Class-based:
<html suppressHydrationWarning>
<head>
<script dangerouslySetInnerHTML={{ __html:
`(function(){try{var t=localStorage.getItem("theme");if(t==="dark")document.documentElement.classList.add("dark")}catch(e){}})()`
}} />
</head>
</html>Attribute-based:
<html suppressHydrationWarning>
<head>
<script dangerouslySetInnerHTML={{ __html:
`(function(){try{var t=localStorage.getItem("theme");if(t)document.documentElement.setAttribute("data-theme",t)}catch(e){}})()`
}} />
</head>
</html>Font Size
Override message text size globally with a wrapper class:
.chat-wrapper { font-size: 15px; }Or use className on individual components.
Spacing and Radius
Components use fixed Tailwind spacing. Override via className:
<ChatInput className="px-4 py-2" />
<Chat className="max-w-4xl mx-auto" />Tailwind Content Scanning
When installing via npm (not CLI), Tailwind doesn't scan node_modules. Add the content path so Tailwind picks up the component classes.
Tailwind v4
/* globals.css */
@source "../node_modules/@polpo-ai/chat/dist/**/*.js";Tailwind v3
// tailwind.config.js
module.exports = {
content: [
"./app/**/*.{ts,tsx}",
"./node_modules/@polpo-ai/chat/dist/**/*.js",
],
}This is not needed when using npx @polpo-ai/ui add chat — the CLI copies source files into your project.