Skip to content

Commit 25d2739

Browse files
committed
Editor: Migrate the Button block to the width block support
Handle the Button block's width through the `dimensions.width` block support so it aligns with Global Styles and offers greater freedom in the block editor. This adds the global styles `calc()` conversion for percentage button widths in `WP_Theme_JSON`, leverages `core/button` dimension size presets in `theme.json` to maintain backward compatibility, and includes test coverage. Developed in: [https://github.com/WordPress/wordpress-develop/pull/12046](https://github.com/WordPress/wordpress-develop/pull/12046) Props aaronrobertshaw, isabel_brison, ramonopoly, adrianduffell. Fixes #65388. git-svn-id: https://develop.svn.wordpress.org/trunk@62746 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 60f7663 commit 25d2739

2 files changed

Lines changed: 262 additions & 0 deletions

File tree

src/wp-includes/class-wp-theme-json.php

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

tests/phpunit/tests/theme/wpThemeJson.php

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6542,6 +6542,178 @@ public function data_update_separator_declarations() {
65426542
);
65436543
}
65446544

6545+
/**
6546+
* Tests that button block width declarations are updated for percentage values.
6547+
*
6548+
* @ticket 65388
6549+
*
6550+
* @dataProvider data_update_button_width_declarations
6551+
*
6552+
* @param array $theme_json_args Theme JSON arguments including styles and optional settings.
6553+
* @param string $expected_output Expected CSS output.
6554+
*/
6555+
public function test_update_button_width_declarations( $theme_json_args, $expected_output ) {
6556+
$theme_json = new WP_Theme_JSON(
6557+
array_merge(
6558+
array( 'version' => WP_Theme_JSON::LATEST_SCHEMA ),
6559+
$theme_json_args
6560+
),
6561+
'default'
6562+
);
6563+
6564+
$button_node = array(
6565+
'name' => 'core/button',
6566+
'path' => array( 'styles', 'blocks', 'core/button' ),
6567+
'selector' => '.wp-block-button .wp-block-button__link',
6568+
'selectors' => array(
6569+
'root' => '.wp-block-button .wp-block-button__link',
6570+
'dimensions' => array(
6571+
'root' => '.wp-block-button',
6572+
'width' => '.wp-block-button',
6573+
),
6574+
),
6575+
'duotone' => null,
6576+
'variations' => array(),
6577+
'css' => '.wp-block-button .wp-block-button__link',
6578+
);
6579+
$this->assertSame( $expected_output, $theme_json->get_styles_for_block( $button_node ) );
6580+
}
6581+
6582+
/**
6583+
* Data provider for button width declaration tests.
6584+
*
6585+
* @return array
6586+
*/
6587+
public function data_update_button_width_declarations() {
6588+
return array(
6589+
'direct percentage value' => array(
6590+
array(
6591+
'styles' => array(
6592+
'blocks' => array(
6593+
'core/button' => array(
6594+
'dimensions' => array(
6595+
'width' => '25%',
6596+
),
6597+
),
6598+
),
6599+
),
6600+
),
6601+
'expected_output' => ':root :where(.wp-block-button){width: calc(25 * 1% - (var(--wp--style--block-gap, 0.5em) * (1 - 25 / 100)));}',
6602+
),
6603+
'decimal percentage value' => array(
6604+
array(
6605+
'styles' => array(
6606+
'blocks' => array(
6607+
'core/button' => array(
6608+
'dimensions' => array(
6609+
'width' => '33.33%',
6610+
),
6611+
),
6612+
),
6613+
),
6614+
),
6615+
'expected_output' => ':root :where(.wp-block-button){width: calc(33.33 * 1% - (var(--wp--style--block-gap, 0.5em) * (1 - 33.33 / 100)));}',
6616+
),
6617+
'non-percentage value is unchanged' => array(
6618+
array(
6619+
'styles' => array(
6620+
'blocks' => array(
6621+
'core/button' => array(
6622+
'dimensions' => array(
6623+
'width' => '200px',
6624+
),
6625+
),
6626+
),
6627+
),
6628+
),
6629+
'expected_output' => ':root :where(.wp-block-button){width: 200px;}',
6630+
),
6631+
'preset dimension with percentage size (block-level settings)' => array(
6632+
array(
6633+
'settings' => array(
6634+
'blocks' => array(
6635+
'core/button' => array(
6636+
'dimensions' => array(
6637+
'dimensionSizes' => array(
6638+
array(
6639+
'slug' => '50',
6640+
'name' => '50%',
6641+
'size' => '50%',
6642+
),
6643+
),
6644+
),
6645+
),
6646+
),
6647+
),
6648+
'styles' => array(
6649+
'blocks' => array(
6650+
'core/button' => array(
6651+
'dimensions' => array(
6652+
'width' => 'var(--wp--preset--dimension--50)',
6653+
),
6654+
),
6655+
),
6656+
),
6657+
),
6658+
'expected_output' => ':root :where(.wp-block-button){width: calc(50 * 1% - (var(--wp--style--block-gap, 0.5em) * (1 - 50 / 100)));}',
6659+
),
6660+
'preset dimension with percentage size (top-level settings)' => array(
6661+
array(
6662+
'settings' => array(
6663+
'dimensions' => array(
6664+
'dimensionSizes' => array(
6665+
array(
6666+
'slug' => '25',
6667+
'name' => '25%',
6668+
'size' => '25%',
6669+
),
6670+
),
6671+
),
6672+
),
6673+
'styles' => array(
6674+
'blocks' => array(
6675+
'core/button' => array(
6676+
'dimensions' => array(
6677+
'width' => 'var(--wp--preset--dimension--25)',
6678+
),
6679+
),
6680+
),
6681+
),
6682+
),
6683+
'expected_output' => ':root :where(.wp-block-button){width: calc(25 * 1% - (var(--wp--style--block-gap, 0.5em) * (1 - 25 / 100)));}',
6684+
),
6685+
'preset dimension with non-percentage size' => array(
6686+
array(
6687+
'settings' => array(
6688+
'blocks' => array(
6689+
'core/button' => array(
6690+
'dimensions' => array(
6691+
'dimensionSizes' => array(
6692+
array(
6693+
'slug' => 'wide',
6694+
'name' => 'Wide',
6695+
'size' => '400px',
6696+
),
6697+
),
6698+
),
6699+
),
6700+
),
6701+
),
6702+
'styles' => array(
6703+
'blocks' => array(
6704+
'core/button' => array(
6705+
'dimensions' => array(
6706+
'width' => 'var(--wp--preset--dimension--wide)',
6707+
),
6708+
),
6709+
),
6710+
),
6711+
),
6712+
'expected_output' => ':root :where(.wp-block-button){width: var(--wp--preset--dimension--wide);}',
6713+
),
6714+
);
6715+
}
6716+
65456717
/**
65466718
* @ticket 57559
65476719
*/

0 commit comments

Comments
 (0)