wp_render_duotone_support

The timeline below displays how wordpress function wp_render_duotone_support has changed across different WordPress versions. If a version is not listed, refer to the next available version below.

WordPress Version: 6.3

/**
 * Renders out the duotone stylesheet and SVG.
 *
 * @since 5.8.0
 * @since 6.1.0 Allow unset for preset colors.
 * @deprecated 6.3.0 Use WP_Duotone::render_duotone_support() instead.
 *
 * @access private
 *
 * @param string $block_content Rendered block content.
 * @param array  $block         Block object.
 * @return string Filtered block content.
 */
function wp_render_duotone_support($block_content, $block)
{
    _deprecated_function(__FUNCTION__, '6.3.0', 'WP_Duotone::render_duotone_support()');
    $wp_block = new WP_Block($block);
    return WP_Duotone::render_duotone_support($block_content, $block, $wp_block);
}

WordPress Version: 6.2

/**
 * Renders out the duotone stylesheet and SVG.
 *
 * @since 5.8.0
 * @since 6.1.0 Allow unset for preset colors.
 * @access private
 *
 * @param string $block_content Rendered block content.
 * @param array  $block         Block object.
 * @return string Filtered block content.
 */
function wp_render_duotone_support($block_content, $block)
{
    $block_type = WP_Block_Type_Registry::get_instance()->get_registered($block['blockName']);
    $duotone_support = false;
    if ($block_type && property_exists($block_type, 'supports')) {
        $duotone_support = _wp_array_get($block_type->supports, array('color', '__experimentalDuotone'), false);
    }
    $has_duotone_attribute = isset($block['attrs']['style']['color']['duotone']);
    if (!$duotone_support || !$has_duotone_attribute) {
        return $block_content;
    }
    $colors = $block['attrs']['style']['color']['duotone'];
    $filter_key = is_array($colors) ? implode('-', $colors) : $colors;
    $filter_preset = array('slug' => wp_unique_id(sanitize_key($filter_key . '-')), 'colors' => $colors);
    $filter_property = wp_get_duotone_filter_property($filter_preset);
    $filter_id = wp_get_duotone_filter_id($filter_preset);
    $scope = '.' . $filter_id;
    $selectors = explode(',', $duotone_support);
    $scoped = array();
    foreach ($selectors as $sel) {
        $scoped[] = $scope . ' ' . trim($sel);
    }
    $selector = implode(', ', $scoped);
    // !important is needed because these styles render before global styles,
    // and they should be overriding the duotone filters set by global styles.
    $filter_style = SCRIPT_DEBUG ? $selector . " {\n\tfilter: " . $filter_property . " !important;\n}\n" : ($selector . '{filter:' . $filter_property . ' !important;}');
    wp_register_style($filter_id, false);
    wp_add_inline_style($filter_id, $filter_style);
    wp_enqueue_style($filter_id);
    if ('unset' !== $colors) {
        $filter_svg = wp_get_duotone_filter_svg($filter_preset);
        add_action('wp_footer', static function () use ($filter_svg, $selector) {
            echo $filter_svg;
            /*
             * Safari renders elements incorrectly on first paint when the
             * SVG filter comes after the content that it is filtering, so
             * we force a repaint with a WebKit hack which solves the issue.
             */
            global $is_safari;
            if ($is_safari) {
                /*
                 * Simply accessing el.offsetHeight flushes layout and style
                 * changes in WebKit without having to wait for setTimeout.
                 */
                printf('<script>( function() { var el = document.querySelector( %s ); var display = el.style.display; el.style.display = "none"; el.offsetHeight; el.style.display = display; } )();</script>', wp_json_encode($selector));
            }
        });
    }
    // Like the layout hook, this assumes the hook only applies to blocks with a single wrapper.
    return preg_replace('/' . preg_quote('class="', '/') . '/', 'class="' . $filter_id . ' ', $block_content, 1);
}

WordPress Version: 6.1

/**
 * Render out the duotone stylesheet and SVG.
 *
 * @since 5.8.0
 * @since 6.1.0 Allow unset for preset colors.
 * @access private
 *
 * @param string $block_content Rendered block content.
 * @param array  $block         Block object.
 * @return string Filtered block content.
 */
