From bcc44acabb2d4aef2a0571568ac92a09589babd0 Mon Sep 17 00:00:00 2001
From: Andrew Serong <14988353+andrewserong@users.noreply.github.com>
Date: Thu, 25 Jan 2024 09:10:12 +1100
Subject: [PATCH] Dimensions: Add Aspect Ratio block support (#56897)
* Try: Dimensions Aspect Ratio block support
* Try server-rendering output
* Try outputting has-aspect-ratio classname
* Ensure block support is checked properly
* Try adding global styles support
* Update docs, add appropriate checks
* Try unsetting minHeight
* Try unsetting min-height when applying aspect ratio and vice versa
* Allow Cover block to expand when the content extends beyond the boundaries of the aspect-ratio
* Hide cover block resize handle when aspect ratio is set, clear aspect ratio when updating cover block min height
* Clear aspect-ratio if minHeight is set on the site frontend
* Attempt to get unset rules playing nicely in the editor with global styles
* Hide aspectRatio control from global styles for the group block
* Skip output of has-aspect-ratio if value isn't set
---
.../themes/global-settings-and-styles.md | 2 +
docs/how-to-guides/themes/theme-support.md | 2 +-
.../block-api/block-supports.md | 4 +-
docs/reference-guides/core-blocks.md | 4 +-
.../theme-json-reference/theme-json-living.md | 4 +-
lib/block-supports/dimensions.php | 77 +++++++++++++++++++
lib/class-wp-theme-json-gutenberg.php | 17 +++-
.../dimensions-tool/aspect-ratio-tool.js | 5 +-
.../global-styles/dimensions-panel.js | 51 +++++++++++-
.../src/components/global-styles/hooks.js | 15 ++--
.../global-styles/use-global-styles-output.js | 6 ++
packages/block-editor/src/hooks/dimensions.js | 77 ++++++++++++++++++-
packages/block-editor/src/hooks/index.js | 2 +
packages/block-editor/src/hooks/style.js | 4 +
packages/block-editor/src/hooks/utils.js | 4 +
packages/block-library/src/cover/block.json | 3 +
.../block-library/src/cover/edit/index.js | 3 +-
.../src/cover/edit/inspector-controls.js | 15 +++-
packages/block-library/src/cover/style.scss | 5 +-
packages/block-library/src/group/block.json | 1 +
packages/blocks/src/api/constants.js | 5 ++
.../components/global-styles/screen-block.js | 10 +++
.../style-engine/class-wp-style-engine.php | 11 ++-
.../src/styles/dimensions/index.ts | 14 +++-
packages/style-engine/src/types.ts | 1 +
schemas/json/block.json | 6 ++
schemas/json/theme.json | 18 ++++-
27 files changed, 340 insertions(+), 26 deletions(-)
diff --git a/docs/how-to-guides/themes/global-settings-and-styles.md b/docs/how-to-guides/themes/global-settings-and-styles.md
index 130b6271d13bdf..617c435b6d70c5 100644
--- a/docs/how-to-guides/themes/global-settings-and-styles.md
+++ b/docs/how-to-guides/themes/global-settings-and-styles.md
@@ -233,6 +233,7 @@ The settings section has the following structure:
},
"custom": {},
"dimensions": {
+ "aspectRatio": false,
"minHeight": false,
},
"layout": {
@@ -773,6 +774,7 @@ Each block declares which style properties it exposes via the [block supports me
"text": "value"
},
"dimensions": {
+ "aspectRatio": "value",
"minHeight": "value"
},
"filter": {
diff --git a/docs/how-to-guides/themes/theme-support.md b/docs/how-to-guides/themes/theme-support.md
index 88e69938737b7a..4a952c4de657d1 100644
--- a/docs/how-to-guides/themes/theme-support.md
+++ b/docs/how-to-guides/themes/theme-support.md
@@ -472,7 +472,7 @@ Use this setting to enable the following Global Styles settings:
- color: link
- spacing: blockGap, margin, padding
- typography: lineHeight
-- dimensions: minHeight
+- dimensions: aspectRatio, minHeight
- position: sticky
```php
diff --git a/docs/reference-guides/block-api/block-supports.md b/docs/reference-guides/block-api/block-supports.md
index 4a59c34813448f..f035e026ff2e11 100644
--- a/docs/reference-guides/block-api/block-supports.md
+++ b/docs/reference-guides/block-api/block-supports.md
@@ -442,6 +442,7 @@ This value signals that a block supports some of the CSS style properties relate
```js
supports: {
dimensions: {
+ aspectRatio: true // Enable aspect ratio control.
minHeight: true // Enable min height control.
}
}
@@ -449,12 +450,13 @@ supports: {
When a block declares support for a specific dimensions property, its attributes definition is extended to include the `style` attribute.
-- `style`: attribute of `object` type with no default assigned. This is added when `minHeight` support is declared. It stores the custom values set by the user, e.g.:
+- `style`: attribute of `object` type with no default assigned. This is added when `aspectRatio` or `minHeight` support is declared. It stores the custom values set by the user, e.g.:
```js
attributes: {
style: {
dimensions: {
+ aspectRatio: "16/9",
minHeight: "50vh"
}
}
diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md
index ab6bb52b6b3b2a..0cf9bb77f71f82 100644
--- a/docs/reference-guides/core-blocks.md
+++ b/docs/reference-guides/core-blocks.md
@@ -238,7 +238,7 @@ Add an image or video with a text overlay. ([Source](https://github.com/WordPres
- **Name:** core/cover
- **Category:** media
-- **Supports:** align, anchor, color (heading, text, ~~background~~, ~~enableContrastChecker~~), layout (~~allowJustification~~), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~
+- **Supports:** align, anchor, color (heading, text, ~~background~~, ~~enableContrastChecker~~), dimensions (aspectRatio), layout (~~allowJustification~~), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~
- **Attributes:** allowedBlocks, alt, backgroundType, contentPosition, customGradient, customOverlayColor, dimRatio, focalPoint, gradient, hasParallax, id, isDark, isRepeated, isUserOverlayColor, minHeight, minHeightUnit, overlayColor, tagName, templateLock, url, useFeaturedImage
## Details
@@ -341,7 +341,7 @@ Gather blocks in a layout container. ([Source](https://github.com/WordPress/gute
- **Name:** core/group
- **Category:** design
-- **Supports:** align (full, wide), anchor, ariaLabel, background (backgroundImage, backgroundSize), color (background, button, gradients, heading, link, text), dimensions (minHeight), layout (allowSizingOnChildren), position (sticky), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~
+- **Supports:** align (full, wide), anchor, ariaLabel, background (backgroundImage, backgroundSize), color (background, button, gradients, heading, link, text), dimensions (aspectRatio, minHeight), layout (allowSizingOnChildren), position (sticky), spacing (blockGap, margin, padding), typography (fontSize, lineHeight), ~~html~~
- **Attributes:** allowedBlocks, tagName, templateLock
## Heading
diff --git a/docs/reference-guides/theme-json-reference/theme-json-living.md b/docs/reference-guides/theme-json-reference/theme-json-living.md
index ee88f779ace1ce..5565336ffedb7f 100644
--- a/docs/reference-guides/theme-json-reference/theme-json-living.md
+++ b/docs/reference-guides/theme-json-reference/theme-json-living.md
@@ -34,7 +34,7 @@ Setting that enables the following UI tools:
- background: backgroundImage
- border: color, radius, style, width
- color: link
-- dimensions: minHeight
+- dimensions: aspectRatio, minHeight
- position: sticky
- spacing: blockGap, margin, padding
- typography: lineHeight
@@ -116,6 +116,7 @@ Settings related to dimensions.
| Property | Type | Default | Props |
| --- | --- | --- |--- |
+| aspectRatio | boolean | false | |
| minHeight | boolean | false | |
---
@@ -237,6 +238,7 @@ Dimensions styles
| Property | Type | Props |
| --- | --- |--- |
+| aspectRatio | string, object | |
| minHeight | string, object | |
---
diff --git a/lib/block-supports/dimensions.php b/lib/block-supports/dimensions.php
index 1ef43133c2cdfb..1980faba278175 100644
--- a/lib/block-supports/dimensions.php
+++ b/lib/block-supports/dimensions.php
@@ -74,6 +74,83 @@ function gutenberg_apply_dimensions_support( $block_type, $block_attributes ) {
return $attributes;
}
+/**
+ * Renders server-side dimensions styles to the block wrapper.
+ * This block support uses the `render_block` hook to ensure that
+ * it is also applied to non-server-rendered blocks.
+ *
+ * @param string $block_content Rendered block content.
+ * @param array $block Block object.
+ * @return string Filtered block content.
+ */
+function gutenberg_render_dimensions_support( $block_content, $block ) {
+ $block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
+ $block_attributes = ( isset( $block['attrs'] ) && is_array( $block['attrs'] ) ) ? $block['attrs'] : array();
+ $has_aspect_ratio_support = block_has_support( $block_type, array( 'dimensions', 'aspectRatio' ), false );
+
+ if (
+ ! $has_aspect_ratio_support ||
+ wp_should_skip_block_supports_serialization( $block_type, 'dimensions', 'aspectRatio' )
+ ) {
+ return $block_content;
+ }
+
+ $dimensions_block_styles = array();
+ $dimensions_block_styles['aspectRatio'] = $block_attributes['style']['dimensions']['aspectRatio'] ?? null;
+
+ // To ensure the aspect ratio does not get overridden by `minHeight` unset any existing rule.
+ if (
+ isset( $dimensions_block_styles['aspectRatio'] )
+ ) {
+ $dimensions_block_styles['minHeight'] = 'unset';
+ } elseif (
+ isset( $block_attributes['style']['dimensions']['minHeight'] ) ||
+ isset( $block_attributes['minHeight'] )
+ ) {
+ $dimensions_block_styles['aspectRatio'] = 'unset';
+ }
+
+ $styles = gutenberg_style_engine_get_styles( array( 'dimensions' => $dimensions_block_styles ) );
+
+ if ( ! empty( $styles['css'] ) ) {
+ // Inject dimensions styles to the first element, presuming it's the wrapper, if it exists.
+ $tags = new WP_HTML_Tag_Processor( $block_content );
+
+ if ( $tags->next_tag() ) {
+ $existing_style = $tags->get_attribute( 'style' );
+ $updated_style = '';
+
+ if ( ! empty( $existing_style ) ) {
+ $updated_style = $existing_style;
+ if ( ! str_ends_with( $existing_style, ';' ) ) {
+ $updated_style .= ';';
+ }
+ }
+
+ $updated_style .= $styles['css'];
+ $tags->set_attribute( 'style', $updated_style );
+
+ if ( ! empty( $styles['classnames'] ) ) {
+ foreach ( explode( ' ', $styles['classnames'] ) as $class_name ) {
+ if (
+ str_contains( $class_name, 'aspect-ratio' ) &&
+ ! isset( $block_attributes['style']['dimensions']['aspectRatio'] )
+ ) {
+ continue;
+ }
+ $tags->add_class( $class_name );
+ }
+ }
+ }
+
+ return $tags->get_updated_html();
+ }
+
+ return $block_content;
+}
+
+add_filter( 'render_block', 'gutenberg_render_dimensions_support', 10, 2 );
+
// Register the block support.
WP_Block_Supports::get_instance()->register(
'dimensions',
diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php
index f4266a7ef66dd5..af074c22ead450 100644
--- a/lib/class-wp-theme-json-gutenberg.php
+++ b/lib/class-wp-theme-json-gutenberg.php
@@ -211,6 +211,7 @@ class WP_Theme_JSON_Gutenberg {
* @var array
*/
const PROPERTIES_METADATA = array(
+ 'aspect-ratio' => array( 'dimensions', 'aspectRatio' ),
'background' => array( 'color', 'gradient' ),
'background-color' => array( 'color', 'background' ),
'border-radius' => array( 'border', 'radius' ),
@@ -381,7 +382,8 @@ class WP_Theme_JSON_Gutenberg {
),
'custom' => null,
'dimensions' => array(
- 'minHeight' => null,
+ 'aspectRatio' => null,
+ 'minHeight' => null,
),
'layout' => array(
'contentSize' => null,
@@ -486,7 +488,8 @@ class WP_Theme_JSON_Gutenberg {
'text' => null,
),
'dimensions' => array(
- 'minHeight' => null,
+ 'aspectRatio' => null,
+ 'minHeight' => null,
),
'filter' => array(
'duotone' => null,
@@ -661,6 +664,7 @@ public static function get_element_class_name( $element ) {
array( 'color', 'heading' ),
array( 'color', 'button' ),
array( 'color', 'caption' ),
+ array( 'dimensions', 'aspectRatio' ),
array( 'dimensions', 'minHeight' ),
// BEGIN EXPERIMENTAL.
// Allow `position.fixed` to be opted-in by default.
@@ -2093,6 +2097,15 @@ protected static function compute_style_properties( $styles, $settings = array()
$value = gutenberg_get_typography_font_size_value( array( 'size' => $value ) );
}
+ if ( 'aspect-ratio' === $css_property ) {
+ // For aspect ratio to work, other dimensions rules must be unset.
+ // This ensures that a fixed height does not override the aspect ratio.
+ $declarations[] = array(
+ 'name' => 'min-height',
+ 'value' => 'unset',
+ );
+ }
+
$declarations[] = array(
'name' => $css_property,
'value' => $value,
diff --git a/packages/block-editor/src/components/dimensions-tool/aspect-ratio-tool.js b/packages/block-editor/src/components/dimensions-tool/aspect-ratio-tool.js
index 988c6b5c286869..5ff35ae0e0c888 100644
--- a/packages/block-editor/src/components/dimensions-tool/aspect-ratio-tool.js
+++ b/packages/block-editor/src/components/dimensions-tool/aspect-ratio-tool.js
@@ -98,6 +98,7 @@ export default function AspectRatioTool( {
onChange = () => {},
options = DEFAULT_ASPECT_RATIO_OPTIONS,
defaultValue = DEFAULT_ASPECT_RATIO_OPTIONS[ 0 ].value,
+ hasValue,
isShownByDefault = true,
} ) {
// Match the CSS default so if the value is used directly in CSS it will look correct in the control.
@@ -105,7 +106,9 @@ export default function AspectRatioTool( {
return (
displayValue !== defaultValue }
+ hasValue={
+ hasValue ? hasValue : () => displayValue !== defaultValue
+ }
label={ __( 'Aspect ratio' ) }
onDeselect={ () => onChange( undefined ) }
isShownByDefault={ isShownByDefault }
diff --git a/packages/block-editor/src/components/global-styles/dimensions-panel.js b/packages/block-editor/src/components/global-styles/dimensions-panel.js
index 47e50aa515e3c6..0d486e29452637 100644
--- a/packages/block-editor/src/components/global-styles/dimensions-panel.js
+++ b/packages/block-editor/src/components/global-styles/dimensions-panel.js
@@ -27,6 +27,7 @@ import { getValueFromVariable, TOOLSPANEL_DROPDOWNMENU_PROPS } from './utils';
import SpacingSizesControl from '../spacing-sizes-control';
import HeightControl from '../height-control';
import ChildLayoutControl from '../child-layout-control';
+import AspectRatioTool from '../dimensions-tool/aspect-ratio-tool';
import { cleanEmptyObject } from '../../hooks/utils';
import { setImmutably } from '../../utils/object';
@@ -39,6 +40,7 @@ export function useHasDimensionsPanel( settings ) {
const hasMargin = useHasMargin( settings );
const hasGap = useHasGap( settings );
const hasMinHeight = useHasMinHeight( settings );
+ const hasAspectRatio = useHasAspectRatio( settings );
const hasChildLayout = useHasChildLayout( settings );
return (
@@ -49,6 +51,7 @@ export function useHasDimensionsPanel( settings ) {
hasMargin ||
hasGap ||
hasMinHeight ||
+ hasAspectRatio ||
hasChildLayout )
);
}
@@ -77,6 +80,10 @@ function useHasMinHeight( settings ) {
return settings?.dimensions?.minHeight;
}
+function useHasAspectRatio( settings ) {
+ return settings?.dimensions?.aspectRatio;
+}
+
function useHasChildLayout( settings ) {
const {
type: parentLayoutType = 'default',
@@ -192,6 +199,7 @@ const DEFAULT_CONTROLS = {
margin: true,
blockGap: true,
minHeight: true,
+ aspectRatio: true,
childLayout: true,
};
@@ -346,8 +354,18 @@ export default function DimensionsPanel( {
const showMinHeightControl = useHasMinHeight( settings );
const minHeightValue = decodeValue( inheritedValue?.dimensions?.minHeight );
const setMinHeightValue = ( newValue ) => {
+ const tempValue = setImmutably(
+ value,
+ [ 'dimensions', 'minHeight' ],
+ newValue
+ );
+ // Apply min-height, while removing any applied aspect ratio.
onChange(
- setImmutably( value, [ 'dimensions', 'minHeight' ], newValue )
+ setImmutably(
+ tempValue,
+ [ 'dimensions', 'aspectRatio' ],
+ undefined
+ )
);
};
const resetMinHeightValue = () => {
@@ -355,6 +373,24 @@ export default function DimensionsPanel( {
};
const hasMinHeightValue = () => !! value?.dimensions?.minHeight;
+ // Aspect Ratio
+ const showAspectRatioControl = useHasAspectRatio( settings );
+ const aspectRatioValue = decodeValue(
+ inheritedValue?.dimensions?.aspectRatio
+ );
+ const setAspectRatioValue = ( newValue ) => {
+ const tempValue = setImmutably(
+ value,
+ [ 'dimensions', 'aspectRatio' ],
+ newValue
+ );
+ // Apply aspect-ratio, while removing any applied min-height.
+ onChange(
+ setImmutably( tempValue, [ 'dimensions', 'minHeight' ], undefined )
+ );
+ };
+ const hasAspectRatioValue = () => !! value?.dimensions?.aspectRatio;
+
// Child Layout
const showChildLayoutControl = useHasChildLayout( settings );
const childLayout = inheritedValue?.layout;
@@ -397,6 +433,7 @@ export default function DimensionsPanel( {
dimensions: {
...previousValue?.dimensions,
minHeight: undefined,
+ aspectRatio: undefined,
},
};
}, [] );
@@ -617,6 +654,18 @@ export default function DimensionsPanel( {
/>
) }
+ { showAspectRatioControl && (
+
+ ) }
{ showChildLayoutControl && (
{
+ if ( ! supportedStyles.includes( key ) ) {
+ updatedSettings.dimensions = {
+ ...updatedSettings.dimensions,
+ [ key ]: false,
+ };
+ }
+ } );
[ 'radius', 'color', 'style', 'width' ].forEach( ( key ) => {
if (
diff --git a/packages/block-editor/src/components/global-styles/use-global-styles-output.js b/packages/block-editor/src/components/global-styles/use-global-styles-output.js
index 1cd63ef4d03f00..ac51a0ca443db6 100644
--- a/packages/block-editor/src/components/global-styles/use-global-styles-output.js
+++ b/packages/block-editor/src/components/global-styles/use-global-styles-output.js
@@ -434,6 +434,12 @@ export function getStylesDeclarations(
);
}
+ // For aspect ratio to work, other dimensions rules (and Cover block defaults) must be unset.
+ // This ensures that a fixed height does not override the aspect ratio.
+ if ( cssProperty === 'aspect-ratio' ) {
+ output.push( 'min-height: unset' );
+ }
+
output.push( `${ cssProperty }: ${ ruleValue }` );
} );
diff --git a/packages/block-editor/src/hooks/dimensions.js b/packages/block-editor/src/hooks/dimensions.js
index bbf5b12ca27cf8..6f03ddac2c6504 100644
--- a/packages/block-editor/src/hooks/dimensions.js
+++ b/packages/block-editor/src/hooks/dimensions.js
@@ -1,7 +1,12 @@
+/**
+ * External dependencies
+ */
+import classnames from 'classnames';
+
/**
* WordPress dependencies
*/
-import { useState, useEffect, useCallback } from '@wordpress/element';
+import { Platform, useState, useEffect, useCallback } from '@wordpress/element';
import { useDispatch, useSelect } from '@wordpress/data';
import { getBlockSupport } from '@wordpress/blocks';
import deprecated from '@wordpress/deprecated';
@@ -18,8 +23,7 @@ import { MarginVisualizer } from './margin';
import { PaddingVisualizer } from './padding';
import { store as blockEditorStore } from '../store';
import { unlock } from '../lock-unlock';
-
-import { cleanEmptyObject } from './utils';
+import { cleanEmptyObject, shouldSkipSerialization } from './utils';
export const DIMENSIONS_SUPPORT_KEY = 'dimensions';
export const SPACING_SUPPORT_KEY = 'spacing';
@@ -125,6 +129,73 @@ export function DimensionsPanel( { clientId, name, setAttributes, settings } ) {
);
}
+/**
+ * Determine whether there is block support for dimensions.
+ *
+ * @param {string} blockName Block name.
+ * @param {string} feature Background image feature to check for.
+ *
+ * @return {boolean} Whether there is support.
+ */
+export function hasDimensionsSupport( blockName, feature = 'any' ) {
+ if ( Platform.OS !== 'web' ) {
+ return false;
+ }
+
+ const support = getBlockSupport( blockName, DIMENSIONS_SUPPORT_KEY );
+
+ if ( support === true ) {
+ return true;
+ }
+
+ if ( feature === 'any' ) {
+ return !! ( support?.aspectRatio || !! support?.minHeight );
+ }
+
+ return !! support?.[ feature ];
+}
+
+export default {
+ useBlockProps,
+ attributeKeys: [ 'minHeight', 'style' ],
+ hasSupport( name ) {
+ return hasDimensionsSupport( name, 'aspectRatio' );
+ },
+};
+
+function useBlockProps( { name, minHeight, style } ) {
+ if (
+ ! hasDimensionsSupport( name, 'aspectRatio' ) ||
+ shouldSkipSerialization( name, DIMENSIONS_SUPPORT_KEY, 'aspectRatio' )
+ ) {
+ return {};
+ }
+
+ const className = classnames( {
+ 'has-aspect-ratio': !! style?.dimensions?.aspectRatio,
+ } );
+
+ // Allow dimensions-based inline style overrides to override any global styles rules that
+ // might be set for the block, and therefore affect the display of the aspect ratio.
+ const inlineStyleOverrides = {};
+
+ // Apply rules to unset incompatible styles.
+ // Note that a set `aspectRatio` will win out if both an aspect ratio and a minHeight are set.
+ // This is because the aspect ratio is a newer block support, so (in theory) any aspect ratio
+ // that is set should be intentional and should override any existing minHeight. The Cover block
+ // and dimensions controls have logic that will manually clear the aspect ratio if a minHeight
+ // is set.
+ if ( style?.dimensions?.aspectRatio ) {
+ // To ensure the aspect ratio does not get overridden by `minHeight` unset any existing rule.
+ inlineStyleOverrides.minHeight = 'unset';
+ } else if ( minHeight || style?.dimensions?.minHeight ) {
+ // To ensure the minHeight does not get overridden by `aspectRatio` unset any existing rule.
+ inlineStyleOverrides.aspectRatio = 'unset';
+ }
+
+ return { className, style: inlineStyleOverrides };
+}
+
/**
* @deprecated
*/
diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js
index 5760c8edb4e2ab..e3c0a7580aab3c 100644
--- a/packages/block-editor/src/hooks/index.js
+++ b/packages/block-editor/src/hooks/index.js
@@ -16,6 +16,7 @@ import './generated-class-name';
import style from './style';
import './settings';
import color from './color';
+import dimensions from './dimensions';
import duotone from './duotone';
import fontFamily from './font-family';
import fontSize from './font-size';
@@ -47,6 +48,7 @@ createBlockListBlockFilter( [
align,
style,
color,
+ dimensions,
duotone,
fontFamily,
fontSize,
diff --git a/packages/block-editor/src/hooks/style.js b/packages/block-editor/src/hooks/style.js
index dec046f888a468..52fd990a3cac44 100644
--- a/packages/block-editor/src/hooks/style.js
+++ b/packages/block-editor/src/hooks/style.js
@@ -133,10 +133,14 @@ const skipSerializationPathsEdit = {
*/
const skipSerializationPathsSave = {
...skipSerializationPathsEdit,
+ [ `${ DIMENSIONS_SUPPORT_KEY }.aspectRatio` ]: [
+ `${ DIMENSIONS_SUPPORT_KEY }.aspectRatio`,
+ ], // Skip serialization of aspect ratio in save mode.
[ `${ BACKGROUND_SUPPORT_KEY }` ]: [ BACKGROUND_SUPPORT_KEY ], // Skip serialization of background support in save mode.
};
const skipSerializationPathsSaveChecks = {
+ [ `${ DIMENSIONS_SUPPORT_KEY }.aspectRatio` ]: true,
[ `${ BACKGROUND_SUPPORT_KEY }` ]: true,
};
diff --git a/packages/block-editor/src/hooks/utils.js b/packages/block-editor/src/hooks/utils.js
index ea31a516ac6343..f13963933e5225 100644
--- a/packages/block-editor/src/hooks/utils.js
+++ b/packages/block-editor/src/hooks/utils.js
@@ -195,6 +195,7 @@ export function useBlockSettings( name, parentLayout ) {
blockGap,
spacingSizes,
units,
+ aspectRatio,
minHeight,
layout,
borderColor,
@@ -244,6 +245,7 @@ export function useBlockSettings( name, parentLayout ) {
'spacing.blockGap',
'spacing.spacingSizes',
'spacing.units',
+ 'dimensions.aspectRatio',
'dimensions.minHeight',
'layout',
'border.color',
@@ -343,6 +345,7 @@ export function useBlockSettings( name, parentLayout ) {
width: borderWidth,
},
dimensions: {
+ aspectRatio,
minHeight,
},
layout,
@@ -371,6 +374,7 @@ export function useBlockSettings( name, parentLayout ) {
blockGap,
spacingSizes,
units,
+ aspectRatio,
minHeight,
layout,
parentLayout,
diff --git a/packages/block-library/src/cover/block.json b/packages/block-library/src/cover/block.json
index d2c55dd26b4d74..80562da3098997 100644
--- a/packages/block-library/src/cover/block.json
+++ b/packages/block-library/src/cover/block.json
@@ -114,6 +114,9 @@
"__experimentalSkipSerialization": [ "gradients" ],
"enableContrastChecker": false
},
+ "dimensions": {
+ "aspectRatio": true
+ },
"typography": {
"fontSize": true,
"lineHeight": true,
diff --git a/packages/block-library/src/cover/edit/index.js b/packages/block-library/src/cover/edit/index.js
index 8c5488584094c6..098a3fe6f5d773 100644
--- a/packages/block-library/src/cover/edit/index.js
+++ b/packages/block-library/src/cover/edit/index.js
@@ -431,7 +431,8 @@ function CoverEdit( {
toggleSelection( true );
setAttributes( { minHeight: newMinHeight } );
},
- showHandle: true,
+ // Hide the resize handle if an aspect ratio is set, as the aspect ratio takes precedence.
+ showHandle: ! attributes.style?.dimensions?.aspectRatio ? true : false,
size: resizableBoxDimensions,
width,
};
diff --git a/packages/block-library/src/cover/edit/inspector-controls.js b/packages/block-library/src/cover/edit/inspector-controls.js
index 4aa655b7229e50..6ac708d0462232 100644
--- a/packages/block-library/src/cover/edit/inspector-controls.js
+++ b/packages/block-library/src/cover/edit/inspector-controls.js
@@ -24,6 +24,7 @@ import {
__experimentalColorGradientSettingsDropdown as ColorGradientSettingsDropdown,
__experimentalUseGradient,
__experimentalUseMultipleOriginColorsAndGradients as useMultipleOriginColorsAndGradients,
+ privateApis as blockEditorPrivateApis,
} from '@wordpress/block-editor';
import { __ } from '@wordpress/i18n';
@@ -31,6 +32,9 @@ import { __ } from '@wordpress/i18n';
* Internal dependencies
*/
import { COVER_MIN_HEIGHT, mediaPosition } from '../shared';
+import { unlock } from '../../lock-unlock';
+
+const { cleanEmptyObject } = unlock( blockEditorPrivateApis );
function CoverHeightInput( {
onChange,
@@ -306,7 +310,16 @@ export default function CoverInspectorControls( {
value={ minHeight }
unit={ minHeightUnit }
onChange={ ( newMinHeight ) =>
- setAttributes( { minHeight: newMinHeight } )
+ setAttributes( {
+ minHeight: newMinHeight,
+ style: cleanEmptyObject( {
+ ...attributes?.style,
+ dimensions: {
+ ...attributes?.style?.dimensions,
+ aspectRatio: undefined, // Reset aspect ratio when minHeight is set.
+ },
+ } ),
+ } )
}
onUnitChange={ ( nextUnit ) =>
setAttributes( {
diff --git a/packages/block-library/src/cover/style.scss b/packages/block-library/src/cover/style.scss
index 837e3834e2e1ba..3b4eac41a0d3b4 100644
--- a/packages/block-library/src/cover/style.scss
+++ b/packages/block-library/src/cover/style.scss
@@ -8,10 +8,9 @@
align-items: center;
padding: 1em;
// Prevent the `wp-block-cover__background` span from overflowing the container when border-radius is applied.
- // `overflow: hidden` is provided as a fallback for browsers that don't support `overflow: clip`.
- overflow: hidden;
// Use clip instead of overflow: hidden so that sticky position works on child elements.
- overflow: clip;
+ // Use overflow-x instead of overflow so that aspect-ratio allows content to expand the area of the cover block.
+ overflow-x: clip;
// This block has customizable padding, border-box makes that more predictable.
box-sizing: border-box;
// Keep the flex layout direction to the physical direction (LTR) in RTL languages.
diff --git a/packages/block-library/src/group/block.json b/packages/block-library/src/group/block.json
index df59c25a7751fb..674b0645f50215 100644
--- a/packages/block-library/src/group/block.json
+++ b/packages/block-library/src/group/block.json
@@ -55,6 +55,7 @@
}
},
"dimensions": {
+ "aspectRatio": true,
"minHeight": true
},
"__experimentalBorder": {
diff --git a/packages/blocks/src/api/constants.js b/packages/blocks/src/api/constants.js
index f08717592cb4b2..8af2d0940c8fcc 100644
--- a/packages/blocks/src/api/constants.js
+++ b/packages/blocks/src/api/constants.js
@@ -20,6 +20,11 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = {
value: [ 'color', 'link' ],
support: [ 'color', 'link' ],
},
+ aspectRatio: {
+ value: [ 'dimensions', 'aspectRatio' ],
+ support: [ 'dimensions', 'aspectRatio' ],
+ useEngine: true,
+ },
background: {
value: [ 'color', 'gradient' ],
support: [ 'color', 'gradients' ],
diff --git a/packages/edit-site/src/components/global-styles/screen-block.js b/packages/edit-site/src/components/global-styles/screen-block.js
index def7d1d627fe46..aaeb1a4a833fa7 100644
--- a/packages/edit-site/src/components/global-styles/screen-block.js
+++ b/packages/edit-site/src/components/global-styles/screen-block.js
@@ -109,6 +109,16 @@ function ScreenBlock( { name, variation } ) {
settings.spacing.blockGap = false;
}
+ // Only allow `aspectRatio` support if the block is not the grouping block.
+ // The grouping block allows the user to use Group, Row and Stack variations,
+ // and it is highly likely that the user will not want to set an aspect ratio
+ // for all three at once. Until there is the ability to set a different aspect
+ // ratio for each variation, we disable the aspect ratio controls for the
+ // grouping block in global styles.
+ if ( settings?.dimensions?.aspectRatio && name === 'core/group' ) {
+ settings.dimensions.aspectRatio = false;
+ }
+
const blockVariations = useBlockVariations( name );
const hasTypographyPanel = useHasTypographyPanel( settings );
const hasColorPanel = useHasColorPanel( settings );
diff --git a/packages/style-engine/class-wp-style-engine.php b/packages/style-engine/class-wp-style-engine.php
index f0ecb9f9038065..31c18e56e34d86 100644
--- a/packages/style-engine/class-wp-style-engine.php
+++ b/packages/style-engine/class-wp-style-engine.php
@@ -185,7 +185,16 @@ final class WP_Style_Engine {
),
),
'dimensions' => array(
- 'minHeight' => array(
+ 'aspectRatio' => array(
+ 'property_keys' => array(
+ 'default' => 'aspect-ratio',
+ ),
+ 'path' => array( 'dimensions', 'aspectRatio' ),
+ 'classnames' => array(
+ 'has-aspect-ratio' => true,
+ ),
+ ),
+ 'minHeight' => array(
'property_keys' => array(
'default' => 'min-height',
),
diff --git a/packages/style-engine/src/styles/dimensions/index.ts b/packages/style-engine/src/styles/dimensions/index.ts
index df3568f7799133..4ffb33e65686d6 100644
--- a/packages/style-engine/src/styles/dimensions/index.ts
+++ b/packages/style-engine/src/styles/dimensions/index.ts
@@ -16,4 +16,16 @@ const minHeight = {
},
};
-export default [ minHeight ];
+const aspectRatio = {
+ name: 'aspectRatio',
+ generate: ( style: Style, options: StyleOptions ) => {
+ return generateRule(
+ style,
+ options,
+ [ 'dimensions', 'aspectRatio' ],
+ 'aspectRatio'
+ );
+ },
+};
+
+export default [ minHeight, aspectRatio ];
diff --git a/packages/style-engine/src/types.ts b/packages/style-engine/src/types.ts
index 91a73e158e1cee..b5085ce53f1363 100644
--- a/packages/style-engine/src/types.ts
+++ b/packages/style-engine/src/types.ts
@@ -48,6 +48,7 @@ export interface Style {
left?: BorderIndividualStyles< 'left' >;
};
dimensions?: {
+ aspectRatio?: CSSProperties[ 'aspectRatio' ];
minHeight?: CSSProperties[ 'minHeight' ];
};
spacing?: {
diff --git a/schemas/json/block.json b/schemas/json/block.json
index 9cc7a83c2304dd..5658361d7aab64 100644
--- a/schemas/json/block.json
+++ b/schemas/json/block.json
@@ -316,6 +316,11 @@
"type": "object",
"description": "This value signals that a block supports some of the CSS style properties related to dimensions. When it does, the block editor will show UI controls for the user to set their values if the theme declares support.\n\nWhen the block declares support for a specific dimensions property, its attributes definition is extended to include the style attribute.",
"properties": {
+ "aspectRatio": {
+ "type": "boolean",
+ "description": "Allow blocks to define an aspect ratio value.",
+ "default": false
+ },
"minHeight": {
"type": "boolean",
"description": "Allow blocks to define a minimum height value.",
@@ -619,6 +624,7 @@
"type": "object",
"properties": {
"root": { "type": "string" },
+ "aspectRatio": { "type": "string" },
"minHeight": { "type": "string" }
}
}
diff --git a/schemas/json/theme.json b/schemas/json/theme.json
index 6ae8d15df63d2d..7f12e4f7097765 100644
--- a/schemas/json/theme.json
+++ b/schemas/json/theme.json
@@ -20,7 +20,7 @@
"type": "object",
"properties": {
"appearanceTools": {
- "description": "Setting that enables the following UI tools:\n\n- background: backgroundImage\n- border: color, radius, style, width\n- color: link\n- dimensions: minHeight\n- position: sticky\n- spacing: blockGap, margin, padding\n- typography: lineHeight",
+ "description": "Setting that enables the following UI tools:\n\n- background: backgroundImage\n- border: color, radius, style, width\n- color: link\n- dimensions: aspectRatio, minHeight\n- position: sticky\n- spacing: blockGap, margin, padding\n- typography: lineHeight",
"type": "boolean",
"default": false
}
@@ -271,6 +271,11 @@
"description": "Settings related to dimensions.",
"type": "object",
"properties": {
+ "aspectRatio": {
+ "description": "Allow users to set an aspect ratio.",
+ "type": "boolean",
+ "default": false
+ },
"minHeight": {
"description": "Allow users to set custom minimum height.",
"type": "boolean",
@@ -1419,6 +1424,17 @@
"description": "Dimensions styles",
"type": "object",
"properties": {
+ "aspectRatio": {
+ "description": "Sets the `aspect-ratio` CSS property.",
+ "oneOf": [
+ {
+ "type": "string"
+ },
+ {
+ "$ref": "#/definitions/refComplete"
+ }
+ ]
+ },
"minHeight": {
"description": "Sets the `min-height` CSS property.",
"oneOf": [