Styling Gutenberg Content & Editor Parity
Styling raw WordPress post markup with @tailwindcss/typography, handling core Gutenberg blocks, and achieving pixel-perfect editor parity.
While you have complete control over the React components you author for your theme's shell (headers, footers, sidebars, and custom template layouts), the body content of blog posts and pages is created dynamically by content authors inside the WordPress Gutenberg Block Editor.
When you render post content in React using:
1<div dangerouslySetInnerHTML={{ __html: content }} />WordPress outputs raw HTML markup containing standard elements (<p>, <h2>, <blockquote>, <ul>) alongside WordPress-specific block wrappers (<figure class="wp-block-image">, <div class="wp-block-columns">).
In this guide, you will learn how to style this content beautifully using @tailwindcss/typography, customize WordPress core blocks, and ensure pixel-perfect parity inside the WordPress admin editor.
Styling Dynamic Content with @tailwindcss/typography
The easiest, most robust way to style WordPress post content is using the official @tailwindcss/typography plugin.
1. Installation & Setup
Because @tailwindcss/typography is an optional plugin, install it into your theme's devDependencies:
pnpm add -D @tailwindcss/typographyThen register the plugin in src/app/globals.css:
1@import "tailwindcss";
2@plugin "tailwindcss-animate";
3@plugin "@tailwindcss/typography";2. Applying the .prose Class
Wrap your post content in the prose utility class. You can render post content using either useWpContent() or ForgeWP's <BlockArea /> primitive:
1import { useWpTitle, useWpContent, BlockArea } from "@forgewp/react"
2
3export default function SinglePostPage() {
4 const title = useWpTitle()
5 const content = useWpContent()
6
7 return (
8 <main className="mx-auto max-w-4xl px-6 py-16">
9 <h1 className="mb-8 font-heading text-4xl font-black tracking-tight">
10 {title}
11 </h1>
12
13 {/* Method A: Direct HTML Injection with Tailwind Prose */}
14 <article
15 className="prose prose-slate dark:prose-invert max-w-none"
16 dangerouslySetInnerHTML={{ __html: content }}
17 />
18
19 {/* Method B: Bounded Editorial Block Slot */}
20 {/* <div className="prose prose-slate max-w-none dark:prose-invert">
21 <BlockArea name="editorial-content" />
22 </div> */}
23 </main>
24 )
25}prose: Immediately provides balanced typographic hierarchy, comfortable line-heights, vertical rhythm, and standard margins for all child HTML elements.max-w-none: Removes the default 65-character line width cap so the content fills the bounds of your layout container.dark:prose-invert: Inverts heading, body, and link colors automatically when dark mode (.dark) is active.
Will the `prose` class affect my custom navigation, cards, or footer?
No! The prose class scopes its typographical rules strictly to child elements nested inside the container that carries the prose class (e.g. <article className="prose ...">). It will never leak into your site header, footer, navigation links, or sidebar cards.
3. Customizing Prose with Tailwind Modifiers
You can customize individual typographic elements directly on the container:
1<article
2 className="prose prose-slate dark:prose-invert prose-headings:font-heading prose-headings:font-bold prose-headings:tracking-tight prose-a:text-brand prose-a:font-semibold prose-a:underline hover:prose-a:opacity-85 prose-img:rounded-2xl prose-img:border prose-img:border-border prose-img:shadow-md prose-blockquote:border-l-4 prose-blockquote:border-brand prose-blockquote:italic prose-pre:bg-zinc-950 prose-pre:border prose-pre:border-zinc-800 max-w-none"
3 dangerouslySetInnerHTML={{ __html: content }}
4/>ForgeWP's Built-In Content Width System
In WordPress, content authors can assign Wide (alignwide) or Full Width (alignfull) alignments to blocks (such as images, cover blocks, and quote callouts).
Native .forgewp-content-width Class
Rather than requiring you to calculate complex breakout margins manually, ForgeWP automatically includes the .forgewp-content-width layout system in style.css:
1.forgewp-content-width {
2 max-width: var(--wp--style--global--content-size, 720px);
3 margin-left: auto;
4 margin-right: auto;
5}
6.forgewp-content-width > .alignwide {
7 max-width: var(--wp--style--global--wide-size, 1200px);
8 margin-left: auto;
9 margin-right: auto;
10}
11.forgewp-content-width > .alignfull {
12 max-width: none;
13 width: 100vw;
14 margin-left: calc(50% - 50vw);
15 margin-right: calc(50% - 50vw);
16}Because ForgeWP automatically generates add_theme_support('align-wide') and add_theme_support('responsive-embeds') during theme compilation, any container with .forgewp-content-width will smoothly respect the contentSize (720px) and wideSize (1200px) values declared in your wp.config.ts!
Example Usage in Post Templates
1<article className="forgewp-content-width prose prose-slate dark:prose-invert max-w-none">
2 <div dangerouslySetInnerHTML={{ __html: content }} />
3</article>Handling Core Gutenberg Blocks
WordPress core blocks output standardized class names. Here is how to style common Gutenberg elements in src/app/globals.css:
1. Image Captions & Figures
1.wp-block-image {
2 margin-top: 2rem;
3 margin-bottom: 2rem;
4}
5
6.wp-block-image.aligncenter {
7 text-align: center;
8}
9
10.wp-block-image figcaption {
11 margin-top: 0.5rem;
12 font-size: 0.875rem;
13 color: var(--color-muted-foreground);
14 text-align: center;
15}2. Multi-Column Layouts (wp-block-columns)
Gutenberg column blocks output flex containers:
1.wp-block-columns {
2 display: flex;
3 flex-wrap: wrap;
4 gap: 1.5rem;
5 margin-top: 2rem;
6 margin-bottom: 2rem;
7}
8
9@media (min-width: 768px) {
10 .wp-block-columns {
11 flex-wrap: nowrap;
12 }
13}
14
15.wp-block-column {
16 flex: 1 1 0;
17}The Gutenberg Editor Reset & Height Loop Guard
One of the most insidious bugs in WordPress block theming occurs when using modern CSS frameworks like Tailwind: the Gutenberg infinite height feedback loop.
The Problem
Tailwind developers frequently add min-h-screen or min-height: 100vh to the body tag in globals.css:
1body {
2 min-height: 100vh; /* ⚠️ Breaks Gutenberg iframe! */
3}Inside the WordPress block editor, editor content can be rendered in an iframe using .editor-styles-wrapper.
When the iframe detects min-height: 100vh, it resizes itself to fit the content. But because resizing increases the viewport height, 100vh calculates to a larger value, causing the iframe to expand again, forever triggering an infinite re-render loop that freezes the browser!
The Solution: The ForgeWP Editor Guard
Every ForgeWP theme includes a built-in guard in src/app/globals.css under the @layer base directive:
1@layer base {
2 /* Apply full screen height ONLY on the live front-end, never in the editor */
3 body:not(.editor-styles-wrapper):not(#tinymce):not(.mce-content-body) {
4 min-height: 100vh;
5 }
6
7 /* Force the Gutenberg editor iframe wrapper to use natural content height */
8 .editor-styles-wrapper {
9 min-height: auto !important;
10 }
11}With this guard in place:
- Your live website remains full-height (
100vh). - The Gutenberg block editor runs smoothly without freezing or oscillating.
Styling the WordPress Block Editor
When content creators edit a post or page, they benefit from the same fonts and design tokens as the front end. The block editor is not a full preview of ForgeWP's PHP-generated page layout.
ForgeWP handles editor parity automatically:
- Editor Styles: The generated theme enqueues its stylesheet for the block editor.
- Design Tokens: WordPress reads supported color, typography, and layout settings from the generated
theme.json. - Content Styling: Use editor-compatible CSS and test Gutenberg blocks in the actual WordPress editor; frontend-only layout wrappers may not appear as they do on the public page.
Summary
With the styling and design system configured, your theme's visual foundation is complete:
wp.config.tsprovides a single source of truth for design tokens (see Design Tokens & theme.json v3).globals.cssconnects Tailwind CSS 4 to WordPress--wp--preset--*variables and provides dark mode (see Tailwind CSS 4 Theming & Tokens).@tailwindcss/typographyand the Gutenberg editor guard deliver seamless styling for both visitors and content editors.
Next, we move to the WordPress Data Layer, where you will learn how to connect real posts, menus, taxonomies, and custom fields to your layout! Proceed to Single Post Primitives & Core Hooks.