pinkie
Pinkie is a simple scoped CSS concatenator generator for Rust.
It does not do any templating, the only feature over static .css files it provides is "per-component" (per-macro-invocation, technically) scoping.
It's implemented as a macro, css!, which allows you to write bits of CSS
throughout your codebase and have them all concatenated into a single string
under content-addressable (they have hashes) classes.
This relies on CSS Nesting - so it is highly recommended to additionally
transpile it by something like lightningcss.
Pinkie does not do any CSS parsing on its own, it just translates Rust tokens into a string, using token span locations to tell whether adjacent tokens should be joined or separated by a space (any run of whitespace collapses to a single space, which is fine for CSS).
In case something doesn't tokenize as Rust, you can also include CSS verbatim
using r"raw strings", their content will be included as is.
Sadly, it's close to impossible to reliably collect macro invocations across
a codebase entirely during the build time, so it uses the excellent inventory
crate to collect and concatenate all generated bits of CSS at runtime.
It's a startup-only thing, so it's not a big deal, and if you use
lightningcss to minimize them they say it takes 4ms to do that for the
entirety of Bootstrap, which is an okay tradeoff for having pinkie exist and be
working.
Example
use LazyLock;
use ;
use css;
/// A maud component styled with pinkie.
// `lightningcss` is strongly recommended as
// pinkie uses CSS Nesting for scoping
Cargo features
validation(default) - enables (some) compile time CSS validation bylightningcss. Slows down the first proc macro compilation by bringing it.location(default) - enables storing the location ofcss!invocations, useful for debugging (the hash collision panic will use it).dynamic- changes the style collection method to trying to read Rust sources (depends onlocation) at runtime, allowing one to implement style hot-reloading during development, as Rust recompilation speeds are a thing. Only does anything whendebug_assertionsare enabled.
If you want custom class prefix you can have it by setting
PINKIE_CSS_CLASS_PREFIX environment variable during compilation.
And you can have the prefix be of any length, non-minimized, literally any compression algorithm will take care of it lol.
Known limitations
Since the CSS is tokenized as Rust code, some things valid in plain CSS don't survive the trip:
- Comments are Rust comments and are stripped - CSS
/* comments */won't make it into the output (you likely don't want them there anyway). - Hex colors are hit-or-miss:
#fffor#c0ffeehappen to tokenize, but some digit/letter combinations don't form valid Rust tokens. emunits on fractional values (1.5em) don't tokenize - theestarts a Rust float exponent. Other units (rem,px,s, ...) are fine.- Single-quoted strings are Rust char literals and generally won't tokenize; use double quotes.
- String escapes follow Rust rules, not CSS ones.
For all of these, the r"raw string" escape hatch has you covered.
The dynamic feature reads source files at paths produced by file!(),
which are relative to the workspace root - so it only works when the process
runs with the workspace root as its working directory, and never for css!
calls from dependencies. When it fails, it logs a warning and falls back to
the statically embedded CSS.
Inspiration
A bit obviously inspired by maud and works great with it.
In case you didn't know, MPAs actually work extremely well for most use-cases in modern browsers, especially if you write your web-server in a real language, like Rust or Go :)