Theme.json layout and spacing options

Level: ,

Estimated reading time: 14 minutes

In this lesson, you will learn about basic usage of the theme.json layout and spacing options, including content width, spacing and spacing units, and blockGap.

Prerequisites: Global Styles & theme.json.

Updated October 27, 2022, for Gutenberg version 14.4.0 and WordPress 6.1.

The layout setting

The theme.json layout setting decides the width of the content and enables wide and full width blocks. This setting replaces add_theme_support( 'align-wide' );.
If you do not enable the layout setting in theme.json, the blocks are full width and aligned to the left in the block editor.

How to set content width using theme.json

The layout setting uses two key and value pairs:
contentSize sets the default width of the blocks in the editor and front.
wideSize enables the wide width option and sets the width for wide blocks.

  • You can use contentSize alone to allow full width but not wide width.
  • There is no theme.json setting for specifying a more precise width for the full width option.

If you include a value for the wideSize but not the contentSize, the editor will show the align none and align wide options, but the actual width of the block is the value placed in the wideSize setting.

Theme.json code example:

{
	"version": 2,
	"settings": {
		"layout": {
			"contentSize": "840px",
			"wideSize": "1100px"
		}
	}
}

Pixels are only one example; you can use any valid CSS width.

It is not possible to have different default content widths for different blocks or to disable wide and full widths for some blocks.

The setting uses a max-width combined with auto margins:

Default (called “None” in the editor):

.wp-container-id > * {
    max-width: 840px;
    margin-left: auto !important;
    margin-right: auto !important;
}

Wide:

.wp-container-id > .alignwide {
    max-width: 1100px;
}

Full:

.wp-container-id .alignfull {
    max-width: none;
}

The block editor layout control

When you add the layout setting to theme.json, you enable a new toggle control in the editor.
The following blocks use the control: Query loop, group, post content, and column.

The wording of this control has changed over time, and different attempts have been made to make the control easier to use. The iterations described here are probably not the last changes we will see.

WordPress 6.0:

The layout control in the block settings sidebar uses a toggle to inherit the default layout. Below the toggle are two input fields: One for the content width, and one for the wide content width.
The panel also includes a Reset button.

Gutenberg 13.7 or newer:

The updated toggle control for the layout setting is toggled on by default (checked).

When “inherit default layout” is toggled off or “Inner blocks use full width” is toggled on:

  • You can specify the width of the inner blocks using the input fields in the layout panel.
  • If you do not specify the width of the inner blocks, they fill the width of the parent block.
  • The inner blocks do not have wide or full width options in the block toolbar.
  • You can position inner blocks that support left, right, and center alignments.

If “inherit default layout” is toggled on or “Inner blocks use full width” is toggled off:

  • The content width options in the layout panel are unavailable.
  • You can set the width of inner blocks to none (default, contentSize), wide (wideSize) and full using their block toolbar.
  • You can position inner blocks that support left, right, and center alignments.

Margin and padding

How to enable margin and padding in theme.json

Margin and padding settings are disabled (false) by default. If you have enabled appearanceTools in the settings section of theme.json, you do not need to enable margin and padding separately.

You can enable margin and padding for all supported blocks under settings.spacing in theme.json:

{
	"version": 2,
	"settings": {
		"spacing": {
			"margin": true,
			"padding": true
		}
	}
}

How to enable or disable padding and margin for individual block types:

{
	"version": 2,
	"settings": {
		"blocks": {
			"core/group": {
				"spacing": {
					"padding": false
				}
			},
			"core/site-title": {
				"spacing": {
					"padding": true,
					"margin": false
				}
			}
		}
	}
}

Which blocks support margins?

Spacing support is greatly improved in WordPress 6.1 compared to earlier versions.

The following blocks have a margin control in the editor (October 2022, Gutenberg 14.4):

Archives, audio, avatar, categories, comment author avatar, comment author name, comment date, comment edit link, comment reply link, comments title, gallery, heading, list, media & text, paragraph, post author, post author biography, post author name, post date, post excerpt, post featured image, post terms, post title, query title, site logo, site tagline, social links, table, table of contents, tag cloud, term description, verse, video.

Buttons, code, columns, cover, group, read more, separator and spacer only support top and bottom margins.

Which blocks support padding?

