Using Seed Style we can apply styles in many different ways. This page demonstrates some applications that include the use of pseudo classes, media queries, and variant styling.
This is a unstyled button, it uses seed hooks to store state with use_state(|| 0)
.
The counter is incremented with counter.on_click(|v| *v += 1)
which creates an Ev::OnClick
EventHandler.
All the other buttons in this page use this simple pattern.
let counter = use_state(|| 0);
button![
"Clicked (", counter, ") times",
counter.on_click(|v| *v += 1 )
]
This is a basic styled button, the button is styled with the following:
s().padding_left(px(24))
.padding_right(px(24))
.padding_top(px(8))
.padding_bottom("8px")
.border_radius(px(4))
.margin("2px")
.background_color("teal")
.color("white")
.outline_style(CssOutlineStyle::None)
.display(CssDisplay::InlineBlock)
Notice how the optional value typing works : px(2)
is an ExactLength
it can be used
whenever a pixel measurement is needed. Other units include pc()
rem()
and em()
.
Strings can be used for properties (e.g. .padding_bottom("8px")
). You can also use the specific Css value type directly.
These types are enums, for instance demonstrated here with a CssDisplay::InlineBlock
passed to the .display()
method
or the CssOutlineStyle::None
passed to the outline_style()
method.
The reason for preferring typed arguments is that typos are detected at compile time. For instance writing
this page I accidentally typed Outine
instead of Outline
. If this was pure css the error would only be
spotted if I had thoroughly checked the resultant page and css for a property that is often easy to miss.
See css_values.rs
for a complete list of properties and values available.
This is a button styled with css directly using the .raw()
method. We can include any css that can be used inside a style block. In this case we have used:
s().raw(
r#"
font-family: verdana;
font-size: 20px;
display: inline-block;
text-align: center;
border-radius: 4px;
background-color: #DB3080;
color: white;
padding: 16px;
margin: 6px;
"#
)
Whilst this is convenient please note that this css is not type checked in anyway, it is usually better to use type checked css so that any issues can be prevented at compile time.
There are convenience methods available in order to make defining styles more efficient.
For instance pl()
for padding-left()
. Horizontal/ vertical padding and margins can be set with .mx()
, py()
etc.
Furthermore all properties with single variant values can be set by appending the value name to the property method.
For instance, .display_inline_block()
will in effect call .display(CssDisplay::InlineBlock)
which generates the css display: inline-block;
.
Similarly, .flex_direction_column()
will call .flex_direction(CssFlexDirection::Column)
which generates the css flex-direction: column;
.
See css_values.rs
for more information.
Here is an example of some shortcuts:
s().radius(px(2))
.bg_color(hsl(40,70,30))
.color("white")
.px(px(16))
.py(px(12))
.outline_none()
.m(px(6));
Unlike direct inline styles, you can correctly use pseudo-selectors
to style things like :hover
status. Simply use the pseudo selector name as a method .hover()
method on a style. Please note that this
sets all properties defined in that style object to be affected by :hover
.
Typically you would use two styles, one normal style and one a hover:
s().bg_color(hsl(200,40,30))
.color("white")
.outline_none()
.px(px(16))
.py(px(12))
.m(px(6)),
s().hover().bg_color("green").color("black"),
s().active().bg_color("darkgreen")
Media queries can be used in a number of ways. The most basic way is by using the media()
method on a style. This will
result in that style being nested within a media query block.
For instance :
s().media("@media only screen and (max-width: 700px)")
.bg_color("green")
will ensure that the background colour will be green if the screen is 700px or less wide.
There are a number of other (and better) ways to define media queries that involve themes and breakpoints. This will be discussed later.
Due to the flexibility of writing rust in view code variants of styles can be trivially implemented.
For instance we can have a base button style:
let base_button_style = s()
.bg_color(seed_colors::Gray::No6)
.color(seed_colors::Base::White)
.px(px(16))
.py(px(12))
.radius(px(6))
.m(px(12));
as well as danger
and ok
variants:
let danger_style = base_button_style.clone().bg_color(seed_colors::Red::No7);
let ok_style = base_button_style.clone().bg_color(seed_colors::Green::No5);
We can then conditionally use this style directly inside the button macro:
button![
if CONDITION { danger_style } else { ok_style },
...
]
Because a style is just an object it can be passed in as the argument to a view function. e.g:
user_styled_btn(
s().bg_color(seed_colors::Indigo::No5).font_size(px(24))
),
For instance this function defines a button with a user provided style, simply placing the user defined style after the default style will ensure that default styles get overwritten by any clashing user style.
This is in contrast to CSS class selectors which annoyingly have no way to specify CSS precedence other than what order the CSS is is defined in the CSS file.
fn user_styled_btn(user_style: seed_style::Style) -> Node<Msg> {
...
...
let button_style = s()
.bg_color(seed_colors::Red::No6)
.color(seed_colors::Base::White)
.px(px(10))
.py(px(8))
.m(px(4));
...
...
button![
button_style, user_style,
...
]
}