Skip to content

Commit f91931a

Browse files
committed
Block Themes: Add section styling via extended block style variations
Provide users with the ability to style entire sections of a page without having to tediously reapply the same sets of styles. This is done by extending block style variations to apply to nested blocks. See WordPress/gutenberg#57908. Fixes #61312. Props aaronrobertshaw, talldanwp, ramonopoly, isabel_brison, andrewserong. git-svn-id: https://develop.svn.wordpress.org/trunk@58264 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 103856d commit f91931a

14 files changed

Lines changed: 920 additions & 70 deletions

src/wp-includes/block-supports/block-style-variations.php

Lines changed: 424 additions & 0 deletions
Large diffs are not rendered by default.

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

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -701,16 +701,46 @@ private static function recursively_iterate_json( $dir ) {
701701
return $nested_json_files;
702702
}
703703

704+
/**
705+
* Determines if a supplied style variation matches the provided scope.
706+
*
707+
* For backwards compatibility, if a variation does not define any scope
708+
* related property, e.g. `blockTypes`, it is assumed to be a theme style
709+
* variation.
710+
*
711+
* @since 6.6.0
712+
*
713+
* @param array $variation Theme.json shaped style variation object.
714+
* @param string $scope Scope to check e.g. theme, block etc.
715+
*
716+
* @return boolean
717+
*/
718+
private static function style_variation_has_scope( $variation, $scope ) {
719+
if ( 'block' === $scope ) {
720+
return isset( $variation['blockTypes'] );
721+
}
722+
723+
if ( 'theme' === $scope ) {
724+
return ! isset( $variation['blockTypes'] );
725+
}
726+
727+
return false;
728+
}
704729