The following blocks have a padding control in the editor (October 2022, Gutenberg 14.4):

Archives, audio, avatar, button, categories, code, column, columns, comment author avatar, comment author name, comment content, comment date, comment edit link, comment reply link, comments title, cover, gallery, heading, list, media & text, paragraph, post author, post author biography, post author name, post date, post excerpt, post featured image, post terms, post title, query title, read more, site logo, site tagline, site title, social links, table, table of contents, tag cloud, term description, verse, video.

The dimensions panel

Some blocks display one or both options by default. For other blocks, you need to toggle the option using the + or ellipsis button:

The dimension panel with the open options modal, where you can enable the padding and margin options.
Dimensions panel toggle
The dimension panel with padding and margin input fields enabled.
The reset all button is hidden inside the modal that can be opened with the "View options" menu.
Dimensions panel with options displaying. Some blocks only support top and bottom margins.

How to add default margin and padding to the website

You can add a default margin and padding to the <body> element in the styles section of theme.json using styles.spacing.margin and styles.spacing.padding:

{
	"version": 2,
	"styles": {
		"spacing": {
			"margin": {
				"top": "0px",
				"right": "0px",
				"bottom": "60px",
				"left": "0px"
			},
			"padding": {
				"top": "100px",
				"right": "0px",
				"bottom": "0px",
				"left": "0px"
			}
		}
	}
}

CSS output:

body {
    margin-top: 0px;
    margin-right: 0px;
    margin-bottom: 60px;
    margin-left: 0px;
    padding-top: 100px;
    padding-right: 0px;
    padding-bottom: 0px;
    padding-left: 0px;

}

How to add default margin and padding to blocks

You can apply a default margin or padding to a block under styles.blocks in theme.json:

styles.blocks.blockname.spacing.margin
styles.blocks.blockname.spacing.padding

Examples:

{
	"version": 2,
	"styles": {
		"blocks": {
			"core/button": {
				"spacing": {
					"padding": {
						"top": "0.3rem",
						"right": "1rem",
						"bottom": "0.3rem",
						"left": "1rem"
					}
				}
			},
			"core/social-links": {
				"spacing": {
					"margin": {
						"top": "0px",
						"bottom": "0px"
					}
				}
			},
			"core/latest-comments": {
				"spacing": {
					"padding": {
						"left": "0px"
					}
				}
			}
		}
	}
}

You can also add spacing to elements inside a block:

{
	"version": 2,
	"styles": {
		"blocks": {
			"core/post-excerpt": {
				"elements": {
					"link": {
						"spacing": {
							"padding": {
							"top": "0.3rem",
							"bottom": "0.3rem"
							}
						}
					}
				}
			}
		}
	}
}

Custom units

The default units for the width, height, and spacing controls vary depending on the block.
The navigation block and the social link blocks specify their units in the block.json file.
Other blocks get the units from the UnitControl component.

The selectable units are px, em, rem, %, vw, and vh. You can add or remove units using settings.spacing.units:

{
	"version": 2,
	"settings": {
		"spacing": {
			"units": [ "px","em","rem"]
		}
	}
}

The setting replaces add_theme_support( 'custom-units', array() );.

If you only include one unit, the unit does not show in the control.

BlockGap

What is blockGap?

The blockGap setting in theme.json sets the vertical spacing between blocks by adjusting the margins depending on the position of the block:

  • The first block on the webpage and the first inner block or child block have no top or bottom margin.
  • The following blocks have a top margin that corresponds to the value in the theme.json setting, and no bottom margin.

The block editor also uses blockGap for horizontal spacing between columns, gallery items, buttons, and social icons.

The default value for the CSS preset (--wp--style--block-gap) vary depending on the block, usually 2em for margins and 0.5em for gap.

The control for blockGap in the editor is called Block spacing, and you can find it in the dimensions panel:

The block spacing control in the Dimensions panel has an input field for a numeric value, and an option to select the unit.

How to enable blockGap

The blockGap settings are disabled (false) by default, meaning, if you don’t want to use blockGap, you don’t need to do anything.
-If you have enabled appearanceTools in the settings section of theme.json, you do not need to enable blockGap separately.

{
	"version": 2,
	"settings": {
		"spacing": {
			"blockGap": true
		}
	}
}

How to add a default blockGap value