function wp_render_duotone_support($block_content, $block)
{
    $block_type = WP_Block_Type_Registry::get_instance()->get_registered($block['blockName']);
    $duotone_support = false;
    if ($block_type && property_exists($block_type, 'supports')) {
        $duotone_support = _wp_array_get($block_type->supports, array('color', '__experimentalDuotone'), false);
    }
    $has_duotone_attribute = isset($block['attrs']['style']['color']['duotone']);
    if (!$duotone_support || !$has_duotone_attribute) {
        return $block_content;
    }
    $colors = $block['attrs']['style']['color']['duotone'];
    $filter_key = is_array($colors) ? implode('-', $colors) : $colors;
    $filter_preset = array('slug' => wp_unique_id(sanitize_key($filter_key . '-')), 'colors' => $colors);
    $filter_property = wp_get_duotone_filter_property($filter_preset);
    $filter_id = wp_get_duotone_filter_id($filter_preset);
    $scope = '.' . $filter_id;
    $selectors = explode(',', $duotone_support);
    $scoped = array();
    foreach ($selectors as $sel) {
        $scoped[] = $scope . ' ' . trim($sel);
    }
    $selector = implode(', ', $scoped);
    // !important is needed because these styles render before global styles,
    // and they should be overriding the duotone filters set by global styles.
    $filter_style = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? $selector . " {\n\tfilter: " . $filter_property . " !important;\n}\n" : ($selector . '{filter:' . $filter_property . ' !important;}');
    wp_register_style($filter_id, false, array(), true, true);
    wp_add_inline_style($filter_id, $filter_style);
    wp_enqueue_style($filter_id);
    if ('unset' !== $colors) {
        $filter_svg = wp_get_duotone_filter_svg($filter_preset);
        add_action('wp_footer', static function () use ($filter_svg, $selector) {
            echo $filter_svg;
            /*
             * Safari renders elements incorrectly on first paint when the
             * SVG filter comes after the content that it is filtering, so
             * we force a repaint with a WebKit hack which solves the issue.
             */
            global $is_safari;
            if ($is_safari) {
                /*
                 * Simply accessing el.offsetHeight flushes layout and style
                 * changes in WebKit without having to wait for setTimeout.
                 */
                printf('<script>( function() { var el = document.querySelector( %s ); var display = el.style.display; el.style.display = "none"; el.offsetHeight; el.style.display = display; } )();</script>', wp_json_encode($selector));
            }
        });
    }
    // Like the layout hook, this assumes the hook only applies to blocks with a single wrapper.
    return preg_replace('/' . preg_quote('class="', '/') . '/', 'class="' . $filter_id . ' ', $block_content, 1);
}

WordPress Version: 9.3

/**
 * Render out the duotone stylesheet and SVG.
 *
 * @since 5.8.0
 * @access private
 *
 * @param string $block_content Rendered block content.
 * @param array  $block         Block object.
 * @return string Filtered block content.
 */
function wp_render_duotone_support($block_content, $block)
{
    $block_type = WP_Block_Type_Registry::get_instance()->get_registered($block['blockName']);
    $duotone_support = false;
    if ($block_type && property_exists($block_type, 'supports')) {
        $duotone_support = _wp_array_get($block_type->supports, array('color', '__experimentalDuotone'), false);
    }
    $has_duotone_attribute = isset($block['attrs']['style']['color']['duotone']);
    if (!$duotone_support || !$has_duotone_attribute) {
        return $block_content;
    }
    $filter_preset = array('slug' => wp_unique_id(sanitize_key(implode('-', $block['attrs']['style']['color']['duotone']) . '-')), 'colors' => $block['attrs']['style']['color']['duotone']);
    $filter_property = wp_get_duotone_filter_property($filter_preset);
    $filter_id = wp_get_duotone_filter_id($filter_preset);
    $filter_svg = wp_get_duotone_filter_svg($filter_preset);
    $scope = '.' . $filter_id;
    $selectors = explode(',', $duotone_support);
    $scoped = array();
    foreach ($selectors as $sel) {
        $scoped[] = $scope . ' ' . trim($sel);
    }
    $selector = implode(', ', $scoped);
    // !important is needed because these styles render before global styles,
    // and they should be overriding the duotone filters set by global styles.
    $filter_style = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? $selector . " {\n\tfilter: " . $filter_property . " !important;\n}\n" : ($selector . '{filter:' . $filter_property . ' !important;}');
    wp_register_style($filter_id, false, array(), true, true);
    wp_add_inline_style($filter_id, $filter_style);
    wp_enqueue_style($filter_id);
    add_action('wp_footer', static function () use ($filter_svg, $selector) {
        echo $filter_svg;
        /*
         * Safari renders elements incorrectly on first paint when the SVG
         * filter comes after the content that it is filtering, so we force
         * a repaint with a WebKit hack which solves the issue.
         */
        global $is_safari;
        if ($is_safari) {
            printf(
                // Simply accessing el.offsetHeight flushes layout and style
                // changes in WebKit without having to wait for setTimeout.
                '<script>( function() { var el = document.querySelector( %s ); var display = el.style.display; el.style.display = "none"; el.offsetHeight; el.style.display = display; } )();</script>',
                wp_json_encode($selector)
            );
        }
    });
    // Like the layout hook, this assumes the hook only applies to blocks with a single wrapper.
    return preg_replace('/' . preg_quote('class="', '/') . '/', 'class="' . $filter_id . ' ', $block_content, 1);
}

