ForgeWP

Styling with Tailwind CSS 4

Master zero-config Tailwind CSS 4, theme.json CSS variable integration, dynamic prose styling, and Gutenberg editor compatibility.

ForgeWP themes use Tailwind CSS 4 for styling. Unlike earlier versions of Tailwind that required complex configuration files (tailwind.config.js), PostCSS pipelines, and custom asset build scripts, Tailwind CSS 4 in ForgeWP is zero-config and CSS-native.

In this guide, you will learn how Tailwind CSS 4 integrates with ForgeWP, how to synchronize design tokens with WordPress theme.json, and how to style both React components and dynamic WordPress Gutenberg content.

CSS-First Configuration

In Tailwind CSS 4, all design tokens, theme customizations, and plugin registrations live directly inside your CSS file (src/app/globals.css) via the @theme directive. There is no tailwind.config.js or tailwind.config.ts to maintain.


Zero-Config Architecture

Every new ForgeWP theme includes a streamlined src/app/globals.css that imports the core Tailwind engine and animation plugins:

src/app/globals.css (Tailwind CSS 4 Setup)
1@import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&display=swap");
2@import "tailwindcss";
3@plugin "tailwindcss-animate";
4
5@theme {
6  /* Font Family Mappings */
7  --font-sans: var(
8    --wp--preset--font-family--sans,
9    "Segoe UI",
10    system-ui,
11    sans-serif
12  );
13  --font-heading: "Space Grotesk", var(--font-sans);
14
15  /* Color Palette Mappings linked to WordPress presets */
16  --color-brand: var(--wp--preset--color--brand, #0f172a);
17  --color-brand-foreground: #ffffff;
18  --color-secondary: var(--wp--preset--color--secondary, #4f46e5);
19  --color-accent: var(--wp--preset--color--accent, #f59e0b);
20  --color-background: var(--wp--preset--color--background, #ffffff);
21  --color-foreground: var(--wp--preset--color--text, #09090b);
22
23  /* UI Borders and Surfaces */
24  --color-border: #e4e4e7;
25  --color-muted: #f4f4f5;
26  --color-muted-foreground: #71717a;
27}

This single stylesheet achieves three key goals:

  1. Zero build boilerplate: Vite processes @import 'tailwindcss' natively with instantaneous Hot Module Replacement (HMR).
  2. Gutenberg Design Token Sync: CSS variables defined in theme.json (such as --wp--preset--color--brand) are bound directly into Tailwind's utility class generator.
  3. Full Utility Availability: All Tailwind classes (bg-brand, text-foreground, font-heading, border-border) automatically become available across your React components.

How ForgeWP Syncs Tailwind with WordPress theme.json

A frequent pain point in headless or hybrid WordPress development is maintaining two sets of design tokens: one in CSS/Tailwind for developers, and one in theme.json for Gutenberg site administrators.

ForgeWP solves this with a unified design token pipeline:

Design Token Synchronization Pipeline
Source of Truth:
  wp.config.ts -> defineConfig({ settings: { color: { palette: [...] } } })

Target 1: WordPress Block Editor (theme.json)
  - Variable:  var(--wp--preset--color--brand)
  - Consumer:  WordPress block editor controls and block content

Target 2: React Theme Components (globals.css)
  - Utilities: bg-brand, text-brand, border-brand
  - Consumer:  React components in src/app/

WordPress generates CSS custom properties from the exported theme.json presets. Tailwind utilities that reference those properties use the generated value. Manage the theme-wide palette in wp.config.ts and export it to your PHP-backed hybrid theme.


Essential Layout Utilities for Theme Builders

Modern WordPress themes require responsive, adaptable grid and flexbox layouts. Here are the core patterns used across ForgeWP themes:

1. Responsive Multi-Column Grids

src/components/ArchiveGrid.tsx
1export function ArchiveGrid({ children }: { children: React.ReactNode }) {
2  return (
3    <div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:gap-8">
4      {children}
5    </div>
6  )
7}
  • grid-cols-1: 1 column on mobile phones (< 640px).
  • sm:grid-cols-2: 2 columns on tablets (≥ 640px).
  • lg:grid-cols-3: 3 columns on desktop monitors (≥ 1024px).
  • gap-6 xl:gap-8: Spacing automatically expands on extra-large screens.

2. Header and Sticky Navigation

src/components/SiteHeader.tsx
1export function SiteHeader() {
2  return (
3    <header className="sticky top-0 z-50 w-full border-b border-zinc-200 bg-white/80 backdrop-blur-md">
4      <div className="mx-auto flex h-16 max-w-7xl items-center justify-between px-4 sm:px-6 lg:px-8">
5        <a href="/" className="text-xl font-bold tracking-tight text-zinc-950">
6          Studio<span className="text-zinc-500">Press</span>
7        </a>
8        <nav className="hidden items-center gap-8 text-sm font-medium text-zinc-600 md:flex">
9          <a href="/about" className="transition hover:text-zinc-950">
10            About
11          </a>
12          <a href="/services" className="transition hover:text-zinc-950">
13            Services
14          </a>
15          <a href="/work" className="transition hover:text-zinc-950">
16            Work
17          </a>
18          <a href="/contact" className="transition hover:text-zinc-950">
19            Contact
20          </a>
21        </nav>
22      </div>
23    </header>
24  )
25}
  • sticky top-0 z-50: Pins the header to the top of the viewport during scrolling.
  • bg-white/80 backdrop-blur-md: Glassmorphism effect that blurs content scrolling underneath.
  • hidden md:flex: Hides desktop navigation on mobile viewports so a mobile drawer can take over.

3. Aspect-Ratio Media Containers

WordPress users upload images with varying resolutions and orientations. To prevent layout shifts (CLS), wrap thumbnails in responsive aspect-ratio containers:

src/components/PostThumbnail.tsx
1export function PostThumbnail({ src, alt }: { src: string; alt: string }) {
2  return (
3    <div className="relative aspect-video w-full overflow-hidden bg-zinc-100">
4      <img
5        src={src}
6        alt={alt}
7        className="h-full w-full object-cover transition-transform duration-500 hover:scale-105"
8        loading="lazy"
9      />
10    </div>
11  )
12}

Styling WordPress Post Content with Typography (prose)

When rendering the body of a WordPress post or page, content is generated by Gutenberg blocks and contains arbitrary HTML: paragraphs, headings, blockquotes, unordered lists, and embedded images.

In React, this content is passed to your component and rendered using dangerouslySetInnerHTML:

src/app/single.tsx (Post Content Rendering)
1import { useWpContent, useWpTitle } from "@forgewp/react"
2
3export default function SinglePost() {
4  const title = useWpTitle()
5  const content = useWpContent()
6
7  return (
8    <main className="mx-auto max-w-3xl px-6 py-16">
9      <h1 className="text-4xl font-black tracking-tight text-zinc-950 sm:text-5xl">
10        {title}
11      </h1>
12
13      {/* Styled with Tailwind Typography prose classes */}
14      <article
15        className="prose prose-zinc prose-headings:font-bold prose-headings:tracking-tight prose-a:text-zinc-900 prose-a:underline prose-img:rounded-none prose-img:border mt-8 max-w-none"
16        dangerouslySetInnerHTML={{ __html: content }}
17      />
18    </main>
19  )
20}

The @tailwindcss/typography plugin provides the prose class family, which automatically applies sensible, beautiful typographic hierarchy to raw HTML:

Table · 2 columns
Class
Purpose
prose
Activates typographic sizing, margins, line-heights, and vertical rhythm.
prose-zinc
Applies neutral zinc shades to text, headings, blockquotes, and code blocks.
max-w-none
Prevents the default 65ch width restriction so the content fills the parent container.
prose-headings:font-bold
Enforces bold weight on h1, h2, h3, and h4 tags generated by Gutenberg.
prose-img:border
Adds a crisp border around embedded post images for clean contrast.

Gutenberg Block Editor Compatibility

When WordPress loads your theme inside the Gutenberg block editor (for full-site editing or post editing), it renders the content inside an iframe with the class .editor-styles-wrapper.

Standard CSS resets that set min-height: 100vh on body can cause an infinite height feedback loop inside the Gutenberg editor iframe. ForgeWP's globals.css protects against this:

src/app/globals.css (Gutenberg Reset Guards)
1@layer base {
2  *,
3  ::before,
4  ::after {
5    border-color: var(--color-border);
6  }
7
8  body {
9    @apply bg-background font-sans text-foreground antialiased;
10  }
11
12  /* Apply 100vh only in front-end preview, NOT inside the Gutenberg editor iframe */
13  body:not(.editor-styles-wrapper):not(#tinymce):not(.mce-content-body) {
14    min-height: 100vh;
15  }
16}
17
18/* Prevent Gutenberg editor iframe height feedback loop */
19.editor-styles-wrapper {
20  min-height: auto !important;
21}

Next Step: Interactivity & State

Proceed to State, Interactivity & Auto-Hydration to learn how ForgeWP detects interactive islands and handles client hydration without manual directives. For advanced typography and design token configuration, explore Styling & Design System.