1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
//! A library for embedding dynamic CSS in Rust (wasm); inspired by [cssinjs/JSS](https://cssinjs.org/)
//!
//! This crate is designed to be framework-independent.
//! It currently provides integrations for [Dioxus](https://dioxuslabs.com/), which is disabled by default.
//!
//! ## Use case
//! This crate allows to develop reusable components for the web which bundle their own
//! styles. Dead-Code-Analysis works as usually with Rust: If you do not need a certain component,
//! its styles won't be embedded in the final binary.
//!
//! The classnames are generated at runtime to avoid collisions (i.e. `css-123`).
//!
//! ## Basic idea
//! The [make_styles!] procmacro allows you to write CSS-like style sheets directly in Rust. You can
//! use normal class names without worrying about collisions. Even if another component uses the same
//! name, it does not matter. The procmacro automatically determines the classnames from the style
//! and generates a helper class containing the real class names.
//!
//! #### Example (Dioxus):
//! ```no_run
//! # #[cfg(feature = "dioxus")] {
//! #![allow(non_snake_case)]
//!
//! use css_in_rs::{Classes, EmptyTheme, make_styles, use_style_provider_quickstart};
//! use dioxus::prelude::*;
//!
//! make_styles! {
//! (_theme: EmptyTheme) -> MyClasses {
//! red_text {
//! color: "red",
//! margin: "5px",
//! },
//! "button" {
//! margin: "5px",
//! padding: "5px",
//! width: "10em",
//! },
//! "button.primary" {
//! border: "2px solid red",
//! },
//! "button.disabled" { // Shows a rust warning: "field never read"
//! disabled: true,
//! },
//! }
//! }
//!
//! fn Demo(cx: Scope) -> Element {
//! let classes: &MyClasses = MyClasses::use_style(cx);
//!
//! render! {
//! div {
//! class: &classes.red_text as &str,
//! "This text is supposed to be red.",
//! }
//! button {
//! class: &classes.primary as &str,
//! "Click me",
//! }
//! }
//! }
//!
//! fn App(cx: Scope) -> Element {
//! use_style_provider_quickstart(cx, || EmptyTheme);
//!
//! cx.render(rsx! {
//! Demo {}
//! })
//! }
//!
//! fn main() {
//! // launch the web app
//! dioxus_web::launch(App);
//! }
//! # }
//! ```
#![cfg_attr(feature = "unstable-doc-cfg", feature(doc_cfg))]
#[doc_cfg(feature = "dioxus")]
use dioxus::prelude::*;
mod style_provider;
pub use css_in_rs_macro::make_styles;
use doc_cfg::doc_cfg;
pub use style_provider::StyleProvider;
pub trait Theme: Clone + 'static {
fn fast_cmp(&self, other: &Self) -> bool;
}
#[derive(Clone, Copy)]
pub struct EmptyTheme;
impl Theme for EmptyTheme {
fn fast_cmp(&self, _: &Self) -> bool {
true
}
}
pub trait Classes: Sized + 'static {
type Theme: Theme;
fn generate(theme: &Self::Theme, css: &mut String, counter: &mut u64);
fn new(start: u64) -> Self;
#[doc_cfg(feature = "dioxus")]
fn use_style(cx: &ScopeState) -> &Self {
let provider = use_style_provider(cx);
provider.use_styles(cx)
}
}
/// Quickly sets up a StyleProvider in the global document. Styles will be attached
/// to `window.document.head`
#[doc_cfg(feature = "dioxus")]
pub fn use_style_provider_quickstart<'a, T: Theme>(
cx: &'a ScopeState,
make_theme: impl FnOnce() -> T,
) -> &'a StyleProvider<T> {
let provider = cx.use_hook(|| StyleProvider::quickstart_web(make_theme()));
use_context_provider(cx, || provider.clone())
}
#[doc_cfg(feature = "dioxus")]
pub fn use_style_provider_root<'a, T: Theme>(
cx: &'a ScopeState,
some_elem: &web_sys::Element,
make_theme: impl FnOnce() -> T,
) -> &'a StyleProvider<T> {
let provider = cx.use_hook(|| StyleProvider::new_and_mount(some_elem, make_theme()));
use_context_provider(cx, || provider.clone())
}
#[doc_cfg(feature = "dioxus")]
pub fn use_style_provider<T: Theme>(cx: &ScopeState) -> &StyleProvider<T> {
use_context(cx).unwrap()
}