Skip to content

Section specific Theme.json #40318

Description

@jorgefilipecosta

Part of: #39281

We already allow a block to have the style object of theme.json, but for now, we only accept styles affecting the block itself, not its descendants. E.g., in the group, we can not say the text color of paragraphs nested inside them is X.

We don’t have a settings object, so we can not say the color palette for this section is Y.

This task is about expanding the style shape to support everything theme.json does and a new settings object.

Although the implementation of the settings should be ready to support every setting, I guess we can lock it down at the start to change the color palette and allow to totally disable colors so patterns can provide a more locked experience inside them. I’m not totally sure if we should support every setting so having an initial locked implementation makes sense, and then we can progressively expand what we support and maybe even support everything if we find it makes sense.

There are multiple edge cases, e.g., the theme.json sets a palette specific to the paragraph block, and the section in a pattern sets a palette specific to pattern. A paragraph inside that pattern uses the color palette of the pattern or the global one of the paragraph?

I think the algorithm should be the block uses the closest style/setting specific to that block (first of the pattern than at the global) if there are no block particular values, it uses the closest global value. In the previous sample, it would use the global paragraph color.

In the first version, I expect that we can store styles and settings as just normal block attributes in the HTML comment blob like we do now for styles.

Tasks

Settings:

Styles:

  • Block type styles: create a blocks section to hold and render them, ala theme.json.
  • Expose UI to users.
  • Absorb named attributes into the style object, e.g.: we should not store the font size value in fontSize if it's a preset value and in styles.typography,fontSize if it's a custom value. We should use the style object for everything. Do so without modifying how is serialized (to classes if it's a preset value and as inline styles if it's a custom one).
    • backgroundColor
    • borderColor
    • fontFamily
    • fontSize
    • gradient
    • textColor

