Layout & Styling
Kael uses GPU-accelerated flexbox (powered by Taffy) with a Tailwind-inspired API. Every style is a method call on a Div.
Flexbox layout
#![allow(unused)]
fn main() {
div().flex().flex_row().gap_2()
.child(div().child("Left"))
.child(div().child("Right"))
div().flex().flex_col().gap_4()
.child(div().child("Top"))
.child(div().child("Bottom"))
}
Alignment
#![allow(unused)]
fn main() {
div().flex()
.items_center()
.justify_center()
.justify_between()
.items_start()
.items_end()
}
Flex sizing
#![allow(unused)]
fn main() {
div().flex_1()
div().flex_grow()
div().flex_shrink_0()
div().flex_none()
}
Grid layout
Switch a container to CSS Grid with .grid(), define tracks with .grid_cols(n) / .grid_rows(n), and place children with .col_span(n) / .row_span(n) (or .col_span_full() / .row_span_full()). .gap_*() sets the gutters:
#![allow(unused)]
fn main() {
div()
.grid()
.grid_cols(5)
.grid_rows(5)
.gap_1()
.child(div().row_span(1).col_span_full().child("Header"))
.child(div().col_span(1).row_span(3).child("Sidebar"))
.child(div().col_span(3).row_span(3).child("Content"))
.child(div().col_span(1).row_span(3).child("Aside"))
.child(div().row_span(1).col_span_full().child("Footer"))
}
See examples/grid_layout.rs for a full “holy grail” layout.
Sizing
#![allow(unused)]
fn main() {
div().w(px(200.0)).h(px(100.0))
div().w_full()
div().h_full()
div().size_full()
div().size_8()
div().w_12()
div().h_6()
div().min_w(px(200.0)).max_w(px(600.0))
}
Spacing
#![allow(unused)]
fn main() {
div().p_4()
div().px_3()
div().py_2()
div().pt_1()
div().pl(px(20.0))
div().m_4()
div().mx_auto()
div().mt_2()
div().flex().gap_2()
div().flex().gap_4()
}
Colors
#![allow(unused)]
fn main() {
div().bg(rgb(0x1E1E1E))
div().text_color(rgb(0xFFFFFF))
div().border_color(rgb(0x3C3C3C))
div().bg(rgba(0x00000080))
div().bg(kael::red())
div().bg(kael::blue())
div().bg(kael::white())
div().bg(kael::black())
use kael::hsla;
div().bg(hsla(210.0 / 360.0, 1.0, 0.5, 1.0))
}
Borders
#![allow(unused)]
fn main() {
div().border_1()
div().border_2()
div().border_t_1()
div().border_b_1()
div().border_l_1()
div().border_r_1()
div().border_color(rgb(0x3C3C3C))
div().border_dashed()
}
Corners
#![allow(unused)]
fn main() {
div().rounded_sm()
div().rounded_md()
div().rounded_lg()
div().rounded_full()
div().rounded(px(8.0))
}
By default, rounded corners use continuous (squircle) rounding to match SwiftUI’s
RoundedRectangle shape on macOS. Use .circular_corners() to opt into the
legacy pure quarter-circle look:
#![allow(unused)]
fn main() {
div().rounded(px(8.0)).circular_corners()
}
Shadows
#![allow(unused)]
fn main() {
div().shadow_sm()
div().shadow_md()
div().shadow_lg()
div().shadow_xl()
}
Typography
#![allow(unused)]
fn main() {
div()
.text_xs()
.text_sm()
.text_base()
.text_lg()
.text_xl()
.text_2xl()
.text_3xl()
div().font_weight(FontWeight::BOLD)
div().font_family(".SystemUIFont")
}
Overflow and scrolling
Control how content behaves when it exceeds the element bounds:
#![allow(unused)]
fn main() {
div().overflow_hidden()
div().overflow_x_scroll()
div().overflow_y_scroll()
div().overflow_y_auto()
.id("scroll-container")
}
When using overflow_y_scroll() or overflow_y_auto() with a ScrollHandle,
Kael automatically renders a macOS-style scrollbar thumb when content overflows.
No extra widget is needed:
#![allow(unused)]
fn main() {
let scroll_handle = ScrollHandle::new();
div()
.id("my-scrollable")
.overflow_y_scroll()
.track_scroll(&scroll_handle)
.child(long_content)
}
The auto-scrollbar appears only when content exceeds the viewport and tracks the scroll position automatically. To keep scrollbars visible at all times (instead of auto-hiding after idle):
#![allow(unused)]
fn main() {
let scroll_handle = ScrollHandle::new().always_show_scrollbars();
}
For custom scrollbar styling, use the explicit scroll_bar() widget instead
(see Lists & Data).
Positioning
#![allow(unused)]
fn main() {
div().relative()
.child(
div().absolute()
.top(px(10.0))
.right(px(10.0))
.child("Badge")
)
}
Opacity
#![allow(unused)]
fn main() {
div().opacity(0.5)
}
Cursor
#![allow(unused)]
fn main() {
div().cursor_pointer()
div().cursor_default()
}
Conditional styling with .when()
#![allow(unused)]
fn main() {
div()
.when(self.is_selected, |this| {
this.bg(rgb(0x2563eb)).text_color(rgb(0xffffff))
})
.when(!self.is_selected, |this| {
this.bg(rgb(0xffffff)).text_color(rgb(0x000000))
})
}