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/oig, Next.js renders app/oig/page.tsx. Every route needs one. It changes on every navigation.layout.tsx — The wrapperapp/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
{/* 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;
}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>{/* 1 column on mobile, 2 on medium, 3 on large */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3"><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">
p-4 = 16px, p-6 = 24px, mb-12 = 48px.p-1p-2p-4p-6p-8p-10p-12p-16Class 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">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.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.
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
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.@theme inline blocks in CSS instead of tailwind.config.js. Our theme variables, fonts, and custom colors are all defined in globals.css.globals.css.Configuration Files
Example
{/* Do this */}
<div className="bg-background text-foreground border-border">
{/* Not this */}
<div className="bg-white text-slate-900 border-slate-200">