diff --git a/packages/block-editor/src/components/block-preview/index.js b/packages/block-editor/src/components/block-preview/index.js
index cf0937507be095..0fb7f55b9955d2 100644
--- a/packages/block-editor/src/components/block-preview/index.js
+++ b/packages/block-editor/src/components/block-preview/index.js
@@ -16,6 +16,7 @@ import deprecated from '@wordpress/deprecated';
*/
import { ExperimentalBlockEditorProvider } from '../provider';
import AutoHeightBlockPreview from './auto';
+import EditorStyles from '../editor-styles';
import { store as blockEditorStore } from '../../store';
import { BlockListItems } from '../block-list';
@@ -113,7 +114,11 @@ export function useBlockPreview( { blocks, props = {}, layout } ) {
[]
);
const settings = useMemo(
- () => ( { ...originalSettings, __unstableIsPreviewMode: true } ),
+ () => ( {
+ ...originalSettings,
+ styles: undefined, // Clear styles included by the parent settings, as they are already output by the parent's EditorStyles.
+ __unstableIsPreviewMode: true,
+ } ),
[ originalSettings ]
);
const disabledRef = useDisabled();
@@ -128,6 +133,7 @@ export function useBlockPreview( { blocks, props = {}, layout } ) {
value={ renderedBlocks }
settings={ settings }
>
+
);
diff --git a/packages/block-editor/src/hooks/duotone.js b/packages/block-editor/src/hooks/duotone.js
index 2bbe18793fe558..39a8979782a0c6 100644
--- a/packages/block-editor/src/hooks/duotone.js
+++ b/packages/block-editor/src/hooks/duotone.js
@@ -37,11 +37,22 @@ import { scopeSelector } from '../components/global-styles/utils';
import { useBlockSettings } from './utils';
import { default as StylesFiltersPanel } from '../components/global-styles/filters-panel';
import { useBlockEditingMode } from '../components/block-editing-mode';
+import { __unstableUseBlockElement as useBlockElement } from '../components/block-list/use-block-props/use-block-refs';
import { store as blockEditorStore } from '../store';
import { unlock } from '../lock-unlock';
const EMPTY_ARRAY = [];
+// Safari does not always update the duotone filter when the duotone colors
+// are changed. This browser check is later used to force a re-render of the block
+// element to ensure the duotone filter is updated. The check is included at the
+// root of this file as it only needs to be run once per page load.
+const isSafari =
+ window?.navigator.userAgent &&
+ window.navigator.userAgent.includes( 'Safari' ) &&
+ ! window.navigator.userAgent.includes( 'Chrome' ) &&
+ ! window.navigator.userAgent.includes( 'Chromium' );
+
extend( [ namesPlugin ] );
function useMultiOriginPresets( { presetSetting, defaultSetting } ) {
@@ -223,6 +234,7 @@ const withDuotoneControls = createHigherOrderComponent(
);
function DuotoneStyles( {
+ clientId,
id: filterId,
selector: duotoneSelector,
attribute: duotoneAttr,
@@ -278,6 +290,8 @@ function DuotoneStyles( {
useDispatch( blockEditorStore )
);
+ const blockElement = useBlockElement( clientId );
+
useEffect( () => {
if ( ! isValidFilter ) return;
@@ -294,12 +308,30 @@ function DuotoneStyles( {
__unstableType: 'svgs',
} );
+ // Safari does not always update the duotone filter when the duotone colors
+ // are changed. When using Safari, force the block element to be repainted by
+ // the browser to ensure any changes are reflected visually. This logic matches
+ // that used on the site frontend in `block-supports/duotone.php`.
+ if ( blockElement && isSafari ) {
+ const display = blockElement.style.display;
+ // Switch to `inline-block` to force a repaint. In the editor, `inline-block`
+ // is used instead of `none` to ensure that scroll position is not affected,
+ // as `none` results in the editor scrolling to the top of the block.
+ blockElement.style.display = 'inline-block';
+ // Simply accessing el.offsetHeight flushes layout and style
+ // changes in WebKit without having to wait for setTimeout.
+ // eslint-disable-next-line no-unused-expressions
+ blockElement.offsetHeight;
+ blockElement.style.display = display;
+ }
+
return () => {
deleteStyleOverride( filterId );
deleteStyleOverride( `duotone-${ filterId }` );
};
}, [
isValidFilter,
+ blockElement,
colors,
selector,
filterId,
@@ -378,6 +410,7 @@ const withDuotoneStyles = createHigherOrderComponent(
<>
{ shouldRender && (