Frontend Development 9 min read

Understanding AST and Its Application in CSS Processing with PostCSS

This article explains what an Abstract Syntax Tree (AST) is, demonstrates how JavaScript code is transformed into an AST, describes the tokenization and parsing steps used by PostCSS to generate a CSS AST, and shows how plugin mechanisms like AutoPrefixer can extend the toolchain.

ByteFE
ByteFE
ByteFE
Understanding AST and Its Application in CSS Processing with PostCSS

In a typical front‑end project, browsers may render CSS inconsistently due to differing support for certain properties. To address this, the author explores using an Abstract Syntax Tree (AST) as an intermediate, easily manipulable representation of source code.

What is an AST?

An AST (Abstract Syntax Tree) is a tree‑structured, language‑agnostic representation of source code where each node corresponds to a syntactic construct. In practice it can be thought of as a JSON object or a collection of JSON nodes.

const randomNum = 0.1;

if (randomNum < 0.5) {
  console.log('right');
} else {
  console.log('error');
}

The above JavaScript snippet is parsed into the following AST (formatted as JSON):

{
  "type": "Program",
  "body": [
    {
      "type": "VariableDeclaration",
      "declarations": [
        {
          "type": "VariableDeclarator",
          "id": { "type": "Identifier", "name": "randomNum" },
          "init": { "type": "Literal", "value": 0.1, "raw": "0.1" }
        }
      ],
      "kind": "var"
    },
    {
      "type": "IfStatement",
      "test": {
        "type": "BinaryExpression",
        "operator": "<",
        "left": { "type": "Identifier", "name": "randomNum" },
        "right": { "type": "Literal", "value": 0.5, "raw": "0.5" }
      },
      "consequent": {
        "type": "BlockStatement",
        "body": [{ "type": "ExpressionStatement", "expression": { "type": "CallExpression", "callee": { "type": "MemberExpression", "computed": false, "object": { "type": "Identifier", "name": "console" }, "property": { "type": "Identifier", "name": "log" } }, "arguments": [{ "type": "Literal", "value": "right", "raw": "'right'" }] } }]
      },
      "alternate": {
        "type": "BlockStatement",
        "body": [{ "type": "ExpressionStatement", "expression": { "type": "CallExpression", "callee": { "type": "MemberExpression", "computed": false, "object": { "type": "Identifier", "name": "console" }, "property": { "type": "Identifier", "name": "log" } }, "arguments": [{ "type": "Literal", "value": "error", "raw": "'error'" }] } }]
      }
    }
  ],
  "sourceType": "script"
}

Typical CSS AST Compilation Process

PostCSS first tokenizes the source CSS, then builds a partial AST while generating tokens, and finally completes the AST by attaching additional information such as raw values and source locations.

Tokenization example (excerpt):

[ ['word', '#app', 1, 1, 1, 4], ['space', ' '], ['{', '{', 1, 6], ['space', '\n  '], ['word', 'color', 2, 3, 2, 7], ['space', '\n'], ['}', '}', 3, 1] ]

After token generation, PostCSS enriches the nodes with raw strings, source positions, and other metadata, producing a complete CSS AST such as:

{
  "raws": { "semicolon": false, "after": "" },
  "type": "root",
  "nodes": [
    {
      "raws": { "before": "", "between": " ", "semicolon": true, "after": "" },
      "type": "rule",
      "nodes": [
        {
          "raws": { "before": "", "between": ": " },
          "type": "decl",
          "prop": "color",
          "value": "red",
          "source": { "start": { "line": 2, "column": 3 }, "end": { "line": 2, "column": 13 }, "input": { "css": "#app {color: red;}", "hasBOM": false, "id": "
" } }
        }
      ],
      "selector": "#app",
      "source": { "start": { "line": 1, "column": 1 }, "end": { "line": 3, "column": 1 }, "input": { "css": "#app {color: red;}", "hasBOM": false, "id": "
" } }
    }
  ],
  "source": { "input": { "css": "#app {color: red;}", "hasBOM": false, "id": "
" }, "start": { "line": 1, "column": 1 } }
}

PostCSS Plugin Mechanism

PostCSS itself only generates an AST; its real power comes from plugins that traverse and transform this tree. This design mirrors Webpack loaders, allowing developers to add custom behavior such as vendor prefixing or linting.

A simple AutoPrefixer plugin works by fetching browser support data from the "Can I Use" database and automatically adding the appropriate vendor prefixes to CSS rules.

// pseudo‑code for AutoPrefixer logic
const prefixes = fetchCanIUseData();
cssAST.walkDecls(decl => {
  const needed = prefixes[decl.prop];
  if (needed) {
    needed.forEach(p => decl.cloneBefore({ prop: `-${p}-${decl.prop}`, value: decl.value }));
  }
});

Other example plugins include a lightweight StyleLint implementation that enforces coding style rules on the generated AST.

The article concludes with a link to the source repository and references for further reading.

Project URL: https://github.com/ding2650/AST

References

https://postcss.org/api/

https://developer.mozilla.org/zh-CN/docs/Mozilla/Projects/SpiderMonkey/Parser_API#%E8%8A%82%E7%82%B9%E5%AF%B9%E8%B1%A1

https://esprima.org/demo/parse.html#

frontendJavaScriptASTPluginPostCSSAutoprefixerCSS parsing
ByteFE
Written by

ByteFE

Cutting‑edge tech, article sharing, and practical insights from the ByteDance frontend team.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.