diff --git a/docs/how-to-guides/themes/theme-json.md b/docs/how-to-guides/themes/theme-json.md
index 63866002ea593e..795c9a8367ccb1 100644
--- a/docs/how-to-guides/themes/theme-json.md
+++ b/docs/how-to-guides/themes/theme-json.md
@@ -182,6 +182,7 @@ The settings section has the following structure and default values:
"wideSize": "1000px",
},
"spacing": {
+ "customMargin": false,
"customPadding": false, /* Supersedes add_theme_support('custom-spacing') */
"units": [ "px", "em", "rem", "vh", "vw" ], /* filter values, as in add_theme_support('custom-units', ... ) */
},
@@ -464,6 +465,12 @@ Each block declares which style properties it exposes via the [block supports me
"text": "value"
},
"spacing": {
+ "margin": {
+ "top": "value",
+ "right": "value",
+ "bottom": "value",
+ "left": "value"
+ },
"padding": {
"top": "value",
"right": "value",
diff --git a/docs/reference-guides/block-api/block-supports.md b/docs/reference-guides/block-api/block-supports.md
index 1f5ab06e7b7b16..5b920ab3b5adf5 100644
--- a/docs/reference-guides/block-api/block-supports.md
+++ b/docs/reference-guides/block-api/block-supports.md
@@ -497,6 +497,7 @@ supports: {
- Type: `Object`
- Default value: null
- Subproperties:
+ - `margin`: type `boolean` or `array`, default value `false`
- `padding`: type `boolean` or `array`, default value `false`
This value signals that a block supports some of the CSS style properties related to spacing. When it does, the block editor will show UI controls for the user to set their values, if [the theme declares support](/docs/how-to-guides/themes/theme-support.md#cover-block-padding).
@@ -504,6 +505,7 @@ This value signals that a block supports some of the CSS style properties relate
```js
supports: {
spacing: {
+ margin: true, // Enable margin UI control.
padding: true, // Enable padding UI control.
}
}
@@ -511,12 +513,13 @@ supports: {
When the block declares support for a specific spacing property, the attributes definition is extended to include the `style` attribute.
-- `style`: attribute of `object` type with no default assigned. This is added when `padding` support is declared. It stores the custom values set by the user.
+- `style`: attribute of `object` type with no default assigned. This is added when `margin` or `padding` support is declared. It stores the custom values set by the user.
```js
supports: {
spacing: {
- padding: [ 'top', 'bottom' ], // Enable padding for arbitrary sides.
+ margin: [ 'top', 'bottom' ], // Enable margin for arbitrary sides.
+ padding: true, // Enable padding for all sides.
}
}
```
diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php
index f13539fd6aad0f..07ec3c9956dd8a 100644
--- a/lib/block-supports/layout.php
+++ b/lib/block-supports/layout.php
@@ -65,8 +65,8 @@ function gutenberg_render_layout_support_flag( $block_content, $block ) {
?>
> * {
;
- margin-left: auto;
- margin-right: auto;
+ margin-left: auto !important;
+ margin-right: auto !important;
}
> .alignwide {
diff --git a/lib/class-wp-theme-json.php b/lib/class-wp-theme-json.php
index f6112584a1295b..f2e6c4571c274e 100644
--- a/lib/class-wp-theme-json.php
+++ b/lib/class-wp-theme-json.php
@@ -55,6 +55,12 @@ class WP_Theme_JSON {
'text' => null,
),
'spacing' => array(
+ 'margin' => array(
+ 'top' => null,
+ 'right' => null,
+ 'bottom' => null,
+ 'left' => null,
+ ),
'padding' => array(
'bottom' => null,
'left' => null,
@@ -91,6 +97,7 @@ class WP_Theme_JSON {
'custom' => null,
'layout' => null,
'spacing' => array(
+ 'customMargin' => null,
'customPadding' => null,
'units' => null,
),
@@ -237,6 +244,10 @@ class WP_Theme_JSON {
'line-height' => array(
'value' => array( 'typography', 'lineHeight' ),
),
+ 'margin' => array(
+ 'value' => array( 'spacing', 'margin' ),
+ 'properties' => array( 'top', 'right', 'bottom', 'left' ),
+ ),
'padding' => array(
'value' => array( 'spacing', 'padding' ),
'properties' => array( 'top', 'right', 'bottom', 'left' ),
diff --git a/lib/experimental-default-theme.json b/lib/experimental-default-theme.json
index 7c0cf2e2401efa..ab27fd425784b5 100644
--- a/lib/experimental-default-theme.json
+++ b/lib/experimental-default-theme.json
@@ -209,6 +209,7 @@
]
},
"spacing": {
+ "customMargin": false,
"customPadding": false,
"units": [ "px", "em", "rem", "vh", "vw" ]
},
diff --git a/packages/block-editor/src/components/block-list/layout.js b/packages/block-editor/src/components/block-list/layout.js
index 3f898ae12c02fe..e29fb8ab05ccaa 100644
--- a/packages/block-editor/src/components/block-list/layout.js
+++ b/packages/block-editor/src/components/block-list/layout.js
@@ -43,8 +43,8 @@ export function LayoutStyle( { selector, layout = {} } ) {
? `
${ appendSelectors( selector, '> *' ) } {
max-width: ${ contentSize ?? wideSize };
- margin-left: auto;
- margin-right: auto;
+ margin-left: auto !important;
+ margin-right: auto !important;
}
${ appendSelectors( selector, '> [data-align="wide"]' ) } {
diff --git a/packages/block-editor/src/hooks/margin.js b/packages/block-editor/src/hooks/margin.js
new file mode 100644
index 00000000000000..a400a1eccb31a4
--- /dev/null
+++ b/packages/block-editor/src/hooks/margin.js
@@ -0,0 +1,101 @@
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { Platform } from '@wordpress/element';
+import { getBlockSupport } from '@wordpress/blocks';
+import { __experimentalBoxControl as BoxControl } from '@wordpress/components';
+
+/**
+ * Internal dependencies
+ */
+import useEditorFeature from '../components/use-editor-feature';
+import { SPACING_SUPPORT_KEY, useCustomSides } from './spacing';
+import { cleanEmptyObject } from './utils';
+import { useCustomUnits } from '../components/unit-control';
+
+/**
+ * Determines if there is margin support.
+ *
+ * @param {string|Object} blockType Block name or Block Type object.
+ * @return {boolean} Whether there is support.
+ */
+export function hasMarginSupport( blockType ) {
+ const support = getBlockSupport( blockType, SPACING_SUPPORT_KEY );
+ return !! ( true === support || support?.margin );
+}
+
+/**
+ * Custom hook that checks if margin settings have been disabled.
+ *
+ * @param {string} name The name of the block.
+ * @return {boolean} Whether margin setting is disabled.
+ */
+export function useIsMarginDisabled( { name: blockName } = {} ) {
+ const isDisabled = ! useEditorFeature( 'spacing.customMargin' );
+ return ! hasMarginSupport( blockName ) || isDisabled;
+}
+
+/**
+ * Inspector control panel containing the margin related configuration
+ *
+ * @param {Object} props Block props.
+ * @return {WPElement} Margin edit element.
+ */
+export function MarginEdit( props ) {
+ const {
+ name: blockName,
+ attributes: { style },
+ setAttributes,
+ } = props;
+
+ const units = useCustomUnits();
+ const sides = useCustomSides( blockName, 'margin' );
+
+ if ( ! hasMarginSupport( blockName ) ) {
+ return null;
+ }
+
+ const onChange = ( next ) => {
+ const newStyle = {
+ ...style,
+ spacing: {
+ ...style?.spacing,
+ margin: next,
+ },
+ };
+
+ setAttributes( {
+ style: cleanEmptyObject( newStyle ),
+ } );
+ };
+
+ const onChangeShowVisualizer = ( next ) => {
+ const newStyle = {
+ ...style,
+ visualizers: {
+ margin: next,
+ },
+ };
+
+ setAttributes( {
+ style: cleanEmptyObject( newStyle ),
+ } );
+ };
+
+ return Platform.select( {
+ web: (
+ <>
+
+ >
+ ),
+ native: null,
+ } );
+}
diff --git a/packages/block-editor/src/hooks/spacing.js b/packages/block-editor/src/hooks/spacing.js
index 1566f3015f39c0..a911727dd82f8f 100644
--- a/packages/block-editor/src/hooks/spacing.js
+++ b/packages/block-editor/src/hooks/spacing.js
@@ -10,6 +10,7 @@ import { getBlockSupport } from '@wordpress/blocks';
* Internal dependencies
*/
import InspectorControls from '../components/inspector-controls';
+import { MarginEdit, hasMarginSupport, useIsMarginDisabled } from './margin';
import {
PaddingEdit,
hasPaddingSupport,
@@ -36,13 +37,14 @@ export function SpacingPanel( props ) {
+
);
}
/**
- * Determine whether there is block support for padding.
+ * Determine whether there is block support for padding or margins.
*
* @param {string} blockName Block name.
* @return {boolean} Whether there is support.
@@ -52,7 +54,7 @@ export function hasSpacingSupport( blockName ) {
return false;
}
- return hasPaddingSupport( blockName );
+ return hasPaddingSupport( blockName ) || hasMarginSupport( blockName );
}
/**
@@ -63,8 +65,9 @@ export function hasSpacingSupport( blockName ) {
*/
const useIsSpacingDisabled = ( props = {} ) => {
const paddingDisabled = useIsPaddingDisabled( props );
+ const marginDisabled = useIsMarginDisabled( props );
- return paddingDisabled;
+ return paddingDisabled && marginDisabled;
};
/**
diff --git a/packages/block-editor/src/hooks/test/style.js b/packages/block-editor/src/hooks/test/style.js
index b6bd98eb56da6b..3067d104674491 100644
--- a/packages/block-editor/src/hooks/test/style.js
+++ b/packages/block-editor/src/hooks/test/style.js
@@ -25,6 +25,7 @@ describe( 'getInlineStyles', () => {
},
spacing: {
padding: { top: '10px' },
+ margin: { bottom: '15px' },
},
} )
).toEqual( {
@@ -36,6 +37,7 @@ describe( 'getInlineStyles', () => {
color: 'red',
lineHeight: 1.5,
fontSize: 10,
+ marginBottom: '15px',
paddingTop: '10px',
} );
} );
diff --git a/packages/blocks/src/api/constants.js b/packages/blocks/src/api/constants.js
index e276484dca72d9..badbc94320f001 100644
--- a/packages/blocks/src/api/constants.js
+++ b/packages/blocks/src/api/constants.js
@@ -66,6 +66,11 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = {
value: [ 'typography', 'lineHeight' ],
support: [ 'lineHeight' ],
},
+ margin: {
+ value: [ 'spacing', 'margin' ],
+ support: [ 'spacing', 'margin' ],
+ properties: [ 'top', 'right', 'bottom', 'left' ],
+ },
padding: {
value: [ 'spacing', 'padding' ],
support: [ 'spacing', 'padding' ],
diff --git a/packages/edit-site/src/components/sidebar/spacing-panel.js b/packages/edit-site/src/components/sidebar/spacing-panel.js
index f8c4e2053b5dd5..8672ae5d5be26c 100644
--- a/packages/edit-site/src/components/sidebar/spacing-panel.js
+++ b/packages/edit-site/src/components/sidebar/spacing-panel.js
@@ -45,15 +45,21 @@ const CSS_UNITS = [
export function useHasSpacingPanel( context ) {
const hasPadding = useHasPadding( context );
+ const hasMargin = useHasMargin( context );
- return hasPadding;
+ return hasPadding || hasMargin;
}
-export function useHasPadding( { name, supports } ) {
- return (
- useEditorFeature( 'spacing.customPadding', name ) &&
- supports.includes( 'padding' )
- );
+function useHasPadding( { name, supports } ) {
+ const settings = useEditorFeature( 'spacing.customPadding', name );
+
+ return settings && supports.includes( 'padding' );
+}
+
+function useHasMargin( { name, supports } ) {
+ const settings = useEditorFeature( 'spacing.customMargin', name );
+
+ return settings && supports.includes( 'margin' );
}
function filterUnitsWithSettings( settings = [], units = [] ) {
@@ -88,6 +94,7 @@ function filterValuesBySides( values, sides ) {
export default function SpacingPanel( { context, getStyle, setStyle } ) {
const { name } = context;
const showPaddingControl = useHasPadding( context );
+ const showMarginControl = useHasMargin( context );
const units = useCustomUnits( { contextName: name, units: CSS_UNITS } );
const paddingValues = getStyle( name, 'padding' );
@@ -98,6 +105,14 @@ export default function SpacingPanel( { context, getStyle, setStyle } ) {
setStyle( name, 'padding', padding );
};
+ const marginValues = getStyle( name, 'margin' );
+ const marginSides = useCustomSides( name, 'margin' );
+
+ const setMarginValues = ( newMarginValues ) => {
+ const margin = filterValuesBySides( newMarginValues, marginSides );
+ setStyle( name, 'margin', margin );
+ };
+
return (
{ showPaddingControl && (
@@ -109,6 +124,15 @@ export default function SpacingPanel( { context, getStyle, setStyle } ) {
units={ units }
/>
) }
+ { showMarginControl && (
+
+ ) }
);
}