Idiomatic Rust styling library inspired by Tailwind CSS
Type-safe, composable styles for native GUI applications
Twill is a backend-agnostic style system for Rust UI code. It provides:
- typed design tokens for color, spacing, typography, shadows, motion, and semantic theme aliases;
- a fluent
Stylebuilder for utility-style composition; - typed arbitrary/custom-property escape hatches for key utility families, inspired by Tailwind's arbitrary values;
- state/style layers such as
hover,focus,focus_visible,selected,checked,open,closed,data_*, andaria_*; - breakpoint-based responsive composition via
sm,md,lg,xl, ands2xl; - backend adapters for
egui,iced, andslint.
Twill does not ship UI components such as Button, Card, or Dialog, and it does not expose a CSS serialization layer.
The ecosystem now has five entry points:
twill-corefor libraries and applications that only need the backend-agnostic style engine;twill-eguifor egui-only adapter code on top oftwill-core;twill-icedfor iced-only adapter code on top oftwill-core;twill-slintfor slint-only adapter code on top oftwill-core;twillas the facade crate that re-exportstwill-coreand optionally adds GUI adapters.
Examples and cookbook pages in main target the 0.3.x API only. Legacy demos built around component APIs or CSS serialization belong to the 0.2.x release line, not this branch.
[dependencies]
twill-core = "0.3"
twill-egui = "0.3"
twill-iced = "0.3"
twill-slint = "0.3"
# or use the facade crate
twill = "0.3"
# Optional backend adapters
twill = { version = "0.3", features = ["egui"] }
twill = { version = "0.3", features = ["iced"] }
twill = { version = "0.3", features = ["slint"] }MSRV: Rust 1.93.
Backend notes:
twill-coreis synchronous and backend-agnostic.twill-egui,twill-iced, andtwill-slintare thin adapter crates that depend ontwill-coredirectly.twillre-exports the full core API and only pulls GUI dependencies when you enable a backend feature.eguiadds conversion helpers for egui types only.icedadds the Iced adapter and the Unix windowing/runtime feature set used by this crate configuration.slintadds Slint conversion helpers only when requested.
use twill::prelude::core::*;
let style = Style::card()
.merged(Style::interactive())
.padding(Padding::symmetric(Spacing::S2, Spacing::S4))
.hover(|style| style.opacity(0.9))
.data_attr(DataState::Open, |style| style.shadow(Shadow::Lg))
.at_md(|style| style.padding(Padding::all(Spacing::S6)));Twill now has a stratified import surface:
twill::prelude::core::*for day-to-dayStyle, tokens, layout, and state builders;twill::prelude::theme::*for semantic aliases such asSemanticColorand theme resolvers;twill::prelude::arbitrary::*for typed arbitrary/custom-property escape hatches;twill::prelude::traits::*for generic integration traits likeMergeandResponsive.
twill::prelude::* still exists when you explicitly want the full power-user surface.
Typed escape hatches stay in the style layer:
use twill::prelude::{arbitrary::*, core::*};
let style = Style::new()
.text_color_arbitrary(ColorValueToken::from_rgb8(248, 250, 252))
.px_var(PaddingVar::new("--panel-pad-x"))
.pb_rem(1.25)
.border_color_var(BorderColorVar::new("--panel-border"))
.min_w_var(WidthVar::new("--panel-min-w"))
.tracking_em(0.035)
.leading_number(1.75)
.transition_custom("filter, transform")
.transition_duration_ms(240);The core crate now ships a few reusable presets that stay intentionally small:
Style::surface()for structural spacing, radius, and elevation;Style::card()for semantic card/background/border defaults;Style::interactive()for cursor, transition, focus ring, and disabled affordances.
Use them as building blocks and layer app-specific details with merged(...) or
merge_in_place(...).
Styleis the central style composition type.twill-coreis the lightest dependency when you do not need backend adapters.twill-egui,twill-iced, andtwill-slintare the narrowest way to depend on one backend adapter.twill::prelude::core::*is the recommended starter import.twill::prelude::{theme::*, arbitrary::*, traits::*}expand the API surface only when needed.SemanticThemeVarsandDynamicSemanticThemeprovide semantic alias mapping inspired by shadcn theme variables.Style::resolved_theme(...),resolved_light_theme(...), andresolved_dark_theme(...)let you turn semantic aliases into concrete color tokens before backend conversion.twill::backends::{egui, iced, slint}expose typed conversion helpers for each supported runtime.
Common state layers:
hoverfocusfocus_visibleactivedisabledselectedcheckedopencloseddata_attr(DataState::..., ...)aria_attr(AriaAttr::..., ...)
Responsive layers:
smmdlgxls2xlStyle::at_breakpoint(...)
Supported adapters:
eguiicedslint
Each backend translates Twill tokens and Style values into framework-specific primitives without changing the core style model.
Use twill-egui, twill-iced, or twill-slint directly when you want one adapter crate; use
twill when you want the facade and feature-gated re-exports.
- mdBook sources live in
docs/ - crate documentation is published on docs.rs/twill
- release notes live in CHANGELOG.md
Useful checks:
cargo fmt --all --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace --all-features
cargo check --workspace --all-features --examplesMIT License — see LICENSE