If you're looking for an easy way to apply scoped styles to your Leptos
components, Styled
is the Leptos macro you need. With Styled
, you can apply high-level selectors like button
or div
to specific components, keeping your markup clean and organized.
Use cargo add
in your project root
bash
cargo add styled stylist
First create a basic Leptos
component. This will serve as the foundation for this little guide.
```rust
pub fn MyComponent(cx: Scope) -> impl IntoView{ view! { cx,
Next, import the style
macro, powered by an awesome crate called Stylist
, to create your styles.
Just add this to the top of your file.
rust
use styled::style;
You can then use the style
macro to create a Result
containing your styles. Let's modify our component:
```rust
pub fn MyComponent(cx: Scope) -> impl IntoView{
let styles = style!( div { background-color: red; color: white; } );
view! { cx,
Now, let's apply those styles with our styled::view!
macro!
```rust
pub fn MyComponent(cx: Scope) -> impl IntoView {
let styles = style!(
div {
background-color: red;
color: white;
}
);
styled::view! {
cx,
styles,
<div>"This text should be red with white text."</div>
}
} ```
Now we can define another component that also uses the div
CSS selector but it's styles will only apply to the elements inside of it's enclosing styled::view!
macro.
```rust
pub fn AnotherComponent(cx: Scope) -> impl IntoView {
// note were using a plain div selector and it wont clash with MyComponent's div style!
let styles = style!(
div {
background-color: blue;
color: gray;
}
);
styled::view! {
cx,
styles,
<div>"This text should be blue with gray text."</div>
}
} ```
```rust // /src/components/button.rs
use crate::theme::get_theme; use leptos::*; use styled::style;
pub enum Variant { PRIMARY, SECONDARY, ALERT, DISABLED, }
impl Variant { pub fn is(&self, variant: &Variant) -> bool { self == variant } }
struct ButtonColors { text: String, background: String, border: String, }
fn getcolors(variant: &Variant) -> ButtonColors { let theme = gettheme().unwrap(); match variant { Variant::PRIMARY => ButtonColors { text: theme.white(), background: theme.black(), border: theme.transparent(), }, Variant::SECONDARY => ButtonColors { text: theme.black(), background: theme.white(), border: theme.gray.lightest(), }, Variant::ALERT => ButtonColors { text: theme.white(), background: theme.red(), border: theme.transparent(), }, Variant::DISABLED => ButtonColors { text: theme.white(), background: theme.red(), border: theme.transparent(), }, } }
pub fn Button(cx: Scope, variant: Variant) -> impl IntoView { let disabled = variant.is(&Variant::DISABLED);
let styles = styles(&variant);
styled::view! {
cx,
styles,
<button disabled=disabled>"Button"</button>
}
}
fn styles<'a>(variant: &Variant) -> styled::Result
style!(
button {
color: ${colors.text};
background-color: ${colors.background};
border: 1px solid ${colors.border};
outline: none;
height: 48px;
min-width: 154px;
font-size: 14px;
font-weight: 700;
text-align: center;
box-shadow: rgba(0, 0, 0, 0.05) 0px 1px 2px 0px;
position: relative;
box-sizing: border-box;
vertical-align: middle;
text-align: center;
text-overflow: ellipsis;
text-transform: uppercase;
overflow: hidden;
cursor: pointer;
transition: box-shadow 0.2s;
margin: 10px;
}
& button:active {
transform: scale(0.99);
}
& button::-moz-focus-inner {
border: none;
}
& button::before {
content: "";
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgb(255, 255, 255);
opacity: 0;
transition: opacity 0.2s;
}
& button::after {
content: "";
position: absolute;
left: 50%;
top: 50%;
border-radius: 50%;
padding: 50%;
background-color: ${colors.text};
opacity: 0;
transform: translate(-50%, -50%) scale(1);
transition: opacity 1s, transform 0.5s;
}
& button:hover,
& button:focus {
box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12);
}
& button:hover::before {
opacity: 0.08;
}
& button:hover:focus::before {
opacity: 0.3;
}
& button:active {
box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);
}
& button:active::after {
opacity: 0.32;
transform: translate(-50%, -50%) scale(0);
transition: transform 0s;
}
& button:disabled {
color: rgba(0, 0, 0, 0.28);
background-color: rgba(0, 0, 0, 0.12);
box-shadow: none;
cursor: initial;
}
& button:disabled::before {
opacity: 0;
}
& button:disabled::after {
opacity: 0;
}
)
}
```
```rust // /src/theme/mod.rs use csscolorparser::Color;
pub fn gettheme() -> Result
Ok(theme)
}
pub struct Theme { pub teal: Colors, pub pink: Colors, pub green: Colors, pub purple: Colors, pub yellow: Colors, pub gray: Colors, pub red: Color, pub black: Color, pub white: Color, pub transparent: Color, }
pub struct Colors { pub main: Color, pub darker: Color, pub lighter: Color, pub lightest: Color, }
impl Colors { pub fn main(&self) -> String { self.main.tohexstring() } pub fn darker(&self) -> String { self.darker.tohexstring() } pub fn lighter(&self) -> String { self.lighter.tohexstring() } pub fn lightest(&self) -> String { self.lightest.tohexstring() } }
impl Theme { pub fn red(&self) -> String { self.red.tohexstring() } pub fn black(&self) -> String { self.black.tohexstring() } pub fn white(&self) -> String { self.white.tohexstring() } pub fn transparent(&self) -> String { self.transparent.tohexstring() } }
```
```rust // /src/app.rs
fn HomePage(cx: Scope) -> impl IntoView { view! { cx,