You can set a site-wide or block-specific value for blockGap inside the styles section in theme.json:

{
	"version": 2,
	"styles": {
		"spacing": {
			"blockGap": "1.5rem"
		}
	}
}

Can I add a blockGap value to a single block type?

This feature was temporarily removed but is enabled again in WordPress 6.1 or if you activate Gutenberg version 13.7 or newer.

Example:

{
	"version": 2,
	"styles": {
		"blocks": {
			"core/group": {
				"spacing": {
					"blockGap": "1.5rem"
				}
			}
		}
	}
}

Spacing presets

With spacing presets both developers and users can select from predefined values for padding, margin and block spacing (block gap). The purpose is to make it easier to use consistent spacing throughout the website.

Presets are automatically enabled once you enable spacing in theme.json. If you have enabled any spacing setting like margin or padding, the spacing presets are also enabled.

The block editor displays the presets as a stepped slider:

The dimensions panel with three controls for padding, margin and block spacing.
The size of the spacing is adjusted with a stepped slider with a marker that can be dragged horizontally.

Spacing presets can be used with or without a scale. The default is to use a scale, but you can choose to disable the scale and use custom values.

How to use the default spacing scale

There are seven steps in the default scale (in addition to the first step, 0) and each step multiplies the value by 1.5. Each value is assigned to a CSS custom property:

CSS custom propertyValueEditor label
–wp–preset–spacing–200.44rem2X-Small
–wp–preset–spacing–300.67remX-Small
–wp–preset–spacing–401remSmall
–wp–preset–spacing–501.5remMedium
–wp–preset–spacing–602.25remLarge
–wp–preset–spacing–703.38remX-Large
–wp–preset–spacing–805.06rem2X-Large

In this theme.json example I have used the preset to add margin to the post title:

{
	"version": 2,	
	"styles": {
		"blocks": {
			"core/post-title": {
				"spacing": {
					"margin": {
						"bottom": "var(--wp--preset--spacing--50)",
						"top": "var(--wp--preset--spacing--30)"
					}
				}
			}
		}
	}
}

Inside HTML templates it is recommended to add spacing presets to the block comment using pipe symbols:

<!-- wp:post-title {"style":{"spacing":{"padding":{"top":"var:preset|spacing|30","right":"var:preset|spacing|30","bottom":"var:preset|spacing|30","left":"var:preset|spacing|30"}}}} /-->

Note that the pipe symbol should not be used inside the block’s style attribute. Below is an example of a group block with padding on all sides:

<!-- wp:group {"layout":{"type":"constrained"},"style":{"spacing":{"padding":{"top":"var:preset|spacing|30","right":"var:preset|spacing|30","bottom":"var:preset|spacing|30","left":"var:preset|spacing|30"}}}} -->
<div class="wp-block-group" style="padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--30)"></div>
<!-- /wp:group -->

How to add your own custom spacing scale

You create your custom spacing scale under settings.spacing.spacingScale in theme.json.
The properties are:

  • operator: Use + to increase or * to multiply. The default is to multiply.
  • increment: A number. Each step in the scale is increased or multiplied with this value. The default value is 1.5.
  • steps: The number of steps to use in the scale. The default is 7.
  • mediumStep: A number. The medium value in the scale. The default value is 1.5.
  • unit: A string of valid CSS spacing units. The available values are: px, em, rem (default), vh, vw and %.

This example snippet autogenerates a scale based on the medium step.
This code is all you need if you want custom values, but you don’t feel a need to change the slugs or names (the visible name in the editor):

{
	"version": 2,
	"settings": {
		"spacing": {
			"spacingScale": {
				"operator": "*",
				"increment": 1.5,
				"steps": 7,
				"mediumStep": 1.5,
				"unit": "rem"
			}
		}
	}
}

Keeping the default slugs and names makes your code more portable, and that way you can easily modify the spacingScale in your theme’s style variations.

Using spacing preset values without a scale

There are several use cases for using presets without a scale. Perhaps the most common reason is to have values that scale, but needing to skip one or two steps and add a custom value.

