Ankerlink an den Anfang der Seite
grafik
Blur backdrop Foto von Gabriele Lässer
Generic filters
Filter by Kategorien

Nicht jedes Projekt hat Beitragsbilder im exakt gleichen Format. In einem Projekt war’s besonders störend, da hatte jedes einzelne Bild ein anderes Seitenverhältnis. Man sollte aber dennoch jedes Bild im Loop „ganz“ sehen, nicht abgeschnitten. Die Idee, einfach jedes Bild mittig in ein Groupelement mit Hintergrund zu setzen sollte das Problem abfangen. Doch so richtig überzeugend sag das nicht aus. Dann erinnerte ich mich, mal einen digitalen Bilderrahmen gesehen zu haben, in dem das Verhältnis-Problem so gelöst worden war, dass vom verfügbaren Format abweichende Bilder auf einer blurried Version ihrer selbst dargestellt worden waren.

Aus dem

z03 9633Foto von Gabriele Lässer

sollte also das werden:

z03 9633Foto von Gabriele Lässer

Um das zu erreichen, erweiterte ich mein Theme um eine Stilvariante „Blur backdrop“.

	register_block_style(
		'core/post-featured-image',
		array(
			'name'  => 'blur-backdrop',
			'label' => __( 'Blur backdrop', 'webentwicklerin' ),
		)
	);

Das Rendering läuft über einen Filter: render_block_core/post-featured-image → webentwicklerin_featured_image_blur_backdrop

Wenn is-style-blur-backdrop nicht im HTML vorkommt passiert nichts. Es gibt drei Hilfsfunktionen, alle nur für diesen Stil:

webentwicklerin_featured_image_blur_backdrop_parse_padding — Padding vom <figure> auf CSS-Variablen

webentwicklerin_featured_image_blur_backdrop_inset_properties — baut --featured-image-inset-*

webentwicklerin_featured_image_blur_backdrop_fit_image — erzwingt object-fit: contain am <img>

Standard-Featured-Images ohne diesen Block-Stil bleiben wie bisher.

/**
 * Featured image block helpers.
 *
 * @package webentwicklerin
 * @since   2.0.0
 */

add_filter( 'render_block_core/post-featured-image', 'webentwicklerin_featured_image_blur_backdrop', 10, 2 );

/**
 * Pass the featured image URL to CSS and inject an isolated blur layer for the blur-backdrop style.
 *
 * No effect on featured images without the blur-backdrop block style.
 *
 * @since 2.0.0
 *
 * @param string $block_content Rendered block HTML.
 * @param array  $block         Block data.
 * @return string
 */
function webentwicklerin_featured_image_blur_backdrop( $block_content, $block ) {
	if ( false === strpos( $block_content, 'is-style-blur-backdrop' ) ) {
		return $block_content;
	}

	if ( ! preg_match( '/<img\b[^>]*\bsrc=(["\'])([^"\']+)\1/', $block_content, $matches ) ) {
		return $block_content;
	}

	$custom_property = sprintf( "--featured-image-url: url('%s');", esc_url( $matches[2] ) );

	$block_content = preg_replace_callback(
		'/<figure\b([^>]*)>/',
		function ( $figure_matches ) use ( $custom_property ) {
			$attributes = $figure_matches[1];

			if ( preg_match( '/\sstyle=(["\'])([^"\']*)\1/', $attributes, $style_matches ) ) {
				$quote           = $style_matches[1];
				$padding_parsed  = webentwicklerin_featured_image_blur_backdrop_parse_padding( $style_matches[2] );
				$inset_property  = webentwicklerin_featured_image_blur_backdrop_inset_properties( $padding_parsed['insets'] );
				$new_styles      = trim( $padding_parsed['styles'], '; ' );
				$new_styles      = '' !== $new_styles ? $new_styles . ';' : '';
				$new_styles     .= $inset_property . $custom_property;
				$attributes      = preg_replace(
					'/\sstyle=(["\'])[^"\']*\1/',
					' style=' . $quote . esc_attr( $new_styles ) . $quote,
					$attributes,
					1
				);
			} else {
				$inset_property  = webentwicklerin_featured_image_blur_backdrop_inset_properties(
					array(
						'top'    => '0',
						'right'  => '0',
						'bottom' => '0',
						'left'   => '0',
					)
				);
				$attributes     .= ' style="' . esc_attr( $inset_property . $custom_property ) . '"';
			}

			return '<figure' . $attributes . '>';
		},
		$block_content,
		1
	);

	$block_content = preg_replace(
		'/(<figure\b[^>]*>)/',
		'$1<span class="featured-image-blur-backdrop" aria-hidden="true"></span>',
		$block_content,
		1
	);

	return webentwicklerin_featured_image_blur_backdrop_fit_image( $block_content );
}

