Skip to main content

maud_ui/primitives/
badge.rs

1//! Badge component — minimal status indicators for tagging and labeling.
2
3use maud::{html, Markup};
4
5/// Badge color variants
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum Variant {
8    Default,
9    Secondary,
10    Success,
11    Warning,
12    Danger,
13    Outline,
14}
15
16impl Variant {
17    fn class(&self) -> &'static str {
18        match self {
19            Self::Default => "mui-badge--default",
20            Self::Secondary => "mui-badge--secondary",
21            Self::Success => "mui-badge--success",
22            Self::Warning => "mui-badge--warning",
23            Self::Danger => "mui-badge--danger",
24            Self::Outline => "mui-badge--outline",
25        }
26    }
27}
28
29/// Badge rendering properties
30#[derive(Debug, Clone)]
31pub struct Props {
32    /// Text content displayed in the badge
33    pub label: String,
34    /// Visual variant (color scheme)
35    pub variant: Variant,
36}
37
38impl Default for Props {
39    fn default() -> Self {
40        Self {
41            label: String::new(),
42            variant: Variant::Default,
43        }
44    }
45}
46
47/// Render a single badge with the given properties
48pub fn render(props: Props) -> Markup {
49    html! {
50        span class={"mui-badge " (props.variant.class())} {
51            (props.label)
52        }
53    }
54}
55
56/// Showcase all badge variants and use cases
57pub fn showcase() -> Markup {
58    html! {
59        div.mui-showcase__grid {
60            // All variants
61            section {
62                h2 { "Variants" }
63                div.mui-showcase__row {
64                    (render(Props { label: "Default".into(), variant: Variant::Default }))
65                    (render(Props { label: "Secondary".into(), variant: Variant::Secondary }))
66                    (render(Props { label: "Success".into(), variant: Variant::Success }))
67                    (render(Props { label: "Warning".into(), variant: Variant::Warning }))
68                    (render(Props { label: "Danger".into(), variant: Variant::Danger }))
69                    (render(Props { label: "Outline".into(), variant: Variant::Outline }))
70                }
71            }
72
73            // Realistic inline context
74            section {
75                h2 { "In Context" }
76                // Navigation-style items with counter badges
77                div style="display:flex;flex-direction:column;gap:0.75rem;max-width:20rem;" {
78                    div style="display:flex;align-items:center;justify-content:space-between;" {
79                        span style="font-size:0.875rem;color:var(--mui-text);" { "Inbox" }
80                        (render(Props { label: "3".into(), variant: Variant::Default }))
81                    }
82                    div style="display:flex;align-items:center;justify-content:space-between;" {
83                        span style="font-size:0.875rem;color:var(--mui-text);" { "Drafts" }
84                        (render(Props { label: "12".into(), variant: Variant::Secondary }))
85                    }
86                    div style="display:flex;align-items:center;justify-content:space-between;" {
87                        span style="font-size:0.875rem;color:var(--mui-text);" { "Errors" }
88                        (render(Props { label: "2".into(), variant: Variant::Danger }))
89                    }
90                }
91            }
92
93            // Labels in a list
94            section {
95                h2 { "Labels" }
96                div style="display:flex;flex-direction:column;gap:0.5rem;" {
97                    div style="display:flex;align-items:center;gap:0.5rem;" {
98                        span style="font-size:0.875rem;color:var(--mui-text);min-width:8rem;" { "Authentication API" }
99                        (render(Props { label: "Stable".into(), variant: Variant::Success }))
100                        (render(Props { label: "v2.1".into(), variant: Variant::Outline }))
101                    }
102                    div style="display:flex;align-items:center;gap:0.5rem;" {
103                        span style="font-size:0.875rem;color:var(--mui-text);min-width:8rem;" { "Streaming SDK" }
104                        (render(Props { label: "Beta".into(), variant: Variant::Warning }))
105                        (render(Props { label: "v0.9".into(), variant: Variant::Outline }))
106                    }
107                    div style="display:flex;align-items:center;gap:0.5rem;" {
108                        span style="font-size:0.875rem;color:var(--mui-text);min-width:8rem;" { "Legacy Client" }
109                        (render(Props { label: "Deprecated".into(), variant: Variant::Danger }))
110                        (render(Props { label: "v1.0".into(), variant: Variant::Outline }))
111                    }
112                }
113            }
114        }
115    }
116}