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:
- 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.
- The block editor (Gutenberg) – the default since WordPress 5.0 (December 2018). Renders blocks inside an iframe, applies styles from
theme.jsonplus 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:
-
theme.json– the canonical, declarative source. The block editor applies these styles to the editor canvas automatically. -
add_theme_support('editor-styles')+ aneditor-style.cssfile – works the same way as the classic editor but for the block editor. - Inline block-level style supports – per-block CSS via
block.jsonfor 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#
| Need | Approach |
|---|---|
| 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 files | Site 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.cssdirectly. 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 useprefers-color-schemeto let the user’s setting through. - Loading fonts in editor-style.css via
@import.@importworks but is slow and renders synchronously inside the editor iframe. Pass the font URL directly toadd_editor_style()instead. - Forgetting that
theme.jsoncascades. A property set at the rootstyleslevel 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.csswitheditor.cssblock.json field. The theme-wide editor stylesheet is conventionally namededitor-style.css. The per-block stylesheet referenced fromblock.jsoncan be named anything (ofteneditor.cssorindex.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:
- 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 aneditor-style.css, tweaking a fewtheme.jsonvalues). - 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.
- 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')infunctions.php+ aneditor-style.cssfile in the theme. - Block theme:
theme.jsonfor typography, colors, spacing, layout. Addadd_theme_support('editor-styles')+add_editor_style()for custom selectorstheme.jsondoes not cover. - Custom blocks:
block.jsoneditorStyle(editor only) +style(both) +supportsdeclarations for inspector controls. - Load editor fonts by passing the font URL to
add_editor_style()directly. Avoid@importinside 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.