diff --git a/lib/block-supports/border.php b/lib/block-supports/border.php
index e400a89fc0119d..9d8d97c690a7b8 100644
--- a/lib/block-supports/border.php
+++ b/lib/block-supports/border.php
@@ -56,8 +56,14 @@ function gutenberg_apply_border_support( $block_type, $block_attributes ) {
gutenberg_has_border_feature_support( $block_type, 'radius' ) &&
isset( $block_attributes['style']['border']['radius'] )
) {
- $border_radius = (int) $block_attributes['style']['border']['radius'];
- $styles[] = sprintf( 'border-radius: %dpx;', $border_radius );
+ $border_radius = $block_attributes['style']['border']['radius'];
+
+ // This check handles original unitless implementation.
+ if ( is_numeric( $border_radius ) ) {
+ $border_radius .= 'px';
+ }
+
+ $styles[] = sprintf( 'border-radius: %s;', $border_radius );
}
// Border style.
@@ -74,8 +80,14 @@ function gutenberg_apply_border_support( $block_type, $block_attributes ) {
gutenberg_has_border_feature_support( $block_type, 'width' ) &&
isset( $block_attributes['style']['border']['width'] )
) {
- $border_width = intval( $block_attributes['style']['border']['width'] );
- $styles[] = sprintf( 'border-width: %dpx;', $border_width );
+ $border_width = $block_attributes['style']['border']['width'];
+
+ // This check handles original unitless implementation.
+ if ( is_numeric( $border_width ) ) {
+ $border_width .= 'px';
+ }
+
+ $styles[] = sprintf( 'border-width: %s;', $border_width );
}
// Border color.
diff --git a/packages/block-editor/src/hooks/border-radius.js b/packages/block-editor/src/hooks/border-radius.js
index 50e9dece97eaf6..a301f0570725ad 100644
--- a/packages/block-editor/src/hooks/border-radius.js
+++ b/packages/block-editor/src/hooks/border-radius.js
@@ -1,16 +1,17 @@
/**
* WordPress dependencies
*/
-import { RangeControl } from '@wordpress/components';
+import { __experimentalUnitControl as UnitControl } from '@wordpress/components';
+import { useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
+import { CSS_UNITS, parseUnit } from './border';
import { cleanEmptyObject } from './utils';
const MIN_BORDER_RADIUS_VALUE = 0;
-const MAX_BORDER_RADIUS_VALUE = 50;
/**
* Inspector control panel containing the border radius related configuration.
@@ -24,6 +25,15 @@ export function BorderRadiusEdit( props ) {
setAttributes,
} = props;
+ // Step value is maintained in state so step is appropriate for current unit
+ // even when current radius value is undefined.
+ const initialStep = parseUnit( style?.border?.radius ) === 'px' ? 1 : 0.25;
+ const [ step, setStep ] = useState( initialStep );
+
+ const onUnitChange = ( newUnit ) => {
+ setStep( newUnit === 'px' ? 1 : 0.25 );
+ };
+
const onChange = ( newRadius ) => {
let newStyle = {
...style,
@@ -33,7 +43,7 @@ export function BorderRadiusEdit( props ) {
},
};
- if ( newRadius === undefined ) {
+ if ( newRadius === undefined || newRadius === '' ) {
newStyle = cleanEmptyObject( newStyle );
}
@@ -41,14 +51,14 @@ export function BorderRadiusEdit( props ) {
};
return (
-
);
}
diff --git a/packages/block-editor/src/hooks/border-width.js b/packages/block-editor/src/hooks/border-width.js
index 24e25f6a063850..341fca06fdc5bc 100644
--- a/packages/block-editor/src/hooks/border-width.js
+++ b/packages/block-editor/src/hooks/border-width.js
@@ -1,16 +1,17 @@
/**
* WordPress dependencies
*/
-import { RangeControl } from '@wordpress/components';
+import { __experimentalUnitControl as UnitControl } from '@wordpress/components';
+import { useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
+import { CSS_UNITS, parseUnit } from './border';
import { cleanEmptyObject } from './utils';
const MIN_BORDER_WIDTH = 0;
-const MAX_BORDER_WIDTH = 50;
/**
* Inspector control for configuring border width property.
@@ -24,8 +25,17 @@ export const BorderWidthEdit = ( props ) => {
setAttributes,
} = props;
+ // Step value is maintained in state so step is appropriate for current unit
+ // even when current radius value is undefined.
+ const initialStep = parseUnit( style?.border?.width ) === 'px' ? 1 : 0.25;
+ const [ step, setStep ] = useState( initialStep );
+
+ const onUnitChange = ( newUnit ) => {
+ setStep( newUnit === 'px' ? 1 : 0.25 );
+ };
+
const onChange = ( newWidth ) => {
- const newStyle = {
+ let newStyle = {
...style,
border: {
...style?.border,
@@ -33,18 +43,22 @@ export const BorderWidthEdit = ( props ) => {
},
};
- setAttributes( { style: cleanEmptyObject( newStyle ) } );
+ if ( newWidth === undefined || newWidth === '' ) {
+ newStyle = cleanEmptyObject( newStyle );
+ }
+
+ setAttributes( { style: newStyle } );
};
return (
-
);
};
diff --git a/packages/block-editor/src/hooks/border.js b/packages/block-editor/src/hooks/border.js
index 6749afa663482f..f7db904d666aa1 100644
--- a/packages/block-editor/src/hooks/border.js
+++ b/packages/block-editor/src/hooks/border.js
@@ -16,7 +16,44 @@ import { BorderRadiusEdit } from './border-radius';
import { BorderStyleEdit } from './border-style';
import { BorderWidthEdit } from './border-width';
+const isWeb = Platform.OS === 'web';
+
export const BORDER_SUPPORT_KEY = '__experimentalBorder';
+export const CSS_UNITS = [
+ {
+ value: 'px',
+ label: isWeb ? 'px' : __( 'Pixels (px)' ),
+ default: '',
+ a11yLabel: __( 'Pixels (px)' ),
+ },
+ {
+ value: 'em',
+ label: isWeb ? 'em' : __( 'Relative to parent font size (em)' ),
+ default: '',
+ a11yLabel: __( 'Relative to parent font size (em)' ),
+ },
+ {
+ value: 'rem',
+ label: isWeb ? 'rem' : __( 'Relative to root font size (rem)' ),
+ default: '',
+ a11yLabel: __( 'Relative to root font size (rem)' ),
+ },
+];
+
+/**
+ * Parses a CSS unit from a border CSS value.
+ *
+ * @param {string} cssValue CSS value to parse e.g. `10px` or `1.5em`.
+ * @return {string} CSS unit from provided value or default 'px'.
+ */
+export function parseUnit( cssValue ) {
+ const value = String( cssValue ).trim();
+ const unitMatch = value.match( /[\d.\-\+]*\s*(.*)/ )[ 1 ];
+ const unit = unitMatch !== undefined ? unitMatch.toLowerCase() : '';
+ const currentUnit = CSS_UNITS.find( ( item ) => item.value === unit );
+
+ return currentUnit?.value || 'px';
+}
export function BorderPanel( props ) {
const isDisabled = useIsBorderDisabled( props );
@@ -44,7 +81,11 @@ export function BorderPanel( props ) {
return (
-
+
{ isStyleSupported && }
{ isWidthSupported && }
{ isRadiusSupported && }
diff --git a/packages/block-editor/src/hooks/border.scss b/packages/block-editor/src/hooks/border.scss
new file mode 100644
index 00000000000000..61d75c59454635
--- /dev/null
+++ b/packages/block-editor/src/hooks/border.scss
@@ -0,0 +1,10 @@
+.block-editor-hooks__border-controls {
+ .components-unit-control-wrapper {
+ margin-bottom: $grid-unit-30;
+
+ &:last-child {
+ margin-bottom: $grid-unit-10;
+ }
+ }
+}
+
diff --git a/packages/block-editor/src/hooks/test/style.js b/packages/block-editor/src/hooks/test/style.js
index 75a520b14ec6f5..85adb56c802e19 100644
--- a/packages/block-editor/src/hooks/test/style.js
+++ b/packages/block-editor/src/hooks/test/style.js
@@ -18,8 +18,8 @@ describe( 'getInlineStyles', () => {
color: { text: 'red', background: 'black' },
typography: { lineHeight: 1.5, fontSize: 10 },
border: {
- radius: 10,
- width: 3,
+ radius: '10px',
+ width: '1em',
style: 'dotted',
color: '#21759b',
},
@@ -31,9 +31,9 @@ describe( 'getInlineStyles', () => {
).toEqual( {
backgroundColor: 'black',
borderColor: '#21759b',
- borderRadius: 10,
+ borderRadius: '10px',
borderStyle: 'dotted',
- borderWidth: 3,
+ borderWidth: '1em',
color: 'red',
lineHeight: 1.5,
fontSize: 10,
diff --git a/packages/block-editor/src/style.scss b/packages/block-editor/src/style.scss
index 0d9b9b01cbde18..92fc673e65ccf5 100644
--- a/packages/block-editor/src/style.scss
+++ b/packages/block-editor/src/style.scss
@@ -58,6 +58,7 @@
@import "./components/warning/style.scss";
@import "./hooks/anchor.scss";
@import "./hooks/layout.scss";
+@import "./hooks/border.scss";
// This tag marks the end of the styles that apply to editing canvas contents and need to be manipulated when we resize the editor.
#end-resizable-editor-section {
diff --git a/packages/block-library/src/button/edit.js b/packages/block-library/src/button/edit.js
index 70ce701138999b..15235369b2ee05 100644
--- a/packages/block-library/src/button/edit.js
+++ b/packages/block-library/src/button/edit.js
@@ -225,9 +225,7 @@ function ButtonEdit( props ) {
}
) }
style={ {
- borderRadius: borderRadius
- ? borderRadius + 'px'
- : undefined,
+ borderRadius: borderRadius ? borderRadius : undefined,
...colorProps.style,
} }
onSplit={ ( value ) =>
diff --git a/packages/block-library/src/button/save.js b/packages/block-library/src/button/save.js
index 695e27e07b5b40..af4f4e6f566f35 100644
--- a/packages/block-library/src/button/save.js
+++ b/packages/block-library/src/button/save.js
@@ -38,7 +38,7 @@ export default function save( { attributes, className } ) {
}
);
const buttonStyle = {
- borderRadius: borderRadius ? borderRadius + 'px' : undefined,
+ borderRadius: borderRadius ? borderRadius : undefined,
...colorProps.style,
};
diff --git a/packages/block-library/src/search/edit.js b/packages/block-library/src/search/edit.js
index ec794a60e44aca..8301ca2c0616be 100644
--- a/packages/block-library/src/search/edit.js
+++ b/packages/block-library/src/search/edit.js
@@ -50,7 +50,7 @@ import {
// Used to calculate border radius adjustment to avoid "fat" corners when
// button is placed inside wrapper.
-const DEFAULT_INNER_PADDING = 4;
+const DEFAULT_INNER_PADDING = '4px';
export default function SearchEdit( {
className,
@@ -323,10 +323,16 @@ export default function SearchEdit( {
if ( 'button-inside' === buttonPosition && style?.border?.radius ) {
// We have button inside wrapper and a border radius value to apply.
// Add default padding so we don't get "fat" corners.
- const outerRadius =
- parseInt( style?.border?.radius, 10 ) + DEFAULT_INNER_PADDING;
+ //
+ // CSS calc() is used here to support non-pixel units. The inline
+ // style using calc() will only apply if both values have units.
+ const radius = Number.isInteger( borderRadius )
+ ? `${ borderRadius }px`
+ : borderRadius;
- return { borderRadius: `${ outerRadius }px` };
+ return {
+ borderRadius: `calc(${ radius } + ${ DEFAULT_INNER_PADDING })`,
+ };
}
return undefined;
diff --git a/packages/block-library/src/search/index.php b/packages/block-library/src/search/index.php
index eba67deeecdce6..faf8741ce7e8bc 100644
--- a/packages/block-library/src/search/index.php
+++ b/packages/block-library/src/search/index.php
@@ -190,7 +190,8 @@ function styles_for_block_core_search( $attributes ) {
if ( $has_border_radius ) {
// Shared style for button and input radius values.
$border_radius = $attributes['style']['border']['radius'];
- $shared_styles[] = sprintf( 'border-radius: %spx;', esc_attr( $border_radius ) );
+ $border_radius = is_numeric( $border_radius ) ? $border_radius . 'px' : $border_radius;
+ $shared_styles[] = sprintf( 'border-radius: %s;', esc_attr( $border_radius ) );
// Apply wrapper border radius if button placed inside.
$button_inside = ! empty( $attributes['buttonPosition'] ) &&
@@ -199,10 +200,13 @@ function styles_for_block_core_search( $attributes ) {
if ( $button_inside ) {
// We adjust the border radius value for the outer wrapper element
// to make it visually consistent with the radius applied to inner
- // elements.
- $default_padding = 4;
- $adjusted_radius = $border_radius + $default_padding;
- $wrapper_styles[] = sprintf( 'border-radius: %dpx;', esc_attr( $adjusted_radius ) );
+ // elements. calc() is used to support non-pixel CSS units.
+ $default_padding = '4px';
+ $wrapper_styles[] = sprintf(
+ 'border-radius: calc(%s + %s);',
+ esc_attr( $border_radius ),
+ esc_attr( $default_padding )
+ );
}
}