diff --git a/php-transformer/resources/wordpress-6.6-core-block-supports.json b/php-transformer/resources/wordpress-6.6-core-block-supports.json new file mode 100644 index 00000000..36d1175b --- /dev/null +++ b/php-transformer/resources/wordpress-6.6-core-block-supports.json @@ -0,0 +1,2772 @@ +{ + "schema": "blocks-engine/php-transformer/core-block-supports/v1", + "source": "WordPress/wordpress-develop@6.6", + "block_count": 93, + "blocks": { + "core/archives": { + "align": true, + "html": false, + "spacing": { + "margin": true, + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/audio": { + "anchor": true, + "align": true, + "spacing": { + "margin": true, + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/avatar": { + "html": false, + "align": true, + "alignWide": false, + "spacing": { + "margin": true, + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } + }, + "__experimentalBorder": { + "__experimentalSkipSerialization": true, + "radius": true, + "width": true, + "color": true, + "style": true, + "__experimentalDefaultControls": { + "radius": true + } + }, + "color": { + "text": false, + "background": false, + "__experimentalDuotone": "img" + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/block": { + "customClassName": false, + "html": false, + "inserter": false, + "renaming": false, + "interactivity": { + "clientNavigation": true + } + }, + "core/button": { + "anchor": true, + "splitting": true, + "align": false, + "alignWide": false, + "color": { + "__experimentalSkipSerialization": true, + "gradients": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "reusable": false, + "shadow": { + "__experimentalSkipSerialization": true + }, + "spacing": { + "__experimentalSkipSerialization": true, + "padding": [ + "horizontal", + "vertical" + ], + "__experimentalDefaultControls": { + "padding": true + } + }, + "__experimentalBorder": { + "color": true, + "radius": true, + "style": true, + "width": true, + "__experimentalSkipSerialization": true, + "__experimentalDefaultControls": { + "color": true, + "radius": true, + "style": true, + "width": true + } + }, + "__experimentalSelector": ".wp-block-button .wp-block-button__link", + "interactivity": { + "clientNavigation": true + } + }, + "core/buttons": { + "anchor": true, + "align": [ + "wide", + "full" + ], + "html": false, + "__experimentalExposeControlsToChildren": true, + "spacing": { + "blockGap": true, + "margin": [ + "top", + "bottom" + ], + "__experimentalDefaultControls": { + "blockGap": true + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "layout": { + "allowSwitching": false, + "allowInheriting": false, + "default": { + "type": "flex" + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/calendar": { + "align": true, + "color": { + "link": true, + "__experimentalSkipSerialization": [ + "text", + "background" + ], + "__experimentalDefaultControls": { + "background": true, + "text": true + }, + "__experimentalSelector": "table, th" + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/categories": { + "align": true, + "html": false, + "spacing": { + "margin": true, + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/code": { + "align": [ + "wide" + ], + "anchor": true, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "spacing": { + "margin": [ + "top", + "bottom" + ], + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } + }, + "__experimentalBorder": { + "radius": true, + "color": true, + "width": true, + "style": true, + "__experimentalDefaultControls": { + "width": true, + "color": true + } + }, + "color": { + "text": true, + "background": true, + "gradients": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/column": { + "__experimentalOnEnter": true, + "anchor": true, + "reusable": false, + "html": false, + "color": { + "gradients": true, + "heading": true, + "button": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "shadow": true, + "spacing": { + "blockGap": true, + "padding": true, + "__experimentalDefaultControls": { + "padding": true, + "blockGap": true + } + }, + "__experimentalBorder": { + "color": true, + "style": true, + "width": true, + "__experimentalDefaultControls": { + "color": true, + "style": true, + "width": true + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "layout": true, + "interactivity": { + "clientNavigation": true + } + }, + "core/columns": { + "anchor": true, + "align": [ + "wide", + "full" + ], + "html": false, + "color": { + "gradients": true, + "link": true, + "heading": true, + "button": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "spacing": { + "blockGap": { + "__experimentalDefault": "2em", + "sides": [ + "horizontal", + "vertical" + ] + }, + "margin": [ + "top", + "bottom" + ], + "padding": true, + "__experimentalDefaultControls": { + "padding": true, + "blockGap": true + } + }, + "layout": { + "allowSwitching": false, + "allowInheriting": false, + "allowEditing": false, + "default": { + "type": "flex", + "flexWrap": "nowrap" + } + }, + "__experimentalBorder": { + "color": true, + "radius": true, + "style": true, + "width": true, + "__experimentalDefaultControls": { + "color": true, + "radius": true, + "style": true, + "width": true + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + }, + "shadow": true + }, + "core/comment-author-name": { + "html": false, + "spacing": { + "margin": true, + "padding": true + }, + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true, + "link": true + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/comment-content": { + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "spacing": { + "padding": [ + "horizontal", + "vertical" + ], + "__experimentalDefaultControls": { + "padding": true + } + }, + "html": false + }, + "core/comment-date": { + "html": false, + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true, + "link": true + } + }, + "spacing": { + "margin": true, + "padding": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/comment-edit-link": { + "html": false, + "color": { + "link": true, + "gradients": true, + "text": false, + "__experimentalDefaultControls": { + "background": true, + "link": true + } + }, + "spacing": { + "margin": true, + "padding": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/comment-reply-link": { + "color": { + "gradients": true, + "link": true, + "text": false, + "__experimentalDefaultControls": { + "background": true, + "link": true + } + }, + "spacing": { + "margin": true, + "padding": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "html": false + }, + "core/comment-template": { + "align": true, + "html": false, + "reusable": false, + "spacing": { + "margin": true, + "padding": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/comments": { + "align": [ + "wide", + "full" + ], + "html": false, + "color": { + "gradients": true, + "heading": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true, + "link": true + } + }, + "spacing": { + "margin": true, + "padding": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + } + }, + "core/comments-pagination": { + "align": true, + "reusable": false, + "html": false, + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true, + "link": true + } + }, + "layout": { + "allowSwitching": false, + "allowInheriting": false, + "default": { + "type": "flex" + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/comments-pagination-next": { + "reusable": false, + "html": false, + "color": { + "gradients": true, + "text": false, + "__experimentalDefaultControls": { + "background": true + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/comments-pagination-numbers": { + "reusable": false, + "html": false, + "color": { + "gradients": true, + "text": false, + "__experimentalDefaultControls": { + "background": true + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/comments-pagination-previous": { + "reusable": false, + "html": false, + "color": { + "gradients": true, + "text": false, + "__experimentalDefaultControls": { + "background": true + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/comments-title": { + "anchor": false, + "align": true, + "html": false, + "__experimentalBorder": { + "radius": true, + "color": true, + "width": true, + "style": true + }, + "color": { + "gradients": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "spacing": { + "margin": true, + "padding": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true, + "__experimentalFontFamily": true, + "__experimentalFontStyle": true, + "__experimentalFontWeight": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/cover": { + "anchor": true, + "align": true, + "html": false, + "shadow": true, + "spacing": { + "padding": true, + "margin": [ + "top", + "bottom" + ], + "blockGap": true, + "__experimentalDefaultControls": { + "padding": true, + "blockGap": true + } + }, + "__experimentalBorder": { + "color": true, + "radius": true, + "style": true, + "width": true, + "__experimentalDefaultControls": { + "color": true, + "radius": true, + "style": true, + "width": true + } + }, + "color": { + "__experimentalDuotone": "> .wp-block-cover__image-background, > .wp-block-cover__video-background", + "heading": true, + "text": true, + "background": false, + "__experimentalSkipSerialization": [ + "gradients" + ], + "enableContrastChecker": false + }, + "dimensions": { + "aspectRatio": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "layout": { + "allowJustification": false + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/details": { + "__experimentalOnEnter": true, + "align": [ + "wide", + "full" + ], + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "__experimentalBorder": { + "color": true, + "width": true, + "style": true + }, + "html": false, + "spacing": { + "margin": true, + "padding": true, + "blockGap": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "layout": { + "allowEditing": false + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/embed": { + "align": true, + "spacing": { + "margin": true + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/file": { + "anchor": true, + "align": true, + "spacing": { + "margin": true, + "padding": true + }, + "color": { + "gradients": true, + "link": true, + "text": false, + "__experimentalDefaultControls": { + "background": true, + "link": true + } + }, + "interactivity": true + }, + "core/footnotes": { + "__experimentalBorder": { + "radius": true, + "color": true, + "width": true, + "style": true, + "__experimentalDefaultControls": { + "radius": false, + "color": false, + "width": false, + "style": false + } + }, + "color": { + "background": true, + "link": true, + "text": true, + "__experimentalDefaultControls": { + "link": true, + "text": true + } + }, + "html": false, + "multiple": false, + "reusable": false, + "inserter": false, + "spacing": { + "margin": true, + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalTextDecoration": true, + "__experimentalFontStyle": true, + "__experimentalFontWeight": true, + "__experimentalLetterSpacing": true, + "__experimentalTextTransform": true, + "__experimentalWritingMode": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/freeform": { + "className": false, + "customClassName": false, + "reusable": false + }, + "core/gallery": { + "anchor": true, + "align": true, + "html": false, + "units": [ + "px", + "em", + "rem", + "vh", + "vw" + ], + "spacing": { + "margin": true, + "padding": true, + "blockGap": [ + "horizontal", + "vertical" + ], + "__experimentalSkipSerialization": [ + "blockGap" + ], + "__experimentalDefaultControls": { + "blockGap": true, + "margin": false, + "padding": false + } + }, + "color": { + "text": false, + "background": true, + "gradients": true + }, + "layout": { + "allowSwitching": false, + "allowInheriting": false, + "allowEditing": false, + "default": { + "type": "flex" + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/group": { + "__experimentalOnEnter": true, + "__experimentalOnMerge": true, + "__experimentalSettings": true, + "align": [ + "wide", + "full" + ], + "anchor": true, + "ariaLabel": true, + "html": false, + "background": { + "backgroundImage": true, + "backgroundSize": true, + "__experimentalDefaultControls": { + "backgroundImage": true + } + }, + "color": { + "gradients": true, + "heading": true, + "button": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "spacing": { + "margin": [ + "top", + "bottom" + ], + "padding": true, + "blockGap": true, + "__experimentalDefaultControls": { + "padding": true, + "blockGap": true + } + }, + "dimensions": { + "minHeight": true + }, + "__experimentalBorder": { + "color": true, + "radius": true, + "style": true, + "width": true, + "__experimentalDefaultControls": { + "color": true, + "radius": true, + "style": true, + "width": true + } + }, + "position": { + "sticky": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "layout": { + "allowSizingOnChildren": true + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/heading": { + "align": [ + "wide", + "full" + ], + "anchor": true, + "className": true, + "splitting": true, + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "spacing": { + "margin": true, + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontStyle": true, + "__experimentalFontWeight": true, + "__experimentalLetterSpacing": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalWritingMode": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "__unstablePasteTextInline": true, + "__experimentalSlashInserter": true, + "interactivity": { + "clientNavigation": true + } + }, + "core/home-link": { + "reusable": false, + "html": false, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/html": { + "customClassName": false, + "className": false, + "html": false, + "interactivity": { + "clientNavigation": true + } + }, + "core/image": { + "interactivity": true, + "align": [ + "left", + "center", + "right", + "wide", + "full" + ], + "anchor": true, + "color": { + "text": false, + "background": false + }, + "filter": { + "duotone": true + }, + "__experimentalBorder": { + "color": true, + "radius": true, + "width": true, + "__experimentalSkipSerialization": true, + "__experimentalDefaultControls": { + "color": true, + "radius": true, + "width": true + } + }, + "shadow": { + "__experimentalSkipSerialization": true + } + }, + "core/latest-comments": { + "align": true, + "html": false, + "spacing": { + "margin": true, + "padding": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/latest-posts": { + "align": true, + "html": false, + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true, + "link": true + } + }, + "spacing": { + "margin": true, + "padding": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/legacy-widget": { + "html": false, + "customClassName": false, + "reusable": false + }, + "core/list": { + "anchor": true, + "html": false, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "spacing": { + "margin": true, + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } + }, + "__unstablePasteTextInline": true, + "__experimentalOnMerge": true, + "__experimentalSlashInserter": true, + "interactivity": { + "clientNavigation": true + } + }, + "core/list-item": { + "className": false, + "__experimentalSelector": ".wp-block-list > li", + "splitting": true, + "spacing": { + "margin": true, + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/loginout": { + "className": true, + "spacing": { + "margin": true, + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/media-text": { + "anchor": true, + "align": [ + "wide", + "full" + ], + "html": false, + "color": { + "gradients": true, + "heading": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "spacing": { + "margin": true, + "padding": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/missing": { + "className": false, + "customClassName": false, + "inserter": false, + "html": false, + "reusable": false, + "interactivity": { + "clientNavigation": true + } + }, + "core/more": { + "customClassName": false, + "className": false, + "html": false, + "multiple": false, + "interactivity": { + "clientNavigation": true + } + }, + "core/navigation": { + "align": [ + "wide", + "full" + ], + "ariaLabel": true, + "html": false, + "inserter": true, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontStyle": true, + "__experimentalFontWeight": true, + "__experimentalTextTransform": true, + "__experimentalFontFamily": true, + "__experimentalLetterSpacing": true, + "__experimentalTextDecoration": true, + "__experimentalSkipSerialization": [ + "textDecoration" + ], + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "spacing": { + "blockGap": true, + "units": [ + "px", + "em", + "rem", + "vh", + "vw" + ], + "__experimentalDefaultControls": { + "blockGap": true + } + }, + "layout": { + "allowSwitching": false, + "allowInheriting": false, + "allowVerticalAlignment": false, + "allowSizingOnChildren": true, + "default": { + "type": "flex" + } + }, + "interactivity": true, + "renaming": false + }, + "core/navigation-link": { + "reusable": false, + "html": false, + "__experimentalSlashInserter": true, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "renaming": false, + "interactivity": { + "clientNavigation": true + } + }, + "core/navigation-submenu": { + "reusable": false, + "html": false, + "interactivity": { + "clientNavigation": true + } + }, + "core/nextpage": { + "customClassName": false, + "className": false, + "html": false, + "interactivity": { + "clientNavigation": true + } + }, + "core/page-list": { + "reusable": false, + "html": false, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/page-list-item": { + "reusable": false, + "html": false, + "lock": false, + "inserter": false, + "__experimentalToolbar": false, + "interactivity": { + "clientNavigation": true + } + }, + "core/paragraph": { + "splitting": true, + "anchor": true, + "className": false, + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "spacing": { + "margin": true, + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalTextDecoration": true, + "__experimentalFontStyle": true, + "__experimentalFontWeight": true, + "__experimentalLetterSpacing": true, + "__experimentalTextTransform": true, + "__experimentalWritingMode": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "__experimentalSelector": "p", + "__unstablePasteTextInline": true, + "interactivity": { + "clientNavigation": true + } + }, + "core/pattern": { + "html": false, + "inserter": false, + "renaming": false, + "interactivity": { + "clientNavigation": true + } + }, + "core/post-author": { + "html": false, + "spacing": { + "margin": true, + "padding": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "color": { + "gradients": true, + "link": true, + "__experimentalDuotone": ".wp-block-post-author__avatar img", + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/post-author-biography": { + "spacing": { + "margin": true, + "padding": true + }, + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/post-author-name": { + "html": false, + "spacing": { + "margin": true, + "padding": true + }, + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true, + "link": true + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/post-comments-form": { + "html": false, + "color": { + "gradients": true, + "heading": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "spacing": { + "margin": true, + "padding": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontStyle": true, + "__experimentalFontWeight": true, + "__experimentalLetterSpacing": true, + "__experimentalTextTransform": true, + "__experimentalDefaultControls": { + "fontSize": true + } + } + }, + "core/post-content": { + "align": [ + "wide", + "full" + ], + "html": false, + "layout": true, + "dimensions": { + "minHeight": true + }, + "spacing": { + "blockGap": true + }, + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": false, + "text": false + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + } + }, + "core/post-date": { + "html": false, + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true, + "link": true + } + }, + "spacing": { + "margin": true, + "padding": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/post-excerpt": { + "html": false, + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true, + "link": true + } + }, + "spacing": { + "margin": true, + "padding": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/post-featured-image": { + "align": [ + "left", + "right", + "center", + "wide", + "full" + ], + "color": { + "text": false, + "background": false + }, + "__experimentalBorder": { + "color": true, + "radius": true, + "width": true, + "__experimentalSkipSerialization": true, + "__experimentalDefaultControls": { + "color": true, + "radius": true, + "width": true + } + }, + "filter": { + "duotone": true + }, + "shadow": { + "__experimentalSkipSerialization": true + }, + "html": false, + "spacing": { + "margin": true, + "padding": true + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/post-navigation-link": { + "reusable": false, + "html": false, + "color": { + "link": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalWritingMode": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/post-template": { + "reusable": false, + "html": false, + "align": [ + "wide", + "full" + ], + "layout": true, + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "spacing": { + "blockGap": { + "__experimentalDefault": "1.25em" + }, + "__experimentalDefaultControls": { + "blockGap": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/post-terms": { + "html": false, + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true, + "link": true + } + }, + "spacing": { + "margin": true, + "padding": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/post-title": { + "align": [ + "wide", + "full" + ], + "html": false, + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true, + "link": true + } + }, + "spacing": { + "margin": true, + "padding": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/preformatted": { + "anchor": true, + "color": { + "gradients": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "spacing": { + "padding": true, + "margin": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/pullquote": { + "anchor": true, + "align": [ + "left", + "right", + "wide", + "full" + ], + "color": { + "gradients": true, + "background": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "spacing": { + "margin": true, + "padding": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "__experimentalBorder": { + "color": true, + "radius": true, + "style": true, + "width": true, + "__experimentalDefaultControls": { + "color": true, + "radius": true, + "style": true, + "width": true + } + }, + "__experimentalStyle": { + "typography": { + "fontSize": "1.5em", + "lineHeight": "1.6" + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/query": { + "align": [ + "wide", + "full" + ], + "html": false, + "layout": true, + "interactivity": true + }, + "core/query-no-results": { + "align": true, + "reusable": false, + "html": false, + "color": { + "gradients": true, + "link": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/query-pagination": { + "align": true, + "reusable": false, + "html": false, + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true, + "link": true + } + }, + "layout": { + "allowSwitching": false, + "allowInheriting": false, + "default": { + "type": "flex" + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/query-pagination-next": { + "reusable": false, + "html": false, + "color": { + "gradients": true, + "text": false, + "__experimentalDefaultControls": { + "background": true + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/query-pagination-numbers": { + "reusable": false, + "html": false, + "color": { + "gradients": true, + "text": false, + "__experimentalDefaultControls": { + "background": true + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/query-pagination-previous": { + "reusable": false, + "html": false, + "color": { + "gradients": true, + "text": false, + "__experimentalDefaultControls": { + "background": true + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/query-title": { + "align": [ + "wide", + "full" + ], + "html": false, + "color": { + "gradients": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "spacing": { + "margin": true, + "padding": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontStyle": true, + "__experimentalFontWeight": true, + "__experimentalLetterSpacing": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/quote": { + "anchor": true, + "html": false, + "__experimentalOnEnter": true, + "__experimentalOnMerge": true, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "color": { + "gradients": true, + "heading": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "layout": { + "allowEditing": false + }, + "spacing": { + "blockGap": true + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/read-more": { + "html": false, + "color": { + "gradients": true, + "text": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalLetterSpacing": true, + "__experimentalTextDecoration": true, + "__experimentalDefaultControls": { + "fontSize": true, + "textDecoration": true + } + }, + "spacing": { + "margin": [ + "top", + "bottom" + ], + "padding": true, + "__experimentalDefaultControls": { + "padding": true + } + }, + "__experimentalBorder": { + "color": true, + "radius": true, + "width": true, + "__experimentalDefaultControls": { + "width": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/rss": { + "align": true, + "html": false, + "interactivity": { + "clientNavigation": true + } + }, + "core/search": { + "align": [ + "left", + "center", + "right" + ], + "color": { + "gradients": true, + "__experimentalSkipSerialization": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "interactivity": true, + "typography": { + "__experimentalSkipSerialization": true, + "__experimentalSelector": ".wp-block-search__label, .wp-block-search__input, .wp-block-search__button", + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "__experimentalBorder": { + "color": true, + "radius": true, + "width": true, + "__experimentalSkipSerialization": true, + "__experimentalDefaultControls": { + "color": true, + "radius": true, + "width": true + } + }, + "html": false + }, + "core/separator": { + "anchor": true, + "align": [ + "center", + "wide", + "full" + ], + "color": { + "enableContrastChecker": false, + "__experimentalSkipSerialization": true, + "gradients": true, + "background": true, + "text": false, + "__experimentalDefaultControls": { + "background": true + } + }, + "spacing": { + "margin": [ + "top", + "bottom" + ] + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/shortcode": { + "className": false, + "customClassName": false, + "html": false + }, + "core/site-logo": { + "html": false, + "align": true, + "alignWide": false, + "color": { + "__experimentalDuotone": "img, .components-placeholder__illustration, .components-placeholder::before", + "text": false, + "background": false + }, + "spacing": { + "margin": true, + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/site-tagline": { + "align": [ + "wide", + "full" + ], + "html": false, + "color": { + "gradients": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "spacing": { + "margin": true, + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalFontStyle": true, + "__experimentalFontWeight": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/site-title": { + "align": [ + "wide", + "full" + ], + "html": false, + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true, + "link": true + } + }, + "spacing": { + "padding": true, + "margin": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalFontStyle": true, + "__experimentalFontWeight": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/social-link": { + "reusable": false, + "html": false, + "interactivity": { + "clientNavigation": true + } + }, + "core/social-links": { + "align": [ + "left", + "center", + "right" + ], + "anchor": true, + "__experimentalExposeControlsToChildren": true, + "layout": { + "allowSwitching": false, + "allowInheriting": false, + "allowVerticalAlignment": false, + "default": { + "type": "flex" + } + }, + "color": { + "enableContrastChecker": false, + "background": true, + "gradients": true, + "text": false, + "__experimentalDefaultControls": { + "background": false + } + }, + "spacing": { + "blockGap": [ + "horizontal", + "vertical" + ], + "margin": true, + "padding": true, + "units": [ + "px", + "em", + "rem", + "vh", + "vw" + ], + "__experimentalDefaultControls": { + "blockGap": true, + "margin": true, + "padding": false + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/spacer": { + "anchor": true, + "spacing": { + "margin": [ + "top", + "bottom" + ], + "__experimentalDefaultControls": { + "margin": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/table": { + "anchor": true, + "align": true, + "color": { + "__experimentalSkipSerialization": true, + "gradients": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "spacing": { + "margin": true, + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontStyle": true, + "__experimentalFontWeight": true, + "__experimentalLetterSpacing": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "__experimentalBorder": { + "__experimentalSkipSerialization": true, + "color": true, + "style": true, + "width": true, + "__experimentalDefaultControls": { + "color": true, + "style": true, + "width": true + } + }, + "__experimentalSelector": ".wp-block-table > table", + "interactivity": { + "clientNavigation": true + } + }, + "core/tag-cloud": { + "html": false, + "align": true, + "spacing": { + "margin": true, + "padding": true + }, + "typography": { + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalLetterSpacing": true + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/template-part": { + "align": true, + "html": false, + "reusable": false, + "renaming": false, + "interactivity": { + "clientNavigation": true + } + }, + "core/term-description": { + "align": [ + "wide", + "full" + ], + "html": false, + "color": { + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "spacing": { + "padding": true, + "margin": true + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontWeight": true, + "__experimentalFontStyle": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalLetterSpacing": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/text-columns": { + "inserter": false, + "interactivity": { + "clientNavigation": true + } + }, + "core/verse": { + "anchor": true, + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "typography": { + "fontSize": true, + "__experimentalFontFamily": true, + "lineHeight": true, + "__experimentalFontStyle": true, + "__experimentalFontWeight": true, + "__experimentalLetterSpacing": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalDefaultControls": { + "fontSize": true + } + }, + "spacing": { + "margin": true, + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } + }, + "__experimentalBorder": { + "radius": true, + "width": true, + "color": true, + "style": true + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/video": { + "anchor": true, + "align": true, + "spacing": { + "margin": true, + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } + }, + "interactivity": { + "clientNavigation": true + } + }, + "core/widget-group": { + "html": false, + "inserter": true, + "customClassName": true, + "reusable": false + } + } +} diff --git a/php-transformer/src/HtmlToBlocks/HtmlTransformer.php b/php-transformer/src/HtmlToBlocks/HtmlTransformer.php index 0125fd83..10a0b185 100644 --- a/php-transformer/src/HtmlToBlocks/HtmlTransformer.php +++ b/php-transformer/src/HtmlToBlocks/HtmlTransformer.php @@ -3648,6 +3648,7 @@ private function createBlock(string $name, array $attrs = array(), array $innerB $attrs['className'] = $this->mergeClassNames((string) ($attrs['className'] ?? ''), $nativeButtonMarker); $this->registerNativeButtonStyleRule($nativeButtonMarker, $hasNativeButtonColor ? $attrs : array(), $nativeButtonTextAlignment); } + $attrs = $this->applyDeclaredBorderSupport($name, $attrs, $sourceElement); $provenanceId = $this->nextSourceProvenanceId++; $this->recordPresentationProvenance($name, $attrs, $sourceElement); $this->recordStructureProvenance($name, $attrs, $sourceElement); @@ -3675,6 +3676,74 @@ private function createBlock(string $name, array $attrs = array(), array $innerB return $block; } + /** + * WordPress ignores style.border components that the registered block type + * does not declare. Keep supported components native; move only unsupported + * width/style/color values into the existing deterministic carrier. Border + * radius deliberately stays on the pre-existing native path unchanged. + * + * @param array $attrs + * @return array + */ + private function applyDeclaredBorderSupport(string $name, array $attrs, DOMElement $sourceElement): array + { + $border = is_array($attrs['style']['border'] ?? null) ? $attrs['style']['border'] : array(); + if ( array() === $border ) { + return $attrs; + } + + $fallback = array(); + foreach ( array( 'width', 'style', 'color' ) as $component ) { + if ( ! array_key_exists($component, $border) || $this->runtime->blockSupportsBorder($name, $component) ) { + continue; + } + $fallback[ $component ] = $border[ $component ]; + unset($border[ $component ]); + } + foreach ( array( 'top', 'right', 'bottom', 'left' ) as $side ) { + $sideBorder = is_array($border[ $side ] ?? null) ? $border[ $side ] : array(); + foreach ( array( 'width', 'style', 'color' ) as $component ) { + if ( ! array_key_exists($component, $sideBorder) || $this->runtime->blockSupportsBorder($name, $component) ) { + continue; + } + $fallback[ $side ][ $component ] = $sideBorder[ $component ]; + unset($sideBorder[ $component ]); + } + if ( array() === $sideBorder ) { + unset($border[ $side ]); + } else { + $border[ $side ] = $sideBorder; + } + } + + if ( array() === $fallback ) { + return $attrs; + } + + $fallbackStyle = $this->styleAttributeMapper()->serialize(array( 'border' => $fallback ))['style']; + $fallbackDeclarations = $this->cssDeclarations($fallbackStyle); + $carrier = $this->inlineGeometryClassName( + $sourceElement, + array(), + array_keys($fallbackDeclarations), + $fallbackDeclarations + ); + if ( '' !== $carrier ) { + $attrs['className'] = $this->mergeClassNames((string) ($attrs['className'] ?? ''), $carrier); + } + + if ( array() === $border ) { + unset($attrs['style']['border']); + } else { + $attrs['style']['border'] = $border; + } + if ( empty($attrs['style']) ) { + unset($attrs['style']); + } + + return $attrs; + } + /** * Project the inherited foreground because core/button supplies a default link * color. Text alignment uses the same scoped link rule for direct and inherited diff --git a/php-transformer/src/HtmlToBlocks/Style/StyleAttributeMapper.php b/php-transformer/src/HtmlToBlocks/Style/StyleAttributeMapper.php index 492d26f1..421c3aca 100644 --- a/php-transformer/src/HtmlToBlocks/Style/StyleAttributeMapper.php +++ b/php-transformer/src/HtmlToBlocks/Style/StyleAttributeMapper.php @@ -168,6 +168,24 @@ public function serialize(array $style): array if ( '' !== trim((string) ($border['radius'] ?? '')) ) { $declarations[] = 'border-radius:' . trim((string) $border['radius']); } + // No class for a per-side color. The core style engine gives `classnames` + // to the uniform `border.color` definition only, and `has-border-color` + // is an all-sides signal: core's block-library `common.css` ships + // `html :where(.has-border-color){border-style:solid}`, which would paint + // the three unauthored sides at the initial `medium` width in + // `currentColor` and grow the box by 6px. + foreach ( array( 'top', 'right', 'bottom', 'left' ) as $side ) { + $sideBorder = is_array($border[ $side ] ?? null) ? $border[ $side ] : array(); + if ( '' !== trim((string) ($sideBorder['color'] ?? '')) ) { + $declarations[] = 'border-' . $side . '-color:' . trim((string) $sideBorder['color']); + } + if ( '' !== trim((string) ($sideBorder['style'] ?? '')) ) { + $declarations[] = 'border-' . $side . '-style:' . trim((string) $sideBorder['style']); + } + if ( '' !== trim((string) ($sideBorder['width'] ?? '')) ) { + $declarations[] = 'border-' . $side . '-width:' . trim((string) $sideBorder['width']); + } + } // Match the core style engine: dimensions precede spacing in save(). $dimensions = is_array($style['dimensions'] ?? null) ? $style['dimensions'] : array(); @@ -448,64 +466,166 @@ private function boxSides(string $property, array $declarations, array &$consume private function border(array $declarations, array &$consumed): array { $border = array(); - $shorthand = $this->parseBorderShorthand((string) ($declarations['border'] ?? '')); - if ( isset($declarations['border']) ) { - $consumed['border'] = true; - } - - $width = trim((string) ($declarations['border-width'] ?? $shorthand['width'] ?? '')); - if ( '' === $width ) { - $width = $this->uniformBorderSideValue('width', $declarations, $consumed); - } - $style = strtolower(trim((string) ($declarations['border-style'] ?? $shorthand['style'] ?? ''))); - $colorValue = $this->cssColor((string) ($declarations['border-color'] ?? $shorthand['color'] ?? '')); - foreach ( array( 'border-width', 'border-style', 'border-color' ) as $name ) { - if ( isset($declarations[ $name ]) ) { + $positions = array_flip(array_keys($declarations)); + foreach ( array_keys($declarations) as $name ) { + if ( $this->isMappedBorderProperty($name) ) { $consumed[ $name ] = true; } } + $global = array(); + foreach ( array( 'width', 'style', 'color' ) as $component ) { + $global[ $component ] = $this->borderComponentCandidate($declarations, $positions, $component); + } + + $width = trim($global['width']['value']); + $style = strtolower(trim($global['style']['value'])); + $colorValue = $this->cssColor($global['color']['value']); + $noBorder = 'none' === $style || ( '' !== $width && (float) $width === 0.0 && '' === $colorValue && '' === $style ); if ( ! $noBorder ) { - if ( '' !== $width && (float) $width !== 0.0 ) { + if ( $global['width']['declared'] && '' !== $width && (float) $width !== 0.0 ) { $border['width'] = $width; } - if ( '' !== $style && 'none' !== $style ) { + if ( $global['style']['declared'] && '' !== $style && 'none' !== $style ) { $border['style'] = $style; } - if ( '' !== $colorValue ) { + if ( $global['color']['declared'] && '' !== $colorValue ) { $border['color'] = $colorValue; } } + $hasGlobalBorder = isset($border['width']) || isset($border['style']) || isset($border['color']); $radius = trim((string) ($declarations['border-radius'] ?? '')); if ( '' !== $radius ) { - $consumed['border-radius'] = true; - $border['radius'] = $radius; + $border['radius'] = $radius; } + foreach ( array( 'top', 'right', 'bottom', 'left' ) as $side ) { + $sideComponents = array(); + $sideDeclared = array(); + foreach ( array( 'width', 'style', 'color' ) as $component ) { + $candidate = $this->borderComponentCandidate($declarations, $positions, $component, $side); + if ( $candidate['index'] > $global[ $component ]['index'] ) { + $sideComponents[ $component ] = $candidate['value']; + $sideDeclared[ $component ] = $candidate['declared']; + } + } + + $sideWidth = trim((string) ($sideComponents['width'] ?? '')); + $sideStyle = strtolower(trim((string) ($sideComponents['style'] ?? ''))); + $sideColor = $this->cssColor((string) ($sideComponents['color'] ?? '')); + + $sideValues = array(); + $noSideBorder = 'none' === $sideStyle || ( '' !== $sideWidth && (float) $sideWidth === 0.0 && '' === $sideColor && '' === $sideStyle ); + if ( ! $noSideBorder || $hasGlobalBorder ) { + if ( ( ($sideDeclared['width'] ?? false) || $hasGlobalBorder ) && '' !== $sideWidth && ( (float) $sideWidth !== 0.0 || $hasGlobalBorder ) ) { + $sideValues['width'] = $sideWidth; + } + if ( ( ($sideDeclared['style'] ?? false) || $hasGlobalBorder ) && '' !== $sideStyle && ( 'none' !== $sideStyle || $hasGlobalBorder ) ) { + $sideValues['style'] = $sideStyle; + } + if ( ( ($sideDeclared['color'] ?? false) || $hasGlobalBorder ) && '' !== $sideColor ) { + $sideValues['color'] = $sideColor; + } + } + if ( array() !== $sideValues ) { + $border[ $side ] = $sideValues; + } + } + + $this->collapseUniformBorderSideComponent($border, 'width'); + return $border; } /** - * Collapse equal physical side values into the canonical border support. - * Unequal sides remain under author stylesheet ownership. + * Return the last authored global or per-side value for one border component. + * A shorthand participates even when it omits the component because CSS + * shorthands reset omitted values to their initial state. Such a substituted + * initial value is reported as `declared: false`: it settles precedence and + * cancels a border this mapper itself emits, but it is never authored, so + * callers must not serialize it. Materializing one would place an inline + * declaration the author never wrote above their own state rules — a + * `border: 2px solid transparent` base plus a `:hover { border-color }` rule + * would freeze at `currentColor`. * * @param array $declarations - * @param array $consumed + * @param array $positions + * @return array{value: string, index: int, declared: bool} */ - private function uniformBorderSideValue(string $property, array $declarations, array &$consumed): string + private function borderComponentCandidate(array $declarations, array $positions, string $component, string $side = ''): array { - $names = array_map(static fn (string $side): string => 'border-' . $side . '-' . $property, array( 'top', 'right', 'bottom', 'left' )); - $values = array_map(static fn (string $name): string => trim((string) ($declarations[ $name ] ?? '')), $names); - if ( in_array('', $values, true) || 1 !== count(array_unique($values)) ) { - return ''; + $shorthandName = '' === $side ? 'border' : 'border-' . $side; + $longhandName = $shorthandName . '-' . $component; + $candidate = array( 'value' => '', 'index' => -1, 'declared' => false ); + + if ( isset($declarations[ $shorthandName ]) ) { + $shorthand = $this->parseBorderShorthand($declarations[ $shorthandName ]); + $initialValues = array( + 'width' => 'medium', + 'style' => 'none', + 'color' => 'currentColor', + ); + $authored = array() !== $shorthand && isset($shorthand[ $component ]); + $candidate = array( + 'value' => array() === $shorthand ? '' : (string) ($shorthand[ $component ] ?? $initialValues[ $component ]), + 'index' => $positions[ $shorthandName ], + 'declared' => $authored, + ); + } + + if ( isset($declarations[ $longhandName ]) && $positions[ $longhandName ] > $candidate['index'] ) { + $candidate = array( + 'value' => $declarations[ $longhandName ], + 'index' => $positions[ $longhandName ], + 'declared' => true, + ); + } + + return $candidate; + } + + private function isMappedBorderProperty(string $name): bool + { + if ( in_array($name, array( 'border', 'border-width', 'border-style', 'border-color', 'border-radius' ), true) ) { + return true; + } + + return (bool) preg_match('/^border-(?:top|right|bottom|left)(?:-(?:width|style|color))?$/', $name); + } + + /** + * Collapse four equal physical side values into one canonical component and + * remove the now-redundant side objects. + * + * @param array $border + */ + private function collapseUniformBorderSideComponent(array &$border, string $component): void + { + $sides = array( 'top', 'right', 'bottom', 'left' ); + $values = array(); + foreach ( $sides as $side ) { + $sideBorder = is_array($border[ $side ] ?? null) ? $border[ $side ] : array(); + $value = trim((string) ($sideBorder[ $component ] ?? '')); + if ( '' === $value ) { + return; + } + $values[] = $value; } - foreach ( $names as $name ) { - $consumed[ $name ] = true; + if ( 1 !== count(array_unique($values)) ) { + return; + } + + foreach ( $sides as $side ) { + unset($border[ $side ][ $component ]); + if ( array() === $border[ $side ] ) { + unset($border[ $side ]); + } } - return $values[0]; + unset($border[ $component ]); + $border = array( $component => $values[0] ) + $border; } /** diff --git a/php-transformer/src/HtmlToBlocks/Style/StyleResolutionTrait.php b/php-transformer/src/HtmlToBlocks/Style/StyleResolutionTrait.php index 8749a241..a9cd2e29 100644 --- a/php-transformer/src/HtmlToBlocks/Style/StyleResolutionTrait.php +++ b/php-transformer/src/HtmlToBlocks/Style/StyleResolutionTrait.php @@ -1695,14 +1695,26 @@ private function safeVisualDeclarations(array $declarations): array 'background-size', 'aspect-ratio', 'border', + 'border-bottom', + 'border-bottom-color', + 'border-bottom-style', 'border-color', + 'border-left', + 'border-left-color', + 'border-left-style', 'border-radius', + 'border-right', + 'border-right-color', + 'border-right-style', 'border-style', 'border-bottom-width', 'border-collapse', 'border-left-width', 'border-right-width', 'border-spacing', + 'border-top', + 'border-top-color', + 'border-top-style', 'border-top-width', 'border-width', 'box-shadow', @@ -1775,6 +1787,10 @@ private function cssDeclarations(string $style): array $value = preg_replace('/\s+/', ' ', $value) ?? $value; $allowsImageUrl = in_array($name, array( 'background', 'background-image', 'list-style', 'list-style-image' ), true) && ! preg_match('/(?:expression\s*\(|javascript\s*:)/i', $value); if ( '' !== $name && '' !== $value && ( $allowsImageUrl || ! preg_match('/(?:expression\s*\(|javascript\s*:|url\s*\()/i', $value) ) ) { + // Keep the surviving declaration at its final authored position. + // Border shorthands and longhands reset one another in source + // order, so overwriting a prior key in place is not sufficient. + unset($declarations[$name]); $declarations[$name] = $value; } } diff --git a/php-transformer/src/WordPress/Runtime.php b/php-transformer/src/WordPress/Runtime.php index 36cdd20f..e955d196 100644 --- a/php-transformer/src/WordPress/Runtime.php +++ b/php-transformer/src/WordPress/Runtime.php @@ -55,6 +55,9 @@ final class Runtime */ private array $diagnostics = array(); + /** @var array>|null */ + private ?array $fallbackCoreBlockSupports = null; + public function hasWordPress(): bool { return $this->canParseBlocks() @@ -123,21 +126,41 @@ public function availableCoreBlockNames(): array } /** - * @return array + * Whether the registered block type declares support for one authored border + * component. WordPress still exposes border support under the historical + * `__experimentalBorder` key in block.json; accept the stabilized `border` + * key as well. Unknown block metadata fails closed so callers can retain the + * declaration through a CSS carrier instead of emitting an ignored attribute. */ - private function registeredCoreBlockNames(): array + public function blockSupportsBorder(string $blockName, string $component): bool { - if ( ! class_exists('WP_Block_Type_Registry') || ! method_exists('WP_Block_Type_Registry', 'get_instance') ) { - return array(); + if ( ! in_array($component, array( 'color', 'style', 'width' ), true) ) { + return false; } - $registry = \WP_Block_Type_Registry::get_instance(); - if ( ! is_object($registry) || ! method_exists($registry, 'get_all_registered') ) { - return array(); + $supports = $this->registeredBlockSupports($blockName); + if ( null === $supports ) { + $supports = $this->fallbackBlockSupports($blockName); + } + if ( null === $supports ) { + return false; } + $border = $supports['border'] ?? $supports['__experimentalBorder'] ?? false; + if ( true === $border ) { + return true; + } + + return is_array($border) && true === ($border[ $component ] ?? false); + } + + /** + * @return array + */ + private function registeredCoreBlockNames(): array + { $names = array(); - foreach ( $registry->get_all_registered() as $key => $blockType ) { + foreach ( $this->registeredBlockTypes() as $key => $blockType ) { $name = is_string($key) ? $key : ''; if ( '' === $name && is_object($blockType) && isset($blockType->name) && is_string($blockType->name) ) { $name = $blockType->name; @@ -154,6 +177,66 @@ private function registeredCoreBlockNames(): array return $names; } + /** + * @return array + */ + private function registeredBlockTypes(): array + { + if ( ! class_exists('WP_Block_Type_Registry') || ! method_exists('WP_Block_Type_Registry', 'get_instance') ) { + return array(); + } + + $registry = \WP_Block_Type_Registry::get_instance(); + if ( ! is_object($registry) || ! method_exists($registry, 'get_all_registered') ) { + return array(); + } + + $registered = $registry->get_all_registered(); + return is_array($registered) ? $registered : array(); + } + + /** + * Resolve support from the block type's registered declaration, never from a + * transformer-owned block-name allowlist. + * + * @return array|null + */ + private function registeredBlockSupports(string $blockName): ?array + { + foreach ( $this->registeredBlockTypes() as $key => $blockType ) { + $name = is_string($key) ? $key : ''; + if ( '' === $name && is_object($blockType) && isset($blockType->name) && is_string($blockType->name) ) { + $name = $blockType->name; + } + if ( $blockName !== $name || ! is_object($blockType) ) { + continue; + } + + return is_array($blockType->supports ?? null) ? $blockType->supports : array(); + } + + return null; + } + + /** + * Standalone transforms have no WP_Block_Type_Registry. Load the generated + * WordPress 6.6 declaration snapshot so the same block.json support check is + * still available. Live registered declarations always take precedence. + * + * @return array|null + */ + private function fallbackBlockSupports(string $blockName): ?array + { + if ( null === $this->fallbackCoreBlockSupports ) { + $path = dirname(__DIR__, 2) . '/resources/wordpress-6.6-core-block-supports.json'; + $registry = is_file($path) ? json_decode((string) file_get_contents($path), true) : null; + $this->fallbackCoreBlockSupports = is_array($registry['blocks'] ?? null) ? $registry['blocks'] : array(); + } + + $supports = $this->fallbackCoreBlockSupports[ $blockName ] ?? null; + return is_array($supports) ? $supports : null; + } + /** * @return array> */ diff --git a/php-transformer/tests/contract/runtime-no-wordpress.php b/php-transformer/tests/contract/runtime-no-wordpress.php index d1870c62..e5de6c76 100644 --- a/php-transformer/tests/contract/runtime-no-wordpress.php +++ b/php-transformer/tests/contract/runtime-no-wordpress.php @@ -12,6 +12,12 @@ assertSame(true, in_array('core/accordion', $availableCoreBlocks, true), 'Fallback native target metadata should include core/accordion.'); assertSame(true, in_array('core/icon', $availableCoreBlocks, true), 'Fallback native target metadata should include core/icon.'); assertSame(true, in_array('core/math', $availableCoreBlocks, true), 'Fallback native target metadata should include core/math.'); +assertSame(true, $runtime->blockSupportsBorder('core/group', 'width'), 'Standalone support resolution should load Group width support from the generated WordPress declaration snapshot.'); +assertSame(true, $runtime->blockSupportsBorder('core/group', 'style'), 'Standalone support resolution should load Group style support from the generated WordPress declaration snapshot.'); +assertSame(true, $runtime->blockSupportsBorder('core/group', 'color'), 'Standalone support resolution should load Group color support from the generated WordPress declaration snapshot.'); +assertSame(false, $runtime->blockSupportsBorder('core/quote', 'width'), 'Standalone support resolution should treat WordPress 6.6 Quote as lacking border support.'); +assertSame(true, $runtime->blockSupportsBorder('core/image', 'width'), 'Standalone support resolution should load Image width support from its declaration.'); +assertSame(false, $runtime->blockSupportsBorder('core/image', 'style'), 'Standalone support resolution should honor Image declarations that omit border style support.'); $blocks = $runtime->parseBlocks('

Hello

'); assertSame('core/paragraph', $blocks[0]['blockName'] ?? null, 'Fallback parser should parse serialized block comments.'); diff --git a/php-transformer/tests/contract/runtime-wordpress-stubs.php b/php-transformer/tests/contract/runtime-wordpress-stubs.php index 837fa953..0861f2cd 100644 --- a/php-transformer/tests/contract/runtime-wordpress-stubs.php +++ b/php-transformer/tests/contract/runtime-wordpress-stubs.php @@ -99,6 +99,17 @@ public function get_all_registered(): array 'plugin/card' => (object) array('name' => 'plugin/card'), (object) array('name' => 'core/math'), 'core/accordion' => (object) array('name' => 'core/accordion'), + 'core/group' => (object) array( + 'name' => 'core/group', + 'supports' => array( + '__experimentalBorder' => array( + 'color' => true, + 'style' => false, + 'width' => true, + ), + ), + ), + 'core/quote' => (object) array('name' => 'core/quote', 'supports' => array()), ); } } @@ -120,7 +131,10 @@ public function get_all_registered(): array assertSame('{"stub":{"path":"/demo"}}', $runtime->encodeJson(array('path' => '/demo')), 'Runtime should delegate JSON encoding to wp_json_encode().'); assertSame('stub html ', $runtime->escapeHtml(''), 'Runtime should delegate HTML escaping to esc_html().'); assertSame('stub attr "value"', $runtime->escapeAttribute('"value"'), 'Runtime should delegate attribute escaping to esc_attr().'); -assertSame(array('core/accordion', 'core/icon', 'core/math'), $runtime->availableCoreBlockNames(), 'Runtime should expose registered core block names as native targets.'); +assertSame(array('core/accordion', 'core/group', 'core/icon', 'core/math', 'core/quote'), $runtime->availableCoreBlockNames(), 'Runtime should expose registered core block names as native targets.'); +assertSame(true, $runtime->blockSupportsBorder('core/group', 'width'), 'Runtime should resolve border width from the registered Group declaration.'); +assertSame(false, $runtime->blockSupportsBorder('core/group', 'style'), 'Registered Group metadata should override the standalone snapshot component by component.'); +assertSame(false, $runtime->blockSupportsBorder('core/quote', 'width'), 'A registered Quote declaration without border support should fail closed.'); fwrite(STDOUT, "WordPress runtime stub contract passed.\n"); diff --git a/php-transformer/tests/packaging/install-proof.php b/php-transformer/tests/packaging/install-proof.php index e2b49a64..3e3b5fe5 100644 --- a/php-transformer/tests/packaging/install-proof.php +++ b/php-transformer/tests/packaging/install-proof.php @@ -40,6 +40,7 @@ 'docs/contracts/php-transformer-visual-parity-fixture.schema.json', 'docs/contracts/php-transformer-visual-parity-report.schema.json', 'docs/contracts/visual-parity-report.md', + 'resources/wordpress-6.6-core-block-supports.json', 'tools/visual-parity/package-lock.json', 'tools/visual-parity/package.json', 'tools/visual-parity/bin/visual-parity.mjs', @@ -53,11 +54,25 @@ exit(1); } } -$result = (new Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\HtmlTransformer())->transform('

Proof

')->toArray(); +$transformer = new Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks\HtmlTransformer(); +$result = $transformer->transform('

Proof

')->toArray(); if ('success' !== ($result['status'] ?? '') || '' === ($result['serialized_blocks'] ?? '')) { fwrite(STDERR, "php-transformer install proof failed\n"); exit(1); } +$group = $transformer->transform('

Native

')->toArray(); +$groupBorder = $group['blocks'][0]['attrs']['style']['border']['left'] ?? null; +if (array('width' => '2px', 'style' => 'solid', 'color' => 'red') !== $groupBorder) { + fwrite(STDERR, "php-transformer install proof failed native border support\n"); + exit(1); +} +$quote = $transformer->transform('
Fallback
')->toArray(); +$quoteAttrs = $quote['blocks'][0]['attrs'] ?? array(); +$quoteCss = implode("\n", array_map(static fn (array $asset): string => (string) ($asset['content'] ?? ''), $quote['assets'] ?? array())); +if (isset($quoteAttrs['style']['border']) || ! str_contains((string) ($quoteAttrs['className'] ?? ''), 'be-inline-geometry-') || ! str_contains($quoteCss, 'border-left-width:2px !important')) { + fwrite(STDERR, "php-transformer install proof failed unsupported border fallback\n"); + exit(1); +} PHP; run($proofRoot, array(PHP_BINARY, '-r', $smoke)); diff --git a/php-transformer/tests/unit/block-style-support-conversion.php b/php-transformer/tests/unit/block-style-support-conversion.php index 57d91d02..e81a26f3 100644 --- a/php-transformer/tests/unit/block-style-support-conversion.php +++ b/php-transformer/tests/unit/block-style-support-conversion.php @@ -54,7 +54,11 @@ 'border-style' => 'solid', 'border-color' => '#ffffff', )); -$assert('12.808px' === ($uniformBorder['style']['border']['width'] ?? ''), '8a: equal physical border widths collapse into canonical border support', json_encode($uniformBorder)); +$assert( + array( 'width' => '12.808px', 'style' => 'solid', 'color' => '#ffffff' ) === ($uniformBorder['style']['border'] ?? array()), + '8a: equal physical border widths collapse without redundant side objects', + json_encode($uniformBorder) +); $classBorderImage = ( new HtmlTransformer() )->transform( 'Portrait', @@ -374,6 +378,227 @@ $compoundCandidateProbe = ( new StaticStyleParityProbe() )->extract(StaticStyleParityRunner::candidateHtmlFromSerializedBlocks($compoundPaintMarkup), $compoundPaintCssAsset); $assert(0 < (int) ($compoundSourceProbe['summary']['styled_total'] ?? 0) && 0 < (int) ($compoundCandidateProbe['summary']['styled_total'] ?? 0), '62: layered background cascade case produces nonzero source and candidate style probes', json_encode(array($compoundSourceProbe['summary'] ?? array(), $compoundCandidateProbe['summary'] ?? array()))); +$amberQuoteHtml = '
Comfort is a result, never a method
'; +$amberQuoteResult = ( new HtmlTransformer() )->transform($amberQuoteHtml, array())->toArray(); +$amberQuote = $amberQuoteResult['blocks'][0] ?? array(); +$amberQuoteAttrs = is_array($amberQuote['attrs'] ?? null) ? $amberQuote['attrs'] : array(); +$amberQuoteCss = implode("\n", array_map(static fn (array $asset): string => 'css' === ($asset['kind'] ?? '') ? (string) ($asset['content'] ?? '') : '', $amberQuoteResult['assets'] ?? array())); +$amberQuoteLeftBorder = is_array($amberQuoteAttrs['style']['border']['left'] ?? null) ? $amberQuoteAttrs['style']['border']['left'] : array(); + +$assert( + 'core/quote' === ($amberQuote['blockName'] ?? '') + && '1.2rem' === ($amberQuoteAttrs['style']['spacing']['padding']['left'] ?? '') + && 'var(--head)' === ($amberQuoteAttrs['style']['typography']['fontFamily'] ?? '') + && '2.2rem' === ($amberQuoteAttrs['style']['typography']['fontSize'] ?? '') + && '700' === ($amberQuoteAttrs['style']['typography']['fontWeight'] ?? '') + && '-.02em' === ($amberQuoteAttrs['style']['typography']['letterSpacing'] ?? ''), + 'N1 setup: amber quote exercises native spacing and typography conversion', + json_encode($amberQuote) +); +$assert( + array( 'width' => '2px', 'style' => 'solid', 'color' => 'var(--secondary)' ) === $amberQuoteLeftBorder + || ( + str_contains($amberQuoteCss, 'border-left-color:var(--secondary) !important') + && str_contains($amberQuoteCss, 'border-left-style:solid !important') + && str_contains($amberQuoteCss, 'border-left-width:2px !important') + ), + 'N1: amber quote retains its authored left border through native support or a carrier', + json_encode(array( 'attrs' => $amberQuoteAttrs, 'css' => $amberQuoteCss )) +); + +$borderMapper = new StyleAttributeMapper(); +$shorthandBorder = $borderMapper->map(array( 'border' => '2px solid red' )); +$leftBorder = $borderMapper->map(array( 'border-left' => '2px solid var(--secondary)' )); +$individualBorder = $borderMapper->map(array( + 'border-width' => '3px', + 'border-style' => 'dashed', + 'border-color' => '#123456', +)); +$globalThenLeftBorder = $borderMapper->map(array( + 'border' => '4px dashed blue', + 'border-left' => '2px solid red', +)); +$leftThenGlobalBorder = $borderMapper->map(array( + 'border-left' => '2px solid red', + 'border' => '4px dashed blue', +)); +$globalThenNoLeftBorder = $borderMapper->map(array( + 'border' => '4px solid blue', + 'border-left' => 'none', +)); +$globalThenColorOnlyLeftBorder = $borderMapper->map(array( + 'border' => '4px solid blue', + 'border-left' => 'red', +)); +$assert( + array( 'width' => '2px', 'style' => 'solid', 'color' => 'red' ) === ($shorthandBorder['style']['border'] ?? array()), + 'N2: border shorthand maps to native width, style, and color', + json_encode($shorthandBorder) +); +$assert( + array( 'left' => array( 'width' => '2px', 'style' => 'solid', 'color' => 'var(--secondary)' ) ) === ($leftBorder['style']['border'] ?? array()), + 'N2: per-side border shorthand maps to the native left-side object', + json_encode($leftBorder) +); +$assert( + array( 'width' => '3px', 'style' => 'dashed', 'color' => '#123456' ) === ($individualBorder['style']['border'] ?? array()), + 'N2: individual border width, style, and color forms map natively', + json_encode($individualBorder) +); +$assert( + array( + 'width' => '4px', + 'style' => 'dashed', + 'color' => 'blue', + 'left' => array( 'width' => '2px', 'style' => 'solid', 'color' => 'red' ), + ) === ($globalThenLeftBorder['style']['border'] ?? array()), + 'N2: a later per-side shorthand overrides an earlier global shorthand', + json_encode($globalThenLeftBorder) +); +$assert( + array( 'width' => '4px', 'style' => 'dashed', 'color' => 'blue' ) === ($leftThenGlobalBorder['style']['border'] ?? array()), + 'N2: a later global shorthand resets an earlier per-side shorthand', + json_encode($leftThenGlobalBorder) +); +$assert( + 'none' === ($globalThenNoLeftBorder['style']['border']['left']['style'] ?? ''), + 'N2: a later none side shorthand actively cancels an earlier global border', + json_encode($globalThenNoLeftBorder) +); +$assert( + array( 'width' => 'medium', 'style' => 'none', 'color' => 'red' ) === ($globalThenColorOnlyLeftBorder['style']['border']['left'] ?? array()), + 'N2: a partial side shorthand resets omitted components instead of inheriting the global border', + json_encode($globalThenColorOnlyLeftBorder) +); + +$nativeBorderGroupResult = ( new HtmlTransformer() )->transform( + '

Native border

', + array() +)->toArray(); +$nativeBorderGroup = $nativeBorderGroupResult['blocks'][0] ?? array(); +$nativeBorderGroupAttrs = is_array($nativeBorderGroup['attrs'] ?? null) ? $nativeBorderGroup['attrs'] : array(); +$nativeBorderGroupMarkup = (string) ($nativeBorderGroupResult['serialized_blocks'] ?? ''); +$assert( + 'core/group' === ($nativeBorderGroup['blockName'] ?? '') + && array( 'width' => '2px', 'style' => 'solid', 'color' => 'var(--secondary)' ) === ($nativeBorderGroupAttrs['style']['border']['left'] ?? array()) + && ! str_contains((string) ($nativeBorderGroupAttrs['className'] ?? ''), 'be-inline-geometry-') + && str_contains($nativeBorderGroupMarkup, 'border-left-color:var(--secondary)') + && str_contains($nativeBorderGroupMarkup, 'border-left-style:solid') + && str_contains($nativeBorderGroupMarkup, 'border-left-width:2px'), + 'N2: a border-supporting Group serializes the native per-side border', + json_encode(array( 'block' => $nativeBorderGroup, 'markup' => $nativeBorderGroupMarkup )) +); + +$matchedNativeBorderGroupResult = ( new HtmlTransformer() )->transform( + '

Matched native border

', + array( 'static_css' => '.matched-border{border-left:3px dotted #123456}' ) +)->toArray(); +$matchedNativeBorderGroup = $matchedNativeBorderGroupResult['blocks'][0] ?? array(); +$matchedNativeBorderGroupAttrs = is_array($matchedNativeBorderGroup['attrs'] ?? null) ? $matchedNativeBorderGroup['attrs'] : array(); +$assert( + array( 'width' => '3px', 'style' => 'dotted', 'color' => '#123456' ) === ($matchedNativeBorderGroupAttrs['style']['border']['left'] ?? array()) + && ! str_contains((string) ($matchedNativeBorderGroupAttrs['className'] ?? ''), 'be-inline-geometry-'), + 'N2: matched stylesheet per-side shorthand reaches native Group border support', + json_encode($matchedNativeBorderGroupResult) +); + +$repeatedBorderGroupResult = ( new HtmlTransformer() )->transform( + '

Ordered native border

', + array() +)->toArray(); +$repeatedBorderGroupAttrs = $repeatedBorderGroupResult['blocks'][0]['attrs'] ?? array(); +$assert( + '4px' === ($repeatedBorderGroupAttrs['style']['border']['width'] ?? '') + && 'dashed' === ($repeatedBorderGroupAttrs['style']['border']['style'] ?? '') + && 'blue' === ($repeatedBorderGroupAttrs['style']['border']['color'] ?? '') + && array( 'width' => '1px', 'style' => 'dotted', 'color' => 'green' ) === ($repeatedBorderGroupAttrs['style']['border']['left'] ?? array()), + 'N2: a repeated side shorthand after a global shorthand keeps its final authored position', + json_encode($repeatedBorderGroupResult) +); + +$radiusRoundTrip = $borderMapper->map(array( 'border-radius' => 'var(--radius)' )); +$assert( + array( 'radius' => 'var(--radius)' ) === ($radiusRoundTrip['style']['border'] ?? array()) + && 'border-radius:var(--radius)' === $borderMapper->serialize($radiusRoundTrip['style'])['style'], + 'N3: existing radius mapping and serialization round-trip unchanged', + json_encode($radiusRoundTrip) +); + +$assert( + ! isset($amberQuoteAttrs['style']['border']) + && str_contains((string) ($amberQuoteAttrs['className'] ?? ''), 'be-inline-geometry-') + && str_contains($amberQuoteCss, 'border-left-color:var(--secondary) !important') + && str_contains($amberQuoteCss, 'border-left-style:solid !important') + && str_contains($amberQuoteCss, 'border-left-width:2px !important'), + 'N4: a Quote without declared border support uses the carrier and emits no ignored native border attribute', + json_encode(array( 'attrs' => $amberQuoteAttrs, 'css' => $amberQuoteCss )) +); + +// The core style engine attaches `has-border-color` to the uniform +// `border.color` definition only; `border.{side}` carries no classnames. The +// class is an all-sides signal because core ships +// `html :where(.has-border-color){border-style:solid}` in +// wp-includes/css/dist/block-library/common.css. Emitting it for a one-sided +// authored border makes WordPress paint the three unauthored sides at the +// initial `medium` width (3px) in `currentColor`, growing the box by 6px. +$sideOnlyBorderSerialized = $borderMapper->serialize(array( + 'border' => array( 'left' => array( 'width' => '2px', 'style' => 'solid', 'color' => 'var(--secondary)' ) ), +)); +$assert( + '' === $sideOnlyBorderSerialized['classes'], + 'N5: a per-side border color emits no all-sides has-border-color class', + json_encode($sideOnlyBorderSerialized) +); +$assert( + 'border-left-color:var(--secondary);border-left-style:solid;border-left-width:2px' === $sideOnlyBorderSerialized['style'], + 'N5: a per-side border still serializes its own side declarations', + json_encode($sideOnlyBorderSerialized) +); + +$uniformBorderSerialized = $borderMapper->serialize(array( + 'border' => array( 'width' => '2px', 'style' => 'solid', 'color' => 'red' ), +)); +$assert( + 'has-border-color' === $uniformBorderSerialized['classes'], + 'N5: a uniform border color keeps the core has-border-color class', + json_encode($uniformBorderSerialized) +); + +$assert( + ! str_contains($nativeBorderGroupMarkup, 'has-border-color'), + 'N5: a Group with only an authored left border serializes without has-border-color', + $nativeBorderGroupMarkup +); + +// A shorthand that omits a component resets it to its initial value, but that +// value is not authored. Serializing it would put an inline declaration the +// author never wrote above their own state rules: `.card{border:2px solid +// transparent}` plus `.card:hover{border-color:var(--x)}` would freeze the card +// at `currentColor`. The substituted initial value settles precedence only. +$transparentShorthandBorder = $borderMapper->map(array( 'border' => '2px solid transparent' )); +$assert( + array( 'width' => '2px', 'style' => 'solid' ) === ($transparentShorthandBorder['style']['border'] ?? array()), + 'N6: a shorthand whose color is unusable emits no substituted currentColor', + json_encode($transparentShorthandBorder) +); +$colorlessShorthandBorder = $borderMapper->map(array( 'border' => '1px solid' )); +$assert( + array( 'width' => '1px', 'style' => 'solid' ) === ($colorlessShorthandBorder['style']['border'] ?? array()), + 'N6: a shorthand that omits the color emits no substituted currentColor', + json_encode($colorlessShorthandBorder) +); +$colorlessSideShorthandBorder = $borderMapper->map(array( 'border-bottom' => '1px solid' )); +$assert( + array( 'bottom' => array( 'width' => '1px', 'style' => 'solid' ) ) === ($colorlessSideShorthandBorder['style']['border'] ?? array()), + 'N6: a per-side shorthand that omits the color emits no substituted currentColor', + json_encode($colorlessSideShorthandBorder) +); +$assert( + array( 'width' => 'medium', 'style' => 'none', 'color' => 'red' ) === ($globalThenColorOnlyLeftBorder['style']['border']['left'] ?? array()), + 'N6: substituted initial values still cancel a global border this mapper emits', + json_encode($globalThenColorOnlyLeftBorder) +); + if ( $failures > 0 ) { fwrite(STDERR, "Block style support conversion tests: {$failures} failed, {$passes} passed\n"); exit(1); diff --git a/php-transformer/tools/generate-core-block-supports.php b/php-transformer/tools/generate-core-block-supports.php new file mode 100644 index 00000000..cc79ce5f --- /dev/null +++ b/php-transformer/tools/generate-core-block-supports.php @@ -0,0 +1,60 @@ + + */ + +if ( 4 !== count($argv) ) { + fwrite(STDERR, "Usage: php tools/generate-core-block-supports.php \n"); + exit(2); +} + +$blocksDirectory = rtrim($argv[1], DIRECTORY_SEPARATOR); +$outputPath = $argv[2]; +$sourceRef = trim($argv[3]); +if ( ! is_dir($blocksDirectory) || '' === $sourceRef ) { + fwrite(STDERR, "A readable WordPress blocks directory and non-empty source ref are required.\n"); + exit(2); +} + +$blocks = array(); +foreach ( (array) glob($blocksDirectory . '/*/block.json') as $blockJsonPath ) { + $metadata = json_decode((string) file_get_contents($blockJsonPath), true); + if ( ! is_array($metadata) || ! is_string($metadata['name'] ?? null) || ! str_starts_with($metadata['name'], 'core/') ) { + continue; + } + + $blocks[ $metadata['name'] ] = is_array($metadata['supports'] ?? null) ? $metadata['supports'] : array(); +} +ksort($blocks, SORT_STRING); + +$registry = array( + 'schema' => 'blocks-engine/php-transformer/core-block-supports/v1', + 'source' => $sourceRef, + 'block_count' => count($blocks), + 'blocks' => $blocks, +); +$json = json_encode($registry, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); +if ( false === $json ) { + fwrite(STDERR, "Unable to encode the core-block support registry.\n"); + exit(1); +} + +$outputDirectory = dirname($outputPath); +if ( ! is_dir($outputDirectory) && ! mkdir($outputDirectory, 0777, true) && ! is_dir($outputDirectory) ) { + fwrite(STDERR, "Unable to create output directory: {$outputDirectory}\n"); + exit(1); +} +if ( false === file_put_contents($outputPath, $json . "\n") ) { + fwrite(STDERR, "Unable to write core-block support registry: {$outputPath}\n"); + exit(1); +} + +fwrite(STDOUT, sprintf("Core block support registry: %d declarations written to %s\n", count($blocks), $outputPath));