An internal method to get the block nodes from a theme.json file.
Parameters
$theme_jsonarrayrequired- The theme.json converted to an array.
$selectorsarrayoptional- Optional list of selectors per block.
Default:
array() $optionsarrayoptional- An array of options for now used for internal purposes only (may change without notice).
include_block_style_variationsboolInclude nodes for block style variations. Default false.include_node_paths_onlyboolReturn only block nodes node paths. Default false.
Default:
array()
Source
private static function get_block_nodes( $theme_json, $selectors = array(), $options = array() ) {
$nodes = array();
if ( ! isset( $theme_json['styles']['blocks'] ) ) {
return $nodes;
}
$include_variations = $options['include_block_style_variations'] ?? false;
$include_node_paths_only = $options['include_node_paths_only'] ?? false;
$responsive_media_queries = static::get_viewport_media_queries( $theme_json['settings']['viewport'] ?? null );
// If only node paths are to be returned, skip selector assignment.
if ( ! $include_node_paths_only ) {
$selectors = empty( $selectors ) ? static::get_blocks_metadata() : $selectors;
}
foreach ( $theme_json['styles']['blocks'] as $name => $node ) {
$node_path = array( 'styles', 'blocks', $name );
if ( $include_node_paths_only ) {
$variation_paths = array();
if ( $include_variations && isset( $node['variations'] ) ) {
foreach ( $node['variations'] as $variation => $variation_node ) {
$variation_paths[] = array(
'path' => array( 'styles', 'blocks', $name, 'variations', $variation ),
);
}
}
$node = array(
'path' => $node_path,
);
if ( ! empty( $variation_paths ) ) {
$node['variations'] = $variation_paths;
}
$nodes[] = $node;
} else {
$selector = null;
if ( isset( $selectors[ $name ]['selector'] ) ) {
$selector = $selectors[ $name ]['selector'];
}
$duotone_selector = null;
if ( isset( $selectors[ $name ]['duotone'] ) ) {
$duotone_selector = $selectors[ $name ]['duotone'];
}
$feature_selectors = null;
if ( isset( $selectors[ $name ]['selectors'] ) ) {
$feature_selectors = $selectors[ $name ]['selectors'];
}
$variation_selectors = array();
if ( $include_variations && isset( $node['variations'] ) ) {
foreach ( $node['variations'] as $variation => $node ) {
$variation_selectors[] = array(
'name' => $variation,
'path' => array( 'styles', 'blocks', $name, 'variations', $variation ),
'selector' => $selectors[ $name ]['styleVariations'][ $variation ],
);
}
}
$nodes[] = array(
'name' => $name,
'path' => $node_path,
'selector' => $selector,
'selectors' => $feature_selectors,
'elements' => $selectors[ $name ]['elements'] ?? array(),
'duotone' => $duotone_selector,
'variations' => $variation_selectors,
'css' => $selector,
);
// Responsive block nodes: emit one node per breakpoint that has styles.
// These are rendered immediately after the base block node so that
// the cascade order is: .block{} → @media{.block{}}
foreach ( array_keys( $responsive_media_queries ) as $breakpoint ) {
if ( isset( $theme_json['styles']['blocks'][ $name ][ $breakpoint ] ) ) {
$nodes[] = array(
'name' => $name,
'path' => array( 'styles', 'blocks', $name, $breakpoint ),
'media_query' => $responsive_media_queries[ $breakpoint ],
'selector' => $selector,
'selectors' => $feature_selectors,
'elements' => $selectors[ $name ]['elements'] ?? array(),
'variations' => $variation_selectors,
'css' => $selector,
);
}
}
// Handle any pseudo selectors for the block.
if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $name ] ) ) {
foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $name ] as $pseudo_selector ) {
$has_pseudo = isset( $theme_json['styles']['blocks'][ $name ][ $pseudo_selector ] );
$has_responsive_pseudo = false;
foreach ( array_keys( $responsive_media_queries ) as $breakpoint ) {
if ( isset( $theme_json['styles']['blocks'][ $name ][ $breakpoint ][ $pseudo_selector ] ) ) {
$has_responsive_pseudo = true;
break;
}
}
if ( ! $has_pseudo && ! $has_responsive_pseudo ) {
continue;
}
/*
* Append the pseudo-selector to each feature selector so that
* get_feature_declarations_for_node generates CSS scoped to the
* pseudo-state (e.g. '.wp-block-button:hover') rather than the
* default state (e.g. '.wp-block-button').
*/
$pseudo_feature_selectors = array();
foreach ( $feature_selectors ?? array() as $feature => $feature_selector ) {
if ( is_array( $feature_selector ) ) {
$pseudo_feature_selectors[ $feature ] = array();
foreach ( $feature_selector as $subfeature => $subfeature_selector ) {
$pseudo_feature_selectors[ $feature ][ $subfeature ] = static::append_to_selector( $subfeature_selector, $pseudo_selector );
}
} else {
$pseudo_feature_selectors[ $feature ] = static::append_to_selector( $feature_selector, $pseudo_selector );
}
}
if ( $has_pseudo ) {
$nodes[] = array(
'name' => $name,
'path' => array( 'styles', 'blocks', $name, $pseudo_selector ),
'selector' => static::append_to_selector( $selector, $pseudo_selector ),
'selectors' => $pseudo_feature_selectors,
'elements' => $selectors[ $name ]['elements'] ?? array(),
'duotone' => $duotone_selector,
'variations' => $variation_selectors,
'css' => static::append_to_selector( $selector, $pseudo_selector ),
);
}
// Responsive pseudo nodes: emit one node per breakpoint that has
// this pseudo state, immediately after the default pseudo node.
// Cascade order: .block:hover{} → @media{.block:hover{}}
foreach ( array_keys( $responsive_media_queries ) as $breakpoint ) {
if ( isset( $theme_json['styles']['blocks'][ $name ][ $breakpoint ][ $pseudo_selector ] ) ) {
$nodes[] = array(
'name' => $name,
'path' => array( 'styles', 'blocks', $name, $breakpoint, $pseudo_selector ),
'media_query' => $responsive_media_queries[ $breakpoint ],
'selector' => static::append_to_selector( $selector, $pseudo_selector ),
'selectors' => $pseudo_feature_selectors,
'elements' => $selectors[ $name ]['elements'] ?? array(),
'variations' => $variation_selectors,
'css' => static::append_to_selector( $selector, $pseudo_selector ),
);
}
}
}
}
// Handle custom states (e.g. '-current' for navigation).
if ( isset( static::VALID_BLOCK_CUSTOM_STATES[ $name ] ) ) {
foreach ( static::VALID_BLOCK_CUSTOM_STATES[ $name ] as $custom_state ) {
if (
isset( $theme_json['styles']['blocks'][ $name ][ $custom_state ] ) &&
isset( $selectors[ $name ]['states'][ $custom_state ] )
) {
$custom_css_selector = $selectors[ $name ]['states'][ $custom_state ];
$nodes[] = array(
'name' => $name,
'path' => array( 'styles', 'blocks', $name, $custom_state ),
'selector' => $custom_css_selector,
'selectors' => $feature_selectors,
'elements' => $selectors[ $name ]['elements'] ?? array(),
'duotone' => $duotone_selector,
'variations' => $variation_selectors,
'css' => $custom_css_selector,
);
// Sub-pseudo-selectors within the custom state.
if ( isset( static::VALID_BLOCK_PSEUDO_SELECTORS[ $name ] ) ) {
foreach ( static::VALID_BLOCK_PSEUDO_SELECTORS[ $name ] as $pseudo ) {
if ( isset( $theme_json['styles']['blocks'][ $name ][ $custom_state ][ $pseudo ] ) ) {
$compound_css_selector = static::append_to_selector( $custom_css_selector, $pseudo );
$nodes[] = array(
'name' => $name,
'path' => array( 'styles', 'blocks', $name, $custom_state, $pseudo ),
'selector' => $compound_css_selector,
'selectors' => $feature_selectors,
'elements' => $selectors[ $name ]['elements'] ?? array(),
'duotone' => $duotone_selector,
'variations' => $variation_selectors,
'css' => $compound_css_selector,
);
}
}
}
}
}
}
}
/*
* Elements can be styled outside any breakpoint, inside one, or both,
* so collect the names from all of those places before looping. An
* element styled only inside a breakpoint still needs a node.
*/
$block_node = $theme_json['styles']['blocks'][ $name ] ?? array();
$element_names = array_keys( $block_node['elements'] ?? array() );
foreach ( array_keys( $responsive_media_queries ) as $breakpoint ) {
$element_names = array_merge(
$element_names,
array_keys( $block_node[ $breakpoint ]['elements'] ?? array() )
);
}
$element_names = array_unique( $element_names );
if ( ! empty( $element_names ) ) {
foreach ( $element_names as $element ) {
$element_path = array( 'styles', 'blocks', $name, 'elements', $element );
if ( $include_node_paths_only ) {
if ( isset( $block_node['elements'][ $element ] ) ) {
$nodes[] = array(
'path' => $element_path,
);
}
continue;
}
if ( ! isset( $selectors[ $name ]['elements'][ $element ] ) ) {
continue;
}
$element_selector = $selectors[ $name ]['elements'][ $element ];
if ( isset( $block_node['elements'][ $element ] ) ) {
$nodes[] = array(
'path' => $element_path,
'selector' => $element_selector,
);
}
// Responsive element nodes: one node per breakpoint that has
// styles for this element. Cascade: a{} → @media{a{}}
foreach ( array_keys( $responsive_media_queries ) as $breakpoint ) {
if ( isset( $theme_json['styles']['blocks'][ $name ][ $breakpoint ]['elements'][ $element ] ) ) {
$nodes[] = array(
'path' => array( 'styles', 'blocks', $name, $breakpoint, 'elements', $element ),
'selector' => $element_selector,
'media_query' => $responsive_media_queries[ $breakpoint ],
);
}
}
// Handle any pseudo selectors for the element.
if ( isset( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] ) ) {
foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element ] as $pseudo_selector ) {
// Emit the default pseudo node only when the default state styles
// the pseudo. Otherwise get_styles_for_block() falls back to the
// element's base styles, outputting a rule the theme never defined.
if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'][ $element ][ $pseudo_selector ] ) ) {
$nodes[] = array(
'path' => array( 'styles', 'blocks', $name, 'elements', $element ),
'selector' => static::append_to_selector( $element_selector, $pseudo_selector ),
);
}
// Responsive element pseudo nodes: one node per breakpoint
// that has this pseudo state for this element.
// Cascade: a:hover{} → @media{a:hover{}}
foreach ( array_keys( $responsive_media_queries ) as $breakpoint ) {
if ( isset( $theme_json['styles']['blocks'][ $name ][ $breakpoint ]['elements'][ $element ][ $pseudo_selector ] ) ) {
$nodes[] = array(
'path' => array( 'styles', 'blocks', $name, $breakpoint, 'elements', $element ),
'selector' => static::append_to_selector( $element_selector, $pseudo_selector ),
'media_query' => $responsive_media_queries[ $breakpoint ],
);
}
}
}
}
}
}
}
return $nodes;
}
User Contributed Notes
You must log in before being able to contribute a note or feedback.