Open-Source Wikis

/

Bun

/

Systems

/

CSS parser

oven-sh/bun

CSS parser

src/css/ is Bun's first-party CSS parser, transformer, minifier, and printer. It's used by the bundler when JS imports a .css file and by bun build --target=browser for HTML entry points. The design is a port and adaptation of Lightning CSS, originally Rust.

Files

File Size Purpose
src/css/css_parser.zig ~298 KB The parser. Single largest file in src/.
src/css/printer.zig ~22 KB Emits CSS bytes from the AST.
src/css/css_modules.zig ~16 KB CSS modules implementation.
src/css/css_internals.zig ~13 KB Shared types and helpers.
src/css/declaration.zig ~18 KB Declarations (color: red, etc.).
src/css/media_query.zig ~59 KB @media queries.
src/css/prefixes.zig, compat.zig ~84 KB + ~185 KB Vendor prefixing and browser compatibility.
src/css/properties/ many files Per-property type definitions and serialisers.
src/css/rules/ many files @font-face, @keyframes, @page, @import, ...
src/css/selectors/ dir Selector parser.
src/css/values/ dir Value type definitions (<color>, <length>, <image>, ...).
src/css/targets.zig ~13 KB browserslist-style target spec consumed during prefixing.
src/css/dependencies.zig ~5 KB Tracks CSS-side imports for the bundler.

What it can do

  • Parse modern CSS (CSS Nesting, container queries, @layer, @scope, :has(), math functions).
  • Add or strip vendor prefixes based on browser targets.
  • Minify whitespace, syntax (e.g. 0px0), and shorthand collapsing.
  • CSS Modules — scoped class names, composes:, value rewriting.
  • Track @import and url(...) dependencies so the bundler can include or copy them.
  • Serialise sourcemaps (basic).

Pipeline

graph LR
    Bytes[input bytes] --> Tokens[tokenise]
    Tokens --> AST[Stylesheet AST]
    AST --> Visit[transformations: prefixes, modules, minify]
    Visit --> Print[printer.zig]
    Print --> Out[output bytes]

The transformation phase is a single AST visit per goal — prefix application, declaration shorthand expansion/collapse, custom-properties resolution, etc.

Browser targets

Targets come from the bundler config (Bun.build({ target, browserslist })). They're parsed into a Targets struct and consulted by compat.zig and prefixes.zig. The compat tables are large because they enumerate property/feature support per browser/version, mirroring caniuse-lite.

CSS Modules

When a file is loaded with loader: "css-module" (or matched by *.module.css), css_modules.zig rewrites class names to globally-unique identifiers and emits a JSON-style export map for the bundle. The runtime exposes the map as the module's default export.

Performance

Largely hand-rolled with no allocations on the hot path:

  • The tokeniser writes into a stack-local buffer for short tokens.
  • The AST is allocated from a per-stylesheet bump arena.
  • Property values use a SmallList(T, N) (small_list.zig) so common cases avoid heap allocation.

Integration points

  • Bundlersrc/bundler/bundle_v2.zig calls in for .css files. Output goes into a separate CSS chunk.
  • Bake — Inlines CSS in the dev server response.
  • HTMLsrc/HTMLScanner.zig extracts inline <style> content and passes it through.
  • @import — Resolved through src/resolver/ so npm CSS imports work.

Entry points for modification

  • To add or fix a property, look at the relevant file under src/css/properties/.
  • To support a new at-rule, add a file under src/css/rules/ and register it in css_parser.zig's rule dispatch.
  • To change browser targeting, edit targets.zig and the relevant tables in compat.zig / prefixes.zig.

Key source files

File Purpose
src/css/css_parser.zig Parser entry.
src/css/printer.zig Output.
src/css/css_modules.zig Modules.
src/css/properties/ Per-property logic.
src/css/rules/ At-rule logic.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

CSS parser – Bun wiki | Factory