diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md
index 1bac37d554d1e7..6ad2bd1f22570c 100644
--- a/docs/reference-guides/core-blocks.md
+++ b/docs/reference-guides/core-blocks.md
@@ -326,6 +326,15 @@ Add a link to a downloadable file. ([Source](https://github.com/WordPress/gutenb
- **Supports:** align, anchor, color (background, gradients, link, ~~text~~), interactivity, spacing (margin, padding)
- **Attributes:** blob, displayPreview, downloadButtonText, fileId, fileName, href, id, previewHeight, showDownloadButton, textLinkHref, textLinkTarget
+## Fit Text
+
+Add text that automatically scales to fit its container. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/fit-text))
+
+- **Name:** core/fit-text
+- **Category:** text
+- **Supports:** __unstablePasteTextInline, align (full, wide), anchor, className, color (background, gradients, text), interactivity (clientNavigation), spacing (margin, padding), typography (lineHeight)
+- **Attributes:** content, level, levelOptions
+
## Footnotes
Display footnotes added to the page. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/footnotes))
@@ -410,7 +419,7 @@ Introduce new sections and organize content to help visitors (and search engines
- **Name:** core/heading
- **Category:** text
-- **Supports:** __unstablePasteTextInline, align (full, wide), anchor, className, color (background, gradients, link, text), interactivity (clientNavigation), spacing (margin, padding), splitting, typography (fitText, fontSize, lineHeight)
+- **Supports:** __unstablePasteTextInline, align (full, wide), anchor, className, color (background, gradients, link, text), interactivity (clientNavigation), spacing (margin, padding), splitting, typography (fontSize, lineHeight)
- **Attributes:** content, level, levelOptions, placeholder, textAlign
## Home Link
@@ -590,7 +599,7 @@ Start with the basic building block of all narrative. ([Source](https://github.c
- **Name:** core/paragraph
- **Category:** text
-- **Supports:** __unstablePasteTextInline, anchor, color (background, gradients, link, text), interactivity (clientNavigation), spacing (margin, padding), splitting, typography (fitText, fontSize, lineHeight), ~~className~~
+- **Supports:** __unstablePasteTextInline, anchor, color (background, gradients, link, text), interactivity (clientNavigation), spacing (margin, padding), splitting, typography (fontSize, lineHeight), ~~className~~
- **Attributes:** align, content, direction, dropCap, placeholder
## Pattern Placeholder
diff --git a/lib/block-supports/typography.php b/lib/block-supports/typography.php
index 8fdf11793511b7..a4719b7bdd4099 100644
--- a/lib/block-supports/typography.php
+++ b/lib/block-supports/typography.php
@@ -244,24 +244,6 @@ function gutenberg_typography_get_preset_inline_style_value( $style_value, $css_
* @return string Filtered block content.
*/
function gutenberg_render_typography_support( $block_content, $block ) {
- if ( ! empty( $block['attrs']['fitText'] ) && ! is_admin() ) {
- wp_enqueue_script_module( '@wordpress/block-editor/utils/fit-text-frontend' );
-
- // Add Interactivity API directives for fit text to work with client-side navigation.
- if ( ! empty( $block_content ) ) {
- $processor = new WP_HTML_Tag_Processor( $block_content );
- if ( $processor->next_tag() ) {
- if ( ! $processor->get_attribute( 'data-wp-interactive' ) ) {
- $processor->set_attribute( 'data-wp-interactive', true );
- }
- $processor->set_attribute( 'data-wp-context---core-fit-text', 'core/fit-text::{"fontSize":""}' );
- $processor->set_attribute( 'data-wp-init---core-fit-text', 'core/fit-text::callbacks.init' );
- $processor->set_attribute( 'data-wp-style--font-size', 'core/fit-text::context.fontSize' );
- $block_content = $processor->get_updated_html();
- }
- }
- }
-
if ( ! isset( $block['attrs']['style']['typography']['fontSize'] ) ) {
return $block_content;
}
diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json
index a1c399315f60d1..52b816e9ba3d7d 100644
--- a/packages/block-editor/package.json
+++ b/packages/block-editor/package.json
@@ -35,9 +35,6 @@
},
"react-native": "src/index",
"wpScript": true,
- "wpScriptModuleExports": {
- "./utils/fit-text-frontend": "./build-module/utils/fit-text-frontend.js"
- },
"sideEffects": [
"build-style/**",
"src/**/*.scss",
diff --git a/packages/block-editor/src/hooks/fit-text.js b/packages/block-editor/src/hooks/fit-text.js
deleted file mode 100644
index 356452035064ae..00000000000000
--- a/packages/block-editor/src/hooks/fit-text.js
+++ /dev/null
@@ -1,329 +0,0 @@
-/**
- * WordPress dependencies
- */
-import { addFilter } from '@wordpress/hooks';
-import { hasBlockSupport } from '@wordpress/blocks';
-import { useEffect, useCallback } from '@wordpress/element';
-import { useSelect } from '@wordpress/data';
-import { __ } from '@wordpress/i18n';
-import {
- ToggleControl,
- __experimentalToolsPanelItem as ToolsPanelItem,
-} from '@wordpress/components';
-
-/**
- * Internal dependencies
- */
-import { optimizeFitText } from '../utils/fit-text-utils';
-import { store as blockEditorStore } from '../store';
-import { useBlockElement } from '../components/block-list/use-block-props/use-block-refs';
-import InspectorControls from '../components/inspector-controls';
-
-export const FIT_TEXT_SUPPORT_KEY = 'typography.fitText';
-
-/**
- * Filters registered block settings, extending attributes to include
- * the `fitText` attribute.
- *
- * @param {Object} settings Original block settings.
- * @return {Object} Filtered block settings.
- */
-function addAttributes( settings ) {
- if ( ! hasBlockSupport( settings, FIT_TEXT_SUPPORT_KEY ) ) {
- return settings;
- }
-
- // Allow blocks to specify their own attribute definition.
- if ( settings.attributes?.fitText ) {
- return settings;
- }
-
- // Add fitText attribute.
- return {
- ...settings,
- attributes: {
- ...settings.attributes,
- fitText: {
- type: 'boolean',
- },
- },
- };
-}
-
-/**
- * Custom hook to handle fit text functionality in the editor.
- *
- * @param {Object} props Component props.
- * @param {?boolean} props.fitText Fit text attribute.
- * @param {string} props.name Block name.
- * @param {string} props.clientId Block client ID.
- */
-function useFitText( { fitText, name, clientId } ) {
- const hasFitTextSupport = hasBlockSupport( name, FIT_TEXT_SUPPORT_KEY );
- const blockElement = useBlockElement( clientId );
-
- // Monitor block attribute changes
- // Any attribute may change the available space.
- const blockAttributes = useSelect(
- ( select ) => {
- if ( ! clientId || ! hasFitTextSupport || ! fitText ) {
- return;
- }
- return select( blockEditorStore ).getBlockAttributes( clientId );
- },
- [ clientId, hasFitTextSupport, fitText ]
- );
-
- const applyFitText = useCallback( () => {
- if ( ! blockElement || ! hasFitTextSupport || ! fitText ) {
- return;
- }
-
- // Get or create style element with unique ID
- const styleId = `fit-text-${ clientId }`;
- let styleElement = blockElement.ownerDocument.getElementById( styleId );
- if ( ! styleElement ) {
- styleElement = blockElement.ownerDocument.createElement( 'style' );
- styleElement.id = styleId;
- blockElement.ownerDocument.head.appendChild( styleElement );
- }
-
- const blockSelector = `#block-${ clientId }`;
-
- const applyFontSize = ( fontSize ) => {
- if ( fontSize === 0 ) {
- styleElement.textContent = '';
- } else {
- styleElement.textContent = `${ blockSelector } { font-size: ${ fontSize }px !important; }`;
- }
- };
-
- optimizeFitText( blockElement, applyFontSize );
- }, [ blockElement, clientId, hasFitTextSupport, fitText ] );
-
- useEffect( () => {
- if (
- ! fitText ||
- ! blockElement ||
- ! clientId ||
- ! hasFitTextSupport
- ) {
- return;
- }
-
- // Store current element value for cleanup
- const currentElement = blockElement;
- const previousVisibility = currentElement.style.visibility;
-
- // Store IDs for cleanup
- let hideFrameId = null;
- let calculateFrameId = null;
- let showTimeoutId = null;
-
- // We are hiding the element doing the calculation of fit text
- // and then showing it again to avoid the user noticing a flash of potentially
- // big fitText while the binary search is happening.
- hideFrameId = window.requestAnimationFrame( () => {
- currentElement.style.visibility = 'hidden';
- // Wait for browser to render the hidden state
- calculateFrameId = window.requestAnimationFrame( () => {
- applyFitText();
-
- // Using a timeout instead of requestAnimationFrame, because
- // with requestAnimationFrame a flash of very high size
- // can still occur although rare.
- showTimeoutId = setTimeout( () => {
- currentElement.style.visibility = previousVisibility;
- }, 10 );
- } );
- } );
-
- // Watch for size changes
- let resizeObserver;
- if ( window.ResizeObserver && currentElement.parentElement ) {
- resizeObserver = new window.ResizeObserver( applyFitText );
- resizeObserver.observe( currentElement.parentElement );
- }
-
- // Cleanup function
- return () => {
- // Cancel pending async operations
- if ( hideFrameId !== null ) {
- window.cancelAnimationFrame( hideFrameId );
- }
- if ( calculateFrameId !== null ) {
- window.cancelAnimationFrame( calculateFrameId );
- }
- if ( showTimeoutId !== null ) {
- clearTimeout( showTimeoutId );
- }
-
- if ( resizeObserver ) {
- resizeObserver.disconnect();
- }
-
- const styleId = `fit-text-${ clientId }`;
- const styleElement =
- currentElement.ownerDocument.getElementById( styleId );
- if ( styleElement ) {
- styleElement.remove();
- }
- };
- }, [ fitText, clientId, applyFitText, blockElement, hasFitTextSupport ] );
-
- // Trigger fit text recalculation when content changes
- useEffect( () => {
- if ( fitText && blockElement && hasFitTextSupport ) {
- // Wait for next frame to ensure DOM has updated after content changes
- const frameId = window.requestAnimationFrame( () => {
- if ( blockElement ) {
- applyFitText();
- }
- } );
-
- return () => window.cancelAnimationFrame( frameId );
- }
- }, [
- blockAttributes,
- fitText,
- applyFitText,
- blockElement,
- hasFitTextSupport,
- ] );
-}
-
-/**
- * Fit text control component for the typography panel.
- *
- * @param {Object} props Component props.
- * @param {string} props.clientId Block client ID.
- * @param {Function} props.setAttributes Function to set block attributes.
- * @param {string} props.name Block name.
- * @param {boolean} props.fitText Whether fit text is enabled.
- * @param {string} props.fontSize Font size slug.
- * @param {Object} props.style Block style object.
- */
-export function FitTextControl( {
- clientId,
- fitText = false,
- setAttributes,
- name,
- fontSize,
- style,
-} ) {
- if ( ! hasBlockSupport( name, FIT_TEXT_SUPPORT_KEY ) ) {
- return null;
- }
- return (
-
- fitText }
- label={ __( 'Fit text' ) }
- onDeselect={ () => setAttributes( { fitText: undefined } ) }
- resetAllFilter={ () => ( { fitText: undefined } ) }
- panelId={ clientId }
- >
- {
- const newFitText = ! fitText || undefined;
- const updates = { fitText: newFitText };
-
- // When enabling fit text, clear font size if it has a value
- if ( newFitText ) {
- if ( fontSize ) {
- updates.fontSize = undefined;
- }
- if ( style?.typography?.fontSize ) {
- updates.style = {
- ...style,
- typography: {
- ...style?.typography,
- fontSize: undefined,
- },
- };
- }
- }
-
- setAttributes( updates );
- } }
- help={
- fitText
- ? __( 'Text will resize to fit its container.' )
- : __(
- 'The text will resize to fit its container, resetting other font size settings.'
- )
- }
- />
-
-
- );
-}
-
-/**
- * Override props applied to the block element on save.
- *
- * @param {Object} props Additional props applied to the block element.
- * @param {Object} blockType Block type.
- * @param {Object} attributes Block attributes.
- * @return {Object} Filtered props applied to the block element.
- */
-function addSaveProps( props, blockType, attributes ) {
- if ( ! hasBlockSupport( blockType, FIT_TEXT_SUPPORT_KEY ) ) {
- return props;
- }
-
- const { fitText } = attributes;
-
- if ( ! fitText ) {
- return props;
- }
-
- // Add CSS class for frontend detection and styling
- const className = props.className
- ? `${ props.className } has-fit-text`
- : 'has-fit-text';
-
- return {
- ...props,
- className,
- };
-}
-/**
- * Override props applied to the block element in the editor.
- *
- * @param {Object} props Component props including block attributes.
- * @param {string} props.name Block name.
- * @param {boolean} props.fitText Whether fit text is enabled.
- * @param {string} props.clientId Block client ID.
- * @return {Object} Filtered props applied to the block element.
- */
-function useBlockProps( { name, fitText, clientId } ) {
- useFitText( { fitText, name, clientId } );
- if ( ! fitText || ! hasBlockSupport( name, FIT_TEXT_SUPPORT_KEY ) ) {
- return {};
- }
- return {
- className: 'has-fit-text',
- };
-}
-
-addFilter(
- 'blocks.registerBlockType',
- 'core/fit-text/addAttribute',
- addAttributes
-);
-
-const hasFitTextSupport = ( blockNameOrType ) => {
- return hasBlockSupport( blockNameOrType, FIT_TEXT_SUPPORT_KEY );
-};
-
-export default {
- useBlockProps,
- addSaveProps,
- attributeKeys: [ 'fitText', 'fontSize', 'style' ],
- hasSupport: hasFitTextSupport,
- edit: FitTextControl,
-};
diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js
index cc8339455fdd84..95c18e6a3f55a7 100644
--- a/packages/block-editor/src/hooks/index.js
+++ b/packages/block-editor/src/hooks/index.js
@@ -23,7 +23,6 @@ import duotone from './duotone';
import fontFamily from './font-family';
import fontSize from './font-size';
import textAlign from './text-align';
-import fitText from './fit-text';
import border from './border';
import position from './position';
import blockStyleVariation from './block-style-variation';
@@ -44,7 +43,6 @@ createBlockEditFilter(
customClassName,
style,
duotone,
- fitText,
position,
layout,
contentLockUI,
@@ -64,7 +62,6 @@ createBlockListBlockFilter( [
duotone,
fontFamily,
fontSize,
- fitText,
border,
position,
blockStyleVariation,
@@ -77,7 +74,6 @@ createBlockSaveFilter( [
ariaLabel,
customClassName,
border,
- fitText,
color,
style,
fontFamily,
diff --git a/packages/block-editor/src/hooks/typography.js b/packages/block-editor/src/hooks/typography.js
index 5c551a4bb35761..1f381a3d0bec14 100644
--- a/packages/block-editor/src/hooks/typography.js
+++ b/packages/block-editor/src/hooks/typography.js
@@ -18,7 +18,6 @@ import { LINE_HEIGHT_SUPPORT_KEY } from './line-height';
import { FONT_FAMILY_SUPPORT_KEY } from './font-family';
import { FONT_SIZE_SUPPORT_KEY } from './font-size';
import { TEXT_ALIGN_SUPPORT_KEY } from './text-align';
-import { FIT_TEXT_SUPPORT_KEY } from './fit-text';
import { cleanEmptyObject } from './utils';
import { store as blockEditorStore } from '../store';
@@ -48,7 +47,6 @@ export const TYPOGRAPHY_SUPPORT_KEYS = [
WRITING_MODE_SUPPORT_KEY,
TEXT_TRANSFORM_SUPPORT_KEY,
LETTER_SPACING_SUPPORT_KEY,
- FIT_TEXT_SUPPORT_KEY,
];
function styleToAttributes( style ) {
@@ -116,13 +114,11 @@ function TypographyInspectorControl( { children, resetAllFilter } ) {
export function TypographyPanel( { clientId, name, setAttributes, settings } ) {
function selector( select ) {
- const { style, fontFamily, fontSize, fitText } =
+ const { style, fontFamily, fontSize } =
select( blockEditorStore ).getBlockAttributes( clientId ) || {};
- return { style, fontFamily, fontSize, fitText };
+ return { style, fontFamily, fontSize };
}
- const { style, fontFamily, fontSize, fitText } = useSelect( selector, [
- clientId,
- ] );
+ const { style, fontFamily, fontSize } = useSelect( selector, [ clientId ] );
const isEnabled = useHasTypographyPanel( settings );
const value = useMemo(
() => attributesToStyle( { style, fontFamily, fontSize } ),
@@ -131,14 +127,6 @@ export function TypographyPanel( { clientId, name, setAttributes, settings } ) {
const onChange = ( newStyle ) => {
const newAttributes = styleToAttributes( newStyle );
-
- // If setting a font size and fitText is currently enabled, disable it
- const hasFontSize =
- newAttributes.fontSize || newAttributes.style?.typography?.fontSize;
- if ( hasFontSize && fitText ) {
- newAttributes.fitText = undefined;
- }
-
setAttributes( newAttributes );
};
diff --git a/packages/block-library/package.json b/packages/block-library/package.json
index f329bbd91da6ab..4a714df91f7c26 100644
--- a/packages/block-library/package.json
+++ b/packages/block-library/package.json
@@ -39,6 +39,7 @@
"wpScriptModuleExports": {
"./accordion/view": "./build-module/accordion/view.js",
"./file/view": "./build-module/file/view.js",
+ "./fit-text/view": "./build-module/fit-text/view.js",
"./form/view": "./build-module/form/view.js",
"./image/view": "./build-module/image/view.js",
"./navigation/view": "./build-module/navigation/view.js",
diff --git a/packages/block-library/src/common.scss b/packages/block-library/src/common.scss
index 8bb0d00460f4a5..63d2337928c69c 100644
--- a/packages/block-library/src/common.scss
+++ b/packages/block-library/src/common.scss
@@ -49,11 +49,6 @@
text-align: right;
}
-// Fit Text
-.has-fit-text {
- white-space: nowrap !important;
-}
-
// 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 {
display: none;
diff --git a/packages/block-library/src/fit-text/block.json b/packages/block-library/src/fit-text/block.json
new file mode 100644
index 00000000000000..ca45e6c4252cdf
--- /dev/null
+++ b/packages/block-library/src/fit-text/block.json
@@ -0,0 +1,69 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 3,
+ "name": "core/fit-text",
+ "title": "Fit Text",
+ "category": "text",
+ "description": "Add text that automatically scales to fit its container.",
+ "keywords": [ "text", "resize", "scale" ],
+ "textdomain": "default",
+ "attributes": {
+ "content": {
+ "type": "rich-text",
+ "source": "rich-text",
+ "selector": "h1,h2,h3,h4,h5,h6,p",
+ "role": "content"
+ },
+ "level": {
+ "type": "number",
+ "default": 0
+ },
+ "levelOptions": {
+ "type": "array",
+ "default": [ 0, 1, 2, 3, 4, 5, 6 ]
+ }
+ },
+ "supports": {
+ "align": [ "wide", "full" ],
+ "anchor": true,
+ "className": true,
+ "__experimentalBorder": {
+ "color": true,
+ "radius": true,
+ "style": true,
+ "width": true
+ },
+ "color": {
+ "gradients": true,
+ "__experimentalDefaultControls": {
+ "background": true,
+ "text": true
+ }
+ },
+ "spacing": {
+ "margin": true,
+ "padding": true,
+ "__experimentalDefaultControls": {
+ "margin": false,
+ "padding": false
+ }
+ },
+ "typography": {
+ "lineHeight": true,
+ "__experimentalFontFamily": true,
+ "__experimentalFontStyle": true,
+ "__experimentalFontWeight": true,
+ "__experimentalLetterSpacing": true,
+ "__experimentalTextTransform": true,
+ "__experimentalTextDecoration": true,
+ "__experimentalDefaultControls": {}
+ },
+ "interactivity": {
+ "clientNavigation": true
+ },
+ "__unstablePasteTextInline": true,
+ "__experimentalSlashInserter": true
+ },
+ "editorStyle": "wp-block-fit-text-editor",
+ "style": "wp-block-fit-text"
+}
diff --git a/packages/block-library/src/fit-text/edit.js b/packages/block-library/src/fit-text/edit.js
new file mode 100644
index 00000000000000..dca9a12406e550
--- /dev/null
+++ b/packages/block-library/src/fit-text/edit.js
@@ -0,0 +1,164 @@
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import {
+ useEffect,
+ useLayoutEffect,
+ useCallback,
+ useRef,
+} from '@wordpress/element';
+import {
+ RichText,
+ BlockControls,
+ useBlockProps,
+ HeadingLevelDropdown,
+ useBlockEditingMode,
+} from '@wordpress/block-editor';
+import { createBlock, getDefaultBlockName } from '@wordpress/blocks';
+
+/**
+ * Internal dependencies
+ */
+import { optimizeFitText } from './utils';
+
+export default function FitTextEdit( {
+ attributes,
+ setAttributes,
+ insertBlocksAfter,
+ clientId,
+} ) {
+ const { level, levelOptions, content } = attributes;
+ const blockEditingMode = useBlockEditingMode();
+ const blockRef = useRef();
+
+ const applyFitText = useCallback( () => {
+ if ( ! blockRef.current ) {
+ return;
+ }
+
+ // Get or create style element with unique ID
+ const styleId = `fit-text-${ clientId }`;
+ let styleElement =
+ blockRef.current.ownerDocument.getElementById( styleId );
+ if ( ! styleElement ) {
+ styleElement =
+ blockRef.current.ownerDocument.createElement( 'style' );
+ styleElement.id = styleId;
+ blockRef.current.ownerDocument.head.appendChild( styleElement );
+ }
+
+ const blockSelector = `#block-${ clientId }`;
+
+ const applyFontSize = ( fontSize ) => {
+ if ( fontSize === 0 ) {
+ styleElement.textContent = '';
+ } else {
+ styleElement.textContent = `${ blockSelector } { font-size: ${ fontSize }px !important; }`;
+ }
+ };
+
+ optimizeFitText( blockRef.current, applyFontSize );
+ }, [ clientId ] );
+
+ useEffect( () => {
+ if ( ! blockRef.current || ! clientId ) {
+ return;
+ }
+
+ let calculateFrameId = null;
+
+ // Wait for next animation frame for DOM to fully render and layout to settle
+ calculateFrameId = window.requestAnimationFrame( () => {
+ applyFitText();
+ } );
+
+ // Watch for size changes
+ let resizeObserver;
+ if ( window.ResizeObserver && blockRef.current.parentElement ) {
+ resizeObserver = new window.ResizeObserver( applyFitText );
+ resizeObserver.observe( blockRef.current.parentElement );
+ }
+
+ const blockRefToCleanup = blockRef.current;
+
+ // Cleanup function
+ return () => {
+ if ( calculateFrameId !== null ) {
+ window.cancelAnimationFrame( calculateFrameId );
+ }
+
+ if ( resizeObserver ) {
+ resizeObserver.disconnect();
+ }
+
+ const styleId = `fit-text-${ clientId }`;
+ const styleElement =
+ blockRefToCleanup.ownerDocument.getElementById( styleId );
+ if ( styleElement ) {
+ styleElement.remove();
+ }
+ };
+ }, [ clientId, applyFitText ] );
+
+ // Trigger fit text recalculation when attributes change
+ useLayoutEffect( () => {
+ if ( blockRef.current ) {
+ // Wait for two animation frames for DOM layout to settle.
+ // If we do it in a single frame, because of some reason when changing
+ // alignment from full to wide or non things don't recompute correctly.
+ let firstFrameId = null;
+ let secondFrameId = null;
+
+ firstFrameId = window.requestAnimationFrame( () => {
+ secondFrameId = window.requestAnimationFrame( () => {
+ if ( blockRef.current ) {
+ applyFitText();
+ }
+ } );
+ } );
+
+ return () => {
+ if ( firstFrameId !== null ) {
+ window.cancelAnimationFrame( firstFrameId );
+ }
+ if ( secondFrameId !== null ) {
+ window.cancelAnimationFrame( secondFrameId );
+ }
+ };
+ }
+ }, [ attributes, applyFitText ] );
+
+ const tagName = level === 0 ? 'p' : `h${ level }`;
+ const blockProps = useBlockProps( {
+ ref: blockRef,
+ } );
+
+ return (
+ <>
+ { blockEditingMode === 'default' && (
+
+
+ setAttributes( { level: newLevel } )
+ }
+ />
+
+ ) }
+ setAttributes( { content: value } ) }
+ disableLineBreaks
+ __unstableOnSplitAtEnd={ () =>
+ insertBlocksAfter( createBlock( getDefaultBlockName() ) )
+ }
+ />
+ >
+ );
+}
diff --git a/packages/block-library/src/fit-text/index.js b/packages/block-library/src/fit-text/index.js
new file mode 100644
index 00000000000000..efefd7d4d5aab0
--- /dev/null
+++ b/packages/block-library/src/fit-text/index.js
@@ -0,0 +1,29 @@
+/**
+ * WordPress dependencies
+ */
+import { paragraph as icon } from '@wordpress/icons';
+
+/**
+ * Internal dependencies
+ */
+import initBlock from '../utils/init-block';
+import metadata from './block.json';
+import edit from './edit';
+import save from './save';
+
+const { name } = metadata;
+export { metadata, name };
+
+export const settings = {
+ icon,
+ example: {
+ attributes: {
+ content: 'Fit Text',
+ level: 2,
+ },
+ },
+ edit,
+ save,
+};
+
+export const init = () => initBlock( { name, metadata, settings } );
diff --git a/packages/block-library/src/fit-text/index.php b/packages/block-library/src/fit-text/index.php
new file mode 100644
index 00000000000000..2e6d1c27dcb521
--- /dev/null
+++ b/packages/block-library/src/fit-text/index.php
@@ -0,0 +1,54 @@
+next_tag() ) {
+ if ( ! $processor->get_attribute( 'data-wp-interactive' ) ) {
+ $processor->set_attribute( 'data-wp-interactive', true );
+ }
+ $processor->set_attribute( 'data-wp-context---core-fit-text', 'core/fit-text::{"fontSize":""}' );
+ $processor->set_attribute( 'data-wp-init---core-fit-text', 'core/fit-text::callbacks.init' );
+ $processor->set_attribute( 'data-wp-style--font-size', 'core/fit-text::context.fontSize' );
+ $content = $processor->get_updated_html();
+ }
+
+ return $content;
+}
+
+/**
+ * Registers the `core/fit-text` block on the server.
+ *
+ * @since 22.0.0
+ */
+function register_block_core_fit_text() {
+ register_block_type_from_metadata(
+ __DIR__ . '/fit-text',
+ array(
+ 'render_callback' => 'render_block_core_fit_text',
+ )
+ );
+}
+add_action( 'init', 'register_block_core_fit_text' );
diff --git a/packages/block-library/src/fit-text/init.js b/packages/block-library/src/fit-text/init.js
new file mode 100644
index 00000000000000..79f0492c2cb2f8
--- /dev/null
+++ b/packages/block-library/src/fit-text/init.js
@@ -0,0 +1,6 @@
+/**
+ * Internal dependencies
+ */
+import { init } from './';
+
+export default init();
diff --git a/packages/block-library/src/fit-text/save.js b/packages/block-library/src/fit-text/save.js
new file mode 100644
index 00000000000000..819a28886cc223
--- /dev/null
+++ b/packages/block-library/src/fit-text/save.js
@@ -0,0 +1,15 @@
+/**
+ * WordPress dependencies
+ */
+import { RichText, useBlockProps } from '@wordpress/block-editor';
+
+export default function save( { attributes } ) {
+ const { content, level } = attributes;
+ const TagName = level === 0 ? 'p' : `h${ level }`;
+
+ return (
+
+
+
+ );
+}
diff --git a/packages/block-library/src/fit-text/style.scss b/packages/block-library/src/fit-text/style.scss
new file mode 100644
index 00000000000000..45ae356081f4a3
--- /dev/null
+++ b/packages/block-library/src/fit-text/style.scss
@@ -0,0 +1,3 @@
+.wp-block-fit-text {
+ white-space: nowrap !important;
+}
diff --git a/packages/block-editor/src/utils/fit-text-utils.js b/packages/block-library/src/fit-text/utils.js
similarity index 100%
rename from packages/block-editor/src/utils/fit-text-utils.js
rename to packages/block-library/src/fit-text/utils.js
diff --git a/packages/block-editor/src/utils/fit-text-frontend.js b/packages/block-library/src/fit-text/view.js
similarity index 89%
rename from packages/block-editor/src/utils/fit-text-frontend.js
rename to packages/block-library/src/fit-text/view.js
index f6729e3c2585c4..27f7a674384939 100644
--- a/packages/block-editor/src/utils/fit-text-frontend.js
+++ b/packages/block-library/src/fit-text/view.js
@@ -1,6 +1,6 @@
/**
* Frontend fit text functionality.
- * Automatically detects and initializes fit text on blocks with the has-fit-text class.
+ * Automatically detects and initializes fit text on core/fit-text blocks.
* Supports both initial page load and Interactivity API client-side navigation.
*/
@@ -12,7 +12,7 @@ import { store, getElement, getContext } from '@wordpress/interactivity';
/**
* Internal dependencies
*/
-import { optimizeFitText } from './fit-text-utils';
+import { optimizeFitText } from './utils';
// Initialize via Interactivity API for client-side navigation
store( 'core/fit-text', {
diff --git a/packages/block-library/src/heading/block.json b/packages/block-library/src/heading/block.json
index d9488a3156528d..2869ee85c55206 100644
--- a/packages/block-library/src/heading/block.json
+++ b/packages/block-library/src/heading/block.json
@@ -65,7 +65,6 @@
"__experimentalTextTransform": true,
"__experimentalTextDecoration": true,
"__experimentalWritingMode": true,
- "fitText": true,
"__experimentalDefaultControls": {
"fontSize": true
}
diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js
index e71d6d1b651ac7..151e86333c40b5 100644
--- a/packages/block-library/src/index.js
+++ b/packages/block-library/src/index.js
@@ -59,6 +59,7 @@ import * as cover from './cover';
import * as details from './details';
import * as embed from './embed';
import * as file from './file';
+import * as fitText from './fit-text';
import * as form from './form';
import * as formInput from './form-input';
import * as formSubmitButton from './form-submit-button';
@@ -178,6 +179,7 @@ const getAllBlocks = () => {
details,
embed,
file,
+ fitText,
group,
html,
math,
diff --git a/packages/block-library/src/paragraph/block.json b/packages/block-library/src/paragraph/block.json
index 9617627ef5d0da..7e004019cbf282 100644
--- a/packages/block-library/src/paragraph/block.json
+++ b/packages/block-library/src/paragraph/block.json
@@ -65,7 +65,6 @@
"__experimentalLetterSpacing": true,
"__experimentalTextTransform": true,
"__experimentalWritingMode": true,
- "fitText": true,
"__experimentalDefaultControls": {
"fontSize": true
}
diff --git a/packages/block-library/src/style.scss b/packages/block-library/src/style.scss
index 907c7105467259..13cc1fa427f213 100644
--- a/packages/block-library/src/style.scss
+++ b/packages/block-library/src/style.scss
@@ -23,6 +23,7 @@
@use "./details/style.scss" as *;
@use "./embed/style.scss" as *;
@use "./file/style.scss" as *;
+@use "./fit-text/style.scss" as *;
@use "./form-input/style.scss" as *;
@use "./gallery/style.scss" as *;
@use "./group/style.scss" as *;
diff --git a/test/e2e/specs/editor/blocks/fit-text.spec.js b/test/e2e/specs/editor/blocks/fit-text.spec.js
index c10e065155c4c7..9dc26ac07c52a4 100644
--- a/test/e2e/specs/editor/blocks/fit-text.spec.js
+++ b/test/e2e/specs/editor/blocks/fit-text.spec.js
@@ -9,123 +9,102 @@ test.describe( 'Fit Text', () => {
} );
test.describe( 'Editor functionality', () => {
- test( 'should enable fit text on a heading block', async ( {
- editor,
- page,
- } ) => {
+ test( 'should insert a fit text block', async ( { editor } ) => {
await editor.insertBlock( {
- name: 'core/heading',
+ name: 'core/fit-text',
attributes: {
- content: 'Test Heading',
+ content: 'Test Fit Text',
level: 2,
},
} );
- await editor.openDocumentSettingsSidebar();
-
- // Enable Fit text control via Typography options menu
- await page
- .getByRole( 'region', { name: 'Editor settings' } )
- .getByRole( 'button', { name: 'Typography options' } )
- .click();
- await page
- .getByRole( 'menu', { name: 'Typography options' } )
- .getByRole( 'menuitemcheckbox', { name: 'Show Fit text' } )
- .click();
-
- const fitTextToggle = page.getByRole( 'checkbox', {
- name: 'Fit text',
- } );
-
- await fitTextToggle.click();
-
await expect.poll( editor.getBlocks ).toMatchObject( [
{
- name: 'core/heading',
+ name: 'core/fit-text',
attributes: {
- content: 'Test Heading',
+ content: 'Test Fit Text',
level: 2,
- fitText: true,
},
},
] );
- const headingBlock = editor.canvas.locator(
- '[data-type="core/heading"]'
+ const fitTextBlock = editor.canvas.locator(
+ '[data-type="core/fit-text"]'
);
- await expect( headingBlock ).toHaveClass( /has-fit-text/ );
+ await expect( fitTextBlock ).toBeVisible();
} );
- test( 'should disable fit text when toggled off', async ( {
+ test( 'should allow changing heading level', async ( {
editor,
page,
} ) => {
await editor.insertBlock( {
- name: 'core/heading',
+ name: 'core/fit-text',
attributes: {
- content: 'Test Heading',
+ content: 'Heading Level Test',
level: 2,
- fitText: true,
},
} );
- await editor.openDocumentSettingsSidebar();
+ const fitTextBlock = editor.canvas.locator(
+ '[data-type="core/fit-text"]'
+ );
+ await fitTextBlock.click();
- const fitTextToggle = page.getByRole( 'checkbox', {
- name: 'Fit text',
- } );
+ // Open heading level dropdown
+ await page.getByRole( 'button', { name: 'Change level' } ).click();
- await fitTextToggle.click();
+ // Select H4
+ await page
+ .getByRole( 'menuitemradio', { name: 'Heading 4' } )
+ .click();
- const blocks = await editor.getBlocks();
- expect( blocks[ 0 ].attributes.fitText ).toBeUndefined();
+ await expect.poll( editor.getBlocks ).toMatchObject( [
+ {
+ name: 'core/fit-text',
+ attributes: {
+ content: 'Heading Level Test',
+ level: 4,
+ },
+ },
+ ] );
} );
- test( 'should enable fit text on a paragraph block', async ( {
+ test( 'should allow changing to paragraph (level 0)', async ( {
editor,
page,
} ) => {
await editor.insertBlock( {
- name: 'core/paragraph',
+ name: 'core/fit-text',
attributes: {
- content: 'Test paragraph with fit text enabled',
+ content: 'Paragraph Test',
+ level: 2,
},
} );
- await editor.openDocumentSettingsSidebar();
+ const fitTextBlock = editor.canvas.locator(
+ '[data-type="core/fit-text"]'
+ );
+ await fitTextBlock.click();
- // Enable Fit text control via Typography options menu
- await page
- .getByRole( 'region', { name: 'Editor settings' } )
- .getByRole( 'button', { name: 'Typography options' } )
- .click();
+ // Open heading level dropdown
+ await page.getByRole( 'button', { name: 'Change level' } ).click();
+
+ // Select Paragraph
await page
- .getByRole( 'menu', { name: 'Typography options' } )
- .getByRole( 'menuitemcheckbox', { name: 'Show Fit text' } )
+ .getByRole( 'menuitemradio', { name: 'Paragraph' } )
.click();
- const fitTextToggle = page.getByRole( 'checkbox', {
- name: 'Fit text',
- } );
-
- await fitTextToggle.click();
-
await expect.poll( editor.getBlocks ).toMatchObject( [
{
- name: 'core/paragraph',
+ name: 'core/fit-text',
attributes: {
- content: 'Test paragraph with fit text enabled',
- fitText: true,
+ content: 'Paragraph Test',
+ level: 0,
},
},
] );
-
- const paragraphBlock = editor.canvas.locator(
- '[data-type="core/paragraph"]'
- );
-
- await expect( paragraphBlock ).toHaveClass( /has-fit-text/ );
} );
test( 'should apply font size dynamically based on container width in editor', async ( {
@@ -133,37 +112,49 @@ test.describe( 'Fit Text', () => {
page,
} ) => {
await editor.insertBlock( {
- name: 'core/heading',
+ name: 'core/fit-text',
attributes: {
content: 'Resizable Text',
level: 2,
- fitText: true,
},
} );
- const headingBlock = editor.canvas.locator(
- '[data-type="core/heading"]'
+ const fitTextBlock = editor.canvas.locator(
+ '[data-type="core/fit-text"]'
);
// Wait for fit text to apply
- await headingBlock.waitFor( { state: 'attached' } );
- await expect( headingBlock ).toHaveClass( /has-fit-text/ );
+ await fitTextBlock.waitFor( { state: 'attached' } );
- const initialFontSize = await headingBlock.evaluate( ( el ) => {
+ const initialFontSize = await fitTextBlock.evaluate( ( el ) => {
return window.getComputedStyle( el ).fontSize;
} );
// Add more text to force smaller font size
- await headingBlock.click();
+ await fitTextBlock.click();
await page.keyboard.press( 'End' );
await page.keyboard.type(
' that is much longer and should have smaller font'
);
- // Wait for DOM to update and fit text to recalculate
- await headingBlock.waitFor( { state: 'attached' } );
+ // Wait for font size to decrease after adding more text
+ await fitTextBlock.evaluate( ( el, prevSize ) => {
+ return new Promise( ( resolve ) => {
+ const checkSize = () => {
+ const currentSize = parseFloat(
+ window.getComputedStyle( el ).fontSize
+ );
+ if ( currentSize < prevSize ) {
+ resolve();
+ } else {
+ window.requestAnimationFrame( checkSize );
+ }
+ };
+ checkSize();
+ } );
+ }, parseFloat( initialFontSize ) );
- const newFontSize = await headingBlock.evaluate( ( el ) => {
+ const newFontSize = await fitTextBlock.evaluate( ( el ) => {
return window.getComputedStyle( el ).fontSize;
} );
@@ -174,46 +165,43 @@ test.describe( 'Fit Text', () => {
expect( newSize ).toBeLessThan( initialSize );
} );
- test( 'should apply much larger font size with fit text compared to without fit text for a short text', async ( {
+ test( 'should apply much larger font size with fit text compared to a normal heading for short text', async ( {
editor,
} ) => {
- // Insert two paragraphs with same content for comparison
+ // Insert a regular heading and a fit text block with same content
await editor.insertBlock( {
- name: 'core/paragraph',
+ name: 'core/heading',
attributes: {
content: 'Hello',
+ level: 2,
},
} );
await editor.insertBlock( {
- name: 'core/paragraph',
+ name: 'core/fit-text',
attributes: {
content: 'Hello',
- fitText: true,
+ level: 2,
},
} );
- const paragraphBlocks = editor.canvas.locator(
- '[data-type="core/paragraph"]'
+ const headingBlock = editor.canvas.locator(
+ '[data-type="core/heading"]'
+ );
+ const fitTextBlock = editor.canvas.locator(
+ '[data-type="core/fit-text"]'
);
// Wait for fit text to apply
- await paragraphBlocks.nth( 1 ).waitFor( { state: 'attached' } );
- await expect( paragraphBlocks.nth( 1 ) ).toHaveClass(
- /has-fit-text/
- );
+ await fitTextBlock.waitFor( { state: 'attached' } );
- const normalFontSize = await paragraphBlocks
- .nth( 0 )
- .evaluate( ( el ) => {
- return window.getComputedStyle( el ).fontSize;
- } );
+ const normalFontSize = await headingBlock.evaluate( ( el ) => {
+ return window.getComputedStyle( el ).fontSize;
+ } );
- const fitTextFontSize = await paragraphBlocks
- .nth( 1 )
- .evaluate( ( el ) => {
- return window.getComputedStyle( el ).fontSize;
- } );
+ const fitTextFontSize = await fitTextBlock.evaluate( ( el ) => {
+ return window.getComputedStyle( el ).fontSize;
+ } );
const normalSize = parseFloat( normalFontSize );
const fitTextSize = parseFloat( fitTextFontSize );
@@ -222,100 +210,16 @@ test.describe( 'Fit Text', () => {
expect( fitTextSize ).toBeGreaterThan( normalSize * 2 );
} );
- test( 'should disable fit text when a font size is selected', async ( {
- editor,
- page,
- } ) => {
- await editor.insertBlock( {
- name: 'core/heading',
- attributes: {
- content: 'Test Heading',
- level: 2,
- fitText: true,
- },
- } );
-
- await editor.openDocumentSettingsSidebar();
-
- // Set a custom font size
- await page.click(
- 'role=region[name="Editor settings"i] >> role=button[name="Set custom size"i]'
- );
- await page.click( 'role=spinbutton[name="Font size"i]' );
- await page.keyboard.type( '24' );
-
- // fitText should be cleared
- await expect.poll( editor.getBlocks ).toMatchObject( [
- {
- name: 'core/heading',
- attributes: expect.objectContaining( {
- content: 'Test Heading',
- level: 2,
- style: {
- typography: {
- fontSize: '24px',
- },
- },
- } ),
- },
- ] );
- } );
-
- test( 'should clear font size when fit text is enabled', async ( {
- editor,
- page,
- } ) => {
- await editor.insertBlock( {
- name: 'core/heading',
- attributes: {
- content: 'Test Heading',
- level: 2,
- fontSize: 'large',
- },
- } );
-
- await editor.openDocumentSettingsSidebar();
-
- // Enable Fit text control via Typography options menu
- await page
- .getByRole( 'region', { name: 'Editor settings' } )
- .getByRole( 'button', { name: 'Typography options' } )
- .click();
- await page
- .getByRole( 'menu', { name: 'Typography options' } )
- .getByRole( 'menuitemcheckbox', { name: 'Show Fit text' } )
- .click();
-
- const fitTextToggle = page.getByRole( 'checkbox', {
- name: 'Fit text',
- } );
-
- await fitTextToggle.click();
-
- // fontSize should be cleared
- await expect.poll( editor.getBlocks ).toMatchObject( [
- {
- name: 'core/heading',
- attributes: expect.objectContaining( {
- content: 'Test Heading',
- level: 2,
- fitText: true,
- } ),
- },
- ] );
- } );
-
test( 'should not load frontend script when editing a saved post with fit text', async ( {
admin,
editor,
page,
} ) => {
await editor.insertBlock( {
- name: 'core/heading',
+ name: 'core/fit-text',
attributes: {
content: 'Test Heading',
level: 2,
- fitText: true,
},
} );
@@ -323,18 +227,17 @@ test.describe( 'Fit Text', () => {
await admin.editPost( postId );
- const headingBlock = editor.canvas.locator(
- '[data-type="core/heading"]'
+ const fitTextBlock = editor.canvas.locator(
+ '[data-type="core/fit-text"]'
);
- await expect( headingBlock ).toBeVisible();
+ await expect( fitTextBlock ).toBeVisible();
await expect.poll( editor.getBlocks ).toMatchObject( [
{
- name: 'core/heading',
+ name: 'core/fit-text',
attributes: {
content: 'Test Heading',
level: 2,
- fitText: true,
},
},
] );
@@ -344,8 +247,10 @@ test.describe( 'Fit Text', () => {
const scripts = Array.from(
document.querySelectorAll( 'script[type="module"]' )
);
- return scripts.some( ( script ) =>
- script.src.includes( 'fit-text-frontend' )
+ return scripts.some(
+ ( script ) =>
+ script.src.includes( 'fit-text' ) &&
+ script.src.includes( 'view' )
);
} );
expect( frontendScriptLoaded ).toBe( false );
@@ -358,11 +263,10 @@ test.describe( 'Fit Text', () => {
page,
} ) => {
await editor.insertBlock( {
- name: 'core/heading',
+ name: 'core/fit-text',
attributes: {
content: 'Frontend Test',
level: 2,
- fitText: true,
},
} );
@@ -374,16 +278,15 @@ test.describe( 'Fit Text', () => {
await page.goto( postUrl );
- const heading = page.locator( 'h2.has-fit-text' );
+ const fitText = page.locator( '.wp-block-fit-text' );
- await expect( heading ).toBeVisible();
- await expect( heading ).toHaveClass( /has-fit-text/ );
+ await expect( fitText ).toBeVisible();
- const inlineStyle = await heading.getAttribute( 'style' );
+ const inlineStyle = await fitText.getAttribute( 'style' );
expect( inlineStyle ).toContain( 'font-size' );
expect( inlineStyle ).toMatch( /font-size:\s*\d+px/ );
- const computedFontSize = await heading.evaluate( ( el ) => {
+ const computedFontSize = await fitText.evaluate( ( el ) => {
return window.getComputedStyle( el ).fontSize;
} );
@@ -398,11 +301,10 @@ test.describe( 'Fit Text', () => {
page,
} ) => {
await editor.insertBlock( {
- name: 'core/heading',
+ name: 'core/fit-text',
attributes: {
content: 'Resize Me',
level: 2,
- fitText: true,
},
} );
@@ -414,33 +316,32 @@ test.describe( 'Fit Text', () => {
await page.goto( postUrl );
- const heading = page.locator( 'h2.has-fit-text' );
+ const fitText = page.locator( '.wp-block-fit-text' );
// Wait for fit text to initialize
- await heading.waitFor( { state: 'visible' } );
- await expect( heading ).toHaveClass( /has-fit-text/ );
+ await fitText.waitFor( { state: 'visible' } );
// Wait for inline style to be applied
await page.waitForFunction(
() => {
- const el = document.querySelector( 'h2.has-fit-text' );
+ const el = document.querySelector( '.wp-block-fit-text' );
return el && el.style.fontSize && el.style.fontSize !== '';
},
{ timeout: 5000 }
);
- const initialFontSize = await heading.evaluate( ( el ) => {
+ const initialFontSize = await fitText.evaluate( ( el ) => {
return window.getComputedStyle( el ).fontSize;
} );
- const initialInlineStyle = await heading.getAttribute( 'style' );
+ const initialInlineStyle = await fitText.getAttribute( 'style' );
await page.setViewportSize( { width: 440, height: 720 } );
// Wait for inline font-size style to change after resize
await page.waitForFunction(
( previousStyle ) => {
- const el = document.querySelector( 'h2.has-fit-text' );
+ const el = document.querySelector( '.wp-block-fit-text' );
return (
el &&
el.style.fontSize &&
@@ -451,7 +352,7 @@ test.describe( 'Fit Text', () => {
{ timeout: 5000 }
);
- const newFontSize = await heading.evaluate( ( el ) => {
+ const newFontSize = await fitText.evaluate( ( el ) => {
return window.getComputedStyle( el ).fontSize;
} );
@@ -466,19 +367,20 @@ test.describe( 'Fit Text', () => {
editor,
page,
} ) => {
- // Insert two paragraphs with same content for comparison
+ // Insert two headings with same content for comparison
await editor.insertBlock( {
- name: 'core/paragraph',
+ name: 'core/heading',
attributes: {
content: 'Hello',
+ level: 2,
},
} );
await editor.insertBlock( {
- name: 'core/paragraph',
+ name: 'core/fit-text',
attributes: {
content: 'Hello',
- fitText: true,
+ level: 2,
},
} );
@@ -490,30 +392,27 @@ test.describe( 'Fit Text', () => {
await page.goto( postUrl );
- const fitTextParagraph = page.locator( 'p.has-fit-text' );
+ const fitText = page.locator( '.wp-block-fit-text' );
// Wait for fit text to initialize
- await fitTextParagraph.waitFor( { state: 'visible' } );
- await expect( fitTextParagraph ).toHaveClass( /has-fit-text/ );
+ await fitText.waitFor( { state: 'visible' } );
// Wait for inline style to be applied
await page.waitForFunction(
() => {
- const el = document.querySelector( 'p.has-fit-text' );
+ const el = document.querySelector( '.wp-block-fit-text' );
return el && el.style.fontSize && el.style.fontSize !== '';
},
{ timeout: 5000 }
);
- const paragraphs = page.locator( 'p' );
+ const headings = page.locator( 'h2' );
- const normalFontSize = await paragraphs
- .first()
- .evaluate( ( el ) => {
- return window.getComputedStyle( el ).fontSize;
- } );
+ const normalFontSize = await headings.first().evaluate( ( el ) => {
+ return window.getComputedStyle( el ).fontSize;
+ } );
- const fitTextFontSize = await fitTextParagraph.evaluate( ( el ) => {
+ const fitTextFontSize = await fitText.evaluate( ( el ) => {
return window.getComputedStyle( el ).fontSize;
} );
diff --git a/test/integration/fixtures/blocks/core__fit-text.html b/test/integration/fixtures/blocks/core__fit-text.html
new file mode 100644
index 00000000000000..0bad0186b0b246
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__fit-text.html
@@ -0,0 +1,3 @@
+
+Fit Text
+
diff --git a/test/integration/fixtures/blocks/core__fit-text.json b/test/integration/fixtures/blocks/core__fit-text.json
new file mode 100644
index 00000000000000..24035c017e63ed
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__fit-text.json
@@ -0,0 +1,12 @@
+[
+ {
+ "name": "core/fit-text",
+ "isValid": true,
+ "attributes": {
+ "content": "Fit Text",
+ "level": 0,
+ "levelOptions": [ 0, 1, 2, 3, 4, 5, 6 ]
+ },
+ "innerBlocks": []
+ }
+]
diff --git a/test/integration/fixtures/blocks/core__fit-text.parsed.json b/test/integration/fixtures/blocks/core__fit-text.parsed.json
new file mode 100644
index 00000000000000..d988f7233d620e
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__fit-text.parsed.json
@@ -0,0 +1,9 @@
+[
+ {
+ "blockName": "core/fit-text",
+ "attrs": {},
+ "innerBlocks": [],
+ "innerHTML": "\nFit Text
\n",
+ "innerContent": [ "\nFit Text
\n" ]
+ }
+]
diff --git a/test/integration/fixtures/blocks/core__fit-text.serialized.html b/test/integration/fixtures/blocks/core__fit-text.serialized.html
new file mode 100644
index 00000000000000..0bad0186b0b246
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__fit-text.serialized.html
@@ -0,0 +1,3 @@
+
+Fit Text
+