Styling
Styles are Rust values too. A misspelled property or keyword, or a value of the wrong type, is a compile error.
Atomic styles
Style has one method per standard CSS property, generated from MDN's CSS data. El::css adds each declaration as one class named after the declaration, as in the atomic CSS of Panda CSS and Tailwind CSS. The page writes the rules its elements use into <head>, and a patch that contains new elements includes their rules.
use hypersig::html::button;
use hypersig::style::value::{Color, Cursor, rem};
use hypersig::style::{Media, PseudoClass, Style, Theme, Token};
use hypersig::{Page, Signals};
use serde::{Deserialize, Serialize};
#[derive(Signals, Serialize, Deserialize)]
struct Cart {
busy: bool,
}
let accent = Token::new("accent", Color::hex(0xb83f45));
let page = Page::new("Cart", Cart { busy: false })
.theme(Theme::new().on(Media::Dark, Theme::new().set(&accent, Color::hex(0xff6b6b))))
.child(
button()
.css(
Style::new()
.padding(rem(0.5))
.color(accent.var())
.cursor(Cursor::Pointer)
.on(PseudoClass::Hover, Style::new().color(Color::CRIMSON))
.on(Media::Md, Style::new().padding(rem(1))),
)
.css_when(Style::new().opacity(0.5), Cart::busy())
.text("Buy"),
);
assert!(page.render().contains("hover:color_crimson"));on(PseudoClass::Hover, ...)andon(Media::Md, ...)apply a style under a condition. Conditions cover pseudo-classes and pseudo-elements, media features, container sizes, and the state of a marked ancestor or sibling (groupandpeer).css_when(style, cond)adds a style while a signal condition holds, andcss_match(value, cases)picks one style per value of a signal.- A
Tokenis a CSS custom property with a typed value.Page::themesets tokens for the page, for example underMedia::Dark. Globalholds rules for elements by tag with a small reset,@font-face, and@keyframes.hypersig::style::patternhas layouts such asstackandgrid.
The badge below takes its color from css_match over an enum, and the green comes from a token with a dark value.
Result
use hypersig::html::{div, span};
use hypersig::style::pattern::{hstack, stack};
use hypersig::style::value::{Color, FontWeight, px, rem};
use hypersig::style::{Media, Style, Theme, Token};
use hypersig::{Page, Signals, if_else};
use hypersig_ui::{ButtonSize, ButtonVariant, button};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq)]
pub enum Status {
Idle,
Busy,
Done,
}
#[derive(Signals, Serialize, Deserialize)]
pub struct Build {
status: Status,
}
pub fn page() -> Page<Build> {
let status = Build::status();
let done = Token::new("done", Color::hex(0x0015_803d));
let badge = span()
.css(
Style::new()
.padding_block(rem(0.125))
.padding_inline(rem(0.625))
.border_radius(px(999))
.font_size(rem(0.75))
.font_weight(FontWeight::Number(600.0))
.color(Color::WHITE),
)
.css_match(
status,
[
(
Status::Idle,
Style::new().background_color(Color::hex(0x0052_525b)),
),
(
Status::Busy,
Style::new().background_color(Color::hex(0x00b4_5309)),
),
(Status::Done, Style::new().background_color(done.var())),
],
)
.bind_text(if_else(
status.equals(&Status::Idle),
"Idle",
if_else(
status.equals(&Status::Busy),
"Building",
"Done",
),
));
let set = |value: Status, label: &str| {
button(ButtonVariant::Outline, ButtonSize::Sm)
.on_click(status.set(&value))
.text(label)
};
Page::new(
"Build",
Build {
status: Status::Idle,
},
)
.theme(Theme::new().on(
Media::Dark,
Theme::new().set(&done, Color::hex(0x0016_a34a)),
))
.child(
div().css(stack(rem(1))).child(badge).child(
div()
.css(hstack(rem(0.5)))
.child(set(
Status::Idle,
"Idle",
))
.child(set(
Status::Busy,
"Busy",
))
.child(set(
Status::Done,
"Done",
)),
),
)
}Values the types do not check
A value with several parts, such as margin: 0 auto, a shadow, or a calc(), goes through raw(...), which is passed to the browser unchecked, and so is a value outside a property's range, such as a negative width. A page that loads an existing stylesheet keeps it: Stylesheet::class("name") checks at compile time that the stylesheet has a selector for the class.
The style example in the repository puts each feature on one page, and the ui example shows the components of hypersig_ui, which are built with these features.