Our styling foundation. Every visual element — spacing, color, typography, layout — is expressed through Tailwind utility classes. We use version 4 with pure CSS configuration, no JavaScript config file.

React Component Basics

Every page and UI element is a React component — a JavaScript function that returns HTML-like code called JSX. Imports go at the top, then you export a function that returns the markup. That's it.

// A basic page component in Next.js

import { Button } from "@/components/ui/button";

export default function Home() {
  return (
    <div className="max-w-5xl mx-auto px-6 py-12">
      <h1 className="text-4xl font-bold text-foreground">
        Hello World
      </h1>
      <p className="text-sm text-muted-foreground mt-2">
        This is JSX — HTML written inside JavaScript.
      </p>
      <Button className="mt-4">Click me</Button>
    </div>
  );
}

The className attribute is where Tailwind utilities go. Every visual style — colors, spacing, typography, layout — is applied here as utility classes.

Pages vs Layouts

Next.js has two special files in every route folder. Understanding the difference is essential.

page.tsx — The content
The unique content for that route. When you visit /oig, Next.js renders app/oig/page.tsx. Every route needs one. It changes on every navigation.
layout.tsx — The wrapper
Wraps the page and persists across navigation. Headers, sidebars, footers go here. The root app/layout.tsx wraps the entire app. Nested layouts like app/oig/layout.tsx wrap only their section.
app/
├── layout.tsx          ← Root layout (body, footer) — wraps everything
├── page.tsx            ← Landing page (/)
├── design/
│   └── page.tsx        ← Design system (/design)
└── oig/
    ├── layout.tsx      ← OIG layout (header, nav) — wraps only /oig/*
    ├── page.tsx         ← Reports page (/oig)
    ├── ask/page.tsx     ← Q&A page (/oig/ask)
    └── about/page.tsx   ← About page (/oig/about)

Key Concepts

Utility Classes
Instead of writing CSS in separate files, you apply small single-purpose classes directly in your HTML/JSX. Each class does one thing.
{/* Tailwind utilities */}
<div className="flex items-center gap-4 p-6 rounded-xl border">

{/* What that replaces in traditional CSS */}
.card {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1.5rem;
  border-radius: 0.75rem;
  border: 1px solid;
}
CSS Variables
Colors and design tokens are stored as CSS variables in globals.css. Tailwind classes reference these variables instead of hardcoded colors. Change the variable once, everything updates.
/* globals.css — the variable */
:root {
  --primary: oklch(0.685 0.169 237);  /* #0ea5e9 */
}

/* In your JSX — the class references the variable */
<button className="bg-primary text-primary-foreground">
  Click me
</button>
Responsive Design
Prefix any utility with a breakpoint to apply it at that screen size and above. Mobile-first — the unprefixed class is the default.
{/* 1 column on mobile, 2 on medium, 3 on large */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
Hover, Focus, and States
Prefix utilities with state modifiers to style interactive states. No separate CSS selectors needed.
<button className="bg-primary hover:bg-primary/90 focus:ring-2 focus:ring-ring">
<a className="text-primary hover:text-primary/80">
<input className="border-border focus:border-primary">
Spacing Scale
Tailwind uses a consistent spacing scale where each unit = 4px. So p-4 = 16px, p-6 = 24px, mb-12 = 48px.
p-1
4px
p-2
8px
p-4
16px
p-6
24px
p-8
32px
p-10
40px
p-12
48px
p-16
64px

Class Precedence

When you put conflicting Tailwind classes on the same element, the last one in the CSS stylesheet wins — not the last one in your className string. This is a common source of bugs.

{/* BROKEN — text-lg and text-4xl conflict, text-muted-foreground and text-blue-600 conflict */}
<p className="text-lg text-muted-foreground mb-12 text-4xl font-bold text-blue-600">

{/* FIXED — pick one of each, no conflicts */}
<p className="text-4xl font-bold text-blue-600 mb-12">
Rule: One class per property
Don't stack text-lg and text-4xl — they both set font-size. Same with text-foreground and text-blue-600 — both set color. Only one can win.
Why order in className doesn't matter
Tailwind generates a stylesheet at build time. The order in that stylesheet determines which class wins, not the order you wrote them in JSX. You can't override a class by putting it later in the string.
Use tailwind-merge for dynamic classes
When you need to conditionally override a class (e.g., a component that accepts a className prop), use the cn() helper from lib/utils.ts. It uses tailwind-merge under the hood to intelligently resolve conflicts.
import { cn } from "@/lib/utils";

// cn() resolves conflicts — the last argument wins
cn("text-lg text-foreground", "text-4xl text-primary")
// Result: "text-4xl text-primary" (conflicts resolved correctly)

Class Reference

Tailwind has hundreds of utility classes and they change between versions. You can't memorize them all — use the official docs as a lookup reference. We're on v4, so always check the v4 docs.

Layoutflex, grid, block, hidden, relative, absolute
Docs
Spacingp-*, m-*, gap-*, space-*
Docs
Sizingw-*, h-*, max-w-*, min-h-*
Docs
Typographytext-*, font-*, leading-*, tracking-*
Docs
Colorsbg-*, text-*, border-*, ring-*
Docs
Bordersborder-*, rounded-*, ring-*
Docs
Effectsshadow-*, opacity-*, blur-*
Docs
Transitionstransition-*, duration-*, ease-*
Docs

Build Tool

Tailwind is processed at build time by Turbopack, the Rust-based bundler built into Next.js 16. When you write a utility class like bg-primary, Turbopack runs PostCSS with the @tailwindcss/postcss plugin, which scans your JSX files, finds every class you used, and generates only the CSS for those classes. Unused classes are never shipped — the final CSS bundle is small regardless of how many utilities exist.

How We Use It

CSS Variables, Not Utility Colors
We never write bg-slate-900 or text-sky-500. Instead we use semantic classes like bg-foreground and text-primary that map to CSS variables in globals.css.
CSS-Based Configuration
Tailwind v4 uses @theme inline blocks in CSS instead of tailwind.config.js. Our theme variables, fonts, and custom colors are all defined in globals.css.
Utility-First, No Custom CSS
All styling is done with utility classes directly in JSX. We don't write custom CSS files for components. The only CSS file is globals.css.

Configuration Files

globals.css
app/globals.css
Source of truth for all design tokens
PostCSS Config
postcss.config.mjs
Uses @tailwindcss/postcss plugin

Example

{/* Do this */}
<div className="bg-background text-foreground border-border">

{/* Not this */}
<div className="bg-white text-slate-900 border-slate-200">

Quick Reference

Version4
ConfigCSS-based (@theme inline in globals.css)
PostCSS Plugin@tailwindcss/postcss
Custom Variants@custom-variant dark (&:is(.dark *))

Useful Docs

Colorscolors
Spacingspacing
Responsive Designresponsive-design
Dark Modedark-mode
CSS Variables in v4theme