Skip to content

Parsing CSS

Package: @lanexio/parser-grammar-css Stable Layer: 2 (Grammar). Depends only on @lanexio/parser-core. Runtime: Universal (browser, server, edge worker).

parseCss implements a CSS Syntax Module Level 3 parser with a two-pass architecture: tokenization (§4) followed by tree construction (§§5/9). It handles qualified rules, at-rules, declarations, functions, blocks, comments, and whitespace — producing a structured AST with Stylesheet, QualifiedRule, AtRule, Declaration, Selector, etc.

Input is always a Uint8Array. Output is always a LexTree. Malformed CSS produces CssKind.Error nodes — parseCss never throws.

The parser passes all 125 CourtBouillon css-parsing-tests cases and preserves comments and whitespace for round-trip fidelity.

import {
parseCss,
CssKind,
CSS_KIND_NAMES_BY_ID,
cssGrammar,
type ParseCssOptions,
type CssKindType,
} from '@lanexio/parser-grammar-css';
import { parseCss } from '@lanexio/parser-grammar-css';
const encoder = new TextEncoder();
const tree = parseCss(encoder.encode('body { color: red; font-size: 16px; }'));
console.log(tree.nodeCount); // total nodes
console.log(tree.root.kind); // Stylesheet root kind id (0x0900)

parseCss accepts a Uint8Array. Always use TextEncoder when converting a string to bytes.

const tree = parseCss(encoder.encode(`
@media screen and (min-width: 768px) {
.container { max-width: 720px; }
}
`));
// Produces: Stylesheet → AtRule("@media") → Block → QualifiedRule(".container") → ...
import { parseCss, CssKind } from '@lanexio/parser-grammar-css';
const tree = parseCss(encoder.encode('/* header */\nbody { color: red; }'));
for (const node of tree.root.children()) {
if (node.kind === CssKind.Comment) {
console.log('comment:', /* extract bytes via node.range */);
}
}

ParseCssOptions is an empty record (Record<string, never>). Reserved for future extension.

import { parseCss, CssKind } from '@lanexio/parser-grammar-css';
const encoder = new TextEncoder();
const tree = parseCss(encoder.encode('body { color: }')); // missing value
for (const node of tree.root.children()) {
if (node.kind === CssKind.Error) {
console.log('parse error at', node.range);
}
}

parseCss never throws. Malformed CSS produces CssKind.Error nodes. The parser always recovers.

import { CssKind } from '@lanexio/parser-grammar-css';
// CssKind is a const object. Use 'as const' pattern, never enum.
const kind: CssKindType = CssKind.Declaration;
// Node kind IDs (0x0900 block)
CssKind.Stylesheet; // 0x0900
CssKind.QualifiedRule; // 0x0901
CssKind.AtRule; // 0x0902
CssKind.Declaration; // 0x0903
CssKind.Block; // 0x0904
CssKind.Function; // 0x0905
CssKind.Selector; // 0x0906
CssKind.PropertyName; // 0x0907
CssKind.Value; // 0x0908
CssKind.Comment; // 0x0909
CssKind.Whitespace; // 0x090a
CssKind.Error; // 0x09ff

CssKind values are stable across versions. Never use raw numbers — always reference CssKind.<name>.

ExportTypeDescription
parseCss(bytes: Uint8Array, options?: ParseCssOptions) => LexTreeParse CSS Syntax Level 3. Never throws.
ParseCssOptionsRecord<string, never>Options for parseCss (empty, reserved).
CssKindconst objectNumeric kind IDs for all CSS node types (0x0900 block).
CssKindTypetype unionUnion of all CssKind values.
CSS_KIND_NAMES_BY_IDReadonly<Record<number, string>>Kind-name lookup by numeric ID.
cssGrammarLanexio ParserPureGrammarGrammar descriptor for parser-pure.
MetricValue
CourtBouillon corpus125 / 125 (100%)
Error cases (not thrown)24
verify:no-throw54 entry points
Bundle4,779 / 16,384 B (29.2%)
Fuzz soak (300s)4.3M rounds / 0 crashes