While component-scoped styles keep your CSS modular, every application needs some global styles—base typography, CSS resets, and especially CSS variables (custom properties) for theming. CSS variables provide dynamic, reusable design tokens that work across your entire application, enable dark mode, and make theme customization effortless. Let's master global styles and CSS variables in Next.js!
Global Styles in Next.js
Creating Global Styles
Create globals.css in your app directory:
/* Tailwind directives (if using Tailwind) */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* CSS Reset / Normalize */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
font-family: system-ui, -apple-system, sans-serif;
line-height: 1.6;
color: #333;
background: #fff;
}
/* Base Typography */
h1, h2, h3, h4, h5, h6 {
font-weight: 700;
line-height: 1.2;
margin-bottom: 0.5em;
}
h1 { font-size: 2.5rem; }
h2 { font-size: 2rem; }
h3 { font-size: 1.75rem; }
p {
margin-bottom: 1rem;
}
a {
color: #3b82f6;
text-decoration: none;
transition: color 0.2s;
}
a:hover {
color: #2563eb;
}
/* Utility Classes */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 1rem;
}
// ✅ Applied to entire application
// ✅ Base styles everyone needs
// ✅ Consistent foundationImport in Root Layout
import './globals.css';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
// ✅ Import in root layout
// ✅ Styles available everywhere
// ✅ Loaded once for entire app⚠️ Only Import Global Styles in Root Layout
Only import globals.css in the root layout. Importing in multiple places causes duplicate CSS and conflicts. Root layout ensures it loads once for the entire application.
CSS Variables (Custom Properties)
What Are CSS Variables?
CSS variables let you store values and reuse them throughout your stylesheet:
❌ Without Variables
/* Repeated colors everywhere */
.button { background: #3b82f6; }
.link { color: #3b82f6; }
.border { border-color: #3b82f6; }
/* Hard to change theme */
/* Search and replace = error-prone */
/* No dynamic changes✅ With Variables
/* Define once */
:root {
--color-primary: #3b82f6;
}
/* Use everywhere */
.button { background: var(--color-primary); }
.link { color: var(--color-primary); }
.border { border-color: var(--color-primary); }
/* Change once = updates everywhere */
/* Dynamic, reusable, maintainableBasic CSS Variables
:root {
/* Colors */
--color-primary: #3b82f6;
--color-secondary: #6b7280;
--color-success: #10b981;
--color-warning: #f59e0b;
--color-error: #ef4444;
/* Grays */
--color-gray-50: #f9fafb;
--color-gray-100: #f3f4f6;
--color-gray-900: #111827;
/* Spacing */
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 1.5rem;
--spacing-xl: 2rem;
/* Typography */
--font-sans: system-ui, -apple-system, sans-serif;
--font-mono: 'Fira Code', monospace;
/* Font Sizes */
--text-xs: 0.75rem;
--text-sm: 0.875rem;
--text-base: 1rem;
--text-lg: 1.125rem;
--text-xl: 1.25rem;
/* Shadows */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
/* Border Radius */
--radius-sm: 0.25rem;
--radius-md: 0.5rem;
--radius-lg: 0.75rem;
--radius-full: 9999px;
/* Transitions */
--transition-fast: 150ms;
--transition-base: 200ms;
--transition-slow: 300ms;
}
// ✅ Centralized design tokens
// ✅ Easy to maintain
// ✅ Consistent across appUsing CSS Variables
.button {
background: var(--color-primary);
color: white;
padding: var(--spacing-md) var(--spacing-lg);
border-radius: var(--radius-md);
font-size: var(--text-base);
transition: all var(--transition-base);
box-shadow: var(--shadow-md);
}
.button:hover {
background: var(--color-secondary);
box-shadow: var(--shadow-lg);
}
.card {
background: white;
padding: var(--spacing-xl);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-sm);
}
// ✅ var() function accesses variables
// ✅ Consistent spacing, colors, shadows
// ✅ Change variables = updates everywhereFallback Values
/* With fallback (if variable not defined) */
.element {
color: var(--color-primary, #3b82f6);
padding: var(--spacing-md, 1rem);
font-family: var(--font-sans, system-ui);
}
/* Multiple fallbacks */
.element {
color: var(--color-brand, var(--color-primary, blue));
}
// ✅ Fallbacks prevent errors
// ✅ Provides default value
// ✅ Can chain multiple fallbacksTheming with CSS Variables
Light and Dark Theme
:root {
/* Light theme (default) */
--bg-primary: #ffffff;
--bg-secondary: #f9fafb;
--text-primary: #111827;
--text-secondary: #6b7280;
--border-color: #e5e7eb;
}
[data-theme='dark'] {
/* Dark theme */
--bg-primary: #111827;
--bg-secondary: #1f2937;
--text-primary: #f9fafb;
--text-secondary: #d1d5db;
--border-color: #374151;
}
/* Apply theme variables */
body {
background: var(--bg-primary);
color: var(--text-primary);
transition: background 0.3s, color 0.3s;
}
.card {
background: var(--bg-secondary);
border: 1px solid var(--border-color);
}
.text-muted {
color: var(--text-secondary);
}
// ✅ Same variable names for both themes
// ✅ Toggle theme by changing data-theme attribute
// ✅ Smooth transitionsTheme Toggle Component
'use client';
import { useEffect, useState } from 'react';
export function ThemeToggle() {
const [theme, setTheme] = useState<'light' | 'dark'>('light');
useEffect(() => {
// Get saved theme or system preference
const savedTheme = localStorage.getItem('theme') as 'light' | 'dark' | null;
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light';
const initialTheme = savedTheme || systemTheme;
setTheme(initialTheme);
document.documentElement.setAttribute('data-theme', initialTheme);
}, []);
const toggleTheme = () => {
const newTheme = theme === 'light' ? 'dark' : 'light';
setTheme(newTheme);
document.documentElement.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
};
return (
<button
onClick={toggleTheme}
className="p-2 rounded-lg bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 transition"
aria-label="Toggle theme"
>
{theme === 'light' ? '🌙' : '☀️'}
</button>
);
}
// ✅ Persists theme preference
// ✅ Respects system preference
// ✅ Toggles data-theme attribute
// ✅ CSS variables update automaticallyMultiple Color Schemes
:root {
/* Default theme */
--color-primary: #3b82f6;
--color-accent: #8b5cf6;
}
[data-theme='blue'] {
--color-primary: #3b82f6;
--color-accent: #0ea5e9;
}
[data-theme='purple'] {
--color-primary: #8b5cf6;
--color-accent: #a855f7;
}
[data-theme='green'] {
--color-primary: #10b981;
--color-accent: #14b8a6;
}
/* All components use same variables */
.button-primary {
background: var(--color-primary);
}
.button-accent {
background: var(--color-accent);
}
// ✅ Multiple color schemes
// ✅ Switch theme by changing attribute
// ✅ All components update automaticallyDesign Token System
Complete Design Token Setup
:root {
/* ========== COLORS ========== */
/* Brand Colors */
--color-brand-50: #eff6ff;
--color-brand-100: #dbeafe;
--color-brand-200: #bfdbfe;
--color-brand-300: #93c5fd;
--color-brand-400: #60a5fa;
--color-brand-500: #3b82f6;
--color-brand-600: #2563eb;
--color-brand-700: #1d4ed8;
--color-brand-800: #1e40af;
--color-brand-900: #1e3a8a;
/* Semantic Colors */
--color-success: #10b981;
--color-success-light: #d1fae5;
--color-warning: #f59e0b;
--color-warning-light: #fef3c7;
--color-error: #ef4444;
--color-error-light: #fee2e2;
--color-info: #3b82f6;
--color-info-light: #dbeafe;
/* Neutral Colors */
--color-gray-50: #f9fafb;
--color-gray-100: #f3f4f6;
--color-gray-200: #e5e7eb;
--color-gray-300: #d1d5db;
--color-gray-400: #9ca3af;
--color-gray-500: #6b7280;
--color-gray-600: #4b5563;
--color-gray-700: #374151;
--color-gray-800: #1f2937;
--color-gray-900: #111827;
/* ========== SPACING ========== */
--space-0: 0;
--space-1: 0.25rem; /* 4px */
--space-2: 0.5rem; /* 8px */
--space-3: 0.75rem; /* 12px */
--space-4: 1rem; /* 16px */
--space-5: 1.25rem; /* 20px */
--space-6: 1.5rem; /* 24px */
--space-8: 2rem; /* 32px */
--space-10: 2.5rem; /* 40px */
--space-12: 3rem; /* 48px */
--space-16: 4rem; /* 64px */
--space-20: 5rem; /* 80px */
/* ========== TYPOGRAPHY ========== */
/* Font Families */
--font-sans: 'Inter', system-ui, -apple-system, sans-serif;
--font-serif: 'Merriweather', Georgia, serif;
--font-mono: 'Fira Code', 'Courier New', monospace;
/* Font Sizes */
--text-xs: 0.75rem; /* 12px */
--text-sm: 0.875rem; /* 14px */
--text-base: 1rem; /* 16px */
--text-lg: 1.125rem; /* 18px */
--text-xl: 1.25rem; /* 20px */
--text-2xl: 1.5rem; /* 24px */
--text-3xl: 1.875rem; /* 30px */
--text-4xl: 2.25rem; /* 36px */
--text-5xl: 3rem; /* 48px */
/* Font Weights */
--font-normal: 400;
--font-medium: 500;
--font-semibold: 600;
--font-bold: 700;
/* Line Heights */
--leading-none: 1;
--leading-tight: 1.25;
--leading-snug: 1.375;
--leading-normal: 1.5;
--leading-relaxed: 1.625;
--leading-loose: 2;
/* ========== SHADOWS ========== */
--shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
--shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.1);
--shadow-2xl: 0 25px 50px rgba(0, 0, 0, 0.25);
/* ========== BORDER RADIUS ========== */
--radius-none: 0;
--radius-sm: 0.25rem; /* 4px */
--radius-md: 0.5rem; /* 8px */
--radius-lg: 0.75rem; /* 12px */
--radius-xl: 1rem; /* 16px */
--radius-2xl: 1.5rem; /* 24px */
--radius-full: 9999px;
/* ========== Z-INDEX ========== */
--z-0: 0;
--z-10: 10;
--z-20: 20;
--z-30: 30;
--z-40: 40;
--z-50: 50;
--z-dropdown: 1000;
--z-sticky: 1020;
--z-fixed: 1030;
--z-modal: 1040;
--z-popover: 1050;
--z-tooltip: 1060;
/* ========== TRANSITIONS ========== */
--transition-fast: 150ms;
--transition-base: 200ms;
--transition-slow: 300ms;
--transition-slower: 500ms;
--ease-in: cubic-bezier(0.4, 0, 1, 1);
--ease-out: cubic-bezier(0, 0, 0.2, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
/* ========== BREAKPOINTS (for JS) ========== */
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
--breakpoint-2xl: 1536px;
}
// ✅ Complete design token system
// ✅ Organized by category
// ✅ Consistent naming convention
// ✅ Easy to maintain and scaleUsing Design Tokens
.button {
/* Using tokens instead of hard-coded values */
padding: var(--space-3) var(--space-6);
font-size: var(--text-base);
font-weight: var(--font-semibold);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-md);
transition: all var(--transition-base) var(--ease-in-out);
}
.card {
padding: var(--space-6);
border-radius: var(--radius-xl);
box-shadow: var(--shadow-sm);
background: var(--color-gray-50);
}
.heading {
font-size: var(--text-3xl);
font-weight: var(--font-bold);
line-height: var(--leading-tight);
margin-bottom: var(--space-4);
color: var(--color-gray-900);
}
// ✅ Semantic, maintainable
// ✅ Change tokens = updates everywhere
// ✅ Consistent design systemPractical Examples
Example 1: Complete Theme System
:root {
/* Light theme tokens */
--bg-primary: #ffffff;
--bg-secondary: #f9fafb;
--bg-tertiary: #f3f4f6;
--text-primary: #111827;
--text-secondary: #6b7280;
--text-tertiary: #9ca3af;
--border-primary: #e5e7eb;
--border-secondary: #d1d5db;
--surface-elevated: #ffffff;
--surface-overlay: rgba(0, 0, 0, 0.5);
}
[data-theme='dark'] {
/* Dark theme tokens */
--bg-primary: #111827;
--bg-secondary: #1f2937;
--bg-tertiary: #374151;
--text-primary: #f9fafb;
--text-secondary: #d1d5db;
--text-tertiary: #9ca3af;
--border-primary: #374151;
--border-secondary: #4b5563;
--surface-elevated: #1f2937;
--surface-overlay: rgba(0, 0, 0, 0.8);
}
/* Apply theme */
body {
background: var(--bg-primary);
color: var(--text-primary);
}
.card {
background: var(--bg-secondary);
border: 1px solid var(--border-primary);
}
.modal {
background: var(--surface-elevated);
box-shadow: 0 25px 50px var(--surface-overlay);
}
// ✅ Semantic token names
// ✅ Works in both themes
// ✅ Easy to extendExample 2: Responsive Spacing System
:root {
/* Base spacing */
--container-padding: var(--space-4);
--section-spacing: var(--space-12);
--card-padding: var(--space-6);
}
/* Responsive spacing */
@media (min-width: 768px) {
:root {
--container-padding: var(--space-6);
--section-spacing: var(--space-16);
--card-padding: var(--space-8);
}
}
@media (min-width: 1024px) {
:root {
--container-padding: var(--space-8);
--section-spacing: var(--space-20);
}
}
/* Use responsive spacing */
.container {
padding: var(--container-padding);
}
.section {
margin-bottom: var(--section-spacing);
}
.card {
padding: var(--card-padding);
}
// ✅ Spacing scales with screen size
// ✅ DRY - change once, updates everywhere
// ✅ Consistent responsive designExample 3: Component Variants with Variables
/* Button component using variables */
.btn {
/* Use theme variables */
display: inline-flex;
align-items: center;
justify-content: center;
padding: var(--space-3) var(--space-6);
font-size: var(--text-base);
font-weight: var(--font-semibold);
border-radius: var(--radius-lg);
border: 2px solid transparent;
cursor: pointer;
transition: all var(--transition-base);
}
.btn-primary {
background: var(--color-brand-500);
color: white;
}
.btn-primary:hover {
background: var(--color-brand-600);
}
.btn-secondary {
background: var(--color-gray-500);
color: white;
}
.btn-outline {
background: transparent;
border-color: var(--color-brand-500);
color: var(--color-brand-500);
}
.btn-outline:hover {
background: var(--color-brand-500);
color: white;
}
.btn-sm {
padding: var(--space-2) var(--space-4);
font-size: var(--text-sm);
}
.btn-lg {
padding: var(--space-4) var(--space-8);
font-size: var(--text-lg);
}
// ✅ All values from design tokens
// ✅ Change theme = updates all buttons
// ✅ Consistent, maintainableGlobal Styles Structure
Organization of global styles and CSS variables
Select a file or folder to see details
Best Practices
1. Use Semantic Naming
/* ✅ GOOD: Semantic names */
:root {
--bg-primary: #ffffff;
--text-primary: #111827;
--color-success: #10b981;
}
/* ❌ BAD: Non-semantic names */
:root {
--white: #ffffff;
--black: #111827;
--green: #10b981;
}
/* Semantic names describe purpose, not appearance */
/* Makes theming easier (--bg-primary works for light or dark)2. Organize Variables by Category
/* ✅ GOOD: Organized by category */
:root {
/* Colors */
--color-primary: #3b82f6;
--color-secondary: #6b7280;
/* Spacing */
--space-sm: 0.5rem;
--space-md: 1rem;
/* Typography */
--font-sans: system-ui;
--text-base: 1rem;
}
/* ❌ BAD: Random order */
:root {
--color-primary: #3b82f6;
--space-sm: 0.5rem;
--font-sans: system-ui;
--color-secondary: #6b7280;
}
/* Organization makes variables easier to find and maintain3. Document Your Variables
/* ✅ GOOD: Documented */
:root {
/* Primary brand color - used for buttons, links, accents */
--color-primary: #3b82f6;
/* Spacing scale: 4px, 8px, 16px, 24px, 32px */
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-4: 1rem;
--space-6: 1.5rem;
--space-8: 2rem;
}
/* Comments help team understand when to use each variable4. Provide Fallbacks
/* ✅ GOOD: Fallback values */
.element {
color: var(--color-primary, #3b82f6);
padding: var(--space-md, 1rem);
}
/* Prevents errors if variable not defined */
/* Provides sensible defaults5. Use calc() for Computed Values
/* ✅ GOOD: Computed from base values */
:root {
--space-base: 1rem;
--space-half: calc(var(--space-base) / 2);
--space-double: calc(var(--space-base) * 2);
--space-triple: calc(var(--space-base) * 3);
}
.element {
padding: var(--space-base);
margin: var(--space-double);
gap: var(--space-half);
}
/* DRY - change --space-base updates all derived valuesKey Takeaways
- Import globals.css in root layout - available everywhere
- CSS variables - --variable-name: value; in :root
- var() function - var(--variable, fallback)
- Theming - change variables with [data-theme='dark']
- Design tokens - centralized colors, spacing, typography
- Semantic naming - describe purpose, not appearance
- Organize by category - colors, spacing, typography
- Document variables - help team understand usage
What's Next?
You've mastered global styles and CSS variables! Next, we'll explore Next.js Font Optimization—how to use next/font for automatic font optimization, loading Google Fonts efficiently, using local fonts, and eliminating layout shift. You'll learn to make typography fast and beautiful!
next/font automatically optimizes web fonts for performance, eliminating external network requests and preventing layout shift for a better user experience.
🎨 Variables + Tailwind
Combine CSS variables with Tailwind! Define theme colors as CSS variables, then reference them in tailwind.config.js. This gives you the best of both worlds—Tailwind's utilities powered by dynamic CSS variables!