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'],
},
backgroundImage: {
'dot-pattern':
'radial-gradient(circle, hsl(var(--foreground) / 0.1) 1px, transparent 1px)',
'gradient-brand':
'linear-gradient(to right, hsl(var(--gradient-start)), hsl(var(--gradient-end)))',
},
backgroundSize: {
'dot-pattern': '20px 20px',
},
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)',
},
keyframes: {
'ping-once': {
'0%': { transform: 'scale(1)', opacity: '1' },
'75%': { transform: 'scale(1.25)', opacity: '0.75' },
'100%': { transform: 'scale(1)', opacity: '1' },
},
'bounce-once': {
'0%': { transform: 'translateY(0)' },
'25%': { transform: 'translateY(-20%)' },
'50%': { transform: 'translateY(0)' },
'75%': { transform: 'translateY(-10%)' },
'100%': { transform: 'translateY(0)' },
},
'pulse-once': {
'0%': { opacity: '0' },
'50%': { opacity: '0.5' },
'100%': { opacity: '0' },
},
ripple: {
'0%': {
transform: 'scale(0)',
opacity: '0.5',
},
'100%': {
transform: 'scale(4)',
opacity: '0',
},
},
},
animation: {
'ping-once': 'ping-once 0.6s cubic-bezier(0.4, 0, 0.6, 1)',
'bounce-once': 'bounce-once 0.6s cubic-bezier(0.4, 0, 0.6, 1)',
'pulse-once': 'pulse-once 0.6s cubic-bezier(0.4, 0, 0.6, 1)',
ripple: 'ripple 0.6s linear',
},
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))',
},
footer: {
DEFAULT: 'hsl(var(--footer-background))',
foreground: 'hsl(var(--footer-foreground))',
},
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
chart: {
1: 'hsl(var(--chart-1))',
2: 'hsl(var(--chart-2))',
3: 'hsl(var(--chart-3))',
4: 'hsl(var(--chart-4))',
5: 'hsl(var(--chart-5))',
},
},
},
},
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 |
bg-gradient-brand | --gradient-start/end | Brand gradient backgrounds |
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.Background Patterns
Two custom background utilities are pre-configured:
typescript
backgroundImage: {
'dot-pattern': 'radial-gradient(circle, hsl(var(--foreground) / 0.1) 1px, transparent 1px)',
'gradient-brand': 'linear-gradient(to right, hsl(var(--gradient-start)), hsl(var(--gradient-end)))',
}
Use
bg-dot-pattern for subtle decorative backgrounds and bg-gradient-brand for hero sections or accent areas. Both are theme-aware — dot-pattern uses the foreground color at 10% opacity, and gradient-brand uses the theme's gradient endpoints.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.The
bg-gradient-brand utility automatically uses each theme's gradient colors. For example, the ocean theme produces a teal-to-cyan gradient while the sunset theme produces an orange-to-pink gradient — no code changes needed.Custom Animations
Kit includes 4 custom animations for interactive feedback:
| Animation | Class | Duration | Use Case |
|---|---|---|---|
| Ping Once | animate-ping-once | 0.6s | Button click feedback, notification pulse |
| Bounce Once | animate-bounce-once | 0.6s | Success confirmation, attention draw |
| Pulse Once | animate-pulse-once | 0.6s | Fade-in highlight effect |
| Ripple | animate-ripple | 0.6s | Material-style ripple on click |
All animations use
cubic-bezier(0.4, 0, 0.6, 1) easing (except ripple which uses linear) and run exactly once — they don't loop. This makes them ideal for interaction feedback where you want a single visual response.To add a new animation, 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';
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
/* Typography System */
h1 {
@apply scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl;
}
h2 {
@apply scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight first:mt-0;
}
h3 {
@apply scroll-m-20 text-2xl font-semibold tracking-tight;
}
h4 {
@apply scroll-m-20 text-xl font-semibold tracking-tight;
}
p {
@apply leading-7 [&:not(:first-child)]:mt-6;
}
blockquote {
@apply mt-6 border-l-2 pl-6 italic;
}
ul {
@apply my-6 ml-6 list-disc [&>li]:mt-2;
}
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 (lines 1–9) — All 9 theme CSS files must be imported first, before any Tailwind directives. This ensures CSS custom properties are available when Tailwind generates utility classes.
- Tailwind directives (lines 12–14) —
@tailwind base,@tailwind components,@tailwind utilitiesinject Tailwind's generated styles at the right layer. - Base layer (lines 16–49) —
@layer basesets global defaults: border color on all elements, background/text color on body, and a full typography system (h1–h4, p, blockquote, ul, code) using semantic Tailwind classes. - 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 |