diff --git a/lib/block-supports/typography.php b/lib/block-supports/typography.php
index fed915d6005075..ed63b56b6a00a2 100644
--- a/lib/block-supports/typography.php
+++ b/lib/block-supports/typography.php
@@ -21,13 +21,15 @@ function gutenberg_register_typography_support( $block_type ) {
$has_line_height_support = _wp_array_get( $block_type->supports, array( 'lineHeight' ), false );
$has_text_decoration_support = _wp_array_get( $block_type->supports, array( '__experimentalTextDecoration' ), false );
$has_text_transform_support = _wp_array_get( $block_type->supports, array( '__experimentalTextTransform' ), false );
+ $has_letter_spacing_support = _wp_array_get( $block_type->supports, array( '__experimentalLetterSpacing' ), false );
$has_typography_support = $has_font_size_support
|| $has_font_weight_support
|| $has_font_style_support
|| $has_line_height_support
|| $has_text_transform_support
- || $has_text_decoration_support;
+ || $has_text_decoration_support
+ || $has_letter_spacing_support;
if ( ! $block_type->attributes ) {
$block_type->attributes = array();
@@ -72,6 +74,7 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) {
$has_line_height_support = _wp_array_get( $block_type->supports, array( 'lineHeight' ), false );
$has_text_decoration_support = _wp_array_get( $block_type->supports, array( '__experimentalTextDecoration' ), false );
$has_text_transform_support = _wp_array_get( $block_type->supports, array( '__experimentalTextTransform' ), false );
+ $has_letter_spacing_support = _wp_array_get( $block_type->supports, array( '__experimentalLetterSpacing' ), false );
$skip_font_size_support_serialization = _wp_array_get( $block_type->supports, array( '__experimentalSkipFontSizeSerialization' ), false );
@@ -151,6 +154,13 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) {
}
}
+ if ( $has_letter_spacing_support ) {
+ $letter_spacing_style = gutenberg_typography_get_css_variable_inline_style( $block_attributes, 'letterSpacing', 'letter-spacing' );
+ if ( $letter_spacing_style ) {
+ $styles[] = $letter_spacing_style;
+ }
+ }
+
if ( ! empty( $classes ) ) {
$attributes['class'] = implode( ' ', $classes );
}
diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php
index f883bbe2e128eb..f0544ed03dba1d 100644
--- a/lib/class-wp-theme-json-gutenberg.php
+++ b/lib/class-wp-theme-json-gutenberg.php
@@ -73,6 +73,7 @@ class WP_Theme_JSON_Gutenberg {
'fontSize' => null,
'fontStyle' => null,
'fontWeight' => null,
+ 'letterSpacing' => null,
'lineHeight' => null,
'textDecoration' => null,
'textTransform' => null,
@@ -105,6 +106,7 @@ class WP_Theme_JSON_Gutenberg {
'customFontSize' => null,
'customFontStyle' => null,
'customFontWeight' => null,
+ 'customLetterSpacing' => null,
'customLineHeight' => null,
'customTextDecorations' => null,
'customTextTransforms' => null,
@@ -238,6 +240,9 @@ class WP_Theme_JSON_Gutenberg {
'font-weight' => array(
'value' => array( 'typography', 'fontWeight' ),
),
+ 'letter-spacing' => array(
+ 'value' => array( 'typography', 'letterSpacing' ),
+ ),
'line-height' => array(
'value' => array( 'typography', 'lineHeight' ),
),
diff --git a/lib/experimental-default-theme.json b/lib/experimental-default-theme.json
index dc4dbbaf058529..7297493491c99a 100644
--- a/lib/experimental-default-theme.json
+++ b/lib/experimental-default-theme.json
@@ -204,6 +204,7 @@
"customFontWeight": true,
"customTextTransforms": true,
"customTextDecorations": true,
+ "customLetterSpacing": true,
"fontSizes": [
{
"name": "Small",
diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js
index 51d9ba8c1a0dbe..7f1316dceeec04 100644
--- a/packages/block-editor/src/components/index.js
+++ b/packages/block-editor/src/components/index.js
@@ -44,6 +44,7 @@ export { default as __experimentalGradientPickerControl } from './gradient-picke
export { default as __experimentalGradientPickerPanel } from './gradient-picker/panel';
export { default as __experimentalFontAppearanceControl } from './font-appearance-control';
export { default as __experimentalFontFamilyControl } from './font-family';
+export { default as __experimentalLetterSpacingControl } from './letter-spacing-control';
export { default as __experimentalColorGradientControl } from './colors-gradients/control';
export { default as __experimentalPanelColorGradientSettings } from './colors-gradients/panel-color-gradient-settings';
export { default as __experimentalImageSizeControl } from './image-size-control';
diff --git a/packages/block-editor/src/components/letter-spacing-control/index.js b/packages/block-editor/src/components/letter-spacing-control/index.js
new file mode 100644
index 00000000000000..4aba5958c9f4a9
--- /dev/null
+++ b/packages/block-editor/src/components/letter-spacing-control/index.js
@@ -0,0 +1,46 @@
+/**
+ * WordPress dependencies
+ */
+import { __experimentalUnitControl as UnitControl } from '@wordpress/components';
+import { Platform } from '@wordpress/element';
+import { __ } from '@wordpress/i18n';
+
+const isWeb = Platform.OS === 'web';
+
+const CSS_UNITS = [
+ {
+ value: 'px',
+ label: isWeb ? 'px' : __( 'Pixels (px)' ),
+ default: '2',
+ },
+ {
+ value: 'em',
+ label: isWeb ? 'em' : __( 'Relative to parent font size (em)' ),
+ default: '.2',
+ },
+ {
+ value: 'rem',
+ label: isWeb ? 'rem' : __( 'Relative to root font size (rem)' ),
+ default: '.2',
+ },
+];
+
+/**
+ * Control for letter-spacing.
+ *
+ * @param {Object} props Component props.
+ * @param {string} props.value Currently selected letter-spacing.
+ * @param {Function} props.onChange Handles change in letter-spacing selection.
+ * @return {WPElement} Letter-spacing control.
+ */
+export default function LetterSpacingControl( { value, onChange } ) {
+ return (
+
+ );
+}
diff --git a/packages/block-editor/src/hooks/letter-spacing.js b/packages/block-editor/src/hooks/letter-spacing.js
new file mode 100644
index 00000000000000..0a70474c47803b
--- /dev/null
+++ b/packages/block-editor/src/hooks/letter-spacing.js
@@ -0,0 +1,71 @@
+/**
+ * WordPress dependencies
+ */
+import { hasBlockSupport } from '@wordpress/blocks';
+
+/**
+ * Internal dependencies
+ */
+import LetterSpacingControl from '../components/letter-spacing-control';
+import useSetting from '../components/use-setting';
+import { cleanEmptyObject } from './utils';
+
+/**
+ * Key within block settings' supports array indicating support for letter-spacing
+ * e.g. settings found in `block.json`.
+ */
+export const LETTER_SPACING_SUPPORT_KEY = '__experimentalLetterSpacing';
+
+/**
+ * Inspector control panel containing the letter-spacing options.
+ *
+ * @param {Object} props Block properties.
+ * @return {WPElement} Letter-spacing edit element.
+ */
+export function LetterSpacingEdit( props ) {
+ const {
+ attributes: { style },
+ setAttributes,
+ } = props;
+
+ const isDisabled = useIsLetterSpacingDisabled( props );
+
+ if ( isDisabled ) {
+ return null;
+ }
+
+ function onChange( newSpacing ) {
+ setAttributes( {
+ style: cleanEmptyObject( {
+ ...style,
+ typography: {
+ ...style?.typography,
+ letterSpacing: newSpacing,
+ },
+ } ),
+ } );
+ }
+
+ return (
+
+ );
+}
+
+/**
+ * Checks if letter-spacing settings have been disabled.
+ *
+ * @param {string} name Name of the block.
+ * @return {boolean} Whether or not the setting is disabled.
+ */
+export function useIsLetterSpacingDisabled( { name: blockName } = {} ) {
+ const notSupported = ! hasBlockSupport(
+ blockName,
+ LETTER_SPACING_SUPPORT_KEY
+ );
+ const hasLetterSpacing = useSetting( 'typography.customLetterSpacing' );
+
+ return notSupported || ! hasLetterSpacing;
+}
diff --git a/packages/block-editor/src/hooks/typography.js b/packages/block-editor/src/hooks/typography.js
index 5edc061a0220f4..92186f5e876219 100644
--- a/packages/block-editor/src/hooks/typography.js
+++ b/packages/block-editor/src/hooks/typography.js
@@ -47,6 +47,11 @@ import {
TEXT_TRANSFORM_SUPPORT_KEY,
useIsTextTransformDisabled,
} from './text-transform';
+import {
+ LETTER_SPACING_SUPPORT_KEY,
+ LetterSpacingEdit,
+ useIsLetterSpacingDisabled,
+} from './letter-spacing';
export const TYPOGRAPHY_SUPPORT_KEYS = [
LINE_HEIGHT_SUPPORT_KEY,
@@ -56,6 +61,7 @@ export const TYPOGRAPHY_SUPPORT_KEYS = [
FONT_FAMILY_SUPPORT_KEY,
TEXT_DECORATION_SUPPORT_KEY,
TEXT_TRANSFORM_SUPPORT_KEY,
+ LETTER_SPACING_SUPPORT_KEY,
];
export function TypographyPanel( props ) {
@@ -75,6 +81,7 @@ export function TypographyPanel( props ) {
+
@@ -98,6 +105,7 @@ function useIsTypographyDisabled( props = {} ) {
useIsFontFamilyDisabled( props ),
useIsTextDecorationDisabled( props ),
useIsTextTransformDisabled( props ),
+ useIsLetterSpacingDisabled( props ),
];
return configs.filter( Boolean ).length === configs.length;
diff --git a/packages/block-library/src/site-tagline/block.json b/packages/block-library/src/site-tagline/block.json
index d85e68f3e1d0b6..2e5689f5994b35 100644
--- a/packages/block-library/src/site-tagline/block.json
+++ b/packages/block-library/src/site-tagline/block.json
@@ -23,6 +23,7 @@
"fontSize": true,
"lineHeight": true,
"__experimentalFontFamily": true,
- "__experimentalTextTransform": true
+ "__experimentalTextTransform": true,
+ "__experimentalLetterSpacing": true
}
}
diff --git a/packages/block-library/src/site-title/block.json b/packages/block-library/src/site-title/block.json
index 26ee7c601f107b..e84e348cbe9190 100644
--- a/packages/block-library/src/site-title/block.json
+++ b/packages/block-library/src/site-title/block.json
@@ -31,6 +31,7 @@
"__experimentalFontFamily": true,
"__experimentalTextTransform": true,
"__experimentalFontStyle": true,
- "__experimentalFontWeight": true
+ "__experimentalFontWeight": true,
+ "__experimentalLetterSpacing": true
}
}
diff --git a/packages/blocks/src/api/constants.js b/packages/blocks/src/api/constants.js
index 33c017933ad76c..b07aa5a174565d 100644
--- a/packages/blocks/src/api/constants.js
+++ b/packages/blocks/src/api/constants.js
@@ -88,6 +88,10 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = {
value: [ 'typography', 'textTransform' ],
support: [ '__experimentalTextTransform' ],
},
+ letterSpacing: {
+ value: [ 'typography', 'letterSpacing' ],
+ support: [ '__experimentalLetterSpacing' ],
+ },
};
export const __EXPERIMENTAL_ELEMENTS = {
diff --git a/packages/edit-site/src/components/sidebar/typography-panel.js b/packages/edit-site/src/components/sidebar/typography-panel.js
index 9752af7d2a90c3..8e14eb08e0b4dc 100644
--- a/packages/edit-site/src/components/sidebar/typography-panel.js
+++ b/packages/edit-site/src/components/sidebar/typography-panel.js
@@ -5,6 +5,7 @@ import {
LineHeightControl,
__experimentalFontFamilyControl as FontFamilyControl,
__experimentalFontAppearanceControl as FontAppearanceControl,
+ __experimentalLetterSpacingControl as LetterSpacingControl,
} from '@wordpress/block-editor';
import { PanelBody, FontSizePicker } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
@@ -17,8 +18,12 @@ import { useSetting } from '../editor/utils';
export function useHasTypographyPanel( { supports, name } ) {
const hasLineHeight = useHasLineHeightControl( { supports, name } );
const hasFontAppearance = useHasAppearanceControl( { supports, name } );
+ const hasLetterSpacing = useHasLetterSpacingControl( { supports, name } );
return (
- hasLineHeight || hasFontAppearance || supports.includes( 'fontSize' )
+ hasLineHeight ||
+ hasFontAppearance ||
+ hasLetterSpacing ||
+ supports.includes( 'fontSize' )
);
}
@@ -39,6 +44,13 @@ function useHasAppearanceControl( { supports, name } ) {
return hasFontStyles || hasFontWeights;
}
+function useHasLetterSpacingControl( { supports, name } ) {
+ return (
+ useSetting( 'typography.customLetterSpacing', name ) &&
+ supports.includes( 'letterSpacing' )
+ );
+}
+
export default function TypographyPanel( {
context: { supports, name },
getStyle,
@@ -58,6 +70,10 @@ export default function TypographyPanel( {
supports.includes( 'fontWeight' );
const hasLineHeightEnabled = useHasLineHeightControl( { supports, name } );
const hasAppearanceControl = useHasAppearanceControl( { supports, name } );
+ const hasLetterSpacingControl = useHasLetterSpacingControl( {
+ supports,
+ name,
+ } );
return (
@@ -102,6 +118,14 @@ export default function TypographyPanel( {
hasFontWeights={ hasFontWeights }
/>
) }
+ { hasLetterSpacingControl && (
+
+ setStyle( name, 'letterSpacing', value )
+ }
+ />
+ ) }
);
}