@@ -990,6 +990,7 @@ private function process_pseudo_selectors( $node, $base_selector, $settings, $bl
990990 if ( is_array ( $ block_metadata ) ) {
991991 $ feature_declarations = $ this ->get_feature_declarations_for_node ( $ block_metadata , $ pseudo_node );
992992 $ feature_declarations = static ::update_paragraph_text_indent_selector ( $ feature_declarations , $ settings , $ block_name );
993+ $ feature_declarations = static ::update_button_width_declarations ( $ feature_declarations , $ settings );
993994
994995 foreach ( $ feature_declarations as $ feature_selector => $ declarations ) {
995996 $ target_selector = is_array ( $ style_variation )
@@ -3405,6 +3406,88 @@ private static function update_paragraph_text_indent_selector( $feature_declarat
34053406 return $ feature_declarations ;
34063407 }
34073408
3409+ /**
3410+ * Updates button width declarations to use a calc() formula for percentage values.
3411+ *
3412+ * When a percentage width is set on the Button block via Global Styles, the
3413+ * resulting CSS needs to account for block gap spacing so that buttons tile
3414+ * correctly on a row (e.g. 4 buttons at 25% width all fit on one row).
3415+ *
3416+ * This mirrors the dynamic calc() formula applied at the block instance level
3417+ * in the button block's stylesheet (style.scss).
3418+ *
3419+ * @since 7.1.0
3420+ *
3421+ * @param array $feature_declarations The feature declarations keyed by selector.
3422+ * @param array $settings The theme.json settings.
3423+ * @return array The updated feature declarations.
3424+ */
3425+ private static function update_button_width_declarations ( $ feature_declarations , $ settings ) {
3426+ if ( ! isset ( $ feature_declarations ['.wp-block-button ' ] ) ) {
3427+ return $ feature_declarations ;
3428+ }
3429+
3430+ foreach ( $ feature_declarations ['.wp-block-button ' ] as &$ declaration ) {
3431+ if ( 'width ' !== $ declaration ['name ' ] || ! isset ( $ declaration ['value ' ] ) ) {
3432+ continue ;
3433+ }
3434+
3435+ $ value = $ declaration ['value ' ];
3436+ $ percentage = null ;
3437+
3438+ // Case 1: Direct percentage value e.g. "25%".
3439+ if ( is_string ( $ value ) && str_ends_with ( $ value , '% ' ) ) {
3440+ $ percentage = (float ) $ value ;
3441+ }
3442+
3443+ // Case 2: Preset CSS var e.g. "var(--wp--preset--dimension--50)".
3444+ if ( null === $ percentage && is_string ( $ value ) && str_starts_with ( $ value , 'var(--wp--preset--dimension-- ' ) ) {
3445+ // Extract the slug from the var name.
3446+ $ slug = substr ( $ value , strlen ( 'var(--wp--preset--dimension-- ' ), -1 );
3447+
3448+ /*
3449+ * Look up the preset size across all origins.
3450+ * Check block-level settings first (core/button), then top-level settings.
3451+ */
3452+ $ dimension_sizes = ( $ settings ['blocks ' ]['core/button ' ]['dimensions ' ]['dimensionSizes ' ] ?? array () )
3453+ + ( $ settings ['dimensions ' ]['dimensionSizes ' ] ?? array () );
3454+ foreach ( $ dimension_sizes as $ origin_sizes ) {
3455+ if ( ! is_array ( $ origin_sizes ) ) {
3456+ continue ;
3457+ }
3458+ foreach ( $ origin_sizes as $ preset ) {
3459+ if ( isset ( $ preset ['slug ' ] ) && $ slug === $ preset ['slug ' ] && isset ( $ preset ['size ' ] ) ) {
3460+ $ size = $ preset ['size ' ];
3461+ if ( is_string ( $ size ) && str_ends_with ( $ size , '% ' ) ) {
3462+ $ percentage = (float ) $ size ;
3463+ }
3464+ break 2 ;
3465+ }
3466+ }
3467+ }
3468+ }
3469+
3470+ if ( null === $ percentage ) {
3471+ continue ;
3472+ }
3473+
3474+ /*
3475+ * Apply the same calc() formula as the block instance level (style.scss).
3476+ * The numeric percentage value is used as a unitless number:
3477+ * - Multiplied by 1% to get the percentage width.
3478+ * - Divided by 100 to calculate the gap adjustment proportion.
3479+ */
3480+ $ declaration ['value ' ] = sprintf (
3481+ 'calc(%s * 1%% - (var(--wp--style--block-gap, 0.5em) * (1 - %s / 100))) ' ,
3482+ $ percentage ,
3483+ $ percentage
3484+ );
3485+ }
3486+ unset( $ declaration );
3487+
3488+ return $ feature_declarations ;
3489+ }
3490+
34083491 /**
34093492 * An internal method to get the block nodes from a theme.json file.
34103493 *
@@ -3728,6 +3811,9 @@ public function get_styles_for_block( $block_metadata ) {
37283811 $ feature_declarations = static ::update_paragraph_text_indent_selector ( $ feature_declarations , $ settings , $ block_name );
37293812 $ block_elements = $ block_metadata ['elements ' ] ?? array ();
37303813
3814+ // Update button width declarations for percentage values to use calc() with block gap.
3815+ $ feature_declarations = static ::update_button_width_declarations ( $ feature_declarations , $ settings );
3816+
37313817 // If there are style variations, generate the declarations for them, including any feature selectors the block may have.
37323818 $ style_variation_declarations = array ();
37333819 $ style_variation_custom_css = array ();
@@ -3744,6 +3830,9 @@ public function get_styles_for_block( $block_metadata ) {
37443830 // Update text indent selector for paragraph blocks based on the textIndent setting.
37453831 $ variation_declarations = static ::update_paragraph_text_indent_selector ( $ variation_declarations , $ settings , $ block_name );
37463832
3833+ // Update button width declarations for percentage values to use calc() with block gap.
3834+ $ variation_declarations = static ::update_button_width_declarations ( $ variation_declarations , $ settings );
3835+
37473836 // Combine selectors with style variation's selector and add to overall style variation declarations.
37483837 foreach ( $ variation_declarations as $ current_selector => $ new_declarations ) {
37493838 $ combined_selectors = static ::get_block_style_variation_feature_selector ( $ style_variation , $ current_selector );
@@ -3798,6 +3887,7 @@ public function get_styles_for_block( $block_metadata ) {
37983887 // Process feature-level declarations for this breakpoint.
37993888 $ breakpoint_feature_declarations = static ::get_feature_declarations_for_node ( $ block_metadata , $ breakpoint_node );
38003889 $ breakpoint_feature_declarations = static ::update_paragraph_text_indent_selector ( $ breakpoint_feature_declarations , $ settings , $ block_name );
3890+ $ breakpoint_feature_declarations = static ::update_button_width_declarations ( $ breakpoint_feature_declarations , $ settings );
38013891 foreach ( $ breakpoint_feature_declarations as $ feature_selector => $ feature_decl ) {
38023892 $ combined_selectors = static ::get_block_style_variation_feature_selector ( $ style_variation , $ feature_selector );
38033893
0 commit comments