Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add defaultSpacingSizes option (theme.json v3) #61842

Merged
merged 36 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
343fafc
Refactor magic number to constant
ajlende May 21, 2024
21cf2ba
Add defaultSpacingSizes option
ajlende May 30, 2024
119bff1
Add migration docs
ajlende May 22, 2024
f19da26
Update API docs
ajlende May 22, 2024
9ede3bb
Refactor to not need constants
ajlende May 22, 2024
b3f304b
Update PHPUnit tests
ajlende May 22, 2024
666fc4e
Fix PHP lint
ajlende May 22, 2024
0301b49
Fix PHP lint
ajlende May 22, 2024
f5010ef
Make sure all spread vairables cannot be null
ajlende May 22, 2024
6fbb99f
Code cleanup
ajlende May 22, 2024
b1126b7
Add backport-changelog
ajlende May 23, 2024
8a5f339
Prevent spacingSizes from entirely replacing presets generated from s…
ajlende May 29, 2024
73d63a7
Be more specific about limits in the schema
ajlende May 29, 2024
ada604b
Move comment
ajlende May 29, 2024
882f02e
Increase limit to 10
ajlende May 30, 2024
3415b6a
Fix merging of origins
ajlende May 30, 2024
8529aef
Use old when checking for existance in migration
ajlende May 30, 2024
5d2762a
Fix copy/paste mistake
ajlende May 30, 2024
8093334
Fix some v2 backcompat
ajlende May 30, 2024
59eaac3
Don't bother with skipping pre-generation on v2 for filters
ajlende May 30, 2024
1aafb16
Switch back to numbers for labels in the range control
ajlende May 30, 2024
21e6de7
Deprecate set_spacing_sizes
ajlende May 30, 2024
d42932b
Remove comment
ajlende May 30, 2024
c5778d0
Remove incorrect doc param
ajlende May 30, 2024
31ac387
Clarify comment
ajlende May 30, 2024
17db2d4
Add method version docs
ajlende May 30, 2024
25dc819
Fix PHPUnit tests
ajlende May 30, 2024
144dc5a
Fix PHPUnit tests
ajlende May 30, 2024
a592617
Revert to bugged spacing scale check
ajlende May 30, 2024
1a3d0c2
Add comment about bad conditional
ajlende May 30, 2024
12edffa
Do sorting on all presets in UI only
ajlende May 30, 2024
44bf7b8
Add a check that slug begins with a number for sorting
ajlende May 30, 2024
de077ad
Reorder schema keys
ajlende May 30, 2024
d9c5c85
Quick fix for redundant info in the defaultFontSizes migration docs
ajlende May 31, 2024
1dfcaa3
Update defaultSpacingSizes migration docs to include merging of spaci…
ajlende May 31, 2024
2af96a3
Update the global settings and styles guide
ajlende May 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions backport-changelog/6.6/6616.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
https://github.com/WordPress/wordpress-develop/pull/6616

* https://github.com/WordPress/gutenberg/pull/58409
* https://github.com/WordPress/gutenberg/pull/61328
* https://github.com/WordPress/gutenberg/pull/61842
11 changes: 1 addition & 10 deletions docs/how-to-guides/themes/global-settings-and-styles.md
Original file line number Diff line number Diff line change
Expand Up @@ -336,16 +336,7 @@ The following presets can be defined via `theme.json`:
- `color.palette`:
- generates 3 classes per preset value: color, background-color, and border-color.
- generates a single custom property per preset value.
- `spacing.spacingScale`: used to generate an array of spacing preset sizes for use with padding, margin, and gap settings.
- `operator`: specifies how to calculate the steps with either `*` for multiplier, or `+` for sum.
- `increment`: the amount to increment each step by. Core by default uses a 'perfect 5th' multiplier of `1.5`.
- `steps`: the number of steps to generate in the spacing scale. The default is 7. To prevent the generation of the spacing presets, and to disable the related UI, this can be set to `0`.
- `mediumStep`: the steps in the scale are generated descending and ascending from a medium step, so this should be the size value of the medium space, without the unit. The default medium step is `1.5rem` so the mediumStep value is `1.5`.
- `unit`: the unit the scale uses, eg. `px, rem, em, %`. The default is `rem`.
- `spacing.spacingSizes`: themes can choose to include a static `spacing.spacingSizes` array of spacing preset sizes if they have a sequence of sizes that can't be generated via an increment or multiplier.
- `name`: a human readable name for the size, eg. `Small, Medium, Large`.
- `slug`: the machine readable name. In order to provide the best cross site/theme compatibility the slugs should be in the format, "10","20","30","40","50","60", with "50" representing the `Medium` size value.
- `size`: the size, including the unit, eg. `1.5rem`. It is possible to include fluid values like `clamp(2rem, 10vw, 20rem)`.
Comment on lines -339 to -348
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't the documentation page where keys are defined, just the one that explains what gets generated in the stylesheet, so I removed this chunk. The actual documentation exists in the Theme Handbook which isn't part of Gutenberg source code.

- `spacing.spacingSizes`/`spacing.spacingScale`: generates a single custom property per preset value.
- `typography.fontSizes`: generates a single class and custom property per preset value.
- `typography.fontFamilies`: generates a single custom property per preset value.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ Settings related to spacing.
| padding | boolean | false | |
| units | array | px,em,rem,vh,vw,% | |
| customSpacingSize | boolean | true | |
| defaultSpacingSizes | boolean | true | |
| spacingSizes | array | | name, size, slug |
| spacingScale | object | | |

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,27 @@ The new `defaultFontSizes` option gives control over showing default font sizes

It is `true` by default when switching to v3. This is to be consistent with how other `default*` options work such as `settings.color.defaultPalette`, but differs from the behavior in v2.

In theme.json v2, the default font sizes were only shown when theme sizes were not defined. A theme providing font sizes with the same slugs as the defaults would always override the default ones.

To keep behavior similar to v2 with a v3 theme.json:
* If you do not have any `fontSizes` defined, `defaultFontSizes` can be left out or set to `true`.
* If you have some `fontSizes` defined, set `defaultFontSizes` to `false`.

#### `settings.spacing.defaultSpacingSizes`

In theme.json v2, there are two settings that could be used to set theme level spacing sizes: `settings.spacing.spacingSizes` and `settings.spacing.spacingScale`. Setting both `spacingSizes` _and_ `spacingScale` would only use the values from `spacingSizes`. And setting either of them would always replace the entire set of default spacing sizes provided by WordPress.

The default `spacingSizes` slugs provided by WordPress are: `20`, `30`, `40`, `50`, `60`, `70`, and `80`.

The new `defaultSpacingSizes` option gives control over showing default spacing sizes and preventing those defaults from being overridden.

- When set to `true` it will show the default spacing sizes and prevent them from being overridden by the theme.
- When set to `false` it will hide the default spacing sizes and allow the theme to use the default slugs.

`defaultSpacingSizes` is `true` by default when switching to v3. This is to be consistent with how other `default*` options work such as `settings.color.defaultPalette`, but differs from the behavior in v2.

Additionally, in v3 both `spacingSizes` and `spacingScale` can be set at the same time. Presets defined in `spacingSizes` with slugs matching the generated presets from `spacingSizes` will override the generated ones.

