or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-css-what

A fast and lightweight CSS selector parser that transforms CSS selector strings into structured AST objects

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/css-what@7.0.x

To install, run

npx @tessl/cli install tessl/npm-css-what@7.0.0

index.mddocs/

CSS What

CSS What is a fast and lightweight CSS selector parser that transforms CSS selector strings into structured AST (Abstract Syntax Tree) objects. It supports parsing all standard CSS selector types including tags, classes, IDs, attributes, pseudo-classes, pseudo-elements, and combinators, with comprehensive support for CSS3 selectors and modern selector features.

Package Information

  • Package Name: css-what
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install css-what

Core Imports

import { parse, isTraversal, stringify, type Selector, SelectorType, AttributeAction } from "css-what";

For CommonJS:

const { parse, isTraversal, stringify, SelectorType } = require("css-what");

Basic Usage

import { parse, stringify, isTraversal } from "css-what";

// Parse a CSS selector into an AST
const ast = parse("div.container > p:nth-child(2n+1)[data-id]");

// Convert AST back to string
const selectorString = stringify(ast);

// Check if a selector token is a traversal (combinator)
ast[0].forEach(selector => {
  if (isTraversal(selector)) {
    console.log(`Found combinator: ${selector.type}`);
  }
});

Capabilities

CSS Selector Parsing

Parses a CSS selector string into a two-dimensional array of selector tokens.

/**
 * Parses a CSS selector string into a structured AST
 * @param selector - CSS selector string to parse
 * @returns Two-dimensional array where first dimension represents comma-separated selectors, second contains tokens for each selector
 * @throws Error for invalid selectors
 */
function parse(selector: string): Selector[][];

The returned structure represents comma-separated selectors as the first dimension, with each selector broken down into individual tokens (tags, classes, IDs, attributes, pseudo-classes, combinators, etc.).

