* Class that encapsulates the processing of structures that adhere to the theme.json spec.
* This class is for internal core usage and is not supposed to be used by extenders (plugins and/or themes).
* This is a low-level API that may need to do breaking changes. Please,
* use get_global_settings, get_global_styles, and get_global_stylesheet instead.
#[AllowDynamicProperties]
* Container of data in theme.json format.
protected $theme_json = null;
* Holds block metadata extracted from block.json
* to be shared among all instances so we don't
* @since 6.1.0 Initialize as an empty array.
protected static $blocks_metadata = array();
* The CSS selector for the top-level preset settings.
const ROOT_CSS_PROPERTIES_SELECTOR = ':root';
* The CSS selector for the top-level styles.
const ROOT_BLOCK_SELECTOR = 'body';
* The sources of data this object can represent.
* @since 6.1.0 Added 'blocks'.
const VALID_ORIGINS = array(
* Presets are a set of values that serve
* to bootstrap some styles: colors, font sizes, etc.
* They are a unkeyed array of values such as:
* 'slug' => 'unique-name-within-the-set',
* 'name' => 'Name for the UI',
* This contains the necessary metadata to process them:
* - path => Where to find the preset within the settings section.
* - prevent_override => Disables override of default presets by theme presets.
* The relationship between whether to override the defaults
* and whether the defaults are enabled is inverse:
* - If defaults are enabled => theme presets should not be overridden
* - If defaults are disabled => theme presets should be overridden
* For example, a theme sets defaultPalette to false,
* making the default palette hidden from the user.
* In that case, we want all the theme presets to be present,
* so they should override the defaults by setting this false.
* - use_default_names => whether to use the default names
* - value_key => the key that represents the value
* - value_func => optionally, instead of value_key, a function to generate
* the value that takes a preset as an argument
* (either value_key or value_func should be present)
* - css_vars => template string to use in generating the CSS Custom Property.
* Example output: "--wp--preset--duotone--blue: <value>" will generate as many CSS Custom Properties as presets defined
* substituting the $slug for the slug's value for each preset value.
* - classes => array containing a structure with the classes to
* generate for the presets, where for each array item
* the key is the class name and the value the property name.
* The "$slug" substring will be replaced by the slug of each preset.
* '.has-$slug-color' => 'color',
* '.has-$slug-background-color' => 'background-color',
* '.has-$slug-border-color' => 'border-color',
* - properties => array of CSS properties to be used by kses to
* validate the content of each preset
* by means of the remove_insecure_properties method.
* @since 5.9.0 Added the `color.duotone` and `typography.fontFamilies` presets,
* `use_default_names` preset key, and simplified the metadata structure.
* @since 6.0.0 Replaced `override` with `prevent_override` and updated the
* `prevent_override` value for `color.duotone` to use `color.defaultDuotone`.
* @since 6.2.0 Added 'shadow' presets.
* @since 6.3.0 Replaced value_func for duotone with `null`. Custom properties are handled by class-wp-duotone.php.
* @since 6.6.0 Added the `dimensions.aspectRatios` and `dimensions.defaultAspectRatios` presets.
* Updated the 'prevent_override' value for font size presets to use 'typography.defaultFontSizes'
* and spacing size presets to use `spacing.defaultSpacingSizes`.
const PRESETS_METADATA = array(
'path' => array( 'dimensions', 'aspectRatios' ),
'prevent_override' => array( 'dimensions', 'defaultAspectRatios' ),
'use_default_names' => false,
'css_vars' => '--wp--preset--aspect-ratio--$slug',
'properties' => array( 'aspect-ratio' ),
'path' => array( 'color', 'palette' ),
'prevent_override' => array( 'color', 'defaultPalette' ),
'use_default_names' => false,
'css_vars' => '--wp--preset--color--$slug',
'.has-$slug-color' => 'color',
'.has-$slug-background-color' => 'background-color',
'.has-$slug-border-color' => 'border-color',
'properties' => array( 'color', 'background-color', 'border-color' ),
'path' => array( 'color', 'gradients' ),
'prevent_override' => array( 'color', 'defaultGradients' ),
'use_default_names' => false,
'value_key' => 'gradient',
'css_vars' => '--wp--preset--gradient--$slug',
'classes' => array( '.has-$slug-gradient-background' => 'background' ),
'properties' => array( 'background' ),
'path' => array( 'color', 'duotone' ),
'prevent_override' => array( 'color', 'defaultDuotone' ),
'use_default_names' => false,
'value_func' => null, // CSS Custom Properties for duotone are handled by block supports in class-wp-duotone.php.
'properties' => array( 'filter' ),
'path' => array( 'typography', 'fontSizes' ),
'prevent_override' => array( 'typography', 'defaultFontSizes' ),
'use_default_names' => true,
'value_func' => 'wp_get_typography_font_size_value',
'css_vars' => '--wp--preset--font-size--$slug',
'classes' => array( '.has-$slug-font-size' => 'font-size' ),
'properties' => array( 'font-size' ),
'path' => array( 'typography', 'fontFamilies' ),
'prevent_override' => false,
'use_default_names' => false,
'value_key' => 'fontFamily',
'css_vars' => '--wp--preset--font-family--$slug',
'classes' => array( '.has-$slug-font-family' => 'font-family' ),
'properties' => array( 'font-family' ),
'path' => array( 'spacing', 'spacingSizes' ),
'prevent_override' => array( 'spacing', 'defaultSpacingSizes' ),
'use_default_names' => true,
'css_vars' => '--wp--preset--spacing--$slug',
'properties' => array( 'padding', 'margin' ),
'path' => array( 'shadow', 'presets' ),
'prevent_override' => array( 'shadow', 'defaultPresets' ),
'use_default_names' => false,
'css_vars' => '--wp--preset--shadow--$slug',
'properties' => array( 'box-shadow' ),
* Metadata for style properties.
* Each element is a direct mapping from the CSS property name to the
* path to the value in theme.json & block attributes.
* @since 5.9.0 Added the `border-*`, `font-family`, `font-style`, `font-weight`,
* `letter-spacing`, `margin-*`, `padding-*`, `--wp--style--block-gap`,
* `text-decoration`, `text-transform`, and `filter` properties,
* simplified the metadata structure.
* @since 6.1.0 Added the `border-*-color`, `border-*-width`, `border-*-style`,
* `--wp--style--root--padding-*`, and `box-shadow` properties,
* removed the `--wp--style--block-gap` property.
* @since 6.2.0 Added `outline-*`, and `min-height` properties.
* @since 6.3.0 Added `column-count` property.
* @since 6.4.0 Added `writing-mode` property.
* @since 6.5.0 Added `aspect-ratio` property.
* @since 6.6.0 Added `background-[image|position|repeat|size]` properties.
const PROPERTIES_METADATA = array(
'aspect-ratio' => array( 'dimensions', 'aspectRatio' ),
'background' => array( 'color', 'gradient' ),
'background-color' => array( 'color', 'background' ),
'background-image' => array( 'background', 'backgroundImage' ),
'background-position' => array( 'background', 'backgroundPosition' ),
'background-repeat' => array( 'background', 'backgroundRepeat' ),
'background-size' => array( 'background', 'backgroundSize' ),
'border-radius' => array( 'border', 'radius' ),
'border-top-left-radius' => array( 'border', 'radius', 'topLeft' ),
'border-top-right-radius' => array( 'border', 'radius', 'topRight' ),
'border-bottom-left-radius' => array( 'border', 'radius', 'bottomLeft' ),
'border-bottom-right-radius' => array( 'border', 'radius', 'bottomRight' ),
'border-color' => array( 'border', 'color' ),
'border-width' => array( 'border', 'width' ),
'border-style' => array( 'border', 'style' ),
'border-top-color' => array( 'border', 'top', 'color' ),
'border-top-width' => array( 'border', 'top', 'width' ),
'border-top-style' => array( 'border', 'top', 'style' ),
'border-right-color' => array( 'border', 'right', 'color' ),
'border-right-width' => array( 'border', 'right', 'width' ),
'border-right-style' => array( 'border', 'right', 'style' ),
'border-bottom-color' => array( 'border', 'bottom', 'color' ),
'border-bottom-width' => array( 'border', 'bottom', 'width' ),
'border-bottom-style' => array( 'border', 'bottom', 'style' ),
'border-left-color' => array( 'border', 'left', 'color' ),
'border-left-width' => array( 'border', 'left', 'width' ),
'border-left-style' => array( 'border', 'left', 'style' ),
'color' => array( 'color', 'text' ),
'text-align' => array( 'typography', 'textAlign' ),
'column-count' => array( 'typography', 'textColumns' ),
'font-family' => array( 'typography', 'fontFamily' ),
'font-size' => array( 'typography', 'fontSize' ),
'font-style' => array( 'typography', 'fontStyle' ),
'font-weight' => array( 'typography', 'fontWeight' ),
'letter-spacing' => array( 'typography', 'letterSpacing' ),
'line-height' => array( 'typography', 'lineHeight' ),
'margin' => array( 'spacing', 'margin' ),
'margin-top' => array( 'spacing', 'margin', 'top' ),
'margin-right' => array( 'spacing', 'margin', 'right' ),
'margin-bottom' => array( 'spacing', 'margin', 'bottom' ),
'margin-left' => array( 'spacing', 'margin', 'left' ),
'min-height' => array( 'dimensions', 'minHeight' ),
'outline-color' => array( 'outline', 'color' ),
'outline-offset' => array( 'outline', 'offset' ),
'outline-style' => array( 'outline', 'style' ),
'outline-width' => array( 'outline', 'width' ),
'padding' => array( 'spacing', 'padding' ),
'padding-top' => array( 'spacing', 'padding', 'top' ),
'padding-right' => array( 'spacing', 'padding', 'right' ),
'padding-bottom' => array( 'spacing', 'padding', 'bottom' ),
'padding-left' => array( 'spacing', 'padding', 'left' ),
'--wp--style--root--padding' => array( 'spacing', 'padding' ),
'--wp--style--root--padding-top' => array( 'spacing', 'padding', 'top' ),
'--wp--style--root--padding-right' => array( 'spacing', 'padding', 'right' ),
'--wp--style--root--padding-bottom' => array( 'spacing', 'padding', 'bottom' ),
'--wp--style--root--padding-left' => array( 'spacing', 'padding', 'left' ),
'text-decoration' => array( 'typography', 'textDecoration' ),
'text-transform' => array( 'typography', 'textTransform' ),
'filter' => array( 'filter', 'duotone' ),
'box-shadow' => array( 'shadow' ),
'writing-mode' => array( 'typography', 'writingMode' ),
* Indirect metadata for style properties that are not directly output.
* Each element maps from a CSS property name to an array of
* paths to the value in theme.json & block attributes.
* Indirect properties are not output directly by `compute_style_properties`,
* but are used elsewhere in the processing of global styles. The indirect
* property is used to validate whether a style value is allowed.
* @since 6.6.0 Added background-image properties.
const INDIRECT_PROPERTIES_METADATA = array(
array( 'spacing', 'blockGap' ),
array( 'spacing', 'blockGap', 'left' ),
array( 'spacing', 'blockGap', 'top' ),
array( 'layout', 'contentSize' ),
array( 'layout', 'wideSize' ),
'background-image' => array(
array( 'background', 'backgroundImage', 'url' ),
* Protected style properties.
* These style properties are only rendered if a setting enables it
* via a value other than `null`.
* Each element maps the style property to the corresponding theme.json
const PROTECTED_PROPERTIES = array(
'spacing.blockGap' => array( 'spacing', 'blockGap' ),
* The top-level keys a theme.json can have.
* @since 5.8.0 As `ALLOWED_TOP_LEVEL_KEYS`.
* @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.
const VALID_TOP_LEVEL_KEYS = array(
* The valid properties under the settings key.
* @since 5.8.0 As `ALLOWED_SETTINGS`.
* @since 5.9.0 Renamed from `ALLOWED_SETTINGS` to `VALID_SETTINGS`,
* added new properties for `border`, `color`, `spacing`,
* and `typography`, and renamed others according to the new schema.
* @since 6.0.0 Added `color.defaultDuotone`.
* @since 6.1.0 Added `layout.definitions` and `useRootPaddingAwareAlignments`.
* @since 6.2.0 Added `dimensions.minHeight`, 'shadow.presets', 'shadow.defaultPresets',
* `position.fixed` and `position.sticky`.
* @since 6.3.0 Added support for `typography.textColumns`, removed `layout.definitions`.
* @since 6.4.0 Added support for `layout.allowEditing`, `background.backgroundImage`,
* `typography.writingMode`, `lightbox.enabled` and `lightbox.allowEditing`.
* @since 6.5.0 Added support for `layout.allowCustomContentAndWideSize`,
* `background.backgroundSize` and `dimensions.aspectRatio`.
* @since 6.6.0 Added support for 'dimensions.aspectRatios', 'dimensions.defaultAspectRatios',
* 'typography.defaultFontSizes', and 'spacing.defaultSpacingSizes'.
const VALID_SETTINGS = array(
'appearanceTools' => null,
'useRootPaddingAwareAlignments' => null,
'backgroundImage' => null,
'backgroundSize' => null,
'customGradient' => null,
'defaultDuotone' => null,
'defaultGradients' => null,
'defaultPalette' => null,
'defaultAspectRatios' => null,
'allowCustomContentAndWideSize' => null,
'customSpacingSize' => null,
'defaultSpacingSizes' => null,
'defaultPresets' => null,
'customFontSize' => null,
'defaultFontSizes' => null,
'textDecoration' => null,
* The valid properties for fontFamilies under settings key.
const FONT_FAMILY_SCHEMA = array(
'ascentOverride' => null,
'descentOverride' => null,
'fontFeatureSettings' => null,
'fontVariationSettings' => null,
'lineGapOverride' => null,