diff --git a/php-transformer/src/HtmlToBlocks/HtmlTransformer.php b/php-transformer/src/HtmlToBlocks/HtmlTransformer.php
index 37ab88a2..c493effd 100644
--- a/php-transformer/src/HtmlToBlocks/HtmlTransformer.php
+++ b/php-transformer/src/HtmlToBlocks/HtmlTransformer.php
@@ -422,7 +422,6 @@ private function createStyleResolutionContext(): StyleResolutionContext
fn (string $value): string => $this->cssComparableValue($value),
fn (string $selector): array => $this->parsedCssSelector($selector),
fn (string $className): string => $this->promotedClassName($className),
- fn (string $value, ?DOMElement $element = null): string => $this->resolveCssVariablesInValue($value, $element),
fn (string $url): string => $this->resolvedAssetImageUrl($url),
fn (string $id): string => $this->safeAnchor($id)
);
@@ -3519,7 +3518,7 @@ function (DOMElement $sourceElement, array $excludedTags): PatternConversionResu
new NavigationPatternContext(
$includeRuntimeDomTarget ? fn (DOMElement $sourceElement): bool => $this->runtimeIslands->isRuntimeDomTarget($sourceElement) : null,
fn (DOMElement $item, DOMElement $anchor): string => $this->navigationUnderlineColor($item, $anchor),
- fn (DOMElement $sourceElement): string => $this->resolveCssVariablesInValue($this->styleResolver->specificityResolvedPresentationStyle($sourceElement)),
+ fn (DOMElement $sourceElement): string => $this->styleResolver->resolveCssVariablesInValue($this->styleResolver->specificityResolvedPresentationStyle($sourceElement)),
fn (DOMElement $sourceElement): array => $this->navigationStyleProjector->navigationColorInteractionStates($sourceElement),
fn (DOMElement $sourceElement): string => $this->navigationToggleSuppressor->navigationOverlayMenu($sourceElement)
),
@@ -3539,7 +3538,7 @@ function (DOMElement $sourceElement, array $excludedTags): PatternConversionResu
),
new ButtonPatternContext(
fn (DOMElement $anchor): ?array => $this->fileBlockFromAnchor($anchor),
- fn (DOMElement $sourceElement): string => $this->resolveCssVariablesInValue($this->styleResolver->specificityResolvedPresentationStyle($sourceElement)),
+ fn (DOMElement $sourceElement): string => $this->styleResolver->resolveCssVariablesInValue($this->styleResolver->specificityResolvedPresentationStyle($sourceElement)),
fn (DOMElement $sourceElement): string => $this->richTextContentWithMaterializedInlineStyles($sourceElement),
fn (DOMElement $sourceElement, string $content): ?string => $this->richTextContentWithMaterializedSvgImages($sourceElement, $content),
fn (DOMElement $sourceElement, string $name): string => $this->attr($sourceElement, $name),
@@ -3606,7 +3605,7 @@ private function createProbePatternContext(): PatternContext
navigationContext: new NavigationPatternContext(
null,
fn (DOMElement $item, DOMElement $anchor): string => $this->navigationUnderlineColor($item, $anchor),
- fn (DOMElement $sourceElement): string => $this->resolveCssVariablesInValue($this->styleResolver->specificityResolvedPresentationStyle($sourceElement))
+ fn (DOMElement $sourceElement): string => $this->styleResolver->resolveCssVariablesInValue($this->styleResolver->specificityResolvedPresentationStyle($sourceElement))
),
markupContext: new MarkupPatternContext(
fn (DOMElement $sourceElement): string => $this->safeFallbackHtml($sourceElement),
@@ -4483,12 +4482,12 @@ private function applySyntheticHeaderAnchorCarrier(array &$attrs, DOMElement $an
foreach ( array( 'display', 'align-items', 'justify-content' ) as $property ) {
$value = trim((string) ($direct[$property] ?? ''));
if ( '' !== $value && ! str_contains(strtolower($value), '!important') ) {
- $declarations[$property] = $this->resolveCssVariablesInValue($value);
+ $declarations[$property] = $this->styleResolver->resolveCssVariablesInValue($value);
}
}
foreach ( $this->styleResolver->specificityResolvedGapDeclarations($anchor) as $property => $value ) {
if ( ! str_contains(strtolower($value), '!important') ) {
- $declarations[$property] = $this->resolveCssVariablesInValue($value);
+ $declarations[$property] = $this->styleResolver->resolveCssVariablesInValue($value);
}
}
if ( array() === $declarations ) {
@@ -4792,7 +4791,7 @@ private function sourceAnchorHasNoTextDecoration(DOMElement $anchor): bool
{
$decorationLine = null;
foreach ( $this->styleResolver->cssDeclarations($this->styleResolver->mergedPresentationStyle($anchor)) as $property => $value ) {
- $value = $this->cssComparableValue($this->resolveCssVariablesInValue($value));
+ $value = $this->cssComparableValue($this->styleResolver->resolveCssVariablesInValue($value));
if ( 'text-decoration' === $property ) {
if ( preg_match('/\b(?:underline|overline|line-through)\b/', $value) ) {
$decorationLine = 'line';
@@ -6981,7 +6980,7 @@ private function hasRenderableEmptyBlockBox(DOMElement $element): bool
{
$declarations = $this->styleResolver->structuralPresentationDeclarations($element);
foreach ( array( 'height', 'min-height', 'padding', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left' ) as $property ) {
- if ( isset($declarations[$property]) && $this->isPositiveCssLength($this->resolveCssVariablesInValue($declarations[$property], $element)) ) {
+ if ( isset($declarations[$property]) && $this->isPositiveCssLength($this->styleResolver->resolveCssVariablesInValue($declarations[$property], $element)) ) {
return true;
}
}
@@ -7162,8 +7161,8 @@ private function emptyVisualSpacerBlock(DOMElement $element): array
if ( array() !== $paint['style'] ) {
$attrs['style'] = array_replace_recursive($attrs['style'] ?? array(), $paint['style']);
}
- $attrs['height'] = $this->resolveCssVariablesInValue($declarations['height']);
- $attrs['width'] = $this->resolveCssVariablesInValue($declarations['width']);
+ $attrs['height'] = $this->styleResolver->resolveCssVariablesInValue($declarations['height']);
+ $attrs['width'] = $this->styleResolver->resolveCssVariablesInValue($declarations['width']);
return $this->createBlock('core/spacer', $attrs, array(), $element);
}
@@ -7200,7 +7199,7 @@ private function flankedSeparatorBlockFromElement(DOMElement $element): ?array
}
$height = SpacerPattern::heightFromStyle($this->attr($flank, 'style'));
- if ( '' === $height || ! $this->isPositiveCssLength($this->resolveCssVariablesInValue($height, $flank)) ) {
+ if ( '' === $height || ! $this->isPositiveCssLength($this->styleResolver->resolveCssVariablesInValue($height, $flank)) ) {
return null;
}
$margins[ $side ] = $height;
@@ -7227,7 +7226,7 @@ private function isEmptyVisualInlineCandidate(DOMElement $element): bool
private function hasExplicitEmptyVisualDimensions(array $declarations): bool
{
foreach ( array( 'width', 'height' ) as $property ) {
- if ( ! isset($declarations[$property]) || ! $this->isPositiveCssLength($this->resolveCssVariablesInValue($declarations[$property])) ) {
+ if ( ! isset($declarations[$property]) || ! $this->isPositiveCssLength($this->styleResolver->resolveCssVariablesInValue($declarations[$property])) ) {
return false;
}
}
@@ -7248,20 +7247,20 @@ private function isPositiveCssLength(string $value): bool
private function hasVisibleEmptyVisualPaint(array $declarations, ?DOMElement $element = null): bool
{
foreach ( array( 'background', 'background-color', 'box-shadow', 'outline' ) as $property ) {
- if ( isset($declarations[$property]) && $this->isVisibleEmptyVisualPaint($this->resolveCssVariablesInValue($declarations[$property], $element)) ) {
+ if ( isset($declarations[$property]) && $this->isVisibleEmptyVisualPaint($this->styleResolver->resolveCssVariablesInValue($declarations[$property], $element)) ) {
return true;
}
}
foreach ( array( 'border', 'border-top', 'border-right', 'border-bottom', 'border-left' ) as $property ) {
- if ( isset($declarations[$property]) && $this->isVisibleEmptyVisualBorder($this->resolveCssVariablesInValue($declarations[$property], $element)) ) {
+ if ( isset($declarations[$property]) && $this->isVisibleEmptyVisualBorder($this->styleResolver->resolveCssVariablesInValue($declarations[$property], $element)) ) {
return true;
}
}
return isset($declarations['border-color'], $declarations['border-width'])
- && $this->isVisibleEmptyVisualPaint($this->resolveCssVariablesInValue($declarations['border-color'], $element))
- && $this->isPositiveCssLength($this->resolveCssVariablesInValue($declarations['border-width'], $element));
+ && $this->isVisibleEmptyVisualPaint($this->styleResolver->resolveCssVariablesInValue($declarations['border-color'], $element))
+ && $this->isPositiveCssLength($this->styleResolver->resolveCssVariablesInValue($declarations['border-width'], $element));
}
private function isVisibleEmptyVisualPaint(string $value): bool
@@ -9414,7 +9413,7 @@ private function shouldPreserveEmptyVisualFigure(DOMElement $figure): bool
$declarations = $this->styleResolver->structuralPresentationDeclarations($figure);
$hasBoundedHeight = false;
foreach ( array( 'height', 'min-height' ) as $property ) {
- if ( isset($declarations[$property]) && $this->isPositiveCssLength($this->resolveCssVariablesInValue($declarations[$property], $figure)) ) {
+ if ( isset($declarations[$property]) && $this->isPositiveCssLength($this->styleResolver->resolveCssVariablesInValue($declarations[$property], $figure)) ) {
$hasBoundedHeight = true;
break;
}
diff --git a/php-transformer/src/HtmlToBlocks/Style/StyleResolutionContext.php b/php-transformer/src/HtmlToBlocks/Style/StyleResolutionContext.php
index a2209b79..164fb99d 100644
--- a/php-transformer/src/HtmlToBlocks/Style/StyleResolutionContext.php
+++ b/php-transformer/src/HtmlToBlocks/Style/StyleResolutionContext.php
@@ -30,7 +30,6 @@ final class StyleResolutionContext
* @param Closure(string): string $cssComparableValue
* @param Closure(string): array $parsedCssSelector
* @param Closure(string): string $promotedClassName
- * @param Closure(string, ?DOMElement): string $resolveCssVariablesInValue
* @param Closure(string): string $resolvedAssetImageUrl
* @param Closure(string): string $safeAnchor
*/
@@ -47,7 +46,6 @@ public function __construct(
private readonly Closure $cssComparableValue,
private readonly Closure $parsedCssSelector,
private readonly Closure $promotedClassName,
- private readonly Closure $resolveCssVariablesInValue,
private readonly Closure $resolvedAssetImageUrl,
private readonly Closure $safeAnchor
) {
@@ -116,11 +114,6 @@ public function promotedClassName(string $className): string
return ($this->promotedClassName)($className);
}
- public function resolveCssVariablesInValue(string $value, ?DOMElement $element = null): string
- {
- return ($this->resolveCssVariablesInValue)($value, $element);
- }
-
public function resolvedAssetImageUrl(string $url): string
{
return ($this->resolvedAssetImageUrl)($url);
diff --git a/php-transformer/src/HtmlToBlocks/Style/StyleResolver.php b/php-transformer/src/HtmlToBlocks/Style/StyleResolver.php
index 9928326b..676c5f5d 100644
--- a/php-transformer/src/HtmlToBlocks/Style/StyleResolver.php
+++ b/php-transformer/src/HtmlToBlocks/Style/StyleResolver.php
@@ -295,7 +295,7 @@ private function resolvedPresentationAttributes(
$declarations = $this->classOwnedBackgroundPaintDeclarations($element, $declarations);
$mapped = $this->styleAttributeMapper()->map(
$declarations,
- fn (string $value): string => $this->context->resolveCssVariablesInValue($value, $element)
+ fn (string $value): string => $this->resolveCssVariablesInValue($value, $element)
);
$forcedGeometryDeclarations = array() === $forcedGeometryProperties
? array()
@@ -762,7 +762,7 @@ private function inlineAuthorOverrideDeclarations(
): array {
$candidates = $this->styleAttributeMapper()->map(
$inlineDeclarations,
- fn (string $value): string => $this->context->resolveCssVariablesInValue($value, $element)
+ fn (string $value): string => $this->resolveCssVariablesInValue($value, $element)
)['leftover'] ?? array();
if ( isset($inlineDeclarations['box-shadow']) ) {
$candidates['box-shadow'] = $inlineDeclarations['box-shadow'];
@@ -1884,7 +1884,7 @@ public function authoredInheritedPropertyWinner(DOMElement $element, string $pro
return '';
}
- return $this->context->resolveCssVariablesInValue($value);
+ return $this->resolveCssVariablesInValue($value);
}
return '';
@@ -2824,4 +2824,52 @@ private function authorClassTokens(DOMElement $element): string
return implode(' ', array_filter($tokens, static fn (string $token): bool => '' !== $token && ! GeneratedGutenbergClassPolicy::isGeneratedClassName($token) && ! self::isTransformerMarkerClassName($token)));
}
+
+ /**
+ * Expand `var(--token)` references against source custom properties, with
+ * ancestor-declared properties layered over them when an element is given.
+ *
+ * Lives here rather than beside SVG materialization because it is CSS
+ * custom-property resolution and already depends on this resolver's own
+ * structural declarations.
+ */
+ public function resolveCssVariablesInValue(string $value, ?DOMElement $element = null): string
+ {
+ if ( false === strpos($value, 'var(') ) {
+ return $value;
+ }
+
+ $customProperties = $this->context->sourceStyles()->customProperties();
+ if ( $element instanceof DOMElement ) {
+ $ancestors = array();
+ for ( $current = $element; $current instanceof DOMElement; $current = $current->parentNode instanceof DOMElement ? $current->parentNode : null ) {
+ $ancestors[] = $current;
+ }
+ foreach ( array_reverse($ancestors) as $ancestor ) {
+ foreach ( $this->structuralPresentationDeclarations($ancestor) as $name => $propertyValue ) {
+ if ( str_starts_with($name, '--') ) {
+ $customProperties[$name] = $propertyValue;
+ }
+ }
+ }
+ }
+
+ for ( $pass = 0; $pass < 5; ++$pass ) {
+ $expanded = preg_replace_callback('/var\(\s*(--[A-Za-z0-9_-]+)\s*(?:,\s*([^()]*))?\)/', static function (array $matches) use ($customProperties): string {
+ $name = (string) $matches[1];
+ if ( isset($customProperties[$name]) && '' !== $customProperties[$name] ) {
+ return $customProperties[$name];
+ }
+
+ return isset($matches[2]) && '' !== trim((string) $matches[2]) ? trim((string) $matches[2]) : (string) $matches[0];
+ }, $value);
+
+ if ( ! is_string($expanded) || $expanded === $value ) {
+ break;
+ }
+ $value = $expanded;
+ }
+
+ return trim($value);
+ }
}
diff --git a/php-transformer/src/HtmlToBlocks/Support/SvgMaterializationTrait.php b/php-transformer/src/HtmlToBlocks/Support/SvgMaterializationTrait.php
index d8e03807..71e7727b 100644
--- a/php-transformer/src/HtmlToBlocks/Support/SvgMaterializationTrait.php
+++ b/php-transformer/src/HtmlToBlocks/Support/SvgMaterializationTrait.php
@@ -482,7 +482,7 @@ private function ensureSvgImageNamespace(string $html): string
private function resolveMaterializedSvgColors(string $html, DOMElement $element): string
{
- $html = $this->resolveCssVariablesInValue($html);
+ $html = $this->styleResolver->resolveCssVariablesInValue($html);
if ( false === stripos($html, 'currentColor') ) {
return $html;
}
@@ -498,7 +498,7 @@ private function inheritedSvgColor(DOMElement $element): string
continue;
}
- $color = $this->resolveCssVariablesInValue(trim((string) $declarations['color']));
+ $color = $this->styleResolver->resolveCssVariablesInValue(trim((string) $declarations['color']));
if ( '' !== $color && ! preg_match('/\bcurrentColor\b|var\s*\(|[<>]/i', $color) ) {
return $color;
}
@@ -507,45 +507,6 @@ private function inheritedSvgColor(DOMElement $element): string
return '#000000';
}
- private function resolveCssVariablesInValue(string $value, ?DOMElement $element = null): string
- {
- if ( false === strpos($value, 'var(') ) {
- return $value;
- }
-
- $customProperties = $this->sourceStyles()->customProperties();
- if ( $element instanceof DOMElement ) {
- $ancestors = array();
- for ( $current = $element; $current instanceof DOMElement; $current = $current->parentNode instanceof DOMElement ? $current->parentNode : null ) {
- $ancestors[] = $current;
- }
- foreach ( array_reverse($ancestors) as $ancestor ) {
- foreach ( $this->styleResolver->structuralPresentationDeclarations($ancestor) as $name => $propertyValue ) {
- if ( str_starts_with($name, '--') ) {
- $customProperties[$name] = $propertyValue;
- }
- }
- }
- }
-
- for ( $pass = 0; $pass < 5; ++$pass ) {
- $expanded = preg_replace_callback('/var\(\s*(--[A-Za-z0-9_-]+)\s*(?:,\s*([^()]*))?\)/', static function (array $matches) use ($customProperties): string {
- $name = (string) $matches[1];
- if ( isset($customProperties[$name]) && '' !== $customProperties[$name] ) {
- return $customProperties[$name];
- }
-
- return isset($matches[2]) && '' !== trim((string) $matches[2]) ? trim((string) $matches[2]) : (string) $matches[0];
- }, $value);
-
- if ( ! is_string($expanded) || $expanded === $value ) {
- break;
- }
- $value = $expanded;
- }
-
- return trim($value);
- }
/**
* @return array