Styling

Day supports fonts, colors, spacing, backgrounds, and canvas drawing. Native controls retain the platform’s appearance, including focus rings, slider tracks, and selection highlights. The sections below describe which properties an app can set and how each toolkit interprets them.

Text

Fonts are chosen by role. Pick a role instead of a point size, and each platform maps it to its text-style system:

label(tr("title")).font(Font::Title)
label(tr("caption")).font(Font::Caption).color(Color::rgb(0.5, 0.5, 0.5))
label("Total").font(Font::Body).weight(FontWeight::Semibold)
label("legalese").italic()

The semantic roles (LargeTitle, Title, Title2, Title3, Headline, Subheadline, Body, Callout, Footnote, Caption, Caption2) resolve to the platform’s typography scale, so text matches the platform’s native controls. Use Font::System(18.0) when you need an exact size. Use Font::custom(res::fonts::family, 18.0) for a font bundled in the project’s resource/fonts/ directory (resources guide).

Color, backgrounds, shape

column((avatar, name, bio))
    .padding(16.0)
    .background(Color::hex(0x1E293B))
    .corner_radius(12.0)

Color is a plain sRGB value (Color::rgb, Color::rgba, Color::hex(0xRRGGBB), plus BLACK, WHITE, CLEAR). .background() accepts a static color or a reactive one (a closure or signal), so appearance can follow state:

label(move || status.get().to_string())
    .background(move || if error.get() { RED_TINT } else { Color::CLEAR })

There is no theme:: token module, because the default appearance is already native: text, controls, separators, and window grounds take the platform’s dynamic colors inside each backend (NSColor.labelColor, Material surface attributes, QPalette roles), so dark/light tracking needs no app-side tokens. The semantic roles that must cross the spec do so as typed values: SurfaceRole for grouped-card surfaces, Font for typography. Colors you specify are applied as given: a hardcoded Color::hex(0xFFFFFF) background is white in both modes, so an app that wants dark-mode-aware custom colors carries its own palette and switches it itself. Avoid custom colors on large surfaces where you can, since the platform defaults follow appearance changes. For screenshots and CI, DAY_THEME=light|dark forces the appearance on every backend.

Reusable style: the Modifier trait

There’s no stylesheet language. Reuse is ordinary Rust (a function or a Modifier, which is anything that maps a Piece to a decorated Piece):

pub struct Card;

impl Modifier for Card {
    fn apply(self, content: AnyPiece) -> AnyPiece {
        content.padding(16.0).background(CARD_BG).corner_radius(12.0)
    }
}

column((label("Plan"), label("Pro"))).modifier(Card)

Any FnOnce(AnyPiece) -> AnyPiece is a Modifier too, so one-off wrappers don’t need a named type. For app-wide theming, combine this with environment context:

with_environment(Palette::dark(), || {
    // Anywhere below: let palette = environment::<Palette>().unwrap();
    home_page()
})

Per-platform divergence

Sometimes the right style differs per platform: denser padding on desktop, larger touch targets on mobile. Today you branch on the compiled toolkit, which is a process constant and costs nothing at runtime:

let pad = if cfg!(feature = "uikit") || cfg!(feature = "mdc") { 16.0 } else { 10.0 };
content.padding(pad)

Piece-specific style hooks exist where a control has real variants (button(...).style(...) takes a ButtonStyle, nav(...).style(NavStyle::Sidebar) picks sidebar vs. tab presentation), and these map to native variants, not custom drawing.

What you can’t restyle

There is no portable API to recolor a slider track, restyle a scrollbar, or reshape a checkbox. If a property can’t be honored by a toolkit, Day logs it once in debug rather than silently approximating it with custom drawing. Because Day doesn’t redraw controls, they update with the OS like any native control.

When a specific platform offers the knob you want (an AppKit bezel style, XAML tick marks), tweaks reach the native widget and set it, per toolkit. When you need fully custom visuals, draw your own leaf with canvas or a composite piece and keep native behavior around it.

If your product requires a heavily branded design system on every pixel (custom controls everywhere, identical on all platforms), a renderer-based framework is the better fit. Day is for apps that want to look like they belong on each platform. That choice is the subject of Why Day.


Next: the Guides cover the everyday tasks (navigation, localization, accessibility, testing), or see the API tour for examples of common UI components and patterns.