* Layout block support flag.
* Returns layout definitions, keyed by layout type.
* Provides a common definition of slugs, classnames, base styles, and spacing styles for each layout type.
* When making changes or additions to layout definitions, the corresponding JavaScript definitions should
* @since 6.6.0 Updated specificity for compatibility with 0-1-0 global styles specificity.
* @return array[] Layout definitions.
function wp_get_layout_definitions() {
$layout_definitions = array(
'className' => 'is-layout-flow',
'selector' => ' > .alignleft',
'margin-inline-start' => '0',
'margin-inline-end' => '2em',
'selector' => ' > .alignright',
'margin-inline-start' => '2em',
'margin-inline-end' => '0',
'selector' => ' > .aligncenter',
'margin-left' => 'auto !important',
'margin-right' => 'auto !important',
'spacingStyles' => array(
'selector' => ' > :first-child',
'margin-block-start' => '0',
'selector' => ' > :last-child',
'margin-block-end' => '0',
'margin-block-start' => null,
'margin-block-end' => '0',
'className' => 'is-layout-constrained',
'selector' => ' > .alignleft',
'margin-inline-start' => '0',
'margin-inline-end' => '2em',
'selector' => ' > .alignright',
'margin-inline-start' => '2em',
'margin-inline-end' => '0',
'selector' => ' > .aligncenter',
'margin-left' => 'auto !important',
'margin-right' => 'auto !important',
'selector' => ' > :where(:not(.alignleft):not(.alignright):not(.alignfull))',
'max-width' => 'var(--wp--style--global--content-size)',
'margin-left' => 'auto !important',
'margin-right' => 'auto !important',
'selector' => ' > .alignwide',
'max-width' => 'var(--wp--style--global--wide-size)',
'spacingStyles' => array(
'selector' => ' > :first-child',
'margin-block-start' => '0',
'selector' => ' > :last-child',
'margin-block-end' => '0',
'margin-block-start' => null,
'margin-block-end' => '0',
'className' => 'is-layout-flex',
'align-items' => 'center',
'selector' => ' > :is(*, div)', // :is(*, div) instead of just * increases the specificity by 001.
'spacingStyles' => array(
'className' => 'is-layout-grid',
'selector' => ' > :is(*, div)', // :is(*, div) instead of just * increases the specificity by 001.
'spacingStyles' => array(
return $layout_definitions;
* Registers the layout block attribute for block types that support it.
* @since 6.3.0 Check for layout support via the `layout` key with fallback to `__experimentalLayout`.
* @param WP_Block_Type $block_type Block Type.
function wp_register_layout_support( $block_type ) {
$support_layout = block_has_support( $block_type, 'layout', false ) || block_has_support( $block_type, '__experimentalLayout', false );
if ( ! $block_type->attributes ) {
$block_type->attributes = array();
if ( ! array_key_exists( 'layout', $block_type->attributes ) ) {
$block_type->attributes['layout'] = array(
* Generates the CSS corresponding to the provided layout.
* @since 6.1.0 Added `$block_spacing` param, use style engine to enqueue styles.
* @since 6.3.0 Added grid layout type.
* @since 6.6.0 Removed duplicated selector from layout styles.
* Enabled negative margins for alignfull children of blocks with custom padding.
* @param string $selector CSS selector.
* @param array $layout Layout object. The one that is passed has already checked
* the existence of default block layout.
* @param bool $has_block_gap_support Optional. Whether the theme has support for the block gap. Default false.
* @param string|string[]|null $gap_value Optional. The block gap value to apply. Default null.
* @param bool $should_skip_gap_serialization Optional. Whether to skip applying the user-defined value set in the editor. Default false.
* @param string $fallback_gap_value Optional. The block gap value to apply. Default '0.5em'.
* @param array|null $block_spacing Optional. Custom spacing set on the block. Default null.
* @return string CSS styles on success. Else, empty string.
function wp_get_layout_style( $selector, $layout, $has_block_gap_support = false, $gap_value = null, $should_skip_gap_serialization = false, $fallback_gap_value = '0.5em', $block_spacing = null ) {
$layout_type = isset( $layout['type'] ) ? $layout['type'] : 'default';
$layout_styles = array();
if ( 'default' === $layout_type ) {
if ( $has_block_gap_support ) {
if ( is_array( $gap_value ) ) {
$gap_value = isset( $gap_value['top'] ) ? $gap_value['top'] : null;
if ( null !== $gap_value && ! $should_skip_gap_serialization ) {
// Get spacing CSS variable from preset value if provided.
if ( is_string( $gap_value ) && str_contains( $gap_value, 'var:preset|spacing|' ) ) {
$index_to_splice = strrpos( $gap_value, '|' ) + 1;
$slug = _wp_to_kebab_case( substr( $gap_value, $index_to_splice ) );
$gap_value = "var(--wp--preset--spacing--$slug)";
'selector' => "$selector > *",
'margin-block-start' => '0',
'margin-block-end' => '0',
'selector' => "$selector > * + *",
'margin-block-start' => $gap_value,
'margin-block-end' => '0',
} elseif ( 'constrained' === $layout_type ) {
$content_size = isset( $layout['contentSize'] ) ? $layout['contentSize'] : '';
$wide_size = isset( $layout['wideSize'] ) ? $layout['wideSize'] : '';
$justify_content = isset( $layout['justifyContent'] ) ? $layout['justifyContent'] : 'center';
$all_max_width_value = $content_size ? $content_size : $wide_size;
$wide_max_width_value = $wide_size ? $wide_size : $content_size;
// Make sure there is a single CSS rule, and all tags are stripped for security.
$all_max_width_value = safecss_filter_attr( explode( ';', $all_max_width_value )[0] );
$wide_max_width_value = safecss_filter_attr( explode( ';', $wide_max_width_value )[0] );
$margin_left = 'left' === $justify_content ? '0 !important' : 'auto !important';
$margin_right = 'right' === $justify_content ? '0 !important' : 'auto !important';
if ( $content_size || $wide_size ) {
'selector' => "$selector > :where(:not(.alignleft):not(.alignright):not(.alignfull))",
'max-width' => $all_max_width_value,
'margin-left' => $margin_left,
'margin-right' => $margin_right,
'selector' => "$selector > .alignwide",
'declarations' => array( 'max-width' => $wide_max_width_value ),
'selector' => "$selector .alignfull",
'declarations' => array( 'max-width' => 'none' ),
if ( isset( $block_spacing ) ) {
$block_spacing_values = wp_style_engine_get_styles(
'spacing' => $block_spacing,
* Handle negative margins for alignfull children of blocks with custom padding set.
* They're added separately because padding might only be set on one side.
if ( isset( $block_spacing_values['declarations']['padding-right'] ) ) {
$padding_right = $block_spacing_values['declarations']['padding-right'];
if ( '0' === $padding_right ) {
$layout_styles[] = array(
'selector' => "$selector > .alignfull",
'declarations' => array( 'margin-right' => "calc($padding_right * -1)" ),
if ( isset( $block_spacing_values['declarations']['padding-left'] ) ) {
$padding_left = $block_spacing_values['declarations']['padding-left'];
if ( '0' === $padding_left ) {
$layout_styles[] = array(
'selector' => "$selector > .alignfull",
'declarations' => array( 'margin-left' => "calc($padding_left * -1)" ),
if ( 'left' === $justify_content ) {
$layout_styles[] = array(
'selector' => "$selector > :where(:not(.alignleft):not(.alignright):not(.alignfull))",
'declarations' => array( 'margin-left' => '0 !important' ),
if ( 'right' === $justify_content ) {
$layout_styles[] = array(
'selector' => "$selector > :where(:not(.alignleft):not(.alignright):not(.alignfull))",
'declarations' => array( 'margin-right' => '0 !important' ),
if ( $has_block_gap_support ) {
if ( is_array( $gap_value ) ) {
$gap_value = isset( $gap_value['top'] ) ? $gap_value['top'] : null;
if ( null !== $gap_value && ! $should_skip_gap_serialization ) {
// Get spacing CSS variable from preset value if provided.
if ( is_string( $gap_value ) && str_contains( $gap_value, 'var:preset|spacing|' ) ) {
$index_to_splice = strrpos( $gap_value, '|' ) + 1;
$slug = _wp_to_kebab_case( substr( $gap_value, $index_to_splice ) );
$gap_value = "var(--wp--preset--spacing--$slug)";
'selector' => "$selector > *",
'margin-block-start' => '0',
'margin-block-end' => '0',
'selector' => "$selector > * + *",
'margin-block-start' => $gap_value,
'margin-block-end' => '0',
} elseif ( 'flex' === $layout_type ) {
$layout_orientation = isset( $layout['orientation'] ) ? $layout['orientation'] : 'horizontal';
$justify_content_options = array(
$vertical_alignment_options = array(
if ( 'horizontal' === $layout_orientation ) {
$justify_content_options += array( 'space-between' => 'space-between' );
$vertical_alignment_options += array( 'stretch' => 'stretch' );
$justify_content_options += array( 'stretch' => 'stretch' );
$vertical_alignment_options += array( 'space-between' => 'space-between' );
if ( ! empty( $layout['flexWrap'] ) && 'nowrap' === $layout['flexWrap'] ) {
$layout_styles[] = array(
'declarations' => array( 'flex-wrap' => 'nowrap' ),
if ( $has_block_gap_support && isset( $gap_value ) ) {
$combined_gap_value = '';
$gap_sides = is_array( $gap_value ) ? array( 'top', 'left' ) : array( 'top' );
foreach ( $gap_sides as $gap_side ) {
$process_value = $gap_value;
if ( is_array( $gap_value ) ) {
$process_value = isset( $gap_value[ $gap_side ] ) ? $gap_value[ $gap_side ] : $fallback_gap_value;
// Get spacing CSS variable from preset value if provided.
if ( is_string( $process_value ) && str_contains( $process_value, 'var:preset|spacing|' ) ) {
$index_to_splice = strrpos( $process_value, '|' ) + 1;
$slug = _wp_to_kebab_case( substr( $process_value, $index_to_splice ) );
$process_value = "var(--wp--preset--spacing--$slug)";
$combined_gap_value .= "$process_value ";
$gap_value = trim( $combined_gap_value );
if ( null !== $gap_value && ! $should_skip_gap_serialization ) {
$layout_styles[] = array(
'declarations' => array( 'gap' => $gap_value ),
if ( 'horizontal' === $layout_orientation ) {
* Add this style only if is not empty for backwards compatibility,
* since we intend to convert blocks that had flex layout implemented
if ( ! empty( $layout['justifyContent'] ) && array_key_exists( $layout['justifyContent'], $justify_content_options ) ) {
$layout_styles[] = array(
'declarations' => array( 'justify-content' => $justify_content_options[ $layout['justifyContent'] ] ),
if ( ! empty( $layout['verticalAlignment'] ) && array_key_exists( $layout['verticalAlignment'], $vertical_alignment_options ) ) {
$layout_styles[] = array(
'declarations' => array( 'align-items' => $vertical_alignment_options[ $layout['verticalAlignment'] ] ),
$layout_styles[] = array(
'declarations' => array( 'flex-direction' => 'column' ),
if ( ! empty( $layout['justifyContent'] ) && array_key_exists( $layout['justifyContent'], $justify_content_options ) ) {
$layout_styles[] = array(
'declarations' => array( 'align-items' => $justify_content_options[ $layout['justifyContent'] ] ),
$layout_styles[] = array(
'declarations' => array( 'align-items' => 'flex-start' ),
if ( ! empty( $layout['verticalAlignment'] ) && array_key_exists( $layout['verticalAlignment'], $vertical_alignment_options ) ) {
$layout_styles[] = array(
'declarations' => array( 'justify-content' => $vertical_alignment_options[ $layout['verticalAlignment'] ] ),
} elseif ( 'grid' === $layout_type ) {
if ( ! empty( $layout['columnCount'] ) ) {
$layout_styles[] = array(
'declarations' => array( 'grid-template-columns' => 'repeat(' . $layout['columnCount'] . ', minmax(0, 1fr))' ),
$minimum_column_width = ! empty( $layout['minimumColumnWidth'] ) ? $layout['minimumColumnWidth'] : '12rem';