Skip to main content

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;