Issue #3631324: Generate more of the global stylesheet from the theme configuration

Summary

Ports more of WP_Theme_JSON::get_stylesheet() (Gutenberg 24.1) so themes can do in *.gutenberg.yml what a theme.json theme does in configuration.

 getActiveThemeMergedDefinition()
-  array_merge() of theme-support (a sub-theme replaced whole keys)
+  mergeThemeSupport()          maps merge, lists replace (like theme.json)
-  THEMENAME_gutenberg_definition_alter() of the default theme
+  THEMENAME_gutenberg_definition_alter() of the base themes, then the default theme
+  expandAppearanceTools()      APPEARANCE_TOOLS_OPT_INS, root and blocks
   PresetCssGenerator
-    body { --wp--preset--* }
+    :root { --wp--preset--*, --wp--custom--* }   (like WordPress: usable on <html>)
+    (the --wp--style--* layout variables stay on body)
+    custom → --wp--custom--*   (root and per block)
+    fluid font size presets → clamp()
   GlobalStylesGenerator
+    elements: :hover, :focus, :focus-visible, :active… of link and button
+    blocks: block.json selectors (feature selectors), states of core/button
+            and core/navigation-link, elements and variations (.is-style-x)
+    block gaps: spacing.blockGap of a block → .wp-block-x-is-layout-*
+    custom css of the root, elements, blocks and variations (& nesting)
+    root padding: --wp--style--root--padding-* and .has-global-padding
+    :root :where() on class selectors, like WordPress
   styles[] in the editor
-  presets, theme files, global styles
+  presets, global styles, theme files   (same order as the frontend)

Also:

  • gutenberg_get_global_settings(), gutenberg_get_global_styles() and DuotoneProcessor read the merged definition instead of the base theme's (or the active theme's) own one.
  • A base theme's styles files are resolved relative to the base theme, not the active sub-theme.
  • Non-core blocks get .wp-block-<namespace>-<name> like WordPress (drupal/field → .wp-block-drupal-field, was .wp-block-field).
  • The preset CSS is generated once with the definition instead of on every request.
  • README "Theme configuration" documents it.

Not ported: shared styles.variations, responsive states (@mobile…) in global styles, the fallback block gaps of themes without the block gap support (the .wp-container-* styles of LayoutProcessor apply the block's default gap) and the top/bottom root padding (WordPress puts it on .wp-site-blocks).

Evidence

  • Before: global-styles.spec.mjs fails on 4.0.x (expect(received).toMatchObject(expected): no custom properties, no merge, no block selector). After: it passes. It installs a test base theme and a sub-theme that changes one custom value, and checks in the canvas and on the frontend: the merged --wp--custom--*, a fluid clamp() font size, the Button radius on .wp-block-button__link, the outline variation, a link :hover, the root padding of a constrained Group, a block gap, root and block custom CSS, the expanded appearanceTools and the base theme's stylesheet.
  • Unit tests (tests/src/Unit/): the generated CSS of states, block selectors, variations, elements in blocks, block gaps, custom CSS, root padding, custom properties, fluid sizes, the merge and appearanceTools.
  • The full Playwright suite passes.

Merge Danger

Door: two-way

Blast Radius: theme styling

  • Sub-themes that relied on replacing a whole theme-support key of their base theme now get it merged. Lists still replace.
  • The preset variables move from body to :root, like WordPress, so rules of <html> can use them. A theme that redefines them on body still wins; one that redefines them on :root now wins too, as its CSS comes later. The layout variables (--wp--style--*) stay on body: the default editor styles that the canvas loads define the block gap there.
  • The alter hooks of base themes now run for their sub-themes.
  • Global styles now come before the theme's editor stylesheets, as on the frontend, so a theme rule of the same specificity now wins in the editor too.
  • Block styles of non-core blocks change selector (see above).
Edited by Marco Fernandes

Merge request reports

Loading
Loading