# 🧙🏻 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
> **Tip** | If you use [leptos](https://github.com/leptos-rs/leptos) - check [this](#leptos-specials) section as well
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 */