705730
/**
706731
* Returns the style variations defined by the theme.
707732
*
708733
* @since 6.0.0
709734
* @since 6.2.0 Returns parent theme variations if theme is a child.
735+
* @since 6.6.0 Added configurable scope parameter to allow filtering
736+
* theme.json partial files by the scope to which they
737+
* can be applied e.g. theme vs block etc.
738+
*
739+
* @param string $scope The scope or type of style variation to retrieve e.g. theme, block etc.
710740
*
711741
* @return array
712742
*/
713-
public static function get_style_variations() {
743+
public static function get_style_variations( $scope = 'theme' ) {
714744
$variation_files = array();
715745
$variations = array();
716746
$base_directory = get_stylesheet_directory() . '/styles';
@@ -733,7 +763,7 @@ public static function get_style_variations() {
733763
ksort( $variation_files );
734764
foreach ( $variation_files as $path => $file ) {
735765
$decoded_file = wp_json_file_decode( $path, array( 'associative' => true ) );
736-
if ( is_array( $decoded_file ) ) {
766+
if ( is_array( $decoded_file ) && static::style_variation_has_scope( $decoded_file, $scope ) ) {
737767
$translated = static::translate( $decoded_file, wp_get_theme()->get( 'TextDomain' ) );
738768
$variation = ( new WP_Theme_JSON( $translated ) )->get_raw_data();
739769
if ( empty( $variation['title'] ) ) {

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

Lines changed: 93 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -346,9 +346,11 @@ class WP_Theme_JSON {
346346
* @since 5.9.0 Renamed from `ALLOWED_TOP_LEVEL_KEYS` to `VALID_TOP_LEVEL_KEYS`,
347347
* added the `customTemplates` and `templateParts` values.
348348
* @since 6.3.0 Added the `description` value.
349+
* @since 6.6.0 Added `blockTypes` to support block style variation theme.json partials.
349350
* @var string[]
350351
*/
351352
const VALID_TOP_LEVEL_KEYS = array(
353+
'blockTypes',
352354
'customTemplates',
353355
'description',
354356
'patterns',
@@ -823,6 +825,7 @@ protected static function do_opt_in_into_settings( &$context ) {
823825
* @since 5.8.0
824826
* @since 5.9.0 Added the `$valid_block_names` and `$valid_element_name` parameters.
825827
* @since 6.3.0 Added the `$valid_variations` parameter.
828+
* @since 6.6.0 Updated schema to allow extended block style variations.
826829
*
827830
* @param array $input Structure to sanitize.
828831
* @param array $valid_block_names List of valid block names.
@@ -881,6 +884,27 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n
881884

882885
$schema_styles_blocks = array();
883886
$schema_settings_blocks = array();
887+
888+
/*
889+
* Generate a schema for blocks.
890+
* - Block styles can contain `elements` & `variations` definitions.
891+
* - Variations definitions cannot be nested.
892+
* - Variations can contain styles for inner `blocks`.
893+
* - Variation inner `blocks` styles can contain `elements`.
894+
*
895+
* As each variation needs a `blocks` schema but further nested
896+
* inner `blocks`, the overall schema will be generated in multiple passes.
897+
*/
898+
foreach ( $valid_block_names as $block ) {
899+
$schema_settings_blocks[ $block ] = static::VALID_SETTINGS;
900+
$schema_styles_blocks[ $block ] = $styles_non_top_level;
901+
$schema_styles_blocks[ $block ]['elements'] = $schema_styles_elements;
902+
}
903+
904+
$block_style_variation_styles = static::VALID_STYLES;
905+
$block_style_variation_styles['blocks'] = $schema_styles_blocks;
906+
$block_style_variation_styles['elements'] = $schema_styles_elements;
907+
884908
foreach ( $valid_block_names as $block ) {
885909
// Build the schema for each block style variation.
886910
$style_variation_names = array();
@@ -897,12 +921,9 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n
897921

898922
$schema_styles_variations = array();
899923
if ( ! empty( $style_variation_names ) ) {
900-
$schema_styles_variations = array_fill_keys( $style_variation_names, $styles_non_top_level );
924+
$schema_styles_variations = array_fill_keys( $style_variation_names, $block_style_variation_styles );
901925
}
902926

903-
$schema_settings_blocks[ $block ] = static::VALID_SETTINGS;
904-
$schema_styles_blocks[ $block ] = $styles_non_top_level;
905-
$schema_styles_blocks[ $block ]['elements'] = $schema_styles_elements;
906927
$schema_styles_blocks[ $block ]['variations'] = $schema_styles_variations;
907928
}
908929

@@ -913,6 +934,12 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n
913934
$schema['settings']['blocks'] = $schema_settings_blocks;
914935
$schema['settings']['typography']['fontFamilies'] = static::schema_in_root_and_per_origin( static::FONT_FAMILY_SCHEMA );
915936

937+
/*
938+
* Shared block style variations can be registered from the theme.json data so we can't
939+
* validate them against pre-registered block style variations.
940+
*/
941+
$schema['styles']['blocks']['variations'] = null;
942+
916943
// Remove anything that's not present in the schema.
917944
foreach ( array( 'styles', 'settings' ) as $subtree ) {
918945
if ( ! isset( $input[ $subtree ] ) ) {
@@ -1016,16 +1043,36 @@ protected static function prepend_to_selector( $selector, $to_prepend ) {
10161043
* @since 5.9.0 Added `duotone` key with CSS selector.
10171044
* @since 6.1.0 Added `features` key with block support feature level selectors.
10181045
* @since 6.3.0 Refactored and stabilized selectors API.
1046+
* @since 6.6.0 Updated to include block style variations from the block styles registry.
10191047
*
10201048
* @return array Block metadata.
10211049
*/
10221050
protected static function get_blocks_metadata() {
1023-
$registry = WP_Block_Type_Registry::get_instance();
1024-
$blocks = $registry->get_all_registered();
1051+
$registry = WP_Block_Type_Registry::get_instance();
1052+
$blocks = $registry->get_all_registered();
1053+
$style_registry = WP_Block_Styles_Registry::get_instance();
10251054

10261055
// Is there metadata for all currently registered blocks?
10271056
$blocks = array_diff_key( $blocks, static::$blocks_metadata );
10281057
if ( empty( $blocks ) ) {
1058+
/*
1059+
* New block styles may have been registered within WP_Block_Styles_Registry.
1060+
* Update block metadata for any new block style variations.
1061+
*/
1062+
$registered_styles = $style_registry->get_all_registered();
1063+
foreach ( static::$blocks_metadata as $block_name => $block_metadata ) {
1064+
if ( ! empty( $registered_styles[ $block_name ] ) ) {
1065+
$style_selectors = $block_metadata['styleVariations'] ?? array();
1066+
1067+
foreach ( $registered_styles[ $block_name ] as $block_style ) {
1068+
if ( ! isset( $style_selectors[ $block_style['name'] ] ) ) {
1069+
$style_selectors[ $block_style['name'] ] = static::get_block_style_variation_selector( $block_style['name'], $block_metadata['selector'] );
1070+
}
1071+
}
1072+
1073+
static::$blocks_metadata[ $block_name ]['styleVariations'] = $style_selectors;
1074+
}
1075+
}
10291076
return static::$blocks_metadata;
10301077
}
10311078

@@ -1060,11 +1107,20 @@ protected static function get_blocks_metadata() {
10601107
}
10611108

10621109
// If the block has style variations, append their selectors to the block metadata.
1110+
$style_selectors = array();
10631111
if ( ! empty( $block_type->styles ) ) {
1064-
$style_selectors = array();
10651112
foreach ( $block_type->styles as $style ) {
10661113
$style_selectors[ $style['name'] ] = static::get_block_style_variation_selector( $style['name'], static::$blocks_metadata[ $block_name ]['selector'] );
10671114
}
1115+
}
1116+
1117+
// Block style variations can be registered through the WP_Block_Styles_Registry as well as block.json.
1118+
$registered_styles = $style_registry->get_registered_styles_for_block( $block_name );
1119+
foreach ( $registered_styles as $style ) {
1120+
$style_selectors[ $style['name'] ] = static::get_block_style_variation_selector( $style['name'], static::$blocks_metadata[ $block_name ]['selector'] );
1121+
}
1122+
1123+
if ( ! empty( $style_selectors ) ) {
10681124
static::$blocks_metadata[ $block_name ]['styleVariations'] = $style_selectors;
10691125
}
10701126
}
@@ -1158,16 +1214,18 @@ public function get_settings() {
11581214
* @since 5.8.0
11591215
* @since 5.9.0 Removed the `$type` parameter, added the `$types` and `$origins` parameters.
11601216
* @since 6.3.0 Add fallback layout styles for Post Template when block gap support isn't available.
1217+
* @since 6.6.0 Added `skip_root_layout_styles` option to omit layout styles if desired.
11611218
*
11621219
* @param string[] $types Types of styles to load. Will load all by default. It accepts:
11631220
* - `variables`: only the CSS Custom Properties for presets & custom ones.
11641221
* - `styles`: only the styles section in theme.json.
11651222
* - `presets`: only the classes for the presets.
11661223
* @param string[] $origins A list of origins to include. By default it includes VALID_ORIGINS.
11671224
* @param array $options An array of options for now used for internal purposes only (may change without notice).
1168-
* The options currently supported are 'scope' that makes sure all style are scoped to a
1169-
* given selector, and root_selector which overwrites and forces a given selector to be
1170-
* used on the root node.
1225+
* The options currently supported are:
1226+
* - 'scope' that makes sure all style are scoped to a given selector
1227+
* - `root_selector` which overwrites and forces a given selector to be used on the root node
1228+
* - `skip_root_layout_styles` which omits root layout styles from the generated stylesheet.
11711229
* @return string The resulting stylesheet.
11721230
*/
11731231
public function get_stylesheet( $types = array( 'variables', 'styles', 'presets' ), $origins = null, $options = array() ) {
@@ -1220,7 +1278,7 @@ public function get_stylesheet( $types = array( 'variables', 'styles', 'presets'
12201278
}
12211279

12221280
if ( in_array( 'styles', $types, true ) ) {
1223-
if ( false !== $root_style_key ) {
1281+
if ( false !== $root_style_key && empty( $options['skip_root_layout_styles'] ) ) {
12241282
$stylesheet .= $this->get_root_layout_rules( $style_nodes[ $root_style_key ]['selector'], $style_nodes[ $root_style_key ] );
12251283
}
12261284
$stylesheet .= $this->get_block_classes( $style_nodes );
@@ -3114,6 +3172,7 @@ protected static function filter_slugs( $node, $slugs ) {
31143172
*
31153173
* @since 5.9.0
31163174
* @since 6.3.2 Preserves global styles block variations when securing styles.
3175+
* @since 6.6.0 Updated to allow variation element styles.
31173176
*
31183177
* @param array $theme_json Structure to sanitize.
31193178
* @return array Sanitized structure.
@@ -3175,6 +3234,29 @@ public static function remove_insecure_properties( $theme_json ) {
31753234
}
31763235

31773236
$variation_output = static::remove_insecure_styles( $variation_input );
3237+
3238+
// Process a variation's elements and element pseudo selector styles.
3239+
if ( isset( $variation_input['elements'] ) ) {
3240+
foreach ( $valid_element_names as $element_name ) {
3241+
$element_input = $variation_input['elements'][ $element_name ] ?? null;
3242+
if ( $element_input ) {
3243+
$element_output = static::remove_insecure_styles( $element_input );
3244+
3245+
if ( isset( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element_name ] ) ) {
3246+
foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element_name ] as $pseudo_selector ) {
3247+
if ( isset( $element_input[ $pseudo_selector ] ) ) {
3248+
$element_output[ $pseudo_selector ] = static::remove_insecure_styles( $element_input[ $pseudo_selector ] );
3249+
}
3250+
}
3251+
}
3252+
3253+
if ( ! empty( $element_output ) ) {
3254+
_wp_array_set( $variation_output, array( 'elements', $element_name ), $element_output );
3255+
}
3256+
}
3257+
}
3258+
}
3259+
31783260
if ( ! empty( $variation_output ) ) {
31793261
_wp_array_set( $sanitized, $variation['path'], $variation_output );
31803262
}

src/wp-settings.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@
366366
require ABSPATH . WPINC . '/block-supports/utils.php';
367367
require ABSPATH . WPINC . '/block-supports/align.php';
368368
require ABSPATH . WPINC . '/block-supports/background.php';
369+
require ABSPATH . WPINC . '/block-supports/block-style-variations.php';
369370
require ABSPATH . WPINC . '/block-supports/border.php';
370371
require ABSPATH . WPINC . '/block-supports/colors.php';
371372
require ABSPATH . WPINC . '/block-supports/custom-classname.php';
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/*
2+
Theme Name: Block Theme Child With Block Style Variations Theme
3+
Theme URI: https://wordpress.org/
4+
Description: For testing purposes only.
5+
Template: block-theme
6+
Version: 1.0.0
7+
Text Domain: block-theme-child-with-block-style-variations
8+
*/
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"version": 3,
3+
"blockTypes": [ "core/group", "core/columns", "core/media-text" ],
4+
"styles": {
5+
"color": {
6+
"background": "darkcyan",
7+
"text": "aliceblue"
8+
}
9+
}
10+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"$schema": "https://schemas.wp.org/trunk/theme.json",
3+
"version": 3
4+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"version": 3,
3+
"blockTypes": [ "core/group", "core/columns" ],
4+
"styles": {
5+
"color": {
6+
"background": "indigo",
7+
"text": "plum"
8+
}
9+
}
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"version": 3,
3+
"blockTypes": [ "core/group", "core/columns" ],
4+
"styles": {
5+
"color": {
6+
"background": "midnightblue",
7+
"text": "lightblue"
8+
}
9+
}
10+
}

0 commit comments

Comments
 (0)