Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

  • BorderWidth
  • BorderStyle
  • BorderRadius

Typography tokens

  • FontFamily
  • FontSize
  • FontWeight
  • LineHeight
  • LetterSpacing
  • TextAlign

Shadow tokens

  • Shadow
  • InsetShadow
  • DropShadow
  • TextShadow

Motion tokens

  • TransitionDuration
  • Easing
  • AnimationToken

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:

  • hover
  • focus
  • focus_visible
  • active
  • disabled
  • selected
  • checked
  • open
  • closed

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:

  • sm
  • md
  • lg
  • xl
  • s2xl

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:

  • egui
  • iced
  • slint

Enabling backend features

[dependencies]
twill = { version = "0.3", features = ["egui", "iced", "slint"] }

Backend modules

  • twill::backends::egui
  • twill::backends::iced
  • twill::backends::slint

Common pattern

  1. Build style/tokens in Twill.
  2. Convert via backend helper.
  3. 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 egui widgets,
  • typed translation from Twill tokens into egui primitives.

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 iced application architecture,
  • typed translation from Twill values into iced primitives.

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::Color conversion helpers,
  • integration with Slint property-based styling,
  • typed translation from Twill values into slint primitives.

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, and Ring.

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 egui primitives such as Color32, 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 iced primitives such as Color, 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 slint primitives such as slint::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 egui window 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 iced window with token swatches, semantic theme sections, and composed cards rendered through twill::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

  • Style
  • twill::prelude::*
  • Merge
  • ComputeValue
  • Responsive

Tokens

Main token families:

  • Color, ColorFamily, Scale, ColorValue
  • SemanticColor, SemanticThemeVars, DynamicSemanticTheme
  • ThemeVariant
  • Spacing, Percentage, Container, Breakpoint
  • FontFamily, FontSize, FontWeight, LetterSpacing, LineHeight
  • BorderRadius, BorderWidth, BorderStyle, OutlineStyle, RingWidth
  • Shadow, InsetShadow, DropShadow, TextShadow
  • AnimationToken, TransitionDuration, TransitionProperty, Easing

Utilities

Common utility-style value types:

  • Padding, PaddingValue, PaddingVar
  • Margin, MarginValue, MarginVar
  • Width, WidthVar
  • Height, HeightVar
  • SizeConstraints
  • Display, Position, Overflow, ZIndex
  • FlexContainer, FlexDirection, Flex
  • GridContainer, GridTemplate
#![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::egui
  • twill::iced
  • twill::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::ShadowColor
  • twill::iced::TextDirection
  • twill::slint::SlintCursor