# 🧙🏻 SABRY - Syntactically Awesome, But RustY
**Y**et another **R**usty **B**oilerplate-free **A**gnostic **S**tyling crate, which brings your SASS/SCSS style into Rust. Written by a fox this time.
\* sabry isn't "syntactically awesome", it refers to SASS abbr expansion.
> **Project status** - early, good enough for my team's non-critical "for-fun" pre-production, on demand. WIP features have no ETA. I'm pretty happy with ergonomics and taste of the crate, and I'll do my best to keep DX the way it is between minor versions - but there's no guarantee on backwards-compatibility and no refunds if something breaks.
>
> "master" branch is what's currently on crates.io
>
> each version ever been on crates.io (except the latest release) is available in "r_x.x.x" branch
>
> the latest-trashy state is in the "window" branch
[](https://crates.io/crates/sabry)
[](https://docs.rs/sabry)
[](https://github.com/yiffyrusdev/sabry/blob/master/LICENSE)
At first, I'll show how this crate "tastes". With SABRY, its in your power to:
|
Write arbitrary SASS/SCSS code and ship it *as a crate* however you please: with modules, with feature flags, etc. Say "bye" to manual copying, cli-tools and consts.
```rust,ignore
sassy!(tokens {@mixin colored{color: red;}});
```
|
📦
|
|
Code your SASS in separate files to get proper syntax highlighting, or do sass-in-the-rust to keep things in one place
```rust,ignore
styly!(component {.btn {color: green;}});
styly!(extras "src/components/extras.scss");
```
|
:3
|
|
Depend on styles with cargo, at build time, which brings all the rusty sweeties in: versions, updates, cratesio, local registries, workspaces, etc.
```toml
[build-dependencies]
tgk_brandstyle = {version = "0.0.1", features = ["utils"]}
```
|
🦀
|
|
`@use` your style-crates in sass code *naturally*
```rust,ignore
styly!(breadbadge {
@use 'tokens';
.scope {@include tokens.badge(primary);}
})
```
|
🪄
|
|
Keep things as private and modular as you wish
```rust,ignore
styly!(pub cats "src/sections/cats/style.scss");
styly!(dogs {#howl {border: none;}})
/// something like render function
{
}
```
|
^_^
|
|
Compile all the sweet SASS/SCSS into the optimized CSS bundle, ship it in CSS shunks or even include the compiled style into the binary
```rust,ignore
styly!(cssbundle {.c1 {color: white;}})
styly!(const binary {.c2 {color: black;}})
```
|
🏗️
|
Sabry will gladly:
- scope and hash your styles, so they won't conflict, with amazing [raffia](https://crates.io/crates/raffia) parser
- compile SASS/SCSS into CSS with [grass](https://crates.io/crates/grass)
- optimize produced CSS with [lightningcss](https://crates.io/crates/lightningcss)
- prepare ready CSS bundle and put it wherever you wish
- include CSS into the build artifact - if you really want it
Also, just about everything is pub-available in this crate - Sabry is ready for experiments.
## Usage
Feel free to check out examples:
|[crate of styles](./examples//define-styles/)|[style usage](./examples/use-styles/)|[leptos-axum with sabry](./examples/leptos-axum)|
|-|-|-|
### Create a crate full of arbitrary SASS
The only need is the dependency
```toml
#Cargo.toml
[dependencies]
sabry = {version = "0.0.1"}
```
And a proc-macro
```rust,ignore
// lib.rs
use sabry::sassy;
sassy!(mixins "assets/mixins.scss");
sassy!(styles {
$primary-color: black;
@mixin colored($col: primary) {
@if $col == primary {
color: $primary-color;
} @else {
color: $col;
}
}
});
```
Now you can build and see two ready for export macros: `mixins!` and `styles!`.
These are usefull on their own, as invocation of `mixins!()` or `styles!()` - both shall give you the code literal.
However there's more sweet use case for them, which is covered below.
### Write scoped styles
Depend on sabry
```toml
# Cargo.toml
[dependencies]
sabry = {version = "0.0.1"}
```
And create a style scope wherever you want:
```rust,ignore
// breadbadgelist.rs
use sabry::styly;
styly!(const styles {
.badges {
display: flex;
&__list {
display: flex;
}
}
#wolf {
color: white;
}
});
fn render() -> HtmlElement {
div().class(styles::badges).chain(
ul().class(styles::_list(styles::badges))
).chain(
span().id(styles::thewolf)
)
}
```
> That `const` usage is covered in the [following](#constant-styly-scopes) section. In this example we dont invoke sabry build-magic, so, to be as close to real life as possible, I used a const.
Every selector, if that does make sense, now available for you as a member of `styles` scope. In this example - `styles::badges`, `styles::thewolf` and `styles::_list()`. More about scoping and member names you can read [here](#styly-scopes).
### Use styles earlier created in another crate
The combination of previous two, with some additional work to do and some extra sugar to enjoy.
Sabry is needed as both dependency and build-dependency.
To be able to compile all styles sabry also needs the *build* feature flag:
```toml
# Cargo.toml
[dependencies]
sabry = {version = "0.0.1"}
[build-dependencies]
sabry = {version = "0.0.1", features = ["build"]}
```
> If you do use some non-default feature flags make sure to keep them in sync between sabry-dependency and sabry-build-dependency.
Then you have to tell sabry when code should be compiled. We'll do this in *build.rs* file.
```rust,ignore
// build.rs
use shared_styles::{mixins, styles};
fn main(){
sabry::buildy(
sabry::usey!(
mixins!(),
styles!()
)
).expect("Failed to build sabry styles");
}
```
`buildy` is the entry function of sabry build-time process. The handy `usey!` macro will do just proper handling of our style-macros for it.
Now lets get back to the code and use the mixin defined in another crate:
```rust,ignore
// breadbadgelist.rs
use sabry::styly;
styly!(styles {
@use "mixins";
.badges {
display: flex;
&__list {
display: flex;
}
}
#wolf {
@include mixins.colored(white);
}
});
fn render() -> HtmlElement {
div().class(styles::badges).chain(
ul().class(styles::_list(styles::badges))
).chain(
span().id(styles::thewolf)
)
}
```
So the `mixins!` macro we just passed to the `usey!` macro inside of `buildy` function call is now accessible with simple and natural `@use "mixins"` SASS rule!
### Leptos specials
With [leptos](https://github.com/leptos-rs/leptos) framework you can do the trick to apply some class to most of HtmlElements for the entire component/island:
```rust,ignore
#[component]
fn component() -> impl IntoView {
view! {class="cls1", /* <- here */