English

トークンとテーマ

トークンは CSS のカスタムプロパティとして書かれる名前付きの値です。テーマはページ全体に、スタイルは 1 つの要素にトークンの値を設定します。

トークン

Token::new(name, default) は、Color、Length、Percentage、Time、Angle のどれかの型のトークンを作ります。var() はトークンを var(--name, default) の形で参照するので、値を何も設定しないページでは既定値が使われます。var() の結果はその型の値を受け取る場所ならどこにでも渡せます。

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 は Theme を受け取り、その値を :root に書きます。Theme::on(Media::Dark, ..) はメディアの条件の下で値を設定します。theme を後からもう一度呼ぶと、前の呼び出しの値に足されます。
  • Style::token は 1 つの要素とその内側の要素にトークンを設定し、既定値とページのテーマの値を置き換えます。
  • El::bind_token は、トークンの値をシグナルから作る文字列の式の値と常に同じにします。El::bind_style と同じく、値は検査しません。

下の色見本とボタンは同じトークンを読みます。このトークンの値は、bind_token がラジオグループのシグナルから設定します。

実行結果

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 {
            accent: "#2563eb".into(),
        },
    )
    .child(
        div()
            .css(stack(rem(1)))
            .bind_token(&accent, Palette::accent())
            .child(radio_group(
                "アクセント",
                Palette::accent(),
                [
                    (
                        "#2563eb".to_owned(),
                        "青",
                    ),
                    (
                        "#16a34a".to_owned(),
                        "緑",
                    ),
                    (
                        "#e11d48".to_owned(),
                        "ローズ",
                    ),
                ],
            ))
            .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("アクセントのボタン"),
                    ),
            ),
    )
}
同じ名前の 2 つのトークンは、ブラウザにとっては 1 つのカスタムプロパティです。名前が重複していないかは検査されません。hypersig_ui のコンポーネントは同じクレートの token モジュールにあるトークンを読みます。このモジュールはテーマで説明しています。