To keep behavior similar to v2 with a v3 theme.json:
* If you do not have any `spacingSizes` presets or `spacingScale` config defined, `defaultSpacingSizes` can be left out or set to `true`.
* If you disabled default spacing sizes by setting `spacingScale` to `{ "steps": 0 }`, remove the `spacingScale` config and set `defaultSpacingSizes` to `false`.
ajlende marked this conversation as resolved.
Show resolved Hide resolved
* If you defined only one of either `spacingScale` or `spacingSizes` for your presets, set `defaultSpacingSizes` to `false`.
* If you defined both `spacingScale` and `spacingSizes`, remove the `spacingSizes` config _and_ set `defaultSpacingSizes` to `false`.
187 changes: 168 additions & 19 deletions lib/class-wp-theme-json-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ class WP_Theme_JSON_Gutenberg {
* @since 6.0.0 Replaced `override` with `prevent_override` and updated the
* `prevent_override` value for `color.duotone` to use `color.defaultDuotone`.
* @since 6.2.0 Added 'shadow' presets.
* @since 6.6.0 Updated the 'prevent_override' value for font size presets to use 'typography.defaultFontSizes' and spacing size presets to use `spacing.defaultSpacingSizes`.
* @since 6.6.0 Added `aspectRatios`.
* @var array
*/
Expand Down Expand Up @@ -187,7 +188,7 @@ class WP_Theme_JSON_Gutenberg {
),
array(
'path' => array( 'spacing', 'spacingSizes' ),
'prevent_override' => false,
'prevent_override' => array( 'spacing', 'defaultSpacingSizes' ),
'use_default_names' => true,
'value_key' => 'size',
'css_vars' => '--wp--preset--spacing--$slug',
Expand Down Expand Up @@ -427,13 +428,14 @@ class WP_Theme_JSON_Gutenberg {
'sticky' => null,
),
'spacing' => array(
'customSpacingSize' => null,
'spacingSizes' => null,
'spacingScale' => null,
'blockGap' => null,
'margin' => null,
'padding' => null,
'units' => null,
'customSpacingSize' => null,
'defaultSpacingSizes' => null,
'spacingSizes' => null,
'spacingScale' => null,
'blockGap' => null,
'margin' => null,
'padding' => null,
'units' => null,
),
'shadow' => array(
'presets' => null,
Expand Down Expand Up @@ -727,6 +729,8 @@ public static function get_element_class_name( $element ) {
* Constructor.
*
* @since 5.8.0
* @since 6.6.0 Key spacingScale by origin, and pre-generate the
* spacingSizes from spacingScale.
*
* @param array $theme_json A structure that follows the theme.json schema.
* @param string $origin Optional. What source of data this object represents.
Expand All @@ -742,8 +746,8 @@ public function __construct( $theme_json = array( 'version' => WP_Theme_JSON_Gut
$valid_block_names = array_keys( $registry->get_all_registered() );
$valid_element_names = array_keys( static::ELEMENTS );
$valid_variations = static::get_valid_block_style_variations();
$theme_json = static::sanitize( $this->theme_json, $valid_block_names, $valid_element_names, $valid_variations );
$this->theme_json = static::maybe_opt_in_into_settings( $theme_json );
$this->theme_json = static::sanitize( $this->theme_json, $valid_block_names, $valid_element_names, $valid_variations );
$this->theme_json = static::maybe_opt_in_into_settings( $this->theme_json );

// Internally, presets are keyed by origin.
$nodes = static::get_setting_nodes( $this->theme_json );
Expand All @@ -762,6 +766,27 @@ public function __construct( $theme_json = array( 'version' => WP_Theme_JSON_Gut
}
}
}

// In addition to presets, spacingScale (which generates presets) is also keyed by origin.
$scale_path = array( 'settings', 'spacing', 'spacingScale' );
$spacing_scale = _wp_array_get( $this->theme_json, $scale_path, null );
if ( null !== $spacing_scale ) {
// If the spacingScale is not already keyed by origin.
if ( empty( array_intersect( array_keys( $spacing_scale ), static::VALID_ORIGINS ) ) ) {
_wp_array_set( $this->theme_json, $scale_path, array( $origin => $spacing_scale ) );
}
}

// Pre-generate the spacingSizes from spacingScale.
$scale_path = array( 'settings', 'spacing', 'spacingScale', $origin );
$spacing_scale = _wp_array_get( $this->theme_json, $scale_path, null );
if ( isset( $spacing_scale ) ) {
$sizes_path = array( 'settings', 'spacing', 'spacingSizes', $origin );
$spacing_sizes = _wp_array_get( $this->theme_json, $sizes_path, array() );
$spacing_scale_sizes = static::compute_spacing_sizes( $spacing_scale );
$merged_spacing_sizes = static::merge_spacing_sizes( $spacing_scale_sizes, $spacing_sizes );
_wp_array_set( $this->theme_json, $sizes_path, $merged_spacing_sizes );
}
}

/**
Expand Down Expand Up @@ -2947,13 +2972,49 @@ protected static function get_metadata_boolean( $data, $path, $default_value = f
*
* @since 5.8.0
* @since 5.9.0 Duotone preset also has origins.
* @since 6.6.0 Use the spacingScale keyed by origin, and re-generate the
* spacingSizes from spacingScale.
*
* @param WP_Theme_JSON_Gutenberg $incoming Data to merge.
*/
public function merge( $incoming ) {
$incoming_data = $incoming->get_raw_data();
$this->theme_json = array_replace_recursive( $this->theme_json, $incoming_data );

/*
* Recompute all the spacing sizes based on the new hierarchy of data. In the constructor
* spacingScale and spacingSizes are both keyed by origin and VALID_ORIGINS is ordered, so
* we can allow partial spacingScale data to inherit missing data from earlier layers when
* computing the spacing sizes.
*
* This happens before the presets are merged to ensure that default spacing sizes can be
* removed from the theme origin if $prevent_override is true.
*/
$flattened_spacing_scale = array();
foreach ( static::VALID_ORIGINS as $origin ) {
$scale_path = array( 'settings', 'spacing', 'spacingScale', $origin );

// Apply the base spacing scale to the current layer.
$base_spacing_scale = _wp_array_get( $this->theme_json, $scale_path, array() );
$flattened_spacing_scale = array_replace( $flattened_spacing_scale, $base_spacing_scale );

$spacing_scale = _wp_array_get( $incoming_data, $scale_path, null );
if ( ! isset( $spacing_scale ) ) {
continue;
}

// Allow partial scale settings by merging with lower layers.
$flattened_spacing_scale = array_replace( $flattened_spacing_scale, $spacing_scale );

// Generate and merge the scales for this layer.
$sizes_path = array( 'settings', 'spacing', 'spacingSizes', $origin );
$spacing_sizes = _wp_array_get( $incoming_data, $sizes_path, array() );
$spacing_scale_sizes = static::compute_spacing_sizes( $flattened_spacing_scale );
$merged_spacing_sizes = static::merge_spacing_sizes( $spacing_scale_sizes, $spacing_sizes );

_wp_array_set( $incoming_data, $sizes_path, $merged_spacing_sizes );
}

/*
* The array_replace_recursive algorithm merges at the leaf level,
* but we don't want leaf arrays to be merged, so we overwrite it.
Expand Down Expand Up @@ -3733,12 +3794,19 @@ public function get_data() {
/**
* Sets the spacingSizes array based on the spacingScale values from theme.json.
*
* No longer used since theme.json version 3 as the spacingSizes are now
* automatically generated during construction and merge instead of manually
* set in the resolver.
*
* @since 6.1.0
* @deprecated 6.6.0
*
* @return null|void
*/
public function set_spacing_sizes() {
$spacing_scale = $this->theme_json['settings']['spacing']['spacingScale'] ?? array();
_deprecated_function( __METHOD__, '6.6.0' );

$spacing_scale = $this->theme_json['settings']['spacing']['spacingScale']['default'] ?? array();

// Gutenberg didn't have the 1st isset check.
if ( ! isset( $spacing_scale['steps'] )
Expand All @@ -3762,6 +3830,94 @@ public function set_spacing_sizes() {
return null;
}

$spacing_sizes = static::compute_spacing_sizes( $spacing_scale );

// If there are 7 or less steps in the scale revert to numbers for labels instead of t-shirt sizes.
if ( $spacing_scale['steps'] <= 7 ) {
for ( $spacing_sizes_count = 0; $spacing_sizes_count < count( $spacing_sizes ); $spacing_sizes_count++ ) {
$spacing_sizes[ $spacing_sizes_count ]['name'] = (string) ( $spacing_sizes_count + 1 );
}
}

_wp_array_set( $this->theme_json, array( 'settings', 'spacing', 'spacingSizes', 'default' ), $spacing_sizes );
}

/**
* Merges two sets of spacing size presets.
*
* @since 6.6.0
*
* @param array $base The base set of spacing sizes.
* @param array $incoming The set of spacing sizes to merge with the base. Duplicate slugs will override the base values.
* @return array The merged set of spacing sizes.
*/
private static function merge_spacing_sizes( $base, $incoming ) {
$merged = array();
foreach ( $base as $item ) {
$merged[ $item['slug'] ] = $item;
}
foreach ( $incoming as $item ) {
$merged[ $item['slug'] ] = $item;
}
return array_values( $merged );
}

/**
* Generates a set of spacing sizes by starting with a medium size and
* applying an operator with an increment value to generate the rest of the
* sizes outward from the medium size. The medium slug is '50' with the rest
* of the slugs being 10 apart. The generated names use t-shirt sizing.
*
* Example:
*
* $spacing_scale = array(
* 'steps' => 4,
* 'mediumStep' => 16,
* 'unit' => 'px',
* 'operator' => '+',
* 'increment' => 2,
* );
* $spacing_sizes = static::compute_spacing_sizes( $spacing_scale );
* // -> array(
* // array( 'name' => 'Small', 'slug' => '40', 'size' => '14px' ),
* // array( 'name' => 'Medium', 'slug' => '50', 'size' => '16px' ),
* // array( 'name' => 'Large', 'slug' => '60', 'size' => '18px' ),
* // array( 'name' => 'X-Large', 'slug' => '70', 'size' => '20px' ),
* // )
*
* @since 6.6.0
*
* @param array $spacing_scale {
* The spacing scale values. All are required.
*
* @type int $steps The number of steps in the scale. (up to 10 steps are supported.)
* @type float $mediumStep The middle value that gets the slug '50'. (For even number of steps, this becomes the first middle value.)
* @type string $unit The CSS unit to use for the sizes.
* @type string $operator The mathematical operator to apply to generate the other sizes. Either '+' or '*'.
* @type float $increment The value used with the operator to generate the other sizes.
* }
* @return array The spacing sizes presets or an empty array if some spacing scale values are missing or invalid.
*/
private static function compute_spacing_sizes( $spacing_scale ) {
/*
* This condition is intentionally missing some checks on ranges for the values in order to
* keep backwards compatibility with the previous implementation.
*/
if (
! isset( $spacing_scale['steps'] ) ||
! is_numeric( $spacing_scale['steps'] ) ||
0 === $spacing_scale['steps'] ||
! isset( $spacing_scale['mediumStep'] ) ||
! is_numeric( $spacing_scale['mediumStep'] ) ||
! isset( $spacing_scale['unit'] ) ||
! isset( $spacing_scale['operator'] ) ||
( '+' !== $spacing_scale['operator'] && '*' !== $spacing_scale['operator'] ) ||
! isset( $spacing_scale['increment'] ) ||
! is_numeric( $spacing_scale['increment'] )
Comment on lines +3907 to +3916
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a blocker but I wonder if we should have unit tests for this mammoth of a condition? Can be a follow up

Copy link
Contributor Author

@ajlende ajlende May 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's leave it for a follow-up. There are already some tests for the existence of these conditions, but not the things it doesn't catch like "steps": 20 only generating 15 steps.

) {
return array();
}

$unit = '%' === $spacing_scale['unit'] ? '%' : sanitize_title( $spacing_scale['unit'] );
$current_step = $spacing_scale['mediumStep'];
$steps_mid_point = round( $spacing_scale['steps'] / 2, 0 );
Expand Down Expand Up @@ -3844,14 +4000,7 @@ public function set_spacing_sizes() {
$spacing_sizes[] = $above_sizes_item;
}

// If there are 7 or less steps in the scale revert to numbers for labels instead of t-shirt sizes.
if ( $spacing_scale['steps'] <= 7 ) {
for ( $spacing_sizes_count = 0; $spacing_sizes_count < count( $spacing_sizes ); $spacing_sizes_count++ ) {
$spacing_sizes[ $spacing_sizes_count ]['name'] = (string) ( $spacing_sizes_count + 1 );
}
}

_wp_array_set( $this->theme_json, array( 'settings', 'spacing', 'spacingSizes', 'default' ), $spacing_sizes );
return $spacing_sizes;
}

/**
Expand Down
3 changes: 0 additions & 3 deletions lib/class-wp-theme-json-resolver-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,6 @@ public static function get_merged_data( $origin = 'custom' ) {
$result = new WP_Theme_JSON_Gutenberg();
$result->merge( static::get_core_data() );
if ( 'default' === $origin ) {
$result->set_spacing_sizes();
Copy link
Contributor Author

@ajlende ajlende May 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to WP_Theme_JSON_Gutenberg->__construct() so that the generated presets for each origin are merged properly rather than always being set as default origin.

return $result;
}

Expand All @@ -611,12 +610,10 @@ public static function get_merged_data( $origin = 'custom' ) {

$result->merge( static::get_theme_data() );
if ( 'theme' === $origin ) {
$result->set_spacing_sizes();
return $result;
}

$result->merge( static::get_user_data() );
$result->set_spacing_sizes();
return $result;
}

Expand Down
Loading
Loading