#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ComponentCategory {
Explore,
Actions,
Forms,
Navigation,
Overlays,
Display,
Messages,
MacOsLayouts,
Charts,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ComponentInfo {
pub id: &'static str,
pub name: &'static str,
pub category: ComponentCategory,
pub description: &'static str,
pub documentation: Option<&'static str>,
}
pub const COMPONENT_GROUPS: &[(&str, &[&str])] = &[
("Explore", &["overview", "theme", "root", "focus_trap"]),
(
"Actions",
&[
"button",
"button_group",
"dropdown_button",
"link",
"toggle",
"toggle_group",
"clipboard",
"command",
],
),
(
"Forms",
&[
"input",
"textarea",
"editor",
"number_input",
"input_group",
"select",
"combobox",
"calendar",
"date_picker",
"time_field",
"color_picker",
"otp_input",
"slider",
"checkbox",
"switch",
"radio",
"form",
"questionnaire",
"stepper",
"search_field",
"secure_field",
],
),
(
"Navigation",
&[
"menu",
"tabs",
"accordion",
"collapsible",
"nav_stack",
"pagination",
"breadcrumb",
"sidebar",
],
),
(
"Overlays",
&[
"sheet",
"dialog",
"alert_dialog",
"popover",
"tooltip",
"hover_card",
"toast",
"notification",
],
),
(
"Display",
&[
"icon",
"avatar",
"text_view",
"label",
"panel",
"group_box",
"description_list",
"list",
"virtual_list",
"scrollbar",
"scrollable",
"table",
"data_table",
"tree",
"resizable",
"dock",
"separator",
"keycap",
"badge",
"tag",
"alert",
"empty_state",
"progress",
"spinner",
"skeleton",
"shimmer",
"rating",
"carousel",
"image",
"diff",
],
),
(
"Messages",
&[
"message",
"bubble",
"marker",
"attachment",
"message_scroller",
"speech",
],
),
(
"macOS layouts",
&[
"title_bar",
"toolbar",
"status_bar",
"settings",
"inspector",
"inset_list",
"material",
],
),
(
"Charts",
&[
"plot",
"line_chart",
"bar_chart",
"area_chart",
"pie_chart",
"radar_chart",
"candlestick_chart",
"sankey_chart",
],
),
];
pub const COMPONENTS: &[ComponentInfo] = &[
ComponentInfo {
id: "overview",
name: "Overview",
category: ComponentCategory::Explore,
description: "Explore the components together, then use the sidebar to inspect each one.",
documentation: Some("index"),
},
ComponentInfo {
id: "theme",
name: "Theme",
category: ComponentCategory::Explore,
description: "System appearance, macOS accent colors, and semantic light and dark tokens.",
documentation: Some("theme"),
},
ComponentInfo {
id: "root",
name: "Root",
category: ComponentCategory::Explore,
description: "The native window host coordinates overlays, focus, and text selection.",
documentation: Some("root"),
},
ComponentInfo {
id: "focus_trap",
name: "Focus trap",
category: ComponentCategory::Explore,
description: "Keep keyboard focus within the current group of related controls.",
documentation: Some("focus-trap"),
},
ComponentInfo {
id: "button",
name: "Button",
category: ComponentCategory::Actions,
description: "Content-sized actions with quiet hover, focus and pressed states.",
documentation: Some("button"),
},
ComponentInfo {
id: "button_group",
name: "Button group",
category: ComponentCategory::Actions,
description: "Choose one setting from a row of mutually exclusive options.",
documentation: Some("button"),
},
ComponentInfo {
id: "dropdown_button",
name: "Dropdown button",
category: ComponentCategory::Actions,
description: "Combine a primary command and its related actions in one compact control.",
documentation: Some("dropdown_button"),
},
ComponentInfo {
id: "link",
name: "Link",
category: ComponentCategory::Actions,
description: "Open a named destination.",
documentation: None,
},
ComponentInfo {
id: "toggle",
name: "Toggle",
category: ComponentCategory::Actions,
description: "Keep a command active until it is pressed again.",
documentation: Some("toggle"),
},
ComponentInfo {
id: "toggle_group",
name: "Toggle group",
category: ComponentCategory::Actions,
description: "Combine independent filters to show more than one status.",
documentation: Some("toggle"),
},
ComponentInfo {
id: "clipboard",
name: "Clipboard",
category: ComponentCategory::Actions,
description: "Copy a value with keyboard-accessible confirmation.",
documentation: Some("clipboard"),
},
ComponentInfo {
id: "command",
name: "Command",
category: ComponentCategory::Actions,
description: "Find and invoke commands with a searchable, keyboard-controlled palette.",
documentation: Some("command"),
},
ComponentInfo {
id: "input",
name: "Input",
category: ComponentCategory::Forms,
description: "Single-line editing with selection, clipboard and IME support.",
documentation: Some("input"),
},
ComponentInfo {
id: "textarea",
name: "Textarea",
category: ComponentCategory::Forms,
description: "Multi-line notes with native text editing.",
documentation: Some("textarea"),
},
ComponentInfo {
id: "editor",
name: "Editor",
category: ComponentCategory::Forms,
description: "Edit source with line numbers, folding, search, and multiple cursors.",
documentation: Some("editor"),
},
ComponentInfo {
id: "number_input",
name: "Number input",
category: ComponentCategory::Forms,
description: "A compact numeric field with keyboard and button stepping.",
documentation: Some("number-input"),
},
ComponentInfo {
id: "input_group",
name: "Input group",
category: ComponentCategory::Forms,
description: "Compose a text field with leading and trailing accessories.",
documentation: Some("input-group"),
},
ComponentInfo {
id: "select",
name: "Select",
category: ComponentCategory::Forms,
description: "Choose one value from a fixed set of options.",
documentation: Some("select"),
},
ComponentInfo {
id: "combobox",
name: "Combobox",
category: ComponentCategory::Forms,
description: "Search a collection, then choose a matching option.",
documentation: Some("combobox"),
},
ComponentInfo {
id: "calendar",
name: "Calendar",
category: ComponentCategory::Forms,
description: "Choose a date with month and year navigation.",
documentation: Some("calendar"),
},
ComponentInfo {
id: "date_picker",
name: "Date picker",
category: ComponentCategory::Forms,
description: "Choose a date from a calendar anchored to a field.",
documentation: Some("date-picker"),
},
ComponentInfo {
id: "time_field",
name: "Time field",
category: ComponentCategory::Forms,
description: "Edit hours and minutes as keyboard-navigable time segments.",
documentation: Some("time-field"),
},
ComponentInfo {
id: "color_picker",
name: "Color picker",
category: ComponentCategory::Forms,
description: "Choose a label color with Hex and HSLA controls.",
documentation: Some("color-picker"),
},
ComponentInfo {
id: "otp_input",
name: "OTP input",
category: ComponentCategory::Forms,
description: "Enter a six-digit verification code.",
documentation: Some("otp-input"),
},
ComponentInfo {
id: "slider",
name: "Slider",
category: ComponentCategory::Forms,
description: "Adjust one value or a range with the pointer or keyboard.",
documentation: Some("slider"),
},
ComponentInfo {
id: "checkbox",
name: "Checkbox",
category: ComponentCategory::Forms,
description: "Independent choices, including a mixed selection.",
documentation: Some("checkbox"),
},
ComponentInfo {
id: "switch",
name: "Switch",
category: ComponentCategory::Forms,
description: "An immediate on/off choice with a visible track and thumb.",
documentation: Some("switch"),
},
ComponentInfo {
id: "radio",
name: "Radio",
category: ComponentCategory::Forms,
description: "Choose one option from a group.",
documentation: Some("radio"),
},
ComponentInfo {
id: "form",
name: "Form",
category: ComponentCategory::Forms,
description: "Labeled, validated fields with consistent alignment and save actions.",
documentation: Some("form"),
},
ComponentInfo {
id: "questionnaire",
name: "Questionnaire",
category: ComponentCategory::Forms,
description: "Move through questions with retained answers, validation, and progress.",
documentation: Some("questionnaire"),
},
ComponentInfo {
id: "stepper",
name: "Stepper",
category: ComponentCategory::Forms,
description: "Walk through the steps of a task with visible completion state.",
documentation: Some("stepper"),
},
ComponentInfo {
id: "search_field",
name: "Search field",
category: ComponentCategory::Forms,
description: "Search using a native inset field with an inline search symbol.",
documentation: Some("input"),
},
ComponentInfo {
id: "secure_field",
name: "Secure field",
category: ComponentCategory::Forms,
description: "Enter a password in a masked text field.",
documentation: Some("input"),
},
ComponentInfo {
id: "menu",
name: "Menu",
category: ComponentCategory::Navigation,
description: "An anchored action menu with one pointer and keyboard cursor.",
documentation: Some("menu"),
},
ComponentInfo {
id: "tabs",
name: "Tabs",
category: ComponentCategory::Navigation,
description: "Switch between related views while keeping context.",
documentation: Some("tabs"),
},
ComponentInfo {
id: "accordion",
name: "Accordion",
category: ComponentCategory::Navigation,
description: "Reveal supporting content when it is needed.",
documentation: Some("accordion"),
},
ComponentInfo {
id: "collapsible",
name: "Collapsible",
category: ComponentCategory::Navigation,
description: "Expand a single region for additional settings.",
documentation: Some("collapsible"),
},
ComponentInfo {
id: "nav_stack",
name: "Navigation stack",
category: ComponentCategory::Navigation,
description: "Navigate between persistent pages and return to where you left off.",
documentation: None,
},
ComponentInfo {
id: "pagination",
name: "Pagination",
category: ComponentCategory::Navigation,
description: "Move through a paged collection.",
documentation: Some("pagination"),
},
ComponentInfo {
id: "breadcrumb",
name: "Breadcrumb",
category: ComponentCategory::Navigation,
description: "Return to an ancestor in a document or folder hierarchy.",
documentation: None,
},
ComponentInfo {
id: "sidebar",
name: "Sidebar",
category: ComponentCategory::Navigation,
description: "Source-list navigation with icons and rounded selected rows.",
documentation: Some("sidebar"),
},
ComponentInfo {
id: "sheet",
name: "Sheet",
category: ComponentCategory::Overlays,
description: "Inspect project details in an edge-attached panel.",
documentation: Some("sheet"),
},
ComponentInfo {
id: "dialog",
name: "Dialog",
category: ComponentCategory::Overlays,
description: "A focused task with confirmation, cancellation and focus return.",
documentation: Some("dialog"),
},
ComponentInfo {
id: "alert_dialog",
name: "Alert dialog",
category: ComponentCategory::Overlays,
description: "An explicit decision that cannot be dismissed by clicking the backdrop.",
documentation: Some("alert-dialog"),
},
ComponentInfo {
id: "popover",
name: "Popover",
category: ComponentCategory::Overlays,
description: "Adjust contextual settings while keeping the workspace in view.",
documentation: Some("popover"),
},
ComponentInfo {
id: "tooltip",
name: "Tooltip",
category: ComponentCategory::Overlays,
description: "A short explanation for an action, shown on hover.",
documentation: Some("tooltip"),
},
ComponentInfo {
id: "hover_card",
name: "Hover card",
category: ComponentCategory::Overlays,
description: "Preview supporting details without leaving the page.",
documentation: Some("hover-card"),
},
ComponentInfo {
id: "toast",
name: "Toast",
category: ComponentCategory::Overlays,
description: "Brief feedback that leaves your current task in place.",
documentation: Some("notification"),
},
ComponentInfo {
id: "notification",
name: "Notification",
category: ComponentCategory::Overlays,
description: "Window-hosted notifications with semantic status and dismissal.",
documentation: Some("notification"),
},
ComponentInfo {
id: "icon",
name: "Icon",
category: ComponentCategory::Display,
description: "Native SF Symbols on macOS with Lucide fallbacks that inherit text color.",
documentation: Some("icon"),
},
ComponentInfo {
id: "avatar",
name: "Avatar",
category: ComponentCategory::Display,
description: "Identify people and workspaces with an image or initials.",
documentation: Some("avatar"),
},
ComponentInfo {
id: "text_view",
name: "Text view",
category: ComponentCategory::Display,
description: "Read structured documents with selectable text and links.",
documentation: Some("text-view"),
},
ComponentInfo {
id: "label",
name: "Label",
category: ComponentCategory::Display,
description: "Readable labels with secondary text, masking, and search highlights.",
documentation: Some("label"),
},
ComponentInfo {
id: "panel",
name: "Panel",
category: ComponentCategory::Display,
description: "A surface for a related group of settings or information.",
documentation: None,
},
ComponentInfo {
id: "group_box",
name: "Group box",
category: ComponentCategory::Display,
description: "Group preferences in a titled, rounded container.",
documentation: Some("group-box"),
},
ComponentInfo {
id: "description_list",
name: "Description list",
category: ComponentCategory::Display,
description: "Compare labels and values in a structured inspector.",
documentation: Some("description-list"),
},
ComponentInfo {
id: "list",
name: "List",
category: ComponentCategory::Display,
description: "Selectable rows with icons, secondary content, and keyboard navigation.",
documentation: Some("list"),
},
ComponentInfo {
id: "virtual_list",
name: "Virtual list",
category: ComponentCategory::Display,
description: "Browse a large activity log with variable-height rows.",
documentation: Some("virtual-list"),
},
ComponentInfo {
id: "scrollbar",
name: "Scrollbar",
category: ComponentCategory::Display,
description: "Drag the scroll thumb to move through a long activity log.",
documentation: None,
},
ComponentInfo {
id: "scrollable",
name: "Scrollable",
category: ComponentCategory::Display,
description: "Scroll long content in a bounded native pane.",
documentation: Some("scrollable"),
},
ComponentInfo {
id: "table",
name: "Table",
category: ComponentCategory::Display,
description: "Aligned columns for comparing records.",
documentation: Some("table"),
},
ComponentInfo {
id: "data_table",
name: "Data table",
category: ComponentCategory::Display,
description: "Use the full table delegate for selection, columns, and large datasets.",
documentation: Some("data-table"),
},
ComponentInfo {
id: "tree",
name: "Tree",
category: ComponentCategory::Display,
description: "Explore nested folders and select a workspace document.",
documentation: Some("tree"),
},
ComponentInfo {
id: "resizable",
name: "Resizable",
category: ComponentCategory::Display,
description: "Drag the divider to adjust space between panes.",
documentation: Some("resizable"),
},
ComponentInfo {
id: "dock",
name: "Dock",
category: ComponentCategory::Display,
description: "Rearrange document panels by dragging their tabs.",
documentation: Some("dock"),
},
ComponentInfo {
id: "separator",
name: "Separator",
category: ComponentCategory::Display,
description: "A quiet boundary between distinct sections.",
documentation: None,
},
ComponentInfo {
id: "keycap",
name: "Keycap",
category: ComponentCategory::Display,
description: "Compact, readable keyboard hints.",
documentation: Some("kbd"),
},
ComponentInfo {
id: "badge",
name: "Badge",
category: ComponentCategory::Display,
description: "Short labels for neutral and semantic status.",
documentation: Some("badge"),
},
ComponentInfo {
id: "tag",
name: "Tag",
category: ComponentCategory::Display,
description: "Compact semantic labels with filled and outline presentations.",
documentation: Some("tag"),
},
ComponentInfo {
id: "alert",
name: "Alert",
category: ComponentCategory::Display,
description: "Inline feedback banners for neutral and semantic conditions.",
documentation: Some("alert"),
},
ComponentInfo {
id: "empty_state",
name: "Empty state",
category: ComponentCategory::Display,
description: "Explain an empty collection and its next step.",
documentation: Some("empty"),
},
ComponentInfo {
id: "progress",
name: "Progress",
category: ComponentCategory::Display,
description: "Show how much of a known task is complete.",
documentation: Some("progress"),
},
ComponentInfo {
id: "spinner",
name: "Spinner",
category: ComponentCategory::Display,
description: "Show an ongoing task with an animated progress indicator.",
documentation: Some("spinner"),
},
ComponentInfo {
id: "skeleton",
name: "Skeleton",
category: ComponentCategory::Display,
description: "Reserve stable layout while content is loading.",
documentation: Some("skeleton"),
},
ComponentInfo {
id: "shimmer",
name: "Shimmer",
category: ComponentCategory::Display,
description: "Animate a subtle highlight over text during background work.",
documentation: Some("shimmer"),
},
ComponentInfo {
id: "rating",
name: "Rating",
category: ComponentCategory::Display,
description: "Choose a star rating or display a read-only score.",
documentation: Some("rating"),
},
ComponentInfo {
id: "carousel",
name: "Carousel",
category: ComponentCategory::Display,
description: "Browse a collection with previous, next, and pagination controls.",
documentation: Some("carousel"),
},
ComponentInfo {
id: "image",
name: "Image",
category: ComponentCategory::Display,
description: "Draw bundled or local images with preserved aspect ratio and cropping.",
documentation: Some("image"),
},
ComponentInfo {
id: "diff",
name: "Diff",
category: ComponentCategory::Display,
description: "Review source changes in unified or side-by-side views.",
documentation: Some("diff"),
},
ComponentInfo {
id: "message",
name: "Message",
category: ComponentCategory::Messages,
description: "Compose avatars, headers, bodies, and footers in a conversation.",
documentation: Some("message"),
},
ComponentInfo {
id: "bubble",
name: "Bubble",
category: ComponentCategory::Messages,
description: "Display incoming and outgoing message bubbles.",
documentation: Some("bubble"),
},
ComponentInfo {
id: "marker",
name: "Marker",
category: ComponentCategory::Messages,
description: "Mark dates and events in a conversation timeline.",
documentation: Some("marker"),
},
ComponentInfo {
id: "attachment",
name: "Attachment",
category: ComponentCategory::Messages,
description: "Show files with progress, retry, and remove actions.",
documentation: Some("attachment"),
},
ComponentInfo {
id: "message_scroller",
name: "Message scroller",
category: ComponentCategory::Messages,
description: "Follow new messages while preserving a manually scrolled position.",
documentation: Some("message-scroller"),
},
ComponentInfo {
id: "speech",
name: "Speech",
category: ComponentCategory::Messages,
description: "Speech capture and waveform controls; system recognition is an optional feature.",
documentation: Some("speech"),
},
ComponentInfo {
id: "title_bar",
name: "Title bar",
category: ComponentCategory::MacOsLayouts,
description: "A native title-bar surface leaves room for the system window controls.",
documentation: Some("title-bar"),
},
ComponentInfo {
id: "toolbar",
name: "Toolbar",
category: ComponentCategory::MacOsLayouts,
description: "Compact command groups on a translucent macOS toolbar surface.",
documentation: Some("toolbar"),
},
ComponentInfo {
id: "status_bar",
name: "Status bar",
category: ComponentCategory::MacOsLayouts,
description: "Quiet document status and contextual controls along a window edge.",
documentation: Some("status-bar"),
},
ComponentInfo {
id: "settings",
name: "Settings",
category: ComponentCategory::MacOsLayouts,
description: "Grouped preference rows with descriptive labels and trailing controls.",
documentation: Some("settings"),
},
ComponentInfo {
id: "inspector",
name: "Inspector",
category: ComponentCategory::MacOsLayouts,
description: "A compact details pane with clear field alignment.",
documentation: None,
},
ComponentInfo {
id: "inset_list",
name: "Inset list",
category: ComponentCategory::MacOsLayouts,
description: "Rounded inset rows for grouped preferences and collections.",
documentation: None,
},
ComponentInfo {
id: "material",
name: "Material",
category: ComponentCategory::MacOsLayouts,
description: "Semantic surface fills and opt-in native system glass underlays.",
documentation: None,
},
ComponentInfo {
id: "plot",
name: "Plot",
category: ComponentCategory::Charts,
description: "Compose axes, marks, scales, and grids for custom visualizations.",
documentation: Some("plot"),
},
ComponentInfo {
id: "line_chart",
name: "Line chart",
category: ComponentCategory::Charts,
description: "Follow one measure over time. Hover a point for its value.",
documentation: Some("chart"),
},
ComponentInfo {
id: "bar_chart",
name: "Bar chart",
category: ComponentCategory::Charts,
description: "Compare totals across categories.",
documentation: Some("chart"),
},
ComponentInfo {
id: "area_chart",
name: "Area chart",
category: ComponentCategory::Charts,
description: "Compare related measures over time, such as download and upload.",
documentation: Some("chart"),
},
ComponentInfo {
id: "pie_chart",
name: "Pie chart",
category: ComponentCategory::Charts,
description: "Show how a whole divides into parts.",
documentation: Some("chart"),
},
ComponentInfo {
id: "radar_chart",
name: "Radar chart",
category: ComponentCategory::Charts,
description: "Compare profiles across several dimensions at once.",
documentation: Some("chart"),
},
ComponentInfo {
id: "candlestick_chart",
name: "Candlestick chart",
category: ComponentCategory::Charts,
description: "Read the open, high, low and close of each trading session.",
documentation: Some("chart"),
},
ComponentInfo {
id: "sankey_chart",
name: "Sankey chart",
category: ComponentCategory::Charts,
description: "Trace how an amount flows from its sources to where it goes.",
documentation: Some("chart"),
},
];
pub fn component(id: &str) -> Option<&'static ComponentInfo> {
COMPONENTS.iter().find(|item| item.id == id)
}
impl ComponentInfo {
pub fn interpretation(&self) -> &'static crate::interpretation::ComponentInterpretation {
crate::interpretation::interpretation(self.id)
.expect("every catalog page has a presentation contract")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn component_inventory_is_complete_and_unique() {
let ids: std::collections::HashSet<_> = COMPONENTS.iter().map(|item| item.id).collect();
assert_eq!(ids.len(), COMPONENTS.len());
assert!(COMPONENTS.len() >= 90);
let grouped: Vec<_> = COMPONENT_GROUPS
.iter()
.flat_map(|(_, ids)| ids.iter().copied())
.collect();
assert_eq!(grouped.len(), COMPONENTS.len());
assert_eq!(
grouped
.iter()
.copied()
.collect::<std::collections::HashSet<_>>(),
ids
);
assert!(
COMPONENTS
.iter()
.all(|item| !item.name.is_empty() && !item.description.is_empty())
);
}
}