To add custom sizes, you will use the spacingSizes setting with the attributes size, slug and name. The size can be any valid CSS size value, including clamp.

  • SpacingSizes replaces the default options in the spacing controls in the editor.
    • If you want to use the default presets but add a custom size to the scale, you need to add all sizes to your theme.json, since the theme.json setting overrides the default sizes.
  • The default CSS custom properties are still output for compatibility reasons, but are not visible in the control.
  • It is strongly recommended to use numeric slugs that range from the smallest to the largest size.

This example is copied from the theme Twenty Twenty-Three. This example is truncated, but notably, the smallest size, with the slug 20, is not being used:

"spacing": {
	"spacingSizes": [
		{
		"size": "clamp(1.5rem, 5vw, 2rem)",
		"slug": "30",
		"name": "1"
		},
		{
		"size": "clamp(1.8rem, 1.8rem + ((1vw - 0.48rem) * 2.885), 3rem)",
		"slug": "40",
		"name": "2"
		},
		{
		"size": "clamp(2.5rem, 8vw, 6.5rem)",
		"slug": "50",
		"name": "3"
		},
	]
}

The order in which you place the sizes is crucial. The control in the editor will display the sizes in the order you have added them in theme.json.

Do not do the following, because “Hello” will show in between size one and two, even though it’s size value is larger. This would make the spacing controls more difficult to use:


"spacingSizes": [
    {
        "size": "1rem",
        "slug": "10",
        "name": "1"
    },
    {
        "size": "6rem",
        "slug": "hello",
        "name": "Hello"
    },
    {
        "size": "2rem",
        "slug": "20",
        "name": "2"
    }
],

Can I use different spacing presets for different blocks?

At this time it is not possible to have different spacing presets for different block types.

How to disable custom spacing

By default, users can enter their own custom values and units in the spacing control:

Besides using the stepped slider, the spacing size values can also be manually entered into a numeric input field.

You can disable this ability by setting customSpacingSize to false:

{
	"version": 2,
	"settings": {
		"spacing": {
			"customSpacingSize": false
		}
	}
}

If you want to disable the default spacing scale, you can set the spacingScale steps to 0 using the following example:

{
	"version": 2,
	"settings": {
		"spacing": {
			"spacingScale": {
				"steps": 0
			}
		}
	}
}

Padding aware alignments

Since the introduction of full width blocks, theme developers have tried different ways to adjust the width of the content that is placed inside full width container blocks.

  • For example, when you used a full width group or columns block with paragraphs inside, the text would touch the edge of the browser window. -Unless you added padding or a background to each block.
  • Secondly, if we added a padding to the root level in the theme.json styles section (styles.spacing.padding), it meant that the blocks were no longer full width.

Gutenberg version 13.8 adds a new theme.json setting called useRootPaddingAwareAlignments that aims to solve this. The setting is opt-in.
Enabling it adds four new CSS variables:
-wp--style--root--padding-top/right/bottom/left,
which uses the value from settings.styles.spacing.padding:

{
	"version": 2,
	"settings": {
		"appearanceTools": true,
		"useRootPaddingAwareAlignments": true,
		"layout": {
			"contentSize": "840px",
			"wideSize": "1100px"
		}
	},
	"styles": {
		"spacing": {
			"padding": {
				"top": "1rem",
				"right": "1rem",
				"bottom": "1rem",
				"left": "1rem"
			}
		}
	}
}

You need to use separate values for the padding, using a single value does not work.

If the value in settings.styles.spacing.padding is 1rem, WordPress adds 1rem padding to the body:

body {
    --wp--style--root--padding-top: 1rem;
    --wp--style--root--padding-right: 1rem;
    --wp--style--root--padding-bottom: 1rem;
    --wp--style--root--padding-left: 1rem;
}

Next, the setting adds a CSS class called .has-global-padding and calculates wether full width blocks should have a negative left or right margin. This margin compensates for the added padding.

<div class="has-global-padding is-layout-flow wp-container-2 wp-block-group alignfull">
.has-global-padding > .alignfull {
    margin-right: calc(var(--wp--style--root--padding-right) * -1);
    margin-left: calc(var(--wp--style--root--padding-left) * -1);
}

In my example theme, 1 rem is 16 pixels and the computed margin for the full width block is:

margin-inline-end -16px
margin-inline-start -16px
margin-left -16px
margin-right -16px

Resources

Fränk Klein explains blockGap very well in this twitter thread:

Rich tabor has written a blog post about using custom spacing in block themes: