The Vercel AI Chatbot uses Tailwind CSS v4 and shadcn/ui for styling, giving you full control over the visual appearance through CSS variables and utility classes.
Design system
The chatbot implements a token-based design system using CSS custom properties defined in app/globals.css. This approach allows you to customize colors, spacing, and other design elements globally.
Color tokens
All colors are defined as HSL values in both light and dark modes:
:root {
--background: hsl(0 0% 100%);
--foreground: hsl(240 10% 3.9%);
--primary: hsl(240 5.9% 10%);
--primary-foreground: hsl(0 0% 98%);
--secondary: hsl(240 4.8% 95.9%);
--secondary-foreground: hsl(240 5.9% 10%);
--muted: hsl(240 4.8% 95.9%);
--muted-foreground: hsl(240 3.8% 46.1%);
--accent: hsl(240 4.8% 95.9%);
--accent-foreground: hsl(240 5.9% 10%);
--destructive: hsl(0 84.2% 60.2%);
--destructive-foreground: hsl(0 0% 98%);
--border: hsl(240 5.9% 90%);
--input: hsl(240 5.9% 90%);
--ring: hsl(240 10% 3.9%);
--radius: 0.5rem;
}
Dark mode
Dark mode is implemented using a custom variant that allows programmatic control:
@custom-variant dark (&:is(.dark, .dark *));
.dark {
--background: hsl(240 10% 3.9%);
--foreground: hsl(0 0% 98%);
--primary: hsl(0 0% 98%);
--primary-foreground: hsl(240 5.9% 10%);
/* ... other dark mode tokens */
}
The theme is managed by next-themes in the ThemeProvider component:
components/theme-provider.tsx
import { ThemeProvider as NextThemesProvider } from "next-themes";
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
}
Customizing colors
Choose your color palette
Select your brand colors in HSL format. You can use tools like HSL Color Picker to convert hex colors to HSL. Update CSS variables
Modify the color tokens in app/globals.css for both light and dark modes::root {
--primary: hsl(220 90% 56%); /* Your brand color */
--primary-foreground: hsl(0 0% 100%);
}
.dark {
--primary: hsl(220 90% 66%); /* Lighter variant for dark mode */
--primary-foreground: hsl(0 0% 0%);
}
Test across components
The color tokens are automatically applied across all UI components. Test your changes in both light and dark modes to ensure proper contrast and readability.
Customizing components
shadcn/ui configuration
The chatbot uses shadcn/ui components configured in components.json:
{
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"css": "app/globals.css",
"baseColor": "zinc",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui"
}
}
All buttons use the Button component with customizable variants:
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md...",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
"icon-sm": "h-8 w-8",
},
},
}
);
You can add custom variants by modifying this configuration.
Styling chat messages
Message styling is controlled through the Message component:
components/ai-elements/message.tsx
export const MessageContent = ({ children, className, ...props }) => (
<div
className={cn(
"is-user:dark flex w-fit min-w-0 max-w-full flex-col gap-2",
"group-[.is-user]:ml-auto group-[.is-user]:rounded-lg",
"group-[.is-user]:bg-secondary group-[.is-user]:px-4 group-[.is-user]:py-3",
"group-[.is-user]:text-foreground",
"group-[.is-assistant]:text-foreground",
className
)}
{...props}
>
{children}
</div>
);
User messages are styled with the secondary background color, while assistant messages have no background. Customize these by modifying the MessageContent component or overriding with the className prop.
The chatbot includes custom scrollbar styling for a polished appearance:
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 3px;
transition: background 0.2s ease;
}
::-webkit-scrollbar-thumb:hover {
background: --alpha(var(--muted-foreground) / 0.5);
}
/* Firefox scrollbar styling */
* {
scrollbar-width: thin;
scrollbar-color: var(--border) transparent;
}
Border radius
Global border radius is controlled by the --radius variable:
:root {
--radius: 0.5rem; /* Default radius */
}
@theme {
--radius-lg: var(--radius);
--radius-md: calc(var(--radius) - 2px);
--radius-sm: calc(var(--radius) - 4px);
}
Change --radius to adjust the roundness of all components globally.
Typography
The chatbot uses Geist Sans and Geist Mono fonts:
@theme {
--font-sans: var(--font-geist);
--font-mono: var(--font-geist-mono);
}
To use different fonts, update these variables and import your chosen fonts in app/layout.tsx.
Advanced customization
The sidebar has dedicated color tokens:
:root {
--sidebar-background: hsl(0 0% 98%);
--sidebar-foreground: hsl(240 5.3% 26.1%);
--sidebar-primary: hsl(240 5.9% 10%);
--sidebar-accent: hsl(240 4.8% 95.9%);
--sidebar-border: hsl(220 13% 91%);
}
Suggestion highlights
Document suggestions use a custom highlight style:
.suggestion-highlight {
@apply bg-blue-200 hover:bg-blue-300
dark:hover:bg-blue-400/50
dark:text-blue-50
dark:bg-blue-500/40;
}
Code editor theme
The code editor (CodeMirror) styling can be customized:
.cm-editor,
.cm-gutters {
@apply bg-background! dark:bg-zinc-800! outline-hidden!;
}
.ͼo.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground {
@apply bg-zinc-200! dark:bg-zinc-900!;
}
The chatbot uses Tailwind CSS v4’s @theme directive for defining design tokens. This is different from v3’s configuration approach. Learn more in the Tailwind CSS v4 documentation.