Important: Class selectors (.class) are represented as attribute selectors with name: "class" and action: "element". ID selectors (#id) are represented as attribute selectors with name: "id" and action: "equals".

Usage Examples:

// Simple selector
const simple = parse("div");
// Result: [[{ type: "tag", name: "div", namespace: null }]]

// Complex selector with multiple parts
const complex = parse("div.container#main[data-role='content']:hover");
// Result: [[
//   { type: "tag", name: "div", namespace: null },
//   { type: "attribute", name: "class", action: "element", value: "container", ignoreCase: "quirks", namespace: null },
//   { type: "attribute", name: "id", action: "equals", value: "main", ignoreCase: "quirks", namespace: null },
//   { type: "attribute", name: "data-role", action: "equals", value: "content", ignoreCase: null, namespace: null },
//   { type: "pseudo", name: "hover", data: null }
// ]]

// Multiple selectors (comma-separated)
const multiple = parse("div, span.highlight");
// Result: [
//   [{ type: "tag", name: "div", namespace: null }],
//   [{ type: "tag", name: "span", namespace: null }, { type: "attribute", name: "class", action: "element", value: "highlight", ignoreCase: "quirks", namespace: null }]
// ]

// Selector with combinators
const withCombinators = parse("div > p + span");
// Result: [[
//   { type: "tag", name: "div", namespace: null },
//   { type: "child" },
//   { type: "tag", name: "p", namespace: null },
//   { type: "adjacent" },
//   { type: "tag", name: "span", namespace: null }
// ]]

AST to String Conversion

Converts a parsed selector array back into a CSS selector string.

/**
 * Converts a parsed selector array back into a CSS selector string
 * @param selector - Two-dimensional array of selector tokens from parse()
 * @returns CSS selector string
 */
function stringify(selector: Selector[][]): string;

Usage Examples:

// Round-trip parsing and stringification
const original = "div.container > p:nth-child(2)";
const ast = parse(original);
const reconstructed = stringify(ast);
// reconstructed === original

// Modify AST and stringify
const ast = parse("div");
ast[0].push({ type: SelectorType.Attribute, name: "class", action: AttributeAction.Element, value: "highlight", ignoreCase: "quirks", namespace: null });
const result = stringify(ast);
// result === "div.highlight"

Traversal Type Detection

Type guard function that checks whether a selector token is a traversal (combinator).

/**
 * Type guard that checks whether a selector is a traversal (combinator)
 * @param selector - Selector object to check
 * @returns Boolean indicating if selector is a traversal type
 */
function isTraversal(selector: Selector): selector is Traversal;

Usage Examples:

const ast = parse("div > p");
ast[0].forEach(selector => {
  if (isTraversal(selector)) {
    console.log(`Found combinator: ${selector.type}`); // "child"
  } else {
    console.log(`Found selector: ${selector.type}, name: ${selector.name}`);
  }
});

Types

Core Selector Types

/**
 * Union type representing any CSS selector token
 */
type Selector = PseudoSelector | PseudoElement | AttributeSelector | TagSelector | UniversalSelector | Traversal;

/**
 * Data type for pseudo-selector content
 */
type DataType = Selector[][] | null | string;

/**
 * Union type for traversal (combinator) selector types
 */
type TraversalType = SelectorType.Adjacent | SelectorType.Child | SelectorType.Descendant | SelectorType.Parent | SelectorType.Sibling | SelectorType.ColumnCombinator;

Selector Type Enumeration

enum SelectorType {
  // Basic selectors
  Attribute = "attribute",
  Pseudo = "pseudo",
  PseudoElement = "pseudo-element", 
  Tag = "tag",
  Universal = "universal",
  
  // Combinators (traversals)
  Adjacent = "adjacent",        // +
  Child = "child",             // >
  Descendant = "descendant",   // (whitespace)
  Parent = "parent",           // < (non-standard)
  Sibling = "sibling",         // ~
  ColumnCombinator = "column-combinator"  // ||
}

Interface Definitions

/**
 * Represents an attribute selector like [attr=value]
 */
interface AttributeSelector {
  type: SelectorType.Attribute;
  name: string;                              // Attribute name
  action: AttributeAction;                   // Comparison operation
  value: string;                            // Attribute value
  ignoreCase: "quirks" | boolean | null;    // Case sensitivity flag
  namespace: string | null;                 // XML namespace
}

/**
 * Represents a pseudo-class selector like :hover, :nth-child(2n+1)
 */
interface PseudoSelector {
  type: SelectorType.Pseudo;
  name: string;           // Pseudo-class name
  data: DataType;         // Associated data (null, string, or nested selectors)
}

/**
 * Represents a pseudo-element selector like ::before, ::after
 */
interface PseudoElement {
  type: SelectorType.PseudoElement;
  name: string;              // Pseudo-element name
  data: string | null;       // Associated data
}

/**
 * Represents a tag selector like div, span, h1
 */
interface TagSelector {
  type: SelectorType.Tag;
  name: string;              // Tag name
  namespace: string | null;  // XML namespace
}

/**
 * Represents the universal selector *
 */
interface UniversalSelector {
  type: SelectorType.Universal;
  namespace: string | null;  // XML namespace
}

/**
 * Represents a combinator like >, +, ~, (space)
 */
interface Traversal {
  type: TraversalType;  // Type of combinator
}

Attribute Actions

/**
 * Enumeration of attribute selector operations
 */
enum AttributeAction {
  Any = "any",         // *= (contains substring)
  Element = "element", // ~= (contains word)
  End = "end",         // $= (ends with)
  Equals = "equals",   // = (exact match)
  Exists = "exists",   // [attr] (attribute exists)
  Hyphen = "hyphen",   // |= (starts with, hyphen-separated)
  Not = "not",         // != (not equal, non-standard)
  Start = "start"      // ^= (starts with)
}

Constants

/**
 * Standard values for case sensitivity modes in attribute selectors
 * Used to specify how attribute value matching should handle case sensitivity
 */
const IgnoreCaseMode = {
  Unknown: null,        // Case sensitivity is unknown/unspecified
  QuirksMode: "quirks", // Browser quirks mode behavior (default for class/id)
  IgnoreCase: true,     // Force case-insensitive matching
  CaseSensitive: false  // Force case-sensitive matching
} as const;

Supported CSS Features

Selector Types

  • Tag selectors: div, span, h1
  • Universal selector: *
  • Class selectors: .class-name
  • ID selectors: #element-id
  • Attribute selectors: [attr], [attr=value], [attr^=value], [attr$=value], [attr*=value], [attr~=value], [attr|=value]
  • Pseudo-classes: :hover, :focus, :nth-child(), :not(), :has(), etc.
  • Pseudo-elements: ::before, ::after, ::first-line, etc.

Combinators

  • Descendant: (whitespace)
  • Child: >
  • Adjacent sibling: +
  • General sibling: ~
  • Parent: < (non-standard, used by some libraries)
  • Column combinator: || (CSS Grid)

Advanced Features

  • CSS escaping and unescaping: Handles \ escaped characters in names, attributes, and values
  • Namespace support: XML namespace syntax namespace|element, supports wildcard *|element and empty namespace |element
  • Case sensitivity flags: [attr=value i] (case-insensitive), [attr=value s] (case-sensitive)
  • Complex pseudo-selectors: Special parsing for selector-containing pseudo-classes like :has(), :not(), :matches(), :is(), :where(), :host(), :host-context()
  • CSS comments: Handles /* */ comments within selectors
  • Error handling: Throws descriptive errors for malformed selectors, unmatched parentheses, and invalid syntax

Error Handling

The parser throws errors for various invalid selector conditions:

// Unmatched selector parts
parse("div ["); // Error: Attribute selector didn't terminate

// Invalid parentheses
parse("div:nth-child(2"); // Error: Parenthesis not matched

// Empty selectors
parse(", div"); // Error: Empty sub-selector

// Invalid characters in specific contexts
parse("div:has('invalid')"); // Error: Pseudo-selector has cannot be quoted

Namespace Support

Full XML namespace support for elements and attributes:

// Namespace prefix
const nsElement = parse("svg|rect");
// Result: [{ type: "tag", name: "rect", namespace: "svg" }]

// Universal namespace
const anyNs = parse("*|div");
// Result: [{ type: "tag", name: "div", namespace: "*" }]

// Empty namespace (no namespace)
const noNs = parse("|div");
// Result: [{ type: "tag", name: "div", namespace: "" }]

// Namespaced attributes
const nsAttr = parse("[xml|lang='en']");
// Result: [{ type: "attribute", name: "lang", namespace: "xml", action: "equals", value: "en", ignoreCase: null }]

Pseudo-Class Data Parsing

Special handling for pseudo-classes that contain nested selectors:

// Selector-containing pseudo-classes parse nested selectors
const has = parse("div:has(> p.highlight)");
// data property contains parsed Selector[][] structure

// String-containing pseudo-classes store raw string data
const nthChild = parse("li:nth-child(2n+1)");
// data property contains "2n+1" as string

// Quote stripping for specific pseudo-classes
const contains = parse("div:contains('text content')");
// Quotes automatically stripped from :contains and :icontains