#leptos #style #scoped #css #styling

styled

为您的 Leptos 组件提供局部作用域样式

38 个版本

0.2.0 2023年12月20日
0.1.41 2023年8月13日
0.1.36 2023年3月4日
0.1.21 2023年2月28日

2205网页编程

Download history 10/week @ 2024-04-14 7/week @ 2024-04-21 1/week @ 2024-04-28 8/week @ 2024-05-12 16/week @ 2024-05-19 17/week @ 2024-05-26 20/week @ 2024-06-02 6/week @ 2024-06-09 1/week @ 2024-06-16 1/week @ 2024-06-23 1/week @ 2024-06-30 265/week @ 2024-07-28

265 每月下载

APL-1.0 许可证

14KB

Styled:Leptos 组件的简易样式化

如果您正在寻找一种轻松将局部作用域样式应用于您的 Leptos 组件的方法,那么 Styled 是您需要的 Leptos 宏。使用 Styled,您可以应用高级选择器,如 buttondiv,到特定的组件上,保持您的标记清洁和组织。

安装

在项目根目录中使用 cargo add

cargo add styled stylist

使用方法

首先创建一个基本的 Leptos 组件。这将是本指南的基础。

#[component]
pub fn MyComponent(cx: Scope) -> impl IntoView{
  view! {
    cx,
    <div>"hello"</div>
  }
}

接下来,导入由一个名为 Stylist 的出色 crate 提供的 style 宏,以创建您的样式。只需将此添加到文件顶部。

use styled::style;

然后,您可以使用 style 宏创建一个包含您的样式的 Result。让我们修改我们的组件

#[component]
pub fn MyComponent(cx: Scope) -> impl IntoView{
  
  let styles = style!(
    div {
      background-color: red;
      color: white;
    }
  );

  view! {
    cx,
    <div>"hello"</div>
  }
}

现在,让我们使用我们的 styled::view! 宏来应用这些样式!

#[component]
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>
    }
}

现在我们可以定义另一个组件,它也使用 div CSS 选择器,但它的样式只会应用于其封装的 styled::view! 宏内的元素。

#[component]
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>
    }
}

更长的示例

// /src/components/button.rs

use crate::theme::get_theme;
use leptos::*;
use styled::style;

#[derive(PartialEq)]
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 get_colors(variant: &Variant) -> ButtonColors {
    let theme = get_theme().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(),
        },
    }
}

#[component]
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<styled::Style> {
    let colors = get_colors(variant);

    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;
            }

    )
}

// /src/theme/mod.rs
use csscolorparser::Color;

pub fn get_theme() -> Result<Theme, csscolorparser::ParseColorError> {
    let theme = Theme {
        teal: Colors {
            main: Color::from_html("#6FDDDB")?,
            darker: Color::from_html("#2BB4B2")?,
            lighter: Color::from_html("#7EE1DF")?,
            lightest: Color::from_html("#B2EDEC")?,
        },
        pink: Colors {
            main: Color::from_html("#E93EF5")?,
            darker: Color::from_html("#C70BD4")?,
            lighter: Color::from_html("#F5A4FA")?,
            lightest: Color::from_html("#FCE1FD")?,
        },
        green: Colors {
            main: Color::from_html("#54D072")?,
            darker: Color::from_html("#30AF4F")?,
            lighter: Color::from_html("#82DD98")?,
            lightest: Color::from_html("#B4EAC1")?,
        },
        purple: Colors {
            main: Color::from_html("#8C18FB")?,
            darker: Color::from_html("#7204DB")?,
            lighter: Color::from_html("#B162FC")?,
            lightest: Color::from_html("#D0A1FD")?,
        },
        yellow: Colors {
            main: Color::from_html("#E1E862")?,
            darker: Color::from_html("#BAC31D")?,
            lighter: Color::from_html("#EFF3AC")?,
            lightest: Color::from_html("#FAFBE3")?,
        },
        gray: Colors {
            main: Color::from_html("#4a4a4a")?,
            darker: Color::from_html("#3d3d3d")?,
            lighter: Color::from_html("#939393")?,
            lightest: Color::from_html("#c4c4c4")?,
        },
        red: Color::from_html("#FF5854")?,
        black: Color::from_html("#000000")?,
        white: Color::from_html("#FFFFFF")?,
        transparent: Color::from_html("transparent")?,
    };

    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.to_hex_string()
    }
    pub fn darker(&self) -> String {
        self.darker.to_hex_string()
    }
    pub fn lighter(&self) -> String {
        self.lighter.to_hex_string()
    }
    pub fn lightest(&self) -> String {
        self.lightest.to_hex_string()
    }
}

impl Theme {
    pub fn red(&self) -> String {
        self.red.to_hex_string()
    }
    pub fn black(&self) -> String {
        self.black.to_hex_string()
    }
    pub fn white(&self) -> String {
        self.white.to_hex_string()
    }
    pub fn transparent(&self) -> String {
        self.transparent.to_hex_string()
    }
}


// /src/app.rs

#[component]
fn HomePage(cx: Scope) -> impl IntoView {
    view! { cx,
            <Button variant={button::Variant::PRIMARY}/>
            <Button variant={button::Variant::SECONDARY}/>
            <Button variant={button::Variant::ALERT}/>
    }
}

依赖项

~19–32MB
~524K SLoC