#css #css-class #css-parser #rsx #parse #html #macro

rcss-bundler

是 Rust CSS 内嵌库的一部分,允许将所有样式收集并保存到一个文件中

1 个不稳定版本

0.2.0 2024年2月20日

#1631网页编程


4 个包中使用(通过 rcss-macro

MIT/Apache

51KB
977

RCSS - Rust 应用中的可重用 CSS。

RCSS 允许您直接在 Rust 代码中编写样式,大多数情况下无需引号。

let css = css!{
    .container {
        background-color: black;
    }
    #id {
        color: red;
    }
    button {
        border: 1px solid black;
    }
};
assert!(css.container.starts_with("container"));

RCSS 使用 LightningCSS(浏览器级别的 CSS 预处理器)来解析和生成 CSS 代码。它可以捆绑所有 CSS 到静态文件中,或者生成内联 CSS 内容。

它使用类似于 JS 世界的 css_modules 的方法以类型安全的方式公开 CSS 类。对于不使用类名的选择器,它使用作用域样式方法(添加一个具有唯一名称的自定义类)。

使用 RCSS 定义的样式可以在其他组件中扩展和重用。

// Declare a public module name foo;
css!{
    @rcss(pub struct Foo);

    .container {
        color: green;
        background-color: black;
    }
};

// Component define what type of css it needs.
fn hello_world<C>(css: C) -> String
C: rcss::ScopeChain<Root = Foo>
{
    // use ScopeChain to include style chain. Checkout rcss-layers for details.

    format!(r#"<div class="{}">Hello</div>"#, css.container)
}
...

// Extend style from module foo, typecheck that no new classes, ids or types are added.
let css = css!{
    @rcss(extend ::path::to::Foo);

    .container {
        background-color: red;
    }
};

let html = hello_world(css);

为了更好地控制级联并避免冲突,RCSS 提供了可以保存扩展样式到 CSS rcss-layers 的包,该包可以保存到 CSS @layer

使用方法

[dependencies]
rcss = "0.2"

RCSS 不专注于任何特定的网页框架,即使没有框架也可以使用,只需使用 format!,但为了使其更方便,此存储库还将包括与流行网页框架的集成。目前,只有一个集成包:用于 leptosrcss-leptos

与 leptos 一起使用

要使用 rcss 与 leptos 一起使用,请将以下内容添加到您的 Cargo.toml

[dependencies]
rcss = "0.2"
rcss-leptos = "0.2"

然后在组件中,您可以使用 css! 宏来定义样式。

use rcss::css;
use leptos::*;

#[component]
fn hello_world() -> Node {
    let css = css!{
        .container {
            background-color: black;
            color: white;
        }
    };
    rcss_leptos::register_style(css.clone());

    view! {
        <div class=css.container>Hello</div>
    }
}

更多示例可以在 examples/leptos 目录中找到。

捆绑 CSS

RCSS 可以将所有 CSS 捆绑到一个静态文件中。要完成此操作,可以在 build.rs 中使用 rcss-bundler 包。

[build-dependencies]
rcss-bundler = "0.2"
fn main() {
    rcss_bundler::bundle_build_rs();
}

默认情况下,捆绑器会将 CSS 保存到 $OUT_DIR/styles.css。但是,这可以通过 cargo 元数据进行自定义。

[package.metadata.rcss]
output-path = "style/counters.css" # Path to save styles
disable-styles = false # If set to true will force `rcss-macro` to remove style strings from macro output.

注意:如果 rcss-bundler 在第一次构建后添加,则 rcss-macro 可忽略 disable-styles。可以使用 cargo clean -p rcss-macrocargo clean -p rcss-macro --target-dir target/front(在 cargo-leptos 的情况下)来强制 cargo 重新构建 rcss-macro

已知问题

  1. rcss-bundler 应仅从单个根 crate 使用。如果您有多个使用 rcss-bundler 的 crate,或者工作区在单个目标目录中共享多个根 crate 的构建工件,则它将导致配置冲突。例如,如果一个根 crate 设置了 disable-styles=true 而另一个没有,则 rcss-bundler 将抛出警告并使用 disable-styles=false 对所有工作区/crate。

  2. 目前,CSS 解析错误尚未完全集成到 Rust 诊断消息中,因此如果 CSS 语法无效,它将仅抛出通用错误,而不会突出显示错误的准确位置。

  3. 宏中的未引用文本有一些限制,例如不能包含单引号 ' 或未完成的括号(如 {()。这是因为 Rust 为宏提供 TokenStream 的方式。

  4. 未引用文本与 em 单位和一些以数字开头并以字母 e 开头的十六进制数字(如 #0ed)不兼容,因为 Rust 将它们解析为指数数文字面量,并期望在 e 后面是数字而不是字母 "m"。

对于问题 3-4,可以使用插值作为解决方案 #{"3em"},其中包含带引号的文本。

宏实现细节

Rust 中编写函数宏有两种方式。

  • 一种是从 proc-macro 处理 TokenStream。这种方式保留了原始代码的链接,因此 IDE 和编译器可以显示与原始代码相关的错误,变量可以被解析,等等。但是 Rust 中的 TokenStream 是针对 Rust 语法专门化的,因此在其中支持 foreign syntax 更困难。

查看 rstml::RawTextrstml::NodeName,这两个结构体都提供了一种解析特定于 HTML 语法的语法的方法,如破折号分隔的标识符或未引用的文本。

  • 另一种方式是将宏输入作为常规字符串处理。这种方式更灵活,因为您可以使用任何所需的解析器,但您将失去 `TokenStream` 的所有好处,并必须编写自己的解析器。

与需要以某种方式混合 Rust 中的反应式变量和模板的 HTML 模板不同 - CSS 通常包含静态预定义内容,很少需要在运行时生成。因此,原始代码与 IDE 之间的链接不太重要。相反,大多数用户只是希望在组件的实现附近编写 CSS。

因此,该库不是在 proc-macro::TokenStream 的基础上编写自定义解析器,而是尝试将宏调用转换为字符串并使用方便的 CSS 预处理器。

依赖关系

~0.5-3MB
~47K SLoC