Skip to main content
Blog|
How-to guides

How to add custom styles to the WordPress visual editor

|
May 19, 2026|10 min read
HOW-TO GUIDESHow to add custom styles tothe WordPress visual editorHOSTNEYhostney.comMay 19, 2026

Short answer: the styles your visitors see and the styles you see while writing are two different stylesheets. Your theme’s regular CSS – style.css , anything you enqueue with wp_enqueue_style() , anything you add through Appearance > Customize > Additional CSS – only renders on the front end. The classic block editor (Gutenberg) and the older TinyMCE visual editor both load their own iframe with their own stylesheet, so by default the editor canvas does not look like the live site. To make the editor canvas match, you tell WordPress to load an additional editor-side stylesheet: in classic themes, add_editor_style() in functions.php plus an editor-style.css file in your theme; in block themes, add_theme_support('editor-styles') plus the same file, or a properly configured theme.json that the block editor reads automatically. This article walks through both approaches and the per-block overrides that complete the picture.

This is a small but high-value piece of theme work. Without editor styles, writers see Times New Roman on a white page while editing and then publish a post that looks completely different on the live site. With editor styles, the editor renders the same fonts, colors, spacing, and component styles as the published page – what writers see is what readers will see. For agencies handing a site over to a client, this is the difference between a custom theme that feels finished and one that feels half-built.

The two editors you might be styling#

WordPress has shipped two distinct editing surfaces in the last decade, and both are still in the field:

  1. The classic TinyMCE editor – the original “visual” tab in the post editor, still present in classic themes and on any site running the Classic Editor plugin. Renders the post body inside an iframe and applies a stylesheet you provide.
  2. The block editor (Gutenberg) – the default since WordPress 5.0 (December 2018). Renders blocks inside an iframe, applies styles from theme.json plus any registered editor stylesheets, plus per-block style supports.

The mechanics overlap but are not identical. If you are writing a custom theme today, you are almost certainly targeting the block editor. If you maintain a legacy classic theme – which is still common for agencies with long-running client sites – you need the classic approach. This article covers both because most real theme work touches both at some point.

Classic themes: add_editor_style() and editor-style.css#

In a classic theme, two pieces hook the editor styles up:

Step 1: Register the editor stylesheet#

In your theme’s functions.php (or in a child theme’s functions.php ):

function mytheme_editor_styles() {
    add_editor_style( 'editor-style.css' );
}
add_action( 'after_setup_theme', 'mytheme_editor_styles' );

add_editor_style() accepts a string (one stylesheet) or an array (multiple). Paths are relative to the theme’s root directory. WordPress automatically appends ?ver= cache-busting based on theme version.

Step 2: Create editor-style.css in your theme directory#

The file is just CSS. It is loaded inside the editor iframe, so the selectors target the same DOM as the front end – paragraphs, headings, images, blockquotes, etc. A minimal example:

body {
    font-family: 'Inter', system-ui, sans-serif;
    color: #1a1a1a;
    max-width: 720px;
    margin: 0 auto;
    padding: 2rem;
    background: #fff;
}

h1, h2, h3 {
    font-family: 'Playfair Display', Georgia, serif;
    line-height: 1.2;
}

h1 { font-size: 2.5rem; }
h2 { font-size: 1.875rem; margin-top: 2rem; }
h3 { font-size: 1.5rem; }

p { line-height: 1.6; margin-bottom: 1rem; }

