| Crates.io | kaolin |
| lib.rs | kaolin |
| version | 0.1.15 |
| created_at | 2025-07-28 00:26:34.348743+00 |
| updated_at | 2025-11-02 01:53:47.504639+00 |
| description | A Rust library for flexible layouts, inspired by Clay |
| homepage | |
| repository | https://github.com/toto04/kaolin |
| max_upload_size | |
| id | 1770549 |
| size | 195,221 |
A flexible, immediate-mode UI layout library for Rust, inspired by Clay. Kaolin provides a powerful and intuitive API for creating responsive layouts that work across different rendering backends.
[!CAUTION]
WORK IN PROGRESS
This library is under active development, and still in its infancy
There WILL be breaking changes, use at your own risk
Add Kaolin to your Cargo.toml:
[dependencies]
kaolin = "0.1"
# For Raylib support
kaolin = { version = "0.1", features = ["raylib"] }
# For embedded graphics support
kaolin = { version = "0.1", features = ["embedded"] }
Here's a simple "Hello, World!" example using the Raylib renderer:
use kaolin::{
Kaolin, grow, sizing,
renderers::KaolinRenderer,
style::{
FlexStyle, TextStyle,
layout::{Alignment, Justification, Layout},
},
};
fn main() {
use kaolin::renderers::raylib::RaylibRenderer;
use raylib::color::Color;
let mut renderer = RaylibRenderer::new(800, 600);
while !renderer.should_close() {
renderer.draw(|k| {
k.with(
FlexStyle::new()
.background_color(Color::WHITE)
.layout(Layout::new()
.alignment(Alignment::Center)
.justification(Justification::Center))
.sizing(sizing!(grow!())),
|k| {
k.text("Hello, World!",
TextStyle::new()
.font_size(48.0)
.color(Color::BLACK))
}
)
});
}
}
Kaolin uses a tree-based layout system where each element can contain child elements. The layout is built using a functional, immediate-mode API:
kaolin.draw(|k| {
k.with(container_style, |k| {
k.text("Some text", text_style)
.with(nested_container_style, |k| {
k.text("Nested content", text_style)
})
})
})
Kaolin supports several sizing modes for flexible layouts:
// Fixed dimensions
sizing!(fixed!(200.0), fixed!(100.0)) // 200px width, 100px height
sizing!(fixed!(150.0)) // 150px for both dimensions
// Fits to content size
sizing!(fit!()) // No constraints
sizing!(fit!(max_width)) // Maximum width constraint
sizing!(fit!(min_width, max_width)) // Min and max constraints
// Grows to fill available space
sizing!(grow!()) // Default growth factor (1.0)
sizing!(grow!(2.0)) // Custom growth factor
sizing!(grow!(1.0, min, max)) // Growth with constraints
Control how child elements are arranged:
Layout::new()
.direction(Direction::LeftToRight) // or TopToBottom, RightToLeft, BottomToTop
.alignment(Alignment::Center) // Cross-axis: Start, End, Center, Stretch
.justification(Justification::Center) // Main-axis: Start, End, Center, SpaceBetween, SpaceAround
.gap(10.0) // Spacing between children
Style containers and text with comprehensive options:
// Container styling
FlexStyle::new()
.background_color(Color::BLUE)
.corner_radius(8.0)
.padding(Padding::all(16.0))
.border(Border::new().width(2.0).color(Color::BLACK))
// Text styling
TextStyle::new()
.font_size(24.0)
.font_id(1)
.color(Color::WHITE)
Create layouts that adapt to different screen sizes:
k.with(
FlexStyle::new()
.layout(Layout::new().direction(Direction::TopToBottom))
.sizing(sizing!(grow!())),
|k| {
// Header
k.with(
FlexStyle::new()
.background_color(Color::DARKBLUE)
.sizing(sizing!(grow!(), fixed!(60.0))),
|k| k.text("Header", TextStyle::new().color(Color::WHITE))
)
// Content area
.with(
FlexStyle::new()
.background_color(Color::LIGHTGRAY)
.sizing(sizing!(grow!())),
|k| {
k.text("Main Content", TextStyle::new())
}
)
// Footer
.with(
FlexStyle::new()
.background_color(Color::DARKGRAY)
.sizing(sizing!(grow!(), fixed!(40.0))),
|k| k.text("Footer", TextStyle::new().color(Color::WHITE))
)
}
)
k.with(
FlexStyle::new()
.layout(Layout::new().gap(20.0))
.sizing(sizing!(grow!())),
|k| {
// Sidebar
k.with(
FlexStyle::new()
.background_color(Color::GRAY)
.sizing(sizing!(fixed!(200.0), grow!())),
|k| k.text("Sidebar", TextStyle::new())
)
// Main content
.with(
FlexStyle::new()
.background_color(Color::WHITE)
.sizing(sizing!(grow!())),
|k| k.text("Main Content", TextStyle::new())
)
}
)
Perfect for desktop applications and games:
use kaolin::renderers::raylib::RaylibRenderer;
let mut renderer = RaylibRenderer::new(800, 600);
while !renderer.should_close() {
renderer.draw(|k| {
// Your layout here
});
}
Ideal for embedded systems and small displays:
use kaolin::renderers::embedded::EmbeddedRenderer;
use embedded_graphics_simulator::{SimulatorDisplay, Window};
let mut display = SimulatorDisplay::new(Size::new(128, 64));
let renderer = EmbeddedRenderer::new(&fonts, display.bounding_box());
renderer.onto(&mut display).draw(|k| {
// Your layout here
});
Implement the KaolinRenderer trait to create your own backend:
impl KaolinRenderer for MyRenderer {
type Color = MyColor;
fn draw(&mut self, draw_fn: impl Fn(Kaolin<Self::Color>)) {
// Implementation
}
}
Kaolin automatically handles text wrapping within constrained widths:
k.with(
FlexStyle::new().sizing(sizing!(fixed!(200.0), fit!())),
|k| {
k.text("This is a long text that will automatically wrap to multiple lines",
TextStyle::new())
}
)
Use growth factors to create proportional layouts:
k.with(FlexStyle::new().sizing(sizing!(grow!(1.0))), |k| k) // Takes 1/4 of space
.with(FlexStyle::new().sizing(sizing!(grow!(3.0))), |k| k) // Takes 3/4 of space
Create complex layouts by nesting containers:
k.with(outer_container, |k| {
k.with(inner_container_1, |k| {
k.text("Content 1", style)
})
.with(inner_container_2, |k| {
k.with(deeply_nested, |k| {
k.text("Nested content", style)
})
})
})
Kaolin is designed for immediate-mode rendering with minimal allocations:
Contributions are welcome! Please feel free to submit issues, feature requests, or pull requests.
See the LICENSE file for details.
Kaolin is heavily inspired by Clay, bringing similar concepts to the Rust ecosystem with type safety and zero-cost abstractions.