Kozan is a platform layer — DOM, events, style, layout, paint, scroll, compositing — that UI frameworks build on top of. Two frameworks on Kozan share the same tree, the same event system, and the same rendering pipeline.
This is experimental software. APIs change without notice.
use kozan::prelude::*;
fn main() -> kozan::Result<()> {
App::new().window(WindowConfig::default(), build_ui).run()
}
fn build_ui(ctx: &ViewContext) {
let doc = ctx.document();
let row = doc.div();
row.style().flex().gap(px(16.0)).pad(px(20.0)).bg(rgb8(44, 62, 80));
row.append(doc.create_text("Hello, Kozan!"));
doc.body().child(row);
}DOM → Style → Layout → Paint → Composite → GPU
Three threads per window. Main thread routes OS events. View thread runs the DOM, style, layout, and paint. Render thread runs the compositor and GPU — scroll happens here at vsync rate, independent of layout.
| Crate | |
|---|---|
kozan |
Facade — re-exports everything |
kozan-core |
DOM, events, style, layout, paint, scroll, compositor |
kozan-primitives |
Geometry, color, arena allocator |
kozan-scheduler |
Event loop, task queues, async executor |
kozan-macros |
Derive macros for Element, Node, Props |
kozan-platform |
Window management, threading, renderer traits |
kozan-winit |
winit adapter |
kozan-vello |
Vello + wgpu backend |
Two ways — inline CSS strings or the type-safe builder:
// CSS string (parsed by Stylo)
div.set_attribute("style", "display: flex; gap: 16px; padding: 20px");
// Builder API
div.style().flex().gap(px(16.0)).pad(px(20.0)).bg(rgb8(44, 62, 80));Load a stylesheet and toggle classes:
doc.add_stylesheet(include_str!("../assets/dashboard.css"));
card.class_add("card");
card.class_add("card-blue");
card.class_remove("card-blue");btn.on::<ClickEvent>(|event, ctx| {
println!("clicked at ({}, {})", event.x, event.y);
});
// Capture phase
container.on_capture::<ClickEvent>(|event, ctx| {
ctx.stop_propagation();
});
// One-shot listener (auto-removed after first call)
btn.on_once::<ClickEvent>(|_, _| {
println!("only fires once");
});ctx.spawn(async move {
sleep(Duration::from_millis(500)).await;
card.class_add("visible");
progress_bar.style().w(pct(75.0));
});let doc = ctx.document();
let container = doc.div();
let child = doc.div();
let text = doc.create_text("Hello");
container.append(child);
child.append(text);
doc.body().child(container);
// Query
let first = container.first_child();
let kids = container.children();
// Remove
child.remove();Layout: block, flexbox, grid (tracks, repeat, minmax, named areas, auto-placement), inline text with shaping (HarfBuzz), RTL/bidi, float (partial).
CSS: width/height/margin/padding (px, %, auto), border (width, color, radius), background-color, color, opacity, font-size/weight/family/style, text-align, text-decoration, visibility, overflow (visible, hidden, scroll), gap, aspect-ratio, box-shadow, outline.
Events: click, dblclick, mousedown/up/move/enter/leave/over/out, contextmenu, keydown/keyup, wheel, focus/blur/focusin/focusout, scroll, resize. Full W3C capture/target/bubble dispatch.
Rendering: rectangles, rounded rectangles, borders (solid), text (pre-shaped glyphs), lines, box shadows, outlines, opacity layers, clip regions, scroll transforms.
Elements: div, span, p, h1-h6, a, button, input (18 types), textarea, select, img, canvas, video, audio, table, ul/ol/li, form, section/article/nav/aside, and more.
- Animations and transitions
- Gradients (linear, radial, conic)
- Images (element exists, rendering stubbed)
- Filters (blur, brightness, etc.)
position: fixed/position: sticky- Text selection, clipboard
- Media playback (video/audio stubs only)
- Custom properties (CSS variables)
cargo run --example hello-world
cargo run --example dashboard
cargo test --workspaceRequires Rust 1.85+.
The name "Kozan" is a trademark of Youssef Khalil. The code is yours to use under Apache-2.0. The name and logo are not — forks can't use them to imply official status. Same policy as Rust and Firefox.