Twill Docs
Twill is a type-safe styling layer for native Rust GUI applications.
Use this guide for token selection, style composition, and backend mapping for egui, iced, and slint.
Getting Started
Start here:
Installation
Minimum setup
Add twill to your Cargo.toml:
[dependencies]
twill = "0.3"
Enable GUI backends
Enable only the backends you use:
[dependencies]
twill = { version = "0.3", features = ["egui"] }
# or
twill = { version = "0.3", features = ["iced"] }
# or
twill = { version = "0.3", features = ["slint"] }
You can combine features:
[dependencies]
twill = { version = "0.3", features = ["egui", "iced", "slint"] }
Verify installation
Run:
& "$env:USERPROFILE\.cargo\bin\cargo.exe" check
If you enabled backend features, you can verify the crate builds with:
& "$env:USERPROFILE\.cargo\bin\cargo.exe" build --features egui
& "$env:USERPROFILE\.cargo\bin\cargo.exe" build --features iced
& "$env:USERPROFILE\.cargo\bin\cargo.exe" build --features slint
Quick Start
#![allow(unused)]
fn main() {
use twill::prelude::*;
let style = Style::new()
.padding(Padding::symmetric(Spacing::S2, Spacing::S4))
.bg(Color::blue(Scale::S500))
.rounded(BorderRadius::Md);
}
#![allow(unused)]
fn main() {
use twill::prelude::*;
let style = Style::new()
.padding(Padding::all(Spacing::S4))
.bg(Color::blue(Scale::S500))
.hover(|style| style.opacity(0.9))
.md(|style| style.padding(Padding::all(Spacing::S6)));
}
Concepts
Core concepts:
Design Tokens
Twill tokens are Rust enums and value types representing styling primitives.
Color tokens
Use Color + Scale:
#![allow(unused)]
fn main() {
use twill::prelude::*;
let primary = Color::blue(Scale::S500);
let danger = Color::red(Scale::S600);
let bg = Color::gray(Scale::S50);
}
Special colors:
Color::white()Color::black()Color::transparent()
Spacing tokens
Use Spacing for paddings, margins, gaps:
#![allow(unused)]
fn main() {
use twill::prelude::*;
let p = Spacing::S4; // 1rem
let gap = Spacing::S2; // 0.5rem
}
Border and radius tokens
BorderWidthBorderStyleBorderRadius
Typography tokens
FontFamilyFontSizeFontWeightLineHeightLetterSpacingTextAlign
Shadow tokens
ShadowInsetShadowDropShadowTextShadow
Motion tokens
TransitionDurationEasingAnimationToken
Motion is applied through Style methods:
transition_property(...)transition_duration(...)transition_ease(...)animate(...)
Style Builder
Style is the central composition object. You combine typed tokens and utilities, then map the resulting structure to a native backend.
#![allow(unused)]
fn main() {
use twill::prelude::*;
let card = Style::new()
.bg(Color::white())
.text_color(Color::gray(Scale::S900));
}
State And Responsive Layers
Twill keeps interactive and responsive styling at the Style layer instead of shipping UI components.
Stateful styling
#![allow(unused)]
fn main() {
use twill::prelude::*;
let style = Style::new()
.hover(|style| style.opacity(0.9))
.focus_visible(|style| style.ring(RingWidth::S2, Color::blue(Scale::S300)))
.selected(|style| style.bg(Color::blue(Scale::S500)))
.checked(|style| {
style.border(
BorderWidth::S1,
BorderStyle::Solid,
Color::green(Scale::S500),
)
})
.open(|style| style.shadow(Shadow::Lg))
.data_state("state=open", |style| style.text_color(Color::white()))
.aria_state("selected", |style| style.font_weight(FontWeight::Bold));
}
Supported built-in state layers:
hoverfocusfocus_visibleactivedisabledselectedcheckedopenclosed
Supported arbitrary hooks:
data_state("...")aria_state("...")
Responsive styling
#![allow(unused)]
fn main() {
use twill::prelude::*;
let style = Style::new()
.w(Spacing::S12)
.sm(|style| style.w(Spacing::S24))
.lg(|style| style.h(Spacing::S32));
let resolved = style.at_breakpoint(Breakpoint::Lg);
assert_eq!(resolved.width_value(), Some(Width::from(Spacing::S24)));
assert_eq!(resolved.height_value(), Some(Height::from(Spacing::S32)));
}
Available breakpoint helpers:
smmdlgxls2xl
You can also attach layers generically with responsive(Breakpoint::..., ...).
Semantic Tokens
Semantic tokens map role-based names (background, foreground, primary, etc.) to concrete colors in light and dark palettes.
#![allow(unused)]
fn main() {
use twill::prelude::*;
let theme = SemanticThemeVars::shadcn_neutral();
let light_bg = theme.resolve_light(SemanticColor::Background);
let dark_bg = theme.resolve_dark(SemanticColor::Background);
}
Backends
Backend adapters:
Backends Overview
Twill core is backend-agnostic.
Adapters convert tokens/styles into framework-specific types.
Supported adapters:
eguiicedslint
Enabling backend features
[dependencies]
twill = { version = "0.3", features = ["egui", "iced", "slint"] }
Backend modules
twill::backends::eguitwill::backends::icedtwill::backends::slint
Common pattern
- Build style/tokens in Twill.
- Convert via backend helper.
- Apply to widgets in that GUI framework.
egui
Enable feature:
twill = { version = "0.3", features = ["egui"] }
What you get
- color conversion helpers,
- style integration points for
eguiwidgets, - typed translation from Twill tokens into
eguiprimitives.
Use twill::backends::egui::ToEgui and the frame/color helpers to bridge Style into egui.
iced
Enable feature:
twill = { version = "0.3", features = ["iced"] }
What you get
- color/style mapping helpers for
iced, - compatibility with
icedapplication architecture, - typed translation from Twill values into
icedprimitives.
Use twill::backends::iced::ToIced together with the helper functions in this module to feed Style data into your own iced widgets, layouts, and themes.
slint
Enable feature:
twill = { version = "0.3", features = ["slint"] }
What you get
- direct
Color -> slint::Colorconversion helpers, - integration with Slint property-based styling,
- typed translation from Twill values into
slintprimitives.
Use twill::backends::slint::{ToSlint, SlintColors, SlintSpacing, SlintRadius} to bridge Twill tokens and style values into your own Slint components.
Examples
This section documents the checked-in examples/ targets for main and the 0.3.x API.
Legacy demos built around component APIs or CSS serialization belong to the 0.2.x release line and are intentionally not reproduced here.
Tokens Example
This example shows the lowest layer of the API: typed tokens for color, spacing, typography, and shadow.
- File:
examples/01_tokens.rs - Run:
& "$env:USERPROFILE\.cargo\bin\cargo.exe" run --example tokens
- Expected output: a small terminal dump of palette values, spacing in pixels, and representative typography/shadow tokens.
Why this exists:
use it when you need to inspect or explain Twill’s primitive values before composing full Style objects.
Style Builder Example
This example shows how a reusable base Style is composed with an additional layer through with(...).
- File:
examples/02_style_builder.rs - Run:
& "$env:USERPROFILE\.cargo\bin\cargo.exe" run --example style_builder
- Expected output: the merged padding, background, text color, radius, and shadow values.
Why this exists: it demonstrates that Twill’s core abstraction is style composition, not framework-specific components.
States Example
This example isolates state and attribute layers such as hover, focus_visible, disabled, data_state, and aria_state.
- File:
examples/03_states.rs - Run:
& "$env:USERPROFILE\.cargo\bin\cargo.exe" run --example states
- Expected output: a terminal summary of the nested state styles stored on the composed
Style.
Why this exists: stateful styling is one of the biggest differences between raw tokens and a useful UI style system, so it deserves its own single-purpose example.
Responsive Example
This example shows breakpoint layers and how they resolve through Responsive::at_breakpoint(...).
- File:
examples/04_responsive.rs - Run:
& "$env:USERPROFILE\.cargo\bin\cargo.exe" run --example responsive
- Expected output: one line per breakpoint showing the resolved width, height, padding, and shadow values.
Why this exists: responsive behavior should be inspectable without launching a GUI backend.
Semantic Theme Example
This example shows both the built-in SemanticThemeVars::shadcn_neutral() palette and a runtime-generated DynamicSemanticTheme.
- File:
examples/05_semantic_theme.rs - Run:
& "$env:USERPROFILE\.cargo\bin\cargo.exe" run --example semantic_theme
- Expected output: light and dark semantic resolutions for key aliases like
Background,Primary,Border, andRing.
Why this exists: semantic aliases are the bridge between raw tokens and app-level themes.
Backend Adapter Examples
These examples keep backend coverage focused on adapter deltas instead of repeating the full product story.
egui
- File:
examples/10_backend_egui.rs - Run:
& "$env:USERPROFILE\.cargo\bin\cargo.exe" run --example backend_egui --features egui
- Expected output: converted
eguiprimitives such asColor32,Frame, cursor, semantic colors, and shadow values.
iced
- File:
examples/11_backend_iced.rs - Run:
& "$env:USERPROFILE\.cargo\bin\cargo.exe" run --example backend_iced --features iced
- Expected output: converted
icedprimitives such asColor,Padding, cursor interaction, semantic colors, and shadows.
slint
- File:
examples/12_backend_slint.rs - Run:
& "$env:USERPROFILE\.cargo\bin\cargo.exe" run --example backend_slint --features slint
- Expected output: converted
slintprimitives such asslint::Color, lengths, cursor wrapper, semantic colors, and shadow tuples.
Why these exist: each adapter example answers “how do I bridge Twill values into this runtime?” without pretending Twill ships a full widget kit.
egui Showcase
This is the full egui showcase for main/0.3.x. It combines tokens, composed styles, state layers, responsive resolution, and semantic theme switching in one window.
- File:
examples/20_showcase_egui.rs - Run:
& "$env:USERPROFILE\.cargo\bin\cargo.exe" run --example showcase_egui --features egui
- Expected UI: a native
eguiwindow with token swatches, semantic theme labels, and composed cards built from shared Twill styles.
Why this exists:
it is the visual “map” of the system for egui, while the smaller examples stay optimized for one concept at a time.
iced Showcase
This is the full iced showcase for main/0.3.x. It mirrors the conceptual content of the egui showcase while keeping the backend-specific wiring in iced.
- File:
examples/21_showcase_iced.rs - Run:
& "$env:USERPROFILE\.cargo\bin\cargo.exe" run --example showcase_iced --features iced
- Expected UI: a native
icedwindow with token swatches, semantic theme sections, and composed cards rendered throughtwill::backends::iced.
Why this exists: it proves that the same Twill style story can be surfaced in a second backend without reintroducing old component abstractions into the crate.
Reference
Public API
twill keeps its public API centered on style primitives and backend adapters.
Core entry points
Styletwill::prelude::*MergeComputeValueResponsive
Tokens
Main token families:
Color,ColorFamily,Scale,ColorValueSemanticColor,SemanticThemeVars,DynamicSemanticThemeThemeVariantSpacing,Percentage,Container,BreakpointFontFamily,FontSize,FontWeight,LetterSpacing,LineHeightBorderRadius,BorderWidth,BorderStyle,OutlineStyle,RingWidthShadow,InsetShadow,DropShadow,TextShadowAnimationToken,TransitionDuration,TransitionProperty,Easing
Utilities
Common utility-style value types:
Padding,PaddingValue,PaddingVarMargin,MarginValue,MarginVarWidth,WidthVarHeight,HeightVarSizeConstraintsDisplay,Position,Overflow,ZIndexFlexContainer,FlexDirection,FlexGridContainer,GridTemplate
Recommended usage
#![allow(unused)]
fn main() {
use twill::prelude::*;
let card = Style::new()
.padding(Padding::all(Spacing::S4))
.bg(Color::slate(Scale::S50))
.rounded(BorderRadius::Lg)
.hover(|style| style.shadow(Shadow::Md))
.lg(|style| style.padding(Padding::all(Spacing::S6)));
assert!(!card.is_empty());
}
Backend surface
Feature-gated backend modules:
twill::eguitwill::icedtwill::slint
These modules convert Twill values into framework-specific primitives. Twill itself does not define components like Button or Card.
Backend-specific helper types:
twill::backends::ShadowColortwill::iced::TextDirectiontwill::slint::SlintCursor