Skip to content

Commit

Permalink
Block Themes: Add section styling via extended block style variations
Browse files Browse the repository at this point in the history
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
  • Loading branch information
noisysocks committed May 31, 2024
1 parent 103856d commit f91931a
Show file tree
Hide file tree
Showing 14 changed files with 920 additions and 70 deletions.
424 changes: 424 additions & 0 deletions src/wp-includes/block-supports/block-style-variations.php

Large diffs are not rendered by default.

34 changes: 32 additions & 2 deletions src/wp-includes/class-wp-theme-json-resolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -701,16 +701,46 @@ private static function recursively_iterate_json( $dir ) {
return $nested_json_files;
}

/**
* Determines if a supplied style variation matches the provided scope.
*
* For backwards compatibility, if a variation does not define any scope
* related property, e.g. `blockTypes`, it is assumed to be a theme style
* variation.
*
* @since 6.6.0
*
* @param array $variation Theme.json shaped style variation object.
* @param string $scope Scope to check e.g. theme, block etc.
*
* @return boolean
*/
private static function style_variation_has_scope( $variation, $scope ) {
if ( 'block' === $scope ) {
return isset( $variation['blockTypes'] );
}

if ( 'theme' === $scope ) {
return ! isset( $variation['blockTypes'] );
}

return false;
}

