# 🧙🏻 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
sabry::scssy!(tokens {"@mixin colored{color: red;}"});
```
|
|
Get the *(almost)* proper autocompletion for your scope from rust-analyzer
```rust
sabry::styly!(scope {".cls1{} #id1{}"});
/* autocompleted for you */
scope::cls1;
scope::theid1;
```
|
|
Code your SASS in separate files to get proper syntax highlighting, or do sass-in-the-rust to keep things in one place
```rust
sabry::styly!(component {".btn {color: green;}"});
sabry::styly!(extras "tests/assets/mixin-module.scss");
```
|
|
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
sabry::styly!(breadbadge {"
@use 'tokens';
.scope {@include tokens.badge(primary);}
"});
```
|
|
Keep things as private and modular as you wish
```rust
sabry::styly!(pub cats "tests/assets/mixin-module.scss");
sabry::styly!(dogs {"#howl {border: none;}"});
```
```rust,ignore
/// 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
sabry::styly!(cssbundle {".c1 {color: white;}"});
sabry::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 (with *internals* feature flag) - Sabry is ready for experiments.
## Usage
Feel free to check out examples:
|[crate of styles](https://github.com/yiffyrusdev/sabry/tree/master/examples/define-styles)|[style usage](https://github.com/yiffyrusdev/sabry/tree/master/examples/use-styles)|[leptos-axum with sabry](https://github.com/yiffyrusdev/sabry/tree/master/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
// lib.rs
use sabry::scssy;
scssy!(mixins "tests/assets/mixin-module.scss");
scssy!(styles {"
$primary-color: black;
@mixin colored($col: primary) {
@if $col == primary {
color: $primary-color;
} @else {
color: $col;
}
}
"});
```
*\* Unlike most of other crates that do sass-in-the-rust, sabry currently does not allow unquoted sass/scss. You still have to write it in a string-quotes. Unquoted sass/scss is reserved for the future, where we shall introduce variable injection.*
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
// breadbadgelist.rs
use sabry::styly;
styly!(const styles {"
.badges {
display: flex;
&__list {
display: flex;
}
}
#wolf {
color: white;
}
"});
/* Now you can use all scoped selectors: */
let badges_scoped_class = styles::badges;
let wolf_scoped_id = styles::thewolf;
let badges__list_scoped_class = styles::___list(styles::badges);
```
> 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
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
// breadbadgelist.rs
use sabry::styly;
styly!(styles {"
// 'mixins' is available beacuse we did call mixins!() macro in the example below
@use 'mixins';
.badges {
display: flex;
&__list {
display: flex;
}
}
#wolf {
@include mixins.colored(white);
}
"});
```
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
use leptos::prelude::*;
#[component]
fn component() -> impl IntoView {
view! {class="cls1", /* <- here */