Customization

Tailwind CSS configuration, semantic color system, custom animations, and global CSS architecture

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 .dark class (managed by next-themes), not by media query
  • content paths — Scans both src/ directories and packages/ui/ so shared components get Tailwind classes
  • container — Centered with 2rem padding and a 1400px max-width at the 2xl breakpoint
  • colors — Every color references a CSS variable via hsl(var(--name)), making all colors theme-aware
  • pluginstailwindcss-animate for animation utilities and @tailwindcss/typography for 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 ClassCSS VariableWhen to Use
bg-background--backgroundPage background
text-foreground--foregroundPrimary body text
bg-card--cardCard and surface backgrounds
bg-primary--primaryPrimary buttons, links, brand elements
text-primary-foreground--primary-foregroundText on primary-colored backgrounds
bg-secondary--secondarySecondary buttons, less prominent elements
bg-muted--mutedSubtle background areas, disabled states
text-muted-foreground--muted-foregroundHelp text, placeholders, timestamps
bg-accent--accentHover states, selected items
bg-destructive--destructiveError states, delete buttons
border-border--borderAll borders and dividers
border-input--inputForm input borders
ring-ring--ringFocus ring outlines

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:
  1. 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.
  2. Tailwind directives@tailwind base, @tailwind components, @tailwind utilities inject Tailwind's generated styles at the right layer. Universal status colors (success, warning, info) are defined between imports and directives.
  3. Base layer@layer base sets global defaults: border color on all elements, background/text color on body, paragraph leading, inline code styling, and dashboard-scoped heading spacing. Heading styles (h1–h4) are applied via explicit className on each element; prose typography (legal pages) is handled by the @tailwindcss/typography plugin.
  4. Components layer (further in file) — @layer components defines reusable patterns like code block styling for prose content with dark mode adjustments.

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

FilePurpose
apps/boilerplate/tailwind.config.jsTailwind configuration — colors, fonts, animations, plugins
apps/boilerplate/src/app/globals.cssGlobal styles — theme imports, base layer, typography, custom utilities
apps/boilerplate/src/styles/themes/*.css9 theme CSS files with CSS custom properties (light + dark)
apps/boilerplate/postcss.config.jsPostCSS configuration for Tailwind and autoprefixer
packages/utils/src/index.tscn() utility — merges Tailwind classes with conflict resolution