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.
0px→0), and shorthand collapsing. - CSS Modules — scoped class names,
composes:, value rewriting. - Track
@importandurl(...)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
- Bundler —
src/bundler/bundle_v2.zigcalls in for.cssfiles. Output goes into a separate CSS chunk. - Bake — Inlines CSS in the dev server response.
- HTML —
src/HTMLScanner.zigextracts inline<style>content and passes it through. @import— Resolved throughsrc/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 incss_parser.zig's rule dispatch. - To change browser targeting, edit
targets.zigand the relevant tables incompat.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.