ForgeWP

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:

src/app/single.tsx
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:

Terminal
pnpm add -D @tailwindcss/typography

Then register the plugin in src/app/globals.css:

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:

src/app/single.tsx
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:

src/app/single.tsx (Prose Customization)
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:

Generated style.css (Excerpt)
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

src/app/single.tsx
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

src/app/globals.css (Image Captions)
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:

src/app/globals.css (Columns)
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:

CSS rule that can cause editor resizing
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:

src/app/globals.css (Editor Guard)
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:

  1. Your live website remains full-height (100vh).
  2. 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:

  1. Editor Styles: The generated theme enqueues its stylesheet for the block editor.
  2. Design Tokens: WordPress reads supported color, typography, and layout settings from the generated theme.json.
  3. 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:

  1. wp.config.ts provides a single source of truth for design tokens (see Design Tokens & theme.json v3).
  2. globals.css connects Tailwind CSS 4 to WordPress --wp--preset--* variables and provides dark mode (see Tailwind CSS 4 Theming & Tokens).
  3. @tailwindcss/typography and 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.