WordPress Version: 9.1

/**
 * Render out the duotone stylesheet and SVG.
 *
 * @since 5.8.0
 * @access private
 *
 * @param string $block_content Rendered block content.
 * @param array  $block         Block object.
 * @return string Filtered block content.
 */
function wp_render_duotone_support($block_content, $block)
{
    $block_type = WP_Block_Type_Registry::get_instance()->get_registered($block['blockName']);
    $duotone_support = false;
    if ($block_type && property_exists($block_type, 'supports')) {
        $duotone_support = _wp_array_get($block_type->supports, array('color', '__experimentalDuotone'), false);
    }
    $has_duotone_attribute = isset($block['attrs']['style']['color']['duotone']);
    if (!$duotone_support || !$has_duotone_attribute) {
        return $block_content;
    }
    $filter_preset = array('slug' => uniqid(), 'colors' => $block['attrs']['style']['color']['duotone']);
    $filter_property = wp_get_duotone_filter_property($filter_preset);
    $filter_id = wp_get_duotone_filter_id($filter_preset);
    $filter_svg = wp_get_duotone_filter_svg($filter_preset);
    $scope = '.' . $filter_id;
    $selectors = explode(',', $duotone_support);
    $scoped = array();
    foreach ($selectors as $sel) {
        $scoped[] = $scope . ' ' . trim($sel);
    }
    $selector = implode(', ', $scoped);
    // !important is needed because these styles render before global styles,
    // and they should be overriding the duotone filters set by global styles.
    $filter_style = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? $selector . " {\n\tfilter: " . $filter_property . " !important;\n}\n" : ($selector . '{filter:' . $filter_property . ' !important;}');
    wp_register_style($filter_id, false, array(), true, true);
    wp_add_inline_style($filter_id, $filter_style);
    wp_enqueue_style($filter_id);
    add_action('wp_footer', static function () use ($filter_svg, $selector) {
        echo $filter_svg;
        /*
         * Safari renders elements incorrectly on first paint when the SVG
         * filter comes after the content that it is filtering, so we force
         * a repaint with a WebKit hack which solves the issue.
         */
        global $is_safari;
        if ($is_safari) {
            printf(
                // Simply accessing el.offsetHeight flushes layout and style
                // changes in WebKit without having to wait for setTimeout.
                '<script>( function() { var el = document.querySelector( %s ); var display = el.style.display; el.style.display = "none"; el.offsetHeight; el.style.display = display; } )();</script>',
                wp_json_encode($selector)
            );
        }
    });
    // Like the layout hook, this assumes the hook only applies to blocks with a single wrapper.
    return preg_replace('/' . preg_quote('class="', '/') . '/', 'class="' . $filter_id . ' ', $block_content, 1);
}

WordPress Version: 5.9

/**
 * Render out the duotone stylesheet and SVG.
 *
 * @since 5.8.0
 * @access private
 *
 * @param string $block_content Rendered block content.
 * @param array  $block         Block object.
 * @return string Filtered block content.
 */