/**
* Returns the style variations defined by the theme.
*
* @since 6.0.0
* @since 6.2.0 Returns parent theme variations if theme is a child.
* @since 6.6.0 Added configurable scope parameter to allow filtering
* theme.json partial files by the scope to which they
* can be applied e.g. theme vs block etc.
*
* @param string $scope The scope or type of style variation to retrieve e.g. theme, block etc.
*
* @return array
*/
public static function get_style_variations() {
public static function get_style_variations( $scope = 'theme' ) {
$variation_files = array();
$variations = array();
$base_directory = get_stylesheet_directory() . '/styles';
Expand All @@ -733,7 +763,7 @@ public static function get_style_variations() {
ksort( $variation_files );
foreach ( $variation_files as $path => $file ) {
$decoded_file = wp_json_file_decode( $path, array( 'associative' => true ) );
if ( is_array( $decoded_file ) ) {
if ( is_array( $decoded_file ) && static::style_variation_has_scope( $decoded_file, $scope ) ) {
$translated = static::translate( $decoded_file, wp_get_theme()->get( 'TextDomain' ) );
$variation = ( new WP_Theme_JSON( $translated ) )->get_raw_data();
if ( empty( $variation['title'] ) ) {
Expand Down
104 changes: 93 additions & 11 deletions src/wp-includes/class-wp-theme-json.php
Original file line number Diff line number Diff line change
Expand Up @@ -346,9 +346,11 @@ class WP_Theme_JSON {
* @since 5.9.0 Renamed from `ALLOWED_TOP_LEVEL_KEYS` to `VALID_TOP_LEVEL_KEYS`,
* added the `customTemplates` and `templateParts` values.
* @since 6.3.0 Added the `description` value.
* @since 6.6.0 Added `blockTypes` to support block style variation theme.json partials.
* @var string[]
*/
const VALID_TOP_LEVEL_KEYS = array(
'blockTypes',
'customTemplates',
'description',
'patterns',
Expand Down Expand Up @@ -823,6 +825,7 @@ protected static function do_opt_in_into_settings( &$context ) {
* @since 5.8.0
* @since 5.9.0 Added the `$valid_block_names` and `$valid_element_name` parameters.
* @since 6.3.0 Added the `$valid_variations` parameter.
* @since 6.6.0 Updated schema to allow extended block style variations.
*
* @param array $input Structure to sanitize.
* @param array $valid_block_names List of valid block names.
Expand Down Expand Up @@ -881,6 +884,27 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n

$schema_styles_blocks = array();
$schema_settings_blocks = array();

/*
* Generate a schema for blocks.
* - Block styles can contain `elements` & `variations` definitions.
* - Variations definitions cannot be nested.
* - Variations can contain styles for inner `blocks`.
* - Variation inner `blocks` styles can contain `elements`.
*
* As each variation needs a `blocks` schema but further nested
* inner `blocks`, the overall schema will be generated in multiple passes.
*/
foreach ( $valid_block_names as $block ) {
$schema_settings_blocks[ $block ] = static::VALID_SETTINGS;
$schema_styles_blocks[ $block ] = $styles_non_top_level;
$schema_styles_blocks[ $block ]['elements'] = $schema_styles_elements;
}

$block_style_variation_styles = static::VALID_STYLES;
$block_style_variation_styles['blocks'] = $schema_styles_blocks;
$block_style_variation_styles['elements'] = $schema_styles_elements;

foreach ( $valid_block_names as $block ) {
// Build the schema for each block style variation.
$style_variation_names = array();
Expand All @@ -897,12 +921,9 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n

$schema_styles_variations = array();
if ( ! empty( $style_variation_names ) ) {
$schema_styles_variations = array_fill_keys( $style_variation_names, $styles_non_top_level );
$schema_styles_variations = array_fill_keys( $style_variation_names, $block_style_variation_styles );
}

$schema_settings_blocks[ $block ] = static::VALID_SETTINGS;
$schema_styles_blocks[ $block ] = $styles_non_top_level;
$schema_styles_blocks[ $block ]['elements'] = $schema_styles_elements;
$schema_styles_blocks[ $block ]['variations'] = $schema_styles_variations;
}

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

/*
* Shared block style variations can be registered from the theme.json data so we can't
* validate them against pre-registered block style variations.
*/
$schema['styles']['blocks']['variations'] = null;

// Remove anything that's not present in the schema.
foreach ( array( 'styles', 'settings' ) as $subtree ) {
if ( ! isset( $input[ $subtree ] ) ) {
Expand Down Expand Up @@ -1016,16 +1043,36 @@ protected static function prepend_to_selector( $selector, $to_prepend ) {
* @since 5.9.0 Added `duotone` key with CSS selector.
* @since 6.1.0 Added `features` key with block support feature level selectors.
* @since 6.3.0 Refactored and stabilized selectors API.
* @since 6.6.0 Updated to include block style variations from the block styles registry.
*
* @return array Block metadata.
*/
protected static function get_blocks_metadata() {
$registry = WP_Block_Type_Registry::get_instance();
$blocks = $registry->get_all_registered();
$registry = WP_Block_Type_Registry::get_instance();
$blocks = $registry->get_all_registered();
$style_registry = WP_Block_Styles_Registry::get_instance();

// Is there metadata for all currently registered blocks?
$blocks = array_diff_key( $blocks, static::$blocks_metadata );
if ( empty( $blocks ) ) {
/*
* New block styles may have been registered within WP_Block_Styles_Registry.
* Update block metadata for any new block style variations.
*/
$registered_styles = $style_registry->get_all_registered();
foreach ( static::$blocks_metadata as $block_name => $block_metadata ) {
if ( ! empty( $registered_styles[ $block_name ] ) ) {
$style_selectors = $block_metadata['styleVariations'] ?? array();

foreach ( $registered_styles[ $block_name ] as $block_style ) {
if ( ! isset( $style_selectors[ $block_style['name'] ] ) ) {
$style_selectors[ $block_style['name'] ] = static::get_block_style_variation_selector( $block_style['name'], $block_metadata['selector'] );
}
}

static::$blocks_metadata[ $block_name ]['styleVariations'] = $style_selectors;
}
}
return static::$blocks_metadata;
}

Expand Down Expand Up @@ -1060,11 +1107,20 @@ protected static function get_blocks_metadata() {
}

// If the block has style variations, append their selectors to the block metadata.
$style_selectors = array();
if ( ! empty( $block_type->styles ) ) {
$style_selectors = array();
foreach ( $block_type->styles as $style ) {
$style_selectors[ $style['name'] ] = static::get_block_style_variation_selector( $style['name'], static::$blocks_metadata[ $block_name ]['selector'] );
}
}

// Block style variations can be registered through the WP_Block_Styles_Registry as well as block.json.
$registered_styles = $style_registry->get_registered_styles_for_block( $block_name );
foreach ( $registered_styles as $style ) {
$style_selectors[ $style['name'] ] = static::get_block_style_variation_selector( $style['name'], static::$blocks_metadata[ $block_name ]['selector'] );
}

if ( ! empty( $style_selectors ) ) {
static::$blocks_metadata[ $block_name ]['styleVariations'] = $style_selectors;
}
}
Expand Down Expand Up @@ -1158,16 +1214,18 @@ public function get_settings() {
* @since 5.8.0
* @since 5.9.0 Removed the `$type` parameter, added the `$types` and `$origins` parameters.
* @since 6.3.0 Add fallback layout styles for Post Template when block gap support isn't available.
* @since 6.6.0 Added `skip_root_layout_styles` option to omit layout styles if desired.
*
* @param string[] $types Types of styles to load. Will load all by default. It accepts:
* - `variables`: only the CSS Custom Properties for presets & custom ones.
* - `styles`: only the styles section in theme.json.
* - `presets`: only the classes for the presets.
* @param string[] $origins A list of origins to include. By default it includes VALID_ORIGINS.
* @param array $options An array of options for now used for internal purposes only (may change without notice).
* The options currently supported are 'scope' that makes sure all style are scoped to a
* given selector, and root_selector which overwrites and forces a given selector to be
* used on the root node.
* The options currently supported are:
* - 'scope' that makes sure all style are scoped to a given selector
* - `root_selector` which overwrites and forces a given selector to be used on the root node
* - `skip_root_layout_styles` which omits root layout styles from the generated stylesheet.
* @return string The resulting stylesheet.
*/
public function get_stylesheet( $types = array( 'variables', 'styles', 'presets' ), $origins = null, $options = array() ) {
Expand Down Expand Up @@ -1220,7 +1278,7 @@ public function get_stylesheet( $types = array( 'variables', 'styles', 'presets'
}

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

$variation_output = static::remove_insecure_styles( $variation_input );

// Process a variation's elements and element pseudo selector styles.
if ( isset( $variation_input['elements'] ) ) {
foreach ( $valid_element_names as $element_name ) {
$element_input = $variation_input['elements'][ $element_name ] ?? null;
if ( $element_input ) {
$element_output = static::remove_insecure_styles( $element_input );

if ( isset( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element_name ] ) ) {
foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element_name ] as $pseudo_selector ) {
if ( isset( $element_input[ $pseudo_selector ] ) ) {
$element_output[ $pseudo_selector ] = static::remove_insecure_styles( $element_input[ $pseudo_selector ] );
}
}
}

if ( ! empty( $element_output ) ) {
_wp_array_set( $variation_output, array( 'elements', $element_name ), $element_output );
}
}
}
}

if ( ! empty( $variation_output ) ) {
_wp_array_set( $sanitized, $variation['path'], $variation_output );
}
Expand Down
1 change: 1 addition & 0 deletions src/wp-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@
require ABSPATH . WPINC . '/block-supports/utils.php';
require ABSPATH . WPINC . '/block-supports/align.php';
require ABSPATH . WPINC . '/block-supports/background.php';
require ABSPATH . WPINC . '/block-supports/block-style-variations.php';
require ABSPATH . WPINC . '/block-supports/border.php';
require ABSPATH . WPINC . '/block-supports/colors.php';
require ABSPATH . WPINC . '/block-supports/custom-classname.php';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
Theme Name: Block Theme Child With Block Style Variations Theme
Theme URI: https://wordpress.org/
Description: For testing purposes only.
Template: block-theme
Version: 1.0.0
Text Domain: block-theme-child-with-block-style-variations
*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"version": 3,
"blockTypes": [ "core/group", "core/columns", "core/media-text" ],
"styles": {
"color": {
"background": "darkcyan",
"text": "aliceblue"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"version": 3,
"blockTypes": [ "core/group", "core/columns" ],
"styles": {
"color": {
"background": "indigo",
"text": "plum"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"version": 3,
"blockTypes": [ "core/group", "core/columns" ],
"styles": {
"color": {
"background": "midnightblue",
"text": "lightblue"
}
}
}
Loading

0 comments on commit f91931a

Please sign in to comment.