Skip to content

Commit

Permalink
Layout: Add a disable-layout-styles theme supports flag to opt out of…
Browse files Browse the repository at this point in the history
… all layout styles (#42544)

* Layout: Add a disable-layout-styles theme supports flag to opt-out of all layout styles

* Try adding documentation entry

* Fix behaviour in site editor

* Add register_theme_feature call to ensure the feature is registered so it appears in API responses

* Remove useThemeHasDisabledLayoutStyles hook to simplify feature checks
  • Loading branch information
andrewserong authored Jul 29, 2022
1 parent bc79532 commit 3c2ee63
Show file tree
Hide file tree
Showing 12 changed files with 161 additions and 40 deletions.
12 changes: 12 additions & 0 deletions docs/how-to-guides/themes/theme-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,18 @@ add_theme_support( 'disable-custom-gradients' );

When set, users will be restricted to the default gradients provided in the block editor or the gradients provided via the `editor-gradient-presets` theme support setting.

### Disabling base layout styles

_**Note:** Since WordPress 6.1._

Themes can opt out of generated block layout styles that provide default structural styles for core blocks including Group, Columns, Buttons, and Social Icons. By using the following code, these themes commit to providing their own structural styling, as using this feature will result in core blocks displaying incorrectly in both the editor and site frontend:

```php
add_theme_support( 'disable-layout-styles' );
```

For themes looking to customize `blockGap` styles or block spacing, see [the developer docs on Global Settings & Styles](/docs/how-to-guides/themes/theme-json/#what-is-blockgap-and-how-can-i-use-it).

### Supporting custom line heights

Some blocks like paragraph and headings support customizing the line height. Themes can enable support for this feature with the following code:
Expand Down
45 changes: 25 additions & 20 deletions lib/block-supports/layout.php
Original file line number Diff line number Diff line change
Expand Up @@ -226,30 +226,35 @@ function gutenberg_render_layout_support_flag( $block_content, $block ) {
$class_names[] = sanitize_title( $layout_classname );
}

$gap_value = _wp_array_get( $block, array( 'attrs', 'style', 'spacing', 'blockGap' ) );
// Skip if gap value contains unsupported characters.
// Regex for CSS value borrowed from `safecss_filter_attr`, and used here
// because we only want to match against the value, not the CSS attribute.
if ( is_array( $gap_value ) ) {
foreach ( $gap_value as $key => $value ) {
$gap_value[ $key ] = $value && preg_match( '%[\\\(&=}]|/\*%', $value ) ? null : $value;
// Only generate Layout styles if the theme has not opted-out.
// Attribute-based Layout classnames are output in all cases.
if ( ! current_theme_supports( 'disable-layout-styles' ) ) {

$gap_value = _wp_array_get( $block, array( 'attrs', 'style', 'spacing', 'blockGap' ) );
// Skip if gap value contains unsupported characters.
// Regex for CSS value borrowed from `safecss_filter_attr`, and used here
// because we only want to match against the value, not the CSS attribute.
if ( is_array( $gap_value ) ) {
foreach ( $gap_value as $key => $value ) {
$gap_value[ $key ] = $value && preg_match( '%[\\\(&=}]|/\*%', $value ) ? null : $value;
}
} else {
$gap_value = $gap_value && preg_match( '%[\\\(&=}]|/\*%', $gap_value ) ? null : $gap_value;
}
} else {
$gap_value = $gap_value && preg_match( '%[\\\(&=}]|/\*%', $gap_value ) ? null : $gap_value;
}

$fallback_gap_value = _wp_array_get( $block_type->supports, array( 'spacing', 'blockGap', '__experimentalDefault' ), '0.5em' );
$block_spacing = _wp_array_get( $block, array( 'attrs', 'style', 'spacing' ), null );
$fallback_gap_value = _wp_array_get( $block_type->supports, array( 'spacing', 'blockGap', '__experimentalDefault' ), '0.5em' );
$block_spacing = _wp_array_get( $block, array( 'attrs', 'style', 'spacing' ), null );

// If a block's block.json skips serialization for spacing or spacing.blockGap,
// don't apply the user-defined value to the styles.
$should_skip_gap_serialization = gutenberg_should_skip_block_supports_serialization( $block_type, 'spacing', 'blockGap' );
$style = gutenberg_get_layout_style( ".$block_classname.$container_class", $used_layout, $has_block_gap_support, $gap_value, $should_skip_gap_serialization, $fallback_gap_value, $block_spacing );
// If a block's block.json skips serialization for spacing or spacing.blockGap,
// don't apply the user-defined value to the styles.
$should_skip_gap_serialization = gutenberg_should_skip_block_supports_serialization( $block_type, 'spacing', 'blockGap' );
$style = gutenberg_get_layout_style( ".$block_classname.$container_class", $used_layout, $has_block_gap_support, $gap_value, $should_skip_gap_serialization, $fallback_gap_value, $block_spacing );

// Only add container class and enqueue block support styles if unique styles were generated.
if ( ! empty( $style ) ) {
$class_names[] = $container_class;
wp_enqueue_block_support_styles( $style );
// Only add container class and enqueue block support styles if unique styles were generated.
if ( ! empty( $style ) ) {
$class_names[] = $container_class;
wp_enqueue_block_support_styles( $style );
}
}

// This assumes the hook only applies to blocks with a single wrapper.
Expand Down
1 change: 1 addition & 0 deletions lib/compat/wordpress-6.1/block-editor-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ function gutenberg_get_block_editor_settings( $settings ) {
}

$settings['localAutosaveInterval'] = 15;
$settings['disableLayoutStyles'] = current_theme_supports( 'disable-layout-styles' );

return $settings;
}
Expand Down
5 changes: 5 additions & 0 deletions lib/compat/wordpress-6.1/class-wp-theme-json-6-1.php
Original file line number Diff line number Diff line change
Expand Up @@ -1252,6 +1252,11 @@ protected function get_layout_styles( $block_metadata ) {
$block_rules = '';
$block_type = null;

// Skip outputting layout styles if explicitly disabled.
if ( current_theme_supports( 'disable-layout-styles' ) ) {
return $block_rules;
}

if ( isset( $block_metadata['name'] ) ) {
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block_metadata['name'] );
if ( ! block_has_support( $block_type, array( '__experimentalLayout' ), false ) ) {
Expand Down
31 changes: 31 additions & 0 deletions lib/compat/wordpress-6.1/theme.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php
/**
* Temporary compatibility shims for features present in Gutenberg.
* This file should be removed when WordPress 6.1.0 becomes the lowest
* supported version by this plugin.
*
* @package gutenberg
*/

/**
* This function runs in addition to the core `create_initial_theme_features`.
* The 6.1 release needs to update the core function to include the body of this function.
*
* Creates the initial theme features when the 'setup_theme' action is fired.
*
* See {@see 'setup_theme'}.
*
* @since 5.5.0
* @since 6.0.1 The `block-templates` feature was added.
* @since 6.1.0 The `disable-layout-styles` feature was added.
*/
function gutenberg_create_initial_theme_features() {
register_theme_feature(
'disable-layout-styles',
array(
'description' => __( 'Whether the theme disables generated layout styles.', 'gutenberg' ),
'show_in_rest' => true,
)
);
}
add_action( 'setup_theme', 'gutenberg_create_initial_theme_features', 0 );
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,12 @@ public function get_item_schema() {
'context' => array( 'post-editor', 'site-editor', 'widgets-editor' ),
),

'disableLayoutStyles' => array(
'description' => __( 'Disables output of layout styles.', 'gutenberg' ),
'type' => 'boolean',
'context' => array( 'post-editor', 'site-editor', 'widgets-editor' ),
),

'enableCustomLineHeight' => array(
'description' => __( 'Enables custom line height.', 'gutenberg' ),
'type' => 'boolean',
Expand Down
1 change: 1 addition & 0 deletions lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/compat/wordpress-6.1/date-settings.php';
require __DIR__ . '/compat/wordpress-6.1/block-patterns.php';
require __DIR__ . '/compat/wordpress-6.1/edit-form-blocks.php';
require __DIR__ . '/compat/wordpress-6.1/theme.php';

// Experimental features.
remove_action( 'plugins_loaded', '_wp_theme_json_webfonts_handler' ); // Turns off WP 6.0's stopgap handler for Webfonts API.
Expand Down
10 changes: 8 additions & 2 deletions packages/block-editor/src/hooks/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -264,10 +264,16 @@ export const withInspectorControls = createHigherOrderComponent(
export const withLayoutStyles = createHigherOrderComponent(
( BlockListBlock ) => ( props ) => {
const { name, attributes } = props;
const shouldRenderLayoutStyles = hasBlockSupport(
const hasLayoutBlockSupport = hasBlockSupport(
name,
layoutBlockSupportKey
);
const disableLayoutStyles = useSelect( ( select ) => {
const { getSettings } = select( blockEditorStore );
return !! getSettings().disableLayoutStyles;
} );
const shouldRenderLayoutStyles =
hasLayoutBlockSupport && ! disableLayoutStyles;
const id = useInstanceId( BlockListBlock );
const defaultThemeLayout = useSetting( 'layout' ) || {};
const element = useContext( BlockList.__unstableElementContext );
Expand All @@ -277,7 +283,7 @@ export const withLayoutStyles = createHigherOrderComponent(
const usedLayout = layout?.inherit
? defaultThemeLayout
: layout || defaultBlockLayout || {};
const layoutClasses = shouldRenderLayoutStyles
const layoutClasses = hasLayoutBlockSupport
? useLayoutClasses( usedLayout, defaultThemeLayout?.definitions )
: null;
const selector = `.${ getBlockDefaultClassName(
Expand Down
34 changes: 20 additions & 14 deletions packages/edit-post/src/components/visual-editor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,15 @@ export default function VisualEditor( { styles } ) {
( select ) => select( editPostStore ).hasMetaBoxes(),
[]
);
const { themeSupportsLayout, assets } = useSelect( ( select ) => {
const _settings = select( blockEditorStore ).getSettings();
return {
themeSupportsLayout: _settings.supportsLayout,
assets: _settings.__unstableResolvedAssets,
};
}, [] );
const { themeHasDisabledLayoutStyles, themeSupportsLayout, assets } =
useSelect( ( select ) => {
const _settings = select( blockEditorStore ).getSettings();
return {
themeHasDisabledLayoutStyles: _settings.disableLayoutStyles,
themeSupportsLayout: _settings.supportsLayout,
assets: _settings.__unstableResolvedAssets,
};
}, [] );
const { clearSelectedBlock } = useDispatch( blockEditorStore );
const { setIsEditingTemplate } = useDispatch( editPostStore );
const desktopCanvasStyles = {
Expand Down Expand Up @@ -241,13 +243,17 @@ export default function VisualEditor( { styles } ) {
assets={ assets }
style={ { paddingBottom } }
>
{ themeSupportsLayout && ! isTemplateMode && (
<LayoutStyle
selector=".edit-post-visual-editor__post-title-wrapper, .block-editor-block-list__layout.is-root-container"
layout={ defaultLayout }
layoutDefinitions={ defaultLayout?.definitions }
/>
) }
{ themeSupportsLayout &&
! themeHasDisabledLayoutStyles &&
! isTemplateMode && (
<LayoutStyle
selector=".edit-post-visual-editor__post-title-wrapper, .block-editor-block-list__layout.is-root-container"
layout={ defaultLayout }
layoutDefinitions={
defaultLayout?.definitions
}
/>
) }
{ ! isTemplateMode && (
<div
className="edit-post-visual-editor__post-title-wrapper"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ import {
__EXPERIMENTAL_ELEMENTS as ELEMENTS,
getBlockTypes,
} from '@wordpress/blocks';
import { useSelect } from '@wordpress/data';
import { useEffect, useState, useContext } from '@wordpress/element';
import { getCSSRules } from '@wordpress/style-engine';
import {
__unstablePresetDuotoneFilter as PresetDuotoneFilter,
__experimentalGetGapCSSValue as getGapCSSValue,
store as blockEditorStore,
} from '@wordpress/block-editor';

/**
Expand Down Expand Up @@ -550,7 +552,8 @@ export const toStyles = (
tree,
blockSelectors,
hasBlockGapSupport,
hasFallbackGapSupport
hasFallbackGapSupport,
disableLayoutStyles = false
) => {
const nodesWithStyles = getNodesWithStyles( tree, blockSelectors );
const nodesWithSettings = getNodesWithSettings( tree, blockSelectors );
Expand Down Expand Up @@ -626,7 +629,10 @@ export const toStyles = (
}

// Process blockGap and layout styles.
if ( ROOT_BLOCK_SELECTOR === selector || hasLayoutSupport ) {
if (
! disableLayoutStyles &&
( ROOT_BLOCK_SELECTOR === selector || hasLayoutSupport )
) {
ruleset += getLayoutStyles( {
tree,
style: styles,
Expand Down Expand Up @@ -783,6 +789,10 @@ export function useGlobalStylesOutput() {
const [ blockGap ] = useSetting( 'spacing.blockGap' );
const hasBlockGapSupport = blockGap !== null;
const hasFallbackGapSupport = ! hasBlockGapSupport; // This setting isn't useful yet: it exists as a placeholder for a future explicit fallback styles support.
const disableLayoutStyles = useSelect( ( select ) => {
const { getSettings } = select( blockEditorStore );
return !! getSettings().disableLayoutStyles;
} );

useEffect( () => {
if ( ! mergedConfig?.styles || ! mergedConfig?.settings ) {
Expand All @@ -798,7 +808,8 @@ export function useGlobalStylesOutput() {
mergedConfig,
blockSelectors,
hasBlockGapSupport,
hasFallbackGapSupport
hasFallbackGapSupport,
disableLayoutStyles
);
const filters = toSvgFilters( mergedConfig, blockSelectors );
setStylesheets( [
Expand All @@ -813,7 +824,12 @@ export function useGlobalStylesOutput() {
] );
setSettings( mergedConfig.settings );
setSvgFilters( filters );
}, [ hasBlockGapSupport, hasFallbackGapSupport, mergedConfig ] );
}, [
hasBlockGapSupport,
hasFallbackGapSupport,
mergedConfig,
disableLayoutStyles,
] );

return [ stylesheets, settings, svgFilters, hasBlockGapSupport ];
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ function useBlockEditorSettings( settings, hasTemplate ) {
'disableCustomColors',
'disableCustomFontSizes',
'disableCustomGradients',
'disableLayoutStyles',
'enableCustomLineHeight',
'enableCustomSpacing',
'enableCustomUnits',
Expand Down
31 changes: 31 additions & 0 deletions phpunit/class-wp-theme-json-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,37 @@ public function test_get_stylesheet_generates_base_fallback_gap_layout_styles( $
);
}

/**
* @dataProvider data_get_layout_definitions
*
* @param array $layout_definitions Layout definitions as stored in core theme.json.
*/
public function test_get_stylesheet_skips_layout_styles( $layout_definitions ) {
add_theme_support( 'disable-layout-styles' );
$theme_json = new WP_Theme_JSON_Gutenberg(
array(
'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA,
'settings' => array(
'layout' => array(
'definitions' => $layout_definitions,
),
'spacing' => array(
'blockGap' => null,
),
),
),
'default'
);
$stylesheet = $theme_json->get_stylesheet( array( 'base-layout-styles' ) );
remove_theme_support( 'disable-layout-styles' );

// All Layout styles should be skipped.
$this->assertEquals(
'',
$stylesheet
);
}

/**
* Data provider.
*
Expand Down

0 comments on commit 3c2ee63

Please sign in to comment.