All free skills / Other majors

css-tree 3.x

Use css-tree 3.x to parse, traverse, transform, generate, and lex CSS ASTs. Covers the v3 AST/API changes that commonly break v2-era code.

For
css-tree@3.2.1
Verified
2026-09-08, 6 examples executed
Skill package
@modootoday/pmcp-css-tree@3.0.0

Use this skill for free

No PMCP login or subscription is required. Download the original instructions, or install the content-only archive with your project's package manager.

Download SKILL.md · Download package archive

npm install --save-dev https://pmcp.build/skills/css-tree/3/package.tgz

PMCP discovers the installed package's instructions. Connect your runtime after installation.

Archive integritysha512-37MVL1RaMK7eF4lzb992gs3eSqyoLWMEbDsYEZutciGygVbmqso/OKQl4KLOvD3uELt2KKuJHRmwUcwInPuFKg==

Complete instructions

Verified against css-tree@3.2.1 on 2026-09-08. 6 of 6 examples executed.

css-tree 3.x

Scope

This skill targets css-tree versions in ^3.0.0 (the researched documentation currently shows 3.2.1). Import the package by name:

import * as csstree from 'css-tree';

The package also exposes subpath exports including css-tree/parser, css-tree/tokenizer, css-tree/selector-parser, css-tree/generator, css-tree/walker, css-tree/convertor, css-tree/lexer, css-tree/definition-syntax, and related data/utilities. Prefer the top-level API when it provides the operation you need.

Parse and generate

parse(source[, options]) creates an AST. generate(ast[, options]) serializes an AST back to CSS. Parsing can be directed at a specific grammar context, such as selector, value, or condition.

import assert from 'node:assert/strict';
import { generate, parse } from 'css-tree';

const ast = parse('.example { world: "!" }');
assert.equal(generate(ast), '.example{world:"!"}');

Useful parsing options documented by the package include:

  • context
  • atrule
  • positions
  • onComment
  • onToken
  • onParseError
  • filename, offset, line, and column
  • parseAtrulePrelude
  • parseRulePrelude
  • parseValue
  • parseCustomProperty

For a fragment rather than a complete stylesheet, provide the appropriate context:

import assert from 'node:assert/strict';
import { generate, parse } from 'css-tree';

const selector = parse('.foo.bar', {
    context: 'selector',
    positions: true
});

assert.equal(generate(selector), '.foo.bar');
assert.equal(selector.type, 'Selector');
assert.ok(selector.loc);

Walk and mutate ASTs

walk(ast, fn) is shorthand for walk(ast, { enter: fn }). A callback receives (node, item, list). The options form also supports enter, leave, visit, and reverse.

List fields such as first and size are getters. Do not treat them as the older mutable fields shown in v2-era code.

import assert from 'node:assert/strict';
import { generate, parse, walk } from 'css-tree';

const ast = parse('.example { world: "!" }');

walk(ast, (node) => {
    if (node.type === 'ClassSelector' && node.name === 'example') {
        node.name = 'hello';
    }
});

assert.equal(generate(ast), '.hello{world:"!"}');

To remove nodes, use the containing list and the callback's item:

import assert from 'node:assert/strict';
import { generate, parse, walk } from 'css-tree';

const ast = parse(`
    .a { foo: 1; bar: 2; }
    .b { bar: 3; baz: 4; }
`);

walk(ast, (node, item, list) => {
    if (node.type === 'Declaration' && node.property === 'bar' && list) {
        list.remove(item);
    }
});

assert.equal(generate(ast), '.a{foo:1}.b{baz:4}');

Find nodes

The top-level API also provides find(ast, fn), findLast(ast, fn), and findAll(ast, fn) for predicate-based lookup. Use these when you need a result rather than an in-place traversal.

import assert from 'node:assert/strict';
import { find, findAll, findLast, parse } from 'css-tree';

const ast = parse('.a { color: red } .b { color: blue }');
const isClass = (node) => node.type === 'ClassSelector';

assert.equal(find(ast, isClass).name, 'a');
assert.equal(findLast(ast, isClass).name, 'b');
assert.deepEqual(findAll(ast, isClass).map((node) => node.name), ['a', 'b']);

Lexer

The top-level lexer can match a parsed value against a property grammar. matchProperty(property, ast) returns a match result. Use isType(node, type) to test a node and getTrace(node) to inspect the grammar trace.

import assert from 'node:assert/strict';
import { lexer, parse } from 'css-tree';

const ast = parse('red 1px solid', { context: 'value' });
const matchResult = lexer.matchProperty('border', ast);
const first = ast.children.first;

assert.equal(matchResult.isType(first, 'color'), true);
assert.ok(matchResult.getTrace(first));

v3 AST and syntax changes

Code written against v2-era examples needs particular care:

  • v3 adds support for @container, @starting-style, @scope, @position-try, and @layer, plus layer, layer(), and supports() in @media.
  • New AST node types include Layer, LayerList, Feature, FeatureRange, FeatureFunction, Condition, GeneralEnclosure, and SupportsDeclaration.
  • Query conditions can be parsed with parse('...', { context: 'condition', kind: 'media' }).
  • MediaQuery now has modifier, mediaType, and condition; its shape is not the older shape.
  • MediaFeature is now Feature with kind: 'media'.
  • Parentheses is replaced by SupportsDeclaration / Condition for @supports.
  • Block no longer includes { and }. Atrule and Rule include them when they have a block.
  • Ratio parts are nodes, can be functions, can contain any number of parts, and may have an omitted right part during construction or transformation.
  • TokenStream has lookupTypeNonSC(); generic <dashed-ident> is supported.

v2 compatibility traps

Do not copy these older assumptions into v3 code:

  • v2's ESM/CommonJS dual-support examples and standalone subpath imports may still appear in code, but verify the v3 package export you intend to use.
  • Lexer#matchDeclaration() was removed.
  • SyntaxError moved to parse.SyntaxError.
  • TokenStream#skipWS() and getTokenLength() were removed.
  • List fields became getters, including list.first and list.size; do not use the old field shape.

Plain-script boundary

The parser, walker, generator, and lexer are usable directly from a Node or browser module. They do not require a test runner, CSS build step, or framework globals. The browser-global form requires the shipped bundle (dist/csstree.js or dist/csstree.esm.js) and a browser script environment.

Not covered

This skill does not cover the detailed AST schema for every CSS grammar node, the complete lexer-definition API, tokenizer internals, selector-parser internals, generator options, convertors, definition-syntax utilities, browser bundling configuration, or APIs not demonstrated or described by the supplied research.