How do you set up a Next.js Tailwind marketing site with Tailwind v4?
You set up Tailwind v4 in Next.js with three moves: install the packages, register the PostCSS plugin, and import Tailwind in your global stylesheet. Everything else, including your brand tokens, is written in CSS.
That's a real change from Tailwind v3, where most setup happened in a JavaScript config file. For a marketing site it's a good change: tokens are plain CSS variables, so they're visible in the browser, easy to theme and simple to share with non-Tailwind code. If you're still deciding whether a coded site suits your team, start with Next.js for a marketing website.
Install the packages
Add tailwindcss, @tailwindcss/postcss and postcss as dev dependencies. Recent create-next-app versions can set this up for you.
Register the PostCSS plugin
In postcss.config.mjs, export a plugins object containing "@tailwindcss/postcss": {}. No autoprefixer is needed; v4 handles vendor prefixes.
Import Tailwind
At the top of app/globals.css, add @import "tailwindcss"; and import that stylesheet in your root layout.
Define tokens in @theme
Below the import, add an @theme block with your colors, fonts, spacing and breakpoints as CSS variables.
Wire fonts through next/font
Load fonts with next/font, expose each as a CSS variable on the html element, and point your font tokens at those variables.
Check the build
Run the dev server, confirm utilities like bg-ink and font-display work, and check that production CSS only includes classes you use.
How do you define design tokens in Tailwind v4?
Define design tokens in Tailwind v4 by declaring CSS variables inside @theme using Tailwind's namespaces. A variable named --color-ink creates utilities like bg-ink, text-ink and border-ink, and stays available as var(--color-ink) for any custom CSS.
Namespace | Example token | Utilities it creates |
|---|---|---|
--color-* | --color-ink: oklch(0.2 0.02 260); | bg-ink, text-ink, border-ink |
--font-* | --font-display: var(--font-serif); | font-display |
--text-* | --text-hero: clamp(2.5rem, 6vw, 5rem); | text-hero |
--spacing | --spacing: 0.25rem; | p-4, gap-6, mt-12 scale from one base |
--breakpoint-* | --breakpoint-3xl: 112rem; | 3xl: variant |
--radius-* | --radius-card: 1.25rem; | rounded-card |
--ease-* | --ease-out-soft: cubic-bezier(0.2, 0.8, 0.2, 1); | ease-out-soft |
If you want only your tokens and none of Tailwind's default palette, reset a namespace first with a line like --color-*: initial; inside @theme. On marketing sites we usually do this for colors, so nobody reaches for a stray default blue, and keep the default spacing scale.
How should you handle fluid type and section spacing?
Handle fluid type and spacing with clamp() values stored as tokens, so headings and section padding scale smoothly between phone and desktop without a pile of breakpoint classes. Marketing pages have big type and generous rhythm, which is where clamp() pays off.
- Display sizes as tokens: --text-hero and --text-section with clamp() values, used as text-hero and text-section.
- Body copy stays fixed: keep base text at a readable fixed size and only scale headings.
- Section rhythm as a variable: define a --section-space variable and use it as py-(--section-space), the v4 shorthand for arbitrary CSS variables.
- Container width as a utility: create a container utility with @utility so every section aligns to the same column.
Keeping these decisions in tokens means a designer can tune the whole site's rhythm in one file, and layout shifts stay predictable, which helps the scores covered in our Core Web Vitals fixes for Framer, Webflow and Next.js.
How do you add dark mode and custom variants?
Add class-based dark mode in v4 with a custom variant: @custom-variant dark (&:where(.dark, .dark *)); in your CSS. Without it, the dark: variant follows the visitor's system preference, which is fine for many marketing sites.
A cleaner pattern for themed sections is to switch token values instead of writing dark: everywhere. Give a section a data-theme attribute, and redefine --color-ink and --color-paper for that attribute in plain CSS. Every utility inside then flips automatically. This is also how we handle dark hero bands on otherwise light pages.
Example: a minimal globals.css for a marketing site
For example, a lean starting file might contain, in order: the line @import "tailwindcss"; then an @theme block that resets --color-* to initial and defines --color-ink, --color-paper, --color-accent and --color-muted; --font-sans and --font-display pointing at next/font variables; --text-hero and --text-section with clamp() values; and --radius-card.
After that, a @custom-variant line for class-based dark mode, a @utility container rule with max-width and side padding, and a small [data-theme=dark] block that swaps ink and paper. That's the whole design-token layer for most startup sites.
What changed from Tailwind v3 that trips people up?
The changes that trip people up are the move from JavaScript to CSS config, automatic content detection and a handful of renamed utilities. Projects upgraded by hand often look almost right, with small spacing, shadow and ring differences.
v3 habit | v4 reality |
|---|---|
tailwind.config.js for everything | @theme in CSS; a JS config only via @config for legacy setups |
content array of file globs | Automatic detection; @source for extra paths such as a shared package |
@tailwind base, components, utilities | One @import "tailwindcss" line |
shadow-sm, rounded-sm, blur-sm | Renamed to shadow-xs, rounded-xs, blur-xs; the bare names shifted up a step |
outline-none | outline-hidden for the old behavior |
ring is 3px by default | ring is 1px; use ring-3 for the old look |
Plugins in the JS config | @plugin directive in CSS |
Does Tailwind affect marketing site performance?
Tailwind itself is light: v4 generates only the classes you use, so production CSS for a marketing site stays small. Performance problems on Tailwind sites usually come from elsewhere, like heavy client components, animation libraries or third-party scripts.
Two habits help. Keep animation in CSS transitions and Tailwind's transition utilities where you can, rather than a JavaScript library for every hover. And keep interactive components small, since hydration cost, not CSS, drives slow interactions, as explained in improving INP on Next.js sites. For multilingual sites, prefer logical utilities like ms-4 and pe-6 over ml-4 and pr-6, so right-to-left languages work without overrides; our guide to multilingual Next.js sites covers the routing side.
This is the stack we build on ourselves: Next.js, Tailwind v4 and design tokens mirrored in Figma. Our fixed-price website is $8,000 for up to six pages if you'd rather have the setup, sections and CMS done for you; see pricing.