/**
 * Parse padding from inline styles and remove it from the figure element.
 *
 * Padding is moved to CSS custom properties so the blur layer fills the full frame
 * while block padding only insets the sharp image.
 *
 * @since 2.0.0
 *
 * @param string $styles Inline style attribute value.
 * @return array {
 *     @type array  $insets Side values for top, right, bottom, left.
 *     @type string $styles Remaining inline styles without padding.
 * }
 */
function webentwicklerin_featured_image_blur_backdrop_parse_padding( $styles ) {
	$insets = array(
		'top'    => '0',
		'right'  => '0',
		'bottom' => '0',
		'left'   => '0',
	);

	foreach ( array_keys( $insets ) as $side ) {
		$pattern = '/\bpadding-' . $side . '\s*:\s*([^;]+)/i';

		if ( preg_match( $pattern, $styles, $matches ) ) {
			$insets[ $side ] = trim( $matches[1] );
			$styles          = preg_replace( $pattern, '', $styles );
		}
	}

	if ( preg_match( '/\bpadding\s*:\s*([^;]+)/i', $styles, $matches ) ) {
		$parts = preg_split( '/\s+/', trim( $matches[1] ) );
		$count = count( $parts );

		if ( 1 === $count ) {
			$insets = array(
				'top'    => $parts[0],
				'right'  => $parts[0],
				'bottom' => $parts[0],
				'left'   => $parts[0],
			);
		} elseif ( 2 === $count ) {
			$insets = array(
				'top'    => $parts[0],
				'right'  => $parts[1],
				'bottom' => $parts[0],
				'left'   => $parts[1],
			);
		} elseif ( 3 === $count ) {
			$insets = array(
				'top'    => $parts[0],
				'right'  => $parts[1],
				'bottom' => $parts[2],
				'left'   => $parts[1],
			);
		} elseif ( 4 === $count ) {
			$insets = array(
				'top'    => $parts[0],
				'right'  => $parts[1],
				'bottom' => $parts[2],
				'left'   => $parts[3],
			);
		}

		$styles = preg_replace( '/\bpadding\s*:[^;]+;?/i', '', $styles );
	}

	$styles = trim( preg_replace( '/;+/', ';', $styles ), '; ' );

	return array(
		'insets' => $insets,
		'styles' => $styles,
	);
}

/**
 * Build CSS custom properties for image inset from block padding values.
 *
 * @since 2.0.0
 *
 * @param array $insets Padding values keyed by side.
 * @return string
 */
function webentwicklerin_featured_image_blur_backdrop_inset_properties( $insets ) {
	return sprintf(
		'--featured-image-inset-top:%1$s;--featured-image-inset-right:%2$s;--featured-image-inset-bottom:%3$s;--featured-image-inset-left:%4$s;',
		$insets['top'],
		$insets['right'],
		$insets['bottom'],
		$insets['left']
	);
}

/**
 * Fit the sharp image inside the figure frame at its natural proportions.
 *
 * WordPress already uses width/height 100% with object-fit when aspect ratio is set.
 * For blur-backdrop we always force contain so the figure frame defines the box.
 *
 * @since 2.0.0
 *
 * @param string $block_content Rendered block HTML.
 * @return string
 */
function webentwicklerin_featured_image_blur_backdrop_fit_image( $block_content ) {
	$fit_styles = 'width:100%;height:100%;object-fit:contain';

	return preg_replace_callback(
		'/<img\b([^>]*)\/?>/',
		static function ( $matches ) use ( $fit_styles ) {
			$attributes = $matches[1];

			if ( preg_match( '/\sstyle=(["\'])([^"\']*)\1/', $attributes, $style_matches ) ) {
				$styles = $style_matches[2];

				$styles = preg_replace( '/\bwidth\s*:\s*[^;]+;?/i', '', $styles );
				$styles = preg_replace( '/\bheight\s*:\s*[^;]+;?/i', '', $styles );
				$styles = preg_replace( '/\bobject-fit\s*:[^;]+;?/i', '', $styles );
				$styles = preg_replace( '/\baspect-ratio\s*:[^;]+;?/i', '', $styles );
				$styles = trim( $styles, '; ' );

				if ( '' !== $styles ) {
					$fit_styles = $styles . ';' . $fit_styles;
				}

				$attributes = preg_replace(
					'/\sstyle=(["\'])[^"\']*\1/',
					' style="' . esc_attr( $fit_styles ) . '"',
					$attributes,
					1
				);
			} else {
				$attributes .= ' style="' . esc_attr( $fit_styles ) . '"';
			}

			return '<img' . $attributes . '>';
		},
		$block_content,
		1
	);
}

