Kit uses Tailwind CSS 3.4 with a semantic color system built on CSS custom properties. Instead of hardcoded color values like
bg-blue-500, the entire application uses theme-aware classes like bg-primary that automatically adapt to the active color theme and dark mode. This page covers the Tailwind configuration, how to extend it, and the global CSS architecture.For switching between themes, see Color Themes & Dark Mode. For component styling patterns, see UI Components.
Tailwind Configuration
The Tailwind config maps every semantic color to its CSS custom property and defines custom fonts, animations, and layout utilities:
tailwind.config.js — Full Configuration
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ['class'],
content: [
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
'../../packages/ui/src/**/*.{js,ts,jsx,tsx}',
],
theme: {
container: {
center: true,
padding: '2rem',
screens: {
'2xl': '1400px',
},
},
extend: {
fontFamily: {
sans: ['var(--font-inter)', 'system-ui', 'sans-serif'],
mono: ['var(--font-jetbrains-mono)', 'ui-monospace', 'monospace'],
},
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)',
},
colors: {
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))',
},
popover: {
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))',
},
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))',
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))',
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))',
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))',
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))',
},
success: {
DEFAULT: 'hsl(var(--success))',
foreground: 'hsl(var(--success-foreground))',
},
warning: {
DEFAULT: 'hsl(var(--warning))',
foreground: 'hsl(var(--warning-foreground))',
},
info: {
DEFAULT: 'hsl(var(--info))',
foreground: 'hsl(var(--info-foreground))',
},
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
},
},
},
plugins: [require('tailwindcss-animate'), require('@tailwindcss/typography')],
}
Key configuration points:
darkMode: ['class']— Dark mode toggles via.darkclass (managed by next-themes), not by media querycontentpaths — Scans bothsrc/directories andpackages/ui/so shared components get Tailwind classescontainer— Centered with2rempadding and a1400pxmax-width at the2xlbreakpointcolors— Every color references a CSS variable viahsl(var(--name)), making all colors theme-awareplugins—tailwindcss-animatefor animation utilities and@tailwindcss/typographyfor prose styling
Semantic Color System
The color system works through three layers:
Theme CSS file (default.css) → --primary: 221 83% 53%;
|
Tailwind config (tailwind.config.js) → primary: 'hsl(var(--primary))'
|
Your components → className="bg-primary text-primary-foreground"
When you use
bg-primary, Tailwind generates background-color: hsl(var(--primary)). The actual color value comes from whichever theme CSS file is active.Opacity Modifiers
Because CSS variables store raw HSL values (not wrapped in
hsl()), Tailwind's opacity modifier syntax works out of the box:tsx
// 50% opacity primary color
<div className="bg-primary/50" />
// Generates: background-color: hsl(221 83% 53% / 0.5)
// 80% opacity foreground text
<p className="text-foreground/80" />
// Generates: color: hsl(0 0% 3.9% / 0.8)
Color Usage Reference
| Tailwind Class | CSS Variable | When to Use |
|---|---|---|
bg-background | --background | Page background |
text-foreground | --foreground | Primary body text |
bg-card | --card | Card and surface backgrounds |
bg-primary | --primary | Primary buttons, links, brand elements |
text-primary-foreground | --primary-foreground | Text on primary-colored backgrounds |
bg-secondary | --secondary | Secondary buttons, less prominent elements |
bg-muted | --muted | Subtle background areas, disabled states |
text-muted-foreground | --muted-foreground | Help text, placeholders, timestamps |
bg-accent | --accent | Hover states, selected items |
bg-destructive | --destructive | Error states, delete buttons |
border-border | --border | All borders and dividers |
border-input | --input | Form input borders |
ring-ring | --ring | Focus ring outlines |
Always use semantic classes (
bg-primary, text-muted-foreground) instead of hardcoded values (bg-blue-500, text-gray-600). Hardcoded colors break when users switch themes and don't respond to dark mode. The only exception is the safelist in the Tailwind config for edge cases that need specific colors.Extending the Configuration
Custom Fonts
The config defines two font families using CSS variables set by
next/font:typescript
fontFamily: {
sans: ['var(--font-inter)', 'system-ui', 'sans-serif'],
mono: ['var(--font-jetbrains-mono)', 'ui-monospace', 'monospace'],
}
To change fonts, update the
next/font imports in apps/boilerplate/src/app/layout.tsx and the variable names will flow through automatically. The font-sans class applies Inter and font-mono applies JetBrains Mono.Border Radius
Border radius values are derived from the
--radius CSS custom property (default: 0.5rem):typescript
borderRadius: {
lg: 'var(--radius)', // 0.5rem
md: 'calc(var(--radius) - 2px)', // ~0.375rem
sm: 'calc(var(--radius) - 4px)', // ~0.25rem
}
Changing
--radius in a theme CSS file adjusts all rounded corners throughout the application.Custom Animations
To add custom animations, define a
keyframes entry and matching animation entry in the extend section of tailwind.config.js:javascript
keyframes: {
'slide-in': {
'0%': { transform: 'translateX(-100%)', opacity: '0' },
'100%': { transform: 'translateX(0)', opacity: '1' },
},
},
animation: {
'slide-in': 'slide-in 0.3s ease-out',
},
Global CSS Architecture
The global stylesheet establishes the base styling for the entire application. It is structured in layers:
src/app/globals.css — Theme Imports, Tailwind Directives & Base Layer
/* Import all available color themes - MUST be first! */
@import '../styles/themes/default.css';
@import '../styles/themes/ocean.css';
@import '../styles/themes/forest.css';
@import '../styles/themes/sunset.css';
@import '../styles/themes/midnight.css';
@import '../styles/themes/coral.css';
@import '../styles/themes/slate.css';
@import '../styles/themes/aurora.css';
@import '../styles/themes/crimson.css';
/* Syntax highlighting (extracted for maintainability) */
@import '../styles/syntax-highlighting.css';
/* Universal status colors — theme-independent */
:root {
--success: 142 76% 36%;
--success-foreground: 0 0% 100%;
--warning: 32 95% 44%;
--warning-foreground: 0 0% 100%;
--info: 199 89% 48%;
--info-foreground: 0 0% 100%;
/* Opaque content surface: muted/80 composited over background.
Used on <main> and sticky overlays to avoid opacity stacking. */
--main-surface: hsl(var(--muted));
--main-surface: color-mix(in srgb, hsl(var(--muted)) 80%, hsl(var(--background)));
}
.dark {
--success: 142 76% 50%;
--success-foreground: 0 0% 100%;
--warning: 32 95% 54%;
--warning-foreground: 0 0% 100%;
--info: 199 89% 58%;
--info-foreground: 0 0% 100%;
--main-surface: hsl(var(--background));
--main-surface: color-mix(in srgb, hsl(var(--muted)) 20%, hsl(var(--background)));
}
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
/* Prevent body scrollbar in dashboard layout — only <main> should scroll */
html:has([data-dashboard-layout]) {
overflow: hidden;
}
/* Typography System
Heading styles (h1-h4) are set via explicit className on each element.
Prose typography (legal pages) is handled by @tailwindcss/typography plugin. */
.prose h2 {
@apply border-b pb-2;
}
p {
@apply leading-7;
}
/* Dashboard heading spacing — vertical rhythm within sections
H3-H6 get top margin when preceded by other content (not when first child).
H1/H2 spacing is handled by .db-page-sections and component containers. */
[data-dashboard-layout] h3:not(:first-child) {
@apply mt-6 sm:mt-8;
}
[data-dashboard-layout] h4:not(:first-child) {
@apply mt-5 sm:mt-6;
}
[data-dashboard-layout] h5:not(:first-child),
[data-dashboard-layout] h6:not(:first-child) {
@apply mt-4;
}
code {
@apply relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold;
}
}
The file is organized in 4 distinct sections:
- Theme imports — All 9 theme CSS files plus syntax highlighting must be imported first, before any Tailwind directives. This ensures CSS custom properties are available when Tailwind generates utility classes.
- Tailwind directives —
@tailwind base,@tailwind components,@tailwind utilitiesinject Tailwind's generated styles at the right layer. Universal status colors (success, warning, info) are defined between imports and directives. - Base layer —
@layer basesets global defaults: border color on all elements, background/text color on body, paragraph leading, inlinecodestyling, and dashboard-scoped heading spacing. Heading styles (h1–h4) are applied via explicitclassNameon each element; prose typography (legal pages) is handled by the@tailwindcss/typographyplugin. - Components layer (further in file) —
@layer componentsdefines reusable patterns like code block styling for prose content with dark mode adjustments.
Theme CSS imports must come before the
@tailwind directives. If they appear after, CSS custom properties may not be available when Tailwind generates its utility classes, causing missing or broken colors.Adding Custom Utilities
CSS Utilities
Add custom utility classes using Tailwind's
@layer utilities directive in globals.css:css
@layer utilities {
.text-balance {
text-wrap: balance;
}
.scrollbar-hide {
-ms-overflow-style: none;
scrollbar-width: none;
}
.scrollbar-hide::-webkit-scrollbar {
display: none;
}
}
Extending Tailwind Config
For utilities that need responsive variants or other Tailwind modifiers, add them to the config:
javascript
// tailwind.config.js — extend section
extend: {
spacing: {
'18': '4.5rem',
'88': '22rem',
},
maxWidth: {
'prose-wide': '85ch',
},
zIndex: {
'60': '60',
'70': '70',
},
}
Config-based extensions automatically get responsive (
md:max-w-prose-wide), hover (hover:z-60), and other modifier support.Key Files
| File | Purpose |
|---|---|
apps/boilerplate/tailwind.config.js | Tailwind configuration — colors, fonts, animations, plugins |
apps/boilerplate/src/app/globals.css | Global styles — theme imports, base layer, typography, custom utilities |
apps/boilerplate/src/styles/themes/*.css | 9 theme CSS files with CSS custom properties (light + dark) |
apps/boilerplate/postcss.config.js | PostCSS configuration for Tailwind and autoprefixer |
packages/utils/src/index.ts | cn() utility — merges Tailwind classes with conflict resolution |