The AST represents CSS as a tree structure where each node has a specific type and properties. All nodes share common properties and have type-specific properties.
All AST nodes have these properties:
The node type as a string. See Node Types for all possible values.
Position information for the node in the source code:
{
start: { line: number; column: number };
end: { line: number; column: number };
source?: string;
}Reference to the parent node in the AST.
The root node representing an entire CSS document.
Properties:
stylesheet.source(optional): Source file pathstylesheet.rules: Array of top-level rules (may includewhitespacenodes whenpreserveFormatting: true)stylesheet.parsingErrors(optional): Array of parse errors whensilentoption is used
Example:
{
"type": "stylesheet",
"stylesheet": {
"rules": [
// ... other nodes
]
}
}A CSS rule with selectors and declarations.
Properties:
selectors: Array of CSS selectors as stringsdeclarations: Array of declarations and comments
Example:
{
"type": "rule",
"selectors": ["body", "html"],
"declarations": [
{
"type": "declaration",
"property": "color",
"value": "red"
}
]
}A CSS property declaration.
Properties:
property: The CSS property namevalue: The CSS property value as a string
Example:
{
"type": "declaration",
"property": "background-color",
"value": "#ffffff"
}A CSS comment.
Properties:
comment: The comment text (without/*and*/)
Example:
{
"type": "comment",
"comment": " This is a comment "
}A whitespace node that preserves original formatting between sibling nodes. Only present when preserveFormatting: true is used during parsing.
Properties:
value: The whitespace text (spaces, newlines, tabs, etc.)
Example:
{
"type": "whitespace",
"value": "\n\n"
}A @media rule.
Properties:
media: The media query stringrules: Array of rules within the media block
Example:
{
"type": "media",
"media": "screen and (max-width: 768px)",
"rules": [
// ... nested rules
]
}A @keyframes rule.
Properties:
name: The keyframes namevendor(optional): Vendor prefix (e.g., "-webkit-")keyframes: Array of keyframe rules and comments
Example:
{
"type": "keyframes",
"name": "fade",
"keyframes": [
{
"type": "keyframe",
"values": ["from"],
"declarations": [
{
"type": "declaration",
"property": "opacity",
"value": "0"
}
]
}
]
}A keyframe within a @keyframes rule.
Properties:
values: Array of keyframe selectors (e.g.,["from"],["to"],["50%"])declarations: Array of declarations and comments
An @import rule.
Properties:
import: The import string (URL or media query)
Example:
{
"type": "import",
"import": "url('styles.css')"
}A @charset rule.
Properties:
charset: The character encoding
Example:
{
"type": "charset",
"charset": "utf-8"
}A @namespace rule.
Properties:
namespace: The namespace declaration
Example:
{
"type": "namespace",
"namespace": "url(http://www.w3.org/1999/xhtml)"
}A @supports rule.
Properties:
supports: The supports conditionrules: Array of rules within the supports block
Example:
{
"type": "supports",
"supports": "(display: grid)",
"rules": [
// ... nested rules
]
}A @document rule.
Properties:
document: The document conditionvendor(optional): Vendor prefixrules: Array of rules within the document block
A @page rule.
Properties:
selectors: Array of page selectorsdeclarations: Array of declarations and comments
A @font-face rule.
Properties:
declarations: Array of font declarations and comments
A :host rule.
Properties:
rules: Array of rules within the host block
A @container rule.
Properties:
container: The container queryrules: Array of rules within the container block
A @layer rule.
Properties:
layer: The layer namerules(optional): Array of rules within the layer block
A @custom-media rule.
Properties:
name: The custom media query namemedia: The media query definition
A @starting-style rule.
Properties:
rules: Array of rules within the starting-style block
The AST nodes are organized in the following hierarchy:
CssStylesheetAST- Root nodeCssAtRuleAST- Union of all at-rule and rule nodesCssAllNodesAST- Union of all possible node types
import { parse, CssStylesheetAST } from '@adobe/css-tools';
const ast: CssStylesheetAST = parse('body { color: red; }');
// Access top-level rules
ast.stylesheet.rules.forEach(rule => {
if (rule.type === 'rule') {
console.log('Selectors:', rule.selectors);
rule.declarations.forEach(decl => {
if (decl.type === 'declaration') {
console.log(`${decl.property}: ${decl.value}`);
}
});
}
});// Add a new declaration
const newDecl = {
type: 'declaration' as const,
property: 'font-size',
value: '16px'
};
// Find a rule and add the declaration
ast.stylesheet.rules.forEach(rule => {
if (rule.type === 'rule' && rule.selectors.includes('body')) {
rule.declarations.push(newDecl);
}
});When parsing with the silent option, errors are collected in the AST:
const ast = parse('invalid css {', { silent: true });
if (ast.stylesheet.parsingErrors) {
ast.stylesheet.parsingErrors.forEach(error => {
console.error('Parse error:', error.message);
});
}Position information is available on most nodes and includes:
start.lineandstart.column: Beginning of the nodeend.lineandend.column: End of the nodesource: Source file path (if provided during parsing)
This is useful for:
- Error reporting
- Source mapping
- Code analysis tools
- IDE integration
When parsing with preserveFormatting: true, the parser stores additional formatting metadata on AST nodes to support exact round-trip via identity mode. These properties are optional and only present when formatting is preserved.
CssWhitespaceAST nodes are inserted between sibling nodes in all arrays (rules, declarations, keyframes) to preserve the original whitespace and line breaks.
rawPrelude(on rules, at-rules with blocks): The exact original text before{, preserving whitespace between selector/condition and bracerawBetween(on declarations): The text between the property name and value, including:and any surrounding whitespace (e.g.,": "or": ")rawValue(on declarations): The untrimmed original value textrawSource(on statement at-rules: import, charset, namespace, custom-media, layer statement): The exact original text of the entire at-rule
Because formatting data is stored per-node rather than as a single cached string, you can freely insert, remove, or modify AST nodes and identity mode will reflect the changes:
const ast = parse(css, { preserveFormatting: true });
// Insert a new rule — it will be beautified since it has no raw properties
ast.stylesheet.rules.push({
type: CssTypes.rule,
selectors: ['.new'],
declarations: [{ type: CssTypes.declaration, property: 'color', value: 'blue' }]
});
// Remove a rule — the surrounding whitespace nodes naturally adapt
ast.stylesheet.rules.splice(1, 1);
// Identity mode outputs original formatting for untouched nodes + beautified for new nodes
stringify(ast, { identity: true });