function wp_render_duotone_support($block_content, $block)
{
    $block_type = WP_Block_Type_Registry::get_instance()->get_registered($block['blockName']);
    $duotone_support = false;
    if ($block_type && property_exists($block_type, 'supports')) {
        $duotone_support = _wp_array_get($block_type->supports, array('color', '__experimentalDuotone'), false);
    }
    $has_duotone_attribute = isset($block['attrs']['style']['color']['duotone']);
    if (!$duotone_support || !$has_duotone_attribute) {
        return $block_content;
    }
    $filter_preset = array('slug' => uniqid(), 'colors' => $block['attrs']['style']['color']['duotone']);
    $filter_property = wp_render_duotone_filter_preset($filter_preset);
    $filter_id = 'wp-duotone-' . $filter_preset['slug'];
    $scope = '.' . $filter_id;
    $selectors = explode(',', $duotone_support);
    $scoped = array();
    foreach ($selectors as $sel) {
        $scoped[] = $scope . ' ' . trim($sel);
    }
    $selector = implode(', ', $scoped);
    // !important is needed because these styles render before global styles,
    // and they should be overriding the duotone filters set by global styles.
    $filter_style = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? $selector . " {\n\tfilter: " . $filter_property . " !important;\n}\n" : ($selector . '{filter:' . $filter_property . ' !important;}');
    wp_register_style($filter_id, false, array(), true, true);
    wp_add_inline_style($filter_id, $filter_style);
    wp_enqueue_style($filter_id);
    // Like the layout hook, this assumes the hook only applies to blocks with a single wrapper.
    return preg_replace('/' . preg_quote('class="', '/') . '/', 'class="' . $filter_id . ' ', $block_content, 1);
}

WordPress Version: 5.8

/**
 * Render out the duotone stylesheet and SVG.
 *
 * @since 5.8.0
 * @access private
 *
 * @param string $block_content Rendered block content.
 * @param array  $block         Block object.
 *
 * @return string Filtered block content.
 */
function wp_render_duotone_support($block_content, $block)
{
    $block_type = WP_Block_Type_Registry::get_instance()->get_registered($block['blockName']);
    $duotone_support = false;
    if ($block_type && property_exists($block_type, 'supports')) {
        $duotone_support = _wp_array_get($block_type->supports, array('color', '__experimentalDuotone'), false);
    }
    $has_duotone_attribute = isset($block['attrs']['style']['color']['duotone']);
    if (!$duotone_support || !$has_duotone_attribute) {
        return $block_content;
    }
    $duotone_colors = $block['attrs']['style']['color']['duotone'];
    $duotone_values = array('r' => array(), 'g' => array(), 'b' => array());
    foreach ($duotone_colors as $color_str) {
        $color = wp_tinycolor_string_to_rgb($color_str);
        $duotone_values['r'][] = $color['r'] / 255;
        $duotone_values['g'][] = $color['g'] / 255;
        $duotone_values['b'][] = $color['b'] / 255;
    }
    $duotone_id = 'wp-duotone-filter-' . uniqid();
    $selectors = explode(',', $duotone_support);
    $selectors_scoped = array_map(function ($selector) use ($duotone_id) {
        return '.' . $duotone_id . ' ' . trim($selector);
    }, $selectors);
    $selectors_group = implode(', ', $selectors_scoped);
    ob_start();
    ?>

	<style>
		<?php 
    echo $selectors_group;
    ?> {
			filter: url( <?php 
    echo esc_url('#' . $duotone_id);
    ?> );
		}
	</style>

	<svg
		xmlns:xlink="http://www.w3.org/1999/xlink"
		viewBox="0 0 0 0"
		width="0"
		height="0"
		focusable="false"
		role="none"
		style="visibility: hidden; position: absolute; left: -9999px; overflow: hidden;"
	>
		<defs>
			<filter id="<?php 
    echo esc_attr($duotone_id);
    ?>">
				<feColorMatrix
					type="matrix"
					<?php 
    // phpcs:disable Generic.WhiteSpace.DisallowSpaceIndent 
    ?>
					values=".299 .587 .114 0 0
							.299 .587 .114 0 0
							.299 .587 .114 0 0
							0 0 0 1 0"
					<?php 
    // phpcs:enable Generic.WhiteSpace.DisallowSpaceIndent 
    ?>
				/>
				<feComponentTransfer color-interpolation-filters="sRGB" >
					<feFuncR type="table" tableValues="<?php 
    echo esc_attr(implode(' ', $duotone_values['r']));
    ?>" />
					<feFuncG type="table" tableValues="<?php 
    echo esc_attr(implode(' ', $duotone_values['g']));
    ?>" />
					<feFuncB type="table" tableValues="<?php 
    echo esc_attr(implode(' ', $duotone_values['b']));
    ?>" />
				</feComponentTransfer>
			</filter>
		</defs>
	</svg>

	<?php 
    $duotone = ob_get_clean();
    // Like the layout hook, this assumes the hook only applies to blocks with a single wrapper.
    $content = preg_replace('/' . preg_quote('class="', '/') . '/', 'class="' . $duotone_id . ' ', $block_content, 1);
    return $content . $duotone;
}