'
+ . str_repeat('Body copy with a typography token.
', 6) . '';
+$plan = (new ArtifactCompiler())->compile(array('entrypoint' => 'index.html', 'files' => array('index.html' => $html)))->toArray()['source_reports']['wordpress_site_plan'];
+$css = '';
+$families = $palette = array();
+foreach ($plan['writes'] as $write) {
+ if ('theme.json' === $write['target_path']) {
+ $theme = json_decode($write['payload']['data'], true, 512, JSON_THROW_ON_ERROR);
+ $families = array_column($theme['settings']['typography']['fontFamilies'], 'fontFamily', 'slug');
+ $palette = array_column($theme['settings']['color']['palette'], 'color', 'slug');
+ }
+ if (str_ends_with($write['target_path'], '.css')) $css .= $write['payload']['data'];
+}
+$slug = array_search($stack, $families, true);
+$colorSlug = array_search('#2f2e2e', $palette, true);
+$assert(is_string($slug), 'The rendered literal stack has an exactly matching preset.');
+$assert(is_string($colorSlug), 'The rendered colour has an exactly matching preset.');
+// Print the relevant emitted declarations as before/after evidence, even on failure.
+preg_match_all('/--(?:t|unmatched):[^;}]+|color:var\(--wp--preset--color--[^;}]+/', $css, $definitions);
+echo implode("\n", array_unique($definitions[0])) . "\n";
+$assert(str_contains($css, '--unmatched:italic 700 21px / 1.4 Unknown Face,serif'), 'An unmatched shorthand stays literal.');
+$assert(str_contains($css, '--near:18px Example Light,serif'), 'Sharing only a primary family is not an exact stack match.');
+$assert(str_contains($css, '--spaced:italic 700 18px / 1.6em var(--wp--preset--font-family--' . $slug . ')!important'), 'Style, weight, size, line-height, spacing and priority survive family binding.');
+$assert(str_contains($css, 'color:var(--wp--preset--color--' . $colorSlug . ',rgb(var(--c)))'), 'Colour variable binding retains its original fallback.');
+$assert(str_contains($css, '--sh:0 0 2px var(--wp--preset--color--' . $colorSlug . ',#2f2e2e)'), 'A non-font custom property keeps its colour token binding.');
+$assert(str_contains($css, '--t:normal normal normal 18px/1.6em var(--wp--preset--font-family--' . $slug . ')'), 'Only the matching shorthand family is bound in place.');
+echo "Font shorthand custom property binding contract passed.\n";
diff --git a/php-transformer/tests/unit/font-shorthand-resolved-cascade.php b/php-transformer/tests/unit/font-shorthand-resolved-cascade.php
index 429f098e6..e8b8ac192 100644
--- a/php-transformer/tests/unit/font-shorthand-resolved-cascade.php
+++ b/php-transformer/tests/unit/font-shorthand-resolved-cascade.php
@@ -5,7 +5,7 @@
require dirname(__DIR__, 2) . '/vendor/autoload.php';
use Automattic\BlocksEngine\PhpTransformer\ArtifactCompiler\ArtifactCompiler;
-use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Style\CssFontShorthand;
+use Automattic\BlocksEngine\PhpTransformer\Css\CssFontShorthand;
use Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\Style\StyleResolver;
$assert = static function (bool $ok, string $message): void {
@@ -51,6 +51,14 @@
foreach (array('font-style' => 'italic', 'font-variant' => 'small-caps', 'font-weight' => 'bold', 'font-size' => '18px', 'line-height' => 'normal', 'font-family' => '"PT Sans",Arial,sans-serif') as $property => $value) {
$assert($value . ' !important' === ($expanded[$property] ?? null), 'expand ' . $property);
}
+foreach (array(
+ ' italic 700 18px / 1.6em "Example Light", Example, sans-serif',
+ 'normal normal normal 18px/1.6em Example Light,Example,sans-serif',
+) as $shorthand) {
+ $font = CssFontShorthand::expand($shorthand, false, $familyOffset);
+ $assert(isset($font['font-family']) && null !== $familyOffset, 'ordinary shorthands expose a family offset');
+ $assert(str_starts_with(substr($shorthand, $familyOffset), '"Example Light"') || str_starts_with(substr($shorthand, $familyOffset), 'Example Light'), 'family offset preserves the original prefix bytes');
+}
// The same block is read by both authored projection and cascade consumers.
$resolver = (new ReflectionClass(StyleResolver::class))->newInstanceWithoutConstructor();
foreach (array('font:bold 18px Arial' => 'bold 18px Arial') as $style => $font) {