npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

zigcss

v0.2.1

Published

The world's fastest CSS compiler - Built with Zig for uncompromising performance

Downloads

189

Readme

zigcss

The world's fastest CSS compiler — Built with Zig for uncompromising performance

zigcss is a zero-dependency CSS compiler written in Zig, designed from the ground up to be the fastest CSS processing tool available. Leveraging Zig's compile-time optimizations, memory safety, and zero-cost abstractions, zigcss delivers unmatched performance for CSS parsing, transformation, and compilation.

Table of Contents

🚀 Performance

zigcss is engineered to be the fastest CSS compiler in the world. Key performance characteristics:

  • Zero runtime dependencies — Single binary, no external libraries
  • Compile-time optimizations — Leverages Zig's comptime for maximum efficiency
  • Memory-efficient parsing — Minimal allocations, zero-copy where possible
  • Parallel processing — Multi-threaded compilation for multiple files (utilizes all CPU cores)
  • Native performance — Compiled to machine code, not interpreted

Benchmarks

Performance tested on a MacBook Pro M3 (16GB RAM) with real-world CSS workloads. All tools tested with minification and optimization enabled.

Small CSS (~100 bytes)

| Compiler | Total Time | Speedup vs zigcss | |----------|----------------|-----------------| | zigcss | 11.1ms | 1x (baseline) | | Lightning CSS | 546.3ms | 49x slower | | esbuild | 599.2ms | 54x slower | | cssnano | 764.3ms | 69x slower | | PostCSS | 773.2ms | 70x slower | | Sass | 766.1ms | 69x slower |

zigcss is 49-70x faster than competitors for small files.

Medium CSS (~10KB, typical production bundle)

| Compiler | Total Time | Speedup vs zigcss | |----------|----------------|-----------------| | zigcss | 10.6ms | 1x (baseline) | | Lightning CSS | 558.0ms | 53x slower | | esbuild | 595.0ms | 56x slower | | PostCSS | 667.5ms | 63x slower | | cssnano | 744.6ms | 70x slower | | Sass | 803.0ms | 76x slower |

zigcss is 53-76x faster than competitors for medium-sized files.

Large CSS (~100KB, complex stylesheet)

| Compiler | Total Time | Speedup vs zigcss | |----------|----------------|-----------------| | zigcss | 27.0ms | 1x (baseline) | | Lightning CSS | 646.9ms | 24x slower | | esbuild | 619.2ms | 23x slower | | PostCSS | 699.8ms | 26x slower | | cssnano | 867.0ms | 32x slower | | Sass | 830.3ms | 31x slower |

zigcss is 23-32x faster than competitors for large files.

Tailwind CSS Build Comparison

Performance comparison of Tailwind CSS build process vs processing Tailwind-generated CSS with other tools:

| Compiler | Small | Medium | Large | |----------|-----------|------------|-----------| | Tailwind (build) | 2617.3ms | 2570.5ms | 2649.7ms | | LightningCSS | 681.5ms | 744.7ms | 744.6ms | | cssnano | 802.3ms | 906.0ms | 905.6ms | | esbuild | 650.1ms | 667.0ms | 691.2ms |

Key insights:

  • Tailwind CSS build process takes ~2.5-2.6 seconds (includes scanning HTML and generating CSS)
  • Processing Tailwind-generated CSS with LightningCSS or esbuild is 3-4x faster than Tailwind's build process
  • For post-processing Tailwind output, LightningCSS and esbuild offer the best performance

Note: Benchmarks run with zig build -Doptimize=ReleaseFast. Competitor tools tested via npx (Node.js v24.11.1). Results averaged over 10 iterations after 2 warmup runs. Performance improvements include SIMD-optimized whitespace skipping, optimized parser hot paths, pre-interned CSS keywords, and improved capacity estimation. Lightning CSS, cssnano, and esbuild are included as modern high-performance competitors. Tailwind benchmark compares the full build process (scanning HTML + CSS generation) vs processing the generated CSS with other tools.

Performance Summary

  • Throughput: ~3.7 MB/s for large files (100KB in 27ms)
  • Parsing speed: ~6,400 rules/second (100KB file with ~1000 rules)
  • Memory efficiency: Single 2.6MB binary, no runtime overhead
  • Startup time: Instant (no VM or interpreter startup)
  • Real-world: Processes typical 10KB production CSS in 10.6ms vs 558ms (Lightning CSS), 595ms (esbuild), or 803ms (Sass)

Why zigcss is Faster

  1. Native compilation - Compiled to machine code, not interpreted
  2. Zero-copy parsing - Minimal allocations, string interning for efficiency
  3. SIMD optimizations - Vectorized whitespace skipping
  4. Hash-based algorithms - O(n) selector merging vs O(n²) in competitors
  5. No runtime overhead - No Node.js, Dart VM, or interpreter startup time

Benchmarks run with --optimize --minify flags. Competitor numbers are estimates based on typical performance. Your results may vary based on hardware and CSS complexity.

✨ Features

  • Blazing fast — Optimized for speed at every level
  • 🔒 Memory safe — Zig's safety guarantees prevent common bugs
  • 📦 Zero dependencies — Single binary, no runtime requirements
  • 🎯 Full CSS3 support — Complete CSS specification compliance
  • 🔧 Extensible — Plugin system for custom transformations
  • 🧩 Modular — Use as a library or standalone CLI tool
  • 🌐 Cross-platform — Works on Linux, macOS, and Windows
  • 📝 Source maps — Full source map generation support
  • 🎨 CSS Nesting — Native support for CSS Nesting specification
  • 🔄 Custom Properties — Full CSS custom properties (variables) support
  • 📐 Media Queries — Advanced media query parsing and optimization
  • 📦 Container Queries — Full CSS Container Queries support with automatic optimization
  • 🎭 Pseudo-classes — Complete pseudo-class and pseudo-element support
  • 📋 Preprocessor Support — SCSS, SASS, LESS, Stylus, PostCSS, CSS Modules, CSS-in-JS
  • 🚀 Parallel Processing — Multi-threaded compilation for multiple files

📦 Installation

Package Managers

npm (Node.js):

npm install -g zigcss

The npm package automatically downloads the appropriate pre-built binary for your platform. If a pre-built binary is not available, you can build from source (requires Zig 0.15.2+).

Homebrew (macOS):

brew tap vyakymenko/zigcss
brew install zigcss

Or install from source:

brew install --build-from-source Formula/zigcss.rb

From Source

Requirements:

  • Zig 0.15.2 or later
  • C compiler (for linking)
git clone https://github.com/vyakymenko/zigcss.git
cd zigcss
zig build -Doptimize=ReleaseFast

The binary will be available at zig-out/bin/zigcss.

Pre-built Binaries

Pre-built binaries are available for all supported platforms on the releases page.

Supported Platforms:

  • Linux (x86_64, aarch64)
  • macOS (x86_64, aarch64)
  • Windows (x86_64)

Quick Install:

  1. Download the appropriate binary for your platform from the latest release
  2. Extract the archive
  3. Add the binary to your PATH or use it directly

Example (Linux/macOS):

# Download and extract
wget https://github.com/vyakymenko/zigcss/releases/download/v0.1.0/zigcss-0.1.0-x86_64-linux.tar.gz
tar -xzf zigcss-0.1.0-x86_64-linux.tar.gz

# Make executable and move to PATH
chmod +x zigcss
sudo mv zigcss /usr/local/bin/

Example (Windows):

# Download and extract
Invoke-WebRequest -Uri "https://github.com/vyakymenko/zigcss/releases/download/v0.1.0/zigcss-0.1.0-x86_64-windows.zip" -OutFile "zigcss.zip"
Expand-Archive -Path zigcss.zip -DestinationPath .

# Add to PATH (PowerShell as Administrator)
$env:Path += ";C:\path\to\zigcss"
[Environment]::SetEnvironmentVariable("Path", $env:Path, [EnvironmentVariableTarget]::Machine)

Using as a Library

Add to your build.zig.zon:

.{
    .name = "my-project",
    .version = "0.1.0",
    .dependencies = .{
        .zigcss = .{
            .path = "../zigcss",
        },
    },
}

Build Integration

zigcss provides build helpers for seamless integration with Zig's build system. Automatically compile CSS files as part of your build process:

1. Add zigcss as a dependency in build.zig.zon:

.{
    .name = "my-project",
    .version = "0.1.0",
    .dependencies = .{
        .zigcss = .{
            .path = "../zigcss",
        },
    },
}

2. Use build helpers in your build.zig:

const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const zigcss_dep = b.dependency("zigcss", .{
        .target = target,
        .optimize = optimize,
    });

    const zigcss_exe = zigcss_dep.artifact("zigcss");
    const zigcss_path = zigcss_dep.path("");

    const build_helpers = @import("build_helpers.zig");
    const build_helpers_path = b.pathJoin(&.{ zigcss_path, "build_helpers.zig" });
    const build_helpers_module = b.createModule(.{
        .root_source_file = b.path(build_helpers_path),
    });

    const css_step = build_helpers.addCssCompileStep(
        b,
        zigcss_exe,
        "zig-out/css",
    );

    css_step.addInputFile("src/styles.css");
    css_step.addInputFile("src/components.scss");
    css_step.setOptimize(true);
    css_step.setMinify(true);
    css_step.setAutoprefix(true);
    css_step.addBrowsers(&.{ "last 2 versions", "> 1%" });

    const install_step = b.getInstallStep();
    install_step.dependOn(&css_step.step);

    const exe = b.addExecutable(.{
        .name = "my-app",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    b.installArtifact(exe);
}

Build Helper API:

  • addCssCompileStep(builder, zigcss_exe, output_dir) - Create a CSS compilation step
  • addCssCompileStepTo(builder, zigcss_exe, output_dir, step) - Create and attach to a build step
  • css_step.addInputFile(file) - Add a single CSS file to compile
  • css_step.addInputFiles(files) - Add multiple CSS files
  • css_step.setOptimize(bool) - Enable/disable optimizations
  • css_step.setMinify(bool) - Enable/disable minification
  • css_step.setSourceMap(bool) - Enable/disable source maps
  • css_step.setAutoprefix(bool) - Enable/disable autoprefixer
  • css_step.addBrowser(browser) - Add browser support requirement
  • css_step.addBrowsers(browsers) - Add multiple browser requirements

CSS files are automatically compiled when you run zig build, and the compiled output is placed in the specified output directory.

🎯 Quick Start

Command Line Usage

# Compile a single CSS file
zigcss input.css -o output.css

# Compile with optimizations
zigcss input.css -o output.css --optimize --minify

# Add vendor prefixes
zigcss input.css -o output.css --autoprefix

# Add vendor prefixes with specific browser support
zigcss input.css -o output.css --autoprefix --browsers "last 2 versions,> 1%"

# Watch mode for development
zigcss input.css -o output.css --watch

# Generate source maps
zigcss input.css -o output.css --source-map

# Extract critical CSS for above-the-fold content
zigcss input.css -o critical.css --critical-classes "header,button,card" --critical-ids "nav" --critical-elements "div,body"

# Compile multiple files
zigcss src/*.css -o dist/ --output-dir

# Start Language Server Protocol server
zigcss --lsp

Supported Formats

zigcss supports multiple CSS preprocessor formats:

# SCSS/SASS
zigcss styles.scss -o styles.css
zigcss styles.sass -o styles.css

# LESS
zigcss styles.less -o styles.css

# CSS Modules
zigcss component.module.css -o component.module.css

# PostCSS
zigcss styles.postcss -o styles.css

# Stylus
zigcss styles.styl -o styles.css

Library Usage

Basic Compilation

const std = @import("std");
const zigcss = @import("zigcss");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const css = ".container { color: red; }";
    const result = try zigcss.compile(allocator, css, .{
        .optimize = true,
        .source_map = true,
    });
    defer result.deinit(allocator);
    
    std.debug.print("Compiled CSS: {s}\n", .{result.css});
}

Advanced Usage

const std = @import("std");
const zigcss = @import("zigcss");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const css = try std.fs.cwd().readFileAlloc(allocator, "styles.css", 10 * 1024 * 1024);
    defer allocator.free(css);

    const options = zigcss.CompileOptions{
        .optimize = true,
        .minify = true,
        .source_map = true,
        .autoprefix = .{
            .browsers = &.{ "last 2 versions", "> 1%" },
        },
        .remove_comments = true,
        .optimize_selectors = true,
    };

    const result = try zigcss.compile(allocator, css, options);
    defer result.deinit(allocator);

    try std.fs.cwd().writeFileAlloc(allocator, "styles.min.css", result.css);
    if (result.source_map) |map| {
        try std.fs.cwd().writeFileAlloc(allocator, "styles.min.css.map", map);
    }
}

📚 Examples

CSS Nesting

zigcss supports the CSS Nesting specification:

Input:

.card {
    padding: 1rem;
    border: 1px solid #ddd;
    
    &:hover {
        border-color: #007bff;
    }
    
    .title {
        font-size: 1.5rem;
        font-weight: bold;
        
        &::after {
            content: " →";
        }
    }
}

Output:

.card{padding:1rem;border:1px solid #ddd}.card:hover{border-color:#007bff}.card .title{font-size:1.5rem;font-weight:bold}.card .title::after{content:" →"}

Custom Properties (CSS Variables)

:root {
    --primary-color: #007bff;
    --spacing-unit: 8px;
    --border-radius: 4px;
}

.button {
    background-color: var(--primary-color);
    padding: calc(var(--spacing-unit) * 2);
    border-radius: var(--border-radius);
}

Media Queries

.container {
    width: 100%;
}

@media (min-width: 768px) {
    .container {
        width: 750px;
        margin: 0 auto;
    }
}

Container Queries

zigcss supports CSS Container Queries, allowing styles to be applied based on the size of a containing element rather than the viewport:

.card {
    container-type: inline-size;
}

@container (min-width: 400px) {
    .card {
        padding: 2rem;
    }
}

@container (min-width: 600px) {
    .card {
        display: grid;
        grid-template-columns: 1fr 1fr;
    }
}

Container queries are automatically optimized by merging identical @container rules, similar to media query optimization.

Cascade Layers

zigcss supports CSS Cascade Layers, allowing you to control the cascade order of your styles:

@layer theme {
    .button {
        color: red;
    }
}

@layer utilities {
    .button {
        color: blue;
    }
}

@layer theme {
    .link {
        color: green;
    }
}

Cascade layers are automatically optimized by merging identical @layer rules with the same name, reducing CSS size while maintaining cascade order.

Tailwind @apply Expansion

zigcss supports Tailwind CSS @apply directive expansion, automatically converting utility classes into CSS declarations:

Input:

.btn {
    @apply px-4 py-2 bg-blue-500 text-white rounded-lg shadow-md;
}

Output:

.btn {
    padding-left: 1rem;
    padding-right: 1rem;
    padding-top: 0.5rem;
    padding-bottom: 0.5rem;
    background-color: #3b82f6;
    color: #fff;
    border-radius: 0.5rem;
    box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
}

zigcss includes a comprehensive Tailwind utility registry covering:

  • Spacing: padding, margin utilities (p-, px-, py-, m-, mx-, my-, etc.)
  • Colors: text and background colors (text-, bg-)
  • Typography: font sizes, weights, styles, transforms
  • Layout: display, width, height, overflow utilities
  • Flexbox: flex direction, wrap, alignment utilities
  • Grid: grid template columns utilities
  • Borders: border width, style, radius utilities
  • Effects: shadows, opacity utilities

Dead Code Elimination

zigcss can remove unused CSS rules based on a list of used selectors (classes, IDs, elements, attributes). This is useful for removing CSS that's not referenced in your HTML/JavaScript:

Input CSS:

.used-class { color: red; }
.unused-class { color: blue; }
#used-id { color: green; }
#unused-id { color: yellow; }
div { color: black; }
span { color: white; }

Usage:

const std = @import("std");
const zigcss = @import("zigcss");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const css = try std.fs.cwd().readFileAlloc(allocator, "styles.css", 10 * 1024 * 1024);
    defer allocator.free(css);

    const used_classes = [_][]const u8{"used-class", "button", "card"};
    const used_ids = [_][]const u8{"used-id", "header"};
    const used_elements = [_][]const u8{"div", "body"};

    const dead_code_opts = zigcss.optimizer.DeadCodeOptions{
        .used_classes = &used_classes,
        .used_ids = &used_ids,
        .used_elements = &used_elements,
    };

    const parser_trait = zigcss.formats.getParser(.css);
    var stylesheet = try parser_trait.parseFn(allocator, css);
    defer stylesheet.deinit();

    const result = try zigcss.codegen.generate(allocator, &stylesheet, .{
        .optimize = true,
        .dead_code = dead_code_opts,
    });
    defer allocator.free(result);

    try std.fs.cwd().writeFile(.{ .sub_path = "styles.min.css", .data = result });
}

Output CSS:

.used-class { color: red; }
#used-id { color: green; }
div { color: black; }

Dead code elimination works with nested rules in @media, @container, and @layer at-rules, automatically removing entire at-rules if all their nested rules are unused.

Critical CSS Extraction

zigcss can extract critical CSS for above-the-fold content, keeping only the CSS rules needed for initial page render. This improves First Contentful Paint (FCP) and Largest Contentful Paint (LCP) metrics by reducing the amount of CSS that needs to be parsed and applied before the page becomes interactive.

Input CSS:

.critical-header { color: red; }
.non-critical-footer { color: blue; }
#critical-nav { color: green; }
#non-critical-sidebar { color: yellow; }
div { color: black; }
span { color: white; }

Usage:

const std = @import("std");
const zigcss = @import("zigcss");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const css = try std.fs.cwd().readFileAlloc(allocator, "styles.css", 10 * 1024 * 1024);
    defer allocator.free(css);

    const critical_classes = [_][]const u8{"critical-header", "button", "card"};
    const critical_ids = [_][]const u8{"critical-nav", "header"};
    const critical_elements = [_][]const u8{"div", "body"};

    const critical_css_opts = zigcss.optimizer.CriticalCssOptions{
        .critical_classes = &critical_classes,
        .critical_ids = &critical_ids,
        .critical_elements = &critical_elements,
    };

    const parser_trait = zigcss.formats.getParser(.css);
    var stylesheet = try parser_trait.parseFn(allocator, css);
    defer stylesheet.deinit();

    const result = try zigcss.codegen.generate(allocator, &stylesheet, .{
        .optimize = true,
        .critical_css = critical_css_opts,
    });
    defer allocator.free(result);

    try std.fs.cwd().writeFile(.{ .sub_path = "critical.css", .data = result });
}

Output CSS:

.critical-header { color: red; }
#critical-nav { color: green; }
div { color: black; }

Critical CSS extraction works with nested rules in @media, @container, and @layer at-rules, automatically removing entire at-rules if all their nested rules are non-critical.

CLI Usage:

# Extract critical CSS with specific classes, IDs, and elements
zigcss styles.css -o critical.css --critical-classes "header,button,card" --critical-ids "nav,header" --critical-elements "div,body"

SCSS Advanced Features

zigcss supports advanced SCSS features including mixins with content blocks and variable arguments:

Mixins with @content:

@mixin button {
    padding: 10px;
    border: 1px solid #ccc;
    @content;
}

.btn {
    @include button {
        color: red;
        background: blue;
    }
}

Variable Arguments:

@mixin box-shadow($shadows...) {
    box-shadow: $shadows;
}

.card {
    @include box-shadow(0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24));
}

Language Server Protocol (LSP) Support

zigcss includes a full LSP server implementation for editor integration:

Start the LSP server:

zigcss --lsp

Supported LSP Features:

  • Diagnostics - Real-time error and warning reporting for CSS parsing issues
  • Hover - Hover information for CSS properties with descriptions and value types
  • Completion - Code completion for common CSS properties
  • Text Document Sync - Full support for document open, change, and close events
  • Go to Definition - Jump to where CSS classes, IDs, and custom properties are defined
  • Find References - Find all references to CSS classes, IDs, and custom properties
  • Rename - Rename CSS classes, IDs, and custom properties across all references

Editor Integration:

zigcss provides official editor integrations for popular editors:

VSCode Integration

Option 1: Use the VSCode Extension (Recommended)

  1. Build zigcss:

    git clone https://github.com/vyakymenko/zigcss.git
    cd zigcss
    zig build -Doptimize=ReleaseFast
  2. Install the extension:

    cd vscode-extension
    npm install
    npm run compile
  3. Press F5 in VSCode to launch a new window with the extension loaded.

Option 2: Manual Configuration

Add to your .vscode/settings.json:

{
  "zigcss.languageServerPath": "zigcss",
  "zigcss.languageServerArgs": ["--lsp"]
}

If zigcss is not in your PATH, provide the full path:

{
  "zigcss.languageServerPath": "/path/to/zigcss/zig-out/bin/zigcss"
}

The VSCode extension provides:

  • Real-time diagnostics for CSS parsing errors
  • Hover information for CSS properties
  • Code completion for common CSS properties
  • Support for CSS, SCSS, SASS, LESS, and Stylus files

Neovim Integration

Using nvim-lspconfig:

  1. Install nvim-lspconfig plugin (using Packer):

    use {
      'neovim/nvim-lspconfig',
      config = function()
        require('lspconfig').zigcss.setup({
          cmd = {'zigcss', '--lsp'},
          filetypes = {'css', 'scss', 'sass', 'less', 'stylus'},
        })
      end
    }
  2. Or copy the configuration from neovim-config/init.lua to your Neovim config.

Features:

  • Diagnostics with [d / ]d navigation
  • Hover information with K
  • Code completion
  • Go to definition with gd
  • Find references with gr

Other Editors

The LSP server can be integrated with any editor that supports LSP:

  • Vim: Use with vim-lsp or coc.nvim
  • Emacs: Use with lsp-mode or eglot
  • Sublime Text: Use with LSP package
  • Atom: Use with atom-languageclient

All integrations use the standard LSP protocol via zigcss --lsp.

🏗️ Architecture

zigcss is built with performance in mind using a multi-stage compilation pipeline:

Parser

  • Hand-written recursive descent parser — No parser generators, maximum control over performance
  • Zero-copy tokenization — Tokens reference original input without copying
  • Streaming parser — Can process large files without loading entirely into memory
  • Error recovery — Continues parsing after errors for better developer experience
  • SIMD-optimized whitespace skipping — Processes 32 bytes at a time for faster parsing

Abstract Syntax Tree (AST)

  • Memory-efficient representation — Compact node structure
  • Type-safe nodes — Compile-time type checking for AST nodes
  • Lazy evaluation — Nodes computed only when needed
  • Immutable by default — Prevents accidental mutations

Optimizer

Multi-pass optimization pipeline:

  1. Empty rule removal ✅ — Remove rules with no declarations
  2. Selector merging ✅ — Merge rules with identical selectors (hash-based, O(n) complexity)
  3. Redundant selector removal ✅ — Remove selectors that are subsets of other selectors
  4. Shorthand property optimization ✅ — Combine longhand properties into shorthand:
    • margin-top, margin-right, margin-bottom, margin-leftmargin
    • padding-top, padding-right, padding-bottom, padding-leftpadding
    • border-width, border-style, border-colorborder
    • font-* properties → font
    • background-* properties → background
    • flex-grow, flex-shrink, flex-basisflex
    • grid-template-rows, grid-template-columns, grid-template-areasgrid-template
    • row-gap, column-gapgap
  5. Advanced selector optimization ✅ — Intelligent selector optimizations:
    • Universal selector removal (* removed when redundant)
    • Selector simplification (redundant combinators removed)
    • Selector specificity calculation for better optimization
    • Improved redundant selector detection using specificity
  6. Duplicate declaration removal ✅ — Remove duplicate properties (keeps last)
  7. Value optimization ✅ — Advanced optimizations:
    • Hex color minification (#ffffff#fff)
    • RGB to hex conversion (rgb(255, 255, 255)#fff)
    • CSS color name to hex conversion (red#f00, white#fff)
    • Transparent color optimization (transparentrgba(0,0,0,0))
    • Zero unit removal (0px0, 0em0)
    • Comprehensive unit support (px, em, rem, %, pt, pc, in, cm, mm, ex, ch, vw, vh, vmin, vmax)
    • CSS Math Functions optimization ✅ — Optimize calc(), min(), max(), and clamp() expressions
      • Evaluate constant expressions (calc(10px + 5px)15px)
      • Simplify nested calc() expressions
      • Remove unnecessary calc() wrappers (calc(10px)10px)
      • Optimize min()/max()/clamp() with numeric values
  8. CSS Logical Properties optimization ✅ — Convert logical properties to physical equivalents:
    • margin-inline-startmargin-left, margin-inline-endmargin-right
    • margin-block-startmargin-top, margin-block-endmargin-bottom
    • padding-inline-*padding-* (left/right), padding-block-*padding-* (top/bottom)
    • border-inline-*border-* (left/right), border-block-*border-* (top/bottom)
    • inset-inline-*left/right, inset-block-*top/bottom
    • Assumes LTR and horizontal-tb writing mode for safe conversion
  9. Media query merging ✅ — Merge identical @media rules into a single rule
  10. Container query merging ✅ — Merge identical @container rules into a single rule
  11. Cascade layer merging ✅ — Merge identical @layer rules into a single rule
  12. Dead code elimination ✅ — Remove unused CSS rules based on used selectors (classes, IDs, elements, attributes)

Code Generator

  • Fast code generation ✅ — Single-pass codegen with minimal allocations
  • Optimized size estimation ✅ — Accurate pre-allocation to reduce reallocations
  • Efficient selector generation ✅ — Optimized spacing logic
  • Advanced minification ✅ — Removes trailing semicolons, optimizes spacing
  • Source map support — Full source map generation
  • Incremental output — Stream output for large files

Performance Optimizations

  1. Arena allocator — Fast allocation for AST nodes
  2. String interning ✅ — Deduplicate repeated strings (property names, class names, identifiers)
  3. SIMD operations ✅ — Vectorized whitespace skipping (32 bytes at a time)
  4. Parallel parsing — Multi-threaded parsing for large files
  5. Zero-copy parsing — Tokens reference original input
  6. Comptime optimizations ✅ — Character classification lookup tables computed at compile time
  7. Capacity estimation ✅ — Pre-allocate ArrayLists with estimated sizes
  8. Hash-based selector merging ✅ — O(n²) → O(n) optimization using hash maps
  9. Optimized character classification ✅ — Lookup tables replace function calls
  10. Backwards iteration for duplicates ✅ — Efficient duplicate removal
  11. Advanced selector optimization ✅ — Universal selector removal, selector simplification, specificity-based optimization

🔧 API Reference

CompileOptions

pub const CompileOptions = struct {
    optimize: bool = false,
    minify: bool = false,
    source_map: bool = false,
    remove_comments: bool = false,
    optimize_selectors: bool = false,
    remove_empty_rules: bool = false,
    autoprefix: ?AutoprefixOptions = null,
    plugins: []const Plugin = &.{},
};

CompileResult

pub const CompileResult = struct {
    css: []const u8,
    source_map: ?[]const u8,
    
    pub fn deinit(self: *const CompileResult, allocator: Allocator) void {
        allocator.free(self.css);
        if (self.source_map) |map| {
            allocator.free(map);
        }
    }
};

Plugin System

zigcss includes a powerful plugin system that allows you to transform the AST during compilation. Plugins run after parsing and before optimization, giving you full control over CSS transformations.

Basic Plugin Usage

const std = @import("std");
const zigcss = @import("zigcss");

fn myTransform(allocator: std.mem.Allocator, stylesheet: *zigcss.ast.Stylesheet) !void {
    // Transform the stylesheet AST
    // For example, add a custom rule
    var style_rule = try zigcss.ast.StyleRule.init(stylesheet.allocator);
    var selector = try zigcss.ast.Selector.init(stylesheet.allocator);
    try selector.parts.append(stylesheet.allocator, .{ .class = "custom-class" });
    try style_rule.selectors.append(stylesheet.allocator, selector);
    
    var decl = zigcss.ast.Declaration.init(stylesheet.allocator);
    decl.property = "color";
    decl.value = "blue";
    try style_rule.declarations.append(stylesheet.allocator, decl);
    
    try stylesheet.rules.append(stylesheet.allocator, .{ .style = style_rule });
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const css = ".container { color: red; }";
    
    const parser_trait = zigcss.formats.getParser(.css);
    var stylesheet = try parser_trait.parseFn(allocator, css);
    defer stylesheet.deinit();

    const my_plugin = zigcss.plugin.Plugin.init("my-transform", myTransform);
    
    const options = zigcss.codegen.CodegenOptions{
        .plugins = &.{my_plugin},
        .optimize = true,
        .minify = true,
    };

    const result = try zigcss.codegen.generate(allocator, &stylesheet, options);
    defer allocator.free(result);
    
    std.debug.print("Compiled CSS: {s}\n", .{result});
}

Plugin Registry

For multiple plugins, use the PluginRegistry:

var registry = try zigcss.plugin.PluginRegistry.init(allocator);
defer registry.deinit();

const plugin1 = zigcss.plugin.Plugin.init("plugin1", transform1);
const plugin2 = zigcss.plugin.Plugin.init("plugin2", transform2);

try registry.add(plugin1);
try registry.add(plugin2);

try registry.run(&stylesheet);

Plugin API

pub const Plugin = struct {
    name: []const u8,
    transform: *const fn (allocator: std.mem.Allocator, stylesheet: *ast.Stylesheet) anyerror!void,
    
    pub fn init(name: []const u8, transform_fn: *const fn (allocator: std.mem.Allocator, stylesheet: *ast.Stylesheet) anyerror!void) Plugin;
};

pub const PluginRegistry = struct {
    pub fn init(allocator: std.mem.Allocator) !PluginRegistry;
    pub fn deinit(self: *PluginRegistry) void;
    pub fn add(self: *PluginRegistry, plugin: Plugin) !void;
    pub fn addSlice(self: *PluginRegistry, plugins: []const Plugin) !void;
    pub fn run(self: *const PluginRegistry, stylesheet: *ast.Stylesheet) !void;
    pub fn count(self: *const PluginRegistry) usize;
};

🧪 Testing

# Run all tests
zig build test

# Run specific test suite
zig build test --test-filter parser
zig build test --test-filter optimizer
zig build test --test-filter codegen

# Run benchmarks
zig build bench

# Run with verbose output
zig build test --summary all

# Compile with performance profiling
zigcss input.css -o output.css --profile

📚 Documentation

Comprehensive documentation is available at https://vyakymenko.github.io/zigcss/

The documentation site includes:

  • Getting started guide
  • Installation instructions
  • API reference
  • Examples and tutorials
  • Performance benchmarks
  • Plugin system documentation
  • Build integration guide
  • LSP support guide

📊 Roadmap

Phase 1: Core Features ✅ COMPLETED

  • [x] CSS3 parser implementation
  • [x] Basic optimization pipeline
  • [x] Minification
  • [x] Source map generation
  • [x] CLI tool
  • [x] Library API

Phase 2: Preprocessor Support ✅ COMPLETED

  • [x] SCSS/SASS support - Variables, nesting, indented syntax
  • [x] LESS support - Variables, at-rules
  • [x] CSS Modules support - Scoped class names
  • [x] CSS-in-JS compilation - Template literals extraction
  • [x] PostCSS support - @apply, @custom-media, @nest directives
  • [x] Stylus support - Variables, indented syntax
  • [x] Advanced nesting features ✅ — Mixins with @content, functions, variable arguments (...)
  • [x] Autoprefixer integration ✅ — Add vendor prefixes for CSS properties and values
  • [x] Custom property resolution ✅ — Resolve CSS custom properties (var()) with fallback support
  • [x] Advanced selector optimization ✅ — Universal selector removal, selector simplification, specificity calculation
  • [x] Plugin system ✅ — Extensible plugin architecture for custom AST transformations
  • [x] Watch mode improvements ✅ — Polling-based file watching with automatic recompilation
  • [x] Incremental compilation ✅ — Content hash-based change detection for faster watch mode

Phase 3: Performance & Polish ✅ COMPLETED

  • [x] Capacity estimation for ArrayLists
  • [x] Optimized character checks (inline functions)
  • [x] Faster whitespace skipping
  • [x] Output size estimation
  • [x] String interning for deduplication
  • [x] SIMD-optimized whitespace skipping
  • [x] CSS optimizer with multiple passes
  • [x] Character classification lookup tables (comptime-computed)
  • [x] Hash-based selector merging optimization (O(n²) → O(n))
  • [x] Comprehensive test suite
  • [x] Better error messages with position tracking
  • [x] Parallel file processing ✅ — Multi-threaded compilation for multiple files
  • [x] Incremental compilation ✅ — Content-based change detection for faster watch mode
  • [x] Performance profiling tools ✅ — Built-in profiling with timing and memory metrics

Phase 4: Ecosystem ✅ COMPLETED

  • [x] Language server protocol (LSP) support ✅ — Full LSP server with diagnostics, hover, and completion
  • [x] Editor integrations ✅ — VSCode extension and Neovim configuration
  • [x] Build tool integrations ✅ — Zig build system integration with build helpers
  • [x] Pre-built binaries for all platforms ✅ — GitHub Actions CI/CD for automated builds and releases (Linux, macOS, Windows)
  • [x] Package manager integration ✅ — npm package and Homebrew formula for easy installation
  • [x] Documentation site ✅ — Comprehensive documentation site with VitePress, deployed to GitHub Pages

Phase 5: Advanced CSS Features ✅ COMPLETED

  • [x] CSS Modules support
  • [x] CSS-in-JS compilation
  • [x] PostCSS plugin compatibility layer
  • [x] CSS Grid/Flexbox optimizations ✅ — Flexbox and Grid shorthand property optimizations
  • [x] Container queries ✅ — Full container query support with merging optimization
  • [x] Cascade layers ✅ — Full CSS Cascade Layers support with merging optimization
  • [x] Tailwind @apply expansion ✅ — Expand Tailwind utility classes in @apply directives

Phase 6: Advanced Optimizations ✅ COMPLETED

  • [x] CSS Math Functions optimization ✅ — Optimize calc(), min(), max(), and clamp() expressions
    • Evaluate constant expressions at compile time
    • Simplify nested calc() expressions
    • Remove unnecessary calc() wrappers
    • Optimize min()/max()/clamp() with numeric values
  • [x] Parser hot path optimizations ✅ — Optimize critical parsing functions
    • Optimize advance() function - reduce bounds checks and improve flow
    • Cache input length in estimate functions to avoid repeated lookups
    • Optimize skipComment - use direct character access
    • Improve SIMD whitespace skipping - cache length and reduce operations
    • Optimize skipWhitespaceScalar - use local variable instead of pointer dereference
  • [x] Codegen optimizations ✅ — Reduce allocations and improve string operations
    • Optimize generateStyleRule - cache minify flag, pre-calculate last index
    • Extract generateSelectorPart to reduce code duplication
    • Add early exit for single-part selectors
    • Reduce conditional checks in hot loops
  • [x] String pool and hash optimizations ✅ — Improve interning and hashing performance
    • Add early exits for empty strings in string pool intern functions
    • Add bounds checking in internSlice to prevent invalid slices
    • Optimize hashSelectors - add early exit for empty selectors, cache counts
    • Pre-allocate capacity when merging declarations to reduce reallocations
  • [x] At-rule merge optimizations ✅ — Faster string comparisons and better capacity estimation
    • Use length checks before mem.eql for faster comparisons
    • Pre-allocate ArrayLists with better capacity estimates
    • Reduce string comparisons in merge operations
  • [x] Parser hot path optimizations ✅ — Optimize critical parsing functions
    • Optimize parseIdentifier - use local variable for position, reduce bounds checks
    • Optimize parseDeclaration - direct character access, avoid unnecessary trim
    • Optimize parseSelector - cache peek results, use direct position updates
    • Optimize parseStyleRule - reduce peek() calls, use direct character access
  • [x] Optimizer loop optimizations ✅ — Cache lengths and reduce lookups
    • Pre-allocate hash map capacity in removeDuplicatesInRule
    • Cache selector and parts counts to avoid repeated lookups
    • Reduce function call overhead in hot loops
  • [x] CSS Logical Properties optimization ✅ — Convert logical properties to physical equivalents when safe
    • Convert margin-inline-* and margin-block-* to margin-* properties
    • Convert padding-inline-* and padding-block-* to padding-* properties
    • Convert border-inline-* and border-block-* to border-* properties
    • Convert inset-inline-* and inset-block-* to positioning properties
    • Assumes LTR and horizontal-tb writing mode for safe conversion
  • [x] Dead code elimination ✅ — Remove unused CSS rules and declarations based on used selectors
    • Remove CSS rules whose selectors don't match any used classes, IDs, elements, or attributes
    • Supports nested rules in @media, @container, and @layer at-rules
    • Configurable via API with DeadCodeOptions
  • [x] Critical CSS extraction ✅ — Extract above-the-fold CSS for faster initial render
    • Extract only CSS rules needed for above-the-fold content
    • Improves First Contentful Paint (FCP) and Largest Contentful Paint (LCP) metrics
    • Supports nested rules in @media, @container, and @layer at-rules
    • Configurable via API with CriticalCssOptions
    • CLI support with --critical-classes, --critical-ids, and --critical-elements flags
  • [x] Enhanced error messages ✅ — Provide suggestions and context for common errors
    • Context-aware error messages with nearby code snippets
    • Helpful suggestions for common mistakes (missing braces, colons, etc.)
    • Improved error formatting with line numbers, column positions, and visual indicators
    • Suggestions for fixing common syntax errors
  • [x] Advanced LSP features ✅ — Go to definition, find references, rename symbols
    • Go to definition for CSS classes, IDs, and custom properties
    • Find all references to CSS symbols across the document
    • Rename symbols with automatic updates to all references
    • Full LSP protocol support for navigation and refactoring
  • [x] Unused custom property removal ✅ — Remove unused CSS custom property definitions after inlining
    • Automatically removes custom property declarations that are no longer referenced
    • Works with nested rules in @media, @container, and @layer at-rules
    • Reduces CSS size by eliminating unused custom property definitions
    • Improves performance by reducing CSS parsing overhead
  • [x] At-rule reordering ✅ — Reorder at-rules for better compression and parsing efficiency
    • Groups @media, @container, and @layer rules together
    • Improves CSS compression by grouping similar at-rules
    • Enhances browser parsing efficiency with better rule organization
    • Works seamlessly with at-rule merging optimizations
  • [x] Early exit optimizations ✅ — Skip optimization passes when no work is needed
    • Early exits for empty stylesheets and rules
    • Skip duplicate removal when <= 1 declaration
    • Skip merging operations when no rules to merge
    • Significantly improves performance for edge cases and small stylesheets
    • Reduces unnecessary allocations and iterations
  • [x] String operation optimizations ✅ — Optimize string operations for maximum performance
    • Skip trimming when not needed (check whitespace before trimming)
    • Use direct character checks instead of startsWith for better performance
    • Add length checks before string operations to avoid unnecessary work
    • Skip processing empty declarations and rules throughout optimizer
    • Reduce allocations in hot paths

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

Development Setup

git clone https://github.com/vyakymenko/zigcss.git
cd zigcss
zig build test

Running Tests

# Run all tests
zig build test

# Run with coverage
zig build test --summary all

# Run specific test
zig build test --test-filter "test_parser_basic"

Code Style

  • Follow Zig's official style guide
  • Use zig fmt for formatting
  • Write tests for new features
  • Update documentation for API changes

Performance Guidelines

  • Profile before optimizing
  • Use std.benchmark for benchmarks
  • Document performance characteristics
  • Consider memory usage alongside speed

Submitting Changes

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🙏 Acknowledgments

  • Built with Zig — a general-purpose programming language designed for robustness, optimality, and maintainability
  • Inspired by the need for faster CSS tooling in modern web development
  • Parser design influenced by PostCSS and Lightning CSS

📞 Contact

📚 Resources


Made with ⚡ for speed