Make WordPress Core

#58997 closed defect (bug) (reported-upstream)

Compound block and layout type classname incorrect for custom blocks

Reported by: wongjn's profile wongjn Owned by:
Milestone: Priority: normal
Severity: normal Version: 6.3
Component: Themes Keywords:
Focuses: css Cc:

Description

Opened in `WordPress/Gutenberg` as #53295 but considered a feature, whereas I would suggest it be considered a bug.

Description

As per Layout updates in the editor for WordPress 6.3:

In 6.3, a new classname is added to the inner wrapper of all blocks with layout, comprised of block classname + layout classname, e.g.: .wp-block-cover-is-layout-constrained.

However, this does not work for third-party blocks since the vendor prefix of the block gets cut off in the class name generated in markup, but the CSS generated from the style engine has the prefix.

Given the block detailed in the code snippet further down, you'll get markup like this in the block editor:

<div  class="block-editor-block-list__block wp-block wp-block-foo-bar">
  <div class="block-editor-block-list__layout is-layout-flow wp-block-bar-is-layout-flow" >

With the associative CSS (where settings.styles.blocks.foo/bar.spacing.blockGap has been set to var(--wp--preset--spacing--12) in theme.json):


.editor-styles-wrapper .wp-block-foo-bar-is-layout-flow > :first-child:first-child {
  margin-block-start: 0;
}

.editor-styles-wrapper .wp-block-foo-bar-is-layout-flow > :last-child:last-child {
  margin-block-end: 0;
}

.editor-styles-wrapper .wp-block-foo-bar-is-layout-flow > * {
  margin-block-start: var(--wp--preset--spacing--12);
  margin-block-end: 0;
}

Notice how the combined CSS classes like wp-block-foo-bar-is-layout-flow have wp-block-foo-bar- but the class name on the HTML block itself is wp-block-bar-.

This difference seems to be due to the `packages/block-editor/src/hooks/layout.js` only looking at the second part of the block name after the slash (`/`):

if ( LAYOUT_DEFINITIONS[ usedLayout?.type || 'default' ]?.className ) {
        const baseClassName =
                LAYOUT_DEFINITIONS[ usedLayout?.type || 'default' ]?.className;
        const compoundClassName = `wp-block-${ blockName
                .split( '/' )
                .pop() }-${ baseClassName }`;
        layoutClassnames.push( baseClassName, compoundClassName );
}

when building the class name for HTML.

This behavior is mirrored for server side layout supports PHP code:

<?php

// Add combined layout and block classname for global styles to hook onto.
$block_name    = explode( '/', $block['blockName'] );
$class_names[] = 'wp-block-' . end( $block_name ) . '-' . $layout_classname;

Again, only looking at the second part of the block name after the slash (/).

The CSS I believe comes from `WP_Theme_JSON::get_layout_styles()` and we can see the difference with how the CSS is constructed:

<?php

$selector = isset( $block_metadata['selector'] ) ? $block_metadata['selector'] : '';
// $selector = '.wp-block-foo-bar'

// …

$format          = static::ROOT_BLOCK_SELECTOR === $selector ? ':where(%s .%s) %s' : '%s-%s%s';
$layout_selector = sprintf(
  $format,                  // = '%s-%s%s'
  $selector,                // = '.wp-block-foo-bar'
  $class_name,              // = 'is-layout-flow'
  $spacing_rule['selector'] // = ' > *', 
);
// $layout_selector = '.wp-block-foo-bar-is-layout-flow > *'

Step-by-step reproduction instructions

  1. Create a custom block like the example provided in the code snippets further down.
  2. Insert into a post.
  3. See that an incorrect compound block and layout type class name is applied to the inner <div>, both in the editor or in the live webpage.

Example block

block.json:

{
  "apiVersion": 3,
  "name": "foo/bar",
  "title": "Foo Bar",
  "supports": {
    "layout": true
  },
  "editorScript": "file:./index.js"
}

index.js:

import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor';
import { registerBlockType } from '@wordpress/blocks';

registerBlockType( 'foo/bar', {
  edit: function FooBarEdit() {
    const blockProps = useBlockProps();
    const innerProps = useInnerBlocksProps();
  
    return (
      <div { ...blockProps }>
        <div { ...innerProps } />
      </div>
    );
  },
  save: function FooBarSave() {
    const blockProps = useBlockProps.save();
    const innerProps = useInnerBlocksProps.save();

    return (
      <div { ...blockProps }>
        <div { ...innerProps } />
      </div>
    );
  },
}

index.js (no build step neccessary):

const { useBlockProps, useInnerBlocksProps } = wp.blockEditor;
const { createElement } = wp.element;

wp.blocks.registerBlockType( 'foo/bar', {
  edit: function FooBarEdit() {
    const blockProps = useBlockProps();
    const innerProps = useInnerBlocksProps();
  
    return createElement( 'div', blockProps, [
      createElement( 'div', innerProps ),
    ] );
  },
  save: function FooBarSave() {
    const blockProps = useBlockProps.save();
    const innerProps = useInnerBlocksProps.save();

    return createElement( 'div', blockProps, [
      createElement( 'div', innerProps ),
    ] );
  },
}

Tested on WordPress 6.3-RC3-56344

Change History (1)

#1 @isabel_brison
10 months ago

  • Milestone Awaiting Review deleted
  • Resolution set to reported-upstream
  • Status changed from new to closed

Thanks for the report @wongjn! I'm closing this as it's already been reported in Gutenberg, which is the correct place for it :)

Note: See TracTickets for help on using tickets.