Title: WP_Theme_JSON::split_selector_list
Published: August 20, 2026

---

# WP_Theme_JSON::split_selector_list( string $selector ): string[]

## In this article

 * [Description](https://developer.wordpress.org/reference/classes/wp_theme_json/split_selector_list/?output_format=md#description)
    - [See also](https://developer.wordpress.org/reference/classes/wp_theme_json/split_selector_list/?output_format=md#see-also)
 * [Parameters](https://developer.wordpress.org/reference/classes/wp_theme_json/split_selector_list/?output_format=md#parameters)
 * [Return](https://developer.wordpress.org/reference/classes/wp_theme_json/split_selector_list/?output_format=md#return)
 * [Source](https://developer.wordpress.org/reference/classes/wp_theme_json/split_selector_list/?output_format=md#source)
 * [Changelog](https://developer.wordpress.org/reference/classes/wp_theme_json/split_selector_list/?output_format=md#changelog)

[ Back to top](https://developer.wordpress.org/reference/classes/wp_theme_json/split_selector_list/?output_format=md#wp--skip-link--target)

Splits a selector list into separate selectors.

## 󠀁[Description](https://developer.wordpress.org/reference/classes/wp_theme_json/split_selector_list/?output_format=md#description)󠁿

While selectors are joined by commas, not all commas separate top-level selectors.

This method only separates top-level selectors, so some commas may appear inside
strings, nested selectors, and comments. Leading and trailing CSS whitespace is 
trimmed from the returned list items.

Non-selector content, such as comments, are retained in the list in the same item
as the selector content they follow.

Example:

    ```php
    array( '.wp-block' )    === self::split_selector_list( '.wp-block' );
    array( '.one', '.two' ) === self::split_selector_list( '.one, .two' );

    // Nested selector lists are retained within their containing selector.
    array( ':is(.a, .b)', 'c' ) === self::split_selector_list( ':is(.a, .b), .c' );

    // Commas within strings do not separate selectors.
    $selectors   = self::split_selector_list( '[data-label="Save, continue"],.fallback' );
    $selectors === array( '[data-label="Save, continue"]', '.fallback' )

    array( 'lang(zh, "*-hant")', '.foo' ) === self::split_selector_list( 'lang(zh, "*-hant"), .foo' );

    // Identifiers may contain escaped commas.
    array( '.foo\,bar', '.baz' ) === self::split_selector_list( '.foo\,bar,.baz' );

    // Comments stay with the selector they follow.
    array( '.a /* a, the first *\/', '.b' ) === self::split_selector_list( '.a /* a, the first *\/,.b' );
    ```

### 󠀁[See also](https://developer.wordpress.org/reference/classes/wp_theme_json/split_selector_list/?output_format=md#see-also)󠁿

 * [https://www.w3.org/TR/selectors/#parse-selector](https://www.w3.org/TR/selectors/#parse-selector/)
 * [https://www.w3.org/TR/css-syntax-3/](https://www.w3.org/TR/css-syntax-3/)

## 󠀁[Parameters](https://developer.wordpress.org/reference/classes/wp_theme_json/split_selector_list/?output_format=md#parameters)󠁿

 `$selector`stringrequired

CSS selector list as a string, e.g. ‘.wp-block .wp-block-paragraph’.

## 󠀁[Return](https://developer.wordpress.org/reference/classes/wp_theme_json/split_selector_list/?output_format=md#return)󠁿

 string[] List of trimmed selectors parsed from input list.

## 󠀁[Source](https://developer.wordpress.org/reference/classes/wp_theme_json/split_selector_list/?output_format=md#source)󠁿

    ```php
    protected static function split_selector_list( $selector ): array {
    	if ( ! str_contains( $selector, ',' ) ) {
    		// See note on trimming CSS whitespace in main loop.
    		return array( trim( $selector, " \t\n" ) );
    	}

    	$selectors         = array();
    	$selector_length   = strlen( $selector );
    	$parentheses_depth = 0;
    	$at                = 0;
    	$was_at            = 0;

    	while ( $at < $selector_length ) {
    		$next_at = $at + strcspn( $selector, '/,\'"()<-\\', $at );
    		if ( $next_at >= $selector_length ) {
    			break;
    		}

    		$next_cp = $selector[ $next_at ];

    		// Escaped syntax characters do not act as delimiters.
    		if ( '\\' === $next_cp ) {
    			$at = min( $next_at + 2, $selector_length );
    			continue;
    		}

    		/*
    		 * Start of a parenthesized expression, which maintains a stack of parentheses.
    		 * For the sake of this function, no selector list will be split inside parentheses.
    		 * Therefore it’s possible to jump ahead until this list completes.
    		 */
    		if ( '(' === $next_cp || ')' === $next_cp ) {
    			$parentheses_depth += '(' === $next_cp ? 1 : -1;
    			$at                 = $next_at + 1;
    			continue;
    		}

    		// Start of a string, which will be incorporated into the selector in which it’s found.
    		if ( "'" === $next_cp || '"' === $next_cp ) {
    			$end_of_string = $next_at + 1;
    			while ( $end_of_string < $selector_length ) {
    				$end_of_string += strcspn( $selector, "{$next_cp}\\", $end_of_string );
    				if ( $end_of_string >= $selector_length ) {
    					break;
    				}

    				$end_cp = $selector[ $end_of_string ];

    				// Skip escaped characters.
    				if ( '\\' === $end_cp ) {
    					$end_of_string = $end_of_string + 2;
    					continue;
    				}

    				if ( $next_cp === $end_cp ) {
    					++$end_of_string;
    					break;
    				}

    				++$end_of_string;
    			}

    			$at = $end_of_string;
    			continue;
    		}

    		// Start of a comment, which will be incorporated into the selector in which it’s found.
    		if ( '/' === $next_cp && ( $next_at + 1 ) < $selector_length && '*' === $selector[ $next_at + 1 ] ) {
    			$comment_end_at = strpos( $selector, '*/', $next_at + 1 );
    			$is_terminated  = false !== $comment_end_at;
    			$after_comment  = $is_terminated ? $comment_end_at + 2 : strlen( $selector );
    			$at             = $after_comment;
    			continue;
    		}

    		// Start of a CDO or CDC, which will be incorporated into the selector in which it’s found.
    		if (
    			( '<' === $next_cp && 0 === substr_compare( $selector, '<!--', $next_at, 4 ) ) ||
    			( '-' === $next_cp && 0 === substr_compare( $selector, '-->', $next_at, 3 ) )
    		) {
    			$at = $next_at + ( '<' === $next_cp ? 4 : 3 );
    			continue;
    		}

    		// Everything else is either a comma token or part of a selector.
    		if ( ',' === $next_cp && 0 === $parentheses_depth ) {
    			/**
    			 * Trim each selector so that downstream code doesn’t see whitespace
    			 * as the first character in a selector and get confused.
    			 *
    			 * There is inconsistency in this because comments and other syntax
    			 * are included which are also not part of the selector itself, but
    			 * a tradeoff is made between removing common syntax which carries
    			 * no meaning and rarer syntax which leaves auxiliary information.
    			 *
    			 * > A newline, U+0009 CHARACTER TABULATION, or U+0020 SPACE.
    			 * > Note that U+000D CARRIAGE RETURN and U+000C FORM FEED are
    			 * > not included in this definition, as they are converted
    			 * > to U+000A LINE FEED during preprocessing.
    			 *
    			 * @see https://www.w3.org/TR/css-syntax/#whitespace
    			 * @see https://www.w3.org/TR/css-syntax/#newline
    			 */
    			$selectors[] = trim( substr( $selector, $was_at, $next_at - $was_at ), " \t\n" );
    			$at          = $next_at + 1;
    			$was_at      = $at;
    			continue;
    		}

    		$at = $next_at + 1;
    	}

    	if ( $was_at < $selector_length ) {
    		// See note on trimming CSS whitespace in main loop.
    		$selectors[] = trim( substr( $selector, $was_at ), " \t\n" );
    	}

    	return $selectors;
    }
    ```

[View all references](https://developer.wordpress.org/reference/files/wp-includes/class-wp-theme-json.php/)
[View on Trac](https://core.trac.wordpress.org/browser/tags/7.1/src/wp-includes/class-wp-theme-json.php#L1612)
[View on GitHub](https://github.com/WordPress/wordpress-develop/blob/7.1/src/wp-includes/class-wp-theme-json.php#L1612-L1730)

## 󠀁[Changelog](https://developer.wordpress.org/reference/classes/wp_theme_json/split_selector_list/?output_format=md#changelog)󠁿

| Version | Description | 
| [7.1.0](https://developer.wordpress.org/reference/since/7.1.0/) | Introduced. |

## User Contributed Notes

You must [log in](https://login.wordpress.org/?redirect_to=https%3A%2F%2Fdeveloper.wordpress.org%2Freference%2Fclasses%2Fwp_theme_json%2Fsplit_selector_list%2F)
before being able to contribute a note or feedback.