Theming & Styling
Customize theme, dark mode, and typography.
Styling is Tailwind CSS v4 with semantic CSS variables, shadcn/ui
(new-york style) components, and Geist fonts. Light and dark themes are
driven by next-themes.
Important files
| File | Role |
|---|---|
src/app/globals.css | Tailwind import, theme tokens (:root / .dark) |
src/components/theme-provider.tsx | next-themes provider |
src/app/[locale]/layout.tsx | Geist font setup + <html> class |
components.json | shadcn config (style, aliases) |
src/lib/utils.ts | cn() class-merging helper |
Color tokens
The palette is defined as CSS variables in globals.css — a :root block for
light and a .dark block for dark — then exposed to Tailwind through
@theme inline. Components reference semantic tokens, not raw colors, so
classes like bg-background, text-foreground, border-border, and
bg-primary automatically adapt to the active theme.
:root {
--background: #f8fafc;
--foreground: #0f172a;
--primary: #3b76f6; /* brand blue */
--border: #e2e8f0;
/* …card, popover, muted, accent, ring, chart-*, sidebar-* */
}
.dark {
--background: #090807; /* neutral near-black */
--foreground: #f8fafc;
--primary: #3b76f6;
/* … */
}To recolor the app, change the variables — every component that uses the
semantic class follows. The template also defines design-system extras
(--sky, --brand-subtle, --success, --warning) and a soft elevation
shadow scale.
Tailwind v4 is CSS-first: there is no tailwind.config.js. Theme tokens,
the @theme inline mapping, and custom utilities all live in
src/app/globals.css.
Dark mode
next-themes toggles a .dark class on <html>:
<NextThemesProvider
attribute="class"
defaultTheme="light"
storageKey="theme"
disableTransitionOnChange
>The root layout sets suppressHydrationWarning on <html> so the
server/client class mismatch from theme detection doesn't warn, and
disableTransitionOnChange prevents color-fade flicker when the theme flips.
Typography
Fonts are loaded with next/font in the locale layout and exposed as CSS
variables consumed by the --font-sans / --font-mono theme tokens:
const geistSans = Geist({ variable: "--font-geist-sans", subsets: ["latin"] });
const geistMono = Geist_Mono({ variable: "--font-geist-mono", subsets: ["latin"] });
// <html className={`${geistSans.variable} ${geistMono.variable}`}>Geist Sans is the UI typeface; Geist Mono is used for code, labels, and metrics.
shadcn/ui components
components.json configures the new-york style with RSC enabled, lucide
icons, and path aliases (@/components, @/components/ui, @/lib,
@/lib/utils). Add a component with:
pnpm dlx shadcn@latest add <component>It lands in src/components/ui/, already wired to the theme tokens above.
Compose class names with the cn() helper (clsx + tailwind-merge):
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}Where to look
| Directory | Contains |
|---|---|
src/components/ui | shadcn primitives (button, card, dialog, …) |
src/components/layout | Header, footer, sidebar, nav |
src/components/motion | Animation helpers (quiet, reduced-motion aware) |