CSS für Blur Backdrops

/* Blur-backdrop block style only (.is-style-blur-backdrop on Post Featured Image) */

.wp-block-post-featured-image.is-style-blur-backdrop {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  box-sizing: border-box;
  display: grid;
  width: 100%;
  max-width: 100%;
  min-width: 0;
  line-height: 0;
  --featured-image-blur: 15px;
}

.wp-block-post-featured-image.is-style-blur-backdrop::before {
  content: "";
  grid-area: 1 / 1;
  width: 100%;
  aspect-ratio: inherit;
  pointer-events: none;
}

.wp-block-post-featured-image.is-style-blur-backdrop .featured-image-blur-backdrop,
.wp-block-post-featured-image.is-style-blur-backdrop::after,
.wp-block-post-featured-image.is-style-blur-backdrop > a,
.wp-block-post-featured-image.is-style-blur-backdrop > img {
  grid-area: 1 / 1;
  min-width: 0;
  min-height: 0;
  width: 100%;
  height: 100%;
}

.wp-block-post-featured-image.is-style-blur-backdrop .featured-image-blur-backdrop {
  z-index: 0;
  box-sizing: border-box;
  background-image: var(--featured-image-url);
  background-size: cover;
  background-position: center;
  transform: scale(1.15);
  filter: blur(var(--featured-image-blur, 15px)) saturate(1.15);
  pointer-events: none;
}

.wp-block-post-featured-image.is-style-blur-backdrop::after {
  content: "";
  z-index: 0;
  box-sizing: border-box;
  background: rgba(0, 0, 0, 0.12);
  pointer-events: none;
}

.wp-block-post-featured-image.is-style-blur-backdrop > a {
  display: block !important;
  margin: 0 !important;
  width: 100%;
  height: 100%;
  max-width: 100%;
  text-decoration: none !important;
  overflow: hidden;
  box-sizing: border-box;
  z-index: 1;
  box-sizing: border-box;
  display: block !important;
  width: 100% !important;
  height: 100% !important;
  max-width: 100% !important;
  margin: 0 !important;
  padding-top: var(--featured-image-inset-top, 0);
  padding-right: var(--featured-image-inset-right, 0);
  padding-bottom: var(--featured-image-inset-bottom, 0);
  padding-left: var(--featured-image-inset-left, 0);
  line-height: 0;
  text-decoration: none !important;
  overflow: hidden;
}

.wp-block-post-featured-image.is-style-blur-backdrop > img {
  padding-top: var(--featured-image-inset-top, 0);
  padding-right: var(--featured-image-inset-right, 0);
  padding-bottom: var(--featured-image-inset-bottom, 0);
  padding-left: var(--featured-image-inset-left, 0);
}

.wp-block-post-featured-image.is-style-blur-backdrop img {
  position: relative;
  z-index: 1;
  box-sizing: border-box !important;
  display: block;
  width: 100% !important;
  height: 100% !important;
  margin: 0 !important;
  object-fit: contain !important;
  object-position: center center;
}

Das ist noch eine erste Rohfassung und benötigt vielleicht noch ein paar Anpassungen. Prinzipiell funktioniert sie.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Hinweis: Name und E-Mail-Adresse (beides optional, da alle Kommentare moderiert werden) werden dauerhaft gespeichert. Über dieses Formular kann jeder Zeit die Löschung persönlicher Daten oder Kommentare angefordert werden; die Anfrage wird nicht veröffentlicht und nach der Bearbeitung gelöscht. IP-Adressen, die mit Kommentaren gespeichert werden, werden nach zwei Monaten automatisch gelöscht.

Ihre Nachricht wird möglicherweise zur Spam-Prüfung an OpenAI (USA) weitergeleitet. Weitere Informationen finden Sie in unserer Datenschutzerklärung.