Activity

  1. added
    [Block] GroupAffects the Group Block (and row, stack and grid variants)
    Global StylesAnything related to the broader Global Styles efforts, including Styles Engine and theme.json
    on Apr 13, 2022
  2. Humanify-nl commented on Apr 16, 2022

    @Humanify-nl

    Happy to see this, theme.json not cascading like CSS has been weird!

    Cascade
    I’m all for making theme.json behave as close to css practices as possible. It is often what is expected from perspective of developers diving into Gutenberg for the first time, and it makes a lot of sense.

    I’d like to propose adding font sizes as well to this (see e.g. #39277)

    Sectioned content
    One of the major problems in writing customized css is not being able to know (dynamically) if a group is a direct child of root-container and as such a section. I have to determine this by e.g. it’s position , if it’s full width, writing complex selectors. Same goes for determining if two section both have background color, and how to remove margins vertically on those conditions.

    Then came template parts, who seem to become the same thing, but do NOT play nice with the ‘main groups’.

    Will there be any plans to ‘set a group as section’? Or even a dedicated section block? I feel template parts and sections should ultimately be close to the same thing in terms of DOM output. Because they create highly sectioned and thematically (and in terms of content) similar areas that are interchangeable.

  3. oandregal commented on Apr 22, 2022

    @oandregal
    Member

    Miguel and I started looking into this and prepared #40547 I've updated the issue description with a preliminary list of tasks.

  4. oandregal commented on Apr 22, 2022

    @oandregal
    Member

    We aim to introduce a new source for settings (& partially to styles), in addition to what we already use, the core/block-editor store. How do they should work together?

    Let's take a given setting, say color.palette, and unwrap how it should work for a children of the section. I see two alternatives

    By source algorithm (settings coming from block attributes first, settings coming from the block editor store later):

    1. blockAttributes.settings.blocks.blockType.color.palette
    2. blockAttributes.settings.color.palette
    3. store.settings.blocks.blockType.color.palette
    4. store.settings.color.palette

    By context algorithm (settings for the block first, settings for the global later - disregarding what the source is):

    1. blockAttributes.settings.blocks.blockType.color.palette
    2. store.settings.blocks.blockType.color.palette
    3. blockAttributes.settings.color.palette
    4. store.settings.color.palette

    For settings, the "by source" algorithm works best. This allows us to do things like this easily:

    • Set a different palette for this pattern and all its children:
      • by source: a single setting blockAttributes.settings.color.palette
      • by context, you need to address every inner block individually in addition to the section itself, using the same values:
        • blockAttributes.settings.color.palette (for the section itselt)
        • blockAttributes.settings.blocks.paragraph.color.palette (for any paragraph children)
        • blockAttributes.settings.blocks.list.color.palette (for any list children)
        • etc
    • Lock-in the ability to add custom font sizes in the section:
      • by source: blockAttributes.settings.typography.customFontSize
      • by context, you need to address every inner block individually, in addition to the section itself:
        • blockAttributes.settings.color.palette (for the section itselt)
        • blockAttributes.settings.blocks.paragraph.color.palette (for any paragraph children)
        • blockAttributes.settings.blocks.list.color.palette (for any list children)
        • etc

    For "styles", it doesn't matter which algorithm we use because they don't cascade (the top-level styles are attached to the "section", they're not used to fill the block styles):

    • Set a background color for the section: only addressable by blockAttributes.styles.color.background.
    • Set a text color for all the paragraphs within: only addressable by blockAttributes.styles.blocks.paragraph.color.text.

    I think the "by source algorithm" is a better representation of what we want to achieve: that sections become the source of truth for configuring whatever is within.

  5. oandregal commented on Apr 22, 2022

    @oandregal
    Member

    Another detail is: if the section is a group block and it has the following settings:

    {
      "settings": {
        "color": {
          "link": true
        },
        "blocks": {
          "core/group": {
            "color": {
              "link": false
            }
          }
        }
      }
    }

    The link color should be disabled for any group that is a children of the section. But, given the section is a group itself, what its behavior should be?

    I think the link should be enabled. This is, the settings.blocks only affect the children of the section but not the section itself.

  6. luisherranz commented on May 3, 2022

    @luisherranz
    Member

    I was reading your conversation on #40547 (comment), but as that PR is already merged, I'll answer here 🙂

    Have you considered going with the by context algorithm but adding the ability to change all blocks at once with "*"?

    @oandregal, in your example of #40547 (comment), instead of doing this to disable the typography settings of all the blocks (even the ones that have been explicitly configured)

    {
      "settings": {
        "typography": {
          "customFontSize": false,
          "dropCap": false,
          "fontSizes": [],
          "fontStyle": false,
          "fontWeight": false,
          "letterSpacing": false,
          "lineHeight": false,
          "textDecoration": false,
          "textTransform": false
        }
      }
    }

    you would do this:

    {
      "settings": {
        "blocks": {
          "*": {
            "typography": {
              "customFontSize": false,
              "dropCap": false,
              "fontSizes": [],
              "fontStyle": false,
              "fontWeight": false,
              "letterSpacing": false,
              "lineHeight": false,
              "textDecoration": false,
              "textTransform": false
            }
          }
        }
      }
    }

    That is saying, "I want to overwrite the explicit settings of all the blocks".

    That way, it's easy to overwrite both the global settings and the explicit block settings.

  7. mtias commented on Jun 24, 2022

    @mtias
    Member

    Noting that this is also related to #27233.

  8. adamziel commented on Jul 1, 2022

    @adamziel
    Contributor

    Related: WordPress/wordpress-develop#2853 adds support for array-based style property in block.json. For example:

    {
    	// ...
    	"style": [
    		"wp-block-button",
    		{
    			"border": {
    				"//": "100% causes an oval, but any explicit but really high value retains the pill shape.",
    				"radius": "9999px"
    			},
    			"color": {
    				"text": "#fff",
    				"background": "#32373c"
    			}
    		}
    	}
    }
  9. SaxonF commented on Aug 9, 2023

    @SaxonF
    Contributor

    Summarising one of main the problems we're trying to solve here:

    When designing the sections of a page (or even entire pages/templates) you often want to vary outside the default element styling of your site (e.g. a dark section with light text). To do this currently you need to manually set background and text colour each and every time and there are gaps in what you can control within a section (e.g.buttons). This project aims to solve that pain point by allowing you to create sets of styles that can be applied to sections or even entire pages.

    Here's an initial exploration that combines rethinking the global styles IA with some previous explorations around colour sets. We're extending the idea of colour sets to all styles.

    element-variations.mp4

    Note that we'd include element selection within the page/template inspector as well so you can style entire pages.

    There are a couple of trade-offs that come with this work that would be worth discussing.

    • It adds complexity as we now have themes, presets, style variations, block variations, element variations (or whatever we call this). That's a lot and possibly overwhelming.
    • Switching themes we'd have to revert sections back to default variations
    • With the solution proposed here we'd have to ship this first

    @WordPress/gutenberg-design

  10. 10 remaining items

  11. moved this from 📥 Todo to ❓ Triage in WordPress 6.5 Editor Taskson Nov 23, 2023
  12. removed their assignment
    on Dec 18, 2023
  13. youknowriad commented on Oct 24, 2024

    @youknowriad
    Contributor

    @aaronrobertshaw Should we close this issue? Are there any other iterations planned for this?

  14. mtias commented on Oct 26, 2024

    @mtias
    Member

    @youknowriad I think related to this is the ability to save these chunks as their own entries in wp_global_styles and then being able to attach them to patterns / templates. Not sure if that is captured beyond this issue.

  15. mtias commented on Oct 26, 2024

    @mtias
    Member

    I guess we have it in #53480.

  16. youknowriad commented on Oct 26, 2024

    @youknowriad
    Contributor

    And this one as well #45371
    That seems like a nice quick win.

  17. aaronrobertshaw commented on Oct 28, 2024

    @aaronrobertshaw
    Contributor

    I think related to this is the ability to save these chunks as their own entries in wp_global_styles and then being able to attach them to patterns / templates. Not sure if that is captured beyond this iss

    Along with the issues linked already, #62686, would also factor into the ability to manage style objects.

    We don’t have a settings object, so we can not say the color palette for this section is Y.

    This is the part of the issue I'm most unclear on and so am not 100% sure if it can be closed.

    The context here is that we had this along with things like colorways and typesets as separate yet partially overlapping concepts. I was never able to achieve a consensus on how we could unify them all. The end result was that Section Styles evolved into an extension of the Block Styles feature. That mechanism doesn't currently support theme.json settings within them, they're purely styles at this point.

    Separate to the block style variations though it is possible to add theme.json settings within a block's attributes. The catch is the two feature aren't integrated.

    If we're happy that Section Styles meets the primary goal of this issue, then I think this can be closed out.

    We can create fresh, focused, issues for required enhancements like custom settings (e.g. palettes) within block style variations. Those can then take into account more recent developments, like support for theme.json partials defining block style variations, what comes of #62686 etc.


    TL;DR

    I'm not 100% sure due to Section Styles not supporting settings yet but if pushed I'd lean towards closing this one out and creating something fresh and clear given the current situation.

  18. youknowriad commented on Nov 4, 2024

    @youknowriad
    Contributor

    What would it take for sections styles to support settings?

  19. aaronrobertshaw commented on Nov 5, 2024

    @aaronrobertshaw
    Contributor

    What would it take for sections styles to support settings?

    That's a good question!

    Given the experience of implementing the first iteration of section styles, I'm sure there'll be plenty of edge cases and a few blockers to work around. So first of all, it might take some time.

    The most immediate question for me is whether this should be done before Global Styles & Theme.json: Next Steps reaches its conclusion or not. If we're completely reworking how we'll use the global styles post type, that might provide a lot of freedom and power to clean up the data around theme.json and/or block style variations. If we proceed on supporting settings for block style variations now, it's one more thing to untangle later.

    To move forward now, we'll need to settle on where we store the settings for variations. My expectation is that we'll have to include them under settings.blocks.{blockName}.variations.{variationName}. The variation settings might also need to be merged once and stored within the overall theme.json data to avoid having to repeatedly merge settings each time they're applied.

    Another catch with the merging of settings is how this is impacted when block style variations get nested within other block style variations.


    Theme.json Partials

    It's been on the cards for a while that these partials would eventually be a complete theme.json file, including settings. When they get processed and their styles merged into the theme.json under styles.blocks.{blockName}.variations.{variationName}, I imagine the same will occur for the settings.

    This would come unstuck though if the block style variation settings need to take into account settings for other variations (or blocks with settings within block attributes). Could the next iteration of section styles only support settings that are a result of merging the main theme.json settings with the variation's?

    Programmatically Registered Block Styles

    The register_block_style function currently only supports receiving a style object via its style_properties.style_data parameter. That structure could be deprecated, and tweaked if detected, so that the function expects a theme.json object i.e. one that has top level properties for settings and styles. From there, it would be consistent again with the theme.json partials above.

    Theme.json Defined Block Styles

    Section styles needed to allow block style variations to be defined within a theme style variation's theme.json file. So unlike the theme.json partial, the variation's settings would need to be scoped in a way that maps to the variation. Hence the settings.blocks.{blockName}.variations.{variationName} path expected above. Essentially the settings equivalent of the styles path for variations.

    This is one of the parts that might change depending on how we end up leveraging the global styles CPT in the future.


    The current style generation for block style variations re-uses the existing theme.json processing for the most part. The inner workings of the theme.json class are getting more and more complex, which adds a little concern around how supporting settings will pan out here. That isn't to say it can't be done just that there is more potential to encounter various hiccups.

    The proposed enhancement likely provides more direct benefit to theme authors than end users. Could that then mean it would be better to wait until the dust settles on Global Styles & Theme.json: Next Steps after all?

  20. youknowriad commented on Nov 5, 2024

    @youknowriad
    Contributor

    Could that then mean it would be better to wait until the dust settles onhttps://github.com//issues/62686 after all?

    Sure, I guess for me, addressing #62686 will create these user style variations (with settings) automatically. I thought "settings" missing from the "sections" styles was preventing that work because in my mind, I actually don't see any difference from the sections styles variations or the global styles variations. and ideally I don't see these as nested styles.blocks.{blockName}.variations.{variationName} ... or settings.blocks.{blockName}.variations.{variationName} (I know we store these like that today) but more as just top level theme.json files that are separate from the main one.

  21. aaronrobertshaw commented on Nov 6, 2024

    @aaronrobertshaw
    Contributor

    I thought "settings" missing from the "sections" styles was preventing that work because in my mind, I actually don't see any difference from the sections styles variations or the global styles variations. and ideally I don't see these as nested

    As you note, ideally, these should all be standalone theme.json objects. They can then be composed in many different ways. This is partly why I was cautious around further nesting settings under the main theme.json.

    I'm not sure if section styles not having their own settings prevents work on #62686 or not. If it helps shed some light on how we get from here to #62686, I'd be happy to explore the changes outlined above and see what that uncovers.

  22. unscripted commented on May 25, 2025

    @unscripted
    Member

    Bumping this as it seems #62686 is also stalled.

    Exposing the settings in a section-1.json would be amazing. My use case is semantic-named design tokens, where I have settings.custom.color.border set in theme.json. If I could simply update the value for that token in the section style, I wouldn't need to redefine the styles for each block.

  23. fabiankaegy commented on May 26, 2025

    @fabiankaegy
    Member

    @unscripted I ran into the exact same thing and decided to solve it in a custom plugin for me. I do think that there are probably too many edge cases for core to support this any time soon because you can do so much inside the settings section of the theme.json file. All I needed was being able to remap the variables in a section.

    This here is the code I use:

    add_action( 'init', 'generate_custom_style_variation_styles', 10, 2 );
    /**
     * Render style variation styles.
     *
     * @return void
     */
    function generate_custom_style_variation_styles() {
    	$style_variations = \WP_Theme_JSON_Resolver::get_style_variations( 'block' );
    
    	$styles = [];
    
    	foreach ( $style_variations as $style_variation ) {
    		$title       = $style_variation['title'];
    		$slug        = $style_variation['slug'];
    		$block_types = $style_variation['blockTypes'];
    		$settings    = isset( $style_variation['settings'] ) ? $style_variation['settings'] : [];
    
    		if ( empty( $block_types ) || empty( $settings ) ) {
    			continue;
    		}
    
    		$lowercase_title = strtolower( str_replace( ' ', '-', $title ) );
    		$variation_name  = ! empty( $slug ) ? $slug : $lowercase_title;
    		$variation_class = "is-style-{$variation_name}";
    
    		$block_type_classes = array_map(
    			'wp_get_block_default_classname',
    			$block_types
    		);
    
    		// ensure that the classes all start with a dot
    		$block_type_classes = array_map(
    			function ( $block_type ) {
    				return ".{$block_type}";
    			},
    			$block_type_classes
    		);
    		$block_type_classes = implode( ', ', $block_type_classes );
    
    		$declarations  = [];
    		$custom_values = isset( $settings['custom'] ) ? $settings['custom'] : [];
    		$css_vars      = flatten_tree( $custom_values );
    		foreach ( $css_vars as $key => $value ) {
    			$declarations[ '--wp--custom--' . $key ] = $value;
    		}
    
    		$styles[] = [
    			'selector'     => ":is({$block_type_classes}).{$variation_class}",
    			'declarations' => $declarations,
    		];
    	}
    
    	wp_style_engine_get_stylesheet_from_css_rules( $styles, [ 'context' => 'tenup--custom-section-style-variables' ] );
    }
    
    add_filter( 'enqueue_block_assets', 'enqueue_custom_section_style_styles' );
    
    /**
     * Enqueue custom section style styles.
     */
    function enqueue_custom_section_style_styles() {
    	wp_register_style( 'tenup-custom-section-style-variables', false ); // phpcs:ignore
    	wp_enqueue_style( 'tenup-custom-section-style-variables' );
    
    	$custom_section_style_variables = wp_style_engine_get_stylesheet_from_context( 'tenup--custom-section-style-variables' );
    	if ( ! empty( $custom_section_style_variables ) ) {
    		wp_add_inline_style( 'tenup-custom-section-style-variables', $custom_section_style_variables );
    	}
    }
    
    
    /**
     * Given a tree, it creates a flattened one
     * by merging the keys and binding the leaf values
     * to the new keys.
     *
     * It also transforms camelCase names into kebab-case
     * and substitutes '/' by '-'.
     *
     * This is thought to be useful to generate
     * CSS Custom Properties from a tree,
     * although there's nothing in the implementation
     * of this function that requires that format.
     *
     * For example, assuming the given prefix is '--wp'
     * and the token is '--', for this input tree:
     *
     *     {
     *       'some/property': 'value',
     *       'nestedProperty': {
     *         'sub-property': 'value'
     *       }
     *     }
     *
     * it'll return this output:
     *
     *     {
     *       '--wp--some-property': 'value',
     *       '--wp--nested-property--sub-property': 'value'
     *     }
     *
     * @param array  $tree   Input tree to process.
     * @param string $prefix Optional. Prefix to prepend to each variable. Default empty string.
     * @param string $token  Optional. Token to use between levels. Default '--'.
     * @return array The flattened tree.
     */
    function flatten_tree( $tree, $prefix = '', $token = '--' ) {
    	$result = array();
    	foreach ( $tree as $property => $value ) {
    		$new_key = $prefix . str_replace(
    			'/',
    			'-',
    			strtolower( _wp_to_kebab_case( $property ) )
    		);
    
    		if ( is_array( $value ) ) {
    			$new_prefix        = $new_key . $token;
    			$flattened_subtree = flatten_tree( $value, $new_prefix, $token );
    			foreach ( $flattened_subtree as $subtree_key => $subtree_value ) {
    				$result[ $subtree_key ] = $subtree_value;
    			}
    		} else {
    			$result[ $new_key ] = $value;
    		}
    	}
    	return $result;
    }
  24. justintadlock commented on Jul 21, 2025

    @justintadlock
    Contributor

    wp_style_engine_get_stylesheet_from_css_rules() was not handling CSS light-dark() for me (see @fabiankaegy's example above), so I ended up going with a different example. I also needed to be able to remap settings.custom for my section styles.

    In case it helps someone:

    <?php 
    
    namespace X3P0\Ideas\Block;
    
    use WP_Style_Engine_CSS_Rule;
    use WP_Theme_JSON_Resolver;
    
    class StyleVariations
    {
    	private array $css_rules = [];
    
    	public function boot(): void
    	{
    		add_action('init', [$this, 'registerCssRules']);
    		add_action('enqueue_block_assets', [$this, 'enqueueAssets']);
    	}
    
    	public function registerCssRules(): void
    	{
    		$variations = WP_Theme_JSON_Resolver::get_style_variations('block');
    
    		foreach ($variations as $variation) {
    			if (
    				empty($variation['slug'])
    				|| empty($variation['blockTypes'])
    				|| empty($variation['settings']['custom'] ?? null)
    			) {
    				continue;
    			}
    
    			$declarations  = [];
    			$css_vars = static::flattenTree($variation['settings']['custom']);
    
    			foreach ($css_vars as $property => $value) {
    				$declarations["--wp--custom--{$property}"] = $value;
    			}
    
    			if ($declarations) {
    				$this->css_rules[] = new WP_Style_Engine_CSS_Rule(
    					":root :where(.is-style-{$variation['slug']})",
    					$base_declarations
    				);
    			}
    		}
    	}
    
    	public function enqueueAssets(): void
    	{
    		if ([] === $this->css_rules) {
    			return;
    		}
    
    		$style = '';
    
    		foreach ($this->css_rules as $rule) {
    			$declarations = '';
    
    			foreach ($rule->get_declarations()->get_declarations() as $property => $value) {
    				$declarations .= "{$property}: {$value};";
    			}
    
    			$style .= "{$rule->get_selector()} { {$declarations} }";
    		}
    
    		wp_register_style('x3p0-ideas-block-styles-custom', false);
    		wp_add_inline_style('x3p0-ideas-block-styles-custom', $style);
    		wp_enqueue_style('x3p0-ideas-block-styles-custom');
    	}
    
    	private static function flattenTree(array $tree, string $prefix = ''): array
    	{
    		$result = [];
    
    		foreach ($tree as $property => $value) {
    			$kebab = strtolower(_wp_to_kebab_case($property));
    			$key   = $prefix . str_replace('/', '-', $kebab);
    
    			if (is_array($value)) {
    				$result = array_replace(
    					$result,
    					static::flattenTree($value, "{$key}--")
    				);
    				continue;
    			}
    
    			$result[$key] = $value;
    		}
    
    		return $result;
    	}
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Global StylesAnything related to the broader Global Styles efforts, including Styles Engine and theme.json[Block] GroupAffects the Group Block (and row, stack and grid variants)[Block] PatternAffects the Patterns Block[Type] EnhancementA suggestion for improvement.

    Type

    No type

    Projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions