Styling: CSS Modules and global styles
Every component built across this series has rendered with no real styling at all. Next.js supports CSS in a few different ways out of the box — this part covers the two this project actually uses: CSS Modules, scoped per component, and globals.css for the small amount that genuinely needs to apply everywhere.
A CSS Module
/* app/components/PostCard.module.css */
.card {
border: 1px solid #ddd;
padding: 1.5rem;
border-radius: 8px;
}
.title {
font-size: 1.25rem;
margin: 0 0 0.5rem;
}// app/components/PostCard.tsx
import styles from "./PostCard.module.css";
export function PostCard({ title, excerpt }: { title: string; excerpt: string }) {
return (
<div className={styles.card}>
<h2 className={styles.title}>{title}</h2>
<p>{excerpt}</p>
</div>
);
}The .module.css suffix is what tells Next.js's build process to treat this file as a CSS Module rather than a plain global stylesheet — every class name inside it gets compiled to something unique to this file (PostCard_card__a1b2c, roughly), which is what styles.card actually resolves to at runtime. Two different components can both define a class named .card in their own .module.css files with zero risk of one silently overriding the other's styles — the scoping is real, enforced by the build, not just a naming convention to remember.
globals.css: for what genuinely needs to be global
/* app/globals.css */
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: system-ui, sans-serif;
color: #1a1a1a;
}// app/layout.tsx
import "./globals.css";Imported exactly once, in the root layout — this is the one file in the project where an un-scoped body selector or a CSS reset genuinely belongs, since those specifically need to apply everywhere, not to one component. Reaching for globals.css for a component-specific style defeats the entire point of CSS Modules' scoping; the reverse mistake — trying to reset box-sizing globally from inside a .module.css file — technically works but is the wrong file to own something meant to apply everywhere.
Combining classes conditionally
import styles from "./PostCard.module.css";
<div className={`${styles.card} ${featured ? styles.featured : ""}`}>Template-literal string concatenation is the plain-CSS-Modules way to combine a base class with a conditional one — no special API needed, since styles.card and styles.featured are both just plain strings once compiled.
Defining the same class name — .card, .title — directly in globals.css instead of a CSS Module, then being surprised when a completely unrelated component elsewhere in the app picks up styles never intended for it. That kind of accidental collision is exactly what CSS Modules exist to make structurally impossible.
Next: fonts — system-ui above works, but a custom typeface needs next/font to load without becoming a layout-shift or performance liability.