4个版本 (2个重大更新)
0.2.0 | 2024年2月20日 |
---|---|
0.1.1 | 2023年12月7日 |
0.1.0 | 2023年12月7日 |
0.0.0 | 2023年11月12日 |
#161 in 过程宏
在 2 crates 中使用
26KB
421 代码行
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 @layer
的crate rcss-layers
。
用法
[dependencies]
rcss = "0.2"
RCSS不针对任何特定的Web框架,即使没有框架也可以使用,只需使用format!
,但为了使其更方便,此存储库还将包括与流行Web框架的集成。目前,只有一个集成crate:为leptos提供的rcss-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
crate。
[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
在首次构建后被添加,则 disable-styles
可以被 rcss-macro
忽略。可以通过使用 cargo clean -p rcss-macro
或 cargo clean -p rcss-macro --target-dir target/front
(在 cargo-leptos 的情况下)来强制重新构建 rcss-macro
。
已知问题
-
rcss-bundler
应仅从一个根 crate 中使用。如果你有多个使用rcss-bundler
的 crate,或者工作区在单个目标目录中共享多个根 crate 的构建工件,则它将导致配置冲突。例如,如果一个根 crate 设置了disable-styles=true
,而另一个没有设置,则rcss-bundler
将抛出一个警告,并为所有工作区/crates 使用disable-styles=false
。 -
目前,CSS 解析错误还没有完全集成到 rust 诊断消息中,所以如果你有无效的 CSS 语法,它只会抛出一个通用的错误,而不会突出显示错误的确切位置。
-
宏中的未引用文本有一些限制,例如它不能包含单引号
'
或未完成的括号(如{
或(
)。这是因为 Rust 向宏提供 TokenStream 的方式。 -
未引用的文本与
em
单位和一些以字母 e 开头且以数字结尾的十六进制数(如#0ed
)配合使用时效果不佳,因为 rust 将它们解析为指数数字字面量,并在 e 后期望一个数字而不是字母 "m"。
对于问题 3-4,可以使用插值作为解决方案 #{"3em"}
,其中包含引号文本。
宏实现细节
Rust 中编写函数宏有两种方式。
- 一种是处理来自 proc-macro 的
TokenStream
。这种方式节省了原始代码的链接,因此 IDE 和编译器可以显示与原始代码相关的错误,变量可以被解析等等。但是 Rust 中的TokenStream
是针对 Rust 语法专门化的,因此在其中支持 foreign syntax 较为困难。
查看 rstml::RawText 或 rstml::NodeName,这两个结构体都提供了一种解析 HTML 特定语法的方法,如破折号分隔的标识符或未引用的文本。
- 另一种方式是将宏输入处理为常规字符串。这种方式更加灵活,因为你可以使用任何你想要的解析器,但你会失去 `TokenStream` 的所有好处,并必须编写自己的解析器。
与需要以某种方式混合 Rust 中的反应式变量和模板的 HTML 模板不同,CSS 通常包含静态预定义内容,很少需要在运行时生成。因此,原始代码与 IDE 之间的链接不太重要。相反,大多数用户只想在其组件的实现附近编写其 CSS。
因此,我们不是在proc-macro::TokenStream
之上编写一个自定义解析器,而是尝试将宏调用转换为字符串,并使用方便的CSS预处理器。
依赖项
~12MB
~165K SLoC