日本語

Tokens and themes

A token is a named value written as a CSS custom property. A theme sets its value for the whole page, and a style sets it for one element.

Tokens

Token::new(name, default) creates a token of a value type: Color, Length, Percentage, Time, or Angle. var() refers to the token as var(--name, default), so a page that sets no value uses the default. The result of var() can be passed anywhere a value of that type is accepted.

use hypersig::html::div;
use hypersig::style::value::{Color, px, rem};
use hypersig::style::{Media, Style, Theme, Token};
use hypersig::{Page, Signals};
use serde::{Deserialize, Serialize};

#[derive(Signals, Serialize, Deserialize)]
struct Board {
    hue: String,
}

let surface = Token::new("surface", Color::hex(0x00ff_ffff));
let gap = Token::new("gap", rem(1));

let html = Page::new("Board", Board { hue: "#2563eb".into() })
    // For the whole page, and under a media condition.
    .theme(
        Theme::new()
            .set(&gap, rem(1.5))
            .on(Media::Dark, Theme::new().set(&surface, Color::hex(0x0018_181b))),
    )
    .child(
        div()
            // Reading a token.
            .css(Style::new().background_color(surface.var()).padding(gap.var()))
            // For one element and those inside it.
            .css(Style::new().token(&gap, px(8)))
            // From a signal, unchecked like `bind_style`.
            .bind_token(&surface, Board::hue()),
    )
    .render();
assert!(html.contains("var(--surface, #ffffff)"));
  • Page::theme takes a Theme, whose values are written on :root. Theme::on(Media::Dark, ..) sets values under a media condition, and a later theme call adds to the earlier one.
  • Style::token sets a token on one element and those inside it, in place of the default and the page's theme.
  • El::bind_token keeps a token's value equal to the value of a string expression over the signals. As with El::bind_style, the value is not checked.

The swatches and the button below read the same token. bind_token sets that token's value from the radio group's signal.

Result

accent.rs
use hypersig::html::div;
use hypersig::style::pattern::{stack, wrap};
use hypersig::style::value::{Color, px, rem};
use hypersig::style::{Style, Token};
use hypersig::{Page, Signals};
use hypersig_ui::{ButtonSize, ButtonVariant, button, radio_group, token};
use serde::{Deserialize, Serialize};

#[derive(Signals, Serialize, Deserialize)]
pub struct Palette {
    accent: String,
}

pub fn page() -> Page<Palette> {
    let accent = Token::new("accent-demo", Color::hex(0x0025_63eb));
    let swatch = Style::new()
        .width(rem(3))
        .height(rem(3))
        .border_radius(px(8))
        .background_color(accent.var());
    Page::new(
        "Palette",
        Palette {
            accent: "#2563eb".into(),
        },
    )
    .child(
        div()
            .css(stack(rem(1)))
            .bind_token(&accent, Palette::accent())
            .child(radio_group(
                "Accent",
                Palette::accent(),
                [
                    (
                        "#2563eb".to_owned(),
                        "Blue",
                    ),
                    (
                        "#16a34a".to_owned(),
                        "Green",
                    ),
                    (
                        "#e11d48".to_owned(),
                        "Rose",
                    ),
                ],
            ))
            .child(
                div()
                    .css(wrap(rem(0.75)))
                    .child(div().css(swatch.clone()))
                    .child(div().css(swatch.border_radius(px(999))))
                    .child(
                        button(ButtonVariant::Default, ButtonSize::Default)
                            // The button reads `primary`, which takes the accent here.
                            .css(Style::new().token(&token::primary(), accent.var()))
                            .text("Accent button"),
                    ),
            ),
    )
}
Two tokens with the same name are one custom property to the browser. Duplicate names are not checked. The components of hypersig_ui read the tokens in the same crate's token module, which Theming covers.