Getting Started
Here's a basic counter component:
use hobo::{
prelude::*,
create as e,
signal::Mutable,
}
// <div class="s-f4d1763947b5e1ff">
// <div>Counter value is: 0</div>
// <button>increment</button>
// <button>decrement</button>
// </div>
fn counter() -> impl hobo::AsElement {
let counter_value = Mutable::new(0_i32);
e::div()
.class((
// enum-like properties can also be set like `css::Display::Flex`
css::display::flex,
css::width::px(400),
// #AA0000FF or #AA0000 or #A00 in normal css
css::background_color::rgb(0xAA_00_00),
css::align_items::center,
css::justify_content::space_between,
))
.child(e::div()
.text_signal(counter_value.signal_ref(|value|
format!("Counter value is: {value}"))
)
)
.component(counter_value)
.with(move |&counter_div| counter_div
.child(e::button()
.class(css::style!(
// .& is replaced with "current" class name, similar to SASS
// or styled-components
.& {
// shortcut for padding-left and padding-right
css::padding_horizontal::px(16),
css::background_color::rgba(css::colors::PALEVIOLETRED),
}
.&:hover {
css::background_color::rgba(css::colors::GREEN),
}
))
.text("increment")
.on_click(move |_| {
*counter_div.get_cmp::<Mutable<i32>>().lock_mut() += 1;
})
)
.add_child(e::button() // same as .child but non-chaining
// since this style is identical to the one above it -
// the class will be reused to avoid copypasting -
// the button generating code can be moved into
// a function or maybe just the code that defines the style
.class(css::style!(
.& {
css::padding_horizontal::px(16),
css::background_color::rgba(css::colors::PALEVIOLETRED),
}
.&:hover {
css::background_color::rgba(css::colors::GREEN),
}
))
.text("decrement")
.on_click(move |_| {
*counter_div.get_cmp::<Mutable<i32>>().lock_mut() -= 1;
})
)
)
}