a { color: #ea580c; text-decoration: underline; }

blockquote {
    border-left: 4px solid #ea580c;
    padding-left: 1rem;
    color: #525252;
    font-style: italic;
}

img { max-width: 100%; height: auto; }

The result: the editor canvas now renders posts using the same typography and spacing as the live site.

Loading the same fonts in the editor#

If your theme loads a Google Font on the front end, you need to load the same font for the editor iframe. The cleanest pattern is to register a font URL and pass it to add_editor_style() :

function mytheme_editor_styles() {
    // Add a remote font (e.g. Google Fonts)
    add_editor_style(
        'https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=Playfair+Display:wght@700&display=swap'
    );
    // Then your local editor stylesheet
    add_editor_style( 'editor-style.css' );
}
add_action( 'after_setup_theme', 'mytheme_editor_styles' );

The deeper picture of how to load fonts properly – including the GDPR considerations around third-party font CDNs – is in how to add custom fonts to WordPress.

Block themes: theme.json and editor-style.css#

The block editor reads styling from three sources, in increasing order of overhead:

  1. theme.json – the canonical, declarative source. The block editor applies these styles to the editor canvas automatically.
  2. add_theme_support('editor-styles') + an editor-style.css file – works the same way as the classic editor but for the block editor.
  3. Inline block-level style supports – per-block CSS via block.json for custom blocks.

theme.json: the canonical approach#

In WordPress 5.8+, theme.json at the root of your theme is the recommended way to define typography, color palettes, spacing, and layout. The block editor reads it automatically and applies the values to the editor canvas, the front end, and the Site Editor. No separate editor stylesheet needed for the things theme.json covers.

A minimal theme.json :

{
    "$schema": "https://schemas.wp.org/trunk/theme.json",
    "version": 2,
    "settings": {
        "color": {
            "palette": [
                { "name": "Primary", "slug": "primary", "color": "#ea580c" },
                { "name": "Ink", "slug": "ink", "color": "#1a1a1a" },
                { "name": "Paper", "slug": "paper", "color": "#fafafa" }
            ]
        },
        "typography": {
            "fontFamilies": [
                {
                    "name": "Inter",
                    "slug": "inter",
                    "fontFamily": "Inter, system-ui, sans-serif"
                },
                {
                    "name": "Playfair Display",
                    "slug": "playfair",
                    "fontFamily": "'Playfair Display', Georgia, serif"
                }
            ],
            "fontSizes": [
                { "name": "Small", "slug": "small", "size": "0.875rem" },
                { "name": "Medium", "slug": "medium", "size": "1rem" },
                { "name": "Large", "slug": "large", "size": "1.25rem" }
            ]
        }
    },
    "styles": {
        "typography": {
            "fontFamily": "var(--wp--preset--font-family--inter)",
            "lineHeight": "1.6"
        },
        "color": {
            "text": "var(--wp--preset--color--ink)",
            "background": "var(--wp--preset--color--paper)"
        },
        "elements": {
            "h1": {
                "typography": {
                    "fontFamily": "var(--wp--preset--font-family--playfair)",
                    "fontSize": "2.5rem"
                }
            },
            "h2": {
                "typography": {
                    "fontFamily": "var(--wp--preset--font-family--playfair)",
                    "fontSize": "1.875rem"
                }
            },
            "link": {
                "color": { "text": "var(--wp--preset--color--primary)" }
            }
        }
    }
}

The block editor reads this file and styles the editor canvas accordingly – the same typography, palette, and spacing appear in the editor as on the front end. Block inserter dropdowns populate from the palette and font families. The Styles panel in the Site Editor lets the user tweak these values per-site without touching the file. The full breakdown of theme.json fontSizes (including fluid sizing) is in how to change the font size in WordPress.

When theme.json is not enough: editor-style.css for block themes#

theme.json covers typography, colors, spacing, and most layout. It does not cover everything. For custom selectors – say, styling figcaption differently in the editor, or adding a specific border treatment to a callout block, or matching a non-block element that your theme renders around the post – you still need a CSS file.

The pattern in a block theme:

function mytheme_block_editor_styles() {
    add_theme_support( 'editor-styles' );
    add_editor_style( 'editor-style.css' );
}
add_action( 'after_setup_theme', 'mytheme_block_editor_styles' );

add_theme_support('editor-styles') tells the block editor to enqueue your styles inside the iframe (without it, add_editor_style() is silently a no-op for the block editor). Then add_editor_style() works the same way as in classic themes.

The CSS inside editor-style.css should target the editor DOM. The block editor adds a few wrappers – .editor-styles-wrapper , .wp-block , .is-root-container – but for most rules you write against the same selectors as the front end ( p , h2 , .wp-block-quote , etc.) and the wrappers do not get in the way.

Per-block style supports#

For custom blocks you build, the per-block route is block.json ‘s editorStyle and style fields:

{
    "name": "mytheme/callout",
    "title": "Callout",
    "category": "design",
    "editorStyle": "file:./editor.css",
    "style": "file:./style.css",
    "supports": {
        "color": { "background": true, "text": true },
        "spacing": { "padding": true, "margin": true },
        "typography": { "fontSize": true, "lineHeight": true }
    }
}

editorStyle is loaded only in the editor. style is loaded on both the editor and the front end. The supports block tells the editor which built-in controls (color picker, spacing, typography) to render in the block inspector sidebar – the user can adjust the block’s appearance without writing CSS, and the values are stored on the block instance.

This is the pattern most agencies use for custom client blocks: a small block-specific stylesheet for the editor (so the block looks right while being edited) plus a separate style sheet that ships with the live site, plus declarative supports for the controls that should appear in the inspector.

When to use each approach#

NeedApproach
Make the classic TinyMCE editor match the front end add_editor_style('editor-style.css') in functions.php + the CSS file
Make the block editor canvas match the front end (typography, colors, spacing) theme.json covers it
Style custom selectors in the block editor that theme.json does not cover add_theme_support('editor-styles') + add_editor_style()
Make a custom block look right in both editor and front end block.json with editorStyle and style
Adjust appearance per-block without writing CSS block.json supports declarations
Per-site tweak without editing filesSite Editor Styles panel (reads/writes theme.json values)

For a brand-new block theme, start with theme.json . Add editor-style.css only when you need something theme.json cannot express. For a classic theme that is being maintained, the add_editor_style() + editor-style.css pattern is the entire toolkit.

Common mistakes#

  • Forgetting add_theme_support('editor-styles') in a block theme. Without it, add_editor_style() is a no-op in the block editor. The CSS file exists, the registration runs, nothing happens. Always pair the two calls in a block theme.
  • Editing the parent theme’s editor-style.css directly. The change disappears the next time the theme updates. Always make editor-style changes in a child theme (or in your own custom theme that does not get updated from a marketplace).
  • Mixing front-end CSS into editor-style.css. The editor stylesheet is meant for the editor iframe only. If you put global resets, navigation styles, or footer rules in there, the editor canvas tries to render the entire layout, which usually looks wrong because the editor iframe does not have the surrounding DOM.
  • Putting body { background: #fff; } in editor-style.css without thinking about dark editor users. The block editor has a dark theme mode in some browser/OS combinations. Hard-coding the editor background to white overrides what the user picked. Either match the front-end background unconditionally (if your live site is always light), or use prefers-color-scheme to let the user’s setting through.
  • Loading fonts in editor-style.css via @import . @import works but is slow and renders synchronously inside the editor iframe. Pass the font URL directly to add_editor_style() instead.
  • Forgetting that theme.json cascades. A property set at the root styles level applies to every block unless overridden. If your block-level rule is not winning over a global rule, check the cascade order: more specific (element / block) overrides less specific (root).
  • Confusing editor-style.css with editor.css block.json field. The theme-wide editor stylesheet is conventionally named editor-style.css . The per-block stylesheet referenced from block.json can be named anything (often editor.css or index.css ). The naming convention is yours – just be consistent within the theme.
  • Skipping editor styles entirely on agency builds. Writers do not know what the live site looks like while editing, so they write paragraphs that look fine in Times New Roman on a white page and break the layout when published. Editor styles are the cheapest single improvement to a custom theme that clients will actually notice.

How Hostney handles theme development#

Theme files live in wp-content/themes/<your-theme>/ on the server. Hostney’s hosting gives you three ways to get edits in:

  1. The browser-based file manager has a built-in code editor with syntax highlighting for PHP, JSON, and CSS – good for the kind of small edits this article covers (registering an editor stylesheet in functions.php , dropping in an editor-style.css , tweaking a few theme.json values).
  2. SSH access is enabled on every site by default, so the standard git/rsync developer workflow works. Pull the theme down, edit locally in VS Code or whatever editor you use, push back up.
  3. FTPS on the standard FTP user account if you prefer a desktop client.

theme.json validates as plain JSON, so a malformed file does not just look wrong – it stops the block editor from loading. The file manager’s code editor flags JSON syntax errors before save, which catches the most common mistake (trailing commas, missing braces). For deeper development the VS Code WordPress workflow over SSH works the same as on any other host.

Theme edits do not invalidate the page cache automatically – if your theme update changes visible CSS, purge the cache from the Hostney control panel afterwards. Editor-style changes specifically do not need a cache purge because they only affect the editor iframe, which is never cached.

Quick reference#

  • The editor canvas has its own stylesheet, separate from the front-end CSS. Without editor styles, the editor looks nothing like the published site.
  • Classic theme: add_editor_style('editor-style.css') in functions.php + an editor-style.css file in the theme.
  • Block theme: theme.json for typography, colors, spacing, layout. Add add_theme_support('editor-styles') + add_editor_style() for custom selectors theme.json does not cover.
  • Custom blocks: block.json editorStyle (editor only) + style (both) + supports declarations for inspector controls.
  • Load editor fonts by passing the font URL to add_editor_style() directly. Avoid @import inside the stylesheet.
  • Always make editor-style changes in a child theme or your own custom theme, never in a marketplace parent theme.
  • For agencies: editor styles are the single cheapest improvement to a custom theme that clients will notice. Skipping them is the most common reason a custom theme feels half-finished.