Skip to content

Instantly share code, notes, and snippets.

@isuke01
Last active March 17, 2026 12:58
Show Gist options
  • Select an option

  • Save isuke01/38506fb250c7e9d3245c9e26440eb17b to your computer and use it in GitHub Desktop.

Select an option

Save isuke01/38506fb250c7e9d3245c9e26440eb17b to your computer and use it in GitHub Desktop.
Wordpress Gutenberg: Media and text extend with mobile controls for media part.
.wp-block-media-text.is-stacked-on-mobile .wp-block-media-text__media,
.wp-block-media-text.is-stacked-on-mobile.has-media-on-the-right .wp-block-media-text__media {
width: var(--mobile-media--width, 100%) !important;
}
.wp-block-media-text.has-mobile-centered-media .wp-block-media-text__media {
align-items: center;
justify-content: center;
margin-left: auto;
margin-right: auto;
}
.wp-block-media-text.has-mobile-centered-media .wp-block-media-text__media img {
margin-left: auto;
margin-right: auto;
}
.wp-block-media-text.has-mobile-top-media.is-stacked-on-mobile .wp-block-media-text__media,
.wp-block-media-text.has-mobile-top-media.is-stacked-on-mobile.has-media-on-the-right .wp-block-media-text__media {
grid-row: 1;
order: -1;
}
.wp-block-media-text.has-mobile-top-media.is-stacked-on-mobile .wp-block-media-text__content,
.wp-block-media-text.has-mobile-top-media.is-stacked-on-mobile.has-media-on-the-right .wp-block-media-text__content {
grid-row: 2;
}
/* OPTIONAL EDITOR STYLES: Add % after the numer in range selector. */
.mobile-media-width-range-control .components-input-control__container {
position: relative;
}
.mobile-media-width-range-control .components-input-control__container:not(:hover)::after {
color: #757575;
content: "%";
pointer-events: none;
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
}
.mobile-media-width-range-control .components-input-control__input {
padding-right: 20px;
}
/**
* WordPress dependencies
*/
import { addFilter } from '@wordpress/hooks';
import { __ } from '@wordpress/i18n';
import { createHigherOrderComponent } from '@wordpress/compose';
import { Fragment } from '@wordpress/element';
import { InspectorControls } from '@wordpress/block-editor';
import { PanelBody, RangeControl, ToggleControl } from '@wordpress/components';
const MOBILE_MEDIA_WIDTH_ATTRIBUTE = 'mobileMediaWidth';
const MOBILE_CENTER_MEDIA_ATTRIBUTE = 'mobile_center_media';
const MOBILE_TOP_MEDIA_ATTRIBUTE = 'mobile_top_media';
const TARGET_BLOCK = 'core/media-text';
const MOBILE_CENTER_MEDIA_CLASS = 'has-mobile-centered-media';
const MOBILE_TOP_MEDIA_CLASS = 'has-mobile-top-media';
/**
* Add custom attribute for mobile media width.
*
* @param {Object} settings Block settings.
* @param {string} name Block name.
* @return {Object} Updated block settings.
*/
function addMobileMediaWidthAttribute(settings, name) {
if (name !== TARGET_BLOCK) {
return settings;
}
return {
...settings,
attributes: {
...settings.attributes,
[MOBILE_MEDIA_WIDTH_ATTRIBUTE]: {
type: 'number',
},
[MOBILE_CENTER_MEDIA_ATTRIBUTE]: {
type: 'boolean',
},
[MOBILE_TOP_MEDIA_ATTRIBUTE]: {
type: 'boolean',
},
},
};
}
/**
* Add inspector control for mobile media width.
*/
const withMobileMediaWidthControl = createHigherOrderComponent(
(BlockEdit) => (props) => {
if (props.name !== TARGET_BLOCK) {
return <BlockEdit {...props} />;
}
const { attributes, setAttributes } = props;
const {
mobileMediaWidth,
isStackedOnMobile,
[MOBILE_CENTER_MEDIA_ATTRIBUTE]: mobileCenterMedia,
[MOBILE_TOP_MEDIA_ATTRIBUTE]: mobileTopMedia,
} = attributes;
const controlValue =
typeof mobileMediaWidth === 'number' ? mobileMediaWidth : 100;
return (
<Fragment>
<BlockEdit {...props} />
<InspectorControls>
<PanelBody
title={__('Mobile media settings', 'block-theme')}
initialOpen={true}
>
<RangeControl
className="mobile-media-width-range-control"
label={__('Mobile media width', 'block-theme')}
value={controlValue}
renderTooltipContent={(value) => `${value}%`}
onChange={(value) =>
setAttributes({
[MOBILE_MEDIA_WIDTH_ATTRIBUTE]: value,
})
}
min={1}
max={100}
allowReset={false}
/>
<ToggleControl
label={__('Center media', 'block-theme')}
checked={!!mobileCenterMedia}
onChange={(value) =>
setAttributes({
[MOBILE_CENTER_MEDIA_ATTRIBUTE]: value,
})
}
/>
{isStackedOnMobile && (
<ToggleControl
label={__('Show image on top', 'block-theme')}
checked={!!mobileTopMedia}
onChange={(value) =>
setAttributes({
[MOBILE_TOP_MEDIA_ATTRIBUTE]: value,
})
}
/>
)}
</PanelBody>
</InspectorControls>
</Fragment>
);
},
'withMobileMediaWidthControl',
);
/**
* Persist CSS custom property on saved block markup.
*
* @param {Object} extraProps Block save props.
* @param {Object} blockType Block type object.
* @param {Object} attributes Block attributes.
* @return {Object} Updated save props.
*/
function addMobileMediaWidthStyle(extraProps, blockType, attributes) {
if (blockType?.name !== TARGET_BLOCK) {
return extraProps;
}
const mobileMediaWidth = attributes?.[MOBILE_MEDIA_WIDTH_ATTRIBUTE];
const isStackedOnMobile = !!attributes?.isStackedOnMobile;
const classes = [extraProps.className || ''];
if (attributes?.[MOBILE_CENTER_MEDIA_ATTRIBUTE]) {
classes.push(MOBILE_CENTER_MEDIA_CLASS);
}
if (isStackedOnMobile && attributes?.[MOBILE_TOP_MEDIA_ATTRIBUTE]) {
classes.push(MOBILE_TOP_MEDIA_CLASS);
}
const nextExtraProps = {
...extraProps,
className: classes.join(' ').trim(),
};
if (typeof mobileMediaWidth !== 'number') {
return nextExtraProps;
}
return {
...nextExtraProps,
style: {
...(extraProps.style || {}),
'--mobile-media--width': `${mobileMediaWidth}%`,
},
};
}
/**
* Apply CSS custom property in editor preview wrapper.
*/
const withMobileMediaWidthEditorStyle = createHigherOrderComponent(
(BlockListBlock) => (props) => {
if (props.name !== TARGET_BLOCK) {
return <BlockListBlock {...props} />;
}
const mobileMediaWidth =
props.attributes?.[MOBILE_MEDIA_WIDTH_ATTRIBUTE];
const isStackedOnMobile = !!props.attributes?.isStackedOnMobile;
const mobileCenterMedia =
props.attributes?.[MOBILE_CENTER_MEDIA_ATTRIBUTE];
const mobileTopMedia = props.attributes?.[MOBILE_TOP_MEDIA_ATTRIBUTE];
const classNames = [props.wrapperProps?.className || ''];
if (mobileCenterMedia) {
classNames.push(MOBILE_CENTER_MEDIA_CLASS);
}
if (isStackedOnMobile && mobileTopMedia) {
classNames.push(MOBILE_TOP_MEDIA_CLASS);
}
const wrapperProps = {
...(props.wrapperProps || {}),
className: classNames.join(' ').trim(),
};
if (typeof mobileMediaWidth !== 'number') {
return <BlockListBlock {...props} wrapperProps={wrapperProps} />;
}
wrapperProps.style = {
...(props.wrapperProps?.style || {}),
'--mobile-media--width': `${mobileMediaWidth}%`,
};
return <BlockListBlock {...props} wrapperProps={wrapperProps} />;
},
'withMobileMediaWidthEditorStyle',
);
addFilter(
'blocks.registerBlockType',
'block-theme/media-text-mobile-media-width-attribute',
addMobileMediaWidthAttribute,
);
addFilter(
'editor.BlockEdit',
'block-theme/media-text-mobile-media-width-control',
withMobileMediaWidthControl,
);
addFilter(
'editor.BlockListBlock',
'block-theme/media-text-mobile-media-width-editor-style',
withMobileMediaWidthEditorStyle,
);
addFilter(
'blocks.getSaveContent.extraProps',
'block-theme/media-text-mobile-media-width-style',
addMobileMediaWidthStyle,
);
@isuke01

isuke01 commented Mar 17, 2026

Copy link
Copy Markdown
Author

It adds mobile media settings:

  • Mobile media width
  • Center media
  • Show image on top (only when on stack on mobile is on)
  • Does not break component when added - fallback friendly.
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment