diff --git a/src/wp-admin/menu.php b/src/wp-admin/menu.php index c9187399cec7a..e544175d153b4 100644 --- a/src/wp-admin/menu.php +++ b/src/wp-admin/menu.php @@ -237,7 +237,7 @@ } // Font Library menu item. -$submenu['themes.php'][8] = array( __( 'Fonts' ), 'edit_theme_options', 'font-library.php' ); +$submenu['themes.php'][9] = array( __( 'Fonts' ), 'edit_theme_options', 'font-library.php' ); $customize_url = add_query_arg( 'return', urlencode( remove_query_arg( wp_removable_query_args(), wp_unslash( $_SERVER['REQUEST_URI'] ) ) ), 'customize.php' ); diff --git a/src/wp-includes/block-editor.php b/src/wp-includes/block-editor.php index 40575f048624e..af873178eb7aa 100644 --- a/src/wp-includes/block-editor.php +++ b/src/wp-includes/block-editor.php @@ -525,41 +525,34 @@ function get_block_editor_settings( array $custom_settings, $block_editor_contex } } - if ( wp_theme_has_theme_json() ) { - $block_classes = array( - 'css' => 'styles', - '__unstableType' => 'theme', - 'isGlobalStyles' => true, - ); - $actual_css = wp_get_global_stylesheet( array( $block_classes['css'] ) ); - if ( '' !== $actual_css ) { - $block_classes['css'] = $actual_css; - $global_styles[] = $block_classes; - } - - /* - * Add the custom CSS as a separate stylesheet so any invalid CSS - * entered by users does not break other global styles. - */ - $global_styles[] = array( - 'css' => wp_get_global_stylesheet( array( 'custom-css' ) ), - '__unstableType' => 'user', - 'isGlobalStyles' => true, - ); - } else { - // If there is no `theme.json` file, ensure base layout styles are still available. - $block_classes = array( - 'css' => 'base-layout-styles', - '__unstableType' => 'base-layout', - 'isGlobalStyles' => true, - ); - $actual_css = wp_get_global_stylesheet( array( $block_classes['css'] ) ); - if ( '' !== $actual_css ) { - $block_classes['css'] = $actual_css; - $global_styles[] = $block_classes; - } + $block_classes = array( + 'css' => 'styles', + '__unstableType' => 'theme', + 'isGlobalStyles' => true, + ); + $actual_css = wp_get_global_stylesheet( array( $block_classes['css'] ) ); + if ( '' !== $actual_css ) { + $block_classes['css'] = $actual_css; + $global_styles[] = $block_classes; } + // Get any additional css from the customizer and add it before global styles custom CSS. + $global_styles[] = array( + 'css' => wp_get_custom_css(), + '__unstableType' => 'user', + 'isGlobalStyles' => false, + ); + + /* + * Add the custom CSS as a separate stylesheet so any invalid CSS + * entered by users does not break other global styles. + */ + $global_styles[] = array( + 'css' => wp_get_global_stylesheet( array( 'custom-css' ) ), + '__unstableType' => 'user', + 'isGlobalStyles' => true, + ); + $editor_settings['styles'] = array_merge( $global_styles, get_block_editor_theme_styles() ); $editor_settings['__experimentalFeatures'] = wp_get_global_settings(); diff --git a/src/wp-includes/class-wp-theme-json-resolver.php b/src/wp-includes/class-wp-theme-json-resolver.php index e696eef894783..4d5bf3dce9ee3 100644 --- a/src/wp-includes/class-wp-theme-json-resolver.php +++ b/src/wp-includes/class-wp-theme-json-resolver.php @@ -480,17 +480,6 @@ public static function get_user_data_from_wp_global_styles( $theme, $create_post $theme = wp_get_theme(); } - /* - * Bail early if the theme does not support a theme.json. - * - * Since wp_theme_has_theme_json() only supports the active - * theme, the extra condition for whether $theme is the active theme is - * present here. - */ - if ( $theme->get_stylesheet() === get_stylesheet() && ! wp_theme_has_theme_json() ) { - return array(); - } - $user_cpt = array(); $post_type_filter = 'wp_global_styles'; $stylesheet = $theme->get_stylesheet(); diff --git a/src/wp-includes/class-wp-theme-json.php b/src/wp-includes/class-wp-theme-json.php index ba2020813aa39..f9965a754989a 100644 --- a/src/wp-includes/class-wp-theme-json.php +++ b/src/wp-includes/class-wp-theme-json.php @@ -1326,12 +1326,13 @@ public function get_settings() { * @since 6.3.0 Add fallback layout styles for Post Template when block gap support isn't available. * @since 6.6.0 Added boolean `skip_root_layout_styles` and `include_block_style_variations` options * to control styles output as desired. + * @since 7.0.0 Deprecated 'base-layout-styles' type; added `base_layout_styles` option for classic themes. * * @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. - * - `base-layout-styles`: only the base layout styles. + * - `base-layout-styles`: only the base layout styles. Deprecated in 7.0.0. * - `custom-css`: only the custom CSS. * @param string[] $origins A list of origins to include. By default it includes VALID_ORIGINS. * @param array $options { @@ -1340,6 +1341,7 @@ public function get_settings() { * @type string $scope Makes sure all style are scoped to a given selector * @type string $root_selector Overwrites and forces a given selector to be used on the root node * @type bool $skip_root_layout_styles Omits root layout styles from the generated stylesheet. Default false. + * @type bool $base_layout_styles When true generates only base layout styles without alignment rules. Default false. * @type bool $include_block_style_variations Includes styles for block style variations in the generated stylesheet. Default false. * } * @return string The resulting stylesheet. @@ -1395,45 +1397,9 @@ public function get_stylesheet( $types = array( 'variables', 'styles', 'presets' if ( in_array( 'styles', $types, true ) ) { 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_root_layout_rules( $style_nodes[ $root_style_key ]['selector'], $style_nodes[ $root_style_key ], $options ); } $stylesheet .= $this->get_block_classes( $style_nodes ); - } elseif ( in_array( 'base-layout-styles', $types, true ) ) { - $root_selector = static::ROOT_BLOCK_SELECTOR; - $columns_selector = '.wp-block-columns'; - $post_template_selector = '.wp-block-post-template'; - if ( ! empty( $options['scope'] ) ) { - $root_selector = static::scope_selector( $options['scope'], $root_selector ); - $columns_selector = static::scope_selector( $options['scope'], $columns_selector ); - $post_template_selector = static::scope_selector( $options['scope'], $post_template_selector ); - } - if ( ! empty( $options['root_selector'] ) ) { - $root_selector = $options['root_selector']; - } - /* - * Base layout styles are provided as part of `styles`, so only output separately if explicitly requested. - * For backwards compatibility, the Columns block is explicitly included, to support a different default gap value. - */ - $base_styles_nodes = array( - array( - 'path' => array( 'styles' ), - 'selector' => $root_selector, - ), - array( - 'path' => array( 'styles', 'blocks', 'core/columns' ), - 'selector' => $columns_selector, - 'name' => 'core/columns', - ), - array( - 'path' => array( 'styles', 'blocks', 'core/post-template' ), - 'selector' => $post_template_selector, - 'name' => 'core/post-template', - ), - ); - - foreach ( $base_styles_nodes as $base_style_node ) { - $stylesheet .= $this->get_layout_styles( $base_style_node, $types ); - } } if ( in_array( 'presets', $types, true ) ) { @@ -1624,12 +1590,13 @@ protected function get_block_classes( $style_nodes ) { * @since 6.5.1 Only output rules referencing content and wide sizes when values exist. * @since 6.5.3 Add types parameter to check if only base layout styles are needed. * @since 6.6.0 Updated layout style specificity to be compatible with overall 0-1-0 specificity in global styles. + * @since 7.0.0 Replaced `$types` parameter with `$options` array; base layout styles controlled via `base_layout_styles` option. * * @param array $block_metadata Metadata about the block to get styles for. - * @param array $types Optional. Types of styles to output. If empty, all styles will be output. + * @param array $options Optional. An array of options for now used for internal purposes only. * @return string Layout styles for the block. */ - protected function get_layout_styles( $block_metadata, $types = array() ) { + protected function get_layout_styles( $block_metadata, $options = array() ) { $block_rules = ''; $block_type = null; @@ -1777,8 +1744,9 @@ protected function get_layout_styles( $block_metadata, $types = array() ) { foreach ( $base_style_rules as $base_style_rule ) { $declarations = array(); - // Skip outputting base styles for flow and constrained layout types if theme doesn't support theme.json. The 'base-layout-styles' type flags this. - if ( in_array( 'base-layout-styles', $types, true ) && ( 'default' === $layout_definition['name'] || 'constrained' === $layout_definition['name'] ) ) { + // Skip outputting base styles for flow and constrained layout types when base_layout_styles is enabled. + // These themes don't use .wp-site-blocks wrapper, so these layout-specific alignment styles aren't needed. + if ( ! empty( $options['base_layout_styles'] ) && ( 'default' === $layout_definition['name'] || 'constrained' === $layout_definition['name'] ) ) { continue; } @@ -3055,12 +3023,14 @@ static function ( $pseudo_selector ) use ( $selector ) { * @since 6.1.0 * @since 6.6.0 Use `ROOT_CSS_PROPERTIES_SELECTOR` for CSS custom properties and improved consistency of root padding rules. * Updated specificity of body margin reset and first/last child selectors. + * @since 7.0.0 Added `$options` parameter to control alignment styles output for classic themes. * * @param string $selector The root node selector. * @param array $block_metadata The metadata for the root block. + * @param array $options Optional. An array of options for now used for internal purposes only. * @return string The additional root rules CSS. */ - public function get_root_layout_rules( $selector, $block_metadata ) { + public function get_root_layout_rules( $selector, $block_metadata, $options = array() ) { $css = ''; $settings = $this->theme_json['settings'] ?? array(); $use_root_padding = isset( $this->theme_json['settings']['useRootPaddingAwareAlignments'] ) && true === $this->theme_json['settings']['useRootPaddingAwareAlignments']; @@ -3101,9 +3071,13 @@ public function get_root_layout_rules( $selector, $block_metadata ) { $css .= '.has-global-padding :where(:not(.alignfull.is-layout-flow) > .has-global-padding:not(.wp-block-block, .alignfull)) > .alignfull { margin-left: 0; margin-right: 0; }'; } - $css .= '.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }'; - $css .= '.wp-site-blocks > .alignright { float: right; margin-left: 2em; }'; - $css .= '.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; + // Skip outputting alignment styles when base_layout_styles is enabled. + // These styles target .wp-site-blocks which is only used by block themes. + if ( empty( $options['base_layout_styles'] ) ) { + $css .= '.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }'; + $css .= '.wp-site-blocks > .alignright { float: right; margin-left: 2em; }'; + $css .= '.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }'; + } // Block gap styles will be output unless explicitly set to `null`. See static::PROTECTED_PROPERTIES. if ( isset( $this->theme_json['settings']['spacing']['blockGap'] ) ) { @@ -3115,7 +3089,7 @@ public function get_root_layout_rules( $selector, $block_metadata ) { // For backwards compatibility, ensure the legacy block gap CSS variable is still available. $css .= static::ROOT_CSS_PROPERTIES_SELECTOR . " { --wp--style--block-gap: $block_gap_value; }"; } - $css .= $this->get_layout_styles( $block_metadata ); + $css .= $this->get_layout_styles( $block_metadata, $options ); return $css; } diff --git a/src/wp-includes/global-styles-and-settings.php b/src/wp-includes/global-styles-and-settings.php index 938648ad47fa2..d50ee14e22015 100644 --- a/src/wp-includes/global-styles-and-settings.php +++ b/src/wp-includes/global-styles-and-settings.php @@ -39,10 +39,7 @@ function wp_get_global_settings( $path = array(), $context = array() ) { * for clearing the cache appropriately. */ $origin = 'custom'; - if ( - ! wp_theme_has_theme_json() || - ( isset( $context['origin'] ) && 'base' === $context['origin'] ) - ) { + if ( isset( $context['origin'] ) && 'base' === $context['origin'] ) { $origin = 'theme'; } @@ -140,12 +137,12 @@ function wp_get_global_styles( $path = array(), $context = array() ) { * @since 5.9.0 * @since 6.1.0 Added 'base-layout-styles' support. * @since 6.6.0 Resolves relative paths in theme.json styles to theme absolute paths. + * @since 7.0.0 Deprecated 'base-layout-styles' type; classic themes now receive full styles + * with layout-specific alignment rules skipped via `base_layout_styles` option. * * @param array $types Optional. Types of styles to load. * See {@see 'WP_Theme_JSON::get_stylesheet'} for all valid types. - * If empty, it'll load the following: - * - for themes without theme.json: 'variables', 'presets', 'base-layout-styles'. - * - for themes with theme.json: 'variables', 'presets', 'styles'. + * If empty, will load: 'variables', 'presets', 'styles'. * @return string Stylesheet. */ function wp_get_global_stylesheet( $types = array() ) { @@ -180,15 +177,21 @@ function wp_get_global_stylesheet( $types = array() ) { } } - $tree = WP_Theme_JSON_Resolver::resolve_theme_file_uris( WP_Theme_JSON_Resolver::get_merged_data() ); - $supports_theme_json = wp_theme_has_theme_json(); + $tree = WP_Theme_JSON_Resolver::resolve_theme_file_uris( WP_Theme_JSON_Resolver::get_merged_data() ); - if ( empty( $types ) && ! $supports_theme_json ) { - $types = array( 'variables', 'presets', 'base-layout-styles' ); - } elseif ( empty( $types ) ) { + if ( empty( $types ) ) { $types = array( 'variables', 'styles', 'presets' ); } + /* + * Enable base layout styles only mode for classic themes without theme.json. + * This skips alignment styles that target .wp-site-blocks which is only used by block themes. + */ + $options = array(); + if ( ! wp_is_block_theme() && ! wp_theme_has_theme_json() ) { + $options['base_layout_styles'] = true; + } + /* * If variables are part of the stylesheet, then add them. * This is so themes without a theme.json still work as before 5.9: @@ -204,7 +207,7 @@ function wp_get_global_stylesheet( $types = array() ) { * @see wp_add_global_styles_for_blocks */ $origins = array( 'default', 'theme', 'custom' ); - $styles_variables = $tree->get_stylesheet( array( 'variables' ), $origins ); + $styles_variables = $tree->get_stylesheet( array( 'variables' ), $origins, $options ); $types = array_diff( $types, array( 'variables' ) ); } @@ -222,17 +225,8 @@ function wp_get_global_stylesheet( $types = array() ) { * (i.e. in the render cycle). Here, only the ones in use are rendered. * @see wp_add_global_styles_for_blocks */ - $origins = array( 'default', 'theme', 'custom' ); - /* - * If the theme doesn't have theme.json but supports both appearance tools and color palette, - * the 'theme' origin should be included so color palette presets are also output. - */ - if ( ! $supports_theme_json && ( current_theme_supports( 'appearance-tools' ) || current_theme_supports( 'border' ) ) && current_theme_supports( 'editor-color-palette' ) ) { - $origins = array( 'default', 'theme' ); - } elseif ( ! $supports_theme_json ) { - $origins = array( 'default' ); - } - $styles_rest = $tree->get_stylesheet( $types, $origins ); + $origins = array( 'default', 'theme', 'custom' ); + $styles_rest = $tree->get_stylesheet( $types, $origins, $options ); } $stylesheet = $styles_variables . $styles_rest; diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 2946f19656d4c..32e3a70be11b6 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -2514,38 +2514,36 @@ function wp_enqueue_global_styles() { $stylesheet = wp_get_global_stylesheet(); - if ( $is_block_theme ) { - /* - * Dequeue the Customizer's custom CSS - * and add it before the global styles custom CSS. - */ - remove_action( 'wp_head', 'wp_custom_css_cb', 101 ); + /* + * Dequeue the Customizer's custom CSS + * and add it before the global styles custom CSS. + */ + remove_action( 'wp_head', 'wp_custom_css_cb', 101 ); - /* - * Get the custom CSS from the Customizer and add it to the global stylesheet. - * Always do this in Customizer preview for the sake of live preview since it be empty. - */ - $custom_css = trim( wp_get_custom_css() ); - if ( $custom_css || is_customize_preview() ) { - if ( is_customize_preview() ) { - /* - * When in the Customizer preview, wrap the Custom CSS in milestone comments to allow customize-preview.js - * to locate the CSS to replace for live previewing. Make sure that the milestone comments are omitted from - * the stored Custom CSS if by chance someone tried to add them, which would be highly unlikely, but it - * would break live previewing. - */ - $before_milestone = '/*BEGIN_CUSTOMIZER_CUSTOM_CSS*/'; - $after_milestone = '/*END_CUSTOMIZER_CUSTOM_CSS*/'; - $custom_css = str_replace( array( $before_milestone, $after_milestone ), '', $custom_css ); - $custom_css = $before_milestone . "\n" . $custom_css . "\n" . $after_milestone; - } - $custom_css = "\n" . $custom_css; + /* + * Get the custom CSS from the Customizer and add it to the global stylesheet. + * Always do this in Customizer preview for the sake of live preview since it be empty. + */ + $custom_css = trim( wp_get_custom_css() ); + if ( $custom_css || is_customize_preview() ) { + if ( is_customize_preview() ) { + /* + * When in the Customizer preview, wrap the Custom CSS in milestone comments to allow customize-preview.js + * to locate the CSS to replace for live previewing. Make sure that the milestone comments are omitted from + * the stored Custom CSS if by chance someone tried to add them, which would be highly unlikely, but it + * would break live previewing. + */ + $before_milestone = '/*BEGIN_CUSTOMIZER_CUSTOM_CSS*/'; + $after_milestone = '/*END_CUSTOMIZER_CUSTOM_CSS*/'; + $custom_css = str_replace( array( $before_milestone, $after_milestone ), '', $custom_css ); + $custom_css = $before_milestone . "\n" . $custom_css . "\n" . $after_milestone; } - $stylesheet .= $custom_css; - - // Add the global styles custom CSS at the end. - $stylesheet .= wp_get_global_stylesheet( array( 'custom-css' ) ); + $custom_css = "\n" . $custom_css; } + $stylesheet .= $custom_css; + + // Add the global styles custom CSS at the end. + $stylesheet .= wp_get_global_stylesheet( array( 'custom-css' ) ); if ( empty( $stylesheet ) ) { return; diff --git a/tests/phpunit/tests/template.php b/tests/phpunit/tests/template.php index dacc69fba6330..6c6f0fcd33aba 100644 --- a/tests/phpunit/tests/template.php +++ b/tests/phpunit/tests/template.php @@ -1561,7 +1561,6 @@ static function () { 'global-styles-inline-css', 'normal-css', 'normal-inline-css', - 'wp-custom-css', ), 'BODY' => array( 'late-css', @@ -1623,7 +1622,6 @@ function (): void { 'global-styles-inline-css', 'normal-css', 'normal-inline-css', - 'wp-custom-css', ), 'BODY' => array( 'late-css', diff --git a/tests/phpunit/tests/theme/wpThemeJson.php b/tests/phpunit/tests/theme/wpThemeJson.php index 2bf0e7d84f266..45bc0681b0223 100644 --- a/tests/phpunit/tests/theme/wpThemeJson.php +++ b/tests/phpunit/tests/theme/wpThemeJson.php @@ -1216,14 +1216,20 @@ public function test_get_stylesheet_generates_base_fallback_gap_layout_styles() 'blockGap' => null, ), ), + 'styles' => array( + 'spacing' => array( + 'blockGap' => '1em', + ), + ), ), 'default' ); - $stylesheet = $theme_json->get_stylesheet( array( 'base-layout-styles' ) ); + // Set base_layout_styles to true to generate only base layout styles without alignment rules. + $stylesheet = $theme_json->get_stylesheet( array( 'styles' ), null, array( 'base_layout_styles' => true ) ); - // Note the `base-layout-styles` includes a fallback gap for the Columns block for backwards compatibility. + // Verify that layout styles are still generated, but without .wp-site-blocks alignment rules and flow/constrained base styles. $this->assertSame( - ':where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex > :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid > :is(*, div){margin: 0;}:where(.wp-block-columns.is-layout-flex){gap: 2em;}:where(.wp-block-columns.is-layout-grid){gap: 2em;}:where(.wp-block-post-template.is-layout-flex){gap: 1.25em;}:where(.wp-block-post-template.is-layout-grid){gap: 1.25em;}', + ':where(body) { margin: 0; }:where(.is-layout-flex){gap: 0.5em;}:where(.is-layout-grid){gap: 0.5em;}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex > :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid > :is(*, div){margin: 0;}', $stylesheet ); } @@ -1245,10 +1251,10 @@ public function test_get_stylesheet_skips_layout_styles() { ), 'default' ); - $stylesheet = $theme_json->get_stylesheet( array( 'base-layout-styles' ) ); + $stylesheet = $theme_json->get_stylesheet( array( 'styles' ), null ); remove_theme_support( 'disable-layout-styles' ); - // All Layout styles should be skipped. + // All Layout styles should be skipped when disable-layout-styles theme support is added. $this->assertSame( '', $stylesheet diff --git a/tests/phpunit/tests/theme/wpThemeJsonResolver.php b/tests/phpunit/tests/theme/wpThemeJsonResolver.php index 599224270649d..ce5609a396f18 100644 --- a/tests/phpunit/tests/theme/wpThemeJsonResolver.php +++ b/tests/phpunit/tests/theme/wpThemeJsonResolver.php @@ -734,22 +734,22 @@ public function test_get_user_data_from_wp_global_styles_does_not_use_uncached_q * @ticket 56945 * @covers WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles */ - public function test_get_user_data_from_wp_global_styles_does_not_run_for_theme_without_support() { - // The 'default' theme does not support theme.json. + public function test_get_user_data_from_wp_global_styles_runs_for_classic_themes() { + // The 'default' theme does not support theme.json (classic theme). switch_theme( 'default' ); wp_set_current_user( self::$administrator_id ); $theme = wp_get_theme(); - $start_queries = get_num_queries(); - - // When theme.json is not supported, the method should not run a query and always return an empty result. - $user_cpt = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme ); - $this->assertEmpty( $user_cpt, 'User CPT is expected to be empty.' ); - $this->assertSame( 0, get_num_queries() - $start_queries, 'Unexpected SQL query detected for theme without theme.json support.' ); - + // Classic themes should now be able to access user global styles data. + // When should_create_post is true, it should create a post. $user_cpt = WP_Theme_JSON_Resolver::get_user_data_from_wp_global_styles( $theme, true ); - $this->assertEmpty( $user_cpt, 'User CPT is expected to be empty.' ); - $this->assertSame( 0, get_num_queries() - $start_queries, 'Unexpected SQL query detected for theme without theme.json support.' ); + $this->assertIsArray( $user_cpt, 'User CPT should be an array for classic themes.' ); + $this->assertArrayHasKey( 'ID', $user_cpt, 'User CPT should have an ID for classic themes.' ); + + // Clean up the created post. + if ( isset( $user_cpt['ID'] ) ) { + wp_delete_post( $user_cpt['ID'], true ); + } } /**