maud_extensions/lib.rs
1#![forbid(unsafe_code)]
2#![deny(missing_docs)]
3#![deny(rustdoc::broken_intra_doc_links)]
4//! Tiny Maud superpowers.
5//!
6//! This crate is the public runtime/support surface:
7//! - reexports proc macros like [`css!`] and [`js!`]
8//! - optionally reexports the experimental [`Component`] derive
9//! - provides runtime slot wrapper types like [`Slot`] and [`Slots`]
10//! - reexports `bon` so generated component code can depend only on
11//! `maud-extensions`
12//!
13//! Recommended dependency spelling:
14//!
15//! ```toml
16//! [dependencies]
17//! mx = { package = "maud-extensions", version = "0.6.7", features = ["components"] }
18//! ```
19//!
20//! # Experimental components
21//!
22//! The component system is currently opt-in behind the `components` feature.
23//! The preferred authoring pattern is:
24//!
25//! - `#[derive(Component)]` on the struct
26//! - `Slot<maud::Markup>` / `Slot<Vec<maud::Markup>>` for slot fields
27//! - `#[mx(default)]` on the single default slot
28//! - reserve `#[mx(default)]` for slot selection only; use Rust `Default` or
29//! `Option<T>` for non-slot defaults
30//! - ordinary inherent helpers like `fn css() -> Markup` and
31//! `fn js() -> Markup`
32//! - ordinary `impl Render` with explicit `(Self::css())` / `(Self::js())`
33//! emission where desired
34//!
35//! ```ignore
36//! use maud::Markup;
37//! use mx::{Component, Slot};
38//!
39//! #[derive(Component)]
40//! struct Card {
41//! title: String,
42//! header: Slot<Markup>,
43//! #[mx(default)]
44//! body: Slot<Markup>,
45//! footer: Slot<Markup>,
46//! #[mx(each = action)]
47//! actions: Slot<Vec<Markup>>,
48//! }
49//!
50//! impl Card {
51//! fn css() -> Markup {
52//! mx::css! {
53//! me {
54//! padding: 1rem;
55//! border: 1px solid #ddd;
56//! }
57//! }
58//! }
59//!
60//! fn js() -> Markup {
61//! mx::js!(once, {
62//! me().class_add("ready");
63//! })
64//! }
65//! }
66//!
67//! impl maud::Render for Card {
68//! fn render(&self) -> Markup {
69//! maud::html! {
70//! article.card {
71//! (Self::css())
72//! (Self::js())
73//! header class="header" { (self.header) }
74//! h2 { (self.title) }
75//! div.body { (self.body) }
76//! footer class="footer" { (self.footer) }
77//! div.actions { (self.actions) }
78//! }
79//! }
80//! }
81//! }
82//! ```
83//!
84//! The broader browser-side runtime pieces this layers on top of today include:
85//!
86//! - Surreal: <https://github.com/gnat/surreal>
87//! - css-scope-inline: <https://github.com/gnat/css-scope-inline>
88//! - Preact Signals: <https://github.com/preactjs/signals>
89//!
90//! To bootstrap those browser-side pieces in a page, use [`Init`] in `<head>`:
91//!
92//! ```ignore
93//! use maud::html;
94//! use mx::Init;
95//!
96//! fn page() -> maud::Markup {
97//! html! {
98//! head { (Init::all()) }
99//! body { /* page body */ }
100//! }
101//! }
102//! ```
103
104extern crate self as maud_extensions;
105
106mod init;
107mod slot;
108
109pub use maud_extensions_macros::{
110 css, js, signals_inline, surreal_scope_inline, surreal_scope_signals_inline,
111};
112pub use init::Init;
113pub use slot::{Slot, Slots};
114
115#[cfg(feature = "components")]
116pub use maud_extensions_macros::Component;
117
118#[doc(hidden)]
119pub use bon;