1 unstable release
| new 0.1.0 | Feb 13, 2026 |
|---|
#5 in #grid
170KB
4K
SLoC
cssbox
A standalone CSS layout engine in Rust. HTML/CSS in, exact coordinates out.
Built for PDF generation, document rendering, native UI, and anywhere you need CSS layout without a browser.
The Problem
If you need to lay out a document with CSS — generate a PDF from HTML, render rich text in a native app, build an e-book reader — your options are:
- Headless Chrome/Puppeteer — works, but it's a 200MB dependency that spawns a browser process
- WeasyPrint — Python-only, slow, flexbox/grid support is incomplete
- Prince XML — proprietary, expensive
- wkhtmltopdf — deprecated and unmaintained
Libraries like Yoga and Taffy are great for app-style UI (flexbox/grid), but they don't handle document-style layout — inline text flow, floats, tables, or the CSS cascade. They solve a different problem.
cssbox fills the gap: a fast, embeddable Rust library that handles the full CSS layout spec, from modern flexbox/grid to traditional document flow.
Getting Started
[dependencies]
cssbox-core = "0.1"
use cssbox_core::tree::BoxTreeBuilder;
use cssbox_core::style::ComputedStyle;
use cssbox_core::geometry::Size;
use cssbox_core::layout::{compute_layout, FixedWidthTextMeasure};
use cssbox_core::values::LengthPercentageAuto;
// Build a tree of styled nodes
let mut builder = BoxTreeBuilder::new();
let root = builder.root(ComputedStyle::block());
let mut child_style = ComputedStyle::block();
child_style.width = LengthPercentageAuto::px(200.0);
child_style.height = LengthPercentageAuto::px(100.0);
let child = builder.element(root, child_style);
let tree = builder.build();
// Compute layout against an 800x600 viewport
let result = compute_layout(&tree, &FixedWidthTextMeasure, Size::new(800.0, 600.0));
// Query the result — just like getBoundingClientRect()
let rect = result.bounding_rect(child).unwrap();
assert_eq!(rect.width, 200.0);
assert_eq!(rect.height, 100.0);
From HTML/CSS
use cssbox_dom::computed::html_to_box_tree;
use cssbox_core::geometry::Size;
use cssbox_core::layout::{compute_layout, FixedWidthTextMeasure};
let tree = html_to_box_tree(r#"
<div style="display: flex; gap: 10px">
<div style="flex: 1; height: 100px"></div>
<div style="flex: 2; height: 100px"></div>
</div>
"#);
let result = compute_layout(&tree, &FixedWidthTextMeasure, Size::new(800.0, 600.0));
What It Supports
cssbox implements 7 CSS layout modes — both the modern layout primitives (flexbox, grid) and the traditional document flow (block, inline, float) that document rendering requires.
| Algorithm | Spec | What it covers |
|---|---|---|
| Block | CSS 2.1 §9.4.1 | Width/height determination, margin collapsing, box-sizing, min/max constraints, percentages |
| Inline | CSS 2.1 §9.4.2 | Line box construction, greedy line breaking, text-align, vertical-align, white-space |
| Float | CSS 2.1 §9.5.1 | Left/right placement, exclusion zones, clear, BFC containment |
| Positioning | CSS 2.1 §9.3 | relative, absolute, fixed, sticky, constraint solving, stretch |
| Flexbox | Flex Level 1 | Direction, wrapping, grow/shrink, all alignment and spacing modes |
| Grid | Grid Level 2 | Track sizing (fr, minmax(), fit-content()), auto-placement, gap |
| Table | CSS 2.1 §17 | Fixed and auto layout, border-collapse/border-spacing, captions |
Landscape
Existing embeddable layout libraries are purpose-built for app UI (flexbox/grid) and intentionally skip document layout. Full browsers handle everything but aren't embeddable. cssbox sits in the middle.
Embeddable libraries
| Engine | Language | Stars | Document layout | App layout |
|---|---|---|---|---|
| Yoga | C++ | 18.7k | — | Flexbox |
| Taffy | Rust | 3k | Block only | Flexbox, Grid |
| Dropflow | TS/Zig | 1.4k | Block, Inline, Float | — |
| cssbox | Rust | — | Block, Inline, Float, Table | Flexbox, Grid |
Full browsers and renderers (not embeddable as libraries)
| Engine | Notes |
|---|---|
| Servo | Rust browser engine. Uses Taffy for flex/grid. Inline/float still incomplete (18-52% WPT). |
| NetSurf | C browser. Strong CSS 2.1 + flexbox. No grid. |
| Ladybird | C++ browser. Pre-alpha. Targeting full CSS. |
| WeasyPrint | Python PDF renderer. Best document layout coverage, but flexbox/grid marked "not deeply tested". |
Use Cases
- PDF / document generation — HTML/CSS to precise coordinates, then render to PDF
- Rich text UI — document-style layout in native apps (editors, readers, email clients)
- Native UI — CSS layout without a webview
- Embedded devices — small footprint, zero runtime dependencies
- Testing tools — verify CSS layout behavior programmatically
Crate Structure
| Crate | Description |
|---|---|
| cssbox-core | Core layout algorithms. Zero dependencies, no_std compatible. |
| cssbox-dom | HTML/CSS parsing, selector matching, cascade, computed values. |
| cssbox-test-harness | WPT test infrastructure (reftest + testharness.js). |
Text Measurement
cssbox doesn't measure text — you provide the font metrics via a trait:
pub trait TextMeasure {
fn measure(&self, text: &str, font_size: f32, max_width: f32) -> Size;
}
A built-in FixedWidthTextMeasure (8px/character) is included for testing. For production, plug in your font rasterizer (e.g., rusttype, fontdue, cosmic-text).
Testing
cargo test --workspace # all 97 tests
cargo test -p cssbox-core # core layout tests
cargo test -p cssbox-core block # specific algorithm
cargo test -p cssbox-core flex
cargo test -p cssbox-core grid
Contributing
Contributions are welcome! The biggest impact areas:
- WPT pass rate — pick a failing test, fix the layout algorithm
- Missing CSS properties — add parsing + layout support
- Text shaping — improve inline layout with real text measurement
- Performance — layout caching, incremental relayout
License
lib.rs:
cssbox-core — A standalone CSS layout engine.
This crate implements CSS layout algorithms: block, inline, float, positioning, flexbox, grid, and table. It takes a tree of styled nodes as input and produces computed positions and sizes as output.
Usage
use cssbox_core::tree::BoxTreeBuilder;
use cssbox_core::style::ComputedStyle;
use cssbox_core::geometry::Size;
use cssbox_core::layout::{compute_layout, FixedWidthTextMeasure};
let mut builder = BoxTreeBuilder::new();
let root = builder.root(ComputedStyle::block());
// ... add children ...
let tree = builder.build();
let result = compute_layout(&tree, &FixedWidthTextMeasure, Size::new(800.0, 600.0));
let root_rect = result.bounding_rect(tree.root());