Skip to main content

Story Macro

The #[story] and #[variant] attribute macros define stories and their variants in Holt Book. Stories are registered automatically via the inventory crate — no manual registration step is needed.

Basic Usage

A story consists of one or more variant functions annotated with #[variant] and a story constant annotated with #[story].

use holt_book::{story, variant};
use leptos::prelude::*;
use crate::components::{Card, CardHeader, CardTitle, CardContent};

#[variant]
fn default() -> AnyView {
view! {
<Card>
<CardHeader><CardTitle>"Hello"</CardTitle></CardHeader>
<CardContent><p>"Card content goes here."</p></CardContent>
</Card>
}.into_any()
}

#[variant]
fn compact() -> AnyView {
view! {
<Card class="w-64">
<CardContent><p>"A compact card."</p></CardContent>
</Card>
}.into_any()
}

/// A container for grouping related content
#[story(id = "card", name = "Card")]
const CARD_STORY: () = &[default, compact];

#[variant]

Applied to a function that returns AnyView. Each variant function represents one visual state of the component.

#[variant]
fn minimal() -> AnyView {
view! {
<Card>
<CardContent><p>"Just content, no header."</p></CardContent>
</Card>
}.into_any()
}

Variant functions take no arguments and must return AnyView (use .into_any() on the view! result).

#[story]

Applied to a const that references an array of variant functions. The macro accepts these attributes:

AttributeRequiredDescription
idyesUnique identifier used in URLs and snapshots
nameyesDisplay name shown in the storybook sidebar
extra_docsnoAdditional Markdown documentation (&'static str constant)
#[story(id = "card", name = "Card")]
const CARD_STORY: () = &[default, compact, minimal];

The const's doc comment becomes the story description displayed in the UI:

/// A container for grouping related content
#[story(id = "card", name = "Card")]
const CARD_STORY: () = &[default, compact];

Extra Documentation

Stories can include additional documentation displayed in the UI via the extra_docs attribute. This accepts a reference to a &'static str constant containing any Markdown content — usage notes, design rationale, API details, or anything else you want to show alongside the component.

const CARD_DOCS: &str = "
## Design Notes

Cards should always have a minimum width of 200px.
Use `CardHeader` for titles and `CardContent` for body text.
";

#[story(id = "card", name = "Card", extra_docs = CARD_DOCS)]
const CARD_STORY: () = &[default, compact];

The constant can come from anywhere — a literal in your source, an include_str! of a Markdown file, or a build-script-generated value. Holt Kit's own storybook uses this to embed source code, but the feature is general-purpose.

Complete Example

A complete story file for a Card component:

use holt_book::{story, variant};
use leptos::prelude::*;
use crate::components::{Card, CardHeader, CardTitle, CardContent};

#[variant]
fn default() -> AnyView {
view! {
<Card>
<CardHeader><CardTitle>"Hello"</CardTitle></CardHeader>
<CardContent><p>"Card content goes here."</p></CardContent>
</Card>
}.into_any()
}

#[variant]
fn compact() -> AnyView {
view! {
<Card class="w-64">
<CardContent><p>"A compact card."</p></CardContent>
</Card>
}.into_any()
}

#[variant]
fn minimal() -> AnyView {
view! {
<Card>
<CardContent><p>"Just content, no header."</p></CardContent>
</Card>
}.into_any()
}

/// A container for grouping related content
#[story(id = "card", name = "Card")]
const CARD_STORY: () = &[default, compact, minimal];