[
{
"id": "accordion",
"label": "Accordion",
"category": "Navigation",
"description": "Expandable accordion sections",
"keywords": "",
"pain": "Accordion allows invalid multi-open logic without enforced selection mode",
"promise": "Selection mode enforced via type, preventing invalid open states",
"why": "AccordionSelection defines whether single or multiple items can be open. The primitive encodes state and behavior in data-rs attributes, ensuring consistent disclosure logic. VisibilityState guarantees SSR-safe open/closed state without runtime drift.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"accordion\">\n <button on:click=move |_| toggle(1)>\"Item 1\"</button>\n {if open == 1 { view! { <div>\"Content\"</div> } } else { view! {} }}\n </div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Accordion selection=AccordionSelection::Single>\n <AccordionItem>\n <AccordionTrigger>\"Item 1\"</AccordionTrigger>\n <AccordionContent>\"Content\"</AccordionContent>\n </AccordionItem>\n </Accordion>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"faq sections",
"settings panels"
],
"related": [
"collapsible"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "accordion",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Accordion Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::{VisibilityState, DisabledState};\nuse crate::infra::uid::generate;\n\n#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, PartialEq, Default, Debug)]\npub enum AccordionSelection {\n #[default]\n Single,\n Multiple,\n}\nimpl AccordionSelection {\n pub fn as_str(&self) -> &'static str {\n match self { Self::Single => \"single\", Self::Multiple => \"multiple\" }\n }\n}\n\n#[component]\npub fn AccordionPrimitive(\n children: Children,\n #[prop(default = AccordionSelection::Single)] selection: AccordionSelection,\n #[prop(into, default = \"true\".to_string())] collapsible: String,\n #[prop(into, default = String::new())] class: String,\n #[prop(optional)] node_ref: Option<NodeRef<leptos::html::Div>>,\n) -> impl IntoView {\n let uid = generate(\"ac\");\n view! {\n <div\n data-rs-accordion=\"\"\n data-rs-uid=uid\n data-rs-interaction=\"nav\"\n data-rs-selection=selection.as_str()\n data-rs-collapsible=collapsible\n class=class\n node_ref=node_ref.unwrap_or_default()\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn AccordionItemPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid = generate(\"ac-item\");\n view! {\n <div\n data-rs-accordion-item=\"\"\n data-rs-uid=uid\n data-rs-state=state.as_str()\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n aria-disabled=disabled.aria_disabled()\n role=\"group\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn AccordionTriggerPrimitive(\n children: Children,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <h3 data-rs-accordion-heading=\"\">\n <button\n type=\"button\"\n data-rs-accordion-trigger=\"\"\n data-rs-uid=generate(\"ac-trigger\")\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n aria-expanded=state.aria_expanded()\n aria-disabled=disabled.aria_disabled()\n class=class\n >\n {children()}\n <svg data-rs-accordion-icon=\"\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\">\n <path d=\"m6 9 6 6 6-6\"/>\n </svg>\n </button>\n </h3>\n }\n}\n\n#[component]\npub fn AccordionContentPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid = generate(\"ac-content\");\n view! {\n <div\n data-rs-accordion-content=\"\"\n data-rs-uid=uid\n data-rs-state=state.as_str()\n class=class\n >\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::meta::{VisibilityState, DisabledState};\nuse canonrs_core::primitives::{\n AccordionPrimitive, AccordionItemPrimitive,\n AccordionTriggerPrimitive, AccordionContentPrimitive,\n AccordionSelection,\n};\n\n#[component]\npub fn Accordion(\n children: Children,\n #[prop(default = AccordionSelection::Single)] selection: AccordionSelection,\n #[prop(into, default = \"true\".to_string())] collapsible: String,\n #[prop(optional)] node_ref: Option<NodeRef<leptos::html::Div>>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <AccordionPrimitive selection=selection collapsible=collapsible class=class node_ref=node_ref.unwrap_or_default()>\n {children()}\n </AccordionPrimitive>\n }\n}\n\n#[component]\npub fn AccordionItem(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <AccordionItemPrimitive state=state disabled=disabled class=class>\n {children()}\n </AccordionItemPrimitive>\n }\n}\n\n#[component]\npub fn AccordionTrigger(\n children: Children,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <AccordionTriggerPrimitive disabled=disabled class=class>\n {children()}\n </AccordionTriggerPrimitive>\n }\n}\n\n#[component]\npub fn AccordionContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <AccordionContentPrimitive class=class>\n {children()}\n </AccordionContentPrimitive>\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! Accordion Island — Canon Rule #340 (zero-logic boundary)\n//! CR-342 v4.0.0: interaction delegated to canonrs-interactions-nav\n\nuse leptos::prelude::*;\nuse super::accordion_ui::{\n Accordion as AccordionUi,\n AccordionItem as AccordionItemUi,\n AccordionTrigger as AccordionTriggerUi,\n AccordionContent as AccordionContentUi\n};\npub use canonrs_core::meta::{VisibilityState, DisabledState};\npub use canonrs_core::primitives::AccordionSelection;\n\n#[component]\npub fn Accordion(\n children: Children,\n #[prop(default = AccordionSelection::Single)] selection: AccordionSelection,\n #[prop(into, default = \"true\".to_string())] collapsible: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <AccordionUi selection=selection collapsible=collapsible class=class>{children()}</AccordionUi> }\n}\n\n#[component]\npub fn AccordionItem(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <AccordionItemUi state=state disabled=disabled class=class>{children()}</AccordionItemUi> }\n}\n\n#[component]\npub fn AccordionTrigger(\n children: Children,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <AccordionTriggerUi disabled=disabled class=class>{children()}</AccordionTriggerUi> }\n}\n\n#[component]\npub fn AccordionContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <AccordionContentUi class=class>{children()}</AccordionContentUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{AccordionSelection}; \n\npub const ACCORDION_API: ComponentApi = ComponentApi {\n id: \"accordion\",\n description: \"Expandable accordion sections\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"selection\", kind: PropType::Enum(&[\"single\", \"multiple\"]), required: false, default: Some(\"single\"), description: \"Prop value\" },\n PropDef { name: \"collapsible\", kind: PropType::String, required: false, default: Some(\"true\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const ACCORDIONITEM_API: ComponentApi = ComponentApi {\n id: \"accordion-item\",\n description: \"Expandable accordion sections\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"state\", kind: PropType::String, required: false, default: Some(\"closed\"), description: \"Loading or visibility state\" },\n PropDef { name: \"disabled\", kind: PropType::String, required: false, default: Some(\"enabled\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const ACCORDIONTRIGGER_API: ComponentApi = ComponentApi {\n id: \"accordion-trigger\",\n description: \"Expandable accordion sections\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"disabled\", kind: PropType::String, required: false, default: Some(\"enabled\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const ACCORDIONCONTENT_API: ComponentApi = ComponentApi {\n id: \"accordion-content\",\n description: \"Expandable accordion sections\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::accordion_boundary::{Accordion, AccordionItem, AccordionTrigger, AccordionContent, AccordionSelection, DisabledState};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn AccordionShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <Accordion>\n <AccordionItem>\n <AccordionTrigger>\"What is CanonRS?\"</AccordionTrigger>\n <AccordionContent>\"CanonRS is a design system built in Rust and Leptos with a 3-layer architecture.\"</AccordionContent>\n </AccordionItem>\n <AccordionItem>\n <AccordionTrigger>\"How does it work?\"</AccordionTrigger>\n <AccordionContent>\"Primitives define structure. Behaviors add interactivity. UI components compose both.\"</AccordionContent>\n </AccordionItem>\n <AccordionItem>\n <AccordionTrigger>\"Is SSR supported?\"</AccordionTrigger>\n <AccordionContent>\"Yes. All state is defined at the primitive level via data-rs-state.\"</AccordionContent>\n </AccordionItem>\n </Accordion>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Open/close state governed by DOM — single or multiple selection.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Multiple selection\"</span>\n <Accordion selection=AccordionSelection::Multiple>\n <AccordionItem>\n <AccordionTrigger>\"Section A\"</AccordionTrigger>\n <AccordionContent>\"Content for section A.\"</AccordionContent>\n </AccordionItem>\n <AccordionItem>\n <AccordionTrigger>\"Section B\"</AccordionTrigger>\n <AccordionContent>\"Content for section B.\"</AccordionContent>\n </AccordionItem>\n </Accordion>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Non-collapsible\"</span>\n <Accordion collapsible=\"false\".to_string()>\n <AccordionItem>\n <AccordionTrigger>\"Always one open\"</AccordionTrigger>\n <AccordionContent>\"This accordion always keeps one item open.\"</AccordionContent>\n </AccordionItem>\n <AccordionItem>\n <AccordionTrigger>\"Second item\"</AccordionTrigger>\n <AccordionContent>\"Click to switch — cannot close all.\"</AccordionContent>\n </AccordionItem>\n </Accordion>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Disabled item\"</span>\n <Accordion>\n <AccordionItem>\n <AccordionTrigger>\"Active item\"</AccordionTrigger>\n <AccordionContent>\"This item is interactive.\"</AccordionContent>\n </AccordionItem>\n <AccordionItem disabled=DisabledState::Disabled>\n <AccordionTrigger disabled=DisabledState::Disabled>\"Disabled item\"</AccordionTrigger>\n <AccordionContent>\"This content is not reachable.\"</AccordionContent>\n </AccordionItem>\n </Accordion>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "alert",
"label": "Alert",
"category": "Feedback",
"description": "Alert message box",
"keywords": "",
"pain": "Alerts use wrong ARIA roles causing accessibility issues silently",
"promise": "Semantic state drives ARIA role and live region — variant is visual only",
"why": "Semantic state (error/warning/success) is derived from variant at the primitive level and drives role and aria-live. Visual styling via data-rs-variant is separate from semantic contract. Accessibility is guaranteed at compile-time.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"alert alert-error\">\"Error occurred\"</div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Alert variant=AlertVariant::Destructive>\n <AlertTitle>\"Error\"</AlertTitle>\n <AlertDescription>\"Error occurred\"</AlertDescription>\n </Alert>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"error messages",
"status notifications"
],
"related": [
"toast",
"banner",
"callout",
"inline_notice",
"status_dot"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "feedback",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Alert Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\n\n#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, PartialEq, Default, Debug)]\npub enum AlertVariant {\n #[default]\n Default, Destructive, Warning, Success,\n}\nimpl AlertVariant {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Default => \"default\",\n Self::Destructive => \"destructive\",\n Self::Warning => \"warning\",\n Self::Success => \"success\",\n }\n }\n pub fn role(&self) -> &'static str {\n match self {\n Self::Destructive | Self::Warning => \"alert\",\n _ => \"status\",\n }\n }\n pub fn aria_live(&self) -> &'static str {\n match self {\n Self::Destructive | Self::Warning => \"assertive\",\n _ => \"polite\",\n }\n }\n}\n\n#[component]\npub fn AlertPrimitive(\n children: Children,\n #[prop(default = AlertVariant::Default)] variant: AlertVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid = crate::infra::uid::generate(\"al\");\n view! {\n <div\n data-rs-alert=\"\"\n data-rs-uid=uid\n data-rs-interaction=\"init\"\n data-rs-variant=variant.as_str()\n role=variant.role()\n aria-live=variant.aria_live()\n aria-atomic=\"true\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn AlertTitlePrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <h5 data-rs-alert-title=\"\" class=class>\n {children()}\n </h5>\n }\n}\n\n#[component]\npub fn AlertDescriptionPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-alert-description=\"\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn AlertCloseButtonPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-alert-close=\"\"\n aria-label=\"Close alert\"\n class=class\n >\n {children()}\n </button>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n AlertPrimitive, AlertTitlePrimitive, AlertDescriptionPrimitive,\n AlertCloseButtonPrimitive, AlertVariant,\n};\n\n#[component]\npub fn Alert(\n children: Children,\n #[prop(default = AlertVariant::Default)] variant: AlertVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <AlertPrimitive variant=variant class=class>\n {children()}\n </AlertPrimitive>\n }\n}\n\n#[component]\npub fn AlertTitle(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <AlertTitlePrimitive class=class>\n {children()}\n </AlertTitlePrimitive>\n }\n}\n\n#[component]\npub fn AlertDescription(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <AlertDescriptionPrimitive class=class>\n {children()}\n </AlertDescriptionPrimitive>\n }\n}\n\n#[component]\npub fn AlertCloseButton(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <AlertCloseButtonPrimitive class=class>\n {children()}\n </AlertCloseButtonPrimitive>\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! Alert Island — Canon Rule #340 (zero-logic boundary)\n\nuse leptos::prelude::*;\nuse super::alert_ui::{\n Alert as AlertUi,\n AlertTitle,\n AlertDescription,\n AlertCloseButton\n};\npub use canonrs_core::primitives::AlertVariant;\n\n#[component]\npub fn Alert(\n #[prop(into, optional)] title: Option<String>,\n #[prop(into, optional)] description: Option<String>,\n #[prop(default = AlertVariant::Default)] variant: AlertVariant,\n #[prop(default = false)] dismissible: bool,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <AlertUi variant=variant class=class>\n {title.map(|t| view! { <AlertTitle>{t}</AlertTitle> })}\n {description.map(|d| view! { <AlertDescription>{d}</AlertDescription> })}\n {dismissible.then(|| view! { <AlertCloseButton>\"×\"</AlertCloseButton> })}\n </AlertUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{AlertVariant}; \n\npub const ALERT_API: ComponentApi = ComponentApi {\n id: \"alert\",\n description: \"Alert message box\",\n props: &[\n PropDef { name: \"title\", kind: PropType::String, required: false, default: None, description: \"Title slot or text\" },\n PropDef { name: \"description\", kind: PropType::String, required: false, default: None, description: \"Description slot or text\" },\n PropDef { name: \"variant\", kind: PropType::Enum(&[\"default\", \"destructive\", \"warning\", \"success\"]), required: false, default: Some(\"default\"), description: \"Visual variant of the component\" },\n PropDef { name: \"dismissible\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::alert_boundary::{Alert, AlertVariant};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn AlertShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <Alert title=\"Info\" description=\"This is a default informational alert.\" />\n <p data-rs-showcase-preview-anchor=\"\">\n \"Semantic state (error/warning/success) drives ARIA role and live region. Visual variant is separate from semantic contract.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Variants\"</span>\n <Alert variant=AlertVariant::Success title=\"Success\" description=\"Your changes have been saved.\" />\n <Alert variant=AlertVariant::Warning title=\"Warning\" description=\"Session expires in 5 minutes.\" />\n <Alert variant=AlertVariant::Destructive title=\"Error\" description=\"Failed to save changes.\" />\n <Alert variant=AlertVariant::Default title=\"Info\" description=\"A new version is available.\" />\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Dismissible\"</span>\n <Alert title=\"Update available\" description=\"New version ready.\" dismissible=true />\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "alert_dialog",
"label": "Alert Dialog",
"category": "Overlay",
"description": "Alert dialog for critical confirmations",
"keywords": "",
"pain": "Destructive dialogs lack proper role and accessibility enforcement",
"promise": "Alertdialog role and accessibility guaranteed by component contract",
"why": "AlertDialog reuses Dialog but enforces role=\"alertdialog\" and assertive aria-live. The specialized content primitive ensures critical actions are announced correctly. This prevents misuse of generic dialogs for destructive flows.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"modal\">\n <p>\"Delete account?\"</p>\n <button>\"Confirm\"</button>\n </div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <AlertDialog>\n <AlertDialogTrigger>\"Delete\"</AlertDialogTrigger>\n <AlertDialogPortal>\n <AlertDialogContent>\n <AlertDialogTitle>\"Confirm\"</AlertDialogTitle>\n <AlertDialogDescription>\"This cannot be undone\"</AlertDialogDescription>\n </AlertDialogContent>\n </AlertDialogPortal>\n </AlertDialog>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"delete confirmation",
"critical actions"
],
"related": [
"dialog",
"drawer",
"sheet",
"modal",
"confirm_dialog",
"tooltip",
"hover_card",
"popover"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "overlay",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! AlertDialog Primitive - Reusa Dialog com role=alertdialog\n\npub use super::dialog::{\n DialogPrimitive as AlertDialogPrimitive,\n DialogTriggerPrimitive as AlertDialogTriggerPrimitive,\n DialogPortalPrimitive as AlertDialogPortalPrimitive,\n DialogOverlayPrimitive as AlertDialogOverlayPrimitive,\n DialogTitlePrimitive as AlertDialogTitlePrimitive,\n DialogDescriptionPrimitive as AlertDialogDescriptionPrimitive,\n DialogClosePrimitive as AlertDialogClosePrimitive,\n};\n\nuse leptos::prelude::*;\n\n#[component]\npub fn AlertDialogContentPrimitive(\n children: Children,\n #[prop(into, default = String::new())] aria_labelledby: String,\n #[prop(optional, into)] aria_describedby: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_ad = crate::infra::uid::generate(\"ad\");\n view! {\n <div\n data-rs-dialog-content=\"\"\n data-rs-uid=uid_ad\n data-rs-interaction=\"overlay\"\n role=\"alertdialog\"\n aria-modal=\"true\"\n aria-live=\"assertive\"\n aria-labelledby=aria_labelledby\n aria-describedby=aria_describedby\n tabindex=\"-1\"\n class=class\n >\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n AlertDialogPrimitive,\n AlertDialogPortalPrimitive,\n AlertDialogOverlayPrimitive,\n AlertDialogContentPrimitive,\n AlertDialogTitlePrimitive,\n AlertDialogDescriptionPrimitive,\n};\nuse crate::ui::button::{Button, ButtonVariant};\n\n#[component]\npub fn AlertDialog(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <AlertDialogPrimitive class=class>\n {children()}\n </AlertDialogPrimitive>\n }\n}\n\n#[component]\npub fn AlertDialogTrigger(\n children: Children,\n #[prop(default = ButtonVariant::Primary)] variant: ButtonVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <Button\n variant=variant\n class=class\n attr:data-rs-dialog-trigger=\"\"\n attr:aria-haspopup=\"dialog\"\n attr:aria-expanded=\"false\"\n >\n {children()}\n </Button>\n }\n}\n\n#[component]\npub fn AlertDialogPortal(\n children: ChildrenFn,\n) -> impl IntoView {\n view! {\n <AlertDialogPortalPrimitive>\n {children()}\n </AlertDialogPortalPrimitive>\n }\n}\n\n#[component]\npub fn AlertDialogOverlay(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <AlertDialogOverlayPrimitive class=class />\n }\n}\n\n#[component]\npub fn AlertDialogContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <AlertDialogContentPrimitive class=class aria_labelledby=\"alert-title\">\n {children()}\n </AlertDialogContentPrimitive>\n }\n}\n\n#[component]\npub fn AlertDialogTitle(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <AlertDialogTitlePrimitive class=class>\n {children()}\n </AlertDialogTitlePrimitive>\n }\n}\n\n#[component]\npub fn AlertDialogDescription(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <AlertDialogDescriptionPrimitive class=class>\n {children()}\n </AlertDialogDescriptionPrimitive>\n }\n}\n\n#[component]\npub fn AlertDialogClose(\n children: Children,\n #[prop(default = ButtonVariant::Outline)] variant: ButtonVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <Button\n variant=variant\n class=class\n attr:data-rs-dialog-close=\"\"\n >\n {children()}\n </Button>\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! AlertDialog Island — Canon Rule #340 (zero-logic boundary)\n//! CR-342 v3.0.0: interaction delegated to canonrs-interactions-overlay\n\nuse leptos::prelude::*;\nuse super::alert_dialog_ui::{\n AlertDialog as AlertDialogUi,\n AlertDialogTrigger,\n AlertDialogOverlay,\n AlertDialogContent,\n AlertDialogTitle,\n AlertDialogDescription,\n AlertDialogClose\n};\nuse crate::ui::button::ButtonVariant;\n\n#[component]\npub fn AlertDialog(\n #[prop(optional)] children: Option<Children>,\n #[prop(into, default = String::from(\"Delete\"))] trigger_label: String,\n #[prop(into, default = String::from(\"Confirm\"))] confirm_label: String,\n #[prop(into, default = String::from(\"Cancel\"))] cancel_label: String,\n #[prop(into, optional)] title: Option<String>,\n #[prop(into, optional)] description: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <AlertDialogUi class=class>\n <AlertDialogTrigger variant=ButtonVariant::Destructive>\n {trigger_label\n};\n </AlertDialogTrigger>\n <AlertDialogOverlay />\n <AlertDialogContent>\n {title.map(|t| view! { <AlertDialogTitle>{t}</AlertDialogTitle> })}\n {description.map(|d| view! { <AlertDialogDescription>{d}</AlertDialogDescription> })}\n {children.map(|c| c())}\n <div data-rs-alert-dialog-actions=\"\">\n <AlertDialogClose variant=ButtonVariant::Outline>\n {cancel_label}\n </AlertDialogClose>\n <AlertDialogClose variant=ButtonVariant::Destructive>\n {confirm_label}\n </AlertDialogClose>\n </div>\n </AlertDialogContent>\n </AlertDialogUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const ALERTDIALOG_API: ComponentApi = ComponentApi {\n id: \"alert-dialog\",\n description: \"Alert dialog for critical confirmations\",\n props: &[\n PropDef { name: \"trigger_label\", kind: PropType::String, required: false, default: Some(\"Delete\"), description: \"Prop value\" },\n PropDef { name: \"confirm_label\", kind: PropType::String, required: false, default: Some(\"Confirm\"), description: \"Prop value\" },\n PropDef { name: \"cancel_label\", kind: PropType::String, required: false, default: Some(\"Cancel\"), description: \"Prop value\" },\n PropDef { name: \"title\", kind: PropType::String, required: false, default: None, description: \"Title slot or text\" },\n PropDef { name: \"description\", kind: PropType::String, required: false, default: None, description: \"Description slot or text\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::alert_dialog_boundary::AlertDialog;\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn AlertDialogShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <AlertDialog\n trigger_label=\"Delete Account\"\n title=\"Are you absolutely sure?\"\n description=\"This action cannot be undone. This will permanently delete your account.\"\n confirm_label=\"Delete\"\n cancel_label=\"Cancel\"\n />\n <p data-rs-showcase-preview-anchor=\"\">\n \"Alert dialog enforces destructive action confirmation via ARIA alertdialog.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Custom trigger\"</span>\n <AlertDialog\n trigger_label=\"Remove item\"\n title=\"Remove this item?\"\n description=\"This item will be permanently removed from your list.\"\n confirm_label=\"Remove\"\n cancel_label=\"Keep it\"\n />\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"center",
"stack"
]
},
{
"id": "animate",
"label": "Animate",
"category": "Display",
"description": "Animation wrapper component",
"keywords": "",
"pain": "Animations rely on fragile class names and inconsistent timing values",
"promise": "Animation type and easing enforced through typed enums",
"why": "AnimationName and AnimationEasing define allowed motion patterns. The primitive encodes animation parameters into data-rs attributes, avoiding class-based drift. This ensures consistent animation behavior across SSR and client.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"fade-in ease-in-out duration-300\">\"Content\"</div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Animate animation=AnimationName::FadeIn duration=\"300ms\">\n \"Content\"\n </Animate>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"page transitions",
"modal animations"
],
"related": [
"empty_state",
"error_state"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "feedback_state",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Animate Primitive - HTML puro + data-attributes de animacao\n\nuse leptos::prelude::*;\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum AnimationName {\n #[default]\n None,\n FadeIn,\n FadeOut,\n SlideIn,\n SlideOut,\n ScaleIn,\n ScaleOut,\n}\nimpl AnimationName {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::None => \"none\",\n Self::FadeIn => \"fade-in\",\n Self::FadeOut => \"fade-out\",\n Self::SlideIn => \"slide-in\",\n Self::SlideOut => \"slide-out\",\n Self::ScaleIn => \"scale-in\",\n Self::ScaleOut => \"scale-out\",\n }\n }\n}\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum AnimationEasing {\n #[default]\n EaseInOut,\n EaseIn,\n EaseOut,\n Linear,\n}\nimpl AnimationEasing {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::EaseInOut => \"ease-in-out\",\n Self::EaseIn => \"ease-in\",\n Self::EaseOut => \"ease-out\",\n Self::Linear => \"linear\",\n }\n }\n}\n\n#[component]\npub fn AnimatePrimitive(\n children: Children,\n #[prop(default = AnimationName::None)] animation: AnimationName,\n #[prop(default = AnimationEasing::EaseInOut)] easing: AnimationEasing,\n #[prop(into, default = String::new())] duration: String,\n #[prop(into, default = String::new())] delay: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_an = crate::infra::uid::generate(\"an\");\n view! {\n <div\n data-rs-animate=\"\"\n data-rs-uid=uid_an\n data-rs-interaction=\"init\"\n data-rs-animation=animation.as_str()\n data-rs-easing=easing.as_str()\n data-rs-duration=duration\n data-rs-delay=delay\n class=class\n >\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::AnimatePrimitive;\npub use canonrs_core::primitives::{AnimationName, AnimationEasing};\n\n#[component]\npub fn Animate(\n children: Children,\n #[prop(default = AnimationName::FadeIn)] animation: AnimationName,\n #[prop(default = AnimationEasing::EaseInOut)] easing: AnimationEasing,\n #[prop(into, default = String::new())] duration: String,\n #[prop(into, default = String::new())] delay: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <AnimatePrimitive\n animation=animation\n easing=easing\n duration=duration\n delay=delay\n class=class\n >\n {children()}\n </AnimatePrimitive>\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! Animate Island — Canon Rule #340 (zero-logic boundary)\n\nuse leptos::prelude::*;\nuse super::animate_ui::{\n Animate as AnimateUi,\n AnimationName,\n AnimationEasing\n};\n\n#[component]\npub fn Animate(\n children: Children,\n #[prop(into, default = String::from(\"fade-in\"))] animation: String,\n #[prop(into, default = String::from(\"ease-in-out\"))] easing: String,\n #[prop(into, default = String::from(\"300ms\"))] duration: String,\n #[prop(into, default = String::new())] delay: String,\n #[prop(optional)] stagger: Option<f64>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let animation_val = match animation.as_str() {\n \"fade-out\" => AnimationName::FadeOut,\n \"slide-in\" => AnimationName::SlideIn,\n \"slide-out\" => AnimationName::SlideOut,\n \"scale-in\" => AnimationName::ScaleIn,\n \"scale-out\" => AnimationName::ScaleOut,\n _ => AnimationName::FadeIn,\n };\n let easing_val = match easing.as_str() {\n \"ease-in\" => AnimationEasing::EaseIn,\n \"ease-out\" => AnimationEasing::EaseOut,\n \"linear\" => AnimationEasing::Linear,\n _ => AnimationEasing::EaseInOut,\n };\n let _ = stagger; // handled by init module\n view! {\n <AnimateUi animation=animation_val easing=easing_val duration=duration delay=delay class=class>\n {children()}\n </AnimateUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const ANIMATE_API: ComponentApi = ComponentApi {\n id: \"animate\",\n description: \"Animation wrapper component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"animation\", kind: PropType::String, required: false, default: Some(\"fade-in\"), description: \"Prop value\" },\n PropDef { name: \"easing\", kind: PropType::String, required: false, default: Some(\"ease-in-out\"), description: \"Prop value\" },\n PropDef { name: \"duration\", kind: PropType::String, required: false, default: Some(\"300ms\"), description: \"Prop value\" },\n PropDef { name: \"delay\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Prop value\" },\n PropDef { name: \"stagger\", kind: PropType::Number, required: false, default: None, description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::animate_boundary::Animate;\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\nuse canonrs_core::primitives::layout::grid::{GridPrimitive as Grid, GridCols};\n\n#[component]\npub fn AnimateShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg attr:data-rs-showcase-preview-hero=\"\">\n <Animate animation=\"fade-in\" duration=\"1.2s\">\n <div data-rs-animate-demo=\"\">\"Fade In\"</div>\n </Animate>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Animation type and easing enforced through typed enums. Respects prefers-reduced-motion.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Animation variants\"</span>\n <Grid cols=GridCols::Three>\n <Animate animation=\"fade-in\" duration=\"1.4s\"><div data-rs-animate-demo=\"\">\"FadeIn\"</div></Animate>\n <Animate animation=\"fade-out\" duration=\"1.4s\"><div data-rs-animate-demo=\"\">\"FadeOut\"</div></Animate>\n <Animate animation=\"slide-in\" duration=\"1.4s\"><div data-rs-animate-demo=\"\">\"SlideIn\"</div></Animate>\n <Animate animation=\"slide-out\" duration=\"1.4s\"><div data-rs-animate-demo=\"\">\"SlideOut\"</div></Animate>\n <Animate animation=\"scale-in\" duration=\"1.4s\"><div data-rs-animate-demo=\"\">\"ScaleIn\"</div></Animate>\n <Animate animation=\"scale-out\" duration=\"1.4s\"><div data-rs-animate-demo=\"\">\"ScaleOut\"</div></Animate>\n </Grid>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Easing\"</span>\n <Grid cols=GridCols::Four>\n <Animate animation=\"slide-in\" easing=\"ease-in\" duration=\"1.6s\"><div data-rs-animate-demo=\"\">\"EaseIn\"</div></Animate>\n <Animate animation=\"slide-in\" easing=\"ease-out\" duration=\"1.6s\"><div data-rs-animate-demo=\"\">\"EaseOut\"</div></Animate>\n <Animate animation=\"slide-in\" easing=\"ease-in-out\" duration=\"1.6s\"><div data-rs-animate-demo=\"\">\"EaseInOut\"</div></Animate>\n <Animate animation=\"slide-in\" easing=\"linear\" duration=\"1.6s\"><div data-rs-animate-demo=\"\">\"Linear\"</div></Animate>\n </Grid>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Stagger\"</span>\n <Animate animation=\"fade-in\" duration=\"0.6s\" stagger=100.0>\n <div data-rs-animate-demo=\"\">\"Item 1\"</div>\n <div data-rs-animate-demo=\"\">\"Item 2\"</div>\n <div data-rs-animate-demo=\"\">\"Item 3\"</div>\n </Animate>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack",
"grid"
]
},
{
"id": "aspect_ratio",
"label": "Aspect Ratio",
"category": "Display",
"description": "Aspect ratio container",
"keywords": "",
"pain": "Aspect ratios break on resize causing layout shift and inconsistent rendering",
"promise": "Aspect ratio enforced structurally with no layout drift",
"why": "AspectRatioPrimitive encodes width/height ratio in data attributes. The structure guarantees consistent layout regardless of content size. This eliminates runtime calculations and ensures SSR-safe rendering.\n",
"before": "// ❌ Typical\nview! {\n <div style=\"position:relative;padding-top:56.25%\">\n <img src=\"img.png\" style=\"position:absolute;width:100%;height:100%\" />\n </div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <AspectRatio ratio_w=16.0 ratio_h=9.0>\n <img src=\"img.png\" />\n </AspectRatio>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"video containers",
"image previews"
],
"related": [
"card",
"resizable",
"scroll_area",
"page_header",
"toolbar",
"separator"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "layout",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! AspectRatio Primitive - HTML puro\n\nuse leptos::prelude::*;\n\n#[component]\npub fn AspectRatioPrimitive(\n children: Children,\n #[prop(default = 16.0f32)] ratio_w: f32,\n #[prop(default = 9.0f32)] ratio_h: f32,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_ar = crate::infra::uid::generate(\"ar\");\n let ratio = format!(\"{}/{}\", ratio_w, ratio_h);\n let ratio_style = format!(\"aspect-ratio:{}/{}\", ratio_w, ratio_h);\n view! {\n <div\n data-rs-aspect-ratio=\"\"\n data-rs-uid=uid_ar\n data-rs-ratio=ratio\n style=ratio_style\n class=class\n >\n <div data-rs-aspect-ratio-content=\"\">\n {children()}\n </div>\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::AspectRatioPrimitive;\n\n#[component]\npub fn AspectRatio(\n children: Children,\n #[prop(default = 16.0f32)] ratio_w: f32,\n #[prop(default = 9.0f32)] ratio_h: f32,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <AspectRatioPrimitive\n ratio_w=ratio_w\n ratio_h=ratio_h\n class=class\n >\n {children()}\n </AspectRatioPrimitive>\n }\n}\n\n",
"boundary_src": "//! AspectRatio Island — Canon Rule #340\n//! Passthrough only. Zero logic, zero transformation.\n\nuse leptos::prelude::*;\nuse super::aspect_ratio_ui::AspectRatio as AspectRatioUi;\n\n#[component]\npub fn AspectRatio(\n children: Children,\n #[prop(default = 16.0f32)] ratio_w: f32,\n #[prop(default = 9.0f32)] ratio_h: f32,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <AspectRatioUi ratio_w=ratio_w ratio_h=ratio_h class=class>\n {children()\n};\n </AspectRatioUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const ASPECTRATIO_API: ComponentApi = ComponentApi {\n id: \"aspect-ratio\",\n description: \"Aspect ratio container\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"ratio_w\", kind: PropType::Number, required: false, default: Some(\"16.0f32\"), description: \"Prop value\" },\n PropDef { name: \"ratio_h\", kind: PropType::Number, required: false, default: Some(\"9.0f32\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::aspect_ratio_boundary::AspectRatio;\nuse canonrs_core::primitives::layout::grid::{GridPrimitive as Grid, GridCols};\n\n#[component]\npub fn AspectRatioShowcasePreview() -> impl IntoView {\n view! {\n <Grid cols=GridCols::One>\n <AspectRatio ratio_w=16.0f32 ratio_h=9.0f32>\n <div data-rs-aspect-demo=\"\">\"16 / 9\"</div>\n </AspectRatio>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Aspect ratio enforced structurally with no layout drift.\"\n </p>\n <span data-rs-showcase-preview-label=\"\">\"Ratios\"</span>\n <Grid cols=GridCols::Three>\n <AspectRatio ratio_w=4.0f32 ratio_h=3.0f32><div data-rs-aspect-demo=\"\">\"4 / 3\"</div></AspectRatio>\n <AspectRatio ratio_w=1.0f32 ratio_h=1.0f32><div data-rs-aspect-demo=\"\">\"1 / 1\"</div></AspectRatio>\n <AspectRatio ratio_w=21.0f32 ratio_h=9.0f32><div data-rs-aspect-demo=\"\">\"21 / 9\"</div></AspectRatio>\n </Grid>\n </Grid>\n }\n}\n",
"block": [],
"blocks_primitives": [
"grid"
]
},
{
"id": "avatar",
"label": "Avatar",
"category": "Display",
"description": "User avatar image",
"keywords": "",
"pain": "Avatar fallback logic breaks when image fails to load",
"promise": "Image and fallback visibility controlled by state system",
"why": "AvatarImage and AvatarFallback use VisibilityState to control rendering. The system ensures fallback is shown when image is unavailable. This avoids manual conditional logic and guarantees consistent behavior.\n",
"before": "// ❌ Typical\nview! {\n <img src=\"user.png\" on:error=move |_| show_fallback() />\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Avatar>\n <AvatarImage src=\"user.png\" alt=\"User\" />\n <AvatarFallback>\"AB\"</AvatarFallback>\n </Avatar>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"user profile",
"team lists"
],
"related": [
"icon",
"logo",
"code_block",
"markdown",
"chart",
"stat",
"inline_meta",
"kbd",
"badge",
"carousel"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "content_display",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Avatar Primitive - HTML puro\n\nuse leptos::prelude::*;\nuse crate::meta::VisibilityState;\n\n#[component]\npub fn AvatarPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] size: String,\n #[prop(into, default = String::new())] shape: String,\n #[prop(into, default = String::new())] status: String,\n) -> impl IntoView {\n let uid = crate::infra::uid::generate(\"av\");\n view! {\n <span\n data-rs-avatar=\"\"\n data-rs-uid=uid\n data-rs-interaction=\"init\"\n data-rs-size=size\n data-rs-shape=shape\n data-rs-status=status\n class=class\n >\n {children()}\n </span>\n }\n}\n\n#[component]\npub fn AvatarImagePrimitive(\n #[prop(into)] src: String,\n #[prop(into)] alt: String,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = VisibilityState::Open)] state: VisibilityState,\n) -> impl IntoView {\n let uid = crate::infra::uid::generate(\"av-img\");\n view! {\n <img\n data-rs-avatar-image=\"\"\n data-rs-uid=uid\n data-rs-state=state.as_str()\n src=src\n alt=alt\n class=class\n />\n }\n}\n\n#[component]\npub fn AvatarFallbackPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n) -> impl IntoView {\n let uid = crate::infra::uid::generate(\"av-fb\");\n view! {\n <span\n data-rs-avatar-fallback=\"\"\n data-rs-uid=uid\n data-rs-state=state.as_str()\n aria-hidden=state.aria_hidden()\n class=class\n >\n {children()}\n </span>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{AvatarPrimitive, AvatarImagePrimitive, AvatarFallbackPrimitive};\nuse canonrs_core::StatusDotVariant;\n\n#[derive(Clone, Copy, PartialEq, Debug)]\npub enum AvatarSize { Xs, Sm, Md, Lg, Xl }\nimpl AvatarSize {\n pub fn as_str(&self) -> &'static str {\n match self { Self::Xs=>\"xs\", Self::Sm=>\"sm\", Self::Md=>\"md\", Self::Lg=>\"lg\", Self::Xl=>\"xl\" }\n }\n}\n\n#[derive(Clone, Copy, PartialEq, Debug)]\npub enum AvatarShape { Circle, Square, Rounded }\nimpl AvatarShape {\n pub fn as_str(&self) -> &'static str {\n match self { Self::Circle=>\"circle\", Self::Square=>\"square\", Self::Rounded=>\"rounded\" }\n }\n}\n\n#[derive(Clone, Copy, PartialEq, Debug)]\npub enum AvatarStatus { Online, Offline, Busy, Away }\nimpl AvatarStatus {\n pub fn to_variant(&self) -> StatusDotVariant {\n match self {\n Self::Online => StatusDotVariant::Online,\n Self::Offline => StatusDotVariant::Offline,\n Self::Busy => StatusDotVariant::Busy,\n Self::Away => StatusDotVariant::Away,\n }\n }\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Online => \"online\",\n Self::Offline => \"offline\",\n Self::Busy => \"busy\",\n Self::Away => \"away\",\n }\n }\n}\n\n#[component]\npub fn Avatar(\n children: Children,\n #[prop(default = AvatarSize::Md)] size: AvatarSize,\n #[prop(default = AvatarShape::Circle)] shape: AvatarShape,\n #[prop(optional)] status: Option<AvatarStatus>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let status_str = status.as_ref().map(|s| s.as_str().to_string()).unwrap_or_default();\n view! {\n <AvatarPrimitive\n status=status_str\n size=size.as_str().to_string()\n shape=shape.as_str().to_string()\n class=class\n >\n {children()}\n </AvatarPrimitive>\n }\n}\n\n#[component]\npub fn AvatarImage(\n src: String,\n alt: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <AvatarImagePrimitive src={src} alt={alt} class={class} />\n }\n}\n\n#[component]\npub fn AvatarFallback(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <AvatarFallbackPrimitive class={class}>\n {children()}\n </AvatarFallbackPrimitive>\n }\n}\n\n",
"boundary_src": "//! Avatar Boundary — Canon Rule #340 (zero-logic boundary)\n\nuse leptos::prelude::*;\nuse super::avatar_ui::{\n Avatar as AvatarUi,\n AvatarImage as AvatarImageUi,\n AvatarFallback as AvatarFallbackUi,\n};\npub use super::avatar_ui::{AvatarSize, AvatarShape, AvatarStatus};\n\n#[component]\npub fn Avatar(\n children: Children,\n #[prop(default = AvatarSize::Md)] size: AvatarSize,\n #[prop(default = AvatarShape::Circle)] shape: AvatarShape,\n #[prop(optional)] status: Option<AvatarStatus>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <AvatarUi size=size shape=shape status=status.unwrap_or(AvatarStatus::Offline) class=class>\n {children()}\n </AvatarUi>\n }\n}\n\n#[component]\npub fn AvatarImage(\n #[prop(into)] src: String,\n #[prop(into)] alt: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <AvatarImageUi src=src alt=alt class=class /> }\n}\n\n#[component]\npub fn AvatarFallback(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <AvatarFallbackUi class=class>{children()}</AvatarFallbackUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const AVATAR_API: ComponentApi = ComponentApi {\n id: \"avatar\",\n description: \"User avatar image\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"size\", kind: PropType::String, required: false, default: Some(\"md\"), description: \"Size variant of the component\" },\n PropDef { name: \"shape\", kind: PropType::String, required: false, default: Some(\"circle\"), description: \"Prop value\" },\n PropDef { name: \"status\", kind: PropType::String, required: false, default: None, description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const AVATARIMAGE_API: ComponentApi = ComponentApi {\n id: \"avatar-image\",\n description: \"User avatar image\",\n props: &[\n PropDef { name: \"src\", kind: PropType::String, required: true, default: None, description: \"Prop value\" },\n PropDef { name: \"alt\", kind: PropType::String, required: true, default: None, description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const AVATARFALLBACK_API: ComponentApi = ComponentApi {\n id: \"avatar-fallback\",\n description: \"User avatar image\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::avatar_boundary::{Avatar, AvatarImage, AvatarFallback};\nuse super::avatar_boundary::{AvatarSize, AvatarShape, AvatarStatus};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn AvatarShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Md>\n <Avatar status=AvatarStatus::Online>\n <AvatarFallback>\"AB\"</AvatarFallback>\n </Avatar>\n <Avatar shape=AvatarShape::Circle size=AvatarSize::Lg status=AvatarStatus::Online>\n <AvatarImage src=\"/assets/avatar_canonrs.webp\".to_string() alt=\"User\".to_string() />\n <AvatarFallback>\"CD\"</AvatarFallback>\n </Avatar>\n </Stack>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Image and fallback visibility controlled by state system.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Sizes\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Md>\n <Avatar size=AvatarSize::Xs><AvatarFallback>\"XS\"</AvatarFallback></Avatar>\n <Avatar size=AvatarSize::Sm><AvatarFallback>\"SM\"</AvatarFallback></Avatar>\n <Avatar size=AvatarSize::Md><AvatarFallback>\"MD\"</AvatarFallback></Avatar>\n <Avatar size=AvatarSize::Lg><AvatarFallback>\"LG\"</AvatarFallback></Avatar>\n <Avatar size=AvatarSize::Xl><AvatarFallback>\"XL\"</AvatarFallback></Avatar>\n </Stack>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Shapes\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Md>\n <Avatar shape=AvatarShape::Circle> <AvatarFallback>\"CI\"</AvatarFallback></Avatar>\n <Avatar shape=AvatarShape::Rounded><AvatarFallback>\"RO\"</AvatarFallback></Avatar>\n <Avatar shape=AvatarShape::Square> <AvatarFallback>\"SQ\"</AvatarFallback></Avatar>\n </Stack>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Status\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Md>\n <Avatar status=AvatarStatus::Online> <AvatarFallback>\"ON\"</AvatarFallback></Avatar>\n <Avatar status=AvatarStatus::Busy> <AvatarFallback>\"BU\"</AvatarFallback></Avatar>\n <Avatar status=AvatarStatus::Away> <AvatarFallback>\"AW\"</AvatarFallback></Avatar>\n <Avatar status=AvatarStatus::Offline><AvatarFallback>\"OF\"</AvatarFallback></Avatar>\n </Stack>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "badge",
"label": "Badge",
"category": "Display",
"description": "Status badge label",
"keywords": "",
"pain": "Badges mix interactive and static behavior without clear intent",
"promise": "Interactivity explicitly defined and enforced by type",
"why": "BadgeInteractivity defines whether the badge is static or interactive. The primitive encodes this into data attributes, preventing misuse. This ensures consistent semantics and avoids accidental clickable badges.\n",
"before": "// ❌ Typical\nview! {\n <span class=\"badge clickable\">\"New\"</span>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Badge interactivity=BadgeInteractivity::Static>\n \"New\"\n </Badge>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"status labels",
"notifications"
],
"related": [
"avatar",
"icon",
"logo",
"code_block",
"markdown",
"chart",
"stat",
"inline_meta",
"kbd",
"carousel"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "content_display",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Badge Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\n\n#[derive(Debug, Clone, Copy, PartialEq, Default)]\npub enum BadgeVariant {\n #[default]\n Default,\n Primary,\n Success,\n Warning,\n Destructive,\n Outline,\n}\n\nimpl BadgeVariant {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Default => \"default\",\n Self::Primary => \"primary\",\n Self::Success => \"success\",\n Self::Warning => \"warning\",\n Self::Destructive => \"destructive\",\n Self::Outline => \"outline\",\n }\n }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Default)]\npub enum BadgeInteractivity {\n #[default]\n Static,\n Interactive,\n}\n\nimpl BadgeInteractivity {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Static => \"static\",\n Self::Interactive => \"interactive\",\n }\n }\n}\n\n#[component]\npub fn BadgePrimitive(\n children: Children,\n #[prop(default = BadgeVariant::Default)] variant: BadgeVariant,\n #[prop(default = BadgeInteractivity::Static)] interactivity: BadgeInteractivity,\n #[prop(into, optional)] aria_label: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_bdg = crate::infra::uid::generate(\"bdg\");\n view! {\n <span\n data-rs-badge=\"\"\n data-rs-uid=uid_bdg\n data-rs-variant=variant.as_str()\n data-rs-interactivity=interactivity.as_str()\n aria-label=aria_label\n class=class\n >\n {children()}\n </span>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{BadgePrimitive, BadgeInteractivity};\npub use canonrs_core::primitives::BadgeVariant;\n\n#[component]\npub fn Badge(\n children: Children,\n #[prop(default = BadgeVariant::Default)] variant: BadgeVariant,\n #[prop(default = BadgeInteractivity::Static)] interactivity: BadgeInteractivity,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <BadgePrimitive variant=variant interactivity=interactivity class=class>\n {children()}\n </BadgePrimitive>\n }\n}\n\n",
"boundary_src": "use leptos::prelude::*;\nuse super::badge_ui::Badge as BadgeUi;\npub use canonrs_core::primitives::{\n BadgeVariant,\n BadgeInteractivity\n};\n\n#[component]\npub fn Badge(\n children: Children,\n #[prop(default = BadgeVariant::Default)] variant: BadgeVariant,\n #[prop(default = BadgeInteractivity::Static)] interactivity: BadgeInteractivity,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = false)] hidden: bool,\n) -> impl IntoView {\n view! {\n <BadgeUi variant=variant interactivity=interactivity class=class attr:hidden=hidden.then(|| \"\")>\n {children()}\n </BadgeUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{BadgeVariant, BadgeInteractivity}; \n\npub const BADGE_API: ComponentApi = ComponentApi {\n id: \"badge\",\n description: \"Status badge label\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"variant\", kind: PropType::Enum(&[\"default\", \"primary\", \"success\", \"warning\", \"destructive\", \"outline\"]), required: false, default: Some(\"default\"), description: \"Visual variant of the component\" },\n PropDef { name: \"interactivity\", kind: PropType::Enum(&[\"static\", \"interactive\"]), required: false, default: Some(\"static\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n PropDef { name: \"hidden\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::badge_boundary::{Badge, BadgeVariant, BadgeInteractivity};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn BadgeShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <Badge variant=BadgeVariant::Success>\"Active\"</Badge>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Interactivity explicitly defined and enforced by type.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Variants\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Sm>\n <Badge>\"Default\"</Badge>\n <Badge variant=BadgeVariant::Primary>\"Primary\"</Badge>\n <Badge variant=BadgeVariant::Success>\"Success\"</Badge>\n <Badge variant=BadgeVariant::Warning>\"Warning\"</Badge>\n <Badge variant=BadgeVariant::Destructive>\"Destructive\"</Badge>\n <Badge variant=BadgeVariant::Outline>\"Outline\"</Badge>\n </Stack>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Interactivity\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Sm>\n <Badge>\"Static\"</Badge>\n <Badge interactivity=BadgeInteractivity::Interactive>\"Interactive\"</Badge>\n </Stack>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Count / status examples\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Sm>\n <Badge variant=BadgeVariant::Primary>\"12\"</Badge>\n <Badge variant=BadgeVariant::Warning>\"Pending\"</Badge>\n <Badge variant=BadgeVariant::Destructive>\"Failed\"</Badge>\n <Badge variant=BadgeVariant::Outline>\"Draft\"</Badge>\n <Badge variant=BadgeVariant::Success>\"Published\"</Badge>\n </Stack>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "banner",
"label": "Banner",
"category": "Feedback",
"description": "Banner message",
"keywords": "",
"pain": "Banner messages lack consistent visibility and accessibility behavior",
"promise": "Visibility and ARIA behavior enforced by state and variant",
"why": "BannerVariant controls semantic role and aria-live behavior. VisibilityState ensures correct open/hidden state without runtime logic. This guarantees accessible, consistent page-level messaging.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"banner\">\"Maintenance scheduled\"</div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Banner>\n <BannerContent>\"Maintenance scheduled\"</BannerContent>\n </Banner>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"system announcements",
"warnings"
],
"related": [
"toast",
"alert",
"callout",
"inline_notice",
"status_dot"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "feedback",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Banner Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::VisibilityState;\n\n#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Default)]\npub enum BannerVariant {\n #[default]\n Info, Success, Warning, Error,\n}\nimpl BannerVariant {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Info => \"info\",\n Self::Success => \"success\",\n Self::Warning => \"warning\",\n Self::Error => \"error\",\n }\n }\n pub fn role(&self) -> &'static str {\n match self {\n Self::Error | Self::Warning => \"alert\",\n _ => \"region\",\n }\n }\n pub fn aria_live(&self) -> &'static str {\n match self {\n Self::Error | Self::Warning => \"assertive\",\n _ => \"polite\",\n }\n }\n pub fn aria_label(&self) -> &'static str {\n match self {\n Self::Error => \"Error notification\",\n Self::Warning => \"Warning notification\",\n Self::Success => \"Success notification\",\n Self::Info => \"System notification\",\n }\n }\n}\n\n#[component]\npub fn BannerPrimitive(\n children: Children,\n #[prop(default = BannerVariant::Info)] variant: BannerVariant,\n #[prop(default = VisibilityState::Open)] visibility: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid = crate::infra::uid::generate(\"bn\");\n view! {\n <div\n data-rs-banner=\"\"\n data-rs-uid=uid\n data-rs-interaction=\"dismiss\"\n data-rs-variant=variant.as_str()\n data-rs-state=visibility.as_str()\n role=variant.role()\n aria-live=variant.aria_live()\n aria-label=variant.aria_label()\n aria-atomic=\"true\"\n hidden=visibility.hidden()\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn BannerClosePrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-banner-close=\"\"\n aria-label=\"Close banner\"\n class=class\n >\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn BannerContentPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-banner-content=\"\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn BannerActionsPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-banner-actions=\"\" class=class>\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n BannerPrimitive, BannerClosePrimitive,\n BannerContentPrimitive, BannerActionsPrimitive,\n};\npub use canonrs_core::primitives::BannerVariant;\n\n#[component]\npub fn Banner(\n children: Children,\n #[prop(default = BannerVariant::Info)] variant: BannerVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <BannerPrimitive variant=variant class=class>\n {children()}\n </BannerPrimitive>\n }\n}\n\n#[component]\npub fn BannerContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <BannerContentPrimitive class=class>\n {children()}\n </BannerContentPrimitive>\n }\n}\n\n#[component]\npub fn BannerActions(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <BannerActionsPrimitive class=class>\n {children()}\n </BannerActionsPrimitive>\n }\n}\n\n#[component]\npub fn BannerClose(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <BannerClosePrimitive class=class>\n {children()}\n </BannerClosePrimitive>\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! Banner Island — Canon Rule #340 (zero-logic boundary)\n\nuse leptos::prelude::*;\nuse super::banner_ui::{\n Banner as BannerUi,\n BannerClose,\n BannerContent\n};\npub use canonrs_core::primitives::BannerVariant;\n\n#[component]\npub fn Banner(\n #[prop(into, optional)] content: Option<String>,\n #[prop(default = BannerVariant::Info)] variant: BannerVariant,\n #[prop(default = true)] dismissible: bool,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <BannerUi variant=variant class=class>\n <BannerContent>{content}</BannerContent>\n {dismissible.then(|| view! { <BannerClose>\"×\"</BannerClose> })}\n </BannerUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{BannerVariant}; \n\npub const BANNER_API: ComponentApi = ComponentApi {\n id: \"banner\",\n description: \"Banner message\",\n props: &[\n PropDef { name: \"content\", kind: PropType::String, required: false, default: None, description: \"Content region slot\" },\n PropDef { name: \"variant\", kind: PropType::Enum(&[\"info\", \"success\", \"warning\", \"error\"]), required: false, default: Some(\"info\"), description: \"Visual variant of the component\" },\n PropDef { name: \"dismissible\", kind: PropType::Bool, required: false, default: Some(\"true\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::banner_boundary::{Banner, BannerVariant};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn BannerShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <Banner content=\"System maintenance scheduled for Saturday at 2am UTC.\" dismissible=true />\n <p data-rs-showcase-preview-anchor=\"\">\n \"Visibility and ARIA behavior enforced by state and variant.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Variants\"</span>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <Banner variant=BannerVariant::Success content=\"Your account has been verified.\" dismissible=true />\n <Banner variant=BannerVariant::Warning content=\"Your subscription expires in 3 days.\" dismissible=true />\n <Banner variant=BannerVariant::Error content=\"Payment failed. Please update billing.\" dismissible=true />\n </Stack>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"container",
"stack"
]
},
{
"id": "breadcrumb",
"label": "Breadcrumb",
"category": "Navigation",
"description": "Navigation breadcrumb trail",
"keywords": "",
"pain": "Breadcrumbs fail to mark current page correctly for accessibility",
"promise": "Current page state enforced via activity state mapping",
"why": "ActivityState defines whether a breadcrumb link is active. The primitive maps this to aria-current automatically. This ensures correct navigation semantics without manual ARIA handling.\n",
"before": "// ❌ Typical\nview! {\n <nav>\n <a href=\"#\">Home</a>\n <span>\"/\"</span>\n <a class=\"active\">Page</a>\n </nav>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Breadcrumb>\n <BreadcrumbItem>\n <BreadcrumbLink href=\"#\">\"Home\"</BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator>\"/\"</BreadcrumbSeparator>\n <BreadcrumbItem>\n <BreadcrumbPage>\"Page\"</BreadcrumbPage>\n </BreadcrumbItem>\n </Breadcrumb>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"navigation trails",
"hierarchy display"
],
"related": [
"navigation_menu",
"sidebar",
"nav_item",
"pagination",
"link_group"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "navigation",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Breadcrumb Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::ActivityState;\n\n#[component]\npub fn BreadcrumbPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_bc = crate::infra::uid::generate(\"bc\");\n view! {\n <nav\n data-rs-breadcrumb=\"\"\n data-rs-uid=uid_bc\n data-rs-interaction=\"nav\"\n aria-label=\"Breadcrumb\"\n class=class\n >\n {children()}\n </nav>\n }\n}\n\n#[component]\npub fn BreadcrumbItemPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <span data-rs-breadcrumb-item=\"\" class=class>\n {children()}\n </span>\n }\n}\n\n#[component]\npub fn BreadcrumbLinkPrimitive(\n children: Children,\n #[prop(into, default = String::new())] href: String,\n #[prop(default = ActivityState::Inactive)] state: ActivityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let aria_current = if state == ActivityState::Active { Some(\"page\") } else { None };\n view! {\n <a\n data-rs-breadcrumb-link=\"\"\n data-rs-activity=state.as_str()\n href=href\n aria-current=aria_current\n class=class\n >\n {children()}\n </a>\n }\n}\n\n#[component]\npub fn BreadcrumbSeparatorPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <span data-rs-breadcrumb-separator=\"\" aria-hidden=\"true\" class=class>\n {children()}\n </span>\n }\n}\n\n#[component]\npub fn BreadcrumbEllipsisPrimitive(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <span data-rs-breadcrumb-ellipsis=\"\" aria-hidden=\"true\" class=class>\n \"...\"\n </span>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n BreadcrumbPrimitive, BreadcrumbItemPrimitive, BreadcrumbLinkPrimitive, BreadcrumbSeparatorPrimitive, BreadcrumbEllipsisPrimitive,\n};\nuse canonrs_core::meta::ActivityState;\n\n#[component]\npub fn Breadcrumb(children: Children, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <BreadcrumbPrimitive class=class>{children()}</BreadcrumbPrimitive> }\n}\n#[component]\npub fn BreadcrumbItem(children: Children, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <BreadcrumbItemPrimitive class=class>{children()}</BreadcrumbItemPrimitive> }\n}\n#[component]\npub fn BreadcrumbLink(children: Children, #[prop(into, default = String::new())] href: String, #[prop(default = ActivityState::Inactive)] state: ActivityState, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <BreadcrumbLinkPrimitive href=href state=state class=class>{children()}</BreadcrumbLinkPrimitive> }\n}\n#[component]\npub fn BreadcrumbPage(children: Children, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <span data-rs-breadcrumb-page=\"\" class=class>{children()}</span> }\n}\n#[component]\npub fn BreadcrumbSeparator(children: Children, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <BreadcrumbSeparatorPrimitive class=class>{children()}</BreadcrumbSeparatorPrimitive> }\n}\n#[component]\npub fn BreadcrumbEllipsis(#[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <BreadcrumbItemPrimitive class=String::new()><BreadcrumbEllipsisPrimitive class=class /></BreadcrumbItemPrimitive> }\n}\n",
"boundary_src": "//! @canon-level: strict\n//! Breadcrumb Island — bootstrap only, delegates to interaction engine\n\nuse leptos::prelude::*;\nuse super::breadcrumb_ui::{\n Breadcrumb as BreadcrumbUi,\n BreadcrumbItem as BreadcrumbItemUi,\n BreadcrumbLink as BreadcrumbLinkUi,\n BreadcrumbPage as BreadcrumbPageUi,\n BreadcrumbSeparator as BreadcrumbSeparatorUi,\n BreadcrumbEllipsis as BreadcrumbEllipsisUi\n};\nuse canonrs_core::meta::ActivityState;\n\n\n\n#[component]\npub fn Breadcrumb(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! {\n <BreadcrumbUi class=class.unwrap_or_default()>{children()}</BreadcrumbUi>\n }\n}\n\n#[component]\npub fn BreadcrumbItem(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <BreadcrumbItemUi class=class.unwrap_or_default()>{children()}</BreadcrumbItemUi> }\n}\n\n#[component]\npub fn BreadcrumbLink(\n children: Children,\n #[prop(optional, into)] href: Option<String>,\n #[prop(default = ActivityState::Inactive)] state: ActivityState,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! {\n <BreadcrumbLinkUi href=href.unwrap_or_default() state=state class=class.unwrap_or_default()>\n {children()}\n </BreadcrumbLinkUi>\n }\n}\n\n#[component]\npub fn BreadcrumbPage(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <BreadcrumbPageUi class=class.unwrap_or_default()>{children()}</BreadcrumbPageUi> }\n}\n\n#[component]\npub fn BreadcrumbSeparator(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <BreadcrumbSeparatorUi class=class.unwrap_or_default()>{children()}</BreadcrumbSeparatorUi> }\n}\n\n#[component]\npub fn BreadcrumbEllipsis(\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <BreadcrumbEllipsisUi class=class.unwrap_or_default() /> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const BREADCRUMB_API: ComponentApi = ComponentApi {\n id: \"breadcrumb\",\n description: \"Navigation breadcrumb trail\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const BREADCRUMBITEM_API: ComponentApi = ComponentApi {\n id: \"breadcrumb-item\",\n description: \"Navigation breadcrumb trail\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const BREADCRUMBLINK_API: ComponentApi = ComponentApi {\n id: \"breadcrumb-link\",\n description: \"Navigation breadcrumb trail\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"href\", kind: PropType::String, required: false, default: None, description: \"Navigation target URL\" },\n PropDef { name: \"state\", kind: PropType::String, required: false, default: Some(\"inactive\"), description: \"Loading or visibility state\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const BREADCRUMBPAGE_API: ComponentApi = ComponentApi {\n id: \"breadcrumb-page\",\n description: \"Navigation breadcrumb trail\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const BREADCRUMBSEPARATOR_API: ComponentApi = ComponentApi {\n id: \"breadcrumb-separator\",\n description: \"Navigation breadcrumb trail\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const BREADCRUMBELLIPSIS_API: ComponentApi = ComponentApi {\n id: \"breadcrumb-ellipsis\",\n description: \"Navigation breadcrumb trail\",\n props: &[\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::breadcrumb_boundary::{\n Breadcrumb, BreadcrumbItem, BreadcrumbLink,\n BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis,\n};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn BreadcrumbShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <Breadcrumb>\n <BreadcrumbItem>\n <BreadcrumbLink href=\"#\">\"Home\"</BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator>\"/\"</BreadcrumbSeparator>\n <BreadcrumbItem>\n <BreadcrumbLink href=\"#\">\"Components\"</BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator>\"/\"</BreadcrumbSeparator>\n <BreadcrumbItem>\n <BreadcrumbPage>\"Breadcrumb\"</BreadcrumbPage>\n </BreadcrumbItem>\n </Breadcrumb>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Current page state enforced via activity state mapping.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Custom separator\"</span>\n <Breadcrumb>\n <BreadcrumbItem>\n <BreadcrumbLink href=\"#\">\"Home\"</BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator>\"›\"</BreadcrumbSeparator>\n <BreadcrumbItem>\n <BreadcrumbLink href=\"#\">\"Settings\"</BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator>\"›\"</BreadcrumbSeparator>\n <BreadcrumbItem>\n <BreadcrumbPage>\"Profile\"</BreadcrumbPage>\n </BreadcrumbItem>\n </Breadcrumb>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"With ellipsis\"</span>\n <Breadcrumb>\n <BreadcrumbItem>\n <BreadcrumbLink href=\"#\">\"Home\"</BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator>\"/\"</BreadcrumbSeparator>\n <BreadcrumbItem>\n <BreadcrumbEllipsis />\n </BreadcrumbItem>\n <BreadcrumbSeparator>\"/\"</BreadcrumbSeparator>\n <BreadcrumbItem>\n <BreadcrumbLink href=\"#\">\"Components\"</BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator>\"/\"</BreadcrumbSeparator>\n <BreadcrumbItem>\n <BreadcrumbPage>\"Breadcrumb\"</BreadcrumbPage>\n </BreadcrumbItem>\n </Breadcrumb>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"flex",
"stack"
]
},
{
"id": "button",
"label": "Button",
"category": "Action",
"description": "Action button with variant and size",
"keywords": "button rust leptos, ssr safe button leptos, canonical button component rust, governed ui button",
"pain": "Buttons rely on string classes causing inconsistent variants and states",
"promise": "Variant and size enforced at compile-time via enums",
"why": "ButtonVariant and ButtonSize define allowed visual and behavioral states. The primitive encodes these into data attributes, ensuring consistent rendering. This eliminates invalid combinations and style drift.\n",
"before": "// ❌ Typical\nview! {\n <button class=\"btn btn-primary btn-md\">\"Submit\"</button>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Button variant=ButtonVariant::Primary size=ButtonSize::Md>\n \"Submit\"\n </Button>\n}\n",
"rules": [
"CR-001",
"CR-004",
"CR-148"
],
"use_cases": [
"form submit",
"cta actions"
],
"related": [
"button_group",
"icon_button",
"copy_button",
"link"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "action",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Button Primitive - HTML puro\nuse leptos::prelude::*;\nuse crate::meta::{DisabledState, LoadingState, ToggleState, ToDataAttr};\n\n#[derive(serde::Serialize, serde::Deserialize, Clone, PartialEq, Default, Debug)]\npub enum ButtonVariant {\n #[default]\n Default, Destructive, Outline, Secondary, Ghost, Link, Primary,\n}\nimpl ButtonVariant {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Default => \"default\",\n Self::Destructive => \"destructive\",\n Self::Outline => \"outline\",\n Self::Secondary => \"secondary\",\n Self::Ghost => \"ghost\",\n Self::Link => \"link\",\n Self::Primary => \"primary\",\n }\n }\n}\n\n#[derive(serde::Serialize, serde::Deserialize, Clone, PartialEq, Default, Debug)]\npub enum ButtonSize {\n Xs, Sm,\n #[default]\n Md,\n Lg, Xl, Icon,\n}\nimpl ButtonSize {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Xs => \"xs\",\n Self::Sm => \"sm\",\n Self::Md => \"md\",\n Self::Lg => \"lg\",\n Self::Xl => \"xl\",\n Self::Icon => \"icon\",\n }\n }\n}\n\n#[derive(Clone, Copy, PartialEq, Default, Debug, serde::Serialize, serde::Deserialize)]\npub enum ButtonStateHint {\n #[default] None,\n First, Last, Hover, Focus,\n}\nimpl ButtonStateHint {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::None => \"\",\n Self::First => \"first\",\n Self::Last => \"last\",\n Self::Hover => \"hover\",\n Self::Focus => \"focus\",\n }\n }\n}\n\n#[derive(Clone, Copy, PartialEq, Default, Debug, serde::Serialize, serde::Deserialize)]\npub enum ButtonType {\n #[default]\n Button, Submit, Reset,\n}\nimpl ButtonType {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Button => \"button\",\n Self::Submit => \"submit\",\n Self::Reset => \"reset\",\n }\n }\n}\n\n#[component]\npub fn ButtonPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(optional, into)] aria_label: Option<String>,\n #[prop(default = ButtonVariant::Default)] variant: ButtonVariant,\n #[prop(default = ButtonSize::Md)] size: ButtonSize,\n #[prop(default = ButtonType::Button)] button_type: ButtonType,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(default = LoadingState::Idle)] loading: LoadingState,\n #[prop(optional)] pressed: Option<ToggleState>,\n) -> impl IntoView {\n let uid = crate::infra::uid::generate(\"bt\");\n view! {\n <button\n type=button_type.as_str()\n data-rs-button=\"\"\n data-rs-uid=uid\n data-rs-interaction=\"init\"\n data-rs-variant=variant.as_str()\n data-rs-size=size.as_str()\n data-rs-disabled=disabled.to_data_attr().1.ne(\"enabled\").then(|| disabled.to_data_attr().1)\n data-rs-loading=loading.to_data_attr().1.ne(\"idle\").then(|| loading.to_data_attr().1)\n disabled=disabled.disabled()\n aria-disabled=disabled.aria_disabled()\n aria-busy=loading.aria_busy()\n aria-pressed=pressed.map(|p| p.aria_pressed())\n aria-label=aria_label\n class=class\n >\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn LinkButtonPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(optional, into)] aria_label: Option<String>,\n #[prop(default = ButtonVariant::Default)] variant: ButtonVariant,\n #[prop(default = ButtonSize::Md)] size: ButtonSize,\n #[prop(into, default = String::new())] href: String,\n #[prop(into, default = String::new())] target: String,\n) -> impl IntoView {\n let uid = crate::infra::uid::generate(\"bt\");\n view! {\n <a\n href=href\n target=if target.is_empty() { None } else { Some(target) }\n data-rs-button=\"\"\n data-rs-uid=uid\n data-rs-interaction=\"init\"\n data-rs-variant=variant.as_str()\n data-rs-size=size.as_str()\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n aria-disabled=disabled.aria_disabled()\n aria-label=aria_label\n class=class\n >\n {children()}\n </a>\n }\n}\n",
"ui_src": "use leptos::prelude::*;\nuse canonrs_core::primitives::{ButtonPrimitive, LinkButtonPrimitive, ButtonVariant, ButtonSize, ButtonType};\nuse canonrs_core::meta::{DisabledState, LoadingState};\n\n#[component]\npub fn Button(\n children: Children,\n #[prop(default = ButtonVariant::Default)] variant: ButtonVariant,\n #[prop(default = ButtonSize::Md)] size: ButtonSize,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(default = LoadingState::Idle)] loading: LoadingState,\n #[prop(into, default = String::new())] class: String,\n #[prop(optional, into)] aria_label: Option<String>,\n #[prop(default = ButtonType::Button)] button_type: ButtonType,\n) -> impl IntoView {\n view! {\n <ButtonPrimitive\n variant=variant\n size=size\n disabled=disabled\n loading=loading\n button_type=button_type\n aria_label=aria_label.unwrap_or_default()\n class=class\n >\n {children()}\n </ButtonPrimitive>\n }\n}\n\n#[component]\npub fn LinkButton(\n children: Children,\n #[prop(default = ButtonVariant::Default)] variant: ButtonVariant,\n #[prop(default = ButtonSize::Md)] size: ButtonSize,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n #[prop(optional, into)] aria_label: Option<String>,\n #[prop(into, default = String::new())] href: String,\n #[prop(into, default = String::new())] target: String,\n) -> impl IntoView {\n view! {\n <LinkButtonPrimitive\n href=href\n target=target\n variant=variant\n size=size\n disabled=disabled\n aria_label=aria_label.unwrap_or_default()\n class=class\n >\n {children()}\n </LinkButtonPrimitive>\n }\n}\n",
"boundary_src": "//! @canon-level: strict\n//! Button Boundary — Tipo 2: Init\n//! Normaliza props (bool -> State), delega para canonrs-interactions-init\n\nuse leptos::prelude::*;\nuse super::button_ui::{Button as ButtonUi, LinkButton as LinkButtonUi};\npub use canonrs_core::primitives::{ButtonVariant, ButtonSize, ButtonType, ButtonStateHint};\nuse canonrs_core::meta::{DisabledState, LoadingState};\n\n#[component]\npub fn Button(\n children: Children,\n #[prop(default = ButtonVariant::Default)] variant: ButtonVariant,\n #[prop(default = ButtonSize::Md)] size: ButtonSize,\n #[prop(default = false)] disabled: bool,\n #[prop(default = false)] loading: bool,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, optional)] aria_label: Option<String>,\n #[prop(into, optional)] validation: Option<String>,\n #[prop(optional)] state_hint: Option<ButtonStateHint>,\n #[prop(default = ButtonType::Button)] button_type: ButtonType,\n) -> impl IntoView {\n let disabled_state = if disabled { DisabledState::Disabled } else { DisabledState::Enabled };\n let loading_state = if loading { LoadingState::Loading } else { LoadingState::Idle };\n let _ = (validation, state_hint);\n view! {\n <ButtonUi\n variant=variant\n size=size\n disabled=disabled_state\n loading=loading_state\n button_type=button_type\n aria_label=aria_label.unwrap_or_default()\n class=class\n >\n {children()}\n </ButtonUi>\n }\n}\n\n#[component]\npub fn LinkButton(\n children: Children,\n #[prop(default = ButtonVariant::Default)] variant: ButtonVariant,\n #[prop(default = ButtonSize::Md)] size: ButtonSize,\n #[prop(default = false)] disabled: bool,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, optional)] aria_label: Option<String>,\n #[prop(into, default = String::new())] href: String,\n #[prop(into, default = String::new())] target: String,\n) -> impl IntoView {\n let disabled_state = if disabled { DisabledState::Disabled } else { DisabledState::Enabled };\n view! {\n <LinkButtonUi\n variant=variant\n size=size\n disabled=disabled_state\n aria_label=aria_label.unwrap_or_default()\n href=href\n target=target\n class=class\n >\n {children()}\n </LinkButtonUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{ButtonVariant, ButtonSize, ButtonType, ButtonStateHint}; \n\npub const BUTTON_API: ComponentApi = ComponentApi {\n id: \"button\",\n description: \"Action button with variant and size\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"variant\", kind: PropType::Enum(&[\"default\", \"destructive\", \"outline\", \"secondary\", \"ghost\", \"link\", \"primary\"]), required: false, default: Some(\"default\"), description: \"Visual variant of the component\" },\n PropDef { name: \"size\", kind: PropType::Enum(&[\"xs\", \"sm\", \"md\", \"lg\", \"xl\", \"icon\"]), required: false, default: Some(\"md\"), description: \"Size variant of the component\" },\n PropDef { name: \"disabled\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"loading\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n PropDef { name: \"aria_label\", kind: PropType::String, required: false, default: None, description: \"Accessible label for screen readers\" },\n PropDef { name: \"validation\", kind: PropType::String, required: false, default: None, description: \"Prop value\" },\n PropDef { name: \"state_hint\", kind: PropType::Enum(&[\"first\", \"last\", \"hover\", \"focus\"]), required: false, default: None, description: \"Prop value\" },\n PropDef { name: \"button_type\", kind: PropType::Enum(&[\"button\", \"submit\", \"reset\"]), required: false, default: Some(\"button\"), description: \"Prop value\" },\n ],\n};\n\npub const LINKBUTTON_API: ComponentApi = ComponentApi {\n id: \"link-button\",\n description: \"Action button with variant and size\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"variant\", kind: PropType::Enum(&[\"default\", \"destructive\", \"outline\", \"secondary\", \"ghost\", \"link\", \"primary\"]), required: false, default: Some(\"default\"), description: \"Visual variant of the component\" },\n PropDef { name: \"size\", kind: PropType::Enum(&[\"xs\", \"sm\", \"md\", \"lg\", \"xl\", \"icon\"]), required: false, default: Some(\"md\"), description: \"Size variant of the component\" },\n PropDef { name: \"disabled\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n PropDef { name: \"aria_label\", kind: PropType::String, required: false, default: None, description: \"Accessible label for screen readers\" },\n PropDef { name: \"href\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Navigation target URL\" },\n PropDef { name: \"target\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Target element selector for copy\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::button_boundary::{Button, ButtonVariant, ButtonSize};\nuse crate::ui::button_group::button_group_boundary::ButtonGroup;\nuse canonrs_core::ToggleState;\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn ButtonPreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <Button variant=ButtonVariant::Primary size=ButtonSize::Lg>\"Confirm Action\"</Button>\n <p data-rs-showcase-preview-anchor=\"\">\"Cannot drift. Cannot break. Cannot diverge.\"</p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Variants\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Sm>\n <Button variant=ButtonVariant::Primary>\"Primary\"</Button>\n <Button variant=ButtonVariant::Secondary>\"Secondary\"</Button>\n <Button variant=ButtonVariant::Outline>\"Outline\"</Button>\n <Button variant=ButtonVariant::Ghost>\"Ghost\"</Button>\n <Button variant=ButtonVariant::Destructive>\"Destructive\"</Button>\n <Button variant=ButtonVariant::Link>\"Link\"</Button>\n <Button variant=ButtonVariant::Default>\"Default\"</Button>\n </Stack>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Sizes\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Sm>\n <Button variant=ButtonVariant::Primary size=ButtonSize::Xs>\"Xs\"</Button>\n <Button variant=ButtonVariant::Primary size=ButtonSize::Sm>\"Sm\"</Button>\n <Button variant=ButtonVariant::Primary size=ButtonSize::Md>\"Md\"</Button>\n <Button variant=ButtonVariant::Primary size=ButtonSize::Lg>\"Lg\"</Button>\n <Button variant=ButtonVariant::Primary size=ButtonSize::Xl>\"Xl\"</Button>\n </Stack>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"States\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Sm>\n <Button variant=ButtonVariant::Primary>\"Default\"</Button>\n <Button variant=ButtonVariant::Primary disabled=true>\"Disabled\"</Button>\n <Button variant=ButtonVariant::Ghost attr:data-rs-state=\"hover\">\"Ghost Hover\"</Button>\n </Stack>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Validation\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Sm>\n <Button variant=ButtonVariant::Primary validation=\"error\">\"Error\"</Button>\n <Button variant=ButtonVariant::Primary validation=\"warning\">\"Warning\"</Button>\n <Button variant=ButtonVariant::Primary validation=\"success\">\"Success\"</Button>\n </Stack>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Button Group — detached\"</span>\n <ButtonGroup aria_label=\"Group detached\">\n <Button variant=ButtonVariant::Primary>\"One\"</Button>\n <Button variant=ButtonVariant::Primary>\"Two\"</Button>\n <Button variant=ButtonVariant::Primary>\"Three\"</Button>\n </ButtonGroup>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Button Group — attached\"</span>\n <ButtonGroup attached=ToggleState::On aria_label=\"Group attached\">\n <Button variant=ButtonVariant::Primary>\"One\"</Button>\n <Button variant=ButtonVariant::Primary>\"Two\"</Button>\n <Button variant=ButtonVariant::Primary>\"Three\"</Button>\n </ButtonGroup>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "button_group",
"label": "Button Group",
"category": "Action",
"description": "Group of action buttons",
"keywords": "",
"pain": "Grouped buttons lose semantic grouping and accessibility context",
"promise": "Group semantics and attachment enforced via component contract",
"why": "ButtonGroupPrimitive defines role=\"group\" and controlled attachment state via ToggleState. This ensures grouped actions are treated as a single logical unit. The contract guarantees consistent accessibility and visual cohesion.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"btn-group\">\n <button>\"Left\"</button>\n <button>\"Right\"</button>\n </div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <ButtonGroup>\n <Button>\"Left\"</Button>\n <Button>\"Right\"</Button>\n </ButtonGroup>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"toolbar actions",
"segmented controls"
],
"related": [
"button",
"icon_button",
"copy_button",
"link"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "action",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! ButtonGroup Primitive - HTML puro\n\nuse leptos::prelude::*;\nuse crate::ToggleState;\n\n#[component]\npub fn ButtonGroupPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = ToggleState::Off)] attached: ToggleState,\n #[prop(optional, into)] aria_label: Option<String>,\n) -> impl IntoView {\n let uid_bg = crate::infra::uid::generate(\"bg\");\n view! {\n <div\n data-rs-button-group=\"\"\n data-rs-uid=uid_bg\n data-rs-toggle=attached.as_str()\n role=\"group\"\n aria-label=aria_label\n class=class\n >\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::ToggleState;\nuse canonrs_core::primitives::{ButtonGroupPrimitive, ButtonPrimitive, ButtonVariant as CoreVariant};\n\n#[component]\npub fn ButtonGroup(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = ToggleState::Off)] attached: ToggleState,\n #[prop(optional, into)] aria_label: Option<String>,\n) -> impl IntoView {\n view! {\n <ButtonGroupPrimitive class=class attached=attached aria_label=aria_label.unwrap_or_default()>\n {children()}\n </ButtonGroupPrimitive>\n }\n}\n\n",
"boundary_src": "//! ButtonGroup Island — Canon Rule passthrough\nuse leptos::prelude::*;\npub use canonrs_core::ToggleState;\n\n\n#[component]\npub fn ButtonGroup(\n children: Children,\n #[prop(default = ToggleState::Off)] attached: ToggleState,\n #[prop(into, default = String::new())] class: String,\n #[prop(optional, into)] aria_label: Option<String>,\n) -> impl IntoView {\n view! {\n <super::button_group_ui::ButtonGroup class=class attached=attached aria_label=aria_label.unwrap_or_default()>\n {children()}\n </super::button_group_ui::ButtonGroup>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const BUTTONGROUP_API: ComponentApi = ComponentApi {\n id: \"button-group\",\n description: \"Group of action buttons\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"attached\", kind: PropType::String, required: false, default: Some(\"off\"), description: \"Whether buttons are visually attached\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n PropDef { name: \"aria_label\", kind: PropType::String, required: false, default: None, description: \"Accessible label for screen readers\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::button_group_boundary::{ButtonGroup, ToggleState};\nuse canonrs_core::primitives::{ButtonVariant, ButtonSize, ButtonStateHint};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\nuse crate::ui::button::button_boundary::Button;\n\n#[component]\npub fn ButtonGroupShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <ButtonGroup attached=ToggleState::On aria_label=\"Actions\">\n <Button variant=ButtonVariant::Secondary size=ButtonSize::Md>\"Left\"</Button>\n <Button variant=ButtonVariant::Secondary size=ButtonSize::Md>\"Center\"</Button>\n <Button variant=ButtonVariant::Secondary size=ButtonSize::Md>\"Right\"</Button>\n </ButtonGroup>\n <p data-rs-showcase-preview-anchor=\"\">\"Grouped actions. One contract.\"</p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Detached\"</span>\n <ButtonGroup>\n <Button variant=ButtonVariant::Secondary>\"A\"</Button>\n <Button variant=ButtonVariant::Secondary>\"B\"</Button>\n <Button variant=ButtonVariant::Secondary>\"C\"</Button>\n </ButtonGroup>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Attached\"</span>\n <ButtonGroup attached=ToggleState::On>\n <Button variant=ButtonVariant::Primary>\"Save\"</Button>\n <Button variant=ButtonVariant::Secondary>\"Cancel\"</Button>\n </ButtonGroup>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"First/last radius\"</span>\n <ButtonGroup attached=ToggleState::On aria_label=\"First last demo\">\n <Button variant=ButtonVariant::Secondary state_hint=ButtonStateHint::First>\"First\"</Button>\n <Button variant=ButtonVariant::Secondary>\"Middle\"</Button>\n <Button variant=ButtonVariant::Secondary state_hint=ButtonStateHint::Last>\"Last\"</Button>\n </ButtonGroup>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Hover z-index\"</span>\n <ButtonGroup attached=ToggleState::On aria_label=\"Hover z-index demo\">\n <Button variant=ButtonVariant::Secondary>\"One\"</Button>\n <Button variant=ButtonVariant::Secondary state_hint=ButtonStateHint::Hover>\"Hover\"</Button>\n <Button variant=ButtonVariant::Secondary>\"Three\"</Button>\n </ButtonGroup>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Focus z-index\"</span>\n <ButtonGroup attached=ToggleState::On aria_label=\"Focus z-index demo\">\n <Button variant=ButtonVariant::Secondary>\"One\"</Button>\n <Button variant=ButtonVariant::Secondary state_hint=ButtonStateHint::Focus>\"Focus\"</Button>\n <Button variant=ButtonVariant::Secondary>\"Three\"</Button>\n </ButtonGroup>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"flex",
"stack"
]
},
{
"id": "callout",
"label": "Callout",
"category": "Feedback",
"description": "Callout info box",
"keywords": "",
"pain": "Callouts use inconsistent roles and lack semantic intent",
"promise": "Semantic role and urgency enforced via variant",
"why": "CalloutVariant determines role and aria-live behavior. The primitive encodes these semantics directly into the DOM. This guarantees consistent accessibility and meaning across all callouts.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"callout warning\">\"Be careful\"</div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Callout variant=CalloutVariant::Warning>\n <CalloutTitle>\"Warning\"</CalloutTitle>\n <CalloutDescription>\"Be careful\"</CalloutDescription>\n </Callout>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"tips",
"warnings"
],
"related": [
"toast",
"alert",
"banner",
"inline_notice",
"status_dot"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "feedback",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Callout Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\n\n#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Default)]\npub enum CalloutVariant {\n #[default]\n Default,\n Info,\n Success,\n Warning,\n Error,\n}\n\nimpl CalloutVariant {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Default => \"default\",\n Self::Info => \"info\",\n Self::Success => \"success\",\n Self::Warning => \"warning\",\n Self::Error => \"error\",\n }\n }\n\n pub fn role(&self) -> &'static str {\n match self {\n Self::Error => \"alert\",\n _ => \"note\",\n }\n }\n\n pub fn aria_live(&self) -> &'static str {\n match self {\n Self::Error => \"assertive\",\n _ => \"polite\",\n }\n }\n}\n\n#[component]\npub fn CalloutPrimitive(\n children: Children,\n #[prop(default = CalloutVariant::Default)] variant: CalloutVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_clt = crate::infra::uid::generate(\"clt\");\n view! {\n <aside\n data-rs-callout=\"\"\n data-rs-uid=uid_clt\n data-rs-variant=variant.as_str()\n role=variant.role()\n aria-live=variant.aria_live()\n aria-atomic=\"true\"\n class=class\n >\n {children()}\n </aside>\n }\n}\n\n#[component]\npub fn CalloutIconPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-callout-icon=\"\"\n aria-hidden=\"true\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn CalloutTitlePrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-callout-title=\"\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn CalloutDescriptionPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-callout-description=\"\" class=class>\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n CalloutPrimitive, CalloutIconPrimitive,\n CalloutTitlePrimitive, CalloutDescriptionPrimitive,\n};\npub use canonrs_core::primitives::CalloutVariant;\n\n#[component]\npub fn Callout(\n children: Children,\n #[prop(default = CalloutVariant::Default)] variant: CalloutVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <CalloutPrimitive variant=variant class=class>\n {children()}\n </CalloutPrimitive>\n }\n}\n\n#[component]\npub fn CalloutIcon(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <CalloutIconPrimitive class=class>\n {children()}\n </CalloutIconPrimitive>\n }\n}\n\n#[component]\npub fn CalloutTitle(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <CalloutTitlePrimitive class=class>\n {children()}\n </CalloutTitlePrimitive>\n }\n}\n\n#[component]\npub fn CalloutDescription(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <CalloutDescriptionPrimitive class=class>\n {children()}\n </CalloutDescriptionPrimitive>\n }\n}\n\n",
"boundary_src": "//! Callout Boundary — Canon Rule passthrough\nuse leptos::prelude::*;\nuse super::callout_ui::{Callout as CalloutUi, CalloutIcon, CalloutTitle, CalloutDescription};\npub use canonrs_core::primitives::CalloutVariant;\n\n#[component]\npub fn Callout(\n #[prop(optional, into)] title: Option<String>,\n #[prop(optional, into)] description: Option<String>,\n #[prop(optional, into)] icon: Option<String>,\n #[prop(default = CalloutVariant::Default)] variant: CalloutVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <CalloutUi variant=variant class=class>\n {icon.map(|i| view! { <CalloutIcon>{i}</CalloutIcon> })}\n {title.map(|t| view! { <CalloutTitle>{t}</CalloutTitle> })}\n {description.map(|d| view! { <CalloutDescription>{d}</CalloutDescription> })}\n </CalloutUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{CalloutVariant}; \n\npub const CALLOUT_API: ComponentApi = ComponentApi {\n id: \"callout\",\n description: \"Callout info box\",\n props: &[\n PropDef { name: \"title\", kind: PropType::String, required: false, default: None, description: \"Title slot or text\" },\n PropDef { name: \"description\", kind: PropType::String, required: false, default: None, description: \"Description slot or text\" },\n PropDef { name: \"icon\", kind: PropType::String, required: false, default: None, description: \"Prop value\" },\n PropDef { name: \"variant\", kind: PropType::Enum(&[\"default\", \"info\", \"success\", \"warning\", \"error\"]), required: false, default: Some(\"default\"), description: \"Visual variant of the component\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::callout_boundary::{Callout, CalloutVariant};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn CalloutShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <Callout variant=CalloutVariant::Info title=\"Information\" description=\"New features are available in the latest release.\" />\n <p data-rs-showcase-preview-anchor=\"\">\"Semantic role and urgency enforced via variant.\"</p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Variants\"</span>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <Callout variant=CalloutVariant::Success icon=\"✓\" title=\"Success\" description=\"Your changes have been deployed.\" />\n <Callout variant=CalloutVariant::Warning icon=\"⚠\" title=\"Warning\" description=\"This action cannot be undone.\" />\n <Callout variant=CalloutVariant::Warning icon=\"✕\" title=\"Error\" description=\"Build failed due to type errors.\" />\n </Stack>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "card",
"label": "Card",
"category": "Display",
"description": "Card component",
"keywords": "",
"pain": "Content containers lack consistent structure and semantic regions",
"promise": "Card structure enforced with defined regions and roles",
"why": "CardPrimitive enforces a semantic region with structured subcomponents like header and content. This guarantees consistent layout composition. The contract prevents ad-hoc container misuse.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"card\">\n <h3>\"Title\"</h3>\n <p>\"Content\"</p>\n </div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Card>\n <CardHeader>\n <CardTitle>\"Title\"</CardTitle>\n </CardHeader>\n <CardContent>\"Content\"</CardContent>\n </Card>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"dashboards",
"content grouping"
],
"related": [
"resizable",
"scroll_area",
"aspect_ratio",
"page_header",
"toolbar",
"separator"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "layout",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Card Primitive - HTML puro\n\nuse leptos::prelude::*;\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum CardVariant {\n #[default]\n Default,\n Outlined,\n Elevated,\n Ghost,\n}\nimpl CardVariant {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Default => \"default\",\n Self::Outlined => \"outlined\",\n Self::Elevated => \"elevated\",\n Self::Ghost => \"ghost\",\n }\n }\n}\n\n#[component]\npub fn CardPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = CardVariant::Default)] variant: CardVariant,\n #[prop(optional, into)] aria_label: Option<String>,\n) -> impl IntoView {\n let uid_crd = crate::infra::uid::generate(\"crd\");\n view! {\n <div\n data-rs-card=\"\"\n data-rs-uid=uid_crd\n data-rs-variant=variant.as_str()\n role=\"region\"\n aria-label=aria_label\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn CardHeaderPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-card-header=\"\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn CardTitlePrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <h3 data-rs-card-title=\"\" class=class>\n {children()}\n </h3>\n }\n}\n\n#[component]\npub fn CardDescriptionPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <p data-rs-card-description=\"\" class=class>\n {children()}\n </p>\n }\n}\n\n#[component]\npub fn CardContentPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-card-content=\"\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn CardFooterPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-card-footer=\"\" class=class>\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n CardPrimitive, CardVariant,\n CardHeaderPrimitive,\n CardTitlePrimitive,\n CardDescriptionPrimitive,\n CardContentPrimitive,\n CardFooterPrimitive,\n};\n\n#[component]\npub fn Card(\n children: Children,\n #[prop(into, default = String::new())] variant: String,\n #[prop(default = String::new())] class: String,\n) -> impl IntoView {\n let base_class = format!(\"card {}\", class);\n\n view! {\n <CardPrimitive\n variant=match variant.as_str() {\n \"outlined\" => CardVariant::Outlined,\n \"elevated\" => CardVariant::Elevated,\n \"ghost\" => CardVariant::Ghost,\n _ => CardVariant::Default,\n }\n class={base_class}\n >\n {children()}\n </CardPrimitive>\n }\n}\n\n#[component]\npub fn CardHeader(\n children: Children,\n #[prop(default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <CardHeaderPrimitive\n class={class}\n >\n {children()}\n </CardHeaderPrimitive>\n }\n}\n\n#[component]\npub fn CardTitle(\n children: Children,\n #[prop(default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <CardTitlePrimitive\n class={class}\n >\n {children()}\n </CardTitlePrimitive>\n }\n}\n\n#[component]\npub fn CardDescription(\n children: Children,\n #[prop(default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <CardDescriptionPrimitive\n class={class}\n >\n {children()}\n </CardDescriptionPrimitive>\n }\n}\n\n#[component]\npub fn CardContent(\n children: Children,\n #[prop(default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <CardContentPrimitive\n class={class}\n >\n {children()}\n </CardContentPrimitive>\n }\n}\n\n#[component]\npub fn CardFooter(\n children: Children,\n #[prop(default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <CardFooterPrimitive\n class={class}\n >\n {children()}\n </CardFooterPrimitive>\n }\n}\n\n",
"boundary_src": "use leptos::prelude::*;\nuse super::card_ui::{\n Card as CardUi,\n CardHeader as CardHeaderUi,\n CardTitle as CardTitleUi,\n CardDescription as CardDescriptionUi,\n CardContent as CardContentUi,\n CardFooter as CardFooterUi,\n};\n\n#[component]\npub fn Card(\n children: Children,\n #[prop(into, default = String::new())] variant: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <CardUi variant=variant class=class>{children()}</CardUi> }\n}\n\n#[component]\npub fn CardHeader(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <CardHeaderUi class=class>{children()}</CardHeaderUi> }\n}\n\n#[component]\npub fn CardTitle(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <CardTitleUi class=class>{children()}</CardTitleUi> }\n}\n\n#[component]\npub fn CardDescription(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <CardDescriptionUi class=class>{children()}</CardDescriptionUi> }\n}\n\n#[component]\npub fn CardContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <CardContentUi class=class>{children()}</CardContentUi> }\n}\n\n#[component]\npub fn CardFooter(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <CardFooterUi class=class>{children()}</CardFooterUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const CARD_API: ComponentApi = ComponentApi {\n id: \"card\",\n description: \"Card component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"variant\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Visual variant of the component\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const CARDHEADER_API: ComponentApi = ComponentApi {\n id: \"card-header\",\n description: \"Card component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const CARDTITLE_API: ComponentApi = ComponentApi {\n id: \"card-title\",\n description: \"Card component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const CARDDESCRIPTION_API: ComponentApi = ComponentApi {\n id: \"card-description\",\n description: \"Card component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const CARDCONTENT_API: ComponentApi = ComponentApi {\n id: \"card-content\",\n description: \"Card component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const CARDFOOTER_API: ComponentApi = ComponentApi {\n id: \"card-footer\",\n description: \"Card component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse canonrs_core::slot;\nuse crate::blocks::card::{CardBlock, CardVariant};\nuse super::card_boundary::{CardHeader, CardTitle, CardDescription, CardContent, CardFooter};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn CardShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <CardBlock\n header=slot!(|| view! {\n <CardHeader>\n <CardTitle>\"Getting Started\"</CardTitle>\n <CardDescription>\"Everything you need to build with CanonRS.\"</CardDescription>\n </CardHeader>\n }.into_any())\n content=slot!(|| view! {\n <CardContent><p>\"Card structure enforced with defined regions and roles.\"</p></CardContent>\n }.into_any())\n footer=slot!(|| view! {\n <CardFooter><span>\"Last updated: today\"</span></CardFooter>\n }.into_any())\n />\n <p data-rs-showcase-preview-anchor=\"\">\n \"Card structure enforced with defined regions and roles.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Variants\"</span>\n <Stack direction=StackDirection::Vertical gap=StackGap::Md>\n <CardBlock\n header=slot!(|| view! {\n <CardHeader><CardTitle>\"Header only\"</CardTitle></CardHeader>\n }.into_any())\n />\n <CardBlock\n content=slot!(|| view! {\n <CardContent><p>\"Content only — no header or footer.\"</p></CardContent>\n }.into_any())\n />\n <CardBlock\n variant=CardVariant::Outlined\n header=slot!(|| view! {\n <CardHeader>\n <CardTitle>\"Full card\"</CardTitle>\n <CardDescription>\"With all three regions.\"</CardDescription>\n </CardHeader>\n }.into_any())\n content=slot!(|| view! {\n <CardContent><p>\"Body content goes here.\"</p></CardContent>\n }.into_any())\n footer=slot!(|| view! {\n <CardFooter><span>\"Footer action\"</span></CardFooter>\n }.into_any())\n />\n </Stack>\n </Stack>\n </Stack>\n }\n}\n",
"block": [
"card_block"
],
"blocks_primitives": [
"stack"
]
},
{
"id": "carousel",
"label": "Carousel",
"category": "Display",
"description": "Image carousel slider",
"keywords": "",
"pain": "Carousels break accessibility and state synchronization across slides",
"promise": "Slide state and navigation semantics enforced via structured primitives",
"why": "CarouselPrimitive defines roles and slide semantics including aria labels and state. ActivityState and VisibilityState control active and hidden slides. This ensures accessibility and predictable slideshow behavior.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"carousel\">\n <div class=\"slide active\">\"Slide 1\"</div>\n </div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Carousel>\n <CarouselTrack>\n <CarouselItem>\"Slide 1\"</CarouselItem>\n </CarouselTrack>\n </Carousel>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"image galleries",
"feature sliders"
],
"related": [
"avatar",
"icon",
"logo",
"code_block",
"markdown",
"chart",
"stat",
"inline_meta",
"kbd",
"badge"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "content_display",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Carousel Primitive - Interactive slideshow\n\nuse leptos::prelude::*;\nuse crate::meta::{ActivityState, DisabledState, VisibilityState};\n\n#[component]\npub fn CarouselPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(optional, into)] aria_label: Option<String>,\n) -> impl IntoView {\n let uid = crate::infra::uid::generate(\"cr\");\n view! {\n <div\n data-rs-carousel=\"\"\n data-rs-uid=uid\n data-rs-interaction=\"gesture\"\n role=\"region\"\n aria-roledescription=\"carousel\"\n aria-label=aria_label\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn CarouselTrackPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-carousel-track=\"\"\n role=\"group\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn CarouselItemPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = ActivityState::Inactive)] activity: ActivityState,\n #[prop(default = VisibilityState::Closed)] visibility: VisibilityState,\n #[prop(optional, into)] aria_label: Option<String>,\n) -> impl IntoView {\n view! {\n <div\n data-rs-carousel-item=\"\"\n data-rs-activity=activity.as_str()\n role=\"group\"\n aria-roledescription=\"slide\"\n aria-label=aria_label\n aria-hidden=visibility.aria_hidden()\n hidden=visibility.hidden()\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn CarouselPrevPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-carousel-prev=\"\"\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n disabled=disabled.disabled()\n aria-disabled=disabled.aria_disabled()\n aria-label=\"Previous slide\"\n class=class\n >\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn CarouselNextPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-carousel-next=\"\"\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n disabled=disabled.disabled()\n aria-disabled=disabled.aria_disabled()\n aria-label=\"Next slide\"\n class=class\n >\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn CarouselIndicatorsPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-carousel-indicators=\"\"\n role=\"group\"\n aria-label=\"Slide indicators\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn CarouselDotPrimitive(\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] aria_label: String,\n #[prop(default = ActivityState::Inactive)] state: ActivityState,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-carousel-dot=\"\"\n data-rs-activity=state.as_str()\n aria-label=aria_label\n class=class\n />\n }\n}\n\n#[component]\npub fn CarouselContentPrimitive(\n #[prop(into, default = String::new())] class: String,\n #[prop(optional)] children: Option<Children>,\n) -> impl IntoView {\n let uid = crate::infra::uid::generate(\"cc\");\n view! {\n <div\n data-rs-carousel-content=\"\"\n data-rs-uid=uid\n class=class\n >\n {children.map(|c| c())}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::ToggleState;\nuse canonrs_core::meta::ActivityState;\nuse canonrs_core::primitives::{\n CarouselPrimitive, CarouselTrackPrimitive, CarouselItemPrimitive,\n CarouselPrevPrimitive, CarouselNextPrimitive, CarouselIndicatorsPrimitive, CarouselDotPrimitive,\n};\n\n#[component]\npub fn Carousel(children: Children, #[prop(default = 0)] initial_index: usize, #[prop(default = ToggleState::Off)] autoplay: ToggleState, #[prop(default = 5000)] interval: u32, #[prop(default = ToggleState::On)] loop_state: ToggleState, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! {\n <CarouselPrimitive class=class>\n <CarouselTrackPrimitive>\n {children()}\n </CarouselTrackPrimitive>\n </CarouselPrimitive>\n }\n}\n#[component]\npub fn CarouselTrack(children: Children, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <CarouselTrackPrimitive class=class>{children()}</CarouselTrackPrimitive> }\n}\n#[component]\npub fn CarouselItem(children: Children, #[prop(into, default = String::new())] class: String, #[prop(default = false)] active: bool) -> impl IntoView {\n use canonrs_core::meta::VisibilityState;\n let activity = if active { ActivityState::Active } else { ActivityState::Inactive };\n let visibility = if active { VisibilityState::Open } else { VisibilityState::Closed };\n view! { <CarouselItemPrimitive class=class activity=activity visibility=visibility>{children()}</CarouselItemPrimitive> }\n}\n#[component]\npub fn CarouselPrev(children: Children, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <CarouselPrevPrimitive class=class>{children()}</CarouselPrevPrimitive> }\n}\n#[component]\npub fn CarouselNext(children: Children, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <CarouselNextPrimitive class=class>{children()}</CarouselNextPrimitive> }\n}\n#[component]\npub fn CarouselIndicators(children: Children, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <CarouselIndicatorsPrimitive class=class>{children()}</CarouselIndicatorsPrimitive> }\n}\n#[component]\npub fn CarouselDot(#[prop(into, default = String::new())] class: String, #[prop(into, default = String::new())] aria_label: String, #[prop(default = false)] active: bool) -> impl IntoView {\n let state = if active { ActivityState::Active } else { ActivityState::Inactive };\n view! { <CarouselDotPrimitive class=class aria_label=aria_label state=state /> }\n}\n",
"boundary_src": "//! Carousel Island — Canon Rule passthrough\nuse leptos::prelude::*;\nuse super::carousel_ui::{\n Carousel as CarouselUi,\n CarouselTrack as CarouselTrackUi,\n CarouselItem as CarouselItemUi,\n CarouselPrev as CarouselPrevUi,\n CarouselNext as CarouselNextUi,\n CarouselIndicators as CarouselIndicatorsUi,\n CarouselDot as CarouselDotUi\n};\nuse canonrs_core::ToggleState;\n\n#[component]\npub fn Carousel(\n children: Children,\n #[prop(default = 0)] initial_index: usize,\n #[prop(default = ToggleState::Off)] autoplay: ToggleState,\n #[prop(default = 5000)] interval: u32,\n #[prop(default = ToggleState::On)] loop_state: ToggleState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <CarouselUi initial_index=initial_index autoplay=autoplay interval=interval loop_state=loop_state class=class>\n {children()}\n </CarouselUi>\n }\n}\n\n#[component]\npub fn CarouselTrack(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <CarouselTrackUi class=class>{children()}</CarouselTrackUi> }\n}\n\n#[component]\npub fn CarouselItem(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = false)] active: bool,\n) -> impl IntoView {\n view! { <CarouselItemUi class=class active=active>{children()}</CarouselItemUi> }\n}\n\n#[component]\npub fn CarouselPrev(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <CarouselPrevUi class=class>{children()}</CarouselPrevUi> }\n}\n\n#[component]\npub fn CarouselNext(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <CarouselNextUi class=class>{children()}</CarouselNextUi> }\n}\n\n#[component]\npub fn CarouselIndicators(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <CarouselIndicatorsUi class=class>{children()}</CarouselIndicatorsUi> }\n}\n\n#[component]\npub fn CarouselDot(\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] aria_label: String,\n #[prop(default = false)] active: bool,\n) -> impl IntoView {\n view! { <CarouselDotUi class=class aria_label=aria_label active=active /> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const CAROUSEL_API: ComponentApi = ComponentApi {\n id: \"carousel\",\n description: \"Image carousel slider\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"initial_index\", kind: PropType::Number, required: false, default: Some(\"0\"), description: \"Prop value\" },\n PropDef { name: \"autoplay\", kind: PropType::String, required: false, default: Some(\"off\"), description: \"Prop value\" },\n PropDef { name: \"interval\", kind: PropType::Number, required: false, default: Some(\"5000\"), description: \"Prop value\" },\n PropDef { name: \"loop_state\", kind: PropType::String, required: false, default: Some(\"on\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const CAROUSELTRACK_API: ComponentApi = ComponentApi {\n id: \"carousel-track\",\n description: \"Image carousel slider\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const CAROUSELITEM_API: ComponentApi = ComponentApi {\n id: \"carousel-item\",\n description: \"Image carousel slider\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n PropDef { name: \"active\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Active/selected state\" },\n ],\n};\n\npub const CAROUSELPREV_API: ComponentApi = ComponentApi {\n id: \"carousel-prev\",\n description: \"Image carousel slider\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const CAROUSELNEXT_API: ComponentApi = ComponentApi {\n id: \"carousel-next\",\n description: \"Image carousel slider\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const CAROUSELINDICATORS_API: ComponentApi = ComponentApi {\n id: \"carousel-indicators\",\n description: \"Image carousel slider\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const CAROUSELDOT_API: ComponentApi = ComponentApi {\n id: \"carousel-dot\",\n description: \"Image carousel slider\",\n props: &[\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n PropDef { name: \"aria_label\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Accessible label for screen readers\" },\n PropDef { name: \"active\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Active/selected state\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::carousel_boundary::{\n Carousel, CarouselTrack, CarouselItem,\n CarouselPrev, CarouselNext, CarouselIndicators, CarouselDot\n};\nuse canonrs_core::ToggleState;\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn CarouselShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <Carousel>\n <CarouselTrack>\n <CarouselItem active=true>\n <img src=\"/assets/canonrs-image1.webp\" alt=\"Slide 1\" style=\"width:100%;height:auto;display:block;\" />\n </CarouselItem>\n <CarouselItem>\n <img src=\"/assets/canonrs-image2.webp\" alt=\"Slide 2\" style=\"width:100%;height:auto;display:block;\" />\n </CarouselItem>\n <CarouselItem>\n <img src=\"/assets/canonrs-image3.webp\" alt=\"Slide 3\" style=\"width:100%;height:auto;display:block;\" />\n </CarouselItem>\n </CarouselTrack>\n <CarouselPrev>\"←\"</CarouselPrev>\n <CarouselNext>\"→\"</CarouselNext>\n <CarouselIndicators>\n <CarouselDot active=true aria_label=\"Slide 1\" />\n <CarouselDot aria_label=\"Slide 2\" />\n <CarouselDot aria_label=\"Slide 3\" />\n </CarouselIndicators>\n </Carousel>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Sequential navigation with optional autoplay and loop.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Autoplay\"</span>\n <Carousel autoplay=ToggleState::On interval=3000u32>\n <CarouselTrack>\n <CarouselItem active=true>\n <img src=\"/assets/canonrs-image1.webp\" alt=\"Slide 1\" style=\"width:100%;height:auto;display:block;\" />\n </CarouselItem>\n <CarouselItem>\n <img src=\"/assets/canonrs-image2.webp\" alt=\"Slide 2\" style=\"width:100%;height:auto;display:block;\" />\n </CarouselItem>\n <CarouselItem>\n <img src=\"/assets/canonrs-image3.webp\" alt=\"Slide 3\" style=\"width:100%;height:auto;display:block;\" />\n </CarouselItem>\n </CarouselTrack>\n <CarouselPrev>\"←\"</CarouselPrev>\n <CarouselNext>\"→\"</CarouselNext>\n <CarouselIndicators>\n <CarouselDot active=true aria_label=\"Slide 1\" />\n <CarouselDot aria_label=\"Slide 2\" />\n <CarouselDot aria_label=\"Slide 3\" />\n </CarouselIndicators>\n </Carousel>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"center",
"stack"
]
},
{
"id": "chart",
"label": "Chart",
"category": "Display",
"description": "Data chart visualization",
"keywords": "",
"pain": "Charts mix rendering logic and data, causing inconsistent behavior",
"promise": "Chart structure and data binding enforced via contract",
"why": "ChartPrimitive separates rendering (canvas) from data (data-rs attributes). ChartType enforces visualization type at compile-time. This guarantees consistent rendering and interaction patterns.\n",
"before": "// ❌ Typical\nview! {\n <canvas id=\"chart\"></canvas>\n <script>renderChart(data)</script>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Chart data=data chart_type=ChartType::Line />\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"analytics dashboards",
"data visualization"
],
"related": [
"avatar",
"icon",
"logo",
"code_block",
"markdown",
"stat",
"inline_meta",
"kbd",
"badge",
"carousel"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "content_display",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Chart Primitive - Canvas + overlay enterprise architecture\n\nuse leptos::prelude::*;\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum ChartGridState {\n #[default] Visible,\n Hidden,\n}\nimpl ChartGridState {\n pub fn as_str(&self) -> &'static str {\n match self { Self::Visible => \"visible\", Self::Hidden => \"hidden\" }\n }\n}\nimpl From<bool> for ChartGridState {\n fn from(b: bool) -> Self { if b { Self::Visible } else { Self::Hidden } }\n}\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum ChartLegendState {\n #[default] Visible,\n Hidden,\n}\nimpl ChartLegendState {\n pub fn as_str(&self) -> &'static str {\n match self { Self::Visible => \"visible\", Self::Hidden => \"hidden\" }\n }\n}\nimpl From<bool> for ChartLegendState {\n fn from(b: bool) -> Self { if b { Self::Visible } else { Self::Hidden } }\n}\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum ChartType {\n #[default]\n Bar, Line, Area, Pie, Donut, Scatter, Radar,\n}\nimpl ChartType {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Bar => \"bar\",\n Self::Line => \"line\",\n Self::Area => \"area\",\n Self::Pie => \"pie\",\n Self::Donut => \"donut\",\n Self::Scatter => \"scatter\",\n Self::Radar => \"radar\",\n }\n }\n}\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum ChartAnimation {\n #[default]\n Auto,\n None,\n}\nimpl ChartAnimation {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Auto => \"auto\",\n Self::None => \"none\",\n }\n }\n}\n\n#[component]\npub fn ChartPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = ChartType::Bar)] chart_type: ChartType,\n #[prop(into, default = \"320\".to_string())] height: String,\n #[prop(optional, into)] aria_label: Option<String>,\n #[prop(into, default = String::new())] value: String,\n #[prop(default = ChartGridState::Visible)] chart_grid: ChartGridState,\n #[prop(default = ChartLegendState::Visible)] chart_legend: ChartLegendState,\n #[prop(default = ChartAnimation::Auto)] animation: ChartAnimation,\n) -> impl IntoView {\n let uid = crate::infra::uid::generate(\"ch\");\n view! {\n <div\n data-rs-chart=\"\"\n data-rs-uid=uid\n data-rs-interaction=\"data\"\n data-rs-chart-type=chart_type.as_str()\n data-rs-chart-height=height\n data-rs-animation=animation.as_str()\n data-rs-value=value\n role=\"img\"\n aria-label=aria_label\n class=class\n >\n <canvas data-rs-chart-canvas=\"\" aria-hidden=\"true\" />\n <div data-rs-chart-overlay=\"\" aria-hidden=\"true\">\n <div data-rs-chart-tooltip=\"\" />\n <div data-rs-chart-crosshair=\"\" />\n </div>\n <div data-rs-chart-legend=\"\" data-rs-state=chart_legend.as_str() />\n <div data-rs-chart-grid=\"\" data-rs-state=chart_grid.as_str() />\n {children()}\n </div>\n }\n}\n\n/// ChartDataPrimitive — injeta dados via data attribute\n/// O runtime lê data-rs-chart-data para renderizar o gráfico\n#[component]\npub fn ChartDataPrimitive(\n #[prop(into, default = String::new())] data: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-chart-data=data\n aria-hidden=\"true\"\n />\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{ChartPrimitive, ChartDataPrimitive, ChartType, ChartAnimation};\nuse canonrs_core::{ChartGridState, ChartLegendState};\npub use canonrs_core::primitives::ChartType as ChartKind;\npub use canonrs_core::{ChartData, ChartSeries};\n\n#[component]\npub fn Chart(\n data: ChartData,\n #[prop(default = ChartType::Line)] chart_type: ChartType,\n #[prop(into, default = \"320\".to_string())] height: String,\n #[prop(default = true)] show_grid: bool,\n #[prop(default = true)] show_legend: bool,\n #[prop(default = true)] _show_tooltip: bool,\n #[prop(default = true)] animate: bool,\n #[prop(into, default = String::new())] class: String,\n #[prop(optional)] _max_width: Option<u32>,\n #[prop(into, default = String::new())] _sync_table: String,\n #[prop(into, default = String::new())] _sync_scope: String,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, optional)] aria_label: Option<String>,\n) -> impl IntoView {\n let json = data.to_json();\n let animation = if animate { ChartAnimation::Auto } else { ChartAnimation::None };\n view! {\n <ChartPrimitive\n class=class\n chart_type=chart_type\n height=height\n value=value\n aria_label=aria_label.unwrap_or_default()\n chart_grid=ChartGridState::from(show_grid)\n chart_legend=ChartLegendState::from(show_legend)\n animation=animation\n >\n <ChartDataPrimitive data=json />\n </ChartPrimitive>\n }\n}\n\n",
"boundary_src": "use leptos::prelude::*;\nuse super::chart_ui::Chart as ChartUi;\npub use canonrs_core::primitives::ChartType;\nuse canonrs_core::ChartData;\n\n#[component]\npub fn Chart(\n data: ChartData,\n #[prop(default = ChartType::Line)] chart_type: ChartType,\n #[prop(into, default = \"320\".to_string())] height: String,\n #[prop(default = true)] show_grid: bool,\n #[prop(default = true)] show_legend: bool,\n #[prop(default = true)] animate: bool,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] aria_label: String,\n) -> impl IntoView {\n view! {\n <ChartUi\n data=data\n chart_type=chart_type\n height=height\n show_grid=show_grid\n show_legend=show_legend\n animate=animate\n class=class\n value=value\n aria_label=aria_label\n />\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{ChartType}; \n\npub const CHART_API: ComponentApi = ComponentApi {\n id: \"chart\",\n description: \"Data chart visualization\",\n props: &[\n PropDef { name: \"data\", kind: PropType::String, required: true, default: None, description: \"Prop value\" },\n PropDef { name: \"chart_type\", kind: PropType::Enum(&[\"bar\", \"line\", \"area\", \"pie\", \"donut\", \"scatter\", \"radar\"]), required: false, default: Some(\"line\"), description: \"Prop value\" },\n PropDef { name: \"height\", kind: PropType::String, required: false, default: Some(\"320\"), description: \"Prop value\" },\n PropDef { name: \"show_grid\", kind: PropType::Bool, required: false, default: Some(\"true\"), description: \"Prop value\" },\n PropDef { name: \"show_legend\", kind: PropType::Bool, required: false, default: Some(\"true\"), description: \"Prop value\" },\n PropDef { name: \"animate\", kind: PropType::Bool, required: false, default: Some(\"true\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n PropDef { name: \"value\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Current value\" },\n PropDef { name: \"aria_label\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Accessible label for screen readers\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::chart_boundary::{Chart, ChartType};\nuse canonrs_core::{ChartData, ChartSeries};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\nfn monthly_data() -> ChartData {\n ChartData {\n labels: vec![\"Jan\".to_string(), \"Feb\".to_string(), \"Mar\".to_string(),\n \"Apr\".to_string(), \"May\".to_string(), \"Jun\".to_string()],\n series: vec![\n ChartSeries { name: \"Revenue\".to_string(), data: vec![42.0, 58.0, 51.0, 73.0, 65.0, 89.0], color: None },\n ChartSeries { name: \"Expenses\".to_string(), data: vec![31.0, 40.0, 38.0, 52.0, 47.0, 61.0], color: None },\n ],\n }\n}\n\nfn bar_data() -> ChartData {\n ChartData {\n labels: vec![\"Q1\".to_string(), \"Q2\".to_string(), \"Q3\".to_string(), \"Q4\".to_string()],\n series: vec![\n ChartSeries { name: \"Growth\".to_string(), data: vec![24.0, 38.0, 55.0, 72.0], color: None },\n ],\n }\n}\n\nfn area_data() -> ChartData {\n ChartData {\n labels: vec![\"Mon\".to_string(), \"Tue\".to_string(), \"Wed\".to_string(), \"Thu\".to_string(), \"Fri\".to_string()],\n series: vec![\n ChartSeries { name: \"Users\".to_string(), data: vec![120.0, 145.0, 132.0, 178.0, 195.0], color: None },\n ],\n }\n}\n\n#[component]\npub fn ChartShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <Chart data=monthly_data() chart_type=ChartType::Line height=\"280\".to_string()\n value=\"revenue-monthly\" aria_label=\"Monthly revenue and expenses line chart\" />\n <p data-rs-showcase-preview-anchor=\"\">\n \"Chart structure and data binding enforced via contract.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Bar\"</span>\n <Chart data=bar_data() chart_type=ChartType::Bar height=\"200\".to_string()\n value=\"growth-quarterly\" aria_label=\"Quarterly growth bar chart\" />\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Area\"</span>\n <Chart data=area_data() chart_type=ChartType::Area height=\"200\".to_string()\n value=\"users-weekly\" aria_label=\"Weekly active users area chart\" />\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "checkbox",
"label": "Checkbox",
"category": "Form",
"description": "Checkbox input",
"keywords": "",
"pain": "Checkbox state desyncs between UI and internal state",
"promise": "Selection state (checked/unchecked/indeterminate) drives visual and aria-checked contract",
"why": "CheckboxPrimitive maps ActivityState to checked and ARIA attributes. This ensures UI and accessibility stay in sync. The contract prevents mismatched boolean and DOM state.\n",
"before": "// ❌ Typical\nview! {\n <input type=\"checkbox\" checked=state />\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Checkbox checked=true>\"Remember me\"</Checkbox>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"forms",
"multi-selection"
],
"related": [
"form",
"input",
"input_group",
"input_otp",
"textarea",
"field",
"label",
"form_error_summary"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "form",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Checkbox Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::DisabledState;\n\n#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, PartialEq, Default, Debug)]\npub enum CheckboxState {\n #[default]\n Unchecked,\n Checked,\n Indeterminate,\n}\n\nimpl CheckboxState {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Unchecked => \"unchecked\",\n Self::Checked => \"checked\",\n Self::Indeterminate => \"indeterminate\",\n }\n }\n pub fn aria_checked(&self) -> &'static str {\n match self {\n Self::Unchecked => \"false\",\n Self::Checked => \"true\",\n Self::Indeterminate => \"mixed\",\n }\n }\n pub fn is_checked(&self) -> bool {\n matches!(self, Self::Checked)\n }\n}\n\n#[component]\npub fn CheckboxPrimitive(\n children: Children,\n #[prop(default = CheckboxState::Unchecked)] checked: CheckboxState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] name: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_cb = crate::infra::uid::generate(\"cb\");\n view! {\n <label\n data-rs-checkbox=\"\"\n data-rs-uid=uid_cb\n data-rs-interaction=\"init\"\n data-rs-selection=checked.as_str()\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n aria-disabled=disabled.aria_disabled()\n class=class\n >\n <input\n type=\"checkbox\"\n data-rs-checkbox-input=\"\"\n checked=checked.is_checked()\n disabled=disabled.disabled()\n aria-checked=checked.aria_checked()\n name={if name.is_empty() { None } else { Some(name) }}\n />\n {children()}\n </label>\n }\n}\n\n#[component]\npub fn CheckboxIndicatorPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <span data-rs-checkbox-indicator=\"\" aria-hidden=\"true\" class=class>\n {children()}\n </span>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{CheckboxPrimitive, CheckboxIndicatorPrimitive};\nuse canonrs_core::primitives::CheckboxState;\nuse canonrs_core::meta::DisabledState;\n\n#[component]\npub fn Checkbox(\n children: Children,\n #[prop(default = CheckboxState::Unchecked)] checked: CheckboxState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] name: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <CheckboxPrimitive checked=checked disabled=disabled name=name class=class>\n <CheckboxIndicatorPrimitive>\"✓\"</CheckboxIndicatorPrimitive>\n {children()}\n </CheckboxPrimitive>\n }\n}\n",
"boundary_src": "//! @canon-level: strict\n//! Checkbox Island — Canon Rule #340 (zero-logic boundary)\n\nuse leptos::prelude::*;\nuse super::checkbox_ui::Checkbox as CheckboxUi;\npub use canonrs_core::primitives::CheckboxState;\nuse canonrs_core::meta::DisabledState;\n\n#[component]\npub fn Checkbox(\n children: Children,\n #[prop(default = false)] checked: bool,\n #[prop(default = false)] disabled: bool,\n #[prop(into, default = String::new())] name: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let checked_state = if checked { CheckboxState::Checked } else { CheckboxState::Unchecked };\n let disabled_state = if disabled { DisabledState::Disabled } else { DisabledState::Enabled };\n view! {\n <CheckboxUi checked=checked_state disabled=disabled_state name=name class=class>\n {children()}\n </CheckboxUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{CheckboxState}; \n\npub const CHECKBOX_API: ComponentApi = ComponentApi {\n id: \"checkbox\",\n description: \"Checkbox input\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"checked\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Whether the component is checked\" },\n PropDef { name: \"disabled\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"name\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Form field name\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::checkbox_boundary::Checkbox;\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn CheckboxShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <Checkbox checked=true>\n <span>\"Remember me\"</span>\n </Checkbox>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Checked state mapped explicitly to activity state.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"States\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Md>\n <Checkbox><span>\"Unchecked\"</span></Checkbox>\n <Checkbox checked=true><span>\"Checked\"</span></Checkbox>\n <Checkbox disabled=true><span>\"Disabled\"</span></Checkbox>\n <Checkbox checked=true disabled=true><span>\"Checked + Disabled\"</span></Checkbox>\n </Stack>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "code_block",
"label": "Code Block",
"category": "Display",
"description": "Syntax-highlighted code display",
"keywords": "",
"pain": "Code blocks rely on client-side highlighting causing hydration mismatch",
"promise": "SSR-safe syntax highlighting with deterministic DOM output",
"why": "CodeBlockPrimitive supports SSR-safe rendering with precomputed HTML. The contract ensures no client mutation is required. This prevents hydration mismatch and ensures consistent output.\n",
"before": "// ❌ Typical\nview! {\n <pre><code>{highlight(code)}</code></pre>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <CodeBlock code=\"fn main() {}\" language=\"rust\" />\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"docs",
"code snippets"
],
"related": [
"avatar",
"icon",
"logo",
"markdown",
"chart",
"stat",
"inline_meta",
"kbd",
"badge",
"carousel"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "content_display",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! CodeBlock Primitive - HTML puro, SSR-safe\n\nuse leptos::prelude::*;\nuse crate::meta::ToggleState;\n\n#[component]\npub fn CodeBlockPrimitive(\n children: Children,\n #[prop(into, default = String::new())] language: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_cob = crate::infra::uid::generate(\"cob\");\n view! {\n <div\n data-rs-code-block=\"\"\n data-rs-uid=uid_cob\n data-rs-interaction=\"content\"\n data-rs-language=language\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn CodeBlockHeaderPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-code-header=\"\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn CodeBlockLanguagePrimitive(\n #[prop(into)] language: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <span data-rs-code-language=\"\" class=class>\n {language}\n </span>\n }\n}\n\n#[component]\npub fn CodeBlockFilenamePrimitive(\n #[prop(into)] filename: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <span data-rs-code-filename=\"\" class=class>\n {filename}\n </span>\n }\n}\n\n#[component]\npub fn CodeBlockCopyButtonPrimitive(\n #[prop(into, default = String::new())] class: String,\n #[prop(default = ToggleState::Off)] copied: ToggleState,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-code-copy-btn=\"\"\n data-rs-toggle=copied.as_str()\n aria-pressed=copied.aria_pressed()\n aria-label=\"Copy code\"\n class=class\n >\n <span data-rs-code-copy-label=\"\">\"Copy\"</span>\n </button>\n }\n}\n\n#[component]\npub fn CodeBlockPrePrimitive(\n #[prop(optional)] children: Option<Children>,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] inner_html: String,\n) -> impl IntoView {\n if inner_html.is_empty() {\n view! {\n <pre data-rs-code-pre=\"\" class=class>\n {children.map(|c| c())}\n </pre>\n }.into_any()\n } else {\n view! {\n <pre data-rs-code-pre=\"\" class=class inner_html=inner_html></pre>\n }.into_any()\n }\n}\n\n#[component]\npub fn CodeBlockLinePrimitive(\n #[prop(into)] _html: String,\n #[prop(default = 0usize)] line_number: usize,\n #[prop(into, default = String::new())] diff: String,\n) -> impl IntoView {\n #[cfg(feature = \"ssr\")]\n {\n view! {\n <span\n data-rs-code-line=\"\"\n data-rs-line-number=line_number.to_string()\n data-rs-diff={if diff.is_empty() { None } else { Some(diff) }}\n inner_html=_html\n ></span>\n }.into_any()\n }\n #[cfg(not(feature = \"ssr\"))]\n {\n view! {\n <span\n data-rs-code-line=\"\"\n data-rs-line-number=line_number.to_string()\n data-rs-diff={if diff.is_empty() { None } else { Some(diff.clone()) }}\n ></span>\n }.into_any()\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code, unused_variables)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n CodeBlockPrimitive, CodeBlockHeaderPrimitive, CodeBlockLanguagePrimitive,\n CodeBlockFilenamePrimitive, CodeBlockPrePrimitive,\n};\nuse crate::ui::copy_button::copy_button_boundary::CopyButton;\n#[cfg(feature = \"ssr\")]\nuse super::highlighter::highlight;\n\n#[component]\npub fn CodeBlock(\n #[prop(into)] code: String,\n #[prop(into, default = \"text\".to_string())] language: String,\n #[prop(into, optional)] filename: Option<String>,\n #[prop(default = false)] show_line_numbers: bool,\n #[prop(default = true)] show_copy: bool,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n #[cfg(feature = \"ssr\")]\n let pre_html = {\n let result = highlight(&code, &language);\n result.lines.into_iter().enumerate().map(|(i, html)| {\n format!(r#\"<span data-rs-code-line=\"\" data-rs-line-number=\"{}\">{}</span>\"#, i + 1, html)\n }).collect::<Vec<_>>().join(\"\")\n };\n #[cfg(not(feature = \"ssr\"))]\n let pre_html = String::new();\n\n let lang_display = language.clone();\n let code_for_copy = code.clone();\n\n view! {\n <CodeBlockPrimitive\n language=lang_display.clone()\n class=class\n attr:data-line-numbers={show_line_numbers.then(|| \"true\")}\n >\n <CodeBlockHeaderPrimitive>\n <div data-code-header-left=\"\">\n <CodeBlockLanguagePrimitive language=lang_display />\n {filename.map(|f| view! {\n <CodeBlockFilenamePrimitive filename=f />\n })}\n </div>\n {show_copy.then(|| view! {\n <CopyButton text=code_for_copy />\n })}\n </CodeBlockHeaderPrimitive>\n <CodeBlockPrePrimitive inner_html=pre_html />\n </CodeBlockPrimitive>\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! CodeBlock Boundary — Canon Rule #340 (zero-logic boundary)\n\nuse leptos::prelude::*;\nuse super::code_block_ui::CodeBlock as CodeBlockUi;\n\n#[component]\npub fn CodeBlock(\n #[prop(into)] code: String,\n #[prop(into, default = \"text\".to_string())] language: String,\n #[prop(into, optional)] filename: Option<String>,\n #[prop(default = false)] show_line_numbers: bool,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <CodeBlockUi\n code=code\n language=language\n filename=filename.unwrap_or_default()\n show_line_numbers=show_line_numbers\n class=class\n />\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const CODEBLOCK_API: ComponentApi = ComponentApi {\n id: \"code-block\",\n description: \"Syntax-highlighted code display\",\n props: &[\n PropDef { name: \"code\", kind: PropType::String, required: true, default: None, description: \"Prop value\" },\n PropDef { name: \"language\", kind: PropType::String, required: false, default: Some(\"text\"), description: \"Prop value\" },\n PropDef { name: \"filename\", kind: PropType::String, required: false, default: None, description: \"Prop value\" },\n PropDef { name: \"show_line_numbers\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::code_block_boundary::CodeBlock;\nuse crate::blocks::card::CardBlock;\nuse crate::ui::card::{CardHeader, CardTitle, CardContent};\nuse canonrs_core::slot;\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn CodeBlockShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <CardBlock\n header=slot!(|| view! {\n <CardHeader><CardTitle>\"Rust\"</CardTitle></CardHeader>\n }.into_any())\n content=slot!(|| view! {\n <CardContent>\n <CodeBlock\n code=\"fn main() {\\n println!(\\\"Hello, world!\\\");\\n}\".to_string()\n language=\"rust\".to_string()\n />\n </CardContent>\n }.into_any())\n />\n <p data-rs-showcase-preview-anchor=\"\">\n \"SSR-safe syntax highlighting with deterministic DOM output.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"With filename\"</span>\n <CardBlock\n content=slot!(|| view! {\n <CardContent>\n <CodeBlock\n code=\"const greet = (name: string) => `Hello, ${name}!`;\".to_string()\n language=\"typescript\".to_string()\n filename=\"greet.ts\".to_string()\n />\n </CardContent>\n }.into_any())\n />\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"With line numbers\"</span>\n <CardBlock\n content=slot!(|| view! {\n <CardContent>\n <CodeBlock\n code=\"[dependencies]\\nleptos = { version = \\\"0.8\\\" }\".to_string()\n language=\"toml\".to_string()\n show_line_numbers=true\n />\n </CardContent>\n }.into_any())\n />\n </Stack>\n </Stack>\n }\n}\n",
"block": [
"card_block"
],
"blocks_primitives": [
"stack"
]
},
{
"id": "collapsible",
"label": "Collapsible",
"category": "Navigation",
"description": "Collapsible section",
"keywords": "",
"pain": "Collapsible sections lose sync between trigger and content state",
"promise": "Trigger and content state always synchronized via shared visibility state",
"why": "CollapsiblePrimitive shares VisibilityState across trigger and content. This ensures both reflect the same open/closed state. The contract eliminates manual sync logic.\n",
"before": "// ❌ Typical\nview! {\n <button on:click=toggle>\"Toggle\"</button>\n {if open { view! { <div>\"Content\"</div> } }}\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Collapsible>\n <CollapsibleTrigger>\"Toggle\"</CollapsibleTrigger>\n <CollapsibleContent>\"Content\"</CollapsibleContent>\n </Collapsible>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"faq sections",
"expandable panels"
],
"related": [
"accordion"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "accordion",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Collapsible Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::{VisibilityState, DisabledState};\n\n\n#[component]\npub fn CollapsiblePrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n #[prop(optional)] node_ref: Option<NodeRef<leptos::html::Div>>,\n) -> impl IntoView {\n let uid_col = crate::infra::uid::generate(\"col\");\n view! {\n <div\n data-rs-collapsible=\"\"\n data-rs-uid=uid_col\n data-rs-interaction=\"init\"\n data-rs-state=state.as_str()\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n aria-disabled=disabled.aria_disabled()\n class=class\n node_ref=node_ref.unwrap_or_default()\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn CollapsibleTriggerPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] controls: String,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-collapsible-trigger=\"\"\n data-rs-state=state.as_str()\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n aria-expanded=state.aria_expanded()\n aria-disabled=disabled.aria_disabled()\n aria-controls=if controls.is_empty() { None } else { Some(controls) }\n class=class\n >\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn CollapsibleContentPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] id: String,\n) -> impl IntoView {\n let content_id = if id.is_empty() { crate::infra::uid::generate(\"col-content\") } else { id };\n view! {\n <div\n data-rs-collapsible-content=\"\"\n data-rs-state=state.as_str()\n role=\"region\"\n aria-hidden=state.aria_hidden()\n id=content_id\n class=class\n >\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n CollapsiblePrimitive, CollapsibleTriggerPrimitive, CollapsibleContentPrimitive,\n};\nuse canonrs_core::meta::{VisibilityState, DisabledState};\nuse canonrs_core::infra::uid::generate;\n\n#[component]\npub fn Collapsible(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n #[prop(optional)] node_ref: Option<NodeRef<leptos::html::Div>>,\n) -> impl IntoView {\n view! {\n <CollapsiblePrimitive state=state disabled=disabled class=class node_ref=node_ref.unwrap_or_default()>\n {children()}\n </CollapsiblePrimitive>\n }\n}\n\n#[component]\npub fn CollapsibleTrigger(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] controls: String,\n) -> impl IntoView {\n view! {\n <CollapsibleTriggerPrimitive class=class controls=controls>\n {children()}\n </CollapsibleTriggerPrimitive>\n }\n}\n\n#[component]\npub fn CollapsibleContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] id: String,\n) -> impl IntoView {\n view! {\n <CollapsibleContentPrimitive class=class id=id>\n {children()}\n </CollapsibleContentPrimitive>\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! Collapsible Island — Canon Rule #340 (zero-logic boundary)\n//! CR-342 v3.0.0: interaction delegated to canonrs-interactions-init\n\nuse leptos::prelude::*;\nuse super::collapsible_ui::{\n Collapsible as CollapsibleUi,\n CollapsibleTrigger as CollapsibleTriggerUi,\n CollapsibleContent as CollapsibleContentUi\n};\nuse canonrs_core::meta::VisibilityState;\n\n#[component]\npub fn Collapsible(\n children: Children,\n #[prop(default = false)] open: bool,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let state = if open { VisibilityState::Open } else { VisibilityState::Closed };\n view! { <CollapsibleUi state=state class=class>{children()}</CollapsibleUi> }\n}\n\n#[component]\npub fn CollapsibleTrigger(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <CollapsibleTriggerUi class=class>{children()}</CollapsibleTriggerUi> }\n}\n\n#[component]\npub fn CollapsibleContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <CollapsibleContentUi class=class>{children()}</CollapsibleContentUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const COLLAPSIBLE_API: ComponentApi = ComponentApi {\n id: \"collapsible\",\n description: \"Collapsible section\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"open\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const COLLAPSIBLETRIGGER_API: ComponentApi = ComponentApi {\n id: \"collapsible-trigger\",\n description: \"Collapsible section\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const COLLAPSIBLECONTENT_API: ComponentApi = ComponentApi {\n id: \"collapsible-content\",\n description: \"Collapsible section\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::collapsible_boundary::{Collapsible, CollapsibleTrigger, CollapsibleContent};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn CollapsibleShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <Collapsible>\n <CollapsibleTrigger>\"Toggle details\"</CollapsibleTrigger>\n <CollapsibleContent>\n \"Hidden content revealed on toggle. State governed by signal.\"\n </CollapsibleContent>\n </Collapsible>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Single toggle — open/close state via signal. SSR-safe, hydration-safe.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Initially open\"</span>\n <Collapsible open=true>\n <CollapsibleTrigger>\"Advanced options\"</CollapsibleTrigger>\n <CollapsibleContent>\n \"These options are visible by default because open=true was passed.\"\n </CollapsibleContent>\n </Collapsible>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Nested\"</span>\n <Collapsible>\n <CollapsibleTrigger>\"Parent\"</CollapsibleTrigger>\n <CollapsibleContent>\n <Collapsible>\n <CollapsibleTrigger>\"Child\"</CollapsibleTrigger>\n <CollapsibleContent>\"Nested collapsible content.\"</CollapsibleContent>\n </Collapsible>\n </CollapsibleContent>\n </Collapsible>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "color_picker",
"label": "Color Picker",
"category": "Form",
"description": "Color picker input",
"keywords": "",
"pain": "Color inputs lack consistent selection and accessibility behavior",
"promise": "Color selection and state enforced via structured primitives",
"why": "ColorPickerPrimitive uses SelectionState and VisibilityState for interaction control. The contract ensures consistent value handling and accessibility. This prevents ad-hoc color input implementations.\n",
"before": "// ❌ Typical\nview! {\n <input type=\"color\" value=\"#000\" />\n}\n",
"after": "// ✅ CanonRS\nview! {\n <ColorPicker value=\"#000000\" />\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"theme customization",
"design tools"
],
"related": [
"select",
"combobox",
"radio",
"radio_group",
"slider"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "select",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! ColorPicker Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::{SelectionState, DisabledState, VisibilityState};\n\n#[component]\npub fn ColorPickerPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] name: String,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid = crate::infra::uid::generate(\"cp\");\n view! {\n <div\n data-rs-color-picker=\"\"\n data-rs-uid=uid\n data-rs-interaction=\"selection\"\n data-rs-state=state.as_str()\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n data-rs-name=name\n data-rs-value=value\n aria-disabled=disabled.aria_disabled()\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ColorPickerTriggerPrimitive(\n children: Children,\n #[prop(into, default = \"#000000\".to_string())] color: String,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-color-picker-trigger=\"\"\n data-rs-state=state.as_str()\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n data-rs-color=color\n aria-haspopup=\"dialog\"\n aria-expanded=state.aria_expanded()\n aria-disabled=disabled.aria_disabled()\n aria-label=\"Open color picker\"\n class=class\n >\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn ColorPickerInputPrimitive(\n #[prop(into, default = \"#000000\".to_string())] value: String,\n #[prop(into, default = String::new())] name: String,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <input\n type=\"color\"\n data-rs-color-picker-input=\"\"\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n disabled=disabled.disabled()\n value=value\n name=name\n aria-label=\"Color picker\"\n aria-disabled=disabled.aria_disabled()\n class=class\n />\n }\n}\n\n#[component]\npub fn ColorPickerSwatchPrimitive(\n #[prop(into, default = \"#000000\".to_string())] color: String,\n #[prop(default = SelectionState::Unselected)] selected: SelectionState,\n #[prop(into, default = String::new())] aria_label: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-color-swatch=\"\"\n data-rs-selection=if selected == SelectionState::Selected { Some(\"selected\") } else { None }\n data-rs-color=color\n aria-selected=if selected == SelectionState::Selected { Some(\"true\") } else { None }\n aria-label=aria_label\n class=class\n />\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n ColorPickerPrimitive, ColorPickerTriggerPrimitive, ColorPickerInputPrimitive,\n ColorPickerSwatchPrimitive,\n};\nuse canonrs_core::meta::{DisabledState, SelectionState, VisibilityState};\n\n#[component]\npub fn ColorPicker(#[prop(into, default = \"#000000\".to_string())] value: String, #[prop(into, default = String::new())] name: String, #[prop(default = DisabledState::Enabled)] disabled: DisabledState, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! {\n <ColorPickerPrimitive state=VisibilityState::Closed disabled=disabled name=name.clone() value=value.clone() class=class>\n <ColorPickerTriggerPrimitive state=VisibilityState::Closed disabled=disabled color=value.clone()>\n <ColorPickerInputPrimitive value=value name=name disabled=disabled />\n </ColorPickerTriggerPrimitive>\n </ColorPickerPrimitive>\n }\n}\n#[component]\npub fn ColorPickerSwatch(#[prop(into)] color: String, #[prop(default = SelectionState::Unselected)] selected: SelectionState, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <ColorPickerSwatchPrimitive color=color selected=selected class=class /> }\n}\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum ColorFormat { #[default] Hex, Rgb, Hsl, Cmyk }\nimpl ColorFormat {\n pub fn as_str(&self) -> &'static str {\n match self { Self::Hex => \"hex\", Self::Rgb => \"rgb\", Self::Hsl => \"hsl\", Self::Cmyk => \"cmyk\" }\n }\n}\n\n#[component]\npub fn ColorPickerDisplay(#[prop(into, default = \"#3b82f6\".to_string())] value: String, #[prop(default = ColorFormat::Hex)] format: ColorFormat, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <ColorPickerInputPrimitive value=value class=class /> }\n}\n#[component]\npub fn ColorPickerSwatches(children: Children, #[prop(into, default = \"#000000\".to_string())] value: String, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <div data-rs-color-picker-swatches=\"\" class=class>{children()}</div> }\n}\n",
"boundary_src": "//! @canon-level: strict\n//! ColorPicker Island — bootstrap only, delegates to interaction engine\n\nuse leptos::prelude::*;\nuse super::color_picker_ui::ColorFormat;\nuse canonrs_core::meta::{\n DisabledState,\n SelectionState\n};\n\n\n\n#[component]\npub fn ColorPicker(\n #[prop(into, default = \"#000000\".to_string())] value: String,\n #[prop(into, default = String::new())] name: String,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <super::color_picker_ui::ColorPicker value=value name=name disabled=disabled class=class />\n }\n}\n\n#[component]\npub fn ColorPickerSwatch(\n #[prop(into)] color: String,\n #[prop(default = SelectionState::Unselected)] selected: SelectionState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <super::color_picker_ui::ColorPickerSwatch color=color selected=selected class=class /> }\n}\n\n#[component]\npub fn ColorPickerDisplay(\n #[prop(into, default = \"#3b82f6\".to_string())] value: String,\n #[prop(default = ColorFormat::Hex)] format: ColorFormat,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <super::color_picker_ui::ColorPickerDisplay value=value format=format class=class /> }\n}\n\n#[component]\npub fn ColorPickerSwatches(\n #[prop(into, default = \"#3b82f6\".to_string())] value: String,\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n use super::color_picker_ui::ColorPickerSwatches as ColorPickerSwatchesUi;\n view! {\n <ColorPickerSwatchesUi value=value class=class>\n {children()}\n </ColorPickerSwatchesUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const COLORPICKER_API: ComponentApi = ComponentApi {\n id: \"color-picker\",\n description: \"Color picker input\",\n props: &[\n PropDef { name: \"value\", kind: PropType::String, required: false, default: Some(\"#000000\"), description: \"Current value\" },\n PropDef { name: \"name\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Form field name\" },\n PropDef { name: \"disabled\", kind: PropType::String, required: false, default: Some(\"enabled\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const COLORPICKERSWATCH_API: ComponentApi = ComponentApi {\n id: \"color-picker-swatch\",\n description: \"Color picker input\",\n props: &[\n PropDef { name: \"color\", kind: PropType::String, required: true, default: None, description: \"Prop value\" },\n PropDef { name: \"selected\", kind: PropType::String, required: false, default: Some(\"unselected\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const COLORPICKERDISPLAY_API: ComponentApi = ComponentApi {\n id: \"color-picker-display\",\n description: \"Color picker input\",\n props: &[\n PropDef { name: \"value\", kind: PropType::String, required: false, default: Some(\"#3b82f6\"), description: \"Current value\" },\n PropDef { name: \"format\", kind: PropType::String, required: false, default: Some(\"hex\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const COLORPICKERSWATCHES_API: ComponentApi = ComponentApi {\n id: \"color-picker-swatches\",\n description: \"Color picker input\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"value\", kind: PropType::String, required: false, default: Some(\"#3b82f6\"), description: \"Current value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::color_picker_boundary::{ColorPicker, ColorPickerSwatch, ColorPickerSwatches};\nuse canonrs_core::meta::DisabledState;\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn ColorPickerShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <ColorPicker value=\"#3b82f6\" />\n <p data-rs-showcase-preview-anchor=\"\">\n \"Color input with swatch preview — state governed by data-rs-state.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Swatches\"</span>\n <ColorPickerSwatches value=\"#3b82f6\">\n <ColorPickerSwatch color=\"#3b82f6\" />\n <ColorPickerSwatch color=\"#ef4444\" />\n <ColorPickerSwatch color=\"#22c55e\" />\n <ColorPickerSwatch color=\"#f59e0b\" />\n <ColorPickerSwatch color=\"#8b5cf6\" />\n <ColorPickerSwatch color=\"#ec4899\" />\n </ColorPickerSwatches>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Disabled\"</span>\n <ColorPicker value=\"#6b7280\" disabled=DisabledState::Disabled />\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "combobox",
"label": "Combobox",
"category": "Form",
"description": "Searchable combo box",
"keywords": "",
"pain": "Searchable selects break ARIA roles and keyboard navigation",
"promise": "Combobox roles and interaction fully enforced by structure",
"why": "ComboboxPrimitive defines proper ARIA roles and input behavior. SelectionState and VisibilityState control dropdown interaction. This guarantees accessible and predictable combobox behavior.\n",
"before": "// ❌ Typical\nview! {\n <input type=\"text\" />\n <ul><li>\"Option\"</li></ul>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Combobox>\n <ComboboxTrigger>\"Select...\"</ComboboxTrigger>\n <ComboboxList>\n <ComboboxItem value=\"1\">\"Option\"</ComboboxItem>\n </ComboboxList>\n </Combobox>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"autocomplete",
"search dropdown"
],
"related": [
"select",
"radio",
"radio_group",
"color_picker",
"slider"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "select",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Combobox Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::{SelectionState, DisabledState, VisibilityState};\n\n#[component]\npub fn ComboboxPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(optional)] node_ref: Option<NodeRef<leptos::html::Div>>,\n #[prop(into, default = String::new())] name: String,\n) -> impl IntoView {\n let uid = crate::infra::uid::generate(\"cbx\");\n view! {\n <div\n data-rs-combobox=\"\"\n data-rs-uid=uid\n data-rs-interaction=\"selection\"\n data-rs-state=state.as_str()\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n data-rs-name=name\n role=\"combobox\"\n aria-expanded=state.aria_expanded()\n aria-haspopup=\"listbox\"\n aria-disabled=disabled.aria_disabled()\n class=class\n node_ref=node_ref.unwrap_or_default()\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ComboboxInputPrimitive(\n #[prop(into, default = String::new())] placeholder: String,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <input\n data-rs-combobox-input=\"\"\n type=\"text\"\n placeholder=placeholder\n aria-autocomplete=\"list\"\n autocomplete=\"off\"\n aria-disabled=disabled.aria_disabled()\n class=class\n />\n }\n}\n\n#[component]\npub fn ComboboxListPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-combobox-list=\"\"\n role=\"listbox\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ComboboxItemPrimitive(\n children: Children,\n #[prop(default = SelectionState::Unselected)] selected: SelectionState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-combobox-item=\"\"\n data-rs-selection=if selected == SelectionState::Selected { Some(\"selected\") } else { None }\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n data-rs-value=value\n role=\"option\"\n tabindex=\"-1\"\n aria-selected=if selected == SelectionState::Selected { Some(\"true\") } else { None }\n aria-disabled=disabled.aria_disabled()\n class=class\n >\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n ComboboxPrimitive, ComboboxInputPrimitive,\n ComboboxListPrimitive, ComboboxItemPrimitive,\n};\nuse canonrs_core::meta::{DisabledState, SelectionState};\n\n#[component]\npub fn Combobox(\n children: Children,\n #[prop(optional)] node_ref: Option<NodeRef<leptos::html::Div>>,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] name: String,\n) -> impl IntoView {\n view! {\n <ComboboxPrimitive disabled=disabled class=class name=name node_ref=node_ref.unwrap_or_default()>\n {children()}\n </ComboboxPrimitive>\n }\n}\n\n#[component]\npub fn ComboboxInput(\n #[prop(into, default = String::new())] placeholder: String,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ComboboxInputPrimitive placeholder=placeholder disabled=disabled class=class />\n }\n}\n\n#[component]\npub fn ComboboxList(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ComboboxListPrimitive class=class>\n {children()}\n </ComboboxListPrimitive>\n }\n}\n\n#[component]\npub fn ComboboxItem(\n children: Children,\n #[prop(default = SelectionState::Unselected)] selected: SelectionState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ComboboxItemPrimitive selected=selected disabled=disabled value=value class=class>\n {children()}\n </ComboboxItemPrimitive>\n }\n}\n\n",
"boundary_src": "//! Combobox Island — Canon Rule passthrough\nuse leptos::prelude::*;\npub use super::combobox_ui::{ComboboxInput, ComboboxList, ComboboxItem};\n\n#[component]\npub fn Combobox(\n children: Children,\n #[prop(into, default = String::from(\"Search...\"))] placeholder: String,\n #[prop(default = canonrs_core::meta::DisabledState::Enabled)] disabled: canonrs_core::meta::DisabledState,\n #[prop(optional)] node_ref: Option<leptos::prelude::NodeRef<leptos::html::Div>>,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] name: String,\n) -> impl IntoView {\n view! {\n <super::combobox_ui::Combobox disabled=disabled class=class name=name node_ref=node_ref.unwrap_or_default()>\n <ComboboxInput placeholder=placeholder />\n <ComboboxList>\n {children()}\n </ComboboxList>\n </super::combobox_ui::Combobox>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const COMBOBOX_API: ComponentApi = ComponentApi {\n id: \"combobox\",\n description: \"Searchable combo box\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"placeholder\", kind: PropType::String, required: false, default: Some(\"Search...\"), description: \"Placeholder text\" },\n PropDef { name: \"disabled\", kind: PropType::String, required: false, default: Some(\"enabled\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"node_ref\", kind: PropType::String, required: false, default: None, description: \"DOM node reference\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n PropDef { name: \"name\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Form field name\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::combobox_boundary::{Combobox, ComboboxInput, ComboboxList, ComboboxItem};\nuse canonrs_core::meta::{DisabledState, SelectionState};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn ComboboxShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <Combobox placeholder=\"Search framework...\">\n <ComboboxItem value=\"leptos\">\"Leptos\"</ComboboxItem>\n <ComboboxItem value=\"dioxus\">\"Dioxus\"</ComboboxItem>\n <ComboboxItem value=\"yew\">\"Yew\"</ComboboxItem>\n <ComboboxItem value=\"react\" disabled=DisabledState::Disabled>\"React (disabled)\"</ComboboxItem>\n </Combobox>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Combobox roles and interaction fully enforced by structure.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Pre-selected\"</span>\n <Combobox>\n <ComboboxInput placeholder=\"Search size...\" />\n <ComboboxList>\n <ComboboxItem value=\"xs\">\"Extra Small\"</ComboboxItem>\n <ComboboxItem value=\"sm\">\"Small\"</ComboboxItem>\n <ComboboxItem value=\"md\" selected=SelectionState::Selected>\"Medium\"</ComboboxItem>\n <ComboboxItem value=\"lg\">\"Large\"</ComboboxItem>\n <ComboboxItem value=\"xl\">\"Extra Large\"</ComboboxItem>\n </ComboboxList>\n </Combobox>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Disabled\"</span>\n <Combobox disabled=DisabledState::Disabled>\n <ComboboxInput placeholder=\"Disabled...\" disabled=DisabledState::Disabled />\n <ComboboxList>\n <ComboboxItem value=\"a\">\"Option A\"</ComboboxItem>\n </ComboboxList>\n </Combobox>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "command",
"label": "Command",
"category": "Action",
"description": "Command palette",
"keywords": "",
"pain": "Command palette lacks keyboard navigation and ARIA consistency",
"promise": "Command palette semantics and selection fully enforced",
"why": "CommandPrimitive enforces listbox semantics with structured input, grouping and items. SelectionState and ActivityState control highlight and selection behavior. This guarantees consistent keyboard navigation and accessibility.\n",
"before": "// ❌ Typical\nview! {\n <input placeholder=\"Search\" />\n <div class=\"list\">\n <div>\"Item\"</div>\n </div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Command>\n <CommandInput placeholder=\"Search...\" />\n <CommandList>\n <CommandItem>\"Item\"</CommandItem>\n </CommandList>\n </Command>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"command palette",
"quick navigation"
],
"related": [
"dropdown_menu",
"context_menu",
"menubar",
"menu"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "menu",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Command Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::{SelectionState, DisabledState, ActivityState, VisibilityState};\n\n#[component]\npub fn CommandPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_cmd = crate::infra::uid::generate(\"cmd\");\n let listbox_id = crate::infra::uid::generate(\"cmd-list\");\n\n view! {\n <div\n data-rs-command=\"\"\n data-rs-uid=uid_cmd\n data-rs-interaction=\"init\"\n data-rs-state=state.as_str()\n data-rs-listbox-id=listbox_id.clone()\n role=\"dialog\"\n aria-label=\"Command palette\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn CommandInputPrimitive(\n #[prop(into, default = String::new())] placeholder: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-command-input-wrapper=\"\" class=class>\n <input\n data-rs-command-input=\"\"\n type=\"text\"\n role=\"combobox\"\n aria-autocomplete=\"list\"\n aria-expanded=\"false\"\n aria-haspopup=\"listbox\"\n placeholder=if placeholder.is_empty() { None } else { Some(placeholder) }\n />\n </div>\n }\n}\n\n#[component]\npub fn CommandListPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let list_id = crate::infra::uid::generate(\"cmd-list\");\n view! {\n <div\n data-rs-command-list=\"\"\n id=list_id\n role=\"listbox\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn CommandEmptyPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-command-empty=\"\"\n role=\"status\"\n aria-live=\"polite\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn CommandGroupPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-command-group=\"\"\n role=\"group\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn CommandGroupHeadingPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-command-group-heading=\"\"\n role=\"presentation\"\n aria-hidden=\"true\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn CommandItemPrimitive(\n children: Children,\n #[prop(into, default = String::new())] value: String,\n #[prop(default = SelectionState::Unselected)] selected: SelectionState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(default = ActivityState::Inactive)] highlighted: ActivityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-command-item=\"\"\n data-rs-value=value\n data-rs-activity=highlighted.as_str()\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n id=crate::infra::uid::generate(\"cmd-item\")\n role=\"option\"\n aria-selected=if selected == SelectionState::Selected { Some(\"true\") } else { None }\n aria-disabled=disabled.aria_disabled()\n tabindex=if disabled.disabled() { \"-1\" } else { \"0\" }\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn CommandSeparatorPrimitive(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-command-separator=\"\"\n role=\"separator\"\n aria-orientation=\"horizontal\"\n class=class\n />\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n CommandPrimitive, CommandInputPrimitive, CommandListPrimitive,\n CommandEmptyPrimitive, CommandGroupPrimitive, CommandGroupHeadingPrimitive,\n CommandItemPrimitive, CommandSeparatorPrimitive,\n};\nuse canonrs_core::meta::SelectionState;\n\n#[component]\npub fn Command(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = canonrs_core::meta::VisibilityState::Closed)] state: canonrs_core::meta::VisibilityState,\n) -> impl IntoView {\n view! {\n <CommandPrimitive class=class state=state>\n {children()}\n </CommandPrimitive>\n }\n}\n\n#[component]\npub fn CommandInput(\n #[prop(into, default = String::new())] placeholder: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <CommandInputPrimitive placeholder=placeholder class=class />\n }\n}\n\n#[component]\npub fn CommandList(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <CommandListPrimitive class=class>\n {children()}\n </CommandListPrimitive>\n }\n}\n\n#[component]\npub fn CommandEmpty(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <CommandEmptyPrimitive class=class>\n {children()}\n </CommandEmptyPrimitive>\n }\n}\n\n#[component]\npub fn CommandGroup(\n children: Children,\n #[prop(optional, into)] heading: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <CommandGroupPrimitive class=class>\n {heading.map(|h: String| view! {\n <CommandGroupHeadingPrimitive>{h}</CommandGroupHeadingPrimitive>\n })}\n {children()}\n </CommandGroupPrimitive>\n }\n}\n\n#[component]\npub fn CommandItem(\n children: Children,\n #[prop(optional, into)] value: Option<String>,\n #[prop(default = SelectionState::Unselected)] selected: SelectionState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <CommandItemPrimitive value=value.unwrap_or_default() selected=selected class=class>\n {children()}\n </CommandItemPrimitive>\n }\n}\n\n#[component]\npub fn CommandSeparator(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <CommandSeparatorPrimitive class=class />\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! Command Island — Canon Rule #340 (zero-logic boundary)\n\nuse leptos::prelude::*;\nuse super::command_ui::{\n CommandInput,\n CommandList,\n CommandItem as CommandItemUi\n};\nuse canonrs_core::meta::VisibilityState;\n\n#[component]\npub fn Command(\n children: Children,\n #[prop(into, default = String::from(\"Search...\"))] placeholder: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <super::command_ui::Command class=class state=VisibilityState::Open>\n <CommandInput placeholder=placeholder />\n <CommandList>{children()}</CommandList>\n </super::command_ui::Command>\n }\n}\n\n#[allow(unused_variables)]\n#[component]\npub fn CommandItem(\n children: Children,\n #[prop(into, optional)] value: Option<String>,\n #[prop(into, optional)] group: Option<String>,\n) -> impl IntoView {\n view! {\n <CommandItemUi value=value.unwrap_or_default()>\n {children()}\n </CommandItemUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const COMMAND_API: ComponentApi = ComponentApi {\n id: \"command\",\n description: \"Command palette\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"placeholder\", kind: PropType::String, required: false, default: Some(\"Search...\"), description: \"Placeholder text\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const COMMANDITEM_API: ComponentApi = ComponentApi {\n id: \"command-item\",\n description: \"Command palette\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"value\", kind: PropType::String, required: false, default: None, description: \"Current value\" },\n PropDef { name: \"group\", kind: PropType::String, required: false, default: None, description: \"Prop value\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::command_boundary::{Command, CommandItem};\nuse crate::blocks::card::CardBlock;\nuse crate::ui::card::{CardHeader, CardTitle, CardContent};\nuse canonrs_core::slot;\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn CommandShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <CardBlock\n header=slot!(|| view! {\n <CardHeader><CardTitle>\"Command Palette\"</CardTitle></CardHeader>\n }.into_any())\n content=slot!(|| view! {\n <CardContent>\n <Command placeholder=\"Search commands...\">\n <CommandItem value=\"calendar\" group=\"Suggestions\">\"Calendar\"</CommandItem>\n <CommandItem value=\"search\" group=\"Suggestions\">\"Search\"</CommandItem>\n <CommandItem value=\"settings\" group=\"Suggestions\">\"Settings\"</CommandItem>\n <CommandItem value=\"new\" group=\"Actions\">\"New file\"</CommandItem>\n <CommandItem value=\"open\" group=\"Actions\">\"Open file\"</CommandItem>\n <CommandItem value=\"save\" group=\"Actions\">\"Save\"</CommandItem>\n </Command>\n </CardContent>\n }.into_any())\n />\n <p data-rs-showcase-preview-anchor=\"\">\n \"Command palette — typeahead filter governed by DOM.\"\n </p>\n </Stack>\n }\n}\n",
"block": [
"card_block"
],
"blocks_primitives": [
"stack"
]
},
{
"id": "confirm_dialog",
"label": "Confirm Dialog",
"category": "Overlay",
"description": "Confirmation dialog",
"keywords": "",
"pain": "Confirmation dialogs miss ARIA roles and destructive intent signaling",
"promise": "Confirmation semantics and intent enforced via variant and structure",
"why": "ConfirmDialogPrimitive enforces role=\"alertdialog\" with variant-driven semantics. VisibilityState controls lifecycle and accessibility attributes. This guarantees proper focus and urgency communication.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"modal\">\"Are you sure?\"</div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <ConfirmDialog />\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"delete confirmation",
"critical actions"
],
"related": [
"dialog",
"alert_dialog",
"drawer",
"sheet",
"modal",
"tooltip",
"hover_card",
"popover"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "overlay",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! ConfirmDialog Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::VisibilityState;\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum ConfirmDialogVariant {\n #[default]\n Default,\n Destructive,\n Warning,\n}\nimpl ConfirmDialogVariant {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Default => \"primary\",\n Self::Destructive => \"destructive\",\n Self::Warning => \"warning\",\n }\n }\n}\n\n#[component]\npub fn ConfirmDialogPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(default = ConfirmDialogVariant::Default)] variant: ConfirmDialogVariant,\n #[prop(into, default = String::new())] uid: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_str = if uid.is_empty() { crate::infra::uid::generate(\"cd\") } else { uid };\n view! {\n <div\n data-rs-confirm-dialog=\"\"\n data-rs-interaction=\"overlay\"\n data-rs-uid=uid_str\n data-rs-variant=variant.as_str()\n data-rs-state=state.as_str()\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ConfirmDialogOverlayPrimitive(\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-confirm-dialog-overlay=\"\"\n data-rs-state=state.as_str()\n class=class\n />\n }\n}\n\n#[component]\npub fn ConfirmDialogTitlePrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <h2 data-rs-confirm-dialog-title=\"\" class=class>\n {children()}\n </h2>\n }\n}\n\n#[component]\npub fn ConfirmDialogDescriptionPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-confirm-dialog-description=\"\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ConfirmDialogCancelPrimitive(\n children: Children,\n #[prop(into, default = \"Cancel\".to_string())] aria_label: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-confirm-dialog-cancel=\"\"\n aria-label=aria_label\n class=class\n >\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn ConfirmDialogConfirmPrimitive(\n children: Children,\n #[prop(default = ConfirmDialogVariant::Default)] variant: ConfirmDialogVariant,\n #[prop(into, default = \"Confirm\".to_string())] aria_label: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-confirm-dialog-confirm=\"\"\n data-rs-variant=variant.as_str()\n aria-label=aria_label\n class=class\n >\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn ConfirmDialogTriggerPrimitive(\n children: Children,\n #[prop(default = ConfirmDialogVariant::Default)] variant: ConfirmDialogVariant,\n #[prop(into, default = String::new())] target: String,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] label: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-confirm-dialog-trigger=\"\"\n data-rs-variant=variant.as_str()\n data-rs-target=target\n data-rs-value=value\n data-rs-label=label\n aria-haspopup=\"alertdialog\"\n class=class\n >\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn ConfirmDialogPortalPrimitive(children: ChildrenFn) -> impl IntoView {\n view! {\n <div data-rs-confirm-dialog-portal=\"\">\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ConfirmDialogContentPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-confirm-dialog-content=\"\"\n data-rs-state=state.as_str()\n role=\"alertdialog\"\n aria-modal=\"true\"\n tabindex=\"-1\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ConfirmDialogFooterPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-confirm-dialog-footer=\"\" class=class>\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n ConfirmDialogPortalPrimitive,\n ConfirmDialogPrimitive, ConfirmDialogTriggerPrimitive,\n ConfirmDialogOverlayPrimitive, ConfirmDialogContentPrimitive,\n ConfirmDialogTitlePrimitive, ConfirmDialogDescriptionPrimitive,\n ConfirmDialogFooterPrimitive, ConfirmDialogCancelPrimitive,\n ConfirmDialogConfirmPrimitive, ConfirmDialogVariant,\n};\n\n#[component]\npub fn ConfirmDialog(\n children: Children,\n #[prop(default = ConfirmDialogVariant::Default)] variant: ConfirmDialogVariant,\n #[prop(default = canonrs_core::meta::VisibilityState::Closed)] state: canonrs_core::meta::VisibilityState,\n #[prop(into, default = String::new())] uid: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ConfirmDialogPrimitive variant=variant state=state uid=uid class=class>\n {children()}\n </ConfirmDialogPrimitive>\n }\n}\n\n#[component]\npub fn ConfirmDialogTrigger(\n children: Children,\n #[prop(default = ConfirmDialogVariant::Default)] variant: ConfirmDialogVariant,\n #[prop(into, default = String::new())] target: String,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] label: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <ConfirmDialogTriggerPrimitive variant=variant target=target value=value label=label class=class>{children()}</ConfirmDialogTriggerPrimitive> }\n}\n\n#[component]\npub fn ConfirmDialogPortal(children: ChildrenFn) -> impl IntoView {\n view! { <ConfirmDialogPortalPrimitive>{children()}</ConfirmDialogPortalPrimitive> }\n}\n\n#[component]\npub fn ConfirmDialogOverlay(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <ConfirmDialogOverlayPrimitive class=class /> }\n}\n\n#[component]\npub fn ConfirmDialogContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <ConfirmDialogContentPrimitive class=class>{children()}</ConfirmDialogContentPrimitive> }\n}\n\n#[component]\npub fn ConfirmDialogTitle(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <ConfirmDialogTitlePrimitive class=class>{children()}</ConfirmDialogTitlePrimitive> }\n}\n\n#[component]\npub fn ConfirmDialogDescription(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <ConfirmDialogDescriptionPrimitive class=class>{children()}</ConfirmDialogDescriptionPrimitive> }\n}\n\n#[component]\npub fn ConfirmDialogFooter(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <ConfirmDialogFooterPrimitive class=class>{children()}</ConfirmDialogFooterPrimitive> }\n}\n\n#[component]\npub fn ConfirmDialogCancel(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <ConfirmDialogCancelPrimitive class=class>{children()}</ConfirmDialogCancelPrimitive> }\n}\n\n#[component]\npub fn ConfirmDialogConfirm(\n children: Children,\n #[prop(default = ConfirmDialogVariant::Default)] variant: ConfirmDialogVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <ConfirmDialogConfirmPrimitive variant=variant class=class>{children()}</ConfirmDialogConfirmPrimitive> }\n}\n\n",
"boundary_src": "//! ConfirmDialog Island — Canon Rule #340 passthrough\nuse leptos::prelude::*;\nuse super::confirm_dialog_ui::{\n ConfirmDialog as ConfirmDialogUi,\n ConfirmDialogTrigger as ConfirmDialogTriggerUi,\n ConfirmDialogPortal as ConfirmDialogPortalUi,\n ConfirmDialogOverlay as ConfirmDialogOverlayUi,\n ConfirmDialogContent as ConfirmDialogContentUi,\n ConfirmDialogTitle as ConfirmDialogTitleUi,\n ConfirmDialogDescription as ConfirmDialogDescriptionUi,\n ConfirmDialogFooter as ConfirmDialogFooterUi,\n ConfirmDialogCancel as ConfirmDialogCancelUi,\n ConfirmDialogConfirm as ConfirmDialogConfirmUi,\n};\npub use canonrs_core::primitives::ConfirmDialogVariant;\n\n#[component]\npub fn ConfirmDialog(\n children: Children,\n #[prop(default = ConfirmDialogVariant::Default)] variant: ConfirmDialogVariant,\n #[prop(into, default = String::new())] uid: String,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <ConfirmDialogUi variant=variant uid=uid class=class.unwrap_or_default()>{children()}</ConfirmDialogUi> }\n}\n\n#[component]\npub fn ConfirmDialogTrigger(\n children: Children,\n #[prop(default = ConfirmDialogVariant::Default)] variant: ConfirmDialogVariant,\n #[prop(into, default = String::new())] target: String,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] label: String,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <ConfirmDialogTriggerUi variant=variant target=target value=value label=label class=class.unwrap_or_default()>{children()}</ConfirmDialogTriggerUi> }\n}\n\n#[component]\npub fn ConfirmDialogPortal(children: ChildrenFn) -> impl IntoView {\n view! { <ConfirmDialogPortalUi>{children()}</ConfirmDialogPortalUi> }\n}\n\n#[component]\npub fn ConfirmDialogOverlay(\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <ConfirmDialogOverlayUi class=class.unwrap_or_default() /> }\n}\n\n#[component]\npub fn ConfirmDialogContent(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <ConfirmDialogContentUi class=class.unwrap_or_default()>{children()}</ConfirmDialogContentUi> }\n}\n\n#[component]\npub fn ConfirmDialogTitle(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <ConfirmDialogTitleUi class=class.unwrap_or_default()>{children()}</ConfirmDialogTitleUi> }\n}\n\n#[component]\npub fn ConfirmDialogDescription(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <ConfirmDialogDescriptionUi class=class.unwrap_or_default()>{children()}</ConfirmDialogDescriptionUi> }\n}\n\n#[component]\npub fn ConfirmDialogFooter(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <ConfirmDialogFooterUi class=class.unwrap_or_default()>{children()}</ConfirmDialogFooterUi> }\n}\n\n#[component]\npub fn ConfirmDialogCancel(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <ConfirmDialogCancelUi class=class.unwrap_or_default()>{children()}</ConfirmDialogCancelUi> }\n}\n\n#[component]\npub fn ConfirmDialogConfirm(\n children: Children,\n #[prop(default = ConfirmDialogVariant::Default)] variant: ConfirmDialogVariant,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <ConfirmDialogConfirmUi variant=variant class=class.unwrap_or_default()>{children()}</ConfirmDialogConfirmUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{ConfirmDialogVariant}; \n\npub const CONFIRMDIALOG_API: ComponentApi = ComponentApi {\n id: \"confirm-dialog\",\n description: \"Confirmation dialog\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"variant\", kind: PropType::Enum(&[\"default\", \"destructive\", \"warning\"]), required: false, default: Some(\"default\"), description: \"Visual variant of the component\" },\n PropDef { name: \"uid\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const CONFIRMDIALOGTRIGGER_API: ComponentApi = ComponentApi {\n id: \"confirm-dialog-trigger\",\n description: \"Confirmation dialog\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"variant\", kind: PropType::Enum(&[\"default\", \"destructive\", \"warning\"]), required: false, default: Some(\"default\"), description: \"Visual variant of the component\" },\n PropDef { name: \"target\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Target element selector for copy\" },\n PropDef { name: \"value\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Current value\" },\n PropDef { name: \"label\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Accessible label text\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const CONFIRMDIALOGPORTAL_API: ComponentApi = ComponentApi {\n id: \"confirm-dialog-portal\",\n description: \"Confirmation dialog\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n ],\n};\n\npub const CONFIRMDIALOGCONTENT_API: ComponentApi = ComponentApi {\n id: \"confirm-dialog-content\",\n description: \"Confirmation dialog\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const CONFIRMDIALOGTITLE_API: ComponentApi = ComponentApi {\n id: \"confirm-dialog-title\",\n description: \"Confirmation dialog\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const CONFIRMDIALOGDESCRIPTION_API: ComponentApi = ComponentApi {\n id: \"confirm-dialog-description\",\n description: \"Confirmation dialog\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const CONFIRMDIALOGFOOTER_API: ComponentApi = ComponentApi {\n id: \"confirm-dialog-footer\",\n description: \"Confirmation dialog\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const CONFIRMDIALOGCANCEL_API: ComponentApi = ComponentApi {\n id: \"confirm-dialog-cancel\",\n description: \"Confirmation dialog\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const CONFIRMDIALOGCONFIRM_API: ComponentApi = ComponentApi {\n id: \"confirm-dialog-confirm\",\n description: \"Confirmation dialog\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"variant\", kind: PropType::Enum(&[\"default\", \"destructive\", \"warning\"]), required: false, default: Some(\"default\"), description: \"Visual variant of the component\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::confirm_dialog_boundary::{ConfirmDialog, ConfirmDialogTrigger, ConfirmDialogPortal, ConfirmDialogOverlay, ConfirmDialogContent, ConfirmDialogTitle, ConfirmDialogDescription, ConfirmDialogFooter, ConfirmDialogCancel, ConfirmDialogConfirm, ConfirmDialogVariant};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn ConfirmDialogShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <ConfirmDialog>\n <ConfirmDialogTrigger>\"Delete item\"</ConfirmDialogTrigger>\n <ConfirmDialogPortal>\n <ConfirmDialogOverlay />\n <ConfirmDialogContent>\n <ConfirmDialogTitle>\"Are you sure?\"</ConfirmDialogTitle>\n <ConfirmDialogDescription>\"This action cannot be undone.\"</ConfirmDialogDescription>\n <ConfirmDialogFooter>\n <ConfirmDialogCancel>\"Cancel\"</ConfirmDialogCancel>\n <ConfirmDialogConfirm variant=ConfirmDialogVariant::Destructive>\"Delete\"</ConfirmDialogConfirm>\n </ConfirmDialogFooter>\n </ConfirmDialogContent>\n </ConfirmDialogPortal>\n </ConfirmDialog>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Confirm dialog enforces action confirmation with variant-aware actions.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Warning variant\"</span>\n <ConfirmDialog variant=ConfirmDialogVariant::Warning>\n <ConfirmDialogTrigger variant=ConfirmDialogVariant::Warning>\"Archive project\"</ConfirmDialogTrigger>\n <ConfirmDialogPortal>\n <ConfirmDialogOverlay />\n <ConfirmDialogContent>\n <ConfirmDialogTitle>\"Archive this project?\"</ConfirmDialogTitle>\n <ConfirmDialogDescription>\"The project will be archived and hidden from your dashboard.\"</ConfirmDialogDescription>\n <ConfirmDialogFooter>\n <ConfirmDialogCancel>\"Cancel\"</ConfirmDialogCancel>\n <ConfirmDialogConfirm variant=ConfirmDialogVariant::Warning>\"Archive\"</ConfirmDialogConfirm>\n </ConfirmDialogFooter>\n </ConfirmDialogContent>\n </ConfirmDialogPortal>\n </ConfirmDialog>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"center",
"stack"
]
},
{
"id": "context_menu",
"label": "Context Menu",
"category": "Action",
"description": "Right-click context menu",
"keywords": "",
"pain": "Right-click menus lack consistent trigger and focus behavior",
"promise": "Context menu interaction and roles enforced structurally",
"why": "ContextMenuPrimitive defines trigger/content separation with ARIA roles. ActivityState and DisabledState ensure correct focus and navigation. This guarantees predictable contextual actions.\n",
"before": "// ❌ Typical\nview! {\n <div on:contextmenu=show_menu>\n <div class=\"menu\">\"Item\"</div>\n </div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <ContextMenu>\n <ContextMenuTrigger>\n <span>\"Right-click\"</span>\n </ContextMenuTrigger>\n <ContextMenuContent>\n <ContextMenuItem>\"Item\"</ContextMenuItem>\n </ContextMenuContent>\n </ContextMenu>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"file actions",
"contextual tools"
],
"related": [
"dropdown_menu",
"menubar",
"menu",
"command"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "menu",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! ContextMenu Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::{VisibilityState, DisabledState, ActivityState};\n\n\n#[component]\npub fn ContextMenuPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_cm = crate::infra::uid::generate(\"cm\");\n view! {\n <div\n data-rs-context-menu=\"\"\n data-rs-uid=uid_cm\n data-rs-interaction=\"overlay\"\n data-rs-state=state.as_str()\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ContextMenuTriggerPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-context-menu-trigger=\"\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ContextMenuContentPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-context-menu-content=\"\"\n data-rs-state=state.as_str()\n role=\"menu\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ContextMenuItemPrimitive(\n children: Children,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(default = ActivityState::Inactive)] highlighted: ActivityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-context-menu-item=\"\"\n role=\"menuitem\"\n data-rs-activity=highlighted.as_str()\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n aria-disabled=disabled.aria_disabled()\n tabindex=if disabled.disabled() { \"-1\" } else { \"0\" }\n class=class\n >\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn ContextMenuSeparatorPrimitive(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-context-menu-separator=\"\"\n role=\"separator\"\n class=class\n />\n }\n}\n\n#[component]\npub fn ContextMenuGroupPrimitive(\n children: Children,\n #[prop(into, optional)] label: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-context-menu-group=\"\"\n role=\"group\"\n aria-label=label\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ContextMenuLabelPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-context-menu-label=\"\"\n role=\"presentation\"\n aria-hidden=\"true\"\n class=class\n >\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::meta::VisibilityState;\nuse canonrs_core::primitives::{\n ContextMenuPrimitive, ContextMenuTriggerPrimitive, ContextMenuContentPrimitive,\n ContextMenuItemPrimitive, ContextMenuSeparatorPrimitive,\n};\n\n#[component]\npub fn ContextMenu(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ContextMenuPrimitive class=class>\n {children()}\n </ContextMenuPrimitive>\n }\n}\n\n#[component]\npub fn ContextMenuTrigger(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ContextMenuTriggerPrimitive class=class>\n {children()}\n </ContextMenuTriggerPrimitive>\n }\n}\n\n#[component]\npub fn ContextMenuContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ContextMenuContentPrimitive state=VisibilityState::Closed class=class>\n {children()}\n </ContextMenuContentPrimitive>\n }\n}\n\n#[component]\npub fn ContextMenuItem(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ContextMenuItemPrimitive class=class>\n {children()}\n </ContextMenuItemPrimitive>\n }\n}\n\n#[component]\npub fn ContextMenuSeparator(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ContextMenuSeparatorPrimitive class=class />\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! ContextMenu Island — passthrough only\n\nuse leptos::prelude::*;\nuse super::context_menu_ui::{\n ContextMenu as ContextMenuUi,\n ContextMenuTrigger as ContextMenuTriggerUi,\n ContextMenuContent as ContextMenuContentUi,\n ContextMenuItem as ContextMenuItemUi\n};\n\n#[component]\npub fn ContextMenu(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <ContextMenuUi class=class.unwrap_or_default()>{children()}</ContextMenuUi> }\n}\n\n#[component]\npub fn ContextMenuTrigger(children: Children) -> impl IntoView {\n view! { <ContextMenuTriggerUi>{children()}</ContextMenuTriggerUi> }\n}\n\n#[component]\npub fn ContextMenuContent(children: Children) -> impl IntoView {\n view! { <ContextMenuContentUi>{children()}</ContextMenuContentUi> }\n}\n\n#[component]\npub fn ContextMenuItem(children: Children) -> impl IntoView {\n view! { <ContextMenuItemUi>{children()}</ContextMenuItemUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const CONTEXTMENU_API: ComponentApi = ComponentApi {\n id: \"context-menu\",\n description: \"Right-click context menu\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const CONTEXTMENUTRIGGER_API: ComponentApi = ComponentApi {\n id: \"context-menu-trigger\",\n description: \"Right-click context menu\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n ],\n};\n\npub const CONTEXTMENUITEM_API: ComponentApi = ComponentApi {\n id: \"context-menu-item\",\n description: \"Right-click context menu\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::context_menu_boundary::{\n ContextMenu, ContextMenuTrigger, ContextMenuContent, ContextMenuItem,\n};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn ContextMenuShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <ContextMenu>\n <ContextMenuTrigger>\n <div data-rs-context-menu-target=\"\">\"Right-click here\"</div>\n </ContextMenuTrigger>\n <ContextMenuContent>\n <ContextMenuItem>\"Edit\"</ContextMenuItem>\n <ContextMenuItem>\"Duplicate\"</ContextMenuItem>\n <ContextMenuItem>\"Delete\"</ContextMenuItem>\n </ContextMenuContent>\n </ContextMenu>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Context menu appears at cursor position on right-click.\"\n </p>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "copy_button",
"label": "Copy Button",
"category": "Action",
"description": "Clipboard copy button",
"keywords": "",
"pain": "Copy buttons require manual state handling and feedback UI",
"promise": "Copy state lifecycle fully encoded in DOM state machine",
"why": "CopyButton encodes idle, copied, and error states via data-rs-state. Behavior layer handles transitions without JS wiring. This guarantees consistent feedback and eliminates manual state management.\n",
"before": "// ❌ Typical\nview! {\n <button on:click=copy>\"Copy\"</button>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <CopyButton id=\"copy\" text=\"value\" />\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"code snippets",
"share links"
],
"related": [
"button",
"button_group",
"icon_button",
"link"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "action",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! CopyButton Primitive - HTML puro\n\nuse leptos::prelude::*;\n\n#[component]\npub fn CopyButtonPrimitive(\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] id: String,\n #[prop(optional, into)] text: Option<String>,\n #[prop(optional, into)] target: Option<String>,\n #[prop(default = 2000)] reset_delay: u32,\n #[prop(into, default = \"Copy to clipboard\".to_string())] aria_label: String,\n) -> impl IntoView {\n let uid_cpb = crate::infra::uid::generate(\"cpb\");\n view! {\n <button\n class=class\n id=id\n data-rs-copy-button=\"\"\n data-rs-uid=uid_cpb\n data-rs-interaction=\"content\"\n data-rs-copy-text=text\n data-rs-copy-target=target\n data-rs-reset-delay=reset_delay.to_string()\n data-rs-activity=\"idle\"\n aria-label=aria_label\n >\n <span data-rs-copy-content=\"\">\n <svg data-rs-copy-icon=\"\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/>\n <path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/>\n </svg>\n <span data-rs-copy-label=\"\">\"Copy\"</span>\n </span>\n\n <span data-rs-copied-content=\"\">\n <svg data-rs-copied-icon=\"\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <path d=\"M20 6 9 17l-5-5\"/>\n </svg>\n <span data-rs-copied-label=\"\" role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\"Copied!\"</span>\n </span>\n\n <span data-rs-error-content=\"\">\n <svg data-rs-error-icon=\"\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/>\n <path d=\"m15 9-6 6M9 9l6 6\"/>\n </svg>\n <span data-rs-error-label=\"\" role=\"status\" aria-live=\"assertive\" aria-atomic=\"true\">\"Failed\"</span>\n </span>\n </button>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! CopyButton UI - SSR wrapper\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::CopyButtonPrimitive;\n\n#[component]\npub fn CopyButton(\n #[prop(optional, into)] text: Option<String>,\n #[prop(optional, into)] target: Option<String>,\n #[prop(default = 2000)] reset_delay: u32,\n #[prop(into, default = String::new())] class: String,\n #[prop(optional, into)] id: Option<String>,\n #[prop(into, default = \"Copy to clipboard\".to_string())] aria_label: String,\n) -> impl IntoView {\n view! {\n <CopyButtonPrimitive\n class=class\n id=id.unwrap_or_default()\n text=text.unwrap_or_default()\n target=target.unwrap_or_default()\n reset_delay=reset_delay\n aria_label=aria_label\n />\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! CopyButton Island — bootstrap only, delegates to interaction engine\n\nuse leptos::prelude::*;\nuse super::copy_button_ui::CopyButton as CopyButtonUi;\n\n#[component]\npub fn CopyButton(\n #[prop(optional, into)] text: Option<String>,\n #[prop(optional, into)] target: Option<String>,\n #[prop(default = 2000u32)] reset_delay: u32,\n #[prop(into, default = String::new())] class: String,\n #[prop(optional, into)] id: Option<String>,\n #[prop(into, default = \"Copy to clipboard\".to_string())] aria_label: String,\n) -> impl IntoView {\n view! {\n <CopyButtonUi\n text=text.unwrap_or_default()\n target=target.unwrap_or_default()\n reset_delay=reset_delay\n class=class\n id=id.unwrap_or_default()\n aria_label=aria_label\n />\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const COPYBUTTON_API: ComponentApi = ComponentApi {\n id: \"copy-button\",\n description: \"Clipboard copy button\",\n props: &[\n PropDef { name: \"text\", kind: PropType::String, required: false, default: None, description: \"Text to copy to clipboard\" },\n PropDef { name: \"target\", kind: PropType::String, required: false, default: None, description: \"Target element selector for copy\" },\n PropDef { name: \"reset_delay\", kind: PropType::Number, required: false, default: Some(\"2000u32\"), description: \"Delay in ms before resetting copy state\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n PropDef { name: \"id\", kind: PropType::String, required: false, default: None, description: \"Element id attribute\" },\n PropDef { name: \"aria_label\", kind: PropType::String, required: false, default: Some(\"Copy to clipboard\"), description: \"Accessible label for screen readers\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::copy_button_boundary::CopyButton;\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn CopyButtonShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <CopyButton text=\"cargo add canonrs\" aria_label=\"Copy to clipboard\" />\n <p data-rs-showcase-preview-anchor=\"\">\n \"Click to copy — state machine: idle → copied → idle\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"With target selector\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Md>\n <code id=\"snippet-1\">\"npm install canonrs\"</code>\n <CopyButton target=\"snippet-1\" aria_label=\"Copy npm command\" />\n </Stack>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Error state (no text — click to trigger)\"</span>\n <CopyButton aria_label=\"Copy empty — triggers error\" />\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "data_table",
"label": "Data Table",
"category": "Data",
"description": "Sortable data table component",
"keywords": "",
"pain": "Tables mix rendering, sorting and state with no structure",
"promise": "Table structure and sorting semantics enforced at component level",
"why": "DataTablePrimitive separates head, body and cells with explicit sort metadata. SelectionState and density are encoded as attributes. This guarantees consistent behavior across all tables.\n",
"before": "// ❌ Typical\nview! {\n <table>\n <tr><td>\"A\"</td></tr>\n </table>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <DataTableCore data=data columns=columns />\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"admin dashboards",
"data grids"
],
"related": [
"table",
"virtual_list",
"empty_table",
"tree",
"list_item"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "data",
"primitive_src": "#![allow(unused_variables)]\n//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! DataTable Primitive - HTML puro\n\nuse leptos::prelude::*;\nuse crate::primitives::table::SortDirection;\nuse crate::meta::SelectionState;\n\n#[derive(Debug, Clone, Copy, PartialEq, Default)]\npub enum DataTableDensity {\n Compact,\n #[default]\n Comfortable,\n Spacious,\n}\n\nimpl DataTableDensity {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Compact => \"compact\",\n Self::Comfortable => \"comfortable\",\n Self::Spacious => \"spacious\",\n }\n }\n}\n\n\n\n\n\n\n\n\n\n\n#[component]\npub fn DataTablePrimitive(\n children: Children,\n #[prop(default = DataTableDensity::Comfortable)] density: DataTableDensity,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] name: String,\n) -> impl IntoView {\n let uid_dt = crate::infra::uid::generate(\"dt\");\n view! {\n <div\n data-rs-datatable=\"\"\n data-rs-uid=uid_dt\n data-rs-interaction=\"data\"\n data-rs-density=density.as_str()\n data-rs-name=name\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn DataTableBulkBarPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-datatable-bulk-bar=\"\" hidden class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn DataTableToolbarPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-datatable-toolbar=\"\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn DataTableScrollPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-datatable-scroll=\"\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn DataTableTablePrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = false)] resizable: bool,\n) -> impl IntoView {\n view! {\n <table\n data-rs-datatable-table=\"\"\n data-rs-resizable=if resizable { Some(\"true\") } else { None }\n class=class\n >\n {children()}\n </table>\n }\n}\n\n#[component]\npub fn DataTableExpandHeadCellPrimitive(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <th data-rs-datatable-head-cell=\"\" data-rs-col-expand=\"\" scope=\"col\" class=class></th>\n }\n}\n\n#[component]\npub fn DataTableExpandCellPrimitive(\n children: Children,\n row_id: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <td data-rs-datatable-cell=\"\" data-rs-col-expand=\"\" class=class>\n {children()}\n </td>\n }\n}\n\n#[component]\npub fn DataTableExpandBtnPrimitive(\n row_id: String,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-datatable-expand-btn=\"\"\n data-rs-row-id=row_id\n aria-expanded=\"false\"\n >\n \"▶\"\n </button>\n }\n}\n\n#[component]\npub fn DataTableExpandRowPrimitive(\n children: Children,\n row_id: String,\n colspan: String,\n) -> impl IntoView {\n view! {\n <tr data-rs-datatable-expand-row=\"\" data-rs-row-id=row_id hidden=true>\n <td data-rs-datatable-cell=\"\" colspan=colspan>\n <div data-rs-datatable-expand-content=\"\">\n {children()}\n </div>\n </td>\n </tr>\n }\n}\n\n#[component]\npub fn DataTableColgroupPrimitive(\n children: Children,\n) -> impl IntoView {\n view! {\n <colgroup data-rs-datatable-colgroup=\"\">\n {children()}\n </colgroup>\n }\n}\n\n#[component]\npub fn DataTableColPrimitive(\n #[prop(into, default = String::new())] col_index: String,\n #[prop(into, default = String::new())] width: String,\n) -> impl IntoView {\n view! {\n <col\n data-rs-datatable-col=\"\"\n data-rs-col-index=col_index\n style=if width.is_empty() { String::new() } else { format!(\"width:{}\", width) }\n />\n }\n}\n\n#[component]\npub fn DataTableHeadPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <thead data-rs-datatable-head=\"\" data-rs-resize-container=\"\" class=class>\n {children()}\n </thead>\n }\n}\n\n#[component]\npub fn DataTableHeadRowPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <tr data-rs-datatable-head-row=\"\" class=class>\n {children()}\n </tr>\n }\n}\n\n#[component]\npub fn DataTableHeadCellPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] sort_key: String,\n #[prop(default = SortDirection::None)] sort_direction: SortDirection,\n #[prop(into, default = String::new())] col_index: String,\n #[prop(into, default = String::new())] style: String,\n #[prop(default = false)] resizable: bool,\n) -> impl IntoView {\n view! {\n <th\n data-rs-datatable-head-cell=\"\"\n scope=\"col\"\n role=\"columnheader\"\n aria-sort=sort_direction.aria_sort()\n data-rs-sort=sort_direction.as_str()\n data-rs-sort-key=sort_key\n data-rs-col-index=col_index\n data-rs-resizable=if resizable { Some(\"true\") } else { None }\n style=style\n class=class\n >\n {children()}\n {resizable.then(|| view! {\n <span data-rs-datatable-resize-handle=\"\" aria-hidden=\"true\"></span>\n })}\n </th>\n }\n}\n\n#[component]\npub fn DataTableBodyPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <tbody data-rs-datatable-body=\"\" class=class>\n {children()}\n </tbody>\n }\n}\n\n#[component]\npub fn DataTableRowPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] row_id: String,\n #[prop(into, default = String::new())] row_label: String,\n #[prop(default = SelectionState::Unselected)] selected: SelectionState,\n #[prop(optional)] row_index: Option<usize>,\n) -> impl IntoView {\n let is_selected = selected == SelectionState::Selected;\n let aria_rowindex = row_index.map(|i| (i + 1).to_string()).unwrap_or_default();\n let row_index_str = row_index.map(|i| i.to_string()).unwrap_or_default();\n view! {\n <tr\n data-rs-datatable-row=\"\"\n data-rs-selection=if is_selected { \"selected\" } else { \"unselected\" }\n data-rs-row-id=row_id\n data-rs-row-label=row_label\n aria-selected=if is_selected { \"true\" } else { \"false\" }\n aria-rowindex=aria_rowindex\n data-rs-row-index=row_index_str\n class=class\n >\n {children()}\n </tr>\n }\n}\n\n#[component]\npub fn DataTableCellPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] col_index: String,\n #[prop(into, default = String::new())] style: String,\n) -> impl IntoView {\n view! {\n <td\n data-rs-datatable-cell=\"\"\n data-rs-col-index=col_index\n style=style\n class=class\n >\n {children()}\n </td>\n }\n}\n\n#[component]\npub fn DataTableFooterPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <tfoot data-rs-datatable-footer=\"\" class=class>\n {children()}\n </tfoot>\n }\n}\n\n#[component]\npub fn DataTablePaginationPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-datatable-pagination=\"\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn DataTableEmptyPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-datatable-empty=\"\"\n data-rs-activity=\"empty\"\n role=\"status\"\n aria-live=\"polite\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn DataTableLoadingPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-datatable-loading=\"\"\n data-rs-loading=\"loading\"\n role=\"status\"\n aria-live=\"polite\"\n class=class\n >\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n//! DataTable Full - HTML estático, comportamento delegado ao behavior JS\n\nuse leptos::prelude::*;\nuse crate::ui::scroll_area::scroll_area_boundary::ScrollArea;\nuse std::sync::Arc;\nuse canonrs_core::primitives::{\n DataTableBulkBarPrimitive,\n DataTablePrimitive, DataTableToolbarPrimitive,\n DataTableTablePrimitive, DataTableHeadPrimitive, DataTableHeadRowPrimitive,\n DataTableHeadCellPrimitive, DataTableBodyPrimitive, DataTableRowPrimitive,\n DataTableCellPrimitive, DataTableEmptyPrimitive,\n DataTableColgroupPrimitive, DataTableColPrimitive,\n DataTableExpandHeadCellPrimitive, DataTableExpandCellPrimitive,\n DataTableExpandBtnPrimitive, DataTableExpandRowPrimitive,\n DataTableDensity, SortDirection,\n};\nuse crate::ui::dropdown_menu::{\n DropdownMenu, DropdownMenuItem, DropdownMenuCheckboxItem,\n};\nuse super::types::{RowIdFn, RowLabelFn, ExpandRenderFn};\nuse crate::ui::context_menu::context_menu_boundary::{\n ContextMenuContent, ContextMenuItem,\n};\n\n#[derive(Clone)]\npub struct DataTableColumn<T> {\n pub key: String,\n pub label: String,\n pub render: Arc<dyn Fn(&T) -> String + Send + Sync>,\n}\n\nimpl<T> DataTableColumn<T> {\n pub fn new(key: impl Into<String>, label: impl Into<String>, render: impl Fn(&T) -> String + Send + Sync + 'static) -> Self {\n Self { key: key.into(), label: label.into(), render: Arc::new(render) }\n }\n}\n\n#[derive(Clone)]\npub struct BulkAction {\n pub id: &'static str,\n pub label: &'static str,\n pub danger: bool,\n}\n\nimpl BulkAction {\n pub fn new(id: &'static str, label: &'static str) -> Self {\n Self { id, label, danger: false }\n }\n pub fn danger(mut self) -> Self { self.danger = true; self }\n}\n\n#[derive(Clone)]\npub struct RowAction {\n pub id: &'static str,\n pub label: &'static str,\n pub danger: bool,\n pub inline: bool,\n}\n\nimpl RowAction {\n pub fn new(id: &'static str, label: &'static str) -> Self {\n Self { id, label, danger: false, inline: false }\n }\n pub fn danger(mut self) -> Self { self.danger = true; self }\n pub fn inline(mut self) -> Self { self.inline = true; self }\n}\n\n#[component]\npub fn DataTableStatic<T>(\n data: Vec<T>,\n columns: Vec<DataTableColumn<T>>,\n #[prop(default = DataTableDensity::default())] density: DataTableDensity,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = 10)] page_size: usize,\n #[prop(default = false)] selectable: bool,\n #[prop(into, default = String::new())] sync_chart: String,\n #[prop(into, default = String::new())] sync_scope: String,\n #[prop(default = false)] show_density: bool,\n #[prop(default = false)] resizable: bool,\n #[prop(default = ExpandRenderFn::default())] expand_render: ExpandRenderFn<T>,\n #[prop(default = vec![])] row_actions: Vec<RowAction>,\n #[prop(default = vec![])] bulk_actions: Vec<BulkAction>,\n #[prop(default = RowIdFn::default())] row_id_fn: RowIdFn<T>,\n #[prop(default = RowLabelFn::default())] row_label_fn: RowLabelFn<T>,\n) -> impl IntoView\nwhere\n T: Clone + Send + Sync + 'static,\n{\n let total = data.len();\n let total_pages = ((total as f64) / (page_size as f64)).ceil().max(1.0) as usize;\n let has_expand = expand_render.0.is_some();\n let col_count = columns.len()\n + if selectable { 1 } else { 0 }\n + if has_expand { 1 } else { 0 }\n + if !row_actions.is_empty() { 1 } else { 0 };\n let visible_data = StoredValue::new(data.into_iter().enumerate().collect::<Vec<_>>());\n let cols = StoredValue::new(columns.clone());\n let expand_render = StoredValue::new(expand_render.0);\n let row_actions = StoredValue::new(row_actions);\n let bulk_actions = StoredValue::new(bulk_actions);\n let row_id_fn = StoredValue::new(row_id_fn.0);\n let row_label_fn = StoredValue::new(row_label_fn.0);\n let initial_density = density.as_str();\n\n view! {\n <DataTablePrimitive\n density=density\n class=class\n attr:data-rs-page-size=page_size.to_string()\n attr:data-rs-current-page=\"1\"\n attr:data-rs-total-pages=total_pages.to_string()\n attr:data-rs-selectable={selectable.then(|| \"true\")}\n attr:data-rs-chart-sync=sync_chart.clone()\n attr:data-rs-chart-sync-scope=sync_scope.clone()\n >\n <DataTableBulkBarPrimitive>\n <span data-rs-datatable-bulk-count=\"\">\"0 selected\"</span>\n <div data-rs-datatable-bulk-actions=\"\">\n {bulk_actions.get_value().into_iter().map(|action| {\n view! {\n <button\n type=\"button\"\n data-rs-datatable-bulk-action=action.id\n class={if action.danger { \"danger\".to_string() } else { String::new() }}\n >\n {action.label}\n </button>\n }\n }).collect::<Vec<_>>()}\n </div>\n <button type=\"button\" data-rs-datatable-bulk-clear=\"\">\"✕ Clear\"</button>\n </DataTableBulkBarPrimitive>\n <DataTableToolbarPrimitive>\n <input type=\"text\" data-rs-datatable-filter=\"\" placeholder=\"Search...\" />\n <div data-rs-datatable-density-toggle=\"\" hidden=(!show_density)>\n <button type=\"button\" data-rs-density-btn=\"compact\"\n data-active={if initial_density == \"compact\" { \"true\" } else { \"false\" }}>\n \"Compact\"\n </button>\n <button type=\"button\" data-rs-density-btn=\"comfortable\"\n data-active={if initial_density == \"comfortable\" { \"true\" } else { \"false\" }}>\n \"Comfortable\"\n </button>\n <button type=\"button\" data-rs-density-btn=\"spacious\"\n data-active={if initial_density == \"spacious\" { \"true\" } else { \"false\" }}>\n \"Spacious\"\n </button>\n </div>\n <DropdownMenu trigger_label=\"Columns\">\n {columns.iter().enumerate().map(|(idx, col)| {\n let label = col.label.clone();\n view! {\n <DropdownMenuCheckboxItem attr:data-rs-col-index=idx.to_string()>\n {label}\n </DropdownMenuCheckboxItem>\n }\n }).collect::<Vec<_>>()}\n </DropdownMenu>\n </DataTableToolbarPrimitive>\n\n <ScrollArea orientation=canonrs_core::primitives::ScrollOrientation::Horizontal auto_hide=false>\n <DataTableTablePrimitive resizable=resizable>\n <DataTableColgroupPrimitive>\n <col data-rs-datatable-col=\"\" data-rs-col-expand=\"\" class=if has_expand { \"\" } else { \"rs-col-hidden\" } />\n <col data-rs-datatable-col=\"\" data-rs-col-select=\"\" class=if selectable { \"\" } else { \"rs-col-hidden\" } />\n {cols.get_value().into_iter().enumerate().map(|(idx, _)| {\n view! { <DataTableColPrimitive col_index=idx.to_string() /> }\n }).collect::<Vec<_>>()}\n <col data-rs-datatable-col=\"\" data-rs-col-actions=\"\" class=if !row_actions.get_value().is_empty() { \"\" } else { \"rs-col-hidden\" } />\n </DataTableColgroupPrimitive>\n <DataTableHeadPrimitive>\n <DataTableHeadRowPrimitive>\n <DataTableExpandHeadCellPrimitive class=if has_expand { \"\" } else { \"rs-col-hidden\" } />\n <th data-rs-datatable-head-cell=\"\" scope=\"col\" data-rs-col-select=\"\" class=if selectable { \"\" } else { \"rs-col-hidden\" }>\n <input type=\"checkbox\" data-rs-datatable-select-all=\"\" />\n </th>\n {cols.get_value().into_iter().enumerate().map(|(idx, col)| {\n let key = col.key.clone();\n let label = col.label.clone();\n view! {\n <DataTableHeadCellPrimitive\n sort_key=key\n sort_direction=SortDirection::None\n col_index=idx.to_string()\n resizable=resizable\n >\n <span data-rs-datatable-head-label=\"\">{label}</span>\n <span data-rs-datatable-sort-icon=\"\" aria-hidden=\"true\">\"↕\"</span>\n </DataTableHeadCellPrimitive>\n }\n }).collect::<Vec<_>>()}\n </DataTableHeadRowPrimitive>\n </DataTableHeadPrimitive>\n\n <DataTableBodyPrimitive>\n {visible_data.get_value().into_iter().map(|(idx, row)| {\n let row_cols = cols.get_value();\n let expand_content: Option<AnyView> = expand_render.get_value().as_ref().map(|f| f(&row));\n let has_expand_row = expand_content.is_some();\n let has_actions = !row_actions.get_value().is_empty();\n let ctx_actions = row_actions.get_value();\n let row_label = row_label_fn.get_value().as_ref().map(|f| f(&row)).unwrap_or_default();\n let real_id = row_id_fn.get_value().as_ref().map(|f| f(&row)).unwrap_or_else(|| idx.to_string());\n let real_id = StoredValue::new(real_id);\n let ctx_row_id = real_id;\n\n let main_row = view! {\n <DataTableRowPrimitive row_id=real_id.get_value() row_label=row_label row_index=idx>\n {\n let rid = real_id.get_value();\n view! {\n <DataTableExpandCellPrimitive row_id=rid.clone() class=if has_expand { \"\" } else { \"rs-col-hidden\" }>\n {if has_expand { Some(view! { <DataTableExpandBtnPrimitive row_id=rid /> }) } else { None }}\n </DataTableExpandCellPrimitive>\n }\n }\n <td data-rs-datatable-cell=\"\" data-rs-col-select=\"\" class=if selectable { \"\" } else { \"rs-col-hidden\" }>\n <input type=\"checkbox\" data-rs-datatable-select-row=\"\" value=idx.to_string() />\n </td>\n {row_cols.into_iter().enumerate().map(|(col_idx, col)| {\n let value = (col.render)(&row);\n view! {\n <DataTableCellPrimitive col_index=col_idx.to_string()>\n {value}\n </DataTableCellPrimitive>\n }\n }).collect::<Vec<_>>()}\n {\n let actions = row_actions.get_value();\n let has_actions_cell = !actions.is_empty();\n let row_id = ctx_row_id.get_value();\n let inline_actions: Vec<RowAction> = actions.iter().filter(|a| a.inline).cloned().collect();\n let menu_actions: Vec<RowAction> = actions.iter().filter(|a| !a.inline).cloned().collect();\n let has_menu = !menu_actions.is_empty();\n let inline_views = inline_actions.into_iter().map(|action| {\n let rid = row_id.clone();\n view! {\n <button type=\"button\"\n data-rs-datatable-action=action.id\n data-rs-row-id=rid\n data-rs-datatable-inline-action=\"\"\n class={if action.danger { \"danger\".to_string() } else { String::new() }}\n >\n {action.label}\n </button>\n }\n }).collect::<Vec<_>>();\n let menu_views = menu_actions.into_iter().map(|action| {\n let rid = row_id.clone();\n view! {\n <DropdownMenuItem\n class={if action.danger { \"danger\".to_string() } else { String::new() }}\n >\n <span data-rs-datatable-action=action.id data-rs-row-id=rid>\n {action.label}\n </span>\n </DropdownMenuItem>\n }\n }).collect::<Vec<_>>();\n view! {\n <td data-rs-datatable-cell=\"\" data-rs-col-actions=\"\" class=if has_actions_cell { \"\" } else { \"rs-col-hidden\" }>\n <div data-rs-datatable-actions-cell=\"\">\n <div data-rs-datatable-inline-actions=\"\">{inline_views}</div>\n <div data-rs-datatable-menu-actions=\"\" hidden=(!has_menu)>\n <DropdownMenu>{menu_views}</DropdownMenu>\n </div>\n </div>\n </td>\n }\n }\n </DataTableRowPrimitive>\n };\n\n view! {\n <>\n {main_row}\n {expand_content.map(|content| {\n let rid = real_id.get_value();\n view! {\n <DataTableExpandRowPrimitive row_id=rid colspan=col_count.to_string()>\n {content}\n </DataTableExpandRowPrimitive>\n }\n })}\n </>\n }\n }).collect::<Vec<_>>()}\n </DataTableBodyPrimitive>\n </DataTableTablePrimitive></ScrollArea>\n\n <div data-rs-datatable-context-menus=\"\">\n {visible_data.get_value().into_iter().map(|(idx, row)| {\n let has_actions = !row_actions.get_value().is_empty();\n let ctx_actions = row_actions.get_value();\n let real_id2 = row_id_fn.get_value().as_ref().map(|f| f(&row)).unwrap_or_else(|| idx.to_string());\n let ctx_row_id2 = real_id2.clone();\n view! {\n <div data-rs-datatable-row-context=\"\" data-rs-context-menu=\"\"\n data-rs-row-id=ctx_row_id2 hidden=(!has_actions)>\n <ContextMenuContent>\n {ctx_actions.into_iter().map(|action| {\n let rid2 = real_id2.clone();\n view! {\n <ContextMenuItem>\n <span data-rs-datatable-action=action.id data-rs-row-id=rid2>\n {action.label}\n </span>\n </ContextMenuItem>\n }\n }).collect::<Vec<_>>()}\n </ContextMenuContent>\n </div>\n }\n }).collect::<Vec<_>>()}\n </div>\n\n <DataTableEmptyPrimitive class=\"hidden\".to_string()>\n \"No results found.\"\n </DataTableEmptyPrimitive>\n <div data-rs-datatable-pagination=\"\">\n <button type=\"button\" data-rs-action=\"prev\" data-rs-datatable-pagination-btn=\"\" disabled=true>\n \"Previous\"\n </button>\n <span data-rs-pagination-info=\"\">{format!(\"1 of {}\", total_pages)}</span>\n <button type=\"button\" data-rs-action=\"next\" data-rs-datatable-pagination-btn=\"\" disabled={total_pages <= 1}>\n \"Next\"\n </button>\n </div>\n </DataTablePrimitive>\n }\n}\n",
"boundary_src": "//! @canon-level: strict\n//! DataTable Island — Canon Rule #340 (zero-logic passthrough)\n\nuse leptos::prelude::*;\npub use canonrs_core::primitives::DataTableDensity;\npub use super::data_table_ui::{\n DataTableColumn,\n RowAction,\n BulkAction\n};\nuse super::data_table_ui::DataTableStatic;\nuse super::types::{ExpandRenderFn, RowIdFn, RowLabelFn};\n\n#[component]\npub fn DataTable<T>(\n data: Vec<T>,\n columns: Vec<DataTableColumn<T>>,\n #[prop(default = DataTableDensity::default())] density: DataTableDensity,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = 10)] page_size: usize,\n #[prop(default = false)] selectable: bool,\n #[prop(default = false)] show_density: bool,\n #[prop(default = false)] resizable: bool,\n #[prop(default = vec![])] row_actions: Vec<RowAction>,\n #[prop(default = vec![])] bulk_actions: Vec<BulkAction>,\n #[prop(optional)] expand_render: Option<std::sync::Arc<dyn Fn(&T) -> leptos::prelude::AnyView + Send + Sync>>,\n #[prop(optional)] row_id_fn: Option<std::sync::Arc<dyn Fn(&T) -> String + Send + Sync>>,\n #[prop(optional)] row_label_fn: Option<std::sync::Arc<dyn Fn(&T) -> String + Send + Sync>>,\n) -> impl IntoView\nwhere\n T: Clone + Send + Sync + 'static,\n{\n match (expand_render, row_id_fn, row_label_fn) {\n (Some(er), Some(ri), Some(rl)) => view! {\n <DataTableStatic\n data=data columns=columns density=density class=class\n page_size=page_size selectable=selectable show_density=show_density resizable=resizable\n row_actions=row_actions bulk_actions=bulk_actions\n expand_render=ExpandRenderFn(Some(er)) row_id_fn=RowIdFn(Some(ri)) row_label_fn=RowLabelFn(Some(rl))\n />\n }.into_any(),\n (Some(er), None, None) => view! {\n <DataTableStatic\n data=data columns=columns density=density class=class\n page_size=page_size selectable=selectable show_density=show_density resizable=resizable\n row_actions=row_actions bulk_actions=bulk_actions\n expand_render=ExpandRenderFn(Some(er))\n />\n }.into_any(),\n (Some(er), Some(ri), None) => view! {\n <DataTableStatic\n data=data columns=columns density=density class=class\n page_size=page_size selectable=selectable show_density=show_density resizable=resizable\n row_actions=row_actions bulk_actions=bulk_actions\n expand_render=ExpandRenderFn(Some(er)) row_id_fn=RowIdFn(Some(ri))\n />\n }.into_any(),\n (None, Some(ri), Some(rl)) => view! {\n <DataTableStatic\n data=data columns=columns density=density class=class\n page_size=page_size selectable=selectable show_density=show_density resizable=resizable\n row_actions=row_actions bulk_actions=bulk_actions\n row_id_fn=RowIdFn(Some(ri)) row_label_fn=RowLabelFn(Some(rl))\n />\n }.into_any(),\n (None, Some(ri), None) => view! {\n <DataTableStatic\n data=data columns=columns density=density class=class\n page_size=page_size selectable=selectable show_density=show_density resizable=resizable\n row_actions=row_actions bulk_actions=bulk_actions\n row_id_fn=RowIdFn(Some(ri))\n />\n }.into_any(),\n _ => view! {\n <DataTableStatic\n data=data columns=columns density=density class=class\n page_size=page_size selectable=selectable show_density=show_density resizable=resizable\n row_actions=row_actions bulk_actions=bulk_actions\n />\n }.into_any(),\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{DataTableDensity}; \n\npub const DATATABLE<T>_API: ComponentApi = ComponentApi {\n id: \"data-table<-t>\",\n description: \"Sortable data table component\",\n props: &[\n PropDef { name: \"data\", kind: PropType::String, required: true, default: None, description: \"Prop value\" },\n PropDef { name: \"columns\", kind: PropType::String, required: true, default: None, description: \"Prop value\" },\n PropDef { name: \"density\", kind: PropType::Enum(&[\"compact\", \"comfortable\", \"spacious\"]), required: false, default: Some(\"default\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n PropDef { name: \"page_size\", kind: PropType::Number, required: false, default: Some(\"10\"), description: \"Prop value\" },\n PropDef { name: \"selectable\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"show_density\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"resizable\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \")] row_actions\", kind: PropType::String, required: false, default: Some(\"vec![]\"), description: \"Prop value\" },\n PropDef { name: \")] bulk_actions\", kind: PropType::String, required: false, default: Some(\"vec![]\"), description: \"Prop value\" },\n PropDef { name: \"expand_render\", kind: PropType::String, required: false, default: None, description: \"Prop value\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::data_table_boundary::DataTable;\nuse super::data_table_boundary::{DataTableColumn, RowAction, BulkAction};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\nuse canonrs_core::primitives::layout::grid::{GridPrimitive as Grid, GridCols, GridGap};\n\nfn sample_data() -> Vec<Vec<String>> {\n vec![\n vec![\"Alice\".to_string(), \"Engineer\".to_string(), \"Active\".to_string(), \"98\".to_string()],\n vec![\"Bob\".to_string(), \"Designer\".to_string(), \"Active\".to_string(), \"87\".to_string()],\n vec![\"Carol\".to_string(), \"Manager\".to_string(), \"Away\".to_string(), \"76\".to_string()],\n vec![\"Dave\".to_string(), \"Engineer\".to_string(), \"Inactive\".to_string(), \"65\".to_string()],\n vec![\"Eve\".to_string(), \"Designer\".to_string(), \"Active\".to_string(), \"91\".to_string()],\n vec![\"Frank\".to_string(), \"DevOps\".to_string(), \"Active\".to_string(), \"82\".to_string()],\n vec![\"Grace\".to_string(), \"QA\".to_string(), \"Active\".to_string(), \"79\".to_string()],\n vec![\"Henry\".to_string(), \"Manager\".to_string(), \"Away\".to_string(), \"88\".to_string()],\n vec![\"Iris\".to_string(), \"Engineer\".to_string(), \"Active\".to_string(), \"95\".to_string()],\n vec![\"Jack\".to_string(), \"Designer\".to_string(), \"Inactive\".to_string(), \"71\".to_string()],\n vec![\"Karen\".to_string(), \"DevOps\".to_string(), \"Active\".to_string(), \"84\".to_string()],\n vec![\"Leo\".to_string(), \"QA\".to_string(), \"Active\".to_string(), \"77\".to_string()],\n ]\n}\n\nfn sample_columns() -> Vec<DataTableColumn<Vec<String>>> {\n vec![\n DataTableColumn::new(\"name\", \"Name\", |r: &Vec<String>| r[0].clone()),\n DataTableColumn::new(\"role\", \"Role\", |r: &Vec<String>| r[1].clone()),\n DataTableColumn::new(\"status\", \"Status\", |r: &Vec<String>| r[2].clone()),\n DataTableColumn::new(\"score\", \"Score\", |r: &Vec<String>| r[3].clone()),\n ]\n}\n\nfn expand_data() -> Vec<Vec<String>> {\n vec![\n vec![\"Alice\".to_string(), \"Engineer\".to_string(), \"Active\".to_string(), \"alice@example.com|alice.j@work.com\".to_string()],\n vec![\"Bob\".to_string(), \"Designer\".to_string(), \"Active\".to_string(), \"bob@example.com|bob.s@work.com\".to_string()],\n vec![\"Carol\".to_string(), \"Manager\".to_string(), \"Away\".to_string(), \"carol@example.com|carol.w@work.com\".to_string()],\n vec![\"Dave\".to_string(), \"Engineer\".to_string(), \"Inactive\".to_string(), \"dave@example.com|dave.b@work.com\".to_string()],\n vec![\"Eve\".to_string(), \"Designer\".to_string(), \"Active\".to_string(), \"eve@example.com|eve.d@work.com\".to_string()],\n vec![\"Frank\".to_string(), \"DevOps\".to_string(), \"Active\".to_string(), \"frank@example.com|frank.m@work.com\".to_string()],\n ]\n}\n\nfn expand_columns() -> Vec<DataTableColumn<Vec<String>>> {\n vec![\n DataTableColumn::new(\"name\", \"Name\", |r: &Vec<String>| r[0].clone()),\n DataTableColumn::new(\"role\", \"Role\", |r: &Vec<String>| r[1].clone()),\n DataTableColumn::new(\"status\", \"Status\", |r: &Vec<String>| r[2].clone()),\n DataTableColumn::new(\"email\", \"Email\", |r: &Vec<String>| r[3].split('|').next().unwrap_or(\"\").to_string()),\n ]\n}\n\n#[component]\npub fn DataTableStaticShowcasePreview() -> impl IntoView {\n view! {\n <DataTable\n data=sample_data()\n columns=sample_columns()\n page_size=5\n show_density=true\n selectable=true\n row_actions=vec![\n RowAction::new(\"edit\", \"Edit\").inline(),\n RowAction::new(\"delete\", \"Delete\").danger(),\n ]\n bulk_actions=vec![\n BulkAction::new(\"export\", \"Export\"),\n BulkAction::new(\"delete\", \"Delete\").danger(),\n ]\n />\n }\n}\n\n#[component]\npub fn DataTableShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Xl>\n <Grid cols=GridCols::Two gap=GridGap::Lg>\n\n // 1. Data Operations — sort, filter, pagination\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-label=\"\">\"Data Operations — Sort · Search · Paginate\"</span>\n <DataTable\n data=sample_data()\n columns=sample_columns()\n page_size=5\n />\n </Stack>\n\n // 2. Visual Density\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-label=\"\">\"Visual Density — Compact · Comfortable · Spacious\"</span>\n <DataTable\n data=sample_data()\n columns=sample_columns()\n page_size=5\n show_density=true\n />\n </Stack>\n\n // 3. Selection Domain — row selection, bulk actions, keyboard nav\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-label=\"\">\"Selection — Row · Bulk · Keyboard\"</span>\n <DataTable\n data=sample_data()\n columns=sample_columns()\n page_size=5\n selectable=true\n bulk_actions=vec![\n BulkAction::new(\"export\", \"Export\"),\n BulkAction::new(\"delete\", \"Delete\").danger(),\n ]\n />\n </Stack>\n\n // 4. Action Dispatch — inline actions, menu actions, context menu\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-label=\"\">\"Actions — Inline · Menu · Context\"</span>\n <DataTable\n data=sample_data()\n columns=sample_columns()\n page_size=5\n row_actions=vec![\n RowAction::new(\"edit\", \"Edit\").inline(),\n RowAction::new(\"delete\", \"Delete\").danger(),\n RowAction::new(\"archive\", \"Archive\"),\n ]\n />\n </Stack>\n\n // 5. Expandable Structure\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-label=\"\">\"Expand Row — Structural Composition\"</span>\n <DataTable\n data=expand_data()\n columns=expand_columns()\n page_size=5\n expand_render=std::sync::Arc::new(|r: &Vec<String>| {\n let emails: Vec<String> = r[3].split('|').map(|s| s.to_string()).collect();\n view! {\n <div style=\"padding:var(--space-sm);display:flex;flex-direction:column;gap:var(--space-xs);\">\n <strong>\"Contact emails:\"</strong>\n {emails.into_iter().map(|e| view! {\n <span style=\"color:var(--theme-action-primary-bg);\">{e}</span>\n }).collect::<Vec<_>>()}\n </div>\n }.into_any()\n })\n />\n </Stack>\n\n // 6. Column Layout — resize, reorder, pin, toggle\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-label=\"\">\"Column Layout — Resize · Reorder · Toggle\"</span>\n <DataTable\n data=sample_data()\n columns=sample_columns()\n page_size=5\n resizable=true\n />\n </Stack>\n\n // 7. Editing Domain — inline edit lifecycle: state transition, commit, rollback\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-label=\"\">\"Editing — Inline Edit · Commit · Rollback\"</span>\n <DataTable\n data=sample_data()\n columns=sample_columns()\n page_size=5\n row_actions=vec![\n RowAction::new(\"edit\", \"✎ Edit\").inline(),\n ]\n />\n </Stack>\n\n // 8. Full Orchestration — all capabilities composed\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-label=\"\">\"Full Orchestration\"</span>\n <DataTable\n data=expand_data()\n columns=expand_columns()\n page_size=3\n show_density=true\n selectable=true\n resizable=true\n row_actions=vec![\n RowAction::new(\"edit\", \"Edit\").inline(),\n RowAction::new(\"delete\", \"Delete\").danger(),\n RowAction::new(\"archive\", \"Archive\"),\n ]\n bulk_actions=vec![\n BulkAction::new(\"export\", \"Export\"),\n BulkAction::new(\"delete\", \"Delete\").danger(),\n ]\n expand_render=std::sync::Arc::new(|r: &Vec<String>| {\n let emails: Vec<String> = r[3].split('|').map(|s| s.to_string()).collect();\n view! {\n <div style=\"padding:var(--space-sm);display:flex;flex-direction:column;gap:var(--space-xs);\">\n <strong>\"Contact emails:\"</strong>\n {emails.into_iter().map(|e| view! {\n <span style=\"color:var(--theme-action-primary-bg);\">{e}</span>\n }).collect::<Vec<_>>()}\n </div>\n }.into_any()\n })\n />\n </Stack>\n\n </Grid>\n <p data-rs-showcase-preview-anchor=\"\">\n \"DataTable — Data Ops · Density · Selection · Actions · Expand · Column Layout · Editing · Full Orchestration\"\n </p>\n </Stack>\n }\n}\n",
"block": [
"data_table_block"
],
"blocks_primitives": [
"container"
]
},
{
"id": "dialog",
"label": "Dialog",
"category": "Overlay",
"description": "Modal dialog component",
"keywords": "",
"pain": "Dialogs break focus trap and accessibility roles",
"promise": "Dialog accessibility and lifecycle enforced via primitives",
"why": "DialogPrimitive defines overlay, portal and content with ARIA compliance. VisibilityState guarantees correct open/close synchronization. This ensures safe modal behavior across SSR and client.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"modal\">\"Content\"</div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Dialog>\n <DialogTrigger>\"Open\"</DialogTrigger>\n <DialogPortal>\n <DialogOverlay />\n <DialogContent>\n <DialogTitle>\"Title\"</DialogTitle>\n </DialogContent>\n </DialogPortal>\n </Dialog>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"modals",
"forms"
],
"related": [
"alert_dialog",
"drawer",
"sheet",
"modal",
"confirm_dialog",
"tooltip",
"hover_card",
"popover"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "overlay",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Dialog Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::VisibilityState;\n\n#[component]\npub fn DialogPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] uid: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_str = if uid.is_empty() { crate::infra::uid::generate(\"dlg\") } else { uid };\n view! {\n <div\n data-rs-dialog=\"\"\n data-rs-interaction=\"overlay\"\n data-rs-uid=uid_str\n data-rs-state=state.as_str()\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn DialogTriggerPrimitive(\n children: Children,\n #[prop(into, default = String::new())] target: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-dialog-trigger=\"\"\n data-rs-button=\"\"\n data-rs-variant=\"primary\"\n data-rs-target=target\n aria-haspopup=\"dialog\"\n class=class\n >\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn DialogPortalPrimitive(children: ChildrenFn) -> impl IntoView {\n view! {\n <div data-rs-dialog-portal=\"\">\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn DialogOverlayPrimitive(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-dialog-overlay=\"\"\n data-rs-state=\"closed\"\n class=class\n />\n }\n}\n\n#[component]\npub fn DialogContentPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-dialog-content=\"\"\n data-rs-state=\"closed\"\n role=\"dialog\"\n aria-modal=\"true\"\n tabindex=\"-1\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn DialogTitlePrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <h2 data-rs-dialog-title=\"\" class=class>\n {children()}\n </h2>\n }\n}\n\n#[component]\npub fn DialogDescriptionPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <p data-rs-dialog-description=\"\" class=class>\n {children()}\n </p>\n }\n}\n\n#[component]\npub fn DialogClosePrimitive(\n children: Children,\n #[prop(into, default = \"Close dialog\".to_string())] aria_label: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-dialog-close=\"\"\n data-rs-button=\"\"\n data-rs-variant=\"ghost\"\n aria-label=aria_label\n class=class\n >\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn DialogFooterPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-dialog-footer=\"\" class=class>\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n DialogPrimitive, DialogTriggerPrimitive, DialogPortalPrimitive,\n DialogOverlayPrimitive, DialogContentPrimitive, DialogTitlePrimitive,\n DialogDescriptionPrimitive, DialogClosePrimitive, DialogFooterPrimitive,\n};\n\n#[component]\npub fn Dialog(\n children: Children,\n #[prop(into, default = String::new())] uid: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <DialogPrimitive uid=uid class=class>\n {children()}\n </DialogPrimitive>\n }\n}\n\n#[component]\npub fn DialogTrigger(\n children: Children,\n #[prop(into, default = String::new())] target: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <DialogTriggerPrimitive target=target class=class>{children()}</DialogTriggerPrimitive> }\n}\n\n#[component]\npub fn DialogPortal(children: ChildrenFn) -> impl IntoView {\n view! { <DialogPortalPrimitive>{children()}</DialogPortalPrimitive> }\n}\n\n#[component]\npub fn DialogOverlay(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <DialogOverlayPrimitive class=class /> }\n}\n\n#[component]\npub fn DialogContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <DialogContentPrimitive class=class>{children()}</DialogContentPrimitive> }\n}\n\n#[component]\npub fn DialogTitle(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <DialogTitlePrimitive class=class>{children()}</DialogTitlePrimitive> }\n}\n\n#[component]\npub fn DialogDescription(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <DialogDescriptionPrimitive class=class>{children()}</DialogDescriptionPrimitive> }\n}\n\n#[component]\npub fn DialogClose(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <DialogClosePrimitive class=class>{children()}</DialogClosePrimitive> }\n}\n\n#[component]\npub fn DialogFooter(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <DialogFooterPrimitive class=class>{children()}</DialogFooterPrimitive> }\n}\n\n",
"boundary_src": "//! Dialog Island — Canon Rule #340 passthrough\nuse leptos::prelude::*;\nuse super::dialog_ui::{\n Dialog as DialogUi,\n DialogTrigger as DialogTriggerUi,\n DialogPortal as DialogPortalUi,\n DialogOverlay as DialogOverlayUi,\n DialogContent as DialogContentUi,\n DialogTitle as DialogTitleUi,\n DialogDescription as DialogDescriptionUi,\n DialogClose as DialogCloseUi,\n DialogFooter as DialogFooterUi,\n};\n\n#[component]\npub fn Dialog(\n children: Children,\n #[prop(into, default = String::new())] uid: String,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <DialogUi uid=uid class=class.unwrap_or_default()>{children()}</DialogUi> }\n}\n\n#[component]\npub fn DialogTrigger(\n children: Children,\n #[prop(into, default = String::new())] target: String,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <DialogTriggerUi target=target class=class.unwrap_or_default()>{children()}</DialogTriggerUi> }\n}\n\n#[component]\npub fn DialogPortal(children: ChildrenFn) -> impl IntoView {\n view! { <DialogPortalUi>{children()}</DialogPortalUi> }\n}\n\n#[component]\npub fn DialogOverlay(\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <DialogOverlayUi class=class.unwrap_or_default() /> }\n}\n\n#[component]\npub fn DialogContent(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <DialogContentUi class=class.unwrap_or_default()>{children()}</DialogContentUi> }\n}\n\n#[component]\npub fn DialogTitle(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <DialogTitleUi class=class.unwrap_or_default()>{children()}</DialogTitleUi> }\n}\n\n#[component]\npub fn DialogDescription(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <DialogDescriptionUi class=class.unwrap_or_default()>{children()}</DialogDescriptionUi> }\n}\n\n#[component]\npub fn DialogClose(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <DialogCloseUi class=class.unwrap_or_default()>{children()}</DialogCloseUi> }\n}\n\n#[component]\npub fn DialogFooter(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <DialogFooterUi class=class.unwrap_or_default()>{children()}</DialogFooterUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const DIALOG_API: ComponentApi = ComponentApi {\n id: \"dialog\",\n description: \"Modal dialog component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"uid\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const DIALOGTRIGGER_API: ComponentApi = ComponentApi {\n id: \"dialog-trigger\",\n description: \"Modal dialog component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"target\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Target element selector for copy\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const DIALOGPORTAL_API: ComponentApi = ComponentApi {\n id: \"dialog-portal\",\n description: \"Modal dialog component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n ],\n};\n\npub const DIALOGCONTENT_API: ComponentApi = ComponentApi {\n id: \"dialog-content\",\n description: \"Modal dialog component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const DIALOGTITLE_API: ComponentApi = ComponentApi {\n id: \"dialog-title\",\n description: \"Modal dialog component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const DIALOGDESCRIPTION_API: ComponentApi = ComponentApi {\n id: \"dialog-description\",\n description: \"Modal dialog component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const DIALOGCLOSE_API: ComponentApi = ComponentApi {\n id: \"dialog-close\",\n description: \"Modal dialog component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const DIALOGFOOTER_API: ComponentApi = ComponentApi {\n id: \"dialog-footer\",\n description: \"Modal dialog component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::dialog_boundary::{\n Dialog, DialogTrigger, DialogPortal, DialogOverlay,\n DialogContent, DialogTitle, DialogDescription,\n DialogClose, DialogFooter,\n};\nuse crate::ui::button::button_boundary::{Button, ButtonVariant};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn DialogShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <Dialog>\n <DialogTrigger>\"Open Dialog\"</DialogTrigger>\n <DialogPortal>\n <DialogOverlay />\n <DialogContent>\n <DialogTitle>\"Confirm action\"</DialogTitle>\n <DialogDescription>\"Are you sure? This action cannot be undone.\"</DialogDescription>\n <DialogFooter>\n <DialogClose>\"Cancel\"</DialogClose>\n <Button variant=ButtonVariant::Primary>\"Confirm\"</Button>\n </DialogFooter>\n </DialogContent>\n </DialogPortal>\n </Dialog>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Dialog accessibility and lifecycle enforced via primitives.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Form dialog\"</span>\n <Dialog>\n <DialogTrigger>\"Edit profile\"</DialogTrigger>\n <DialogPortal>\n <DialogOverlay />\n <DialogContent>\n <DialogTitle>\"Edit profile\"</DialogTitle>\n <DialogDescription>\"Update your profile information below.\"</DialogDescription>\n <DialogFooter>\n <DialogClose>\"Cancel\"</DialogClose>\n <Button variant=ButtonVariant::Primary>\"Save changes\"</Button>\n </DialogFooter>\n </DialogContent>\n </DialogPortal>\n </Dialog>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"center",
"stack"
]
},
{
"id": "doc_progress",
"label": "Doc Progress",
"category": "Display",
"description": "Document progress indicator",
"keywords": "",
"pain": "Scroll progress indicators require manual scroll tracking logic",
"promise": "Progress tracking injected automatically via behavior layer",
"why": "DocProgressPrimitive exposes progress via data attributes and ARIA. Portal variant allows injection anywhere in layout. This guarantees consistent scroll tracking without custom JS.\n",
"before": "// ❌ Typical\nwindow.onscroll = () => updateProgress();\n",
"after": "// ✅ CanonRS\nview! {\n <DocProgress />\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"docs reading",
"long articles"
],
"related": [
"progress",
"spinner",
"skeleton",
"pulse",
"loading_overlay"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "progress",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! DocProgress Primitive\n\nuse leptos::prelude::*;\n\n#[derive(Clone, Copy, PartialEq, Default)]\npub enum DocProgressPosition {\n Top,\n #[default]\n Bottom,\n}\n\nimpl DocProgressPosition {\n pub fn as_str(&self) -> &'static str {\n match self {\n DocProgressPosition::Top => \"top\",\n DocProgressPosition::Bottom => \"bottom\",\n }\n }\n}\n\n#[component]\npub fn DocProgressPrimitive(\n #[prop(into, default = String::new())] class: String,\n #[prop(default = 0u8)] progress: u8,\n) -> impl IntoView {\n let uid_dp_1 = crate::infra::uid::generate(\"dp\");\n let progress_str = progress.to_string();\n view! {\n <div\n data-rs-doc-progress=\"\"\n data-rs-uid=uid_dp_1\n data-rs-interaction=\"init\"\n data-rs-progress=progress_str.clone()\n role=\"progressbar\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n aria-valuenow=progress_str\n aria-label=\"Reading progress\"\n class=class\n >\n <div data-rs-doc-progress-bar=\"\" />\n </div>\n }\n}\n\n#[component]\npub fn DocProgressPortal(\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] scroll_target: String,\n #[prop(default = DocProgressPosition::Top)] position: DocProgressPosition,\n) -> impl IntoView {\n let uid_dp_1 = crate::infra::uid::generate(\"dp\");\n view! {\n <div\n data-rs-doc-progress-portal=\"\"\n data-rs-uid=uid_dp_1\n data-rs-interaction=\"init\"\n data-rs-scroll-target=scroll_target\n data-rs-position=position.as_str()\n aria-hidden=\"true\"\n class=class\n >\n <div data-rs-doc-progress-bar=\"\" />\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::doc_progress::{DocProgressPrimitive, DocProgressPortal, DocProgressPosition};\n\npub use canonrs_core::primitives::doc_progress::DocProgressPosition as DocProgressSlotPosition;\n\n#[component]\npub fn DocProgress(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <DocProgressPrimitive class=class progress=0u8 />\n }\n}\n\n#[component]\npub fn DocProgressSlot(\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] scroll_target: String,\n #[prop(default = DocProgressPosition::Top)] position: DocProgressPosition,\n) -> impl IntoView {\n view! {\n <DocProgressPortal class=class scroll_target=scroll_target position=position />\n }\n}\n",
"boundary_src": "//! @canon-level: strict\n//! DocProgress Island — Canon Rule #340 (zero-logic boundary)\n\nuse leptos::prelude::*;\nuse super::doc_progress_ui::{\n DocProgress as DocProgressUi,\n DocProgressSlot as DocProgressSlotUi,\n DocProgressSlotPosition\n};\n\n#[component]\npub fn DocProgress(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <DocProgressUi class=class /> }\n}\n\n#[component]\npub fn DocProgressSlot(\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] scroll_target: String,\n #[prop(into, default = String::from(\"top\"))] position: String,\n) -> impl IntoView {\n let pos = match position.as_str() {\n \"bottom\" => DocProgressSlotPosition::Bottom,\n _ => DocProgressSlotPosition::Top,\n };\n view! { <DocProgressSlotUi class=class scroll_target=scroll_target position=pos /> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const DOCPROGRESS_API: ComponentApi = ComponentApi {\n id: \"doc-progress\",\n description: \"Document progress indicator\",\n props: &[\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const DOCPROGRESSSLOT_API: ComponentApi = ComponentApi {\n id: \"doc-progress-slot\",\n description: \"Document progress indicator\",\n props: &[\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n PropDef { name: \"scroll_target\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Scroll target element id\" },\n PropDef { name: \"position\", kind: PropType::String, required: false, default: Some(\"top\"), description: \"Prop value\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::doc_progress_boundary::DocProgressSlot;\nuse crate::ui::scroll_area::scroll_area_boundary::ScrollArea;\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\nuse canonrs_core::primitives::layout::container::{ContainerPrimitive as Container, ContainerSize};\n\n#[component]\npub fn DocProgressShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Progress tracking injected automatically via behavior layer.\"\n </p>\n <Container size=ContainerSize::Md>\n <div data-rs-doc-progress-demo=\"\" style=\"position:relative; height: 320px; border: var(--border-thin) solid var(--theme-surface-border); border-radius: var(--radius-md); overflow: hidden;\">\n <DocProgressSlot scroll_target=\"doc-progress-viewport\" position=\"top\" />\n <ScrollArea viewport_id=\"doc-progress-viewport\">\n <Stack direction=StackDirection::Vertical gap=StackGap::Xl>\n <section>\n <h2>\"Introduction\"</h2>\n <p>\"This document tracks your reading progress as you scroll through the content. The progress bar updates in real time based on scroll position, giving readers a clear visual indicator of how far they have come.\"</p>\n <p>\"The implementation uses a behavior layer that observes scroll events and maps them to a percentage value. This value is stored as a data attribute on the root element, keeping the DOM as the single source of truth.\"</p>\n </section>\n <section>\n <h2>\"Getting Started\"</h2>\n <p>\"DocProgress uses a behavior layer that observes scroll position and computes a reading percentage. The component is SSR-safe and hydration-safe — the initial state is rendered on the server and the behavior layer takes over on the client.\"</p>\n <p>\"To use DocProgress, simply add it to your layout. No configuration is required for basic usage. The component will automatically track the scroll position of the window or a custom scroll container.\"</p>\n </section>\n <section>\n <h2>\"Installation\"</h2>\n <p>\"Add the DocProgress component to your layout and the behavior handles the rest. The component exposes two variants: a standalone fixed bar that sits at the top of the viewport, and a portal variant that can be embedded inside a custom header or container.\"</p>\n <p>\"The portal variant is useful when you want the progress bar to appear inside a sticky header or a custom scrollable area rather than at the top of the page.\"</p>\n </section>\n <section>\n <h2>\"Configuration\"</h2>\n <p>\"Tokens control height, color and transition speed. The following tokens are available: doc-progress-height, doc-progress-bg, doc-progress-bar-bg, doc-progress-z-index, and doc-progress-transition.\"</p>\n <p>\"All tokens follow the CanonRS token system and can be overridden at any level of the component tree. This makes it easy to customize the appearance of the progress bar without touching the component source code.\"</p>\n </section>\n <section>\n <h2>\"Advanced Usage\"</h2>\n <p>\"Use DocProgressSlot to embed the bar inside a custom header. This is useful when you want the progress bar to appear inside a sticky navigation bar or a custom layout component.\"</p>\n <p>\"The scroll_target attribute allows you to specify a custom scroll container. This is useful when the scrollable content is not the window but a specific element on the page, such as a modal or a side panel.\"</p>\n </section>\n <section>\n <h2>\"API Reference\"</h2>\n <p>\"DocProgress — standalone fixed bar that tracks window scroll. DocProgressSlot — portal for custom placement inside headers or containers. Both components share the same behavior layer and token system.\"</p>\n <p>\"The aria-valuenow attribute is updated in real time to reflect the current progress percentage. Screen readers can announce the progress to users who rely on assistive technology.\"</p>\n </section>\n <section>\n <h2>\"Accessibility\"</h2>\n <p>\"The progress bar uses role=progressbar with aria-valuemin, aria-valuemax, aria-valuenow, and aria-valuetext attributes. The aria-valuetext attribute provides a human-readable description of the progress value for screen readers.\"</p>\n </section>\n </Stack>\n </ScrollArea>\n </div>\n </Container>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"container",
"stack"
]
},
{
"id": "drawer",
"label": "Drawer",
"category": "Overlay",
"description": "Slide-out drawer component",
"keywords": "",
"pain": "Slide panels lack consistent direction and accessibility semantics",
"promise": "Drawer direction and visibility enforced via typed contract",
"why": "DrawerPrimitive encodes side and visibility with DrawerSide and VisibilityState. ARIA attributes ensure accessibility compliance. This guarantees predictable slide behavior.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"drawer left\">\"Content\"</div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Drawer>\n <DrawerOverlay />\n <DrawerContent aria_labelledby=\"title\" />\n </Drawer>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"mobile navigation",
"side panels"
],
"related": [
"dialog",
"alert_dialog",
"sheet",
"modal",
"confirm_dialog",
"tooltip",
"hover_card",
"popover"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "overlay",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Drawer Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::VisibilityState;\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum DrawerSide {\n #[default]\n Right,\n Left,\n Top,\n Bottom,\n}\nimpl DrawerSide {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Right => \"right\",\n Self::Left => \"left\",\n Self::Top => \"top\",\n Self::Bottom => \"bottom\",\n }\n }\n}\n\n\n#[component]\npub fn DrawerPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(default = DrawerSide::Right)] side: DrawerSide,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_dr = crate::infra::uid::generate(\"dr\");\n view! {\n <div\n data-rs-drawer=\"\"\n data-rs-interaction=\"overlay\"\n data-rs-uid=uid_dr\n data-rs-state=state.as_str()\n data-rs-side=side.as_str()\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn DrawerTriggerPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(optional, into)] aria_controls: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-drawer-trigger=\"\"\n data-rs-button=\"\"\n data-rs-variant=\"primary\"\n data-rs-state=state.as_str()\n aria-haspopup=\"dialog\"\n aria-expanded=state.aria_expanded()\n aria-controls=aria_controls\n class=class\n >\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn DrawerOverlayPrimitive(\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-drawer-overlay=\"\"\n data-rs-state=state.as_str()\n class=class\n />\n }\n}\n\n#[component]\npub fn DrawerContentPrimitive(\n children: Children,\n #[prop(optional, into)] aria_labelledby: Option<String>,\n #[prop(optional, into)] aria_describedby: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-drawer-content=\"\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=aria_labelledby\n aria-describedby=aria_describedby\n tabindex=\"-1\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn DrawerPortalPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <div data-rs-drawer-portal=\"\" class=class>{children()}</div> }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n DrawerPrimitive, DrawerTriggerPrimitive, DrawerOverlayPrimitive,\n DrawerContentPrimitive, DrawerPortalPrimitive, DrawerSide,\n};\nuse canonrs_core::meta::VisibilityState;\n\n#[component]\npub fn Drawer(\n children: Children,\n #[prop(default = DrawerSide::Right)] side: DrawerSide,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <DrawerPrimitive side=side state=state class=class>{children()}</DrawerPrimitive> }\n}\n\n#[component]\npub fn DrawerTrigger(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <DrawerTriggerPrimitive class=class>{children()}</DrawerTriggerPrimitive> }\n}\n\n#[component]\npub fn DrawerOverlay(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <DrawerOverlayPrimitive class=class /> }\n}\n\n#[component]\npub fn DrawerContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] aria_labelledby: String,\n) -> impl IntoView {\n view! {\n <DrawerContentPrimitive aria_labelledby=aria_labelledby class=class>\n {children()}\n </DrawerContentPrimitive>\n }\n}\n\n#[component]\npub fn DrawerPortal(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <DrawerPortalPrimitive class=class>{children()}</DrawerPortalPrimitive> }\n}\n",
"boundary_src": "//! @canon-level: strict\n//! Drawer Island — Canon Rule #340 (zero-logic boundary)\n//! CR-342 v3.0.0: interaction delegated to canonrs-interactions-overlay\n\nuse leptos::prelude::*;\nuse super::drawer_ui::{\n Drawer as DrawerUi,\n DrawerTrigger,\n DrawerOverlay,\n DrawerContent,\n DrawerPortal\n};\npub use canonrs_core::primitives::DrawerSide;\nuse canonrs_core::meta::VisibilityState;\n\n#[component]\npub fn Drawer(\n #[prop(optional)] children: Option<Children>,\n #[prop(into, default = String::from(\"Open\"))] trigger_label: String,\n #[prop(into, default = String::from(\"Close\"))] close_label: String,\n #[prop(into, optional)] title: Option<String>,\n #[prop(into, optional)] description: Option<String>,\n #[prop(default = DrawerSide::Right)] side: DrawerSide,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <DrawerUi side=side state=VisibilityState::Closed class=class>\n <DrawerTrigger>{trigger_label}</DrawerTrigger>\n <DrawerPortal>\n <DrawerOverlay />\n <DrawerContent aria_labelledby=\"drawer-title\">\n {title.map(|t| view! { <h2 id=\"drawer-title\" data-rs-drawer-title=\"\">{t}</h2> })}\n {description.map(|d| view! { <p data-rs-drawer-description=\"\">{d}</p> })}\n {children.map(|c| c())}\n <button type=\"button\" data-rs-drawer-close=\"\">{close_label}</button>\n </DrawerContent>\n </DrawerPortal>\n </DrawerUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{DrawerSide}; \n\npub const DRAWER_API: ComponentApi = ComponentApi {\n id: \"drawer\",\n description: \"Slide-out drawer component\",\n props: &[\n PropDef { name: \"trigger_label\", kind: PropType::String, required: false, default: Some(\"Open\"), description: \"Prop value\" },\n PropDef { name: \"close_label\", kind: PropType::String, required: false, default: Some(\"Close\"), description: \"Prop value\" },\n PropDef { name: \"title\", kind: PropType::String, required: false, default: None, description: \"Title slot or text\" },\n PropDef { name: \"description\", kind: PropType::String, required: false, default: None, description: \"Description slot or text\" },\n PropDef { name: \"side\", kind: PropType::Enum(&[\"right\", \"left\", \"top\", \"bottom\"]), required: false, default: Some(\"right\"), description: \"Tooltip or popover side\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::drawer_boundary::{Drawer, DrawerSide};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn DrawerShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <Drawer\n trigger_label=\"Open Drawer\"\n title=\"Drawer Title\"\n description=\"Slides in from the side. State governed via DOM.\"\n close_label=\"Close\"\n />\n <p data-rs-showcase-preview-anchor=\"\">\n \"Drawer visibility and overlay fully governed via shared state.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Left side\"</span>\n <Drawer\n trigger_label=\"Open left\"\n title=\"Left Drawer\"\n close_label=\"Close\"\n side=DrawerSide::Left\n />\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"flex"
]
},
{
"id": "dropdown_menu",
"label": "Dropdown Menu",
"category": "Action",
"description": "Dropdown menu",
"keywords": "",
"pain": "Dropdown menus break keyboard navigation and selection state",
"promise": "Menu interaction and state fully encoded via primitives",
"why": "DropdownMenuPrimitive defines trigger/content with ARIA roles. ToggleState and ActivityState manage selection and highlight. This guarantees consistent menu behavior.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"dropdown\">\n <button>\"Open\"</button>\n <div class=\"menu\">\"Item\"</div>\n </div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <DropdownMenu>\n <DropdownMenuTrigger>\"Open\"</DropdownMenuTrigger>\n <DropdownMenuContent>\n <DropdownMenuItem>\"Item\"</DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"actions menu",
"user menu"
],
"related": [
"context_menu",
"menubar",
"menu",
"command"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "menu",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! DropdownMenu Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::{DisabledState, ToggleState, VisibilityState};\n\n\n\n#[component]\npub fn DropdownMenuPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(optional)] node_ref: Option<NodeRef<leptos::html::Div>>,\n) -> impl IntoView {\n let uid_dm = crate::infra::uid::generate(\"dm\");\n view! {\n <div\n data-rs-dropdown-menu=\"\"\n data-rs-uid=uid_dm\n data-rs-interaction=\"overlay\"\n data-rs-state=state.as_str()\n class=class\n node_ref=node_ref.unwrap_or_default()\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn DropdownMenuTriggerPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n) -> impl IntoView {\n let aria_disabled = if disabled == DisabledState::Disabled { \"true\" } else { \"false\" };\n view! {\n <button\n type=\"button\"\n data-rs-dropdown-menu-trigger=\"\"\n aria-haspopup=\"menu\"\n aria-expanded=\"false\"\n aria-disabled=aria_disabled\n class=class\n >\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn DropdownMenuContentPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-dropdown-menu-content=\"\"\n role=\"menu\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn DropdownMenuGroupPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, optional)] label: Option<String>,\n) -> impl IntoView {\n view! {\n <div\n data-rs-dropdown-menu-group=\"\"\n role=\"group\"\n aria-label=label\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn DropdownMenuItemPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n) -> impl IntoView {\n let is_disabled = disabled == DisabledState::Disabled;\n view! {\n <button\n type=\"button\"\n data-rs-dropdown-menu-item=\"\"\n role=\"menuitem\"\n data-rs-activity=\"inactive\"\n data-rs-disabled=if is_disabled { Some(\"disabled\") } else { None }\n aria-disabled=if is_disabled { Some(\"true\") } else { None }\n tabindex=if is_disabled { \"-1\" } else { \"0\" }\n class=class\n >\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn DropdownMenuSeparatorPrimitive(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-dropdown-menu-separator=\"\"\n role=\"separator\"\n class=class\n />\n }\n}\n\n#[component]\npub fn DropdownMenuCheckboxItemPrimitive(\n children: Children,\n #[prop(default = ToggleState::Off)] checked: ToggleState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let checked_str = checked.as_str();\n view! {\n <button\n type=\"button\"\n data-rs-dropdown-menu-checkbox-item=\"\"\n data-rs-toggle=checked_str\n data-rs-state=checked_str\n role=\"menuitemcheckbox\"\n aria-checked=checked.aria_pressed()\n aria-disabled=disabled.aria_disabled()\n tabindex=if disabled.disabled() { \"-1\" } else { \"0\" }\n class=class\n >\n <span data-rs-dropdown-menu-indicator=\"\" aria-hidden=\"true\"></span>\n <span data-rs-dropdown-menu-label=\"\">{children()}</span>\n </button>\n }\n}\n\n#[component]\npub fn DropdownMenuLabelPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-dropdown-menu-label=\"\"\n role=\"presentation\"\n aria-hidden=\"true\"\n class=class\n >\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::separator::SeparatorOrientation;\nuse canonrs_core::primitives::{\n DropdownMenuPrimitive, DropdownMenuContentPrimitive,\n DropdownMenuGroupPrimitive, DropdownMenuItemPrimitive,\n DropdownMenuCheckboxItemPrimitive, DropdownMenuTriggerPrimitive,\n DropdownMenuLabelPrimitive,\n SeparatorPrimitive,\n};\nuse canonrs_core::meta::{DisabledState, ToggleState, VisibilityState};\n\n#[component]\npub fn DropdownMenu(\n children: Children,\n #[prop(optional)] node_ref: Option<NodeRef<leptos::html::Div>>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <DropdownMenuPrimitive state=VisibilityState::Closed class=class node_ref=node_ref.unwrap_or_default()>\n {children()}\n </DropdownMenuPrimitive>\n }\n}\n\n#[component]\npub fn DropdownMenuTrigger(\n children: Children,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <DropdownMenuTriggerPrimitive disabled=disabled class=class>\n {children()}\n </DropdownMenuTriggerPrimitive>\n }\n}\n\n#[component]\npub fn DropdownMenuContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <DropdownMenuContentPrimitive class=class>\n {children()}\n </DropdownMenuContentPrimitive>\n }\n}\n\n#[component]\npub fn DropdownMenuGroup(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <DropdownMenuGroupPrimitive class=class>\n {children()}\n </DropdownMenuGroupPrimitive>\n }\n}\n\n#[component]\npub fn DropdownMenuItem(\n children: Children,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <DropdownMenuItemPrimitive disabled=disabled class=class>\n {children()}\n </DropdownMenuItemPrimitive>\n }\n}\n\n#[component]\npub fn DropdownMenuCheckboxItem(\n children: Children,\n #[prop(default = ToggleState::Off)] checked: ToggleState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <DropdownMenuCheckboxItemPrimitive checked=checked disabled=disabled class=class>\n {children()}\n </DropdownMenuCheckboxItemPrimitive>\n }\n}\n\n#[component]\npub fn DropdownMenuLabel(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <DropdownMenuLabelPrimitive class=class>\n {children()}\n </DropdownMenuLabelPrimitive>\n }\n}\n\n#[component]\npub fn DropdownMenuSeparator(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <SeparatorPrimitive orientation=SeparatorOrientation::Horizontal decorative=true class=class />\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! DropdownMenu Island — passthrough only\n\nuse leptos::prelude::*;\nuse super::dropdown_menu_ui::{\n DropdownMenu as DropdownMenuUi,\n DropdownMenuTrigger as DropdownMenuTriggerUi,\n DropdownMenuContent as DropdownMenuContentUi,\n DropdownMenuGroup as DropdownMenuGroupUi,\n DropdownMenuItem as DropdownMenuItemUi,\n DropdownMenuCheckboxItem as DropdownMenuCheckboxItemUi,\n DropdownMenuSeparator as DropdownMenuSeparatorUi\n};\nuse canonrs_core::meta::{DisabledState, ToggleState};\n\n#[component]\npub fn DropdownMenu(\n children: Children,\n #[prop(optional, into)] trigger_label: Option<String>,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! {\n <DropdownMenuUi class=class.unwrap_or_default()>\n <DropdownMenuTriggerUi>{trigger_label.unwrap_or_else(|| \"Options\".to_string())}</DropdownMenuTriggerUi>\n <DropdownMenuContentUi>{children()}</DropdownMenuContentUi>\n </DropdownMenuUi>\n }\n}\n\n#[component]\npub fn DropdownMenuTrigger(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <DropdownMenuTriggerUi class=class>{children()}</DropdownMenuTriggerUi> }\n}\n\n#[component]\npub fn DropdownMenuContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <DropdownMenuContentUi class=class>{children()}</DropdownMenuContentUi> }\n}\n\n#[component]\npub fn DropdownMenuGroup(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <DropdownMenuGroupUi class=class>{children()}</DropdownMenuGroupUi> }\n}\n\n#[component]\npub fn DropdownMenuItem(\n children: Children,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <DropdownMenuItemUi disabled=disabled class=class>{children()}</DropdownMenuItemUi> }\n}\n\n#[component]\npub fn DropdownMenuCheckboxItem(\n children: Children,\n #[prop(default = ToggleState::Off)] checked: ToggleState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <DropdownMenuCheckboxItemUi checked=checked disabled=disabled class=class>{children()}</DropdownMenuCheckboxItemUi> }\n}\n\n#[component]\npub fn DropdownMenuSeparator() -> impl IntoView {\n view! { <DropdownMenuSeparatorUi /> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const DROPDOWNMENU_API: ComponentApi = ComponentApi {\n id: \"dropdown-menu\",\n description: \"Dropdown menu\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"trigger_label\", kind: PropType::String, required: false, default: None, description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const DROPDOWNMENUTRIGGER_API: ComponentApi = ComponentApi {\n id: \"dropdown-menu-trigger\",\n description: \"Dropdown menu\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const DROPDOWNMENUCONTENT_API: ComponentApi = ComponentApi {\n id: \"dropdown-menu-content\",\n description: \"Dropdown menu\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const DROPDOWNMENUGROUP_API: ComponentApi = ComponentApi {\n id: \"dropdown-menu-group\",\n description: \"Dropdown menu\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const DROPDOWNMENUITEM_API: ComponentApi = ComponentApi {\n id: \"dropdown-menu-item\",\n description: \"Dropdown menu\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"disabled\", kind: PropType::String, required: false, default: Some(\"enabled\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const DROPDOWNMENUCHECKBOXITEM_API: ComponentApi = ComponentApi {\n id: \"dropdown-menu-checkbox-item\",\n description: \"Dropdown menu\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"checked\", kind: PropType::String, required: false, default: Some(\"off\"), description: \"Whether the component is checked\" },\n PropDef { name: \"disabled\", kind: PropType::String, required: false, default: Some(\"enabled\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const DROPDOWNMENUSEPARATOR_API: ComponentApi = ComponentApi {\n id: \"dropdown-menu-separator\",\n description: \"Dropdown menu\",\n props: &[\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::dropdown_menu_boundary::{\n DropdownMenu, DropdownMenuItem, DropdownMenuSeparator,\n DropdownMenuCheckboxItem,\n};\nuse canonrs_core::meta::{DisabledState, ToggleState};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn DropdownMenuShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <DropdownMenu trigger_label=\"Options\">\n <DropdownMenuItem>\"Edit\"</DropdownMenuItem>\n <DropdownMenuItem>\"Duplicate\"</DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem disabled=DisabledState::Disabled>\"Delete\"</DropdownMenuItem>\n </DropdownMenu>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Dropdown menu with keyboard navigation and disabled state.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"With checkboxes\"</span>\n <DropdownMenu trigger_label=\"View\">\n <DropdownMenuCheckboxItem checked=ToggleState::On>\"Show toolbar\"</DropdownMenuCheckboxItem>\n <DropdownMenuCheckboxItem>\"Show sidebar\"</DropdownMenuCheckboxItem>\n <DropdownMenuCheckboxItem checked=ToggleState::On>\"Show status bar\"</DropdownMenuCheckboxItem>\n </DropdownMenu>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "empty_state",
"label": "Empty State",
"category": "Feedback",
"description": "Empty state placeholder",
"keywords": "",
"pain": "Empty states are inconsistent and lack semantic meaning",
"promise": "Empty state intent and variant enforced via contract",
"why": "EmptyStatePrimitive encodes variant and ARIA role=\"status\". Variants ensure consistent messaging patterns. This guarantees predictable feedback across all empty states.\n",
"before": "// ❌ Typical\nview! {\n <div>\"No data\"</div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <EmptyState>\n <EmptyStateTitle>\"No data\"</EmptyStateTitle>\n </EmptyState>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"no results",
"empty dashboards"
],
"related": [
"error_state",
"animate"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "feedback_state",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! EmptyState Primitive - HTML puro\n\nuse leptos::prelude::*;\n\n#[derive(Debug, Clone, Copy, PartialEq, Default)]\npub enum EmptyStateVariant {\n #[default]\n Default,\n NoData,\n NoResults,\n Error,\n}\nimpl EmptyStateVariant {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Default => \"default\",\n Self::NoData => \"no-data\",\n Self::NoResults => \"no-results\",\n Self::Error => \"error\",\n }\n }\n}\n\n#[component]\npub fn EmptyStatePrimitive(\n children: Children,\n #[prop(default = EmptyStateVariant::Default)] variant: EmptyStateVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_es = crate::infra::uid::generate(\"es\");\n view! {\n <div\n data-rs-empty=\"\"\n data-rs-uid=uid_es\n data-rs-variant=variant.as_str()\n role=\"status\"\n aria-live=\"polite\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn EmptyStateIconPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-empty-icon=\"\" aria-hidden=\"true\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn EmptyStateTitlePrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <h3 data-rs-empty-title=\"\" class=class>\n {children()}\n </h3>\n }\n}\n\n#[component]\npub fn EmptyStateDescriptionPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <p data-rs-empty-description=\"\" class=class>\n {children()}\n </p>\n }\n}\n\n#[component]\npub fn EmptyStateActionPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-empty-action=\"\" class=class>\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n EmptyStatePrimitive, EmptyStateIconPrimitive,\n EmptyStateTitlePrimitive, EmptyStateDescriptionPrimitive,\n EmptyStateActionPrimitive,\n};\npub use canonrs_core::primitives::EmptyStateVariant;\n\n#[component]\npub fn EmptyState(\n children: Children,\n #[prop(default = EmptyStateVariant::Default)] variant: EmptyStateVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <EmptyStatePrimitive variant=variant class=class>\n {children()}\n </EmptyStatePrimitive>\n }\n}\n\n#[component]\npub fn EmptyStateIcon(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <EmptyStateIconPrimitive class=class>\n {children()}\n </EmptyStateIconPrimitive>\n }\n}\n\n#[component]\npub fn EmptyStateTitle(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <EmptyStateTitlePrimitive class=class>\n {children()}\n </EmptyStateTitlePrimitive>\n }\n}\n\n#[component]\npub fn EmptyStateDescription(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <EmptyStateDescriptionPrimitive class=class>\n {children()}\n </EmptyStateDescriptionPrimitive>\n }\n}\n\n#[component]\npub fn EmptyStateAction(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <EmptyStateActionPrimitive class=class>\n {children()}\n </EmptyStateActionPrimitive>\n }\n}\n\n",
"boundary_src": "//! EmptyState Island — Canon Rule #340\n//! Passthrough only. Zero logic, zero transformation.\n\nuse leptos::prelude::*;\nuse super::empty_state_ui::{\n EmptyState as EmptyStateUi,\n EmptyStateIcon as EmptyStateIconUi,\n EmptyStateTitle as EmptyStateTitleUi,\n EmptyStateDescription as EmptyStateDescriptionUi,\n EmptyStateAction as EmptyStateActionUi\n};\npub use canonrs_core::primitives::EmptyStateVariant;\n\n#[component]\npub fn EmptyState(\n children: Children,\n #[prop(default = EmptyStateVariant::Default)] variant: EmptyStateVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <EmptyStateUi variant=variant class=class>{children()}</EmptyStateUi> }\n}\n\n#[component]\npub fn EmptyStateIcon(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <EmptyStateIconUi class=class>{children()}</EmptyStateIconUi> }\n}\n\n#[component]\npub fn EmptyStateTitle(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <EmptyStateTitleUi class=class>{children()}</EmptyStateTitleUi> }\n}\n\n#[component]\npub fn EmptyStateDescription(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <EmptyStateDescriptionUi class=class>{children()}</EmptyStateDescriptionUi> }\n}\n\n#[component]\npub fn EmptyStateAction(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <EmptyStateActionUi class=class>{children()}</EmptyStateActionUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{EmptyStateVariant}; \n\npub const EMPTYSTATE_API: ComponentApi = ComponentApi {\n id: \"empty-state\",\n description: \"Empty state placeholder\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"variant\", kind: PropType::Enum(&[\"default\", \"no-data\", \"no-results\", \"error\"]), required: false, default: Some(\"default\"), description: \"Visual variant of the component\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const EMPTYSTATEICON_API: ComponentApi = ComponentApi {\n id: \"empty-state-icon\",\n description: \"Empty state placeholder\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const EMPTYSTATETITLE_API: ComponentApi = ComponentApi {\n id: \"empty-state-title\",\n description: \"Empty state placeholder\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const EMPTYSTATEDESCRIPTION_API: ComponentApi = ComponentApi {\n id: \"empty-state-description\",\n description: \"Empty state placeholder\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const EMPTYSTATEACTION_API: ComponentApi = ComponentApi {\n id: \"empty-state-action\",\n description: \"Empty state placeholder\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::empty_state_boundary::{EmptyState, EmptyStateIcon, EmptyStateTitle, EmptyStateDescription, EmptyStateAction, EmptyStateVariant};\nuse canonrs_core::primitives::layout::grid::{GridPrimitive as Grid, GridCols};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn EmptyStateShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <EmptyState>\n <EmptyStateIcon>\"📭\"</EmptyStateIcon>\n <EmptyStateTitle>\"No items found\"</EmptyStateTitle>\n <EmptyStateDescription>\"Try adjusting your search or filters.\"</EmptyStateDescription>\n <EmptyStateAction>\"Clear filters\"</EmptyStateAction>\n </EmptyState>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Empty state intent and variant enforced via contract.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Variants\"</span>\n <Grid cols=GridCols::Two>\n <EmptyState>\n <EmptyStateIcon>\"🗂️\"</EmptyStateIcon>\n <EmptyStateTitle>\"Default\"</EmptyStateTitle>\n <EmptyStateDescription>\"Nothing here yet.\"</EmptyStateDescription>\n </EmptyState>\n <EmptyState variant=EmptyStateVariant::NoResults>\n <EmptyStateIcon>\"🔍\"</EmptyStateIcon>\n <EmptyStateTitle>\"No results\"</EmptyStateTitle>\n <EmptyStateDescription>\"No matches for your query.\"</EmptyStateDescription>\n </EmptyState>\n <EmptyState variant=EmptyStateVariant::NoData>\n <EmptyStateIcon>\"📊\"</EmptyStateIcon>\n <EmptyStateTitle>\"No data\"</EmptyStateTitle>\n <EmptyStateDescription>\"Start by adding some entries.\"</EmptyStateDescription>\n </EmptyState>\n <EmptyState variant=EmptyStateVariant::Error>\n <EmptyStateIcon>\"⚠️\"</EmptyStateIcon>\n <EmptyStateTitle>\"Something went wrong\"</EmptyStateTitle>\n <EmptyStateDescription>\"Please try again later.\"</EmptyStateDescription>\n <EmptyStateAction>\"Retry\"</EmptyStateAction>\n </EmptyState>\n </Grid>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"center",
"stack",
"grid"
]
},
{
"id": "empty_table",
"label": "Empty Table",
"category": "Display",
"description": "Empty table state display",
"keywords": "",
"pain": "Empty table rows break layout and screen reader semantics",
"promise": "Empty state rendered as valid table row with proper ARIA",
"why": "EmptyTablePrimitive enforces correct table structure using tr and td with colspan. It preserves row semantics while exposing status via aria-live. This guarantees no layout or accessibility break inside tables.\n",
"before": "// ❌ Typical\nview! {\n <tbody>\n <div>\"No data\"</div>\n </tbody>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <tbody>\n <EmptyTable colspan=3u32 />\n </tbody>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"empty data grids",
"admin tables"
],
"related": [
"table",
"data_table",
"virtual_list",
"tree",
"list_item"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "data",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! EmptyTable Primitive - Empty state for tables\n\nuse leptos::prelude::*;\n\n#[component]\npub fn EmptyTablePrimitive(\n children: Children,\n #[prop(default = 1u32)] colspan: u32,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_et = crate::infra::uid::generate(\"et\");\n view! {\n <tr\n data-rs-empty-table-row=\"\"\n data-rs-uid=uid_et\n role=\"row\"\n class=class\n >\n <td colspan=colspan role=\"cell\">\n <div\n data-rs-empty-table-content=\"\"\n data-rs-activity=\"empty\"\n role=\"status\"\n aria-live=\"polite\"\n >\n {children()}\n </div>\n </td>\n </tr>\n }\n}\n\n#[component]\npub fn EmptyTableTitlePrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-empty-table-title=\"\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn EmptyTableDescriptionPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-empty-table-description=\"\" class=class>\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n EmptyTablePrimitive,\n EmptyTableTitlePrimitive,\n EmptyTableDescriptionPrimitive,\n};\n\n#[component]\npub fn EmptyTable(\n #[prop(into, default = \"No data available\".to_string())] title: String,\n #[prop(into, default = \"Add your first item to get started\".to_string())] description: String,\n #[prop(optional)] children: Option<Children>,\n #[prop(default = 999)] colspan: u32,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <EmptyTablePrimitive colspan=colspan class=class>\n <EmptyTableTitlePrimitive>{title}</EmptyTableTitlePrimitive>\n <EmptyTableDescriptionPrimitive>{description}</EmptyTableDescriptionPrimitive>\n {children.map(|c| c())}\n </EmptyTablePrimitive>\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! EmptyTable Island — Canon Rule #340 (zero-logic boundary)\n\nuse leptos::prelude::*;\nuse super::empty_table_ui::EmptyTable as EmptyTableUi;\n\n#[component]\npub fn EmptyTable(\n #[prop(into, default = String::from(\"No data available\"))] title: String,\n #[prop(into, default = String::from(\"Add your first item to get started\"))] description: String,\n #[prop(default = 999u32)] colspan: u32,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <EmptyTableUi title=title description=description colspan=colspan class=class />\n};\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const EMPTYTABLE_API: ComponentApi = ComponentApi {\n id: \"empty-table\",\n description: \"Empty table state display\",\n props: &[\n PropDef { name: \"title\", kind: PropType::String, required: false, default: Some(\"No data available\"), description: \"Title slot or text\" },\n PropDef { name: \"description\", kind: PropType::String, required: false, default: Some(\"Add your first item to get started\"), description: \"Description slot or text\" },\n PropDef { name: \"colspan\", kind: PropType::Number, required: false, default: Some(\"999u32\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::empty_table_boundary::EmptyTable;\nuse crate::blocks::data_table::DataTableBlock;\nuse crate::ui::table::table_boundary::{Table, TableHeader, TableBody, TableRow, TableHead};\nuse canonrs_core::slot;\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn EmptyTableShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <DataTableBlock\n empty=slot!(|| view! {\n <EmptyTable\n colspan=3u32\n title=\"No data available\"\n description=\"Add your first item to get started.\"\n />\n }.into_any())\n />\n <p data-rs-showcase-preview-anchor=\"\">\n \"Empty state rendered as valid table row with proper ARIA.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Inside table\"</span>\n <DataTableBlock\n header=slot!(|| view! {\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead><span>\"Name\"</span></TableHead>\n <TableHead><span>\"Email\"</span></TableHead>\n </TableRow>\n </TableHeader>\n </Table>\n }.into_any())\n empty=slot!(|| view! {\n <Table>\n <TableBody>\n <EmptyTable\n colspan=2u32\n title=\"No users found\"\n description=\"Invite your team members to get started.\"\n />\n </TableBody>\n </Table>\n }.into_any())\n />\n </Stack>\n </Stack>\n }\n}\n",
"block": [
"data_table_block"
],
"blocks_primitives": [
"stack"
]
},
{
"id": "error_state",
"label": "Error State",
"category": "Feedback",
"description": "Error state display",
"keywords": "",
"pain": "Error messages inconsistent and not announced to assistive technologies",
"promise": "Error feedback always announced and structurally consistent",
"why": "ErrorStatePrimitive enforces role=\"status\" with aria-live=\"assertive\". This guarantees immediate announcement of critical errors. Structure ensures consistent composition of icon, title and actions.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"error\">\"Something went wrong\"</div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <ErrorState>\n <ErrorStateTitle>\"Error\"</ErrorStateTitle>\n </ErrorState>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"api failures",
"form submission errors"
],
"related": [
"empty_state",
"animate"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "feedback_state",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! ErrorState Primitive - HTML puro\n\nuse leptos::prelude::*;\n\n#[component]\npub fn ErrorStatePrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_ers = crate::infra::uid::generate(\"ers\");\n view! {\n <div\n data-rs-error-state=\"\"\n data-rs-uid=uid_ers\n role=\"status\"\n aria-live=\"assertive\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ErrorStateIconPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-error-state-icon=\"\" aria-hidden=\"true\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ErrorStateTitlePrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <h3 data-rs-error-state-title=\"\" class=class>\n {children()}\n </h3>\n }\n}\n\n#[component]\npub fn ErrorStateDescriptionPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <p data-rs-error-state-description=\"\" class=class>\n {children()}\n </p>\n }\n}\n\n#[component]\npub fn ErrorStateActionsPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-error-state-actions=\"\" class=class>\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n ErrorStatePrimitive, ErrorStateIconPrimitive,\n ErrorStateTitlePrimitive, ErrorStateDescriptionPrimitive,\n ErrorStateActionsPrimitive,\n};\n\n#[component]\npub fn ErrorState(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ErrorStatePrimitive class=class>\n {children()}\n </ErrorStatePrimitive>\n }\n}\n\n#[component]\npub fn ErrorStateIcon(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ErrorStateIconPrimitive class=class>\n {children()}\n </ErrorStateIconPrimitive>\n }\n}\n\n#[component]\npub fn ErrorStateTitle(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ErrorStateTitlePrimitive class=class>\n {children()}\n </ErrorStateTitlePrimitive>\n }\n}\n\n#[component]\npub fn ErrorStateDescription(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ErrorStateDescriptionPrimitive class=class>\n {children()}\n </ErrorStateDescriptionPrimitive>\n }\n}\n\n#[component]\npub fn ErrorStateActions(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ErrorStateActionsPrimitive class=class>\n {children()}\n </ErrorStateActionsPrimitive>\n }\n}\n\n",
"boundary_src": "use leptos::prelude::*;\nuse super::error_state_ui::{\n ErrorState as ErrorStateUi,\n ErrorStateIcon as ErrorStateIconUi,\n ErrorStateTitle as ErrorStateTitleUi,\n ErrorStateDescription as ErrorStateDescriptionUi,\n ErrorStateActions as ErrorStateActionsUi\n};\n\n#[component]\npub fn ErrorState(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <ErrorStateUi class=class.unwrap_or_default()>{children()}</ErrorStateUi> }\n}\n\n#[component]\npub fn ErrorStateIcon(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <ErrorStateIconUi class=class.unwrap_or_default()>{children()}</ErrorStateIconUi> }\n}\n\n#[component]\npub fn ErrorStateTitle(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <ErrorStateTitleUi class=class.unwrap_or_default()>{children()}</ErrorStateTitleUi> }\n}\n\n#[component]\npub fn ErrorStateDescription(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <ErrorStateDescriptionUi class=class.unwrap_or_default()>{children()}</ErrorStateDescriptionUi> }\n}\n\n#[component]\npub fn ErrorStateActions(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <ErrorStateActionsUi class=class.unwrap_or_default()>{children()}</ErrorStateActionsUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const ERRORSTATE_API: ComponentApi = ComponentApi {\n id: \"error-state\",\n description: \"Error state display\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const ERRORSTATEICON_API: ComponentApi = ComponentApi {\n id: \"error-state-icon\",\n description: \"Error state display\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const ERRORSTATETITLE_API: ComponentApi = ComponentApi {\n id: \"error-state-title\",\n description: \"Error state display\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const ERRORSTATEDESCRIPTION_API: ComponentApi = ComponentApi {\n id: \"error-state-description\",\n description: \"Error state display\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const ERRORSTATEACTIONS_API: ComponentApi = ComponentApi {\n id: \"error-state-actions\",\n description: \"Error state display\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::error_state_boundary::{\n ErrorState, ErrorStateIcon, ErrorStateTitle,\n ErrorStateDescription, ErrorStateActions,\n};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\nuse canonrs_core::primitives::layout::grid::{GridPrimitive as Grid, GridCols};\n\n#[component]\npub fn ErrorStateShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <ErrorState>\n <ErrorStateIcon>\"⚠️\"</ErrorStateIcon>\n <ErrorStateTitle>\"Something went wrong\"</ErrorStateTitle>\n <ErrorStateDescription>\"We encountered an unexpected error. Please try again.\"</ErrorStateDescription>\n <ErrorStateActions>\"Retry\"</ErrorStateActions>\n </ErrorState>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Error feedback always announced and structurally consistent.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Examples\"</span>\n <Grid cols=GridCols::Three>\n <ErrorState>\n <ErrorStateIcon>\"🔌\"</ErrorStateIcon>\n <ErrorStateTitle>\"Connection failed\"</ErrorStateTitle>\n <ErrorStateDescription>\"Check your network and try again.\"</ErrorStateDescription>\n <ErrorStateActions>\"Reconnect\"</ErrorStateActions>\n </ErrorState>\n <ErrorState>\n <ErrorStateIcon>\"🔒\"</ErrorStateIcon>\n <ErrorStateTitle>\"Access denied\"</ErrorStateTitle>\n <ErrorStateDescription>\"You do not have permission to view this page.\"</ErrorStateDescription>\n <ErrorStateActions>\"Go back\"</ErrorStateActions>\n </ErrorState>\n <ErrorState>\n <ErrorStateIcon>\"🗄️\"</ErrorStateIcon>\n <ErrorStateTitle>\"Failed to load data\"</ErrorStateTitle>\n <ErrorStateDescription>\"The requested data could not be fetched.\"</ErrorStateDescription>\n <ErrorStateActions>\"Try again\"</ErrorStateActions>\n </ErrorState>\n </Grid>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"center",
"stack",
"grid"
]
},
{
"id": "field",
"label": "Field",
"category": "Form",
"description": "Form field wrapper with label and error",
"keywords": "",
"pain": "Form fields lose validation and accessibility linkage between label and input",
"promise": "Validation, label and error state unified in a single contract",
"why": "FieldPrimitive encodes validation and disabled state into data attributes. FieldLabel and FieldError ensure correct ARIA mapping. This guarantees consistent field behavior and accessibility.\n",
"before": "// ❌ Typical\nview! {\n <label>\"Email\"</label>\n <input />\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Field>\n <FieldLabel>\"Email\"</FieldLabel>\n </Field>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"forms",
"input validation"
],
"related": [
"form",
"input",
"input_group",
"input_otp",
"textarea",
"label",
"checkbox",
"form_error_summary"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "form",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Field Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::DisabledState;\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum FieldValidationState {\n #[default]\n Idle,\n Valid,\n Invalid,\n Warning,\n}\n\nimpl FieldValidationState {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Idle => \"idle\",\n Self::Valid => \"valid\",\n Self::Invalid => \"invalid\",\n Self::Warning => \"warning\",\n }\n }\n\n pub fn aria_invalid(&self) -> Option<&'static str> {\n match self { Self::Invalid => Some(\"true\"), _ => None }\n }\n}\n\n#[component]\npub fn FieldPrimitive(\n children: Children,\n #[prop(default = FieldValidationState::Idle)] validation: FieldValidationState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_fi = crate::infra::uid::generate(\"fi\");\n view! {\n <div\n data-rs-field=\"\"\n data-rs-uid=uid_fi\n data-rs-interaction=\"init\"\n data-rs-validation=validation.as_str()\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n aria-invalid=validation.aria_invalid()\n aria-disabled=disabled.aria_disabled()\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn FieldLabelPrimitive(\n children: Children,\n #[prop(into, default = String::new())] html_for: String,\n #[prop(default = false)] required: bool,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <label\n data-rs-field-label=\"\"\n for={if html_for.is_empty() { None } else { Some(html_for) }}\n data-rs-required={if required { Some(\"\") } else { None }}\n aria-required={if required { Some(\"true\") } else { None }}\n class=class\n >\n {children()}\n </label>\n }\n}\n\n#[component]\npub fn FieldDescriptionPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-field-description=\"\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn FieldErrorPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-field-error=\"\"\n role=\"alert\"\n aria-live=\"assertive\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn FieldGroupPrimitive(\n children: Children,\n #[prop(into, optional)] aria_label: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-field-group=\"\"\n role=\"group\"\n aria-label=aria_label\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn FieldSetPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <fieldset data-rs-fieldset=\"\" class=class>\n {children()}\n </fieldset>\n }\n}\n\n#[component]\npub fn FieldLegendPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <legend data-rs-field-legend=\"\" class=class>\n {children()}\n </legend>\n }\n}\n\n#[component]\npub fn FieldContentPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <div data-rs-field-content=\"\" class=class>{children()}</div> }\n}\n\n#[component]\npub fn FieldTitlePrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <div data-rs-field-title=\"\" class=class>{children()}</div> }\n}\n\n#[component]\npub fn FieldSeparatorWrapperPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <div data-rs-field-separator-wrapper=\"\" class=class>{children()}</div> }\n}\n\n#[component]\npub fn FieldSeparatorContentPrimitive(\n children: Children,\n) -> impl IntoView {\n view! { <span data-rs-field-separator-content=\"\">{children()}</span> }\n}\n\n#[component]\npub fn FieldErrorItemPrimitive(\n children: Children,\n) -> impl IntoView {\n view! { <span data-rs-field-error-item=\"\">{children()}</span> }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::separator::SeparatorOrientation;\nuse canonrs_core::primitives::{\n FieldPrimitive, FieldSetPrimitive, FieldLabelPrimitive,\n FieldDescriptionPrimitive, FieldErrorPrimitive, FieldGroupPrimitive,\n FieldLegendPrimitive, SeparatorPrimitive,\n FieldErrorItemPrimitive, FieldSeparatorWrapperPrimitive, FieldSeparatorContentPrimitive,\n FieldContentPrimitive, FieldTitlePrimitive,\n};\nuse super::variants::{FieldOrientation, FieldValidation};\nuse super::types::FieldLegendVariant;\n\n#[component]\npub fn FieldSet(children: Children, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <FieldSetPrimitive class=class>{children()}</FieldSetPrimitive> }\n}\n#[component]\npub fn FieldLegend(children: Children, #[prop(default = FieldLegendVariant::Legend)] variant: FieldLegendVariant, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <FieldLegendPrimitive attr:data-rs-variant=variant.as_str() class=class>{children()}</FieldLegendPrimitive> }\n}\n#[component]\npub fn FieldGroup(children: Children, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <FieldGroupPrimitive class=class>{children()}</FieldGroupPrimitive> }\n}\n#[component]\npub fn Field(children: Children, #[prop(default = FieldOrientation::Vertical)] orientation: FieldOrientation, #[prop(default = FieldValidation::None)] _validation: FieldValidation, #[prop(default = false)] disabled: bool, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! {\n <FieldPrimitive attr:data-rs-orientation=orientation.as_str() attr:data-rs-validation=_validation.as_str() attr:data-rs-disabled=if disabled { \"true\" } else { \"false\" } class=class>\n {children()}\n </FieldPrimitive>\n }\n}\n#[component]\npub fn FieldContent(children: Children, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <FieldContentPrimitive class=class>{children()}</FieldContentPrimitive> }\n}\n#[component]\npub fn FieldLabel(children: Children, #[prop(into, default = String::new())] html_for: String, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <FieldLabelPrimitive html_for=html_for class=class>{children()}</FieldLabelPrimitive> }\n}\n#[component]\npub fn FieldTitle(children: Children, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <FieldTitlePrimitive class=class>{children()}</FieldTitlePrimitive> }\n}\n#[component]\npub fn FieldDescription(children: Children, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <FieldDescriptionPrimitive class=class>{children()}</FieldDescriptionPrimitive> }\n}\n#[component]\npub fn FieldError(#[prop(default = vec![])] errors: Vec<String>, #[prop(default = FieldValidation::None)] _validation: FieldValidation, #[prop(optional)] children: Option<Children>, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n if errors.is_empty() && children.is_none() { return view! {}.into_any(); }\n view! {\n <FieldErrorPrimitive class=class>\n {if let Some(child) = children { child().into_any() }\n else { view! { {errors.into_iter().map(|e| view! { <FieldErrorItemPrimitive>{e}</FieldErrorItemPrimitive> }).collect_view()} }.into_any() }}\n </FieldErrorPrimitive>\n }.into_any()\n}\n#[component]\npub fn FieldSeparator(#[prop(optional)] children: Option<Children>, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! {\n <FieldSeparatorWrapperPrimitive class=class>\n <SeparatorPrimitive orientation=SeparatorOrientation::Horizontal decorative=true class=String::new() />\n {children.map(|c| view! { <FieldSeparatorContentPrimitive>{c()}</FieldSeparatorContentPrimitive> })}\n </FieldSeparatorWrapperPrimitive>\n }\n}\n",
"boundary_src": "//! @canon-level: strict\n//! Field Island — Canon Rule #340 (zero-logic boundary)\n\npub use super::field_ui::{FieldLabel, FieldDescription, FieldError, FieldSet, FieldGroup, FieldContent};\npub use super::variants::{FieldOrientation, FieldValidation};\nuse leptos::prelude::*;\nuse super::field_ui::Field as FieldUi;\n\n#[component]\npub fn Field(\n children: Children,\n #[prop(default = FieldOrientation::Vertical)] orientation: FieldOrientation,\n #[prop(default = FieldValidation::None)] _validation: FieldValidation,\n #[prop(default = false)] disabled: bool,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <FieldUi orientation=orientation _validation=_validation disabled=disabled class=class>\n {children()}\n </FieldUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const FIELD_API: ComponentApi = ComponentApi {\n id: \"field\",\n description: \"Form field wrapper with label and error\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"orientation\", kind: PropType::String, required: false, default: Some(\"vertical\"), description: \"Horizontal or vertical orientation\" },\n PropDef { name: \"_validation\", kind: PropType::String, required: false, default: Some(\"none\"), description: \"Prop value\" },\n PropDef { name: \"disabled\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::field_boundary::{Field, FieldLabel, FieldDescription, FieldError, FieldSet, FieldGroup, FieldContent};\nuse super::variants::{FieldOrientation, FieldValidation};\nuse crate::ui::input::input_boundary::Input;\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn FieldShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <FieldSet>\n <FieldGroup>\n <Field>\n <FieldLabel html_for=\"field-email\"><span>\"Email\"</span></FieldLabel>\n <FieldDescription><span>\"Enter your email address.\"</span></FieldDescription>\n <FieldContent>\n <Input placeholder=\"john@example.com\" />\n </FieldContent>\n </Field>\n </FieldGroup>\n </FieldSet>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Validation, label and error state unified in a single contract.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Error state\"</span>\n <FieldSet>\n <FieldGroup>\n <Field _validation=FieldValidation::Error>\n <FieldLabel html_for=\"field-email2\"><span>\"Email\"</span></FieldLabel>\n <FieldContent>\n <Input placeholder=\"invalid@\" />\n </FieldContent>\n <FieldError errors=vec![\"Please enter a valid email.\".to_string()] />\n </Field>\n </FieldGroup>\n </FieldSet>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Horizontal\"</span>\n <FieldSet>\n <FieldGroup>\n <Field orientation=FieldOrientation::Horizontal>\n <FieldLabel html_for=\"field-name\"><span>\"Name\"</span></FieldLabel>\n <FieldContent>\n <Input placeholder=\"John Doe\" />\n </FieldContent>\n </Field>\n </FieldGroup>\n </FieldSet>\n </Stack>\n </Stack>\n }\n}\n",
"block": [
"form_field_block"
],
"blocks_primitives": [
"stack"
]
},
{
"id": "form",
"label": "Form",
"category": "Form",
"description": "Form component",
"keywords": "",
"pain": "Forms mix validation, submission and accessibility inconsistently",
"promise": "Form lifecycle and validation state enforced at container level",
"why": "FormPrimitive encodes validation, disabled and submission states into attributes. ARIA states like busy and invalid are derived automatically. This guarantees consistent form behavior across all inputs.\n",
"before": "// ❌ Typical\nview! {\n <form>\n <input />\n </form>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Form>\n <FormField>\n <FormLabel>\"Name\"</FormLabel>\n </FormField>\n </Form>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"user forms",
"checkout flows"
],
"related": [
"input",
"input_group",
"input_otp",
"textarea",
"field",
"label",
"checkbox",
"form_error_summary"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "form",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Form Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::DisabledState;\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum FormMethod {\n #[default]\n Post,\n Get,\n}\nimpl FormMethod {\n pub fn as_str(&self) -> &'static str {\n match self { Self::Post => \"post\", Self::Get => \"get\" }\n }\n}\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum FormEnctype {\n #[default]\n UrlEncoded,\n Multipart,\n Text,\n}\nimpl FormEnctype {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::UrlEncoded => \"application/x-www-form-urlencoded\",\n Self::Multipart => \"multipart/form-data\",\n Self::Text => \"text/plain\",\n }\n }\n}\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum FormValidationState {\n #[default]\n Idle,\n Submitting,\n Success,\n Error,\n}\nimpl FormValidationState {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Idle => \"idle\",\n Self::Submitting => \"submitting\",\n Self::Success => \"success\",\n Self::Error => \"error\",\n }\n }\n pub fn aria_busy(&self) -> Option<&'static str> {\n if *self == Self::Submitting { Some(\"true\") } else { None }\n }\n pub fn aria_invalid(&self) -> Option<&'static str> {\n if *self == Self::Error { Some(\"true\") } else { None }\n }\n}\n\n#[component]\npub fn FormPrimitive(\n children: Children,\n #[prop(into, default = String::new())] action: String,\n #[prop(default = FormMethod::Post)] method: FormMethod,\n #[prop(default = FormEnctype::UrlEncoded)] enctype: FormEnctype,\n #[prop(default = FormValidationState::Idle)] validation: FormValidationState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(default = true)] novalidate: bool,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_fo = crate::infra::uid::generate(\"fo\");\n view! {\n <form\n data-rs-form=\"\"\n data-rs-uid=uid_fo\n data-rs-interaction=\"init\"\n data-rs-validation=validation.as_str()\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n action={if action.is_empty() { None } else { Some(action) }}\n method=method.as_str()\n enctype=enctype.as_str()\n novalidate=novalidate\n aria-busy=validation.aria_busy()\n aria-invalid=validation.aria_invalid()\n aria-disabled=disabled.aria_disabled()\n class=class\n >\n {children()}\n </form>\n }\n}\n\n#[component]\npub fn FormSectionPrimitive(\n children: Children,\n #[prop(into, optional)] aria_label: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <section\n data-rs-form-section=\"\"\n role=\"group\"\n aria-label=aria_label.unwrap_or_default()\n class=class\n >\n {children()}\n </section>\n }\n}\n\n#[component]\npub fn FormActionsPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-form-actions=\"\"\n role=\"toolbar\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n// ── Form Field System ─────────────────────────────────────────────────────\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum FieldValidationState {\n #[default]\n Idle,\n Valid,\n Invalid,\n Warning,\n}\nimpl FieldValidationState {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Idle => \"idle\",\n Self::Valid => \"valid\",\n Self::Invalid => \"invalid\",\n Self::Warning => \"warning\",\n }\n }\n pub fn aria_invalid(&self) -> Option<&'static str> {\n match self { Self::Invalid => Some(\"true\"), _ => None }\n }\n}\n\n#[component]\npub fn FormFieldPrimitive(\n children: ChildrenFn,\n #[prop(default = FieldValidationState::Idle)] validation: FieldValidationState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-form-field=\"\"\n data-rs-validation=validation.as_str()\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n aria-invalid=validation.aria_invalid()\n aria-disabled=disabled.aria_disabled()\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn FormLabelPrimitive(\n children: Children,\n #[prop(into, default = String::new())] html_for: String,\n #[prop(default = false)] required: bool,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <label\n data-rs-form-label=\"\"\n for=html_for\n data-rs-required={if required { \"true\" } else { \"false\" }}\n aria-required={if required { \"true\" } else { \"false\" }}\n class=class\n >\n <span data-rs-label-text=\"\">{children()}</span>\n </label>\n }\n}\n\n#[component]\npub fn FormErrorPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-form-error=\"\"\n role=\"alert\"\n aria-live=\"assertive\"\n class=class\n >\n <span data-rs-error-text=\"\">{children()}</span>\n </div>\n }\n}\n\n#[component]\npub fn FormHintPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-form-hint=\"\" class=class>\n <span data-rs-form-hint-text=\"\">{children()}</span>\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n FormPrimitive, FormSectionPrimitive, FormActionsPrimitive,\n FormFieldPrimitive, FormLabelPrimitive, FormErrorPrimitive, FormHintPrimitive,\n};\npub use canonrs_core::primitives::{\n FormMethod, FormEnctype, FormValidationState, FieldValidationState,\n};\nuse canonrs_core::meta::DisabledState;\n\n#[component]\npub fn Form(\n children: Children,\n #[prop(into, default = String::new())] action: String,\n #[prop(default = FormMethod::Post)] method: FormMethod,\n #[prop(default = FormEnctype::UrlEncoded)] enctype: FormEnctype,\n #[prop(default = FormValidationState::Idle)] validation: FormValidationState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(default = true)] novalidate: bool,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <FormPrimitive\n action=action method=method enctype=enctype\n validation=validation disabled=disabled\n novalidate=novalidate class=class\n >\n {children()}\n </FormPrimitive>\n }\n}\n\n#[component]\npub fn FormSection(\n children: Children,\n #[prop(into, optional)] aria_label: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <FormSectionPrimitive aria_label=aria_label.unwrap_or_default() class=class>\n {children()}\n </FormSectionPrimitive>\n }\n}\n\n#[component]\npub fn FormActions(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <FormActionsPrimitive class=class>\n {children()}\n </FormActionsPrimitive>\n }\n}\n\n#[component]\npub fn FormField(\n children: ChildrenFn,\n #[prop(default = FieldValidationState::Idle)] validation: FieldValidationState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <FormFieldPrimitive validation=validation disabled=disabled class=class>\n {children()}\n </FormFieldPrimitive>\n }\n}\n\n#[component]\npub fn FormLabel(\n children: Children,\n #[prop(into, default = String::new())] html_for: String,\n #[prop(default = false)] required: bool,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <FormLabelPrimitive html_for=html_for required=required class=class>\n {children()}\n </FormLabelPrimitive>\n }\n}\n\n#[component]\npub fn FormError(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <FormErrorPrimitive class=class>\n {children()}\n </FormErrorPrimitive>\n }\n}\n\n#[component]\npub fn FormHint(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <FormHintPrimitive class=class>\n {children()}\n </FormHintPrimitive>\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! Form Island — Canon Rule #340 (zero-logic boundary)\n\nuse leptos::prelude::*;\npub use canonrs_core::primitives::{FormValidationState, FormMethod, FormEnctype};\npub use super::form_ui::FieldValidationState;\npub use canonrs_core::meta::DisabledState;\nuse super::form_ui::{\n Form as FormUi, FormSection as FormSectionUi, FormActions as FormActionsUi,\n FormField as FormFieldUi, FormLabel as FormLabelUi,\n FormHint as FormHintUi, FormError as FormErrorUi,\n};\n\n#[component]\npub fn Form(\n children: Children,\n #[prop(into, default = String::new())] action: String,\n #[prop(default = FormMethod::Post)] method: FormMethod,\n #[prop(default = FormEnctype::UrlEncoded)] enctype: FormEnctype,\n #[prop(default = FormValidationState::Idle)] validation: FormValidationState,\n) -> impl IntoView {\n view! { <FormUi action=action method=method enctype=enctype validation=validation>{children()}</FormUi> }\n}\n\n#[component]\npub fn FormSection(\n children: Children,\n #[prop(into, optional)] aria_label: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let aria = aria_label.unwrap_or_default();\n view! { <FormSectionUi aria_label=aria class=class>{children()}</FormSectionUi> }\n}\n\n#[component]\npub fn FormActions(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <FormActionsUi class=class>{children()}</FormActionsUi> }\n}\n\n#[component]\npub fn FormField(\n children: ChildrenFn,\n #[prop(default = FieldValidationState::Idle)] validation: FieldValidationState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <FormFieldUi validation=validation disabled=disabled class=class>{children()}</FormFieldUi> }\n}\n\n#[component]\npub fn FormLabel(\n children: Children,\n #[prop(into, default = String::new())] html_for: String,\n #[prop(default = false)] required: bool,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <FormLabelUi html_for=html_for required=required class=class>{children()}</FormLabelUi> }\n}\n\n#[component]\npub fn FormHint(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <FormHintUi class=class>{children()}</FormHintUi> }\n}\n\n#[component]\npub fn FormError(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <FormErrorUi class=class>{children()}</FormErrorUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{FormValidationState, FormMethod, FormEnctype}; \n\npub const FORM_API: ComponentApi = ComponentApi {\n id: \"form\",\n description: \"Form component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"action\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Prop value\" },\n PropDef { name: \"method\", kind: PropType::Enum(&[\"post\", \"get\"]), required: false, default: Some(\"post\"), description: \"Prop value\" },\n PropDef { name: \"enctype\", kind: PropType::Enum(&[\"url-encoded\", \"multipart\", \"text\"]), required: false, default: Some(\"url-encoded\"), description: \"Prop value\" },\n PropDef { name: \"validation\", kind: PropType::Enum(&[\"idle\", \"submitting\", \"success\", \"error\"]), required: false, default: Some(\"idle\"), description: \"Prop value\" },\n ],\n};\n\npub const FORMSECTION_API: ComponentApi = ComponentApi {\n id: \"form-section\",\n description: \"Form component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"aria_label\", kind: PropType::String, required: false, default: None, description: \"Accessible label for screen readers\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const FORMACTIONS_API: ComponentApi = ComponentApi {\n id: \"form-actions\",\n description: \"Form component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const FORMFIELD_API: ComponentApi = ComponentApi {\n id: \"form-field\",\n description: \"Form component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"validation\", kind: PropType::Enum(&[\"idle\", \"valid\", \"invalid\", \"warning\"]), required: false, default: Some(\"idle\"), description: \"Prop value\" },\n PropDef { name: \"disabled\", kind: PropType::String, required: false, default: Some(\"enabled\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const FORMLABEL_API: ComponentApi = ComponentApi {\n id: \"form-label\",\n description: \"Form component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"html_for\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Prop value\" },\n PropDef { name: \"required\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const FORMHINT_API: ComponentApi = ComponentApi {\n id: \"form-hint\",\n description: \"Form component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const FORMERROR_API: ComponentApi = ComponentApi {\n id: \"form-error\",\n description: \"Form component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::form_boundary::{Form, FormActions, FormSection, FormField, FormLabel, FormHint, FormError};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\nuse crate::ui::button::button_boundary::Button;\nuse canonrs_core::primitives::ButtonVariant;\nuse crate::ui::input::input_boundary::Input;\n\n#[component]\npub fn FormShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <Form>\n <FormSection>\n <FormField>\n <FormLabel html_for=\"name\" required=true><span>\"Full name\"</span></FormLabel>\n <FormHint><span>\"Your full legal name.\"</span></FormHint>\n <Input placeholder=\"John Doe\" />\n </FormField>\n <FormField>\n <FormLabel html_for=\"email\"><span>\"Email\"</span></FormLabel>\n <Input placeholder=\"john@example.com\" />\n </FormField>\n </FormSection>\n <FormActions>\n <Button variant=ButtonVariant::Primary><span>\"Submit\"</span></Button>\n </FormActions>\n </Form>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Form lifecycle and validation state enforced at container level.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"With validation\"</span>\n <Form>\n <FormSection>\n <FormField >\n <FormLabel html_for=\"email2\"><span>\"Email\"</span></FormLabel>\n <Input placeholder=\"invalid@\" />\n <FormError><span>\"Please enter a valid email address.\"</span></FormError>\n </FormField>\n <FormField >\n <FormLabel html_for=\"name2\"><span>\"Name\"</span></FormLabel>\n <Input placeholder=\"John Doe\" />\n </FormField>\n </FormSection>\n <FormActions>\n <Button variant=ButtonVariant::Primary><span>\"Submit\"</span></Button>\n </FormActions>\n </Form>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"container",
"stack"
]
},
{
"id": "form_error_summary",
"label": "Form Error Summary",
"category": "Form",
"description": "Form validation error summary",
"keywords": "",
"pain": "Form errors scattered and not announced collectively",
"promise": "All form errors announced together with structured summary",
"why": "FormErrorSummaryPrimitive uses role=\"alert\" with aria-atomic to ensure full error announcement. Errors are grouped and structured. This guarantees accessibility and visibility of all validation issues.\n",
"before": "// ❌ Typical\nview! {\n <div>\"Error\"</div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <FormErrorSummary errors=vec![] />\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"form validation",
"multi-field errors"
],
"related": [
"form",
"input",
"input_group",
"input_otp",
"textarea",
"field",
"label",
"checkbox"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "form",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! FormErrorSummary Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\n\n#[component]\npub fn FormErrorSummaryPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_fes = crate::infra::uid::generate(\"fes\");\n view! {\n <div\n data-rs-form-error-summary=\"\"\n data-rs-uid=uid_fes\n role=\"alert\"\n aria-live=\"assertive\"\n aria-atomic=\"true\"\n tabindex=\"-1\"\n class=class\n >\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n FormErrorSummaryPrimitive\n};\n\n#[derive(Clone, Debug, PartialEq)]\npub struct FormError { pub field_label: String, pub message: String }\n\n#[component]\npub fn FormErrorSummary(#[prop(default = vec![])] errors: Vec<FormError>, #[prop(into, default = \"Please fix the following errors:\".to_string())] title: String, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! {\n <FormErrorSummaryPrimitive class=class>\n <>{title}</>\n <>\n {errors.iter().map(|e| view! {\n <>\n <>\n {e.field_label.clone()}{\": \"}{e.message.clone()}\n </>\n </>\n }).collect_view()}\n </>\n </FormErrorSummaryPrimitive>\n }\n}\n",
"boundary_src": "//! FormErrorSummary Island — Canon Rule passthrough\nuse leptos::prelude::*;\npub use super::form_error_summary_ui::FormError;\n\n#[component]\npub fn FormErrorSummary(\n #[prop(optional)] errors: Option<Vec<FormError>>,\n #[prop(into, default = String::from(\"Please fix the following errors:\"))] title: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <super::form_error_summary_ui::FormErrorSummary\n errors=errors.unwrap_or_default()\n title=title\n class=class\n />\n}\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const FORMERRORSUMMARY_API: ComponentApi = ComponentApi {\n id: \"form-error-summary\",\n description: \"Form validation error summary\",\n props: &[\n PropDef { name: \"errors\", kind: PropType::String, required: false, default: None, description: \"Prop value\" },\n PropDef { name: \"title\", kind: PropType::String, required: false, default: Some(\"Please fix the following errors:\"), description: \"Title slot or text\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::form_error_summary_boundary::{FormErrorSummary, FormError};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn FormErrorSummaryShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <FormErrorSummary\n errors=vec![\n FormError { field_label: \"Email\".to_string(), message: \"Required field.\".to_string() },\n FormError { field_label: \"Password\".to_string(), message: \"Must be at least 8 characters.\".to_string() },\n ]\n />\n <p data-rs-showcase-preview-anchor=\"\">\n \"All form errors announced together with structured summary.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Single error\"</span>\n <FormErrorSummary\n title=\"Please fix this error:\"\n errors=vec![\n FormError { field_label: \"Name\".to_string(), message: \"Cannot be empty.\".to_string() },\n ]\n />\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "hero_ui",
"label": "Hero UI",
"category": "Display",
"description": "Hero typography and label components",
"keywords": "",
"pain": "Hero sections lack consistent structure and semantic grouping",
"promise": "Hero content structured via explicit semantic primitives",
"why": "HeroPrimitive defines a structural block with dedicated parts like title, description and actions. Each part is explicitly declared. This guarantees predictable layout composition.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"hero\">\n <h1>\"Title\"</h1>\n </div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <HeroTitle>\"Title\"</HeroTitle>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"landing pages",
"marketing headers"
],
"related": [
"card",
"resizable",
"scroll_area",
"aspect_ratio",
"page_header",
"toolbar",
"separator"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Zero Drift",
"Deterministic API"
],
"pillar": "layout",
"primitive_src": "",
"ui_src": "",
"boundary_src": "",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const HEROTITLE_API: ComponentApi = ComponentApi {\n id: \"hero-title\",\n description: \"Hero typography and label components\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const HEROSUBTITLE_API: ComponentApi = ComponentApi {\n id: \"hero-subtitle\",\n description: \"Hero typography and label components\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const HERODESCRIPTION_API: ComponentApi = ComponentApi {\n id: \"hero-description\",\n description: \"Hero typography and label components\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const HEROMEDIA_API: ComponentApi = ComponentApi {\n id: \"hero-media\",\n description: \"Hero typography and label components\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const HEROACTIONS_API: ComponentApi = ComponentApi {\n id: \"hero-actions\",\n description: \"Hero typography and label components\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse canonrs_core::slot;\nuse crate::blocks::hero::hero_block::{Hero, HeroVariant};\nuse super::hero_boundary::{HeroTitle, HeroSubtitle, HeroDescription, HeroActions};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\nuse crate::ui::button::button_boundary::Button;\nuse canonrs_core::primitives::ButtonVariant;\n\n#[component]\npub fn HeroShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <Hero\n content=slot!(|| view! {\n <HeroTitle>\"Build faster with CanonRS\"</HeroTitle>\n <HeroSubtitle>\"A design system built for production.\"</HeroSubtitle>\n <HeroDescription>\"Composable, accessible, and fully typed components for Leptos.\"</HeroDescription>\n }.into_any())\n actions=slot!(|| view! {\n <HeroActions>\n <Button variant=ButtonVariant::Primary>\"Get Started\"</Button>\n <Button variant=ButtonVariant::Ghost>\"View Docs\"</Button>\n </HeroActions>\n }.into_any())\n />\n <p data-rs-showcase-preview-anchor=\"\">\n \"Composable subcomponents — use only what you need.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Split variant\"</span>\n <Hero\n variant=HeroVariant::Split\n content=slot!(|| view! {\n <HeroTitle>\"Visual storytelling\"</HeroTitle>\n <HeroSubtitle>\"Media and content side by side.\"</HeroSubtitle>\n }.into_any())\n media=slot!(|| view! {\n <img src=\"/placeholder.png\" alt=\"Hero media example\" />\n }.into_any())\n />\n </Stack>\n </Stack>\n }\n}\n",
"block": [
"hero_block"
],
"blocks_primitives": [
"container",
"stack",
"flex"
]
},
{
"id": "hover_card",
"label": "Hover Card",
"category": "Overlay",
"description": "Hover card popup",
"keywords": "",
"pain": "Hover previews break focus and visibility synchronization",
"promise": "Hover state and positioning enforced via visibility contract",
"why": "HoverCardPrimitive uses VisibilityState and side enums to control display. Trigger and content share synchronized state. This guarantees consistent hover behavior and accessibility.\n",
"before": "// ❌ Typical\nview! {\n <div on:mouseover=show>\"Hover\"</div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <HoverCard>\n <HoverCardTrigger>\"Hover\"</HoverCardTrigger>\n <HoverCardContent>\"Content\"</HoverCardContent>\n </HoverCard>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"user previews",
"tooltips with content"
],
"related": [
"dialog",
"alert_dialog",
"drawer",
"sheet",
"modal",
"confirm_dialog",
"tooltip",
"popover"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "overlay",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! HoverCard Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::VisibilityState;\n\n#[derive(Debug, Clone, Copy, PartialEq, Default)]\npub enum HoverCardSide {\n #[default]\n Top,\n Bottom,\n Left,\n Right,\n}\n\nimpl HoverCardSide {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Top => \"top\",\n Self::Bottom => \"bottom\",\n Self::Left => \"left\",\n Self::Right => \"right\",\n }\n }\n}\n\n\n#[component]\npub fn HoverCardPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_hc = crate::infra::uid::generate(\"hc\");\n view! {\n <div\n data-rs-hover-card=\"\"\n data-rs-uid=uid_hc\n data-rs-interaction=\"overlay\"\n data-rs-state=state.as_str()\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn HoverCardTriggerPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <span\n data-rs-hover-card-trigger=\"\"\n data-rs-state=state.as_str()\n aria-expanded=state.aria_expanded()\n tabindex=\"0\"\n class=class\n >\n {children()}\n </span>\n }\n}\n\n#[component]\npub fn HoverCardContentPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(default = HoverCardSide::Top)] side: HoverCardSide,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-hover-card-content=\"\"\n data-rs-state=state.as_str()\n data-rs-side=side.as_str()\n role=\"tooltip\"\n class=class\n >\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n HoverCardPrimitive, HoverCardTriggerPrimitive, HoverCardContentPrimitive,\n HoverCardSide,\n};\nuse canonrs_core::meta::VisibilityState;\n\n#[component]\npub fn HoverCard(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <HoverCardPrimitive state=state class=class>\n {children()}\n </HoverCardPrimitive>\n }\n}\n\n#[component]\npub fn HoverCardTrigger(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <HoverCardTriggerPrimitive class=class>{children()}</HoverCardTriggerPrimitive> }\n}\n\n#[component]\npub fn HoverCardContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = HoverCardSide::Top)] side: HoverCardSide,\n) -> impl IntoView {\n view! {\n <HoverCardContentPrimitive side=side class=class>\n {children()}\n </HoverCardContentPrimitive>\n }\n}\n",
"boundary_src": "//! @canon-level: strict\n//! HoverCard Island — bootstrap only, delegates to interaction engine\n\nuse leptos::prelude::*;\nuse super::hover_card_ui::{\n HoverCard as HoverCardUi,\n HoverCardTrigger as HoverCardTriggerUi,\n HoverCardContent as HoverCardContentUi\n};\npub use canonrs_core::primitives::HoverCardSide;\nuse canonrs_core::meta::VisibilityState;\n\n#[component]\npub fn HoverCard(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! {\n <HoverCardUi state=VisibilityState::Closed class=class.unwrap_or_default()>\n {children()}\n </HoverCardUi>\n }\n}\n\n#[component]\npub fn HoverCardTrigger(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <HoverCardTriggerUi class=class.unwrap_or_default()>{children()}</HoverCardTriggerUi> }\n}\n\n#[component]\npub fn HoverCardContent(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n #[prop(default = HoverCardSide::Top)] side: HoverCardSide,\n) -> impl IntoView {\n view! { <HoverCardContentUi side=side class=class.unwrap_or_default()>{children()}</HoverCardContentUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{HoverCardSide}; \n\npub const HOVERCARD_API: ComponentApi = ComponentApi {\n id: \"hover-card\",\n description: \"Hover card popup\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const HOVERCARDTRIGGER_API: ComponentApi = ComponentApi {\n id: \"hover-card-trigger\",\n description: \"Hover card popup\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const HOVERCARDCONTENT_API: ComponentApi = ComponentApi {\n id: \"hover-card-content\",\n description: \"Hover card popup\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n PropDef { name: \"side\", kind: PropType::Enum(&[\"top\", \"bottom\", \"left\", \"right\"]), required: false, default: Some(\"top\"), description: \"Tooltip or popover side\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::hover_card_boundary::{HoverCard, HoverCardTrigger, HoverCardContent, HoverCardSide};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn HoverCardShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <HoverCard>\n <HoverCardTrigger>\"Hover me\"</HoverCardTrigger>\n <HoverCardContent side=HoverCardSide::Bottom>\n <p>\"This card appears on hover.\"</p>\n </HoverCardContent>\n </HoverCard>\n <p data-rs-showcase-preview-anchor=\"\">\n \"HoverCard appears on mouse enter and dismisses on mouse leave.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Sides\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Md>\n <HoverCard>\n <HoverCardTrigger>\"Top\"</HoverCardTrigger>\n <HoverCardContent side=HoverCardSide::Top>\"Opens above\"</HoverCardContent>\n </HoverCard>\n <HoverCard>\n <HoverCardTrigger>\"Bottom\"</HoverCardTrigger>\n <HoverCardContent side=HoverCardSide::Bottom>\"Opens below\"</HoverCardContent>\n </HoverCard>\n <HoverCard>\n <HoverCardTrigger>\"Right\"</HoverCardTrigger>\n <HoverCardContent side=HoverCardSide::Right>\"Opens right\"</HoverCardContent>\n </HoverCard>\n </Stack>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "icon",
"label": "Icon",
"category": "Display",
"description": "SVG icon display",
"keywords": "",
"pain": "Icons lack consistent sizing and variant handling",
"promise": "Icon size and variant enforced via typed enums",
"why": "Icon component encodes size and variant through enums. These map directly to data attributes. This guarantees consistent rendering and eliminates class-based inconsistencies.\n",
"before": "// ❌ Typical\nview! {\n <svg class=\"icon-lg\">\"★\"</svg>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Icon size=IconSize::Md>\"★\"</Icon>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"buttons",
"status indicators"
],
"related": [
"avatar",
"logo",
"code_block",
"markdown",
"chart",
"stat",
"inline_meta",
"kbd",
"badge",
"carousel"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "content_display",
"primitive_src": "#![allow(unused_variables)]\n//! @canon-level: strict\n//! Icon Primitive - SVG wrapper\n\nuse leptos::prelude::*;\n\n#[component]\npub fn IconPrimitive(\n children: Children,\n #[prop(default = String::new())] class: String,\n #[prop(optional, into)] id: Option<String>,\n) -> impl IntoView {\n let _uid_ico = crate::infra::uid::generate(\"ico\");\n view! {\n <span data-rs-icon=\"\" class={class} id=id>\n data-rs-uid=uid_ico\n {children()}\n </span>\n }\n}\n\n#[component]\npub fn IconInnerPrimitive(\n children: Children,\n) -> impl IntoView {\n view! { <span data-rs-icon-inner=\"\">{children()}</span> }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{IconPrimitive, IconInnerPrimitive};\n\n#[derive(Clone, Copy, PartialEq, Debug)]\npub enum IconSize { Sm, Md, Lg }\nimpl IconSize { pub fn as_str(&self) -> &'static str { match self { Self::Sm => \"sm\", Self::Md => \"md\", Self::Lg => \"lg\" } } }\n\n#[derive(Clone, Copy, PartialEq, Debug)]\npub enum IconVariant { Default, Muted, Primary, Destructive, Success, Warning }\nimpl IconVariant { pub fn as_str(&self) -> &'static str { match self { Self::Default => \"default\", Self::Muted => \"muted\", Self::Primary => \"primary\", Self::Destructive => \"destructive\", Self::Success => \"success\", Self::Warning => \"warning\" } } }\n\n#[component]\npub fn Icon(#[prop(optional)] children: Option<Children>, #[prop(default = IconSize::Md)] size: IconSize, #[prop(default = IconVariant::Default)] variant: IconVariant, #[prop(default = false)] spin: bool, #[prop(default = String::new())] class: String, #[prop(into, optional)] id: Option<String>) -> impl IntoView {\n view! {\n <IconPrimitive class=class>\n <IconInnerPrimitive>\n {children.map(|c| c())}\n </IconInnerPrimitive>\n </IconPrimitive>\n }\n}\n",
"boundary_src": "//! Icon Island — Canon Rule #340\n//! Passthrough only. Zero logic, zero transformation.\n\nuse leptos::prelude::*;\npub use super::icon_ui::{IconSize, IconVariant};\nuse super::icon_ui::Icon as IconUi;\n\n#[component]\npub fn Icon(\n #[prop(optional)] children: Option<Children>,\n #[prop(default = IconSize::Md)] size: IconSize,\n #[prop(default = IconVariant::Default)] variant: IconVariant,\n #[prop(default = false)] spin: bool,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, optional)] id: Option<String>,\n) -> impl IntoView {\n view! {\n <IconUi size=size variant=variant spin=spin class=class id=id.unwrap_or_default()>\n {children.map(|c| c())}\n </IconUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const ICON_API: ComponentApi = ComponentApi {\n id: \"icon\",\n description: \"SVG icon display\",\n props: &[\n PropDef { name: \"size\", kind: PropType::String, required: false, default: Some(\"md\"), description: \"Size variant of the component\" },\n PropDef { name: \"variant\", kind: PropType::String, required: false, default: Some(\"default\"), description: \"Visual variant of the component\" },\n PropDef { name: \"spin\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n PropDef { name: \"id\", kind: PropType::String, required: false, default: None, description: \"Element id attribute\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::icon_boundary::{Icon, IconSize, IconVariant};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn IconShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Md>\n <Icon size=IconSize::Lg variant=IconVariant::Primary>\"★\"</Icon>\n <Icon size=IconSize::Lg>\"★\"</Icon>\n <Icon size=IconSize::Lg variant=IconVariant::Muted>\"★\"</Icon>\n </Stack>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Icon size and variant enforced via typed enums.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Sizes\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Md>\n <Icon size=IconSize::Sm>\"★\"</Icon>\n <Icon size=IconSize::Md>\"★\"</Icon>\n <Icon size=IconSize::Lg>\"★\"</Icon>\n </Stack>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Variants\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Md>\n <Icon>\"★\"</Icon>\n <Icon variant=IconVariant::Muted>\"★\"</Icon>\n <Icon variant=IconVariant::Primary>\"★\"</Icon>\n <Icon variant=IconVariant::Destructive>\"★\"</Icon>\n <Icon variant=IconVariant::Success>\"★\"</Icon>\n <Icon variant=IconVariant::Warning>\"★\"</Icon>\n </Stack>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Spin\"</span>\n <Icon spin=true>\"⟳\"</Icon>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "icon_button",
"label": "Icon Button",
"category": "Action",
"description": "Button with icon only",
"keywords": "",
"pain": "Icon buttons miss accessibility labels and loading state handling",
"promise": "Accessibility and loading state enforced in button contract",
"why": "IconButtonPrimitive requires aria-label and encodes loading and disabled states. ARIA attributes are derived automatically. This guarantees accessible and predictable behavior.\n",
"before": "// ❌ Typical\nview! {\n <button><svg /></button>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <IconButton aria_label=\"Close\">\"×\"</IconButton>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"close actions",
"toolbar buttons"
],
"related": [
"button",
"button_group",
"copy_button",
"link"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "action",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! IconButton Primitive - HTML puro\n\nuse leptos::prelude::*;\nuse crate::meta::{DisabledState, LoadingState, ToggleState};\n\n#[derive(serde::Serialize, serde::Deserialize, Clone, PartialEq, Default, Debug)]\npub enum IconButtonSize {\n Xs,\n Sm,\n #[default]\n Md,\n Lg,\n Xl,\n}\nimpl IconButtonSize {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Xs => \"xs\",\n Self::Sm => \"sm\",\n Self::Md => \"md\",\n Self::Lg => \"lg\",\n Self::Xl => \"xl\",\n }\n }\n}\n\n#[derive(serde::Serialize, serde::Deserialize, Clone, PartialEq, Default, Debug)]\npub enum IconButtonVariant {\n #[default]\n Default,\n Ghost,\n Outline,\n Solid,\n Subtle,\n Destructive,\n}\nimpl IconButtonVariant {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Default => \"default\",\n Self::Ghost => \"ghost\",\n Self::Outline => \"outline\",\n Self::Solid => \"solid\",\n Self::Subtle => \"subtle\",\n Self::Destructive => \"destructive\",\n }\n }\n}\n\n#[component]\npub fn IconButtonPrimitive(\n children: Children,\n #[prop(into)] aria_label: String,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(default = LoadingState::Idle)] loading: LoadingState,\n #[prop(default = IconButtonVariant::Default)] variant: IconButtonVariant,\n #[prop(default = IconButtonSize::Md)] size: IconButtonSize,\n #[prop(optional)] pressed: Option<ToggleState>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_ib = crate::infra::uid::generate(\"ib\");\n let ta = pressed;\n view! {\n <button\n type=\"button\"\n data-rs-icon-button=\"\"\n data-rs-uid=uid_ib\n data-rs-interaction=\"init\"\n data-rs-variant=variant.as_str()\n data-rs-size=size.as_str()\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n data-rs-loading=loading.as_str()\n data-rs-toggle=ta.map(|t| t.as_str())\n disabled=disabled.disabled()\n aria-disabled=disabled.aria_disabled()\n aria-busy=loading.aria_busy()\n aria-pressed=ta.as_ref().map(|t| t.aria_pressed())\n aria-label=aria_label\n class=class\n >\n <span data-rs-icon-button-inner=\"\">{children()}</span>\n </button>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::meta::{DisabledState, LoadingState, ToggleState};\nuse canonrs_core::primitives::IconButtonPrimitive;\npub use canonrs_core::primitives::{IconButtonSize, IconButtonVariant};\n\n#[component]\npub fn IconButton(\n children: Children,\n aria_label: String,\n #[prop(default = IconButtonSize::Md)] size: IconButtonSize,\n #[prop(default = IconButtonVariant::Default)] variant: IconButtonVariant,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(default = LoadingState::Idle)] loading: LoadingState,\n #[prop(optional)] pressed: Option<ToggleState>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <IconButtonPrimitive\n aria_label=aria_label\n size=size\n variant=variant\n disabled=disabled\n loading=loading\n pressed=pressed.unwrap_or_default()\n class=class\n >\n {children()}\n </IconButtonPrimitive>\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! IconButton Island — Canon Rule #340 (zero-logic boundary)\n\nuse leptos::prelude::*;\nuse super::icon_button_ui::IconButton as IconButtonUi;\npub use canonrs_core::primitives::{\n IconButtonVariant,\n IconButtonSize\n};\nuse canonrs_core::meta::{DisabledState, LoadingState, ToggleState};\n\n#[component]\npub fn IconButton(\n children: Children,\n #[prop(into, default = String::new())] aria_label: String,\n #[prop(default = IconButtonVariant::Default)] variant: IconButtonVariant,\n #[prop(default = IconButtonSize::Md)] size: IconButtonSize,\n #[prop(default = false)] disabled: bool,\n #[prop(default = false)] pressed: bool,\n #[prop(default = false)] loading: bool,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let disabled_state = if disabled { DisabledState::Disabled } else { DisabledState::Enabled };\n let loading_state = if loading { LoadingState::Loading } else { LoadingState::Idle };\n let pressed_state = if pressed { ToggleState::On } else { ToggleState::Off };\n view! {\n <IconButtonUi\n aria_label=aria_label\n variant=variant\n size=size\n disabled=disabled_state\n loading=loading_state\n pressed=pressed_state\n class=class\n >\n {children()}\n </IconButtonUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{IconButtonVariant, IconButtonSize}; \n\npub const ICONBUTTON_API: ComponentApi = ComponentApi {\n id: \"icon-button\",\n description: \"Button with icon only\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"aria_label\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Accessible label for screen readers\" },\n PropDef { name: \"variant\", kind: PropType::Enum(&[\"default\", \"ghost\", \"outline\", \"solid\", \"subtle\", \"destructive\"]), required: false, default: Some(\"default\"), description: \"Visual variant of the component\" },\n PropDef { name: \"size\", kind: PropType::Enum(&[\"xs\", \"sm\", \"md\", \"lg\", \"xl\"]), required: false, default: Some(\"md\"), description: \"Size variant of the component\" },\n PropDef { name: \"disabled\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"pressed\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"loading\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::icon_button_boundary::{IconButton, IconButtonVariant, IconButtonSize};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\nuse canonrs_core::primitives::layout::grid::{GridPrimitive as Grid, GridCols};\n\n#[component]\npub fn IconButtonShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <IconButton aria_label=\"Close\" variant=IconButtonVariant::Solid size=IconButtonSize::Xl>\n \"✕\"\n </IconButton>\n <p data-rs-showcase-preview-anchor()>\n \"Accessibility and loading state enforced in button contract.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Variants\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Sm>\n <IconButton aria_label=\"Default\" variant=IconButtonVariant::Default>\"★\"</IconButton>\n <IconButton aria_label=\"Ghost\" variant=IconButtonVariant::Ghost>\"★\"</IconButton>\n <IconButton aria_label=\"Outline\" variant=IconButtonVariant::Outline>\"★\"</IconButton>\n <IconButton aria_label=\"Solid\" variant=IconButtonVariant::Solid>\"★\"</IconButton>\n <IconButton aria_label=\"Subtle\" variant=IconButtonVariant::Subtle>\"★\"</IconButton>\n <IconButton aria_label=\"Destructive\" variant=IconButtonVariant::Destructive>\"★\"</IconButton>\n </Stack>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Sizes\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Sm>\n <IconButton aria_label=\"Xs\" size=IconButtonSize::Xs variant=IconButtonVariant::Solid>\"★\"</IconButton>\n <IconButton aria_label=\"Sm\" size=IconButtonSize::Sm variant=IconButtonVariant::Solid>\"★\"</IconButton>\n <IconButton aria_label=\"Md\" size=IconButtonSize::Md variant=IconButtonVariant::Solid>\"★\"</IconButton>\n <IconButton aria_label=\"Lg\" size=IconButtonSize::Lg variant=IconButtonVariant::Solid>\"★\"</IconButton>\n <IconButton aria_label=\"Xl\" size=IconButtonSize::Xl variant=IconButtonVariant::Solid>\"★\"</IconButton>\n </Stack>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"States\"</span>\n <Grid cols=GridCols::Four>\n <Stack direction=StackDirection::Vertical gap=StackGap::Xs>\n <span data-rs-showcase-preview-label=\"\">\"Default\"</span>\n <IconButton aria_label=\"Default\" variant=IconButtonVariant::Solid>\"★\"</IconButton>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Xs>\n <span data-rs-showcase-preview-label=\"\">\"Pressed\"</span>\n <IconButton aria_label=\"Pressed\" variant=IconButtonVariant::Solid pressed=true>\"★\"</IconButton>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Xs>\n <span data-rs-showcase-preview-label=\"\">\"Disabled\"</span>\n <IconButton aria_label=\"Disabled\" variant=IconButtonVariant::Solid disabled=true>\"★\"</IconButton>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Xs>\n <span data-rs-showcase-preview-label=\"\">\"Loading\"</span>\n <IconButton aria_label=\"Loading\" variant=IconButtonVariant::Solid loading=true>\"★\"</IconButton>\n </Stack>\n </Grid>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack",
"grid"
]
},
{
"id": "inline_meta",
"label": "Inline Meta",
"category": "Display",
"description": "Inline metadata display for stats, versions, dates and counts",
"keywords": "",
"pain": "Inline metadata mixes labels and values without structure",
"promise": "Metadata pairs structured and consistently rendered",
"why": "InlineMetaPrimitive separates label and value into explicit primitives. This enforces consistent layout and semantics. It prevents ad-hoc inline data rendering.\n",
"before": "// ❌ Typical\nview! {\n <span>\"Rules: 284\"</span>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <InlineMeta>\n <InlineMetaLabel>\"Rules\"</InlineMetaLabel>\n <InlineMetaValue>\"284\"</InlineMetaValue>\n </InlineMeta>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"stats display",
"metadata labels"
],
"related": [
"avatar",
"icon",
"logo",
"code_block",
"markdown",
"chart",
"stat",
"kbd",
"badge",
"carousel"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "content_display",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! InlineMeta Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\n\n#[component]\npub fn InlineMetaPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_im = crate::infra::uid::generate(\"im\");\n view! {\n <span\n data-rs-inline-meta=\"\"\n data-rs-uid=uid_im\n class=class\n >\n {children()}\n </span>\n }\n}\n\n#[component]\npub fn InlineMetaLabelPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <span data-rs-inline-meta-label=\"\" class=class>{children()}</span>\n }\n}\n\n#[component]\npub fn InlineMetaValuePrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <span data-rs-inline-meta-value=\"\" class=class>{children()}</span>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{InlineMetaPrimitive, InlineMetaLabelPrimitive, InlineMetaValuePrimitive};\n\n#[component]\npub fn InlineMeta(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <InlineMetaPrimitive class=class>\n {children()}\n </InlineMetaPrimitive>\n }\n}\n\n#[component]\npub fn InlineMetaLabel(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <InlineMetaLabelPrimitive class=class>\n {children()}\n </InlineMetaLabelPrimitive>\n }\n}\n\n#[component]\npub fn InlineMetaValue(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <InlineMetaValuePrimitive class=class>\n {children()}\n </InlineMetaValuePrimitive>\n }\n}\n\n",
"boundary_src": "//! InlineMeta Island — Canon Rule #340\n//! Passthrough only. Zero logic, zero transformation.\n\nuse leptos::prelude::*;\nuse super::inline_meta_ui::{\n InlineMeta as InlineMetaUi,\n InlineMetaLabel as InlineMetaLabelUi,\n InlineMetaValue as InlineMetaValueUi\n};\n\n#[component]\npub fn InlineMeta(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <InlineMetaUi class=class>{children()}</InlineMetaUi> }\n}\n\n#[component]\npub fn InlineMetaLabel(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <InlineMetaLabelUi class=class>{children()}</InlineMetaLabelUi> }\n}\n\n#[component]\npub fn InlineMetaValue(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <InlineMetaValueUi class=class>{children()}</InlineMetaValueUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const INLINEMETA_API: ComponentApi = ComponentApi {\n id: \"inline-meta\",\n description: \"Inline metadata display for stats, versions, dates and counts\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const INLINEMETALABEL_API: ComponentApi = ComponentApi {\n id: \"inline-meta-label\",\n description: \"Inline metadata display for stats, versions, dates and counts\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const INLINEMETAVALUE_API: ComponentApi = ComponentApi {\n id: \"inline-meta-value\",\n description: \"Inline metadata display for stats, versions, dates and counts\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::inline_meta_boundary::{InlineMeta, InlineMetaLabel, InlineMetaValue};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn InlineMetaShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <InlineMeta>\n <InlineMetaLabel>\"Author\"</InlineMetaLabel>\n <InlineMetaValue>\"Cristiano Bertulucci\"</InlineMetaValue>\n </InlineMeta>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Metadata pairs structured and consistently rendered.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Pairs\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Md>\n <InlineMeta>\n <InlineMetaLabel>\"Status\"</InlineMetaLabel>\n <InlineMetaValue>\"Active\"</InlineMetaValue>\n </InlineMeta>\n <InlineMeta>\n <InlineMetaLabel>\"Version\"</InlineMetaLabel>\n <InlineMetaValue>\"1.4.2\"</InlineMetaValue>\n </InlineMeta>\n <InlineMeta>\n <InlineMetaLabel>\"License\"</InlineMetaLabel>\n <InlineMetaValue>\"MIT\"</InlineMetaValue>\n </InlineMeta>\n </Stack>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Context examples\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Md>\n <InlineMeta>\n <InlineMetaLabel>\"Rules\"</InlineMetaLabel>\n <InlineMetaValue>\"284\"</InlineMetaValue>\n </InlineMeta>\n <InlineMeta>\n <InlineMetaLabel>\"Components\"</InlineMetaLabel>\n <InlineMetaValue>\"97\"</InlineMetaValue>\n </InlineMeta>\n <InlineMeta>\n <InlineMetaLabel>\"Last updated\"</InlineMetaLabel>\n <InlineMetaValue>\"2025-06-01\"</InlineMetaValue>\n </InlineMeta>\n </Stack>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"flex",
"stack"
]
},
{
"id": "inline_notice",
"label": "Inline Notice",
"category": "Feedback",
"description": "Inline notice message",
"keywords": "",
"pain": "Inline messages use wrong ARIA roles and urgency levels",
"promise": "Role and aria-live automatically enforced by variant",
"why": "InlineNoticeVariant controls both semantic role and aria-live behavior. Error uses alert/assertive while others use status/polite. This guarantees correct urgency signaling without manual ARIA decisions.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"notice error\">\"Error\"</div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <InlineNotice variant=InlineNoticeVariant::Error>\n <InlineNoticeContent>\"Error\"</InlineNoticeContent>\n </InlineNotice>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"form inline errors",
"contextual hints"
],
"related": [
"toast",
"alert",
"banner",
"callout",
"status_dot"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "feedback",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! InlineNotice Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\n\n#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Default)]\npub enum InlineNoticeVariant {\n #[default]\n Default,\n Info,\n Success,\n Warning,\n Error,\n}\n\nimpl InlineNoticeVariant {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Default => \"default\",\n Self::Info => \"info\",\n Self::Success => \"success\",\n Self::Warning => \"warning\",\n Self::Error => \"error\",\n }\n }\n\n pub fn role(&self) -> &'static str {\n match self {\n Self::Error => \"alert\",\n _ => \"status\",\n }\n }\n\n pub fn aria_live(&self) -> &'static str {\n match self {\n Self::Error => \"assertive\",\n _ => \"polite\",\n }\n }\n}\n\n#[component]\npub fn InlineNoticePrimitive(\n children: Children,\n #[prop(default = InlineNoticeVariant::Default)] variant: InlineNoticeVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_in = crate::infra::uid::generate(\"in\");\n view! {\n <div\n data-rs-inline-notice=\"\"\n data-rs-uid=uid_in\n data-rs-variant=variant.as_str()\n role=variant.role()\n aria-live=variant.aria_live()\n aria-atomic=\"true\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn InlineNoticeIconPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <span\n data-rs-inline-notice-icon=\"\"\n aria-hidden=\"true\"\n class=class\n >\n {children()}\n </span>\n }\n}\n\n#[component]\npub fn InlineNoticeContentPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <span data-rs-inline-notice-content=\"\" class=class>\n {children()}\n </span>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n InlineNoticePrimitive, InlineNoticeIconPrimitive,\n InlineNoticeContentPrimitive,\n};\npub use canonrs_core::primitives::InlineNoticeVariant;\n\n#[component]\npub fn InlineNotice(\n children: Children,\n #[prop(default = InlineNoticeVariant::Default)] variant: InlineNoticeVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <InlineNoticePrimitive variant=variant class=class>\n {children()}\n </InlineNoticePrimitive>\n }\n}\n\n#[component]\npub fn InlineNoticeIcon(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <InlineNoticeIconPrimitive class=class>\n {children()}\n </InlineNoticeIconPrimitive>\n }\n}\n\n#[component]\npub fn InlineNoticeContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <InlineNoticeContentPrimitive class=class>\n {children()}\n </InlineNoticeContentPrimitive>\n }\n}\n\n",
"boundary_src": "//! InlineNotice Boundary — Canon Rule passthrough\nuse leptos::prelude::*;\nuse super::inline_notice_ui::{InlineNotice as InlineNoticeUi, InlineNoticeIcon, InlineNoticeContent};\npub use canonrs_core::primitives::InlineNoticeVariant;\n\n#[component]\npub fn InlineNotice(\n #[prop(optional, into)] content: Option<String>,\n #[prop(optional, into)] icon: Option<String>,\n #[prop(default = InlineNoticeVariant::Default)] variant: InlineNoticeVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <InlineNoticeUi variant=variant class=class>\n {icon.map(|i| view! { <InlineNoticeIcon>{i}</InlineNoticeIcon> })}\n {content.map(|c| view! { <InlineNoticeContent>{c}</InlineNoticeContent> })}\n </InlineNoticeUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{InlineNoticeVariant}; \n\npub const INLINENOTICE_API: ComponentApi = ComponentApi {\n id: \"inline-notice\",\n description: \"Inline notice message\",\n props: &[\n PropDef { name: \"content\", kind: PropType::String, required: false, default: None, description: \"Content region slot\" },\n PropDef { name: \"icon\", kind: PropType::String, required: false, default: None, description: \"Prop value\" },\n PropDef { name: \"variant\", kind: PropType::Enum(&[\"default\", \"info\", \"success\", \"warning\", \"error\"]), required: false, default: Some(\"default\"), description: \"Visual variant of the component\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::inline_notice_boundary::{InlineNotice, InlineNoticeVariant};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn InlineNoticeShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <InlineNotice variant=InlineNoticeVariant::Info icon=\"ℹ\" content=\"This is an inline informational notice.\" />\n <p data-rs-showcase-preview-anchor=\"\">\n \"Role and aria-live automatically enforced by variant.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Variants\"</span>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <InlineNotice variant=InlineNoticeVariant::Success icon=\"✓\" content=\"Password updated successfully.\" />\n <InlineNotice variant=InlineNoticeVariant::Warning icon=\"⚠\" content=\"This field is required to continue.\" />\n <InlineNotice variant=InlineNoticeVariant::Error icon=\"✕\" content=\"Invalid email address format.\" />\n </Stack>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "input",
"label": "Input",
"category": "Form",
"description": "Text input field",
"keywords": "",
"pain": "Inputs accept invalid visual states without consistent validation mapping",
"promise": "Variant and size strictly constrained via typed enums",
"why": "InputVariant and InputSize define allowed visual states at compile-time. DisabledState is mapped to both DOM and ARIA. This guarantees consistent rendering and prevents invalid combinations.\n",
"before": "// ❌ Typical\nview! {\n <input class=\"input error large\" />\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Input variant=InputVariant::Error size=InputSize::Lg />\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"forms",
"search fields"
],
"related": [
"form",
"input_group",
"input_otp",
"textarea",
"field",
"label",
"checkbox",
"form_error_summary"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "form",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Input Primitive - HTML puro\n\nuse leptos::prelude::*;\nuse crate::meta::DisabledState;\n\n#[derive(Clone, PartialEq, Default, Debug)]\npub enum InputVariant {\n #[default]\n Default,\n Error,\n Success,\n Warning,\n}\nimpl InputVariant {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Default => \"default\",\n Self::Error => \"error\",\n Self::Success => \"success\",\n Self::Warning => \"warning\",\n }\n }\n}\n\n#[derive(Clone, PartialEq, Default, Debug)]\npub enum InputSize {\n #[default]\n Md,\n Sm,\n Lg,\n}\nimpl InputSize {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Md => \"md\",\n Self::Sm => \"sm\",\n Self::Lg => \"lg\",\n }\n }\n}\n\n#[component]\npub fn InputPrimitive(\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = \"text\".to_string())] input_type: String,\n #[prop(into, default = String::new())] name: String,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] rs_value: String,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] placeholder: String,\n #[prop(into, default = String::new())] aria_label: String,\n #[prop(default = InputVariant::Default)] variant: InputVariant,\n #[prop(default = InputSize::Md)] size: InputSize,\n #[prop(optional)] node_ref: Option<NodeRef<leptos::html::Input>>,\n) -> impl IntoView {\n let uid_inp = crate::infra::uid::generate(\"inp\");\n let aria_disabled = if disabled == DisabledState::Disabled { \"true\" } else { \"false\" };\n let nr = node_ref.unwrap_or_default();\n view! {\n <input\n data-rs-input=\"\"\n data-rs-uid=uid_inp\n data-rs-interaction=\"init\"\n data-rs-variant=variant.as_str()\n data-rs-size=size.as_str()\n type=input_type\n class=class\n name=name\n prop:value=value\n data-rs-value=rs_value\n placeholder=placeholder\n disabled=disabled.as_bool()\n aria-disabled=aria_disabled\n aria-label=aria_label\n node_ref=nr\n />\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{InputPrimitive, InputVariant, InputSize};\nuse canonrs_core::meta::DisabledState;\n\n#[component]\npub fn Input(\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = \"text\".to_string())] input_type: String,\n #[prop(into, default = String::new())] name: String,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] rs_value: String,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(default = InputVariant::Default)] variant: InputVariant,\n #[prop(default = InputSize::Md)] size: InputSize,\n #[prop(into, default = String::new())] placeholder: String,\n #[prop(into, default = String::new())] aria_label: String,\n #[prop(optional)] node_ref: Option<NodeRef<leptos::html::Input>>,\n) -> impl IntoView {\n view! {\n <InputPrimitive\n class=class\n name=name\n value=value\n rs_value=rs_value\n disabled=disabled\n placeholder=placeholder\n aria_label=aria_label\n input_type=input_type\n variant=variant\n size=size\n node_ref=node_ref.unwrap_or_default()\n />\n }\n}\n\n",
"boundary_src": "//! Input Island — Canon Rule #340\n//! Passthrough only. Zero logic, zero transformation.\n\nuse leptos::prelude::*;\nuse super::input_ui::Input as InputUi;\npub use canonrs_core::primitives::{\n InputVariant,\n InputSize\n};\npub use canonrs_core::meta::DisabledState;\n\n#[component]\npub fn Input(\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = \"text\".to_string())] input_type: String,\n #[prop(into, default = String::new())] name: String,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] placeholder: String,\n #[prop(into, default = String::new())] aria_label: String,\n #[prop(default = InputVariant::Default)] variant: InputVariant,\n #[prop(default = InputSize::Md)] size: InputSize,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(optional)] node_ref: Option<NodeRef<leptos::html::Input>>,\n) -> impl IntoView {\n view! {\n <InputUi\n class=class\n input_type=input_type\n name=name\n value=value.clone()\n rs_value=value\n placeholder=placeholder\n aria_label=aria_label\n variant=variant\n size=size\n disabled=disabled\n node_ref=node_ref.unwrap_or_default()\n />\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{InputVariant, InputSize}; \n\npub const INPUT_API: ComponentApi = ComponentApi {\n id: \"input\",\n description: \"Text input field\",\n props: &[\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n PropDef { name: \"input_type\", kind: PropType::String, required: false, default: Some(\"text\"), description: \"HTML input type attribute\" },\n PropDef { name: \"name\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Form field name\" },\n PropDef { name: \"value\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Current value\" },\n PropDef { name: \"placeholder\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Placeholder text\" },\n PropDef { name: \"aria_label\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Accessible label for screen readers\" },\n PropDef { name: \"variant\", kind: PropType::Enum(&[\"default\", \"error\", \"success\", \"warning\"]), required: false, default: Some(\"default\"), description: \"Visual variant of the component\" },\n PropDef { name: \"size\", kind: PropType::Enum(&[\"md\", \"sm\", \"lg\"]), required: false, default: Some(\"md\"), description: \"Size variant of the component\" },\n PropDef { name: \"disabled\", kind: PropType::String, required: false, default: Some(\"enabled\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"node_ref\", kind: PropType::String, required: false, default: None, description: \"DOM node reference\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::input_boundary::{Input, InputVariant, InputSize, DisabledState};\nuse canonrs_core::primitives::layout::grid::{GridPrimitive as Grid, GridCols};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn InputShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <Input placeholder=\"Type something...\" />\n <p data-rs-showcase-preview-anchor=\"\">\n \"Variant and size strictly constrained via typed enums.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Variants\"</span>\n <Grid cols=GridCols::Two>\n <Stack direction=StackDirection::Vertical gap=StackGap::Xs>\n <span data-rs-showcase-preview-label=\"\">\"Default\"</span>\n <Input placeholder=\"Default\" variant=InputVariant::Default />\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Xs>\n <span data-rs-showcase-preview-label=\"\">\"Success\"</span>\n <Input placeholder=\"Success\" variant=InputVariant::Success />\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Xs>\n <span data-rs-showcase-preview-label=\"\">\"Warning\"</span>\n <Input placeholder=\"Warning\" variant=InputVariant::Warning />\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Xs>\n <span data-rs-showcase-preview-label=\"\">\"Error\"</span>\n <Input placeholder=\"Error\" variant=InputVariant::Error />\n </Stack>\n </Grid>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Sizes\"</span>\n <Grid cols=GridCols::Three>\n <Stack direction=StackDirection::Vertical gap=StackGap::Xs>\n <span data-rs-showcase-preview-label=\"\">\"Small\"</span>\n <Input placeholder=\"Small\" size=InputSize::Sm />\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Xs>\n <span data-rs-showcase-preview-label=\"\">\"Medium\"</span>\n <Input placeholder=\"Medium\" size=InputSize::Md />\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Xs>\n <span data-rs-showcase-preview-label=\"\">\"Large\"</span>\n <Input placeholder=\"Large\" size=InputSize::Lg />\n </Stack>\n </Grid>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Disabled\"</span>\n <Input placeholder=\"Disabled\" disabled=DisabledState::Disabled />\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack",
"grid"
]
},
{
"id": "input_group",
"label": "Input Group",
"category": "Form",
"description": "Input group with addons",
"keywords": "",
"pain": "Input addons break alignment and border radius consistency",
"promise": "Grouped inputs maintain consistent structure and visual merging",
"why": "InputGroupPrimitive encodes merge-radius behavior via ActivityState. This ensures inputs and addons render as a unified control. It prevents layout inconsistencies across grouped inputs.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"input-group\">\n <span>@</span>\n <input />\n </div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <InputGroup merge_radius=true>\n <span data-rs-input-group-addon=\"\">\"@\"</span>\n </InputGroup>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"email fields",
"currency inputs"
],
"related": [
"form",
"input",
"input_otp",
"textarea",
"field",
"label",
"checkbox",
"form_error_summary"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "form",
"primitive_src": "//! @canon-level: strict\n//! InputGroup Primitive - HTML puro\n\nuse leptos::prelude::*;\nuse crate::meta::ToggleState;\n\n#[component]\npub fn InputGroupPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = ToggleState::Off)] merge_radius: ToggleState,\n #[prop(optional)] node_ref: Option<NodeRef<leptos::html::Div>>,\n) -> impl IntoView {\n let uid_ig = crate::infra::uid::generate(\"ig\");\n view! {\n <div\n data-rs-input-group=\"\"\n data-rs-uid=uid_ig\n data-rs-interaction=\"init\"\n role=\"group\"\n data-rs-toggle=if merge_radius == ToggleState::On { Some(\"on\") } else { None }\n class=class\n node_ref=node_ref.unwrap_or_default()\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn InputGroupPrefix(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_igp = crate::infra::uid::generate(\"igp\");\n view! {\n <div\n data-rs-input-group-prefix=\"\"\n\n data-rs-uid=uid_igp\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn InputGroupSuffix(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_igs = crate::infra::uid::generate(\"igs\");\n view! {\n <div\n data-rs-input-group-suffix=\"\"\n\n data-rs-uid=uid_igs\n class=class\n >\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{InputGroupPrimitive, InputGroupPrefix as InputGroupPrefixPrimitive, InputGroupSuffix as InputGroupSuffixPrimitive};\nuse canonrs_core::meta::ToggleState;\n\n#[component]\npub fn InputGroup(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = ToggleState::Off)] merge_radius: ToggleState,\n #[prop(optional)] node_ref: Option<NodeRef<leptos::html::Div>>,\n) -> impl IntoView {\n view! {\n <InputGroupPrimitive class=class merge_radius=merge_radius node_ref=node_ref.unwrap_or_default()>\n {children()}\n </InputGroupPrimitive>\n }\n}\n\n\n#[component]\npub fn InputGroupPrefix(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <InputGroupPrefixPrimitive class=class>\n {children()}\n </InputGroupPrefixPrimitive>\n }\n}\n\n#[component]\npub fn InputGroupSuffix(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <InputGroupSuffixPrimitive class=class>\n {children()}\n </InputGroupSuffixPrimitive>\n }\n}\n",
"boundary_src": "//! @canon-level: strict\n//! InputGroup Island — Canon Rule #340 (zero-logic boundary)\n\nuse leptos::prelude::*;\nuse super::input_group_ui::{InputGroup as InputGroupUi, InputGroupPrefix as InputGroupPrefixUi, InputGroupSuffix as InputGroupSuffixUi};\nuse canonrs_core::meta::ToggleState;\n\n#[component]\npub fn InputGroup(\n children: Children,\n #[prop(default = false)] merge_radius: bool,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let merge = if merge_radius { ToggleState::On\n} else { ToggleState::Off };\n view! {\n <InputGroupUi merge_radius=merge class=class>\n {children()}\n </InputGroupUi>\n }\n}\n\n#[component]\npub fn InputGroupPrefix(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <InputGroupPrefixUi class=class>\n {children()}\n </InputGroupPrefixUi>\n }\n}\n\n#[component]\npub fn InputGroupSuffix(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <InputGroupSuffixUi class=class>\n {children()}\n </InputGroupSuffixUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const INPUTGROUP_API: ComponentApi = ComponentApi {\n id: \"input-group\",\n description: \"Input group with addons\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"merge_radius\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const INPUTGROUPPREFIX_API: ComponentApi = ComponentApi {\n id: \"input-group-prefix\",\n description: \"Input group with addons\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const INPUTGROUPSUFFIX_API: ComponentApi = ComponentApi {\n id: \"input-group-suffix\",\n description: \"Input group with addons\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::input_group_boundary::InputGroup;\nuse crate::ui::input::input_boundary::Input;\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn InputGroupShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <InputGroup merge_radius=true>\n <span data-rs-input-group-addon=\"\">\"@\"</span>\n <Input placeholder=\"username\" name=\"username\" />\n </InputGroup>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Grouped inputs maintain consistent structure and visual merging.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Detached\"</span>\n <InputGroup>\n <span data-rs-input-group-addon=\"\">\"@\"</span>\n <Input placeholder=\"username\" name=\"username-detached\" />\n </InputGroup>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"URL input\"</span>\n <InputGroup merge_radius=true>\n <span data-rs-input-group-addon=\"\">\"https://\"</span>\n <Input placeholder=\"example.com\" name=\"url\" />\n </InputGroup>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"With suffix\"</span>\n <InputGroup merge_radius=true>\n <Input placeholder=\"0.00\" name=\"amount\" />\n <span data-rs-input-group-addon=\"\">\"USD\"</span>\n </InputGroup>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"flex",
"stack"
]
},
{
"id": "input_otp",
"label": "OTP Input",
"category": "Form",
"description": "One-time password input",
"keywords": "",
"pain": "OTP inputs require complex state sync across multiple fields",
"promise": "OTP slots and active state managed automatically",
"why": "InputOtp distributes value across slots using ActivityState. Each slot reflects position and focus without manual logic. This guarantees synchronized UI and input state.\n",
"before": "// ❌ Typical\n// multiple inputs with manual focus handling\n",
"after": "// ✅ CanonRS\nview! {\n <InputOtp length=6 />\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"2FA codes",
"verification inputs"
],
"related": [
"form",
"input",
"input_group",
"textarea",
"field",
"label",
"checkbox",
"form_error_summary"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "form",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! InputOtp Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::DisabledState;\n\n#[component]\npub fn InputOtpPrimitive(\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] name: String,\n #[prop(into, default = String::new())] value: String,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(default = 1u32)] maxlength: u32,\n #[prop(into, default = String::new())] pattern: String,\n #[prop(into, default = String::new())] inputmode: String,\n #[prop(into, default = String::new())] autocomplete: String,\n) -> impl IntoView {\n let uid_otp = crate::infra::uid::generate(\"otp\");\n view! {\n <input\n data-rs-input-otp=\"\"\n data-rs-uid=uid_otp\n data-rs-interaction=\"init\"\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n type=\"text\"\n name={if name.is_empty() { None } else { Some(name) }}\n value=value\n disabled=disabled.disabled()\n maxlength=maxlength.to_string()\n pattern={if pattern.is_empty() { None } else { Some(pattern) }}\n inputmode={if inputmode.is_empty() { None } else { Some(inputmode) }}\n autocomplete={if autocomplete.is_empty() { None } else { Some(autocomplete) }}\n aria-disabled=disabled.aria_disabled()\n class=class\n />\n }\n}\n\n#[component]\npub fn InputOtpContainerPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(optional)] disabled: Option<bool>,\n) -> impl IntoView {\n view! {\n <div\n data-rs-input-otp-container=\"\"\n data-rs-disabled=disabled.filter(|&d| d).map(|_| \"disabled\")\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn InputOtpSlotsPrimitive(\n children: Children,\n) -> impl IntoView {\n view! { <div data-rs-input-otp-slots=\"\">{children()}</div> }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{InputOtpPrimitive, InputOtpSlotPrimitive, InputOtpContainerPrimitive, InputOtpSlotsPrimitive};\nuse canonrs_core::meta::{ActivityState, DisabledState};\n\n#[component]\npub fn InputOtp(#[prop(into, default = String::new())] class: String, #[prop(into, default = String::new())] name: String, #[prop(into, default = String::new())] value: String, #[prop(default = DisabledState::Enabled)] disabled: DisabledState, #[prop(default = 6u32)] length: u32) -> impl IntoView {\n let is_disabled = disabled == DisabledState::Disabled;\n let slots: Vec<AnyView> = (0..length).map(|i| {\n let ch = value.chars().nth(i as usize).map(|c| c.to_string()).unwrap_or_default();\n let state = if !is_disabled && i == value.len() as u32 { ActivityState::Active } else { ActivityState::Inactive };\n view! { <InputOtpSlotPrimitive state=state>{ch}</InputOtpSlotPrimitive> }.into_any()\n }).collect();\n view! {\n <InputOtpContainerPrimitive disabled=disabled.disabled() class=class>\n <InputOtpPrimitive name=name value=value.clone() disabled=disabled maxlength=length inputmode=\"numeric\".to_string() autocomplete=\"one-time-code\".to_string() />\n <InputOtpSlotsPrimitive>{slots}</InputOtpSlotsPrimitive>\n </InputOtpContainerPrimitive>\n }\n}\n",
"boundary_src": "//! @canon-level: strict\n//! InputOtp Island — Canon Rule #340 (zero-logic boundary)\n\nuse leptos::prelude::*;\nuse super::input_otp_ui::InputOtp as InputOtpUi;\nuse canonrs_core::meta::DisabledState;\n\n#[component]\npub fn InputOtp(\n #[prop(default = 6)] length: u32,\n #[prop(into, default = String::new())] name: String,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] initial_value: String,\n #[prop(default = false)] disabled: bool,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let disabled_state = if disabled { DisabledState::Disabled\n} else { DisabledState::Enabled };\n let val = if !initial_value.is_empty() { initial_value } else { value };\n view! {\n <InputOtpUi length=length name=name value=val disabled=disabled_state class=class />\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const INPUTOTP_API: ComponentApi = ComponentApi {\n id: \"input-otp\",\n description: \"One-time password input\",\n props: &[\n PropDef { name: \"length\", kind: PropType::Number, required: false, default: Some(\"6\"), description: \"Prop value\" },\n PropDef { name: \"name\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Form field name\" },\n PropDef { name: \"value\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Current value\" },\n PropDef { name: \"initial_value\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Prop value\" },\n PropDef { name: \"disabled\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::input_otp_boundary::InputOtp;\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn InputOtpShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <InputOtp length=6 />\n <p data-rs-showcase-preview-anchor=\"\">\n \"OTP slots and active state managed automatically.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"4 digits\"</span>\n <InputOtp length=4 />\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Filled\"</span>\n <InputOtp length=6 value=\"123456\".to_string() />\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Partial\"</span>\n <InputOtp length=6 initial_value=\"123\".to_string() />\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Disabled\"</span>\n <InputOtp length=6 disabled=true />\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"flex",
"stack"
]
},
{
"id": "kbd",
"label": "Kbd",
"category": "Display",
"description": "Keyboard shortcut display",
"keywords": "",
"pain": "Keyboard shortcuts displayed inconsistently with ad-hoc styling",
"promise": "Shortcut representation standardized via size and variant enums",
"why": "KbdPrimitive encodes size and variant into data attributes. Group and separator primitives enforce consistent composition. This guarantees uniform shortcut display.\n",
"before": "// ❌ Typical\nview! {\n <span class=\"kbd\">Ctrl + K</span>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <KbdGroup>\n <Kbd>\"Ctrl\"</Kbd>\n <KbdSeparator />\n <Kbd>\"K\"</Kbd>\n </KbdGroup>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"shortcuts",
"docs"
],
"related": [
"avatar",
"icon",
"logo",
"code_block",
"markdown",
"chart",
"stat",
"inline_meta",
"badge",
"carousel"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "content_display",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Kbd Primitive - HTML puro\n\nuse leptos::prelude::*;\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum KbdSize {\n Sm,\n #[default]\n Md,\n Lg,\n}\nimpl KbdSize {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Sm => \"sm\",\n Self::Md => \"md\",\n Self::Lg => \"lg\",\n }\n }\n}\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum KbdVariant {\n #[default]\n Default,\n Outline,\n Ghost,\n Muted,\n}\nimpl KbdVariant {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Default => \"default\",\n Self::Outline => \"outline\",\n Self::Ghost => \"ghost\",\n Self::Muted => \"muted\",\n }\n }\n}\n\n#[component]\npub fn KbdPrimitive(\n children: Children,\n #[prop(default = KbdSize::Md)] size: KbdSize,\n #[prop(default = KbdVariant::Default)] variant: KbdVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_kbd = crate::infra::uid::generate(\"kbd\");\n view! {\n <kbd\n data-rs-kbd=\"\"\n data-rs-uid=uid_kbd\n data-rs-size=size.as_str()\n data-rs-variant=variant.as_str()\n class=class\n >\n {children()}\n </kbd>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{KbdPrimitive, KbdGroupPrimitive, KbdSeparatorPrimitive};\npub use canonrs_core::primitives::{KbdSize, KbdVariant};\n\n#[component]\npub fn Kbd(\n children: Children,\n #[prop(default = KbdSize::Md)] size: KbdSize,\n #[prop(default = KbdVariant::Default)] variant: KbdVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <KbdPrimitive size=size variant=variant class=class>\n {children()}\n </KbdPrimitive>\n }\n}\n\n#[component]\npub fn KbdGroup(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <KbdGroupPrimitive class=class>\n {children()}\n </KbdGroupPrimitive>\n }\n}\n\n#[component]\npub fn KbdSeparator() -> impl IntoView {\n view! { <KbdSeparatorPrimitive /> }\n}\n\n",
"boundary_src": "//! Kbd Island — Canon Rule #340\n//! Passthrough only. Zero logic, zero transformation.\n\nuse leptos::prelude::*;\npub use super::kbd_ui::{KbdSize, KbdVariant};\nuse super::kbd_ui;\n\n#[component]\npub fn Kbd(\n children: Children,\n #[prop(default = KbdSize::Md)] size: KbdSize,\n #[prop(default = KbdVariant::Default)] variant: KbdVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <kbd_ui::Kbd size=size variant=variant class=class>{children()}</kbd_ui::Kbd> }\n}\n\n#[component]\npub fn KbdGroup(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <kbd_ui::KbdGroup class=class>{children()}</kbd_ui::KbdGroup> }\n}\n\n#[component]\npub fn KbdSeparator() -> impl IntoView {\n view! { <kbd_ui::KbdSeparator /> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const KBD_API: ComponentApi = ComponentApi {\n id: \"kbd\",\n description: \"Keyboard shortcut display\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"size\", kind: PropType::Enum(&[\"sm\", \"md\", \"lg\"]), required: false, default: Some(\"md\"), description: \"Size variant of the component\" },\n PropDef { name: \"variant\", kind: PropType::Enum(&[\"default\", \"outline\", \"ghost\", \"muted\"]), required: false, default: Some(\"default\"), description: \"Visual variant of the component\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const KBDGROUP_API: ComponentApi = ComponentApi {\n id: \"kbd-group\",\n description: \"Keyboard shortcut display\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const KBDSEPARATOR_API: ComponentApi = ComponentApi {\n id: \"kbd-separator\",\n description: \"Keyboard shortcut display\",\n props: &[\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::kbd_boundary::{Kbd, KbdGroup, KbdSeparator, KbdSize, KbdVariant};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn KbdShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <KbdGroup>\n <Kbd>\"Ctrl\"</Kbd>\n <KbdSeparator />\n <Kbd>\"K\"</Kbd>\n </KbdGroup>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Shortcut representation standardized via size and variant enums.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Size variants\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Sm>\n <Kbd size=KbdSize::Sm>\"Sm\"</Kbd>\n <Kbd size=KbdSize::Md>\"Md\"</Kbd>\n <Kbd size=KbdSize::Lg>\"Lg\"</Kbd>\n </Stack>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Variant\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Sm>\n <Kbd>\"Default\"</Kbd>\n <Kbd variant=KbdVariant::Outline>\"Outline\"</Kbd>\n <Kbd variant=KbdVariant::Ghost>\"Ghost\"</Kbd>\n </Stack>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Common shortcuts\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Md>\n <KbdGroup>\n <Kbd>\"Ctrl\"</Kbd><KbdSeparator /><Kbd>\"C\"</Kbd>\n </KbdGroup>\n <KbdGroup>\n <Kbd>\"Ctrl\"</Kbd><KbdSeparator /><Kbd>\"Shift\"</Kbd><KbdSeparator /><Kbd>\"P\"</Kbd>\n </KbdGroup>\n <KbdGroup>\n <Kbd>\"⌘\"</Kbd><KbdSeparator /><Kbd>\"Z\"</Kbd>\n </KbdGroup>\n </Stack>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"flex",
"stack"
]
},
{
"id": "label",
"label": "Label",
"category": "Form",
"description": "Form label component",
"keywords": "",
"pain": "Labels not correctly associated with inputs, breaking accessibility",
"promise": "Label-to-input association enforced via explicit html_for contract",
"why": "LabelPrimitive ensures proper for/id mapping and required state. ARIA attributes are derived automatically. This guarantees accessible labeling without manual wiring.\n",
"before": "// ❌ Typical\nview! {\n <label>\"Email\"</label>\n <input />\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Label for_id=\"email\">\"Email\"</Label>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"forms",
"inputs"
],
"related": [
"form",
"input",
"input_group",
"input_otp",
"textarea",
"field",
"checkbox",
"form_error_summary"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "form",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Label Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\n\n#[component]\npub fn LabelPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] html_for: String,\n #[prop(default = false)] required: bool,\n) -> impl IntoView {\n let uid_lbl = crate::infra::uid::generate(\"lbl\");\n view! {\n <label\n data-rs-label=\"\"\n data-rs-uid=uid_lbl\n for={if html_for.is_empty() { None } else { Some(html_for) }}\n data-rs-required={if required { Some(\"\") } else { None }}\n aria-required={if required { Some(\"true\") } else { None }}\n class=class\n >\n {children()}\n </label>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::LabelPrimitive;\n\n#[component]\npub fn Label(\n children: Children,\n #[prop(into, default = String::new())] for_id: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <LabelPrimitive html_for=for_id class=class>\n {children()}\n </LabelPrimitive>\n }\n}\n\n",
"boundary_src": "//! Label Island — Canon Rule #340\n//! Passthrough only. Zero logic, zero transformation.\n\nuse leptos::prelude::*;\nuse super::label_ui::Label as LabelUi;\n\n#[component]\npub fn Label(\n children: Children,\n #[prop(into, default = String::new())] for_id: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <LabelUi for_id=for_id class=class>\n {children()\n};\n </LabelUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const LABEL_API: ComponentApi = ComponentApi {\n id: \"label\",\n description: \"Form label component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"for_id\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::label_boundary::Label;\nuse crate::ui::input::input_boundary::Input;\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn LabelShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <Stack direction=StackDirection::Vertical gap=StackGap::Xs>\n <Label for_id=\"label-input\">\"Username\"</Label>\n <Input placeholder=\"johndoe\" />\n </Stack>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Label-to-input association enforced via explicit html_for contract.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Multiple labels\"</span>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <Stack direction=StackDirection::Vertical gap=StackGap::Xs>\n <Label for_id=\"label-email\"><span>\"Email\"</span></Label>\n <Input placeholder=\"john@example.com\" input_type=\"email\" />\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Xs>\n <Label for_id=\"label-password\"><span>\"Password\"</span></Label>\n <Input placeholder=\"••••••••\" input_type=\"password\" />\n </Stack>\n </Stack>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "link",
"label": "Link",
"category": "Navigation",
"description": "Hyperlink",
"keywords": "",
"pain": "Links misuse target, rel and disabled states inconsistently",
"promise": "Navigation semantics and external behavior enforced structurally",
"why": "LinkPrimitive controls variant, disabled and external behavior. It automatically sets target and rel attributes. This guarantees safe navigation and consistent semantics.\n",
"before": "// ❌ Typical\nview! {\n <a href=\"/\" target=\"_blank\">\"Link\"</a>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Link href=\"/\" external=true>\"Link\"</Link>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"navigation",
"external links"
],
"related": [
"button",
"button_group",
"icon_button",
"copy_button"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "action",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Link Primitive - HTML puro\n\nuse leptos::prelude::*;\nuse crate::meta::DisabledState;\n\n#[derive(Clone, Copy, PartialEq, Default, Debug, serde::Serialize, serde::Deserialize)]\npub enum LinkVariant {\n #[default]\n Default,\n Muted,\n Underline,\n}\nimpl LinkVariant {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Default => \"default\",\n Self::Muted => \"muted\",\n Self::Underline => \"underline\",\n }\n }\n}\n\n#[component]\npub fn LinkPrimitive(\n children: Children,\n #[prop(into, default = String::new())] href: String,\n #[prop(default = LinkVariant::Default)] variant: LinkVariant,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(default = false)] external: bool,\n #[prop(into, default = String::new())] class: String,\n #[prop(optional)] node_ref: Option<NodeRef<leptos::html::A>>,\n) -> impl IntoView {\n let uid_lnk = crate::infra::uid::generate(\"lnk\");\n let target = if external { \"_blank\" } else { \"\" };\n let rel = if external { \"noopener noreferrer\" } else { \"\" };\n view! {\n <a\n data-rs-link=\"\"\n data-rs-uid=uid_lnk\n data-rs-variant=variant.as_str()\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n href=href\n target=target\n rel=rel\n aria-disabled=disabled.aria_disabled()\n class=class\n node_ref=node_ref.unwrap_or_default()\n >\n {children()}\n </a>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{LinkPrimitive, LinkVariant};\nuse canonrs_core::meta::DisabledState;\n\n#[component]\npub fn Link(\n children: Children,\n href: String,\n #[prop(default = LinkVariant::Default)] variant: LinkVariant,\n #[prop(default = false)] disabled: bool,\n #[prop(default = false)] external: bool,\n #[prop(into, default = String::new())] class: String,\n #[prop(optional)] node_ref: Option<NodeRef<leptos::html::A>>,\n) -> impl IntoView {\n let disabled_state = DisabledState::from(disabled);\n view! {\n <LinkPrimitive\n href=href\n variant=variant\n disabled=disabled_state\n external=external\n class=class\n node_ref=node_ref.unwrap_or_default()\n >\n {children()}\n </LinkPrimitive>\n }\n}\n",
"boundary_src": "//! Link Island — Canon Rule #340\n//! Passthrough only. Zero logic, zero transformation.\n\nuse leptos::prelude::*;\nuse super::link_ui::Link as LinkUi;\npub use canonrs_core::primitives::LinkVariant;\n\n#[component]\npub fn Link(\n children: Children,\n #[prop(into, default = String::new())] href: String,\n #[prop(default = LinkVariant::Default)] variant: LinkVariant,\n #[prop(default = false)] disabled: bool,\n #[prop(default = false)] external: bool,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <LinkUi href=href variant=variant disabled=disabled external=external class=class>\n {children()\n};\n </LinkUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{LinkVariant}; \n\npub const LINK_API: ComponentApi = ComponentApi {\n id: \"link\",\n description: \"Hyperlink\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"href\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Navigation target URL\" },\n PropDef { name: \"variant\", kind: PropType::Enum(&[\"default\", \"muted\", \"underline\"]), required: false, default: Some(\"default\"), description: \"Visual variant of the component\" },\n PropDef { name: \"disabled\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"external\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::link_boundary::{Link, LinkVariant};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn LinkShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <Link href=\"/showcase\" variant=LinkVariant::Default>\"View the Showcase →\"</Link>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Navigation semantics and external behavior enforced structurally.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Variants\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Md>\n <Link href=\"#\" variant=LinkVariant::Default>\"Default\"</Link>\n <Link href=\"#\" variant=LinkVariant::Muted>\"Muted\"</Link>\n <Link href=\"#\" variant=LinkVariant::Underline>\"Underline\"</Link>\n </Stack>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"States\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Md>\n <Link href=\"#\">\"Default\"</Link>\n <Link href=\"#\" disabled=true>\"Disabled\"</Link>\n <Link href=\"https://canonrs.com\" external=true>\"External ↗\"</Link>\n </Stack>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "link_group",
"label": "Link Group",
"category": "Navigation",
"description": "Wrapper that organizes multiple NavItems into a labeled navigation group",
"keywords": "",
"pain": "Navigation links lack grouping semantics and structural consistency",
"promise": "Grouped navigation structured with direction and labeling contract",
"why": "LinkGroup uses NavigationGroup primitives to enforce grouping and labeling. Direction is encoded via enum. This guarantees consistent navigation structure across layouts.\n",
"before": "// ❌ Typical\nview! {\n <div>\n <a>\"A\"</a>\n <a>\"B\"</a>\n </div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <LinkGroup>\n <NavItem label=\"A\" href=\"/a\" />\n </LinkGroup>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"sidebars",
"footers"
],
"related": [
"navigation_menu",
"sidebar",
"nav_item",
"breadcrumb",
"pagination"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "navigation",
"primitive_src": "//! LinkGroup Primitive\n\nuse leptos::prelude::*;\n\n#[component]\npub fn LinkGroupPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_lg = crate::infra::uid::generate(\"lg\");\n view! {\n <nav\n data-rs-link-group=\"\"\n data-rs-uid=uid_lg\n data-rs-interaction=\"nav\"\n class=class\n >\n {children()}\n </nav>\n }\n}\n\n#[component]\npub fn LinkGroupLabelPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-link-group-label=\"\" class=class>\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{LinkGroupPrimitive, LinkGroupLabelPrimitive};\n\n#[derive(Clone, Copy, PartialEq, Default)]\npub enum LinkGroupDirection { #[default] Vertical, Horizontal }\nimpl LinkGroupDirection { pub fn as_str(&self) -> &'static str { match self { Self::Vertical => \"vertical\", Self::Horizontal => \"horizontal\" } } }\n\n#[component]\npub fn LinkGroup(children: Children, #[prop(optional)] label: Option<std::sync::Arc<dyn Fn() -> AnyView + Send + Sync>>, #[prop(default = LinkGroupDirection::Vertical)] direction: LinkGroupDirection, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! {\n <LinkGroupPrimitive class=class>\n {if let Some(l) = label { view! { <LinkGroupLabelPrimitive>{l()}</LinkGroupLabelPrimitive> }.into_any() }\n else { view! { <span data-rs-link-group-empty-label=\"\"/> }.into_any() }}\n <div data-rs-link-group-items=\"\">\n {children()}\n </div>\n </LinkGroupPrimitive>\n }\n}\n",
"boundary_src": "//! @canon-level: strict\n//! LinkGroup Island — bootstrap only, delegates to interaction engine\n\nuse leptos::prelude::*;\nuse super::link_group_ui::{\n LinkGroup as LinkGroupUi,\n LinkGroupDirection\n};\n\n\n\n#[component]\npub fn LinkGroup(\n children: Children,\n #[prop(optional)] label: Option<std::sync::Arc<dyn Fn() -> AnyView + Send + Sync>>,\n #[prop(default = LinkGroupDirection::Vertical)] direction: LinkGroupDirection,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <LinkGroupUi label=label.unwrap_or_else(|| std::sync::Arc::new(|| leptos::prelude::view! {}.into_any())) direction=direction class=class>\n {children()}\n </LinkGroupUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const LINKGROUP_API: ComponentApi = ComponentApi {\n id: \"link-group\",\n description: \"Wrapper that organizes multiple NavItems into a labeled navigation group\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"label\", kind: PropType::String, required: false, default: None, description: \"Accessible label text\" },\n PropDef { name: \"direction\", kind: PropType::String, required: false, default: Some(\"vertical\"), description: \"Stack or flex direction\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::link_group_boundary::LinkGroup;\nuse crate::ui::nav_item::nav_item_boundary::NavItem;\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn LinkGroupShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <LinkGroup label=std::sync::Arc::new(|| view! { \"Product\" }.into_any())>\n <NavItem label=\"Features\" href=\"/features\" active=true.into() />\n <NavItem label=\"Pricing\" href=\"/pricing\" />\n <NavItem label=\"Changelog\" href=\"/changelog\" />\n <NavItem label=\"Roadmap\" href=\"/roadmap\" />\n </LinkGroup>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Grouped navigation structured with direction and labeling contract.\"\n </p>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "list_item",
"label": "List Item",
"category": "Display",
"description": "Single list item with title and description",
"keywords": "",
"pain": "Lists lack consistent selection, disabled and accessibility states",
"promise": "Selection and interaction states encoded via structured attributes",
"why": "ListItem encodes selectable, selected and disabled states into data attributes. ARIA attributes are derived automatically. This guarantees consistent list interaction behavior.\n",
"before": "// ❌ Typical\nview! {\n <li class=\"active\">\"Item\"</li>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <List>\n <ListItem selectable=true selected=true>\"Item\"</ListItem>\n </List>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"menus",
"lists"
],
"related": [
"table",
"data_table",
"virtual_list",
"empty_table",
"tree"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "data",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! List Primitives - Accessible list container + items\n\nuse leptos::prelude::*;\nuse crate::meta::{SelectionState, DisabledState, ActivityState};\n\n#[component]\npub fn ListPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, optional)] aria_label: Option<String>,\n) -> impl IntoView {\n let uid_lst = crate::infra::uid::generate(\"lst\");\n view! {\n <div\n data-rs-list=\"\"\n data-rs-uid=uid_lst\n data-rs-interaction=\"data\"\n role=\"listbox\"\n aria-label=aria_label\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ListItemPrimitive(\n children: Children,\n #[prop(default = SelectionState::Unselected)] selected: SelectionState,\n #[prop(default = ActivityState::Inactive)] focused: ActivityState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let tabindex = if focused.as_str() == \"active\" { \"0\" } else { \"-1\" };\n view! {\n <div\n data-rs-list-item=\"\"\n data-rs-selection=if selected == SelectionState::Selected { Some(\"selected\") } else { None }\n data-rs-focused=focused.as_str()\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n role=\"option\"\n tabindex=tabindex\n aria-selected=if selected == SelectionState::Selected { Some(\"true\") } else { None }\n aria-disabled=disabled.aria_disabled()\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ListItemTitlePrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-list-item-title=\"\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ListItemDescriptionPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-list-item-description=\"\" class=class>\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n ListPrimitive, ListItemPrimitive,\n ListItemTitlePrimitive, ListItemDescriptionPrimitive,\n};\n\n#[derive(Clone, Copy, PartialEq, Debug, Default)]\npub enum ListSelectionMode { #[default] None, Single, Multiple }\nimpl ListSelectionMode { pub fn as_str(&self) -> Option<&'static str> { match self { Self::None => None, Self::Single => Some(\"single\"), Self::Multiple => Some(\"multiple\") } } }\n\n#[component]\npub fn List(children: Children, #[prop(default = ListSelectionMode::None)] selection_mode: ListSelectionMode, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <ListPrimitive class=class attr:data-rs-selection=selection_mode.as_str()>{children()}</ListPrimitive> }\n}\n#[component]\npub fn ListItem(children: Children, #[prop(into, default = String::new())] class: String, #[prop(default = false)] selected: bool, #[prop(default = false)] disabled: bool) -> impl IntoView {\n view! {\n <ListItemPrimitive class=class attr:data-rs-state=if selected { \"selected\" } else { \"idle\" } attr:data-rs-disabled=disabled.then_some(\"\") attr:aria-selected=selected.then_some(\"true\") attr:aria-disabled=disabled.then_some(\"true\") attr:tabindex=if !disabled { Some(\"0\") } else { None }>\n <div data-rs-list-item-content=\"\">{children()}</div>\n </ListItemPrimitive>\n }\n}\n#[component]\npub fn ListItemTitle(children: Children, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <ListItemTitlePrimitive class=class>{children()}</ListItemTitlePrimitive> }\n}\n#[component]\npub fn ListItemDescription(children: Children, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <ListItemDescriptionPrimitive class=class>{children()}</ListItemDescriptionPrimitive> }\n}\n",
"boundary_src": "//! @canon-level: strict\n//! ListItem Island — bootstrap only, delegates to interaction engine\n\nuse leptos::prelude::*;\nuse super::list_item_ui::{\n List as ListUi,\n ListItem as ListItemUi,\n ListItemTitle as ListItemTitleUi,\n ListItemDescription as ListItemDescriptionUi,\n};\npub use super::list_item_ui::ListSelectionMode;\n\n#[component]\npub fn List(\n children: Children,\n #[prop(default = ListSelectionMode::None)] selection_mode: ListSelectionMode,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ListUi selection_mode=selection_mode class=class>\n {children()}\n </ListUi>\n }\n}\n\n#[component]\npub fn ListItem(\n children: Children,\n #[prop(default = false)] selected: bool,\n #[prop(default = false)] disabled: bool,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ListItemUi selected=selected disabled=disabled class=class>\n {children()}\n </ListItemUi>\n }\n}\n\n#[component]\npub fn ListItemTitle(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ListItemTitleUi class=class>\n {children()}\n </ListItemTitleUi>\n }\n}\n\n#[component]\npub fn ListItemDescription(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ListItemDescriptionUi class=class>\n {children()}\n </ListItemDescriptionUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const LIST_API: ComponentApi = ComponentApi {\n id: \"list\",\n description: \"Single list item with title and description\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"selection_mode\", kind: PropType::String, required: false, default: Some(\"none\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const LISTITEM_API: ComponentApi = ComponentApi {\n id: \"list-item\",\n description: \"Single list item with title and description\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"selected\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"disabled\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const LISTITEMTITLE_API: ComponentApi = ComponentApi {\n id: \"list-item-title\",\n description: \"Single list item with title and description\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const LISTITEMDESCRIPTION_API: ComponentApi = ComponentApi {\n id: \"list-item-description\",\n description: \"Single list item with title and description\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::list_item_boundary::{List, ListItem, ListItemTitle, ListItemDescription, ListSelectionMode};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn ListItemShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <List>\n <ListItem>\n <ListItemTitle>\"Alice Johnson\"</ListItemTitle>\n <ListItemDescription>\"Engineer · Active\"</ListItemDescription>\n </ListItem>\n <ListItem>\n <ListItemTitle>\"Bob Smith\"</ListItemTitle>\n <ListItemDescription>\"Designer · Away\"</ListItemDescription>\n </ListItem>\n <ListItem>\n <ListItemTitle>\"Carol White\"</ListItemTitle>\n <ListItemDescription>\"Manager · Active\"</ListItemDescription>\n </ListItem>\n </List>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Selection and interaction states encoded via structured attributes.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Selectable\"</span>\n <List selection_mode=ListSelectionMode::Single>\n <ListItem selected=true>\n <ListItemTitle>\"Alice Johnson\"</ListItemTitle>\n <ListItemDescription>\"Engineer\"</ListItemDescription>\n </ListItem>\n <ListItem>\n <ListItemTitle>\"Bob Smith\"</ListItemTitle>\n <ListItemDescription>\"Designer\"</ListItemDescription>\n </ListItem>\n <ListItem disabled=true>\n <ListItemTitle>\"Carol White\"</ListItemTitle>\n <ListItemDescription>\"Disabled\"</ListItemDescription>\n </ListItem>\n </List>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "loading_overlay",
"label": "Loading Overlay",
"category": "Display",
"description": "Full loading overlay",
"keywords": "",
"pain": "Loading states require manual visibility and aria synchronization",
"promise": "Loading visibility and aria-busy managed automatically",
"why": "LoadingOverlayPrimitive maps LoadingState to visibility and ARIA attributes. Overlay visibility is derived automatically. This guarantees consistent loading feedback without manual logic.\n",
"before": "// ❌ Typical\nview! {\n {if loading { view! { <div class=\"overlay\">\"Loading\"</div> } }}\n}\n",
"after": "// ✅ CanonRS\nview! {\n <LoadingOverlay state=LoadingState::Loading>\n \"Content\"\n </LoadingOverlay>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"async operations",
"page blocking"
],
"related": [
"progress",
"spinner",
"skeleton",
"pulse",
"doc_progress"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "progress",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! LoadingOverlay Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::LoadingState;\n\n#[derive(Clone, PartialEq, Default)]\npub enum OverlayMode {\n #[default]\n Blocking,\n Subtle,\n Skeleton,\n}\n\nimpl OverlayMode {\n pub fn as_str(&self) -> &'static str {\n match self {\n OverlayMode::Blocking => \"blocking\",\n OverlayMode::Subtle => \"subtle\",\n OverlayMode::Skeleton => \"skeleton\",\n }\n }\n}\n\n#[component]\npub fn LoadingOverlayPrimitive(\n children: Children,\n #[prop(default = LoadingState::Idle)] state: LoadingState,\n #[prop(default = OverlayMode::Blocking)] mode: OverlayMode,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_lo = crate::infra::uid::generate(\"lo\");\n let is_loading = state == LoadingState::Loading;\n let data_state = if is_loading { \"loading\" } else { \"idle\" };\n let overlay_aria_hidden = if is_loading { \"false\" } else { \"true\" };\n view! {\n <div\n data-rs-overlay-container=\"\" data-rs-interaction=\"init\"\n data-rs-loading=data_state\n data-rs-overlay-mode=mode.as_str()\n aria-busy=if is_loading { \"true\" } else { \"false\" }\n class=class\n >\n <div data-rs-overlay-content=\"\">\n {children()}\n </div>\n <div\n data-rs-loading-overlay=\"\"\n data-rs-uid=uid_lo\n aria-hidden=overlay_aria_hidden\n aria-live=\"polite\"\n >\n <span data-rs-loading-overlay-spinner=\"\" aria-hidden=\"true\"><svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 12a9 9 0 1 1-6.219-8.56\"/></svg></span>\n </div>\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{LoadingOverlayPrimitive, OverlayMode};\nuse canonrs_core::meta::LoadingState;\n\npub use canonrs_core::primitives::OverlayMode as LoadingOverlayMode;\n\n#[component]\npub fn LoadingOverlay(\n children: Children,\n #[prop(default = LoadingState::Idle)] state: LoadingState,\n #[prop(default = OverlayMode::Blocking)] mode: OverlayMode,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <LoadingOverlayPrimitive state=state mode=mode class=class>\n {children()}\n </LoadingOverlayPrimitive>\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! LoadingOverlay Island — Canon Rule #340 (zero-logic boundary)\n\nuse leptos::prelude::*;\nuse super::loading_overlay_ui::{\n LoadingOverlay as LoadingOverlayUi,\n LoadingOverlayMode\n};\nuse canonrs_core::meta::LoadingState;\n\n#[component]\npub fn LoadingOverlay(\n children: Children,\n #[prop(into, default = String::new())] state: String,\n #[prop(into, default = String::new())] mode: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let loading_state = match state.as_str() {\n \"loading\" => LoadingState::Loading,\n _ => LoadingState::Idle,\n };\n let overlay_mode = match mode.as_str() {\n \"subtle\" => LoadingOverlayMode::Subtle,\n \"skeleton\" => LoadingOverlayMode::Skeleton,\n _ => LoadingOverlayMode::Blocking,\n };\n view! {\n <LoadingOverlayUi state=loading_state mode=overlay_mode class=class>\n {children()}\n </LoadingOverlayUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const LOADINGOVERLAY_API: ComponentApi = ComponentApi {\n id: \"loading-overlay\",\n description: \"Full loading overlay\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"state\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Loading or visibility state\" },\n PropDef { name: \"mode\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Operational mode of the component\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::loading_overlay_boundary::LoadingOverlay;\nuse crate::blocks::card::CardBlock;\nuse crate::ui::card::{CardHeader, CardTitle, CardContent};\nuse canonrs_core::slot;\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn LoadingOverlayShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <CardBlock\n header=slot!(|| view! {\n <CardHeader><CardTitle>\"Loading\"</CardTitle></CardHeader>\n }.into_any())\n content=slot!(|| view! {\n <CardContent>\n <LoadingOverlay state=\"loading\">\n <div data-rs-loading-demo=\"\">\n <span>\"Card title\"</span>\n <span>\"Card description content\"</span>\n </div>\n </LoadingOverlay>\n </CardContent>\n }.into_any())\n />\n <p data-rs-showcase-preview-anchor=\"\">\n \"Loading visibility and aria-busy managed automatically.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"States\"</span>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <CardBlock content=slot!(|| view! {\n <CardContent>\n <LoadingOverlay>\"Idle — content visible\"</LoadingOverlay>\n </CardContent>\n }.into_any()) />\n <CardBlock content=slot!(|| view! {\n <CardContent>\n <LoadingOverlay state=\"loading\">\"Loading — content blocked\"</LoadingOverlay>\n </CardContent>\n }.into_any()) />\n </Stack>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Modes\"</span>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <CardBlock content=slot!(|| view! {\n <CardContent>\n <LoadingOverlay state=\"loading\" mode=\"blocking\">\"Blocking\"</LoadingOverlay>\n </CardContent>\n }.into_any()) />\n <CardBlock content=slot!(|| view! {\n <CardContent>\n <LoadingOverlay state=\"loading\" mode=\"subtle\">\"Subtle\"</LoadingOverlay>\n </CardContent>\n }.into_any()) />\n </Stack>\n </Stack>\n </Stack>\n }\n}\n",
"block": [
"card_block"
],
"blocks_primitives": [
"center",
"stack"
]
},
{
"id": "logo",
"label": "Site Logo",
"category": "Brand",
"description": "CanonRS logo combining SVG icon, wordmark and optional tagline",
"keywords": "",
"pain": "Brand logos implemented inconsistently across layouts and break navigation semantics",
"promise": "Brand identity structure and navigation behavior enforced in a single contract",
"why": "LogoPrimitive enforces anchor semantics, size and variant at the root element. Icon, wordmark and tagline are explicitly structured parts. This guarantees consistent brand rendering and correct navigation behavior.\n",
"before": "// ❌ Typical\nview! {\n <a href=\"/\" class=\"logo\">\n <img src=\"/logo.svg\" />\n <span>\"Brand\"</span>\n </a>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Logo>\n <LogoWordmarkPrimitive>\"Brand\"</LogoWordmarkPrimitive>\n </Logo>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"site header",
"app navigation"
],
"related": [
"avatar",
"icon",
"code_block",
"markdown",
"chart",
"stat",
"inline_meta",
"kbd",
"badge",
"carousel"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "content_display",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Logo Primitive - Brand identity anchor\n\nuse leptos::prelude::*;\n\n#[component]\npub fn LogoPrimitive(\n children: Children,\n #[prop(into, default = \"/\".to_string())] href: String,\n #[prop(into, optional)] aria_label: Option<String>,\n #[prop(into, default = \"md\".to_string())] size: String,\n #[prop(into, default = \"brand\".to_string())] variant: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_lgo = crate::infra::uid::generate(\"lgo\");\n view! {\n <a\n data-rs-logo=\"\"\n data-rs-uid=uid_lgo\n data-rs-size=size\n data-rs-variant=variant\n href=href\n aria-label=aria_label.unwrap_or_default()\n class=class\n >\n {children()}\n </a>\n }\n}\n\n#[component]\npub fn LogoIconPrimitive(\n #[prop(into)] src: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <img\n data-rs-logo-icon=\"\"\n src=src\n alt=\"\"\n aria-hidden=\"true\"\n class=class\n />\n }\n}\n\n#[component]\npub fn LogoWordmarkPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <span data-rs-logo-wordmark=\"\" class=class>{children()}</span>\n }\n}\n\n#[component]\npub fn LogoTaglinePrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <span data-rs-logo-tagline=\"\" class=class>{children()}</span>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{LogoPrimitive, LogoIconPrimitive, LogoWordmarkPrimitive, LogoTaglinePrimitive};\n\n#[derive(Clone, Copy, PartialEq, Default)]\npub enum LogoSize {\n Sm,\n #[default]\n Md,\n Lg,\n}\n\nimpl LogoSize {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Sm => \"sm\",\n Self::Md => \"md\",\n Self::Lg => \"lg\",\n }\n }\n}\n\n#[derive(Clone, Copy, PartialEq, Default)]\npub enum LogoVariant {\n #[default]\n Brand,\n Neutral,\n}\n\nimpl LogoVariant {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Brand => \"brand\",\n Self::Neutral => \"neutral\",\n }\n }\n}\n\n#[component]\npub fn Logo(\n #[prop(default = LogoSize::Md)] size: LogoSize,\n #[prop(default = LogoVariant::Brand)] variant: LogoVariant,\n #[prop(optional)] wordmark: Option<ChildrenFn>,\n #[prop(optional)] tagline: Option<ChildrenFn>,\n #[prop(into, default = \"/\".to_string())] href: String,\n #[prop(into, optional)] aria_label: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <LogoPrimitive\n href=href\n aria_label=aria_label.unwrap_or_default()\n size=size.as_str().to_string()\n variant=variant.as_str().to_string()\n class=class\n >\n <LogoIconPrimitive src=\"/assets/logo_canonrs.svg\".to_string() />\n {wordmark.map(|w| view! {\n <LogoWordmarkPrimitive>{w()}</LogoWordmarkPrimitive>\n })}\n {tagline.map(|t| view! {\n <LogoTaglinePrimitive>{t()}</LogoTaglinePrimitive>\n })}\n </LogoPrimitive>\n }\n}\n",
"boundary_src": "//! Logo Island — Canon Rule #340\n//! Passthrough only. Zero logic, zero transformation.\n//! Note: match on wordmark/tagline is structural (Option<ChildrenFn> Leptos constraint), not logic.\n\nuse leptos::prelude::*;\npub use super::logo_ui::{LogoSize, LogoVariant};\nuse super::logo_ui::Logo as LogoUi;\n\n#[component]\npub fn Logo(\n #[prop(default = LogoSize::Md)] size: LogoSize,\n #[prop(default = LogoVariant::Brand)] variant: LogoVariant,\n #[prop(optional)] wordmark: Option<ChildrenFn>,\n #[prop(optional)] tagline: Option<ChildrenFn>,\n #[prop(into, default = \"/\".to_string())] href: String,\n #[prop(into, default = String::new())] aria_label: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n match (wordmark, tagline) {\n (Some(wm), Some(tl)) => view! {\n <LogoUi size=size variant=variant wordmark=wm tagline=tl href=href aria_label=aria_label class=class />\n }.into_any(),\n (Some(wm), None) => view! {\n <LogoUi size=size variant=variant wordmark=wm href=href aria_label=aria_label class=class />\n }.into_any(),\n (None, Some(tl)) => view! {\n <LogoUi size=size variant=variant tagline=tl href=href aria_label=aria_label class=class />\n }.into_any(),\n (None, None) => view! {\n <LogoUi size=size variant=variant href=href aria_label=aria_label class=class />\n }.into_any(),\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const LOGO_API: ComponentApi = ComponentApi {\n id: \"logo\",\n description: \"CanonRS logo combining SVG icon, wordmark and optional tagline\",\n props: &[\n PropDef { name: \"size\", kind: PropType::String, required: false, default: Some(\"md\"), description: \"Size variant of the component\" },\n PropDef { name: \"variant\", kind: PropType::String, required: false, default: Some(\"brand\"), description: \"Visual variant of the component\" },\n PropDef { name: \"wordmark\", kind: PropType::Children, required: false, default: None, description: \"Prop value\" },\n PropDef { name: \"tagline\", kind: PropType::Children, required: false, default: None, description: \"Prop value\" },\n PropDef { name: \"href\", kind: PropType::String, required: false, default: Some(\"/\"), description: \"Navigation target URL\" },\n PropDef { name: \"aria_label\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Accessible label for screen readers\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::logo_boundary::{Logo, LogoSize, LogoVariant};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn LogoShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <Logo />\n <p data-rs-showcase-preview-anchor=\"\">\n \"Brand identity structure and navigation behavior enforced in a single contract.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Sizes\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Lg>\n <Logo size=LogoSize::Sm />\n <Logo size=LogoSize::Md />\n <Logo size=LogoSize::Lg />\n </Stack>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Variants\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Lg>\n <Logo variant=LogoVariant::Brand />\n <Logo variant=LogoVariant::Neutral />\n </Stack>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"With wordmark\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Lg>\n <Logo wordmark=leptos::children::ToChildren::to_children(|| view! { \"CanonRS\" }) />\n <Logo\n wordmark=leptos::children::ToChildren::to_children(|| view! { \"CanonRS\" })\n tagline=leptos::children::ToChildren::to_children(|| view! { \"Design System\" })\n />\n </Stack>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"flex",
"stack"
]
},
{
"id": "markdown",
"label": "Markdown",
"category": "Display",
"description": "Rendered markdown content",
"keywords": "",
"pain": "Client-side markdown rendering causes hydration mismatch and inconsistent DOM",
"promise": "SSR-safe markdown rendering with deterministic DOM output",
"why": "MarkdownPrimitive injects HTML only during SSR and avoids client mutation. Content and TOC are generated as stable HTML structure. This guarantees hydration-safe rendering with no runtime divergence.\n",
"before": "// ❌ Typical\nview! {\n <div inner_html={render_markdown(md)} />\n}\n",
"after": "// ✅ CanonRS\nview! {\n <MarkdownSurface rendered=rendered />\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"docs",
"blog content"
],
"related": [
"avatar",
"icon",
"logo",
"code_block",
"chart",
"stat",
"inline_meta",
"kbd",
"badge",
"carousel"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "content_display",
"primitive_src": "#![allow(unused_variables)]\n//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Markdown Primitive - HTML puro\n\nuse leptos::prelude::*;\nuse crate::meta::{NavigationState, VisibilityState};\n\n#[component]\npub fn MarkdownPrimitive(\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] inner: String,\n) -> impl IntoView {\n let uid_md = crate::infra::uid::generate(\"md\");\n // inner contém o HTML do layout completo (TOC + content)\n // É injetado via inner_html apenas em SSR — hydration via MarkdownContentPrimitive\n #[cfg(feature = \"ssr\")]\n {\n view! {\n <div\n data-rs-markdown=\"\"\n data-rs-uid=uid_md\n data-rs-interaction=\"content\"\n class=class\n inner_html=inner\n ></div>\n }.into_any()\n }\n #[cfg(not(feature = \"ssr\"))]\n {\n let _ = inner;\n view! {\n <div\n data-rs-markdown=\"\"\n class=class\n ></div>\n }.into_any()\n }\n}\n\n#[component]\npub fn MarkdownToolbarPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-markdown-toolbar=\"\"\n role=\"toolbar\"\n aria-label=\"Markdown toolbar\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn MarkdownToolbarItemPrimitive(\n children: Children,\n #[prop(into, default = String::new())] action: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-markdown-toolbar-item=\"\"\n data-rs-action=action\n class=class\n >\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn MarkdownTocPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <nav\n data-rs-markdown-toc=\"\"\n data-rs-state=state.as_str()\n aria-hidden=state.aria_hidden()\n aria-label=\"Table of contents\"\n class=class\n >\n {children()}\n </nav>\n }\n}\n\n#[component]\npub fn MarkdownTocItemPrimitive(\n #[prop(into)] href: String,\n #[prop(into)] text: String,\n #[prop(default = 2u8)] level: u8,\n #[prop(default = NavigationState::Inactive)] state: NavigationState,\n) -> impl IntoView {\n let aria_current = if state == NavigationState::Current { Some(\"page\") } else { None };\n view! {\n <li\n data-rs-markdown-toc-item=\"\"\n data-rs-level=level.to_string()\n data-rs-navigation=state.as_str()\n >\n <a\n data-rs-markdown-toc-link=\"\"\n aria-current=aria_current\n href=href\n >{text}</a>\n </li>\n }\n}\n\n#[component]\npub fn MarkdownContentPrimitive(\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] html: String,\n) -> impl IntoView {\n #[cfg(feature = \"ssr\")]\n let inner = html;\n #[cfg(not(feature = \"ssr\"))]\n let inner = { let _ = html; String::new() };\n view! {\n <div\n data-rs-markdown-content=\"\"\n class=class\n inner_html=inner.clone()\n ></div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::TocItem;\nuse crate::ui::table_of_contents::table_of_contents_boundary::TableOfContents;\nuse crate::ui::scroll_area::scroll_area_boundary::ScrollArea;\nuse canonrs_core::primitives::table_of_contents::TocMode;\n\n#[derive(Clone, Debug, Default)]\npub struct RenderedMarkdown {\n pub html: String,\n pub toc: Vec<TocItem>,\n}\n\n#[derive(Clone, Copy, Debug, Default, PartialEq)]\npub enum TocPosition {\n #[default]\n Top,\n Sidebar,\n}\n\n#[component]\npub fn MarkdownSurface(\n rendered: RenderedMarkdown,\n #[prop(default = true)] show_toc: bool,\n #[prop(default = true)] show_toolbar: bool,\n #[prop(default = TocPosition::Top)] toc_position: TocPosition,\n #[prop(default = false)] contained: bool,\n #[prop(into, default = String::new())] id: String,\n) -> impl IntoView {\n let has_toc = !rendered.toc.is_empty() && show_toc;\n let is_sidebar = toc_position == TocPosition::Sidebar;\n let toc_items = rendered.toc.clone();\n let content_id = format!(\"{}-content\", id);\n let html = rendered.html.clone();\n let _ = show_toolbar;\n\n let container_style = if contained {\n \"display:flex;flex-direction:row;gap:var(--space-xl);height:500px\"\n } else if has_toc && is_sidebar {\n \"display:flex;flex-direction:row;gap:var(--space-xl)\"\n } else {\n \"\"\n };\n\n view! {\n <div\n data-rs-markdown=\"\"\n data-rs-uid=canonrs_core::infra::uid::generate(\"md\")\n data-rs-interaction=\"content\"\n data-toc-position=if is_sidebar { \"sidebar\" } else { \"top\" }\n data-rs-value=id\n style=container_style\n >\n {(has_toc && is_sidebar).then(|| view! {\n <aside\n data-rs-md-toc-sidebar=\"\"\n style=\"width:var(--markdown-toc-width);flex-shrink:0;height:100%;overflow-y:auto\"\n >\n <TableOfContents\n items=toc_items\n mode=TocMode::Nested\n title=\"On this page\"\n />\n </aside>\n })}\n {if contained { view! {\n <ScrollArea attr:style=\"flex:1;min-width:0;max-width:66%;height:100%\">\n <div\n data-rs-markdown-content=\"\"\n id=content_id\n inner_html=html\n />\n </ScrollArea>\n }.into_any() } else { view! {\n <div\n data-rs-markdown-content=\"\"\n id=content_id\n inner_html=html\n />\n }.into_any() }}\n </div>\n }\n}\n\n#[component]\npub fn MarkdownLayout(\n children: Children,\n #[prop(into, default = String::new())] value: String,\n #[prop(default = TocPosition::Sidebar)] toc_position: TocPosition,\n) -> impl IntoView {\n let toc_pos = if toc_position == TocPosition::Sidebar { \"sidebar\" } else { \"top\" };\n view! {\n <div\n data-rs-markdown=\"\"\n data-rs-component=\"Markdown\"\n data-rs-interaction=\"content\"\n data-toc-position=toc_pos\n data-rs-value=value\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn MarkdownContent(\n rendered: RenderedMarkdown,\n #[prop(into, default = String::new())] id: String,\n) -> impl IntoView {\n let content_id = format!(\"{}-content\", id);\n view! {\n <div\n data-rs-markdown-content=\"\"\n id=content_id\n inner_html=rendered.html\n />\n }\n}\n\n#[component]\npub fn MarkdownTOC(\n toc: Vec<TocItem>,\n #[prop(into, default = String::new())] id: String,\n #[prop(into, optional)] scroll_target: Option<String>,\n) -> impl IntoView {\n let _ = id;\n let _ = scroll_target;\n view! {\n <aside data-rs-md-toc-sidebar=\"\">\n <TableOfContents\n items=toc\n mode=TocMode::Nested\n title=\"On this page\"\n />\n </aside>\n }\n}\n\n",
"boundary_src": "use leptos::prelude::*;\nuse super::markdown_ui::{\n MarkdownSurface as MarkdownSurfaceUi,\n MarkdownTOC as MarkdownTOCUi,\n MarkdownContent as MarkdownContentUi,\n MarkdownLayout as MarkdownLayoutUi,\n RenderedMarkdown,\n};\n\npub use super::markdown_ui::TocPosition;\n\n#[component]\npub fn MarkdownSurface(\n rendered: RenderedMarkdown,\n #[prop(optional)] show_toc: Option<bool>,\n #[prop(optional)] show_toolbar: Option<bool>,\n #[prop(optional)] toc_position: Option<TocPosition>,\n #[prop(default = false)] contained: bool,\n #[prop(optional, into)] id: Option<String>,\n) -> impl IntoView {\n let toc_pos = toc_position.unwrap_or(TocPosition::Top);\n view! {\n <MarkdownSurfaceUi\n rendered=rendered\n show_toc=show_toc.unwrap_or(true)\n show_toolbar=show_toolbar.unwrap_or(true)\n toc_position=toc_pos\n contained=contained\n id=id.unwrap_or_default()\n />\n }\n}\n\n#[component]\npub fn MarkdownLayout(\n children: Children,\n #[prop(optional, into)] value: Option<String>,\n #[prop(optional)] toc_position: Option<TocPosition>,\n) -> impl IntoView {\n let toc_pos = toc_position.unwrap_or(TocPosition::Top);\n view! {\n <MarkdownLayoutUi value=value.unwrap_or_default() toc_position=toc_pos>\n {children()}\n </MarkdownLayoutUi>\n }\n}\n\n#[component]\npub fn MarkdownContent(\n rendered: RenderedMarkdown,\n #[prop(optional, into)] id: Option<String>,\n) -> impl IntoView {\n view! { <MarkdownContentUi rendered=rendered id=id.unwrap_or_default() /> }\n}\n\n#[component]\npub fn MarkdownTOC(\n toc: Vec<canonrs_core::TocItem>,\n #[prop(optional, into)] id: Option<String>,\n #[prop(optional, into)] scroll_target: Option<String>,\n) -> impl IntoView {\n view! { <MarkdownTOCUi toc=toc id=id.unwrap_or_default() scroll_target=scroll_target.unwrap_or_default() /> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const MARKDOWNSURFACE_API: ComponentApi = ComponentApi {\n id: \"markdown-surface\",\n description: \"Rendered markdown content\",\n props: &[\n PropDef { name: \"rendered\", kind: PropType::String, required: true, default: None, description: \"Prop value\" },\n PropDef { name: \"show_toc\", kind: PropType::Bool, required: false, default: None, description: \"Whether to show table of contents\" },\n PropDef { name: \"show_toolbar\", kind: PropType::Bool, required: false, default: None, description: \"Whether to show markdown toolbar\" },\n PropDef { name: \"toc_position\", kind: PropType::String, required: false, default: None, description: \"Position of table of contents\" },\n PropDef { name: \"contained\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Whether to constrain width\" },\n PropDef { name: \"id\", kind: PropType::String, required: false, default: None, description: \"Element id attribute\" },\n ],\n};\n\npub const MARKDOWNLAYOUT_API: ComponentApi = ComponentApi {\n id: \"markdown-layout\",\n description: \"Rendered markdown content\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"value\", kind: PropType::String, required: false, default: None, description: \"Current value\" },\n PropDef { name: \"toc_position\", kind: PropType::String, required: false, default: None, description: \"Position of table of contents\" },\n ],\n};\n\npub const MARKDOWNCONTENT_API: ComponentApi = ComponentApi {\n id: \"markdown-content\",\n description: \"Rendered markdown content\",\n props: &[\n PropDef { name: \"rendered\", kind: PropType::String, required: true, default: None, description: \"Prop value\" },\n PropDef { name: \"id\", kind: PropType::String, required: false, default: None, description: \"Element id attribute\" },\n ],\n};\n\npub const MARKDOWNTOC_API: ComponentApi = ComponentApi {\n id: \"markdown-t-o-c\",\n description: \"Rendered markdown content\",\n props: &[\n PropDef { name: \"toc\", kind: PropType::String, required: true, default: None, description: \"Prop value\" },\n PropDef { name: \"id\", kind: PropType::String, required: false, default: None, description: \"Element id attribute\" },\n PropDef { name: \"scroll_target\", kind: PropType::String, required: false, default: None, description: \"Scroll target element id\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::markdown_boundary::{MarkdownSurface, TocPosition};\nuse super::render_markdown;\n\n#[component]\npub fn MarkdownShowcasePreview() -> impl IntoView {\n let sample = concat!(\n \"# CanonRS Design System\\n\\n\",\n \"CanonRS is a design system built with **Leptos** and **Rust**. \\n\",\n \"Every component follows a strict contract between primitives, tokens, and interaction modules.\\n\\n\",\n \"## Architecture\\n\\n\",\n \"The system is built on three layers: primitives define the contract, \\n\",\n \"UI components compose them, and interaction modules handle behavior exclusively on the client.\\n\\n\",\n \"### Primitives\\n\\n\",\n \"Primitives are the foundation. They render SSR-safe HTML with `data-rs-*` attributes \\n\",\n \"that declare behavior without executing it. No logic lives inside a primitive.\\n\\n\",\n \"### Tokens\\n\\n\",\n \"All visual decisions are encoded as CSS custom properties. \\n\",\n \"Tokens are organized by family — layout, color, motion, typography — \\n\",\n \"and are the single source of truth for theming.\\n\\n\",\n \"## Features\\n\\n\",\n \"- SSR-safe rendering with deterministic DOM output\\n\",\n \"- Token-driven theming via CSS custom properties\\n\",\n \"- Behavior layer that runs exclusively on the client\\n\",\n \"- Full ARIA compliance via structured contracts\\n\",\n \"- Zero hydration mismatch by design\\n\\n\",\n \"## Component Table\\n\\n\",\n \"| Component | Category | Status |\\n\",\n \"|-----------|----------|--------|\\n\",\n \"| Button | Actions | Stable |\\n\",\n \"| Avatar | Identity | Stable |\\n\",\n \"| Progress | Feedback | Stable |\\n\",\n \"| CodeBlock | Content | Stable |\\n\",\n \"| Markdown | Content | Stable |\\n\\n\",\n \"## Code Example\\n\\n\",\n \"```rust\\n\",\n \"fn main() {\\n\",\n \" println!(\\\"Hello, CanonRS!\\\");\\n\",\n \"}\\n\",\n \"```\\n\\n\",\n \"## Interaction Model\\n\\n\",\n \"All behavior is delegated to WASM modules that operate directly on the DOM. \\n\",\n \"The boundary layer is zero-logic — it declares, never executes.\\n\\n\",\n \"### Init vs Interaction\\n\\n\",\n \"Simple stateless behaviors use the init layer. \\n\",\n \"Complex coordinated behaviors — sorting, selection engines, virtualization — \\n\",\n \"belong to the interaction layer and run as dedicated WASM modules.\\n\\n\",\n \"## Summary\\n\\n\",\n \"All components follow the Canon contract. \\n\",\n \"The DOM is the source of truth. CSS reacts to state. WASM drives behavior.\\n\"\n );\n\n let rendered = render_markdown(sample);\n\n view! {\n <MarkdownSurface\n rendered=rendered\n show_toc=true\n show_toolbar=false\n toc_position=TocPosition::Sidebar\n contained=true\n id=\"md-preview\"\n />\n }\n}\n",
"block": [],
"blocks_primitives": [
"flex",
"stack"
]
},
{
"id": "menu",
"label": "Menu",
"category": "Navigation",
"description": "Menu component",
"keywords": "",
"pain": "Menus lack consistent selection, disabled and focus behavior",
"promise": "Menu interaction fully governed via structured ARIA and state attributes",
"why": "MenuItemPrimitive encodes selection, disabled and activity states into data-rs and ARIA attributes. Navigation semantics are enforced at the container level. This guarantees predictable keyboard and accessibility behavior.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"menu\">\n <button class=\"active\">\"Item\"</button>\n </div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Menu>\n <MenuItem selected=true>\"Item\"</MenuItem>\n </Menu>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"dropdown lists",
"action menus"
],
"related": [
"dropdown_menu",
"context_menu",
"menubar",
"command"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "menu",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Menu Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::{SelectionState, DisabledState};\n\n#[component]\npub fn MenuPrimitive(\n children: Children,\n #[prop(into, optional)] aria_label: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_mn = crate::infra::uid::generate(\"mn\");\n view! {\n <nav\n data-rs-menu=\"\"\n data-rs-uid=uid_mn\n data-rs-interaction=\"init\"\n aria-label=aria_label\n class=class\n >\n {children()}\n </nav>\n }\n}\n\n#[component]\npub fn MenuItemPrimitive(\n children: Children,\n #[prop(default = SelectionState::Unselected)] selected: SelectionState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_mi = crate::infra::uid::generate(\"mi\");\n view! {\n <button\n type=\"button\"\n data-rs-menu-item=\"\"\n\n data-rs-uid=uid_mi\n role=\"menuitem\"\n data-rs-selection=if selected == SelectionState::Selected { Some(\"selected\") } else { None }\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n aria-selected=if selected == SelectionState::Selected { Some(\"true\") } else { None }\n aria-disabled=disabled.aria_disabled()\n tabindex=if disabled.disabled() { \"-1\" } else { \"0\" }\n class=class\n >\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn MenuGroupPrimitive(\n children: Children,\n #[prop(into, optional)] label: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-menu-group=\"\"\n role=\"group\"\n aria-label=label\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn MenuLabelPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-menu-label=\"\"\n role=\"presentation\"\n aria-hidden=\"true\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn MenuSeparatorPrimitive(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-menu-separator=\"\" role=\"separator\" class=class />\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n MenuPrimitive, MenuItemPrimitive, MenuGroupPrimitive,\n MenuLabelPrimitive, MenuSeparatorPrimitive,\n};\nuse canonrs_core::meta::{DisabledState, SelectionState};\n\n#[component]\npub fn Menu(\n children: Children,\n #[prop(optional, into)] aria_label: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <MenuPrimitive aria_label=aria_label.unwrap_or_default() class=class>\n {children()}\n </MenuPrimitive>\n }\n}\n\n#[component]\npub fn MenuItem(\n children: Children,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(default = SelectionState::Unselected)] selected: SelectionState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <MenuItemPrimitive disabled=disabled selected=selected class=class>\n {children()}\n </MenuItemPrimitive>\n }\n}\n\n#[component]\npub fn MenuGroup(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <MenuGroupPrimitive class=class>\n {children()}\n </MenuGroupPrimitive>\n }\n}\n\n#[component]\npub fn MenuLabel(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <MenuLabelPrimitive class=class>\n {children()}\n </MenuLabelPrimitive>\n }\n}\n\n#[component]\npub fn MenuSeparator(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <MenuSeparatorPrimitive class=class />\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! Menu Island — Canon Rule #340 (zero-logic boundary)\n\nuse leptos::prelude::*;\nuse super::menu_ui::{\n Menu as MenuUi,\n MenuItem as MenuItemUi\n};\nuse canonrs_core::meta::{DisabledState, SelectionState};\n\n#[component]\npub fn Menu(\n children: Children,\n #[prop(into, default = String::new())] aria_label: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <MenuUi aria_label=aria_label class=class>{children()}</MenuUi> }\n}\n\n#[component]\npub fn MenuItem(\n children: Children,\n #[prop(into, default = String::new())] value: String,\n #[prop(default = false)] selected: bool,\n #[prop(default = false)] disabled: bool,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let selected_state = if selected { SelectionState::Selected } else { SelectionState::Unselected };\n let disabled_state = if disabled { DisabledState::Disabled } else { DisabledState::Enabled };\n let _ = value; // stored in data-rs-value by primitive\n view! { <MenuItemUi selected=selected_state disabled=disabled_state class=class>{children()}</MenuItemUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const MENU_API: ComponentApi = ComponentApi {\n id: \"menu\",\n description: \"Menu component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"aria_label\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Accessible label for screen readers\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const MENUITEM_API: ComponentApi = ComponentApi {\n id: \"menu-item\",\n description: \"Menu component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"value\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Current value\" },\n PropDef { name: \"selected\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"disabled\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::menu_boundary::{Menu, MenuItem};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn MenuShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Menu interaction fully governed via DOM — SSR-safe, hydration-safe.\"\n </p>\n <Menu aria_label=\"Main menu\">\n <MenuItem value=\"new\">\"New file\"</MenuItem>\n <MenuItem value=\"open\">\"Open file\"</MenuItem>\n <MenuItem value=\"save\" selected=true>\"Save\"</MenuItem>\n <MenuItem value=\"export\" disabled=true>\"Export (disabled)\"</MenuItem>\n </Menu>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Edit menu\"</span>\n <Menu aria_label=\"Edit menu\">\n <MenuItem value=\"cut\">\"Cut\"</MenuItem>\n <MenuItem value=\"copy\">\"Copy\"</MenuItem>\n <MenuItem value=\"paste\">\"Paste\"</MenuItem>\n <MenuItem value=\"undo\">\"Undo\"</MenuItem>\n <MenuItem value=\"redo\">\"Redo\"</MenuItem>\n </Menu>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "menubar",
"label": "Menubar",
"category": "Navigation",
"description": "Menu bar navigation",
"keywords": "",
"pain": "Horizontal menus break ARIA roles and keyboard navigation",
"promise": "Menubar semantics and structure enforced via primitives",
"why": "MenubarPrimitive enforces role=\"menubar\" and structured menu composition. Trigger and content follow strict ARIA relationships. This guarantees accessible and predictable navigation behavior.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"menubar\">\n <button>\"File\"</button>\n </div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Menubar>\n <MenubarMenu>\n <MenubarTrigger>\"File\"</MenubarTrigger>\n </MenubarMenu>\n </Menubar>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"desktop apps",
"top navigation"
],
"related": [
"dropdown_menu",
"context_menu",
"menu",
"command"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "menu",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Menubar Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::DisabledState;\n\n#[component]\npub fn MenubarPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_mb = crate::infra::uid::generate(\"mb\");\n view! {\n <div\n data-rs-menubar=\"\"\n data-rs-uid=uid_mb\n data-rs-interaction=\"nav\"\n role=\"menubar\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn MenubarMenuPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-menubar-menu=\"\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn MenubarTriggerPrimitive(\n children: Children,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-menubar-trigger=\"\"\n role=\"menuitem\"\n aria-haspopup=\"menu\"\n aria-expanded=\"false\"\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n aria-disabled=disabled.aria_disabled()\n class=class\n >\n {children()}\n <svg data-rs-menubar-chevron=\"\" xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><path d=\"m6 9 6 6 6-6\"/></svg>\n </button>\n }\n}\n\n#[component]\npub fn MenubarContentPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-menubar-content=\"\"\n role=\"menu\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn MenubarItemPrimitive(\n children: Children,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-menubar-item=\"\"\n role=\"menuitem\"\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n aria-disabled=disabled.aria_disabled()\n tabindex=if disabled.disabled() { \"-1\" } else { \"0\" }\n class=class\n >\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn MenubarSeparatorPrimitive(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-menubar-separator=\"\" role=\"separator\" class=class />\n }\n}\n\n#[component]\npub fn MenubarLabelPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-menubar-label=\"\"\n role=\"presentation\"\n aria-hidden=\"true\"\n class=class\n >\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n MenubarPrimitive,\n MenubarMenuPrimitive,\n MenubarTriggerPrimitive,\n MenubarContentPrimitive,\n MenubarItemPrimitive,\n MenubarSeparatorPrimitive,\n};\n\n#[component]\npub fn Menubar(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <MenubarPrimitive class=class>\n {children()}\n </MenubarPrimitive>\n }\n}\n\n#[component]\npub fn MenubarMenu(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <MenubarMenuPrimitive class=class>\n {children()}\n </MenubarMenuPrimitive>\n }\n}\n\n#[component]\npub fn MenubarTrigger(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <MenubarTriggerPrimitive class=class>\n {children()}\n </MenubarTriggerPrimitive>\n }\n}\n\n#[component]\npub fn MenubarContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <MenubarContentPrimitive class=class>\n {children()}\n </MenubarContentPrimitive>\n }\n}\n\n#[component]\npub fn MenubarItem(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <MenubarItemPrimitive class=class>\n {children()}\n </MenubarItemPrimitive>\n }\n}\n\n#[component]\npub fn MenubarSeparator(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <MenubarSeparatorPrimitive class=class />\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! Menubar Island — bootstrap only, delegates to interaction engine\n\nuse leptos::prelude::*;\nuse super::menubar_ui::{\n Menubar as MenubarUi,\n MenubarMenu as MenubarMenuUi,\n MenubarTrigger as MenubarTriggerUi,\n MenubarContent as MenubarContentUi,\n MenubarItem as MenubarItemUi,\n MenubarSeparator as MenubarSeparatorUi\n};\n\n\n\n#[component]\npub fn Menubar(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! {\n <MenubarUi class=class.unwrap_or_default()>{children()}</MenubarUi>\n }\n}\n\n#[component]\npub fn MenubarMenu(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <MenubarMenuUi class=class.unwrap_or_default()>{children()}</MenubarMenuUi> }\n}\n\n#[component]\npub fn MenubarTrigger(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <MenubarTriggerUi class=class.unwrap_or_default()>{children()}</MenubarTriggerUi> }\n}\n\n#[component]\npub fn MenubarContent(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <MenubarContentUi class=class.unwrap_or_default()>{children()}</MenubarContentUi> }\n}\n\n#[component]\npub fn MenubarItem(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <MenubarItemUi class=class.unwrap_or_default()>{children()}</MenubarItemUi> }\n}\n\n#[component]\npub fn MenubarSeparator(\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <MenubarSeparatorUi class=class.unwrap_or_default() /> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const MENUBAR_API: ComponentApi = ComponentApi {\n id: \"menubar\",\n description: \"Menu bar navigation\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const MENUBARMENU_API: ComponentApi = ComponentApi {\n id: \"menubar-menu\",\n description: \"Menu bar navigation\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const MENUBARTRIGGER_API: ComponentApi = ComponentApi {\n id: \"menubar-trigger\",\n description: \"Menu bar navigation\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const MENUBARCONTENT_API: ComponentApi = ComponentApi {\n id: \"menubar-content\",\n description: \"Menu bar navigation\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const MENUBARITEM_API: ComponentApi = ComponentApi {\n id: \"menubar-item\",\n description: \"Menu bar navigation\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const MENUBARSEPARATOR_API: ComponentApi = ComponentApi {\n id: \"menubar-separator\",\n description: \"Menu bar navigation\",\n props: &[\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::menubar_boundary::{\n Menubar, MenubarMenu, MenubarTrigger,\n MenubarContent, MenubarItem, MenubarSeparator,\n};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn MenubarShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Menubar semantics and structure governed by DOM state — SSR-safe, hydration-safe.\"\n </p>\n <Menubar>\n <MenubarMenu>\n <MenubarTrigger>\"File\"</MenubarTrigger>\n <MenubarContent>\n <MenubarItem>\"New\"</MenubarItem>\n <MenubarItem>\"Open\"</MenubarItem>\n <MenubarSeparator />\n <MenubarItem>\"Exit\"</MenubarItem>\n </MenubarContent>\n </MenubarMenu>\n <MenubarMenu>\n <MenubarTrigger>\"Edit\"</MenubarTrigger>\n <MenubarContent>\n <MenubarItem>\"Cut\"</MenubarItem>\n <MenubarItem>\"Copy\"</MenubarItem>\n <MenubarItem>\"Paste\"</MenubarItem>\n </MenubarContent>\n </MenubarMenu>\n <MenubarMenu>\n <MenubarTrigger>\"View\"</MenubarTrigger>\n <MenubarContent>\n <MenubarItem>\"Zoom in\"</MenubarItem>\n <MenubarItem>\"Zoom out\"</MenubarItem>\n <MenubarItem>\"Full screen\"</MenubarItem>\n </MenubarContent>\n </MenubarMenu>\n <MenubarMenu>\n <MenubarTrigger>\"Help\"</MenubarTrigger>\n <MenubarContent>\n <MenubarItem>\"Documentation\"</MenubarItem>\n <MenubarItem>\"About\"</MenubarItem>\n </MenubarContent>\n </MenubarMenu>\n </Menubar>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"flex"
]
},
{
"id": "modal",
"label": "Modal",
"category": "Overlay",
"description": "Modal window component",
"keywords": "",
"pain": "Modals desync visibility, aria-hidden and focus management",
"promise": "Modal visibility and accessibility fully synchronized via state",
"why": "ModalPrimitive maps VisibilityState to aria-hidden and hidden attributes. Trigger and content share the same state contract. This guarantees consistent open/close behavior and accessibility.\n",
"before": "// ❌ Typical\nview! {\n {if open { view! { <div class=\"modal\">\"Content\"</div> } }}\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Modal state=VisibilityState::Open>\n <ModalContent>\"Content\"</ModalContent>\n </Modal>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"dialogs",
"overlays"
],
"related": [
"dialog",
"alert_dialog",
"drawer",
"sheet",
"confirm_dialog",
"tooltip",
"hover_card",
"popover"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "overlay",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Modal Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::VisibilityState;\n\n\n#[component]\npub fn ModalPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_mo = crate::infra::uid::generate(\"mo\");\n view! {\n <div\n data-rs-modal=\"\"\n data-rs-interaction=\"overlay\"\n data-rs-uid=uid_mo\n data-rs-state=state.as_str()\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ModalTriggerPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(optional, into)] aria_controls: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-modal-trigger=\"\"\n data-rs-button=\"\"\n data-rs-variant=\"primary\"\n data-rs-state=state.as_str()\n aria-haspopup=\"dialog\"\n aria-expanded=state.aria_expanded()\n aria-controls=aria_controls\n class=class\n >\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn ModalOverlayPrimitive(\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-modal-overlay=\"\"\n data-rs-state=state.as_str()\n class=class\n />\n }\n}\n\n#[component]\npub fn ModalContentPrimitive(\n children: Children,\n #[prop(optional, into)] aria_labelledby: Option<String>,\n #[prop(optional, into)] aria_describedby: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-modal-content=\"\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=aria_labelledby\n aria-describedby=aria_describedby\n tabindex=\"-1\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ModalPortalPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <div data-rs-modal-portal=\"\" class=class>{children()}</div> }\n}\n\n#[component]\npub fn ModalTitlePrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <h2 data-rs-modal-title=\"\" class=class>{children()}</h2> }\n}\n\n#[component]\npub fn ModalDescriptionPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <p data-rs-modal-description=\"\" class=class>{children()}</p> }\n}\n\n#[component]\npub fn ModalClosePrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <button type=\"button\" data-rs-modal-close=\"\" data-rs-button=\"\" data-rs-variant=\"ghost\" class=class>\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn ModalFooterPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <div data-rs-modal-footer=\"\" class=class>{children()}</div> }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n ModalPrimitive, ModalTriggerPrimitive, ModalPortalPrimitive,\n ModalOverlayPrimitive, ModalContentPrimitive, ModalTitlePrimitive,\n ModalDescriptionPrimitive, ModalClosePrimitive, ModalFooterPrimitive,\n};\nuse canonrs_core::meta::VisibilityState;\n\n#[component]\npub fn Modal(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <ModalPrimitive state=state class=class>{children()}</ModalPrimitive> }\n}\n\n#[component]\npub fn ModalTrigger(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <ModalTriggerPrimitive class=class>{children()}</ModalTriggerPrimitive> }\n}\n\n#[component]\npub fn ModalPortal(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <ModalPortalPrimitive class=class>{children()}</ModalPortalPrimitive> }\n}\n\n#[component]\npub fn ModalOverlay(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <ModalOverlayPrimitive class=class /> }\n}\n\n#[component]\npub fn ModalContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <ModalContentPrimitive class=class>{children()}</ModalContentPrimitive> }\n}\n\n#[component]\npub fn ModalTitle(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <ModalTitlePrimitive class=class>{children()}</ModalTitlePrimitive> }\n}\n\n#[component]\npub fn ModalDescription(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <ModalDescriptionPrimitive class=class>{children()}</ModalDescriptionPrimitive> }\n}\n\n#[component]\npub fn ModalClose(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <ModalClosePrimitive class=class>{children()}</ModalClosePrimitive> }\n}\n\n#[component]\npub fn ModalFooter(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <ModalFooterPrimitive class=class>{children()}</ModalFooterPrimitive> }\n}\n",
"boundary_src": "//! Modal Island — Canon Rule #340 passthrough\nuse leptos::prelude::*;\nuse super::modal_ui::{\n Modal as ModalUi,\n ModalTrigger as ModalTriggerUi,\n ModalPortal as ModalPortalUi,\n ModalOverlay as ModalOverlayUi,\n ModalContent as ModalContentUi,\n ModalTitle as ModalTitleUi,\n ModalDescription as ModalDescriptionUi,\n ModalClose as ModalCloseUi,\n ModalFooter as ModalFooterUi\n};\n\n#[component]\npub fn Modal(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <ModalUi class=class>{children()}</ModalUi> }\n}\n\n#[component]\npub fn ModalTrigger(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <ModalTriggerUi class=class.unwrap_or_default()>{children()}</ModalTriggerUi> }\n}\n\n#[component]\npub fn ModalPortal(\n children: Children,\n) -> impl IntoView {\n view! { <ModalPortalUi>{children()}</ModalPortalUi> }\n}\n\n#[component]\npub fn ModalOverlay(\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <ModalOverlayUi class=class.unwrap_or_default() /> }\n}\n\n#[component]\npub fn ModalContent(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <ModalContentUi class=class.unwrap_or_default()>{children()}</ModalContentUi> }\n}\n\n#[component]\npub fn ModalTitle(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <ModalTitleUi class=class.unwrap_or_default()>{children()}</ModalTitleUi> }\n}\n\n#[component]\npub fn ModalDescription(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <ModalDescriptionUi class=class.unwrap_or_default()>{children()}</ModalDescriptionUi> }\n}\n\n#[component]\npub fn ModalClose(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <ModalCloseUi class=class.unwrap_or_default()>{children()}</ModalCloseUi> }\n}\n\n#[component]\npub fn ModalFooter(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n) -> impl IntoView {\n view! { <ModalFooterUi class=class.unwrap_or_default()>{children()}</ModalFooterUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const MODAL_API: ComponentApi = ComponentApi {\n id: \"modal\",\n description: \"Modal window component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const MODALTRIGGER_API: ComponentApi = ComponentApi {\n id: \"modal-trigger\",\n description: \"Modal window component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const MODALPORTAL_API: ComponentApi = ComponentApi {\n id: \"modal-portal\",\n description: \"Modal window component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n ],\n};\n\npub const MODALOVERLAY_API: ComponentApi = ComponentApi {\n id: \"modal-overlay\",\n description: \"Modal window component\",\n props: &[\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const MODALCONTENT_API: ComponentApi = ComponentApi {\n id: \"modal-content\",\n description: \"Modal window component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const MODALTITLE_API: ComponentApi = ComponentApi {\n id: \"modal-title\",\n description: \"Modal window component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const MODALDESCRIPTION_API: ComponentApi = ComponentApi {\n id: \"modal-description\",\n description: \"Modal window component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const MODALCLOSE_API: ComponentApi = ComponentApi {\n id: \"modal-close\",\n description: \"Modal window component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\npub const MODALFOOTER_API: ComponentApi = ComponentApi {\n id: \"modal-footer\",\n description: \"Modal window component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::modal_boundary::{Modal, ModalTrigger, ModalPortal, ModalOverlay, ModalContent, ModalTitle, ModalDescription, ModalClose, ModalFooter};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\nuse crate::ui::button::button_boundary::Button;\nuse canonrs_core::primitives::ButtonVariant;\n\n#[component]\npub fn ModalShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Modal accessibility and lifecycle enforced via primitives.\"\n </p>\n <Modal>\n <ModalTrigger>\"Open Modal\"</ModalTrigger>\n <ModalPortal>\n <ModalOverlay />\n <ModalContent>\n <ModalTitle>\"Confirm action\"</ModalTitle>\n <ModalDescription>\"Are you sure? This action cannot be undone.\"</ModalDescription>\n <ModalFooter>\n <ModalClose>\"Cancel\"</ModalClose>\n <Button variant=ButtonVariant::Primary>\"Confirm\"</Button>\n </ModalFooter>\n </ModalContent>\n </ModalPortal>\n </Modal>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Form dialog\"</span>\n <Modal>\n <ModalTrigger>\"Edit profile\"</ModalTrigger>\n <ModalPortal>\n <ModalOverlay />\n <ModalContent>\n <ModalTitle>\"Edit profile\"</ModalTitle>\n <ModalDescription>\"Update your profile information below.\"</ModalDescription>\n <ModalFooter>\n <ModalClose>\"Cancel\"</ModalClose>\n <Button variant=ButtonVariant::Primary>\"Save changes\"</Button>\n </ModalFooter>\n </ModalContent>\n </ModalPortal>\n </Modal>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"center",
"stack"
]
},
{
"id": "nav_item",
"label": "Nav Item",
"category": "Navigation",
"description": "Single navigation item",
"keywords": "",
"pain": "Navigation links lack active state and accessibility consistency",
"promise": "Active and disabled navigation states enforced structurally",
"why": "NavItemPrimitive encodes ActivityState and DisabledState into data attributes and ARIA. aria-current is derived automatically. This guarantees consistent navigation behavior and accessibility.\n",
"before": "// ❌ Typical\nview! {\n <a class=\"active\" href=\"/\">\"Home\"</a>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <NavItem label=\"Home\" active=true />\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"sidebars",
"menus"
],
"related": [
"navigation_menu",
"sidebar",
"breadcrumb",
"pagination",
"link_group"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "navigation",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! NavItem Primitive - Item atomico de navegacao\n\nuse leptos::prelude::*;\nuse crate::meta::{ActivityState, DisabledState};\n\n#[component]\npub fn NavItemPrimitive(\n children: Children,\n #[prop(into, default = String::new())] href: String,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, optional)] aria_label: Option<String>,\n #[prop(default = ActivityState::Inactive)] active: ActivityState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n) -> impl IntoView {\n let uid_ni = crate::infra::uid::generate(\"ni\");\n let is_active = active == ActivityState::Active;\n view! {\n <a\n data-rs-nav-item=\"\"\n data-rs-uid=uid_ni\n data-rs-interaction=\"init\"\n data-rs-activity=active.as_str()\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n aria-current=if is_active { Some(\"page\") } else { None }\n aria-label=aria_label\n aria-disabled=disabled.aria_disabled()\n href=if disabled.disabled() { \"#\".to_string() } else { href }\n tabindex=if disabled.disabled() { \"-1\" } else { \"0\" }\n class=class\n >\n {children()}\n </a>\n }\n}\n\n#[component]\npub fn NavGroupPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, optional)] aria_label: Option<String>,\n #[prop(into, default = String::from(\"vertical\"))] direction: String,\n) -> impl IntoView {\n let uid_ng = crate::infra::uid::generate(\"ng\");\n view! {\n <nav\n data-rs-nav-group=\"\"\n\n data-rs-uid=uid_ng\n data-rs-interaction=\"init\"\n data-rs-direction=direction\n aria-label=aria_label\n class=class\n >\n {children()}\n </nav>\n }\n}\n\n#[component]\npub fn NavItemIconPrimitive(\n children: Children,\n) -> impl IntoView {\n view! { <span data-rs-nav-item-icon=\"\">{children()}</span> }\n}\n\n#[component]\npub fn NavItemLabelPrimitive(\n children: Children,\n) -> impl IntoView {\n view! { <span data-rs-nav-item-label=\"\">{children()}</span> }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{NavItemPrimitive, NavGroupPrimitive, NavItemIconPrimitive, NavItemLabelPrimitive};\nuse canonrs_core::meta::{ActivityState, DisabledState};\n\n#[component]\npub fn NavItem(#[prop(into)] label: String, #[prop(optional, into)] href: Option<String>, #[prop(default = ActivityState::Inactive)] active: ActivityState, #[prop(default = DisabledState::Enabled)] disabled: DisabledState, #[prop(optional)] icon: Option<Children>, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! {\n <NavItemPrimitive href=href.unwrap_or_default() active=active disabled=disabled class=class>\n {icon.map(|i| view! { <NavItemIconPrimitive>{i()}</NavItemIconPrimitive> })}\n <NavItemLabelPrimitive>{label}</NavItemLabelPrimitive>\n </NavItemPrimitive>\n }\n}\n#[component]\npub fn NavGroup(children: Children, #[prop(into, default = String::new())] class: String, #[prop(into, optional)] aria_label: Option<String>, #[prop(into, default = String::from(\"vertical\"))] direction: String) -> impl IntoView {\n view! { <NavGroupPrimitive class=class aria_label=aria_label.unwrap_or_default() direction=direction>{children()}</NavGroupPrimitive> }\n}\n",
"boundary_src": "//! @canon-level: strict\n//! NavItem Boundary — Canon Rule #340 (zero-logic boundary)\n\nuse leptos::prelude::*;\nuse super::nav_item_ui::{NavItem as NavItemUi, NavGroup as NavGroupUi};\nuse canonrs_core::meta::{ActivityState, DisabledState};\n\n#[component]\npub fn NavItem(\n #[prop(into)] label: String,\n #[prop(into, default = String::new())] href: String,\n #[prop(default = ActivityState::Inactive)] active: ActivityState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <NavItemUi label=label href=href active=active disabled=disabled class=class /> }\n}\n\n#[component]\npub fn NavGroup(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, optional)] aria_label: Option<String>,\n #[prop(into, default = String::from(\"vertical\"))] direction: String,\n) -> impl IntoView {\n view! { <NavGroupUi class=class aria_label=aria_label.unwrap_or_default() direction=direction>{children()}</NavGroupUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const NAVITEM_API: ComponentApi = ComponentApi {\n id: \"nav-item\",\n description: \"Single navigation item\",\n props: &[\n PropDef { name: \"label\", kind: PropType::String, required: true, default: None, description: \"Accessible label text\" },\n PropDef { name: \"href\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Navigation target URL\" },\n PropDef { name: \"active\", kind: PropType::String, required: false, default: Some(\"inactive\"), description: \"Active/selected state\" },\n PropDef { name: \"disabled\", kind: PropType::String, required: false, default: Some(\"enabled\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const NAVGROUP_API: ComponentApi = ComponentApi {\n id: \"nav-group\",\n description: \"Single navigation item\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n PropDef { name: \"aria_label\", kind: PropType::String, required: false, default: None, description: \"Accessible label for screen readers\" },\n PropDef { name: \"direction\", kind: PropType::String, required: false, default: Some(\"vertical\"), description: \"Stack or flex direction\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::nav_item_boundary::{NavItem, NavGroup};\nuse canonrs_core::meta::{ActivityState, DisabledState};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn NavItemShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Active and disabled navigation states enforced structurally.\"\n </p>\n\n // Vertical (sidebar)\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Vertical (sidebar)\"</span>\n <NavGroup>\n <NavItem label=\"Dashboard\" href=\"#\" active=ActivityState::Active />\n <NavItem label=\"Components\" href=\"#\" />\n <NavItem label=\"Tokens\" href=\"#\" />\n <NavItem label=\"Settings\" href=\"#\" />\n </NavGroup>\n </Stack>\n\n // Horizontal (inline nav)\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Horizontal (inline)\"</span>\n <NavGroup direction=\"horizontal\">\n <NavItem label=\"Home\" href=\"#\" active=ActivityState::Active />\n <NavItem label=\"About\" href=\"#\" />\n <NavItem label=\"Contact\" href=\"#\" />\n </NavGroup>\n </Stack>\n\n // States\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"States\"</span>\n <NavGroup>\n <NavItem label=\"Active\" href=\"#\" active=ActivityState::Active />\n <NavItem label=\"Inactive\" href=\"#\" />\n <NavItem label=\"Disabled\" href=\"#\" disabled=DisabledState::Disabled />\n </NavGroup>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"flex"
]
},
{
"id": "navigation_menu",
"label": "Navigation Menu",
"category": "Navigation",
"description": "Navigation menu",
"keywords": "",
"pain": "Nested navigation menus require id wiring and break interaction consistency",
"promise": "Trigger-content relationship enforced without id wiring",
"why": "NavigationMenu uses DOM structure and data-rs-state instead of ids. Trigger and content are linked via closest/sibling logic. This guarantees stable interaction without manual wiring.\n",
"before": "// ❌ Typical\nview! {\n <button id=\"trigger\">\"Menu\"</button>\n <div id=\"content\">\"Items\"</div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <NavigationMenu>\n <NavigationMenuItem>\n <NavigationMenuTrigger>\"Menu\"</NavigationMenuTrigger>\n <NavigationMenuContent>\"Items\"</NavigationMenuContent>\n </NavigationMenuItem>\n </NavigationMenu>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"site navigation",
"dropdown navigation"
],
"related": [
"sidebar",
"nav_item",
"breadcrumb",
"pagination",
"link_group"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "navigation",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! NavigationMenu Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::VisibilityState;\n\n#[component]\npub fn NavigationMenuPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_nm = crate::infra::uid::generate(\"nm\");\n view! {\n <nav\n data-rs-navigation-menu=\"\"\n data-rs-uid=uid_nm\n data-rs-interaction=\"init\"\n class=class\n >\n {children()}\n </nav>\n }\n}\n\n#[component]\npub fn NavigationMenuListPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ul\n data-rs-navigation-menu-list=\"\"\n role=\"menubar\"\n aria-orientation=\"horizontal\"\n class=class\n >\n {children()}\n </ul>\n }\n}\n\n#[component]\npub fn NavigationMenuItemPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let item_uid = crate::infra::uid::generate(\"ni\");\n view! {\n <li data-rs-navigation-menu-item=\"\" data-rs-uid=item_uid class=class>\n {children()}\n </li>\n }\n}\n\n/// Trigger sem id wiring — relação com content via DOM closest/sibling\n#[component]\npub fn NavigationMenuTriggerPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_nt = crate::infra::uid::generate(\"nt\");\n view! {\n <button\n type=\"button\"\n data-rs-navigation-menu-trigger=\"\"\n data-rs-uid=uid_nt\n data-rs-state=state.as_str()\n aria-haspopup=\"menu\"\n aria-expanded=state.aria_expanded()\n class=class\n >\n {children()}\n </button>\n }\n}\n\n/// Content — visibilidade controlada por CSS via data-rs-state no trigger pai\n#[component]\npub fn NavigationMenuContentPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-navigation-menu-content=\"\"\n data-rs-state=state.as_str()\n aria-hidden=state.aria_hidden()\n role=\"menu\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn NavigationMenuLinkPrimitive(\n children: Children,\n #[prop(into, default = String::new())] href: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <a\n data-rs-navigation-menu-link=\"\"\n role=\"menuitem\"\n href=href\n class=class\n >\n {children()}\n </a>\n }\n}\n\n#[component]\npub fn NavigationMenuSubItemPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-navigation-menu-subitem=\"\" class=class>\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::VisibilityState;\nuse canonrs_core::primitives::{\n NavigationMenuPrimitive,\n NavigationMenuListPrimitive,\n NavigationMenuItemPrimitive,\n NavigationMenuTriggerPrimitive,\n NavigationMenuContentPrimitive,\n NavigationMenuLinkPrimitive,\n NavigationMenuSubItemPrimitive,\n};\n\n#[component]\npub fn NavigationMenu(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <NavigationMenuPrimitive class=class>\n {children()}\n </NavigationMenuPrimitive>\n }\n}\n\n#[component]\npub fn NavigationMenuList(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <NavigationMenuListPrimitive class=class>\n {children()}\n </NavigationMenuListPrimitive>\n }\n}\n\n#[component]\npub fn NavigationMenuItem(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <NavigationMenuItemPrimitive class=class>\n {children()}\n </NavigationMenuItemPrimitive>\n }\n}\n\n/// Trigger sem id wiring — behavior JS conecta via DOM closest\n#[component]\npub fn NavigationMenuTrigger(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <NavigationMenuTriggerPrimitive state=VisibilityState::Closed class=class>\n {children()}\n </NavigationMenuTriggerPrimitive>\n }\n}\n\n/// Content sem id wiring — abre via CSS :hover/:focus-within + behavior keyboard\n#[component]\npub fn NavigationMenuContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <NavigationMenuContentPrimitive class=class>\n {children()}\n </NavigationMenuContentPrimitive>\n }\n}\n\n#[component]\npub fn NavigationMenuLink(\n children: Children,\n #[prop(into, default = String::new())] href: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <NavigationMenuLinkPrimitive href=href class=class>\n {children()}\n </NavigationMenuLinkPrimitive>\n }\n}\n\n#[component]\npub fn NavigationMenuSubItem(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <NavigationMenuSubItemPrimitive class=class>\n {children()}\n </NavigationMenuSubItemPrimitive>\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! NavigationMenu Island — Canon Rule #340 (zero-logic boundary)\n\nuse leptos::prelude::*;\nuse super::navigation_menu_ui::{\n NavigationMenu as NavigationMenuUi,\n NavigationMenuItem as NavigationMenuItemUi,\n NavigationMenuTrigger as NavigationMenuTriggerUi,\n NavigationMenuContent as NavigationMenuContentUi,\n NavigationMenuLink as NavigationMenuLinkUi\n};\n\n#[component]\npub fn NavigationMenu(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <NavigationMenuUi class=class>{children()}</NavigationMenuUi> }\n}\n\n#[component]\npub fn NavigationMenuItem(children: Children) -> impl IntoView {\n view! { <NavigationMenuItemUi>{children()}</NavigationMenuItemUi> }\n}\n\n#[component]\npub fn NavigationMenuTrigger(children: Children) -> impl IntoView {\n view! { <NavigationMenuTriggerUi>{children()}</NavigationMenuTriggerUi> }\n}\n\n#[component]\npub fn NavigationMenuContent(children: Children) -> impl IntoView {\n view! { <NavigationMenuContentUi>{children()}</NavigationMenuContentUi> }\n}\n\n#[component]\npub fn NavigationMenuLink(\n children: Children,\n #[prop(into, default = String::new())] href: String,\n) -> impl IntoView {\n view! { <NavigationMenuLinkUi href=href>{children()}</NavigationMenuLinkUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const NAVIGATIONMENU_API: ComponentApi = ComponentApi {\n id: \"navigation-menu\",\n description: \"Navigation menu\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const NAVIGATIONMENUITEM_API: ComponentApi = ComponentApi {\n id: \"navigation-menu-item\",\n description: \"Navigation menu\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n ],\n};\n\npub const NAVIGATIONMENUCONTENT_API: ComponentApi = ComponentApi {\n id: \"navigation-menu-content\",\n description: \"Navigation menu\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::navigation_menu_boundary::{\n NavigationMenu, NavigationMenuItem, NavigationMenuTrigger,\n NavigationMenuContent, NavigationMenuLink,\n};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn NavigationMenuShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Trigger-content relationship governed by DOM — no id wiring needed.\"\n </p>\n <NavigationMenu>\n <NavigationMenuItem>\n <NavigationMenuTrigger>\"Products\"</NavigationMenuTrigger>\n <NavigationMenuContent>\n <NavigationMenuLink href=\"#\">\"CanonRS Core\"</NavigationMenuLink>\n <NavigationMenuLink href=\"#\">\"CanonRS UI\"</NavigationMenuLink>\n <NavigationMenuLink href=\"#\">\"CanonRS Tokens\"</NavigationMenuLink>\n </NavigationMenuContent>\n </NavigationMenuItem>\n <NavigationMenuItem>\n <NavigationMenuTrigger>\"Docs\"</NavigationMenuTrigger>\n <NavigationMenuContent>\n <NavigationMenuLink href=\"#\">\"Getting Started\"</NavigationMenuLink>\n <NavigationMenuLink href=\"#\">\"API Reference\"</NavigationMenuLink>\n </NavigationMenuContent>\n </NavigationMenuItem>\n <NavigationMenuItem>\n <NavigationMenuLink href=\"#\">\"Pricing\"</NavigationMenuLink>\n </NavigationMenuItem>\n <NavigationMenuItem>\n <NavigationMenuLink href=\"#\">\"Blog\"</NavigationMenuLink>\n </NavigationMenuItem>\n </NavigationMenu>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"flex"
]
},
{
"id": "page_header",
"label": "Page Header",
"category": "Display",
"description": "Page header with title and actions",
"keywords": "",
"pain": "Page headers mix title, actions and breadcrumbs inconsistently",
"promise": "Header structure enforced with explicit semantic regions",
"why": "PageHeaderPrimitive defines structured parts like title, description and actions. Each region is explicitly declared. This guarantees consistent layout composition across pages.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"header\">\n <h1>\"Title\"</h1>\n </div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <PageHeader>\n <PageHeaderContent>\n <PageHeaderTitle>\"Title\"</PageHeaderTitle>\n </PageHeaderContent>\n </PageHeader>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"dashboards",
"admin pages"
],
"related": [
"card",
"resizable",
"scroll_area",
"aspect_ratio",
"toolbar",
"separator"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "layout",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! PageHeader Primitive - HTML puro\n\nuse leptos::prelude::*;\n\n#[component]\npub fn PageHeaderPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_ph = crate::infra::uid::generate(\"ph\");\n view! {\n <header\n data-rs-page-header=\"\"\n data-rs-uid=uid_ph\n role=\"banner\"\n class={(!class.is_empty()).then(|| class)}\n >\n {children()}\n </header>\n }\n}\n\n#[component]\npub fn PageHeaderBreadcrumbsPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <nav\n data-rs-page-header-breadcrumbs=\"\"\n aria-label=\"Breadcrumb\"\n class={(!class.is_empty()).then(|| class)}\n >\n {children()}\n </nav>\n }\n}\n\n#[component]\npub fn PageHeaderContentPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-page-header-content=\"\"\n class={(!class.is_empty()).then(|| class)}\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn PageHeaderTitlePrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <h1\n data-rs-page-header-title=\"\"\n class={(!class.is_empty()).then(|| class)}\n >\n {children()}\n </h1>\n }\n}\n\n#[component]\npub fn PageHeaderDescriptionPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <p\n data-rs-page-header-description=\"\"\n class={(!class.is_empty()).then(|| class)}\n >\n {children()}\n </p>\n }\n}\n\n#[component]\npub fn PageHeaderActionsPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-page-header-actions=\"\"\n class={(!class.is_empty()).then(|| class)}\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn PageHeaderTabsPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-page-header-tabs=\"\"\n class={(!class.is_empty()).then(|| class)}\n >\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n PageHeaderPrimitive,\n PageHeaderBreadcrumbsPrimitive,\n PageHeaderContentPrimitive,\n PageHeaderTitlePrimitive,\n PageHeaderDescriptionPrimitive,\n PageHeaderActionsPrimitive,\n PageHeaderTabsPrimitive,\n};\n\n#[component]\npub fn PageHeader(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <PageHeaderPrimitive class=class>\n {children()}\n </PageHeaderPrimitive>\n }\n}\n\n#[component]\npub fn PageHeaderBreadcrumbs(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <PageHeaderBreadcrumbsPrimitive class=class>\n {children()}\n </PageHeaderBreadcrumbsPrimitive>\n }\n}\n\n#[component]\npub fn PageHeaderContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <PageHeaderContentPrimitive class=class>\n {children()}\n </PageHeaderContentPrimitive>\n }\n}\n\n#[component]\npub fn PageHeaderTitle(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <PageHeaderTitlePrimitive class=class>\n {children()}\n </PageHeaderTitlePrimitive>\n }\n}\n\n#[component]\npub fn PageHeaderDescription(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <PageHeaderDescriptionPrimitive class=class>\n {children()}\n </PageHeaderDescriptionPrimitive>\n }\n}\n\n#[component]\npub fn PageHeaderActions(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <PageHeaderActionsPrimitive class=class>\n {children()}\n </PageHeaderActionsPrimitive>\n }\n}\n\n#[component]\npub fn PageHeaderTabs(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <PageHeaderTabsPrimitive class=class>\n {children()}\n </PageHeaderTabsPrimitive>\n }\n}\n\n",
"boundary_src": "use leptos::prelude::*;\nuse super::page_header_ui::{\n PageHeader as PageHeaderUi,\n PageHeaderBreadcrumbs as PageHeaderBreadcrumbsUi,\n PageHeaderContent as PageHeaderContentUi,\n PageHeaderTitle as PageHeaderTitleUi,\n PageHeaderDescription as PageHeaderDescriptionUi,\n PageHeaderActions as PageHeaderActionsUi,\n PageHeaderTabs as PageHeaderTabsUi\n};\n\n#[component]\npub fn PageHeader(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <PageHeaderUi class=class>{children()}</PageHeaderUi> }\n}\n\n#[component]\npub fn PageHeaderBreadcrumbs(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <PageHeaderBreadcrumbsUi class=class>{children()}</PageHeaderBreadcrumbsUi> }\n}\n\n#[component]\npub fn PageHeaderContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <PageHeaderContentUi class=class>{children()}</PageHeaderContentUi> }\n}\n\n#[component]\npub fn PageHeaderTitle(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <PageHeaderTitleUi class=class>{children()}</PageHeaderTitleUi> }\n}\n\n#[component]\npub fn PageHeaderDescription(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <PageHeaderDescriptionUi class=class>{children()}</PageHeaderDescriptionUi> }\n}\n\n#[component]\npub fn PageHeaderActions(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <PageHeaderActionsUi class=class>{children()}</PageHeaderActionsUi> }\n}\n\n#[component]\npub fn PageHeaderTabs(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <PageHeaderTabsUi class=class>{children()}</PageHeaderTabsUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const PAGEHEADER_API: ComponentApi = ComponentApi {\n id: \"page-header\",\n description: \"Page header with title and actions\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const PAGEHEADERBREADCRUMBS_API: ComponentApi = ComponentApi {\n id: \"page-header-breadcrumbs\",\n description: \"Page header with title and actions\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const PAGEHEADERCONTENT_API: ComponentApi = ComponentApi {\n id: \"page-header-content\",\n description: \"Page header with title and actions\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const PAGEHEADERTITLE_API: ComponentApi = ComponentApi {\n id: \"page-header-title\",\n description: \"Page header with title and actions\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const PAGEHEADERDESCRIPTION_API: ComponentApi = ComponentApi {\n id: \"page-header-description\",\n description: \"Page header with title and actions\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const PAGEHEADERACTIONS_API: ComponentApi = ComponentApi {\n id: \"page-header-actions\",\n description: \"Page header with title and actions\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const PAGEHEADERTABS_API: ComponentApi = ComponentApi {\n id: \"page-header-tabs\",\n description: \"Page header with title and actions\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::page_header_boundary::{\n PageHeader, PageHeaderBreadcrumbs, PageHeaderContent,\n PageHeaderTitle, PageHeaderDescription, PageHeaderActions,\n PageHeaderTabs,\n};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn PageHeaderShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Header structure enforced with explicit semantic regions.\"\n </p>\n <PageHeader>\n <PageHeaderBreadcrumbs>\n <span>\"Home\"</span>\n <span>\" / \"</span>\n <span>\"Components\"</span>\n <span>\" / \"</span>\n <span>\"PageHeader\"</span>\n </PageHeaderBreadcrumbs>\n <PageHeaderContent>\n <PageHeaderTitle>\"Page Title\"</PageHeaderTitle>\n <PageHeaderDescription>\"Header structure enforced with explicit semantic regions.\"</PageHeaderDescription>\n </PageHeaderContent>\n <PageHeaderActions>\n <span data-rs-button=\"\">\"Action\"</span>\n </PageHeaderActions>\n </PageHeader>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Variants\"</span>\n <Stack direction=StackDirection::Vertical gap=StackGap::Md>\n <PageHeader>\n <PageHeaderContent>\n <PageHeaderTitle>\"Title only\"</PageHeaderTitle>\n </PageHeaderContent>\n </PageHeader>\n <PageHeader>\n <PageHeaderContent>\n <PageHeaderTitle>\"With description\"</PageHeaderTitle>\n <PageHeaderDescription>\"A short description of this page.\"</PageHeaderDescription>\n </PageHeaderContent>\n </PageHeader>\n <PageHeader>\n <PageHeaderContent>\n <PageHeaderTitle>\"With tabs\"</PageHeaderTitle>\n </PageHeaderContent>\n <PageHeaderTabs>\n <span>\"Overview\"</span>\n <span>\"Settings\"</span>\n <span>\"Members\"</span>\n </PageHeaderTabs>\n </PageHeader>\n </Stack>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"flex",
"stack"
]
},
{
"id": "pagination",
"label": "Pagination",
"category": "Navigation",
"description": "Page navigation control",
"keywords": "",
"pain": "Pagination links lack active state and disabled navigation semantics",
"promise": "Navigation state and accessibility enforced via structured primitives",
"why": "PaginationPrimitive encodes navigation semantics with aria-current and disabled states. Previous and next controls enforce accessibility automatically. This guarantees consistent pagination behavior.\n",
"before": "// ❌ Typical\nview! {\n <a class=\"active\">\"1\"</a>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Pagination>\n <PaginationContent>\n <PaginationLink state=ActivityState::Active href=\"#\">\"1\"</PaginationLink>\n </PaginationContent>\n </Pagination>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"tables",
"lists"
],
"related": [
"navigation_menu",
"sidebar",
"nav_item",
"breadcrumb",
"link_group"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "navigation",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Pagination Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::{ActivityState, DisabledState};\n\n#[component]\npub fn PaginationPrimitive(\n children: Children,\n #[prop(default = 1usize)] current_page: usize,\n #[prop(default = 1usize)] total_pages: usize,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_pg = crate::infra::uid::generate(\"pg\");\n view! {\n <nav\n data-rs-pagination=\"\"\n data-rs-current-page=current_page.to_string()\n data-rs-total-pages=total_pages.to_string()\n data-rs-uid=uid_pg\n data-rs-interaction=\"nav\"\n aria-label=\"Page navigation\"\n class=class\n >\n {children()}\n </nav>\n }\n}\n\n#[component]\npub fn PaginationContentPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ul data-rs-pagination-content=\"\" class=class>\n {children()}\n </ul>\n }\n}\n\n#[component]\npub fn PaginationItemPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <li data-rs-pagination-item=\"\" class=class>\n {children()}\n </li>\n }\n}\n\n#[component]\npub fn PaginationLinkPrimitive(\n children: Children,\n #[prop(into, default = String::new())] href: String,\n #[prop(default = ActivityState::Inactive)] state: ActivityState,\n #[prop(default = 0usize)] page: usize,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let is_active = state == ActivityState::Active;\n view! {\n <a\n data-rs-pagination-link=\"\"\n data-rs-page=page.to_string()\n data-rs-activity=state.as_str()\n aria-current=if is_active { Some(\"page\") } else { None }\n href=href\n class=class\n >\n {children()}\n </a>\n }\n}\n\n#[component]\npub fn PaginationPreviousPrimitive(\n children: Children,\n #[prop(into, default = String::new())] href: String,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <a\n data-rs-pagination-previous=\"\"\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n aria-disabled=disabled.aria_disabled()\n aria-label=\"Go to previous page\"\n href=if disabled.disabled() { \"#\".to_string() } else { href }\n tabindex=if disabled.disabled() { \"-1\" } else { \"0\" }\n class=class\n >\n {children()}\n </a>\n }\n}\n\n#[component]\npub fn PaginationNextPrimitive(\n children: Children,\n #[prop(into, default = String::new())] href: String,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <a\n data-rs-pagination-next=\"\"\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n aria-disabled=disabled.aria_disabled()\n aria-label=\"Go to next page\"\n href=if disabled.disabled() { \"#\".to_string() } else { href }\n tabindex=if disabled.disabled() { \"-1\" } else { \"0\" }\n class=class\n >\n {children()}\n </a>\n }\n}\n\n#[component]\npub fn PaginationEllipsisPrimitive(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <span data-rs-pagination-ellipsis=\"\" aria-hidden=\"true\" class=class>\n \"\\u{2026}\"\n <span class=\"sr-only\">\"More pages\"</span>\n </span>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::meta::{ActivityState, DisabledState};\nuse canonrs_core::primitives::{\n PaginationPrimitive, PaginationContentPrimitive, PaginationItemPrimitive,\n PaginationLinkPrimitive, PaginationPreviousPrimitive, PaginationNextPrimitive,\n PaginationEllipsisPrimitive,\n};\n\n#[component]\npub fn Pagination(\n children: Children,\n #[prop(default = 1usize)] current_page: usize,\n #[prop(default = 1usize)] total_pages: usize,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <PaginationPrimitive current_page=current_page total_pages=total_pages class=class>\n {children()}\n </PaginationPrimitive>\n }\n}\n\n#[component]\npub fn PaginationContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <PaginationContentPrimitive class=class>\n {children()}\n </PaginationContentPrimitive>\n }\n}\n\n#[component]\npub fn PaginationItem(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <PaginationItemPrimitive class=class>\n {children()}\n </PaginationItemPrimitive>\n }\n}\n\n#[component]\npub fn PaginationLink(\n children: Children,\n #[prop(into, default = String::new())] href: String,\n #[prop(default = ActivityState::Inactive)] state: ActivityState,\n #[prop(default = 0usize)] page: usize,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <PaginationLinkPrimitive href=href state=state page=page class=class>\n {children()}\n </PaginationLinkPrimitive>\n }\n}\n\n#[component]\npub fn PaginationPrevious(\n children: Children,\n #[prop(into, default = String::new())] href: String,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <PaginationPreviousPrimitive href=href disabled=disabled class=class>\n {children()}\n </PaginationPreviousPrimitive>\n }\n}\n\n#[component]\npub fn PaginationNext(\n children: Children,\n #[prop(into, default = String::new())] href: String,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <PaginationNextPrimitive href=href disabled=disabled class=class>\n {children()}\n </PaginationNextPrimitive>\n }\n}\n\n#[component]\npub fn PaginationEllipsis(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <PaginationEllipsisPrimitive class=class />\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! Pagination Island — bootstrap only, delegates to interaction engine\n\nuse leptos::prelude::*;\nuse super::pagination_ui::{\n Pagination as PaginationUi,\n PaginationContent as PaginationContentUi,\n PaginationItem as PaginationItemUi,\n PaginationLink as PaginationLinkUi,\n PaginationPrevious as PaginationPreviousUi,\n PaginationNext as PaginationNextUi,\n PaginationEllipsis as PaginationEllipsisUi\n};\nuse canonrs_core::meta::{ActivityState, DisabledState};\n\n\n\n#[component]\npub fn Pagination(\n children: Children,\n #[prop(default = 1usize)] current_page: usize,\n #[prop(default = 1usize)] total_pages: usize,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <PaginationUi current_page=current_page total_pages=total_pages class=class>{children()}</PaginationUi>\n }\n}\n\n#[component]\npub fn PaginationContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <PaginationContentUi class=class>{children()}</PaginationContentUi> }\n}\n\n#[component]\npub fn PaginationItem(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <PaginationItemUi class=class>{children()}</PaginationItemUi> }\n}\n\n#[component]\npub fn PaginationLink(\n children: Children,\n #[prop(into, default = String::new())] href: String,\n #[prop(default = ActivityState::Inactive)] state: ActivityState,\n #[prop(default = 0usize)] page: usize,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <PaginationLinkUi href=href state=state page=page class=class>{children()}</PaginationLinkUi> }\n}\n\n#[component]\npub fn PaginationPrevious(\n children: Children,\n #[prop(into, default = String::new())] href: String,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <PaginationPreviousUi href=href disabled=disabled class=class>{children()}</PaginationPreviousUi> }\n}\n\n#[component]\npub fn PaginationNext(\n children: Children,\n #[prop(into, default = String::new())] href: String,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <PaginationNextUi href=href disabled=disabled class=class>{children()}</PaginationNextUi> }\n}\n\n#[component]\npub fn PaginationEllipsis(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <PaginationEllipsisUi class=class /> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const PAGINATION_API: ComponentApi = ComponentApi {\n id: \"pagination\",\n description: \"Page navigation control\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"current_page\", kind: PropType::Number, required: false, default: Some(\"1usize\"), description: \"Prop value\" },\n PropDef { name: \"total_pages\", kind: PropType::Number, required: false, default: Some(\"1usize\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const PAGINATIONCONTENT_API: ComponentApi = ComponentApi {\n id: \"pagination-content\",\n description: \"Page navigation control\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const PAGINATIONITEM_API: ComponentApi = ComponentApi {\n id: \"pagination-item\",\n description: \"Page navigation control\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const PAGINATIONLINK_API: ComponentApi = ComponentApi {\n id: \"pagination-link\",\n description: \"Page navigation control\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"href\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Navigation target URL\" },\n PropDef { name: \"state\", kind: PropType::String, required: false, default: Some(\"inactive\"), description: \"Loading or visibility state\" },\n PropDef { name: \"page\", kind: PropType::Number, required: false, default: Some(\"0usize\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const PAGINATIONPREVIOUS_API: ComponentApi = ComponentApi {\n id: \"pagination-previous\",\n description: \"Page navigation control\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"href\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Navigation target URL\" },\n PropDef { name: \"disabled\", kind: PropType::String, required: false, default: Some(\"enabled\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const PAGINATIONNEXT_API: ComponentApi = ComponentApi {\n id: \"pagination-next\",\n description: \"Page navigation control\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"href\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Navigation target URL\" },\n PropDef { name: \"disabled\", kind: PropType::String, required: false, default: Some(\"enabled\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const PAGINATIONELLIPSIS_API: ComponentApi = ComponentApi {\n id: \"pagination-ellipsis\",\n description: \"Page navigation control\",\n props: &[\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::pagination_boundary::{\n Pagination, PaginationContent, PaginationItem,\n PaginationLink, PaginationPrevious, PaginationNext, PaginationEllipsis,\n};\nuse canonrs_core::meta::{ActivityState, DisabledState};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn PaginationShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Navigation state and accessibility governed by DOM.\"\n </p>\n <Pagination current_page=3 total_pages=10>\n <PaginationContent>\n <PaginationItem>\n <PaginationPrevious href=\"#\">\"←\"</PaginationPrevious>\n </PaginationItem>\n <PaginationItem><PaginationLink href=\"#\" page=1>\"1\"</PaginationLink></PaginationItem>\n <PaginationItem><PaginationLink href=\"#\" page=2>\"2\"</PaginationLink></PaginationItem>\n <PaginationItem><PaginationLink href=\"#\" page=3 state=ActivityState::Active>\"3\"</PaginationLink></PaginationItem>\n <PaginationItem><PaginationLink href=\"#\" page=4>\"4\"</PaginationLink></PaginationItem>\n <PaginationItem><PaginationEllipsis /></PaginationItem>\n <PaginationItem><PaginationLink href=\"#\" page=10>\"10\"</PaginationLink></PaginationItem>\n <PaginationItem>\n <PaginationNext href=\"#\">\"→\"</PaginationNext>\n </PaginationItem>\n </PaginationContent>\n </Pagination>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"First page (prev disabled)\"</span>\n <Pagination current_page=1 total_pages=5>\n <PaginationContent>\n <PaginationItem>\n <PaginationPrevious href=\"#\" disabled=DisabledState::Disabled>\"←\"</PaginationPrevious>\n </PaginationItem>\n <PaginationItem><PaginationLink href=\"#\" page=1 state=ActivityState::Active>\"1\"</PaginationLink></PaginationItem>\n <PaginationItem><PaginationLink href=\"#\" page=2>\"2\"</PaginationLink></PaginationItem>\n <PaginationItem><PaginationLink href=\"#\" page=3>\"3\"</PaginationLink></PaginationItem>\n <PaginationItem>\n <PaginationNext href=\"#\">\"→\"</PaginationNext>\n </PaginationItem>\n </PaginationContent>\n </Pagination>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"flex",
"stack"
]
},
{
"id": "popover",
"label": "Popover",
"category": "Overlay",
"description": "Floating popover component",
"keywords": "",
"pain": "Popovers lose sync between trigger state and content visibility",
"promise": "Trigger and content visibility governed by shared state contract",
"why": "PopoverPrimitive uses VisibilityState across trigger and content. ARIA attributes and positioning are derived automatically. This guarantees consistent overlay behavior without manual sync.\n",
"before": "// ❌ Typical\nview! {\n <button on:click=toggle>\"Open\"</button>\n {if open { view! { <div>\"Popover\"</div> } }}\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Popover>\n <button data-rs-popover-trigger=\"\">\"Open\"</button>\n <PopoverContent>\"Popover\"</PopoverContent>\n </Popover>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"context actions",
"tooltips with actions"
],
"related": [
"dialog",
"alert_dialog",
"drawer",
"sheet",
"modal",
"confirm_dialog",
"tooltip",
"hover_card"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "overlay",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Popover Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::VisibilityState;\n\n#[derive(Debug, Clone, Copy, PartialEq, Default)]\npub enum PopoverSide {\n #[default]\n Bottom,\n Top,\n Left,\n Right,\n}\n\nimpl PopoverSide {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Bottom => \"bottom\",\n Self::Top => \"top\",\n Self::Left => \"left\",\n Self::Right => \"right\",\n }\n }\n}\n\n\n#[component]\npub fn PopoverPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] name: String,\n) -> impl IntoView {\n let uid_pop = crate::infra::uid::generate(\"pop\");\n view! {\n <div\n data-rs-popover=\"\"\n data-rs-uid=uid_pop\n data-rs-interaction=\"overlay\"\n data-rs-state=state.as_str()\n data-rs-name=name\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn PopoverTriggerPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] label: String,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-popover-trigger=\"\"\n data-rs-button=\"\"\n data-rs-variant=\"outline\"\n data-rs-state=state.as_str()\n data-rs-value=value\n data-rs-label=label\n aria-haspopup=\"dialog\"\n aria-expanded=state.aria_expanded()\n class=class\n >\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn PopoverContentPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(default = PopoverSide::Bottom)] side: PopoverSide,\n #[prop(into, optional)] aria_label: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-popover-content=\"\"\n data-rs-state=state.as_str()\n data-rs-side=side.as_str()\n role=\"dialog\"\n aria-modal=\"false\"\n aria-label=aria_label\n tabindex=\"-1\"\n class=class\n >\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{PopoverPrimitive, PopoverTriggerPrimitive, PopoverContentPrimitive};\nuse canonrs_core::primitives::PopoverSide;\nuse canonrs_core::meta::VisibilityState;\n\n#[component]\npub fn Popover(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] name: String,\n) -> impl IntoView {\n view! {\n <PopoverPrimitive state=state class=class name=name>\n {children()}\n </PopoverPrimitive>\n }\n}\n\n#[component]\npub fn PopoverTrigger(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] label: String,\n) -> impl IntoView {\n view! { <PopoverTriggerPrimitive class=class value=value label=label>{children()}</PopoverTriggerPrimitive> }\n}\n\n#[component]\npub fn PopoverContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = PopoverSide::Bottom)] side: PopoverSide,\n) -> impl IntoView {\n view! {\n <PopoverContentPrimitive side=side class=class>\n {children()}\n </PopoverContentPrimitive>\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! Popover Island — bootstrap only, delegates to interaction engine\n\nuse leptos::prelude::*;\nuse super::popover_ui::{\n Popover as PopoverUi,\n PopoverTrigger as PopoverTriggerUi,\n PopoverContent as PopoverContentUi\n};\nuse canonrs_core::meta::VisibilityState;\npub use canonrs_core::primitives::PopoverSide;\n\n\n\n#[component]\npub fn Popover(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n #[prop(into, default = String::new())] name: String,\n) -> impl IntoView {\n view! {\n <PopoverUi state=VisibilityState::Closed class=class.unwrap_or_default() name=name>\n {children()}\n </PopoverUi>\n }\n}\n\n#[component]\npub fn PopoverContent(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n #[prop(default = PopoverSide::Bottom)] side: PopoverSide,\n) -> impl IntoView {\n view! { <PopoverContentUi side=side class=class.unwrap_or_default()>{children()}</PopoverContentUi> }\n}\n\n#[component]\npub fn PopoverTrigger(\n children: Children,\n #[prop(optional, into)] class: Option<String>,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] label: String,\n) -> impl IntoView {\n view! { <PopoverTriggerUi class=class.unwrap_or_default() value=value label=label>{children()}</PopoverTriggerUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{PopoverSide}; \n\npub const POPOVER_API: ComponentApi = ComponentApi {\n id: \"popover\",\n description: \"Floating popover component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n PropDef { name: \"name\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Form field name\" },\n ],\n};\n\npub const POPOVERCONTENT_API: ComponentApi = ComponentApi {\n id: \"popover-content\",\n description: \"Floating popover component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n PropDef { name: \"side\", kind: PropType::Enum(&[\"bottom\", \"top\", \"left\", \"right\"]), required: false, default: Some(\"bottom\"), description: \"Tooltip or popover side\" },\n ],\n};\n\npub const POPOVERTRIGGER_API: ComponentApi = ComponentApi {\n id: \"popover-trigger\",\n description: \"Floating popover component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: None, description: \"Additional CSS class names\" },\n PropDef { name: \"value\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Current value\" },\n PropDef { name: \"label\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Accessible label text\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::popover_boundary::{Popover, PopoverTrigger, PopoverContent, PopoverSide};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn PopoverShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Popover with keyboard and click-outside dismiss.\"\n </p>\n <Popover>\n <PopoverTrigger>\"Open Popover\"</PopoverTrigger>\n <PopoverContent>\n <p>\"This is a popover. Click outside to close.\"</p>\n </PopoverContent>\n </Popover>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Sides\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Sm>\n <Popover>\n <PopoverTrigger>\"Bottom\"</PopoverTrigger>\n <PopoverContent side=PopoverSide::Bottom>\"Opens below\"</PopoverContent>\n </Popover>\n <Popover>\n <PopoverTrigger>\"Top\"</PopoverTrigger>\n <PopoverContent side=PopoverSide::Top>\"Opens above\"</PopoverContent>\n </Popover>\n <Popover>\n <PopoverTrigger>\"Right\"</PopoverTrigger>\n <PopoverContent side=PopoverSide::Right>\"Opens right\"</PopoverContent>\n </Popover>\n </Stack>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "progress",
"label": "Progress",
"category": "Feedback",
"description": "Progress bar indicator",
"keywords": "",
"pain": "Progress bars overflow or misreport values beyond valid range",
"promise": "Progress value always clamped and ARIA-compliant",
"why": "ProgressPrimitive clamps value between 0–100 and maps it to aria-valuenow. Indicator movement is derived from this value. This guarantees consistent visual and accessibility behavior.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"progress\" style=\"width:150%\"></div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Progress value=75.0 />\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"file upload",
"task completion"
],
"related": [
"spinner",
"skeleton",
"pulse",
"loading_overlay",
"doc_progress"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "progress",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Progress Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\n\n#[derive(Clone, Copy, PartialEq, Default)]\npub enum ProgressState {\n #[default]\n Default,\n Indeterminate,\n Loading,\n}\n\nimpl ProgressState {\n pub fn as_str(&self) -> Option<&'static str> {\n match self {\n ProgressState::Default => None,\n ProgressState::Indeterminate => Some(\"indeterminate\"),\n ProgressState::Loading => Some(\"loading\"),\n }\n }\n}\n\n#[component]\npub fn ProgressPrimitive(\n children: Children,\n #[prop(default = 0.0)] value: f64,\n #[prop(default = ProgressState::Default)] state: ProgressState,\n #[prop(optional, into)] aria_label: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_pr = crate::infra::uid::generate(\"pr\");\n let clamped = value.clamp(0.0, 100.0);\n let aria_now = if state == ProgressState::Indeterminate { None } else { Some(clamped.to_string()) };\n view! {\n <div\n data-rs-progress=\"\"\n data-rs-uid=uid_pr\n data-rs-interaction=\"init\"\n data-rs-value=clamped.to_string()\n data-rs-loading=state.as_str()\n role=\"progressbar\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n aria-valuenow=aria_now\n aria-label=aria_label\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ProgressIndicatorPrimitive(\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] style: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-progress-indicator=\"\"\n class=class\n style=style\n />\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{ProgressPrimitive, ProgressIndicatorPrimitive};\nuse canonrs_core::primitives::progress::ProgressState;\n\n#[component]\npub fn Progress(\n #[prop(default = 0.0)] value: f64,\n #[prop(default = ProgressState::Default)] state: ProgressState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let clamped = value.clamp(0.0, 100.0);\n let transform = format!(\"transform: translateX(-{:.0}%)\", 100.0 - clamped);\n view! {\n <ProgressPrimitive value=clamped state=state class=class>\n <ProgressIndicatorPrimitive style=transform />\n </ProgressPrimitive>\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! Progress Boundary — Canon Rule #340 (zero-logic boundary)\n\nuse leptos::prelude::*;\nuse super::progress_ui::Progress as ProgressUi;\nuse canonrs_core::primitives::progress::ProgressState;\n\n#[component]\npub fn Progress(\n #[prop(default = 0.0)] value: f64,\n #[prop(default = ProgressState::Default)] state: ProgressState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <ProgressUi value=value state=state class=class /> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{ProgressState}; \n\npub const PROGRESS_API: ComponentApi = ComponentApi {\n id: \"progress\",\n description: \"Progress bar indicator\",\n props: &[\n PropDef { name: \"value\", kind: PropType::Number, required: false, default: Some(\"0.0\"), description: \"Current value\" },\n PropDef { name: \"state\", kind: PropType::Enum(&[\"default\", \"indeterminate\", \"loading\"]), required: false, default: Some(\"default\"), description: \"Loading or visibility state\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::progress_boundary::Progress;\nuse canonrs_core::primitives::progress::ProgressState;\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn ProgressShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Value always clamped between 0-100 and ARIA-compliant.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Values\"</span>\n <Progress value=0.0 />\n <Progress value=25.0 />\n <Progress value=50.0 />\n <Progress value=75.0 />\n <Progress value=100.0 />\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Indeterminate\"</span>\n <Progress state=ProgressState::Indeterminate />\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Loading\"</span>\n <Progress state=ProgressState::Loading />\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "pulse",
"label": "Pulse",
"category": "Feedback",
"description": "Pulse animation wrapper",
"keywords": "",
"pain": "Animations applied inconsistently with arbitrary classes and speeds",
"promise": "Animation variant, size and speed strictly typed",
"why": "PulsePrimitive encodes variant, size and speed as enums mapped to data attributes. This removes class-based inconsistencies. It guarantees predictable animation behavior.\n",
"before": "// ❌ Typical\nview! {\n <span class=\"pulse-fast big\"></span>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Pulse variant=PulseVariant::Emphasized speed=PulseSpeed::Fast />\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"status indicators",
"attention highlights"
],
"related": [
"progress",
"spinner",
"skeleton",
"loading_overlay",
"doc_progress"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "progress",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Pulse Primitive - Motion indicator\n\nuse leptos::prelude::*;\n\n#[derive(Debug, Clone, Copy, PartialEq, Default)]\npub enum PulseVariant {\n #[default]\n Default,\n Subtle,\n Emphasized,\n}\nimpl PulseVariant {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Default => \"default\",\n Self::Subtle => \"subtle\",\n Self::Emphasized => \"emphasized\",\n }\n }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Default)]\npub enum PulseSize {\n Small,\n #[default]\n Medium,\n Large,\n}\nimpl PulseSize {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Small => \"small\",\n Self::Medium => \"medium\",\n Self::Large => \"large\",\n }\n }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Default)]\npub enum PulseSpeed {\n Slow,\n #[default]\n Normal,\n Fast,\n}\nimpl PulseSpeed {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Slow => \"slow\",\n Self::Normal => \"normal\",\n Self::Fast => \"fast\",\n }\n }\n}\n\n#[component]\npub fn PulsePrimitive(\n #[prop(optional)] children: Option<Children>,\n #[prop(default = PulseVariant::Default)] variant: PulseVariant,\n #[prop(default = PulseSize::Medium)] size: PulseSize,\n #[prop(default = PulseSpeed::Normal)] speed: PulseSpeed,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_pls = crate::infra::uid::generate(\"pls\");\n view! {\n <span\n data-rs-pulse=\"\"\n data-rs-uid=uid_pls\n data-rs-variant=variant.as_str()\n data-rs-size=size.as_str()\n data-rs-speed=speed.as_str()\n aria-hidden=\"true\"\n class=class\n >\n {children.map(|c| c())}\n </span>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::PulsePrimitive;\npub use canonrs_core::primitives::{PulseVariant, PulseSize, PulseSpeed};\n\n#[component]\npub fn Pulse(\n children: Children,\n #[prop(default = PulseVariant::Default)] variant: PulseVariant,\n #[prop(default = PulseSize::Medium)] size: PulseSize,\n #[prop(default = PulseSpeed::Normal)] speed: PulseSpeed,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <PulsePrimitive\n variant=variant\n size=size\n speed=speed\n class=class\n >\n {children()}\n </PulsePrimitive>\n }\n}\n\n",
"boundary_src": "//! Pulse Island — Canon Rule #340\n//! Passthrough only. Zero logic, zero transformation.\n\nuse leptos::prelude::*;\npub use super::pulse_ui::{PulseVariant, PulseSize, PulseSpeed};\nuse super::pulse_ui::Pulse as PulseUi;\n\n#[component]\npub fn Pulse(\n #[prop(default = PulseVariant::Default)] variant: PulseVariant,\n #[prop(default = PulseSize::Medium)] size: PulseSize,\n #[prop(default = PulseSpeed::Normal)] speed: PulseSpeed,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <PulseUi variant=variant size=size speed=speed class=class>{\"\"}</PulseUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const PULSE_API: ComponentApi = ComponentApi {\n id: \"pulse\",\n description: \"Pulse animation wrapper\",\n props: &[\n PropDef { name: \"variant\", kind: PropType::Enum(&[\"default\", \"subtle\", \"emphasized\"]), required: false, default: Some(\"default\"), description: \"Visual variant of the component\" },\n PropDef { name: \"size\", kind: PropType::Enum(&[\"small\", \"medium\", \"large\"]), required: false, default: Some(\"medium\"), description: \"Size variant of the component\" },\n PropDef { name: \"speed\", kind: PropType::Enum(&[\"slow\", \"normal\", \"fast\"]), required: false, default: Some(\"normal\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::pulse_boundary::{Pulse, PulseVariant, PulseSize, PulseSpeed};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn PulseShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Animation variant, size and speed strictly typed.\"\n </p>\n <Pulse />\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Variants\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Md>\n <Pulse variant=PulseVariant::Subtle />\n <Pulse />\n <Pulse variant=PulseVariant::Emphasized />\n </Stack>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Sizes\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Md>\n <Pulse size=PulseSize::Small />\n <Pulse size=PulseSize::Medium />\n <Pulse size=PulseSize::Large />\n </Stack>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Speed\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Md>\n <Pulse speed=PulseSpeed::Slow />\n <Pulse speed=PulseSpeed::Normal />\n <Pulse speed=PulseSpeed::Fast />\n </Stack>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "radio",
"label": "Radio",
"category": "Form",
"description": "Radio button input",
"keywords": "",
"pain": "Radio inputs desync checked state and accessibility attributes",
"promise": "Selection state mapped directly to DOM and ARIA",
"why": "RadioPrimitive maps SelectionState to checked and aria attributes. Disabled state is also enforced structurally. This guarantees consistent selection behavior.\n",
"before": "// ❌ Typical\nview! {\n <input type=\"radio\" checked />\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Radio value=\"a\" name=\"group\" checked=true>\"Option\"</Radio>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"forms",
"single choice inputs"
],
"related": [
"select",
"combobox",
"radio_group",
"color_picker",
"slider"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "select",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Radio Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::{SelectionState, DisabledState};\n\n#[component]\npub fn RadioPrimitive(\n children: Children,\n #[prop(default = SelectionState::Unselected)] selected: SelectionState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] name: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_ra = crate::infra::uid::generate(\"ra\");\n let is_selected = selected == SelectionState::Selected;\n let is_disabled = disabled == DisabledState::Disabled;\n\n view! {\n <label\n data-rs-radio=\"\"\n data-rs-uid=uid_ra\n data-rs-interaction=\"init\"\n data-rs-selection=if is_selected { Some(\"selected\") } else { None }\n data-rs-disabled=if is_disabled { Some(\"disabled\") } else { None }\n class=class\n >\n <input\n type=\"radio\"\n data-rs-radio-input=\"\"\n name=name\n value=value\n checked=is_selected\n disabled=is_disabled\n aria-checked=if is_selected { \"true\" } else { \"false\" }\n aria-disabled=if is_disabled { Some(\"true\") } else { None }\n />\n <span data-rs-radio-indicator=\"\" />\n {children()}\n </label>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{RadioPrimitive, RadioGroupPrimitive, RadioGroupItemPrimitive};\nuse canonrs_core::meta::{SelectionState, DisabledState};\n\n#[component]\npub fn Radio(\n children: Children,\n #[prop(default = SelectionState::Unselected)] selected: SelectionState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] name: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <RadioPrimitive\n selected=selected\n disabled=disabled\n value=value\n name=name\n class=class\n >\n {children()}\n </RadioPrimitive>\n }\n}\n\n#[component]\npub fn RadioGroup(\n children: Children,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <RadioGroupPrimitive disabled=disabled class=class>\n {children()}\n </RadioGroupPrimitive>\n }\n}\n\n#[component]\npub fn RadioGroupItem(\n children: Children,\n #[prop(default = SelectionState::Unselected)] selected: SelectionState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] name: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <RadioGroupItemPrimitive\n selected=selected\n disabled=disabled\n value=value\n name=name\n class=class\n >\n {children()}\n </RadioGroupItemPrimitive>\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! Radio Island — bootstrap only, delegates to interaction engine\n\nuse leptos::prelude::*;\nuse super::radio_ui::RadioGroup as RadioGroupUi;\nuse canonrs_core::meta::{\n SelectionState,\n DisabledState\n};\n\n\n\n#[component]\npub fn RadioGroup(\n children: Children,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <RadioGroupUi disabled=disabled class=class>{children()}</RadioGroupUi>\n }\n}\n\n#[component]\npub fn Radio(\n children: Children,\n #[prop(default = SelectionState::Unselected)] selected: SelectionState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] name: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <super::radio_ui::Radio selected=selected disabled=disabled value=value name=name class=class>\n {children()}\n </super::radio_ui::Radio>\n }\n}\n\n#[component]\npub fn RadioGroupItem(\n children: Children,\n #[prop(default = SelectionState::Unselected)] selected: SelectionState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] name: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <super::radio_ui::RadioGroupItem selected=selected disabled=disabled value=value name=name class=class>\n {children()}\n </super::radio_ui::RadioGroupItem>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const RADIOGROUP_API: ComponentApi = ComponentApi {\n id: \"radio-group\",\n description: \"Radio button input\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"disabled\", kind: PropType::String, required: false, default: Some(\"enabled\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const RADIO_API: ComponentApi = ComponentApi {\n id: \"radio\",\n description: \"Radio button input\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"selected\", kind: PropType::String, required: false, default: Some(\"unselected\"), description: \"Prop value\" },\n PropDef { name: \"disabled\", kind: PropType::String, required: false, default: Some(\"enabled\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"value\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Current value\" },\n PropDef { name: \"name\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Form field name\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const RADIOGROUPITEM_API: ComponentApi = ComponentApi {\n id: \"radio-group-item\",\n description: \"Radio button input\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"selected\", kind: PropType::String, required: false, default: Some(\"unselected\"), description: \"Prop value\" },\n PropDef { name: \"disabled\", kind: PropType::String, required: false, default: Some(\"enabled\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"value\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Current value\" },\n PropDef { name: \"name\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Form field name\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::radio_boundary::Radio;\nuse canonrs_core::meta::{SelectionState, DisabledState};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn RadioShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Selection state mapped directly to DOM and ARIA.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <Radio value=\"leptos\" name=\"framework\">\"Leptos\"</Radio>\n <Radio value=\"dioxus\" name=\"framework\" selected=SelectionState::Selected>\"Dioxus\"</Radio>\n <Radio value=\"yew\" name=\"framework\">\"Yew\"</Radio>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Disabled\"</span>\n <Radio value=\"a\" name=\"disabled-radio\" disabled=DisabledState::Disabled>\"Option A\"</Radio>\n <Radio value=\"b\" name=\"disabled-radio\" disabled=DisabledState::Disabled>\"Option B\"</Radio>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "radio_group",
"label": "Radio Group",
"category": "Form",
"description": "Group of radio buttons",
"keywords": "",
"pain": "Radio groups lose exclusivity and accessibility grouping",
"promise": "Group semantics and exclusivity enforced at container level",
"why": "RadioGroupPrimitive enforces role=\"radiogroup\" and shared disabled state. Items derive selection state consistently. This guarantees exclusive selection behavior.\n",
"before": "// ❌ Typical\nview! {\n <div>\n <input type=\"radio\" name=\"a\" />\n <input type=\"radio\" name=\"a\" />\n </div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <RadioGroup>\n <RadioGroupItem name=\"a\" value=\"1\">\"One\"</RadioGroupItem>\n </RadioGroup>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"settings",
"forms"
],
"related": [
"select",
"combobox",
"radio",
"color_picker",
"slider"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "select",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! RadioGroup Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::{SelectionState, DisabledState};\n\n#[component]\npub fn RadioGroupPrimitive(\n children: Children,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_rg = crate::infra::uid::generate(\"rg\");\n let is_disabled = disabled == DisabledState::Disabled;\n\n view! {\n <div\n data-rs-radio-group=\"\"\n data-rs-uid=uid_rg\n data-rs-interaction=\"selection\"\n data-rs-disabled=if is_disabled { Some(\"disabled\") } else { None }\n role=\"radiogroup\"\n aria-disabled=if is_disabled { Some(\"true\") } else { None }\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn RadioGroupItemPrimitive(\n children: Children,\n #[prop(default = SelectionState::Unselected)] selected: SelectionState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] name: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let is_selected = selected == SelectionState::Selected;\n let is_disabled = disabled == DisabledState::Disabled;\n\n view! {\n <label\n data-rs-radio=\"\"\n data-rs-selection=if is_selected { Some(\"selected\") } else { None }\n data-rs-disabled=if is_disabled { Some(\"disabled\") } else { None }\n class=class\n >\n <input\n type=\"radio\"\n data-rs-radio-input=\"\"\n name=name\n value=value\n checked=is_selected\n disabled=is_disabled\n aria-checked=if is_selected { \"true\" } else { \"false\" }\n aria-disabled=if is_disabled { Some(\"true\") } else { None }\n />\n <span data-rs-radio-indicator=\"\" />\n {children()}\n </label>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{RadioGroupPrimitive, RadioGroupItemPrimitive};\nuse canonrs_core::meta::{SelectionState, DisabledState};\n\n#[component]\npub fn RadioGroup(\n children: Children,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <RadioGroupPrimitive disabled=disabled class=class>\n {children()}\n </RadioGroupPrimitive>\n }\n}\n\n#[component]\npub fn RadioGroupItem(\n children: Children,\n #[prop(into, default = String::new())] name: String,\n #[prop(into, default = String::new())] value: String,\n #[prop(default = SelectionState::Unselected)] selected: SelectionState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <RadioGroupItemPrimitive\n selected=selected\n disabled=disabled\n value=value\n name=name\n class=class\n >\n {children()}\n </RadioGroupItemPrimitive>\n }\n}\n\n",
"boundary_src": "// Re-export from radio island — RadioGroup covers radio_group functionality\npub use crate::ui::radio::{RadioGroup, Radio, RadioGroupItem};\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_ui.rs\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const RADIOGROUP_API: ComponentApi = ComponentApi {\n id: \"radio-group\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"disabled\", kind: PropType::String, required: false, default: Some(\"enabled\"), description: \"\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"\" },\n ],\n};\n\npub const RADIOGROUPITEM_API: ComponentApi = ComponentApi {\n id: \"radio-group-item\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"name\", kind: PropType::String, required: false, default: Some(\"\"), description: \"\" },\n PropDef { name: \"value\", kind: PropType::String, required: false, default: Some(\"\"), description: \"\" },\n PropDef { name: \"selected\", kind: PropType::String, required: false, default: Some(\"unselected\"), description: \"\" },\n PropDef { name: \"disabled\", kind: PropType::String, required: false, default: Some(\"enabled\"), description: \"\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::radio_group_boundary::{RadioGroup, RadioGroupItem};\nuse canonrs_core::meta::{SelectionState, DisabledState};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn RadioGroupShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Selection state mapped directly to DOM and ARIA.\"\n </p>\n <RadioGroup>\n <RadioGroupItem value=\"leptos\" name=\"framework\">\"Leptos\"</RadioGroupItem>\n <RadioGroupItem value=\"dioxus\" name=\"framework\">\"Dioxus\"</RadioGroupItem>\n <RadioGroupItem value=\"yew\" name=\"framework\">\"Yew\"</RadioGroupItem>\n </RadioGroup>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Pre-selected\"</span>\n <RadioGroup>\n <RadioGroupItem value=\"sm\" name=\"size\">\"Small\"</RadioGroupItem>\n <RadioGroupItem value=\"md\" name=\"size\" selected=SelectionState::Selected>\"Medium\"</RadioGroupItem>\n <RadioGroupItem value=\"lg\" name=\"size\">\"Large\"</RadioGroupItem>\n </RadioGroup>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Disabled\"</span>\n <RadioGroup disabled=DisabledState::Disabled>\n <RadioGroupItem value=\"a\" name=\"dis\">\"Option A\"</RadioGroupItem>\n <RadioGroupItem value=\"b\" name=\"dis\">\"Option B\"</RadioGroupItem>\n </RadioGroup>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "resizable",
"label": "Resizable",
"category": "Layout",
"description": "Resizable panel component",
"keywords": "",
"pain": "Resizable panels break layout constraints and overflow limits",
"promise": "Panel sizes constrained and behavior encoded structurally",
"why": "ResizablePrimitive defines orientation and size limits via attributes. Panels and handles follow strict composition. This guarantees controlled resizing behavior.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"split\">\n <div></div>\n <div></div>\n </div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Resizable>\n <ResizablePanel />\n <ResizableHandle />\n </Resizable>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"editors",
"dashboards"
],
"related": [
"card",
"scroll_area",
"aspect_ratio",
"page_header",
"toolbar",
"separator"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "layout",
"primitive_src": "//! @canon-level: strict\n//! Resizable Primitives\nuse leptos::prelude::*;\nuse crate::infra::uid::generate;\n\n#[derive(Clone, Copy, PartialEq, Default, Debug, serde::Serialize, serde::Deserialize)]\npub enum ResizableOrientation { #[default] Horizontal, Vertical }\nimpl ResizableOrientation {\n pub fn as_str(&self) -> &'static str {\n match self { Self::Horizontal => \"horizontal\", Self::Vertical => \"vertical\" }\n }\n}\n\n#[component]\npub fn ResizablePrimitive(\n children: Children,\n #[prop(default = ResizableOrientation::Horizontal)] orientation: ResizableOrientation,\n #[prop(default = 20u32)] min_size: u32,\n #[prop(default = 80u32)] max_size: u32,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid = generate(\"rs\");\n view! {\n <div\n data-rs-resizable=\"\"\n data-rs-uid=uid\n data-rs-interaction=\"gesture\"\n data-rs-orientation=orientation.as_str()\n data-rs-min-size=min_size.to_string()\n data-rs-max-size=max_size.to_string()\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ResizablePanelPrimitive(\n children: Children,\n #[prop(default = 50u32)] default_size: u32,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, optional)] id: Option<String>,\n) -> impl IntoView {\n let uid = generate(\"rsp\");\n view! {\n <div\n data-rs-resizable-panel=\"\"\n data-rs-uid=uid\n data-rs-default-size=default_size.to_string()\n id=id\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ResizableHandlePrimitive(\n #[prop(into, default = String::new())] class: String,\n #[prop(into, optional)] id: Option<String>,\n #[prop(default = false)] disabled: bool,\n) -> impl IntoView {\n let uid = generate(\"rsh\");\n view! {\n <div\n data-rs-resizable-handle=\"\"\n data-rs-uid=uid\n data-rs-disabled=disabled.then_some(\"disabled\")\n aria-disabled=if disabled { Some(\"true\") } else { None }\n id=id\n class=class\n >\n <span data-rs-resizable-handle-icon=\"\" aria-hidden=\"true\" />\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{ResizablePrimitive, ResizablePanelPrimitive, ResizableHandlePrimitive, ResizableOrientation};\n\n#[component]\npub fn Resizable(\n children: Children,\n #[prop(default = ResizableOrientation::Horizontal)] orientation: ResizableOrientation,\n #[prop(default = 20u32)] min_size: u32,\n #[prop(default = 80u32)] max_size: u32,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <ResizablePrimitive orientation=orientation min_size=min_size max_size=max_size class=class>{children()}</ResizablePrimitive> }\n}\n\n#[component]\npub fn ResizablePanel(\n children: Children,\n #[prop(default = 50u32)] default_size: u32,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <ResizablePanelPrimitive default_size=default_size class=class>{children()}</ResizablePanelPrimitive> }\n}\n\n#[component]\npub fn ResizableHandle(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <ResizableHandlePrimitive class=class /> }\n}\n",
"boundary_src": "//! Resizable Island — Canon Rule passthrough\nuse leptos::prelude::*;\nuse super::resizable_ui::{\n Resizable as ResizableUi,\n ResizablePanel as ResizablePanelUi,\n ResizableHandle as ResizableHandleUi\n};\npub use canonrs_core::primitives::ResizableOrientation;\n\n#[component]\npub fn Resizable(\n children: Children,\n #[prop(default = ResizableOrientation::Horizontal)] orientation: ResizableOrientation,\n #[prop(default = 20u32)] min_size: u32,\n #[prop(default = 80u32)] max_size: u32,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ResizableUi orientation=orientation min_size=min_size max_size=max_size class=class>\n {children()}\n </ResizableUi>\n }\n}\n\n#[component]\npub fn ResizablePanel(\n children: Children,\n #[prop(default = 50u32)] default_size: u32,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ResizablePanelUi default_size=default_size class=class>\n {children()}\n </ResizablePanelUi>\n }\n}\n\n#[component]\npub fn ResizableHandle(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <ResizableHandleUi class=class /> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{ResizableOrientation}; \n\npub const RESIZABLE_API: ComponentApi = ComponentApi {\n id: \"resizable\",\n description: \"Resizable panel component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"orientation\", kind: PropType::String, required: false, default: Some(\"horizontal\"), description: \"Horizontal or vertical orientation\" },\n PropDef { name: \"min_size\", kind: PropType::Number, required: false, default: Some(\"20u32\"), description: \"Prop value\" },\n PropDef { name: \"max_size\", kind: PropType::Number, required: false, default: Some(\"80u32\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const RESIZABLEPANEL_API: ComponentApi = ComponentApi {\n id: \"resizable-panel\",\n description: \"Resizable panel component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"default_size\", kind: PropType::Number, required: false, default: Some(\"50u32\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const RESIZABLEHANDLE_API: ComponentApi = ComponentApi {\n id: \"resizable-handle\",\n description: \"Resizable panel component\",\n props: &[\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::resizable_boundary::{Resizable, ResizablePanel, ResizableHandle, ResizableOrientation};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn ResizableShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Panel sizes constrained and behavior encoded structurally.\"\n </p>\n <div style=\"height:200px;width:100%;\">\n <Resizable>\n <ResizablePanel default_size=50u32>\n <div style=\"padding:var(--space-md);height:100%;display:flex;align-items:center;justify-content:center;border-right:1px solid rgba(255,255,255,0.06);overflow:hidden;\">\n \"Left Panel\"\n </div>\n </ResizablePanel>\n <ResizableHandle />\n <ResizablePanel default_size=50u32>\n <div style=\"padding:var(--space-md);height:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;\">\n \"Right Panel\"\n </div>\n </ResizablePanel>\n </Resizable>\n </div>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Vertical\"</span>\n <div style=\"height:200px;width:100%;\">\n <Resizable orientation=ResizableOrientation::Vertical>\n <ResizablePanel default_size=50u32>\n <div style=\"padding:var(--space-md);width:100%;display:flex;align-items:center;justify-content:center;border-bottom:1px solid rgba(255,255,255,0.06);overflow:hidden;\">\n \"Top Panel\"\n </div>\n </ResizablePanel>\n <ResizableHandle />\n <ResizablePanel default_size=50u32>\n <div style=\"padding:var(--space-md);width:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;\">\n \"Bottom Panel\"\n </div>\n </ResizablePanel>\n </Resizable>\n </div>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"flex"
]
},
{
"id": "scroll_area",
"label": "Scroll Area",
"category": "Layout",
"description": "Scrollable area container",
"keywords": "",
"pain": "Custom scroll containers break keyboard navigation and accessibility",
"promise": "Scroll behavior and accessibility enforced via structured container",
"why": "ScrollAreaPrimitive defines viewport, scrollbars and orientation explicitly. ARIA roles and keyboard access are enforced. This guarantees accessible scrolling behavior.\n",
"before": "// ❌ Typical\nview! {\n <div style=\"overflow:auto;height:200px\"></div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <ScrollArea>\n <div>\"Content\"</div>\n </ScrollArea>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"long lists",
"logs"
],
"related": [
"card",
"resizable",
"aspect_ratio",
"page_header",
"toolbar",
"separator"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "layout",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! ScrollArea Primitive - HTML puro\n\nuse leptos::prelude::*;\n\n#[derive(Clone, Copy, Debug, Default, PartialEq)]\npub enum ScrollOrientation {\n #[default]\n Vertical,\n Horizontal,\n Both,\n}\n\nimpl ScrollOrientation {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Vertical => \"vertical\",\n Self::Horizontal => \"horizontal\",\n Self::Both => \"both\",\n }\n }\n}\n\n#[component]\npub fn ScrollAreaPrimitive(\n children: Children,\n #[prop(default = ScrollOrientation::Vertical)] orientation: ScrollOrientation,\n #[prop(default = true)] auto_hide: bool,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] style: String,\n #[prop(into, optional)] viewport_id: Option<String>,\n) -> impl IntoView {\n let uid_sa = crate::infra::uid::generate(\"sa\");\n let show_v = matches!(orientation, ScrollOrientation::Vertical | ScrollOrientation::Both);\n let show_h = matches!(orientation, ScrollOrientation::Horizontal | ScrollOrientation::Both);\n\n view! {\n <div\n data-rs-scroll-area=\"\"\n data-rs-uid=uid_sa\n data-rs-interaction=\"gesture\"\n data-rs-orientation=orientation.as_str()\n data-rs-auto-hide={auto_hide.then_some(\"\")}\n role=\"region\"\n class=class\n style=style\n >\n <div\n data-rs-scroll-viewport=\"\"\n id={viewport_id}\n role=\"presentation\"\n tabindex=\"0\"\n >\n {children()}\n </div>\n\n {show_v.then(|| view! {\n <div data-rs-scrollbar=\"\" data-rs-orientation=\"vertical\" role=\"scrollbar\" aria-orientation=\"vertical\">\n <div data-rs-scroll-track=\"\">\n <div data-rs-scroll-thumb=\"\" data-rs-orientation=\"vertical\" />\n </div>\n </div>\n })}\n\n {show_h.then(|| view! {\n <div data-rs-scrollbar=\"\" data-rs-orientation=\"horizontal\" role=\"scrollbar\" aria-orientation=\"horizontal\">\n <div data-rs-scroll-track=\"\">\n <div data-rs-scroll-thumb=\"\" data-rs-orientation=\"horizontal\" />\n </div>\n </div>\n })}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{ScrollAreaPrimitive, ScrollOrientation};\n\n#[component]\npub fn ScrollArea(\n children: Children,\n #[prop(default = ScrollOrientation::Vertical)] orientation: ScrollOrientation,\n #[prop(default = true)] auto_hide: bool,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] style: String,\n #[prop(into, optional)] viewport_id: Option<String>,\n) -> impl IntoView {\n view! {\n <ScrollAreaPrimitive\n orientation=orientation\n auto_hide=auto_hide\n class=class\n style=style\n viewport_id=viewport_id.unwrap_or_default()\n >\n {children()}\n </ScrollAreaPrimitive>\n }\n}\n\n",
"boundary_src": "//! ScrollArea Island — Canon Rule passthrough\nuse leptos::prelude::*;\nuse super::scroll_area_ui::ScrollArea as ScrollAreaUi;\npub use canonrs_core::primitives::ScrollOrientation;\n\n#[component]\npub fn ScrollArea(\n children: Children,\n #[prop(default = ScrollOrientation::Vertical)] orientation: ScrollOrientation,\n #[prop(default = true)] auto_hide: bool,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] style: String,\n #[prop(into, default = String::new())] viewport_id: String,\n) -> impl IntoView {\n view! {\n <ScrollAreaUi style=style orientation=orientation auto_hide=auto_hide class=class viewport_id=viewport_id>\n {children()}\n </ScrollAreaUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{ScrollOrientation}; \n\npub const SCROLLAREA_API: ComponentApi = ComponentApi {\n id: \"scroll-area\",\n description: \"Scrollable area container\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"orientation\", kind: PropType::Enum(&[\"vertical\", \"horizontal\", \"both\"]), required: false, default: Some(\"vertical\"), description: \"Horizontal or vertical orientation\" },\n PropDef { name: \"auto_hide\", kind: PropType::Bool, required: false, default: Some(\"true\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n PropDef { name: \"style\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Prop value\" },\n PropDef { name: \"viewport_id\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Prop value\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::scroll_area_boundary::{ScrollArea, ScrollOrientation};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn ScrollAreaShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Custom scrollbar with auto-hide and orientation control.\"\n </p>\n <ScrollArea style=\"height:200px;\">\n <div style=\"padding:var(--space-md);display:flex;flex-direction:column;gap:var(--space-xs);\">\n {(1..=20).map(|i| view! {\n <div style=\"padding:var(--space-xs) 0;border-bottom:1px solid rgba(255,255,255,0.05);\">\n {format!(\"Item {:02}\", i)}\n </div>\n }).collect::<Vec<_>>()}\n </div>\n </ScrollArea>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Horizontal\"</span>\n <div style=\"width:100%;overflow:hidden;\">\n <ScrollArea orientation=ScrollOrientation::Horizontal>\n <div style=\"display:flex;gap:var(--space-md);padding:var(--space-sm);width:200px;\">\n {(1..=12).map(|i| view! {\n <div style=\"width:120px;flex-shrink:0;padding:var(--space-sm);border:1px solid rgba(255,255,255,0.08);border-radius:var(--radius-md);text-align:center;\">\n {format!(\"Card {:02}\", i)}\n </div>\n }).collect::<Vec<_>>()}\n </div>\n </ScrollArea>\n </div>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"container",
"stack"
]
},
{
"id": "section_ui",
"label": "Section UI",
"category": "Display",
"description": "Section header typography components",
"keywords": "",
"pain": "",
"promise": "",
"why": "",
"before": "",
"after": "",
"rules": [],
"use_cases": [],
"related": [],
"badges": [],
"pillar": "",
"primitive_src": "",
"ui_src": "",
"boundary_src": "",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const SECTIONHEADER_API: ComponentApi = ComponentApi {\n id: \"section-header\",\n description: \"Section header typography components\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const SECTIONTITLE_API: ComponentApi = ComponentApi {\n id: \"section-title\",\n description: \"Section header typography components\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const SECTIONSUBTITLE_API: ComponentApi = ComponentApi {\n id: \"section-subtitle\",\n description: \"Section header typography components\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const SECTIONBADGE_API: ComponentApi = ComponentApi {\n id: \"section-badge\",\n description: \"Section header typography components\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const SECTIONACTIONS_API: ComponentApi = ComponentApi {\n id: \"section-actions\",\n description: \"Section header typography components\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::section_boundary::{\n SectionHeader, SectionTitle, SectionSubtitle,\n SectionBadge, SectionActions,\n};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn SectionShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Semantic layout components for composing section headers.\"\n </p>\n <SectionHeader>\n <SectionTitle>\"Section Title\"</SectionTitle>\n <SectionSubtitle>\"Section subtitle description.\"</SectionSubtitle>\n </SectionHeader>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Header with badge\"</span>\n <SectionHeader>\n <SectionTitle>\"Users\"</SectionTitle>\n <SectionBadge>\"New\"</SectionBadge>\n </SectionHeader>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Header with actions\"</span>\n <SectionHeader>\n <SectionTitle>\"Reports\"</SectionTitle>\n <SectionSubtitle>\"Monthly overview.\"</SectionSubtitle>\n <SectionActions>\n <span>\"Export\"</span>\n </SectionActions>\n </SectionHeader>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Title only\"</span>\n <SectionTitle>\"Standalone Title\"</SectionTitle>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"container"
]
},
{
"id": "select",
"label": "Select",
"category": "Form",
"description": "Dropdown select input",
"keywords": "",
"pain": "Select components require manual state sync between trigger and options",
"promise": "Selection, visibility and interaction fully governed by structure",
"why": "SelectPrimitive encodes open state, selection and disabled behavior via data attributes. Trigger and content are synchronized without id wiring. This guarantees consistent dropdown behavior.\n",
"before": "// ❌ Typical\nview! {\n <select>\n <option>\"A\"</option>\n </select>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Select>\n <SelectTrigger>\n <SelectValue placeholder=\"Select...\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"a\">\"A\"</SelectItem>\n </SelectContent>\n </Select>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"forms",
"filters"
],
"related": [
"combobox",
"radio",
"radio_group",
"color_picker",
"slider"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "select",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Select Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::{SelectionState, VisibilityState, DisabledState};\n\n#[component]\npub fn SelectPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(optional)] node_ref: Option<NodeRef<leptos::html::Div>>,\n #[prop(into, default = String::new())] name: String,\n) -> impl IntoView {\n let uid_sel = crate::infra::uid::generate(\"sel\");\n let is_disabled = disabled == DisabledState::Disabled;\n let is_open = state == VisibilityState::Open;\n\n view! {\n <div\n data-rs-select=\"\"\n data-rs-uid=uid_sel\n data-rs-interaction=\"selection\"\n data-rs-role=\"root\"\n data-rs-state=if is_open { \"open\" } else { \"closed\" }\n data-rs-disabled=if is_disabled { Some(\"disabled\") } else { None }\n data-rs-name=name\n aria-disabled=if is_disabled { \"true\" } else { \"false\" }\n class=class\n node_ref=node_ref.unwrap_or_default()\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn SelectTriggerPrimitive(\n children: Children,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let is_disabled = disabled == DisabledState::Disabled;\n view! {\n <button\n type=\"button\"\n data-rs-select-trigger=\"\"\n aria-haspopup=\"listbox\"\n aria-expanded=\"false\"\n aria-disabled=if is_disabled { \"true\" } else { \"false\" }\n class=class\n >\n {children()}\n <svg data-rs-select-chevron=\"\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" aria-hidden=\"true\"><path d=\"m6 9 6 6 6-6\"/></svg>\n </button>\n }\n}\n\n#[component]\npub fn SelectValuePrimitive(\n children: Children,\n #[prop(into, default = String::new())] placeholder: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <span\n data-rs-select-value=\"\"\n data-rs-placeholder=placeholder\n class=class\n >\n {children()}\n </span>\n }\n}\n\n#[component]\npub fn SelectContentPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-select-content=\"\"\n role=\"listbox\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn SelectItemPrimitive(\n children: Children,\n #[prop(default = SelectionState::Unselected)] selected: SelectionState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let is_selected = selected == SelectionState::Selected;\n let is_disabled = disabled == DisabledState::Disabled;\n\n view! {\n <div\n data-rs-select-item=\"\"\n data-rs-selection=if is_selected { Some(\"selected\") } else { None }\n data-rs-disabled=if is_disabled { Some(\"disabled\") } else { None }\n data-rs-value=value\n role=\"option\"\n tabindex=\"-1\"\n aria-selected=if is_selected { \"true\" } else { \"false\" }\n aria-disabled=if is_disabled { \"true\" } else { \"false\" }\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn SelectSeparatorPrimitive(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-select-separator=\"\"\n role=\"separator\"\n class=class\n />\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::meta::{SelectionState, DisabledState, VisibilityState};\nuse canonrs_core::primitives::{\n SelectPrimitive, SelectTriggerPrimitive, SelectValuePrimitive,\n SelectContentPrimitive, SelectItemPrimitive, SelectSeparatorPrimitive,\n};\n\n#[component]\npub fn Select(\n children: Children,\n #[prop(optional)] node_ref: Option<NodeRef<leptos::html::Div>>,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <SelectPrimitive state=VisibilityState::Closed disabled=disabled class=class node_ref=node_ref.unwrap_or_default()>\n {children()}\n </SelectPrimitive>\n }\n}\n\n#[component]\npub fn SelectTrigger(\n children: Children,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <SelectTriggerPrimitive disabled=disabled class=class>\n {children()}\n </SelectTriggerPrimitive>\n }\n}\n\n#[component]\npub fn SelectValue(\n children: Children,\n #[prop(into, default = String::new())] placeholder: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <SelectValuePrimitive placeholder=placeholder class=class>\n {children()}\n </SelectValuePrimitive>\n }\n}\n\n#[component]\npub fn SelectContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <SelectContentPrimitive class=class>\n {children()}\n </SelectContentPrimitive>\n }\n}\n\n#[component]\npub fn SelectItem(\n children: Children,\n #[prop(default = SelectionState::Unselected)] selected: SelectionState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <SelectItemPrimitive selected=selected disabled=disabled value=value class=class>\n {children()}\n </SelectItemPrimitive>\n }\n}\n\n#[component]\npub fn SelectSeparator(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <SelectSeparatorPrimitive class=class /> }\n}\n\n",
"boundary_src": "//! Select Island — Canon Rule passthrough\nuse leptos::prelude::*;\n\npub use canonrs_core::meta::{DisabledState, SelectionState};\npub use super::select_ui::{\n SelectTrigger, SelectValue, SelectContent, SelectItem, SelectSeparator,\n};\n\n#[component]\npub fn Select(\n children: Children,\n #[prop(optional)] node_ref: Option<leptos::prelude::NodeRef<leptos::html::Div>>,\n #[prop(default = canonrs_core::meta::DisabledState::Enabled)] disabled: canonrs_core::meta::DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <super::select_ui::Select class=class disabled=disabled node_ref=node_ref.unwrap_or_default()>\n {children()}\n </super::select_ui::Select>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const SELECT_API: ComponentApi = ComponentApi {\n id: \"select\",\n description: \"Dropdown select input\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"node_ref\", kind: PropType::String, required: false, default: None, description: \"DOM node reference\" },\n PropDef { name: \"disabled\", kind: PropType::String, required: false, default: Some(\"enabled\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::select_boundary::{Select, SelectTrigger, SelectValue, SelectContent, SelectItem};\nuse canonrs_core::meta::{DisabledState, SelectionState};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\nuse canonrs_core::primitives::layout::container::{ContainerPrimitive as Container, ContainerSize};\n\n#[component]\npub fn SelectShowcasePreview() -> impl IntoView {\n view! {\n <Container size=ContainerSize::Sm>\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Selection, visibility and interaction fully governed by structure.\"\n </p>\n <Select>\n <SelectTrigger>\n <SelectValue placeholder=\"Choose a framework...\">{\"\"}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"leptos\">\"Leptos\"</SelectItem>\n <SelectItem value=\"dioxus\">\"Dioxus\"</SelectItem>\n <SelectItem value=\"yew\" disabled=DisabledState::Disabled>\"Yew (disabled)\"</SelectItem>\n </SelectContent>\n </Select>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Pre-selected\"</span>\n <Select>\n <SelectTrigger>\n <SelectValue placeholder=\"Select size...\">\"Medium\"</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"sm\">\"Small\"</SelectItem>\n <SelectItem value=\"md\" selected=SelectionState::Selected>\"Medium\"</SelectItem>\n <SelectItem value=\"lg\">\"Large\"</SelectItem>\n </SelectContent>\n </Select>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"With disabled options\"</span>\n <Select>\n <SelectTrigger>\n <SelectValue placeholder=\"Select plan...\">{\"\"}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"free\">\"Free\"</SelectItem>\n <SelectItem value=\"pro\">\"Pro\"</SelectItem>\n <SelectItem value=\"enterprise\" disabled=DisabledState::Disabled>\"Enterprise (disabled)\"</SelectItem>\n <SelectItem value=\"custom\" disabled=DisabledState::Disabled>\"Custom (disabled)\"</SelectItem>\n </SelectContent>\n </Select>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Many options — keyboard nav\"</span>\n <Select>\n <SelectTrigger>\n <SelectValue placeholder=\"Select country...\">{\"\"}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"br\">\"Brazil\"</SelectItem>\n <SelectItem value=\"us\">\"United States\"</SelectItem>\n <SelectItem value=\"de\">\"Germany\"</SelectItem>\n <SelectItem value=\"jp\">\"Japan\"</SelectItem>\n <SelectItem value=\"fr\">\"France\"</SelectItem>\n <SelectItem value=\"uk\">\"United Kingdom\"</SelectItem>\n </SelectContent>\n </Select>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Disabled\"</span>\n <Select disabled=DisabledState::Disabled>\n <SelectTrigger disabled=DisabledState::Disabled>\n <SelectValue placeholder=\"Disabled...\">{\"\"}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"a\">\"Option A\"</SelectItem>\n </SelectContent>\n </Select>\n </Stack>\n </Stack>\n </Container>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "separator",
"label": "Separator",
"category": "Layout",
"description": "Visual divider line",
"keywords": "",
"pain": "Dividers lack semantic meaning and accessibility roles",
"promise": "Separator semantics enforced via orientation and role contract",
"why": "SeparatorPrimitive encodes orientation and decorative behavior. ARIA roles are derived automatically. This guarantees semantic and accessible separators.\n",
"before": "// ❌ Typical\nview! {\n <hr />\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Separator />\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"layout separation",
"menus"
],
"related": [
"card",
"resizable",
"scroll_area",
"aspect_ratio",
"page_header",
"toolbar"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "layout",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Separator Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum SeparatorOrientation {\n #[default]\n Horizontal,\n Vertical,\n}\nimpl SeparatorOrientation {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Horizontal => \"horizontal\",\n Self::Vertical => \"vertical\",\n }\n }\n}\n\n#[component]\npub fn SeparatorPrimitive(\n #[prop(default = SeparatorOrientation::Horizontal)] orientation: SeparatorOrientation,\n #[prop(default = true)] decorative: bool,\n #[prop(optional, into)] aria_label: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_sep = crate::infra::uid::generate(\"sep\");\n let role = if decorative { \"presentation\" } else { \"separator\" };\n let aria_orientation = if !decorative { Some(orientation.as_str()) } else { None };\n let aria_label_val = if !decorative { aria_label } else { None };\n view! {\n <div\n data-rs-separator=\"\"\n data-rs-uid=uid_sep\n data-rs-orientation=orientation.as_str()\n role=role\n aria-orientation=aria_orientation\n aria-label=aria_label_val\n class=class\n />\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::SeparatorPrimitive;\nuse canonrs_core::separator::SeparatorOrientation;\n\n#[component]\npub fn Separator(#[prop(default = SeparatorOrientation::Horizontal)] orientation: SeparatorOrientation, #[prop(default = true)] decorative: bool, #[prop(into, default = String::new())] aria_label: String, #[prop(default = String::new())] class: String, #[prop(into, optional)] id: Option<String>) -> impl IntoView {\n view! { <SeparatorPrimitive orientation=orientation decorative=decorative aria_label=aria_label class=class /> }\n}\n",
"boundary_src": "//! Separator Island — Canon Rule #340\n//! Passthrough only. Zero logic, zero transformation.\n\nuse leptos::prelude::*;\nuse super::separator_ui::Separator as SeparatorUi;\nuse canonrs_core::separator::SeparatorOrientation;\n\n#[component]\npub fn Separator(\n #[prop(default = SeparatorOrientation::Horizontal)] orientation: SeparatorOrientation,\n #[prop(default = true)] decorative: bool,\n #[prop(into, default = String::new())] aria_label: String,\n #[prop(into, default = String::new())] class: String,\n #[prop(optional)] id: Option<String>,\n) -> impl IntoView {\n view! {\n <SeparatorUi\n orientation=orientation\n decorative=decorative\n aria_label=aria_label\n class=class\n id=id.unwrap_or_default()\n />\n};\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const SEPARATOR_API: ComponentApi = ComponentApi {\n id: \"separator\",\n description: \"Visual divider line\",\n props: &[\n PropDef { name: \"orientation\", kind: PropType::Enum(&[\"horizontal\", \"vertical\"]), required: false, default: Some(\"horizontal\"), description: \"Horizontal or vertical orientation\" },\n PropDef { name: \"decorative\", kind: PropType::Bool, required: false, default: Some(\"true\"), description: \"Prop value\" },\n PropDef { name: \"aria_label\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Accessible label for screen readers\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n PropDef { name: \"id\", kind: PropType::String, required: false, default: None, description: \"Element id attribute\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse canonrs_core::separator::SeparatorOrientation;\nuse super::separator_boundary::Separator;\nuse canonrs_core::Orientation;\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\n\n#[component]\npub fn SeparatorShowcasePreview() -> impl IntoView {\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Lg>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Separator semantics enforced via orientation and role contract.\"\n </p>\n <Stack direction=StackDirection::Vertical gap=StackGap::Md>\n <span>\"Above\"</span>\n <Separator />\n <span>\"Below\"</span>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Vertical\"</span>\n <Stack direction=StackDirection::Horizontal gap=StackGap::Md>\n <span>\"Left\"</span>\n <Separator orientation=SeparatorOrientation::Vertical />\n <span>\"Center\"</span>\n <Separator orientation=SeparatorOrientation::Vertical />\n <span>\"Right\"</span>\n </Stack>\n </Stack>\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-preview-label=\"\">\"Semantic\"</span>\n <Stack direction=StackDirection::Vertical gap=StackGap::Md>\n <span>\"Section A\"</span>\n <Separator decorative=false aria_label=\"Between sections\" />\n <span>\"Section B\"</span>\n </Stack>\n </Stack>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "sheet",
"label": "Sheet",
"category": "Overlay",
"description": "Sheet panel overlay",
"keywords": "",
"pain": "Side panels desync visibility, overlay and focus behavior",
"promise": "Sheet visibility and overlay fully governed via shared state",
"why": "SheetPrimitive maps VisibilityState to both panel and overlay. Side positioning is encoded structurally. This guarantees consistent slide-in behavior and accessibility.\n",
"before": "// ❌ Typical\nview! {\n {if open { view! { <div class=\"drawer\">\"Content\"</div> } }}\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Sheet>\n <SheetContent aria_labelledby=\"title\">\"Content\"</SheetContent>\n </Sheet>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"mobile navigation",
"side panels"
],
"related": [
"dialog",
"alert_dialog",
"drawer",
"modal",
"confirm_dialog",
"tooltip",
"hover_card",
"popover"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "overlay",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Sheet Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::VisibilityState;\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum SheetSide {\n #[default]\n Right,\n Left,\n Top,\n Bottom,\n}\nimpl SheetSide {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Right => \"right\",\n Self::Left => \"left\",\n Self::Top => \"top\",\n Self::Bottom => \"bottom\",\n }\n }\n}\n\n\n#[component]\npub fn SheetPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(default = SheetSide::Right)] side: SheetSide,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_sh = crate::infra::uid::generate(\"sh\");\n view! {\n <div\n data-rs-sheet=\"\"\n data-rs-interaction=\"overlay\"\n data-rs-uid=uid_sh\n data-rs-state=state.as_str()\n data-rs-side=side.as_str()\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn SheetTriggerPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(optional, into)] aria_controls: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-sheet-trigger=\"\"\n data-rs-button=\"\"\n data-rs-variant=\"primary\"\n data-rs-state=state.as_str()\n aria-haspopup=\"dialog\"\n aria-expanded=state.aria_expanded()\n aria-controls=aria_controls\n class=class\n >\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn SheetContentPrimitive(\n children: Children,\n #[prop(optional, into)] aria_labelledby: Option<String>,\n #[prop(optional, into)] aria_describedby: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-sheet-content=\"\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=aria_labelledby\n aria-describedby=aria_describedby\n tabindex=\"-1\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn SheetOverlayPrimitive(\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-sheet-overlay=\"\"\n data-rs-state=state.as_str()\n class=class\n />\n }\n}\n\n#[component]\npub fn SheetPortalPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <div data-rs-sheet-portal=\"\" class=class>{children()}</div> }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{SheetPrimitive, SheetTriggerPrimitive, SheetOverlayPrimitive, SheetContentPrimitive, SheetSide};\nuse canonrs_core::primitives::SheetPortalPrimitive;\nuse canonrs_core::meta::VisibilityState;\n\n#[component]\npub fn Sheet(\n children: Children,\n #[prop(default = SheetSide::Right)] side: SheetSide,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <SheetPrimitive side=side state=state class=class>\n {children()}\n </SheetPrimitive>\n }\n}\n\n#[component]\npub fn SheetOverlay(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <SheetOverlayPrimitive class=class /> }\n}\n\n#[component]\npub fn SheetContent(\n children: Children,\n #[prop(into)] aria_labelledby: String,\n #[prop(into, default = String::new())] class: String,\n #[prop(optional, into)] aria_describedby: Option<String>,\n) -> impl IntoView {\n view! {\n <SheetContentPrimitive\n class=class\n aria_labelledby=aria_labelledby\n aria_describedby=aria_describedby.unwrap_or_default()\n >\n {children()}\n </SheetContentPrimitive>\n }\n}\n\n\n#[component]\npub fn SheetTrigger(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <SheetTriggerPrimitive class=class>{children()}</SheetTriggerPrimitive> }\n}\n\n#[component]\npub fn SheetPortal(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <SheetPortalPrimitive class=class>{children()}</SheetPortalPrimitive> }\n}\n",
"boundary_src": "//! @canon-level: strict\n//! Sheet Island — Canon Rule #340 (zero-logic boundary)\n//! CR-342 v3.0.0: interaction delegated to canonrs-interactions-overlay\n\nuse leptos::prelude::*;\nuse super::sheet_ui::{\n Sheet as SheetUi,\n SheetTrigger,\n SheetOverlay as SheetOverlayUi,\n SheetContent as SheetContentUi,\n SheetPortal\n};\npub use canonrs_core::primitives::SheetSide;\nuse canonrs_core::meta::VisibilityState;\n\n#[component]\npub fn Sheet(\n #[prop(optional)] children: Option<Children>,\n #[prop(into, default = String::from(\"Open\"))] trigger_label: String,\n #[prop(into, default = String::from(\"Close\"))] close_label: String,\n #[prop(into, optional)] title: Option<String>,\n #[prop(into, optional)] description: Option<String>,\n #[prop(default = SheetSide::Right)] side: SheetSide,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <SheetUi side=side state=VisibilityState::Closed class=class>\n {if !trigger_label.is_empty() { Some(view! { <SheetTrigger>{trigger_label}</SheetTrigger> }) } else { None }}\n <SheetPortal>\n <SheetOverlayUi />\n <SheetContentUi aria_labelledby=\"sheet-title\">\n <h2 data-rs-sheet-title=\"\">{title.unwrap_or_default()}</h2>\n <p data-rs-sheet-description=\"\">{description.unwrap_or_default()}</p>\n {children.map(|c| c())}\n <button type=\"button\" data-rs-sheet-close=\"\">\n {close_label}\n </button>\n </SheetContentUi>\n </SheetPortal>\n </SheetUi>\n }\n}\n\n#[component]\npub fn SheetOverlay(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <SheetOverlayUi class=class /> }\n}\n\n#[component]\npub fn SheetContent(\n #[prop(optional)] children: Option<Children>,\n #[prop(into)] aria_labelledby: String,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] aria_describedby: String,\n) -> impl IntoView {\n view! {\n <SheetContentUi\n aria_labelledby=aria_labelledby\n aria_describedby=aria_describedby\n class=class\n >\n {children.map(|c| c())}\n </SheetContentUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{SheetSide}; \n\npub const SHEET_API: ComponentApi = ComponentApi {\n id: \"sheet\",\n description: \"Sheet panel overlay\",\n props: &[\n PropDef { name: \"trigger_label\", kind: PropType::String, required: false, default: Some(\"Open\"), description: \"Prop value\" },\n PropDef { name: \"close_label\", kind: PropType::String, required: false, default: Some(\"Close\"), description: \"Prop value\" },\n PropDef { name: \"title\", kind: PropType::String, required: false, default: None, description: \"Title slot or text\" },\n PropDef { name: \"description\", kind: PropType::String, required: false, default: None, description: \"Description slot or text\" },\n PropDef { name: \"side\", kind: PropType::Enum(&[\"right\", \"left\", \"top\", \"bottom\"]), required: false, default: Some(\"right\"), description: \"Tooltip or popover side\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const SHEETOVERLAY_API: ComponentApi = ComponentApi {\n id: \"sheet-overlay\",\n description: \"Sheet panel overlay\",\n props: &[\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const SHEETCONTENT_API: ComponentApi = ComponentApi {\n id: \"sheet-content\",\n description: \"Sheet panel overlay\",\n props: &[\n PropDef { name: \"aria_labelledby\", kind: PropType::String, required: true, default: None, description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n PropDef { name: \"aria_describedby\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Prop value\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::sheet_boundary::Sheet;\n\n#[component]\npub fn SheetShowcasePreview() -> impl IntoView {\n view! {\n <div data-rs-showcase-preview-hero=\"\">\n <div data-rs-showcase-preview-stage=\"\">\n <Sheet\n trigger_label=\"Open Sheet\"\n title=\"Sheet Title\"\n description=\"Sheet slides in from the right. Visibility fully governed via shared state.\"\n close_label=\"Close\"\n />\n </div>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Sheet visibility and overlay fully governed via shared state.\"\n </p>\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"Settings panel\"</span>\n <div data-rs-showcase-preview-row=\"\">\n <Sheet\n trigger_label=\"Open settings\"\n title=\"Settings\"\n description=\"Manage your account settings and preferences.\"\n close_label=\"Close\"\n />\n </div>\n </div>\n </div>\n }\n}\n",
"block": [],
"blocks_primitives": [
"flex"
]
},
{
"id": "sidebar",
"label": "Sidebar",
"category": "Navigation",
"description": "Sidebar navigation component",
"keywords": "",
"pain": "Sidebars lose active state, disabled links and structural consistency",
"promise": "Navigation state and structure enforced at component level",
"why": "SidebarPrimitive encodes visibility, variant and navigation semantics. Menu items derive active and disabled states automatically. This guarantees consistent navigation behavior.\n",
"before": "// ❌ Typical\nview! {\n <aside>\n <a class=\"active\">\"Home\"</a>\n </aside>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Sidebar>\n <SidebarMenu>\n <SidebarMenuItem active=ActivityState::Active href=\"/\">\"Home\"</SidebarMenuItem>\n </SidebarMenu>\n </Sidebar>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"app navigation",
"admin panels"
],
"related": [
"navigation_menu",
"nav_item",
"breadcrumb",
"pagination",
"link_group"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "navigation",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Sidebar Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::{VisibilityState, ActivityState, DisabledState};\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum SidebarVariant {\n #[default]\n Default,\n Rail,\n}\nimpl SidebarVariant {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Default => \"default\",\n Self::Rail => \"rail\",\n }\n }\n}\n\n#[component]\npub fn SidebarPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Open)] state: VisibilityState,\n #[prop(default = SidebarVariant::Default)] variant: SidebarVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_sb = crate::infra::uid::generate(\"sb\");\n let state_str = match state {\n VisibilityState::Open => \"expanded\",\n _ => \"collapsed\",\n };\n view! {\n <aside\n data-rs-sidebar=\"\"\n data-rs-uid=uid_sb\n data-rs-interaction=\"nav\"\n data-rs-state=state_str\n data-rs-variant=variant.as_str()\n aria-hidden=state.aria_hidden()\n role=\"complementary\"\n class=class\n >\n {children()}\n </aside>\n }\n}\n\n#[component]\npub fn SidebarHeaderPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-sidebar-header=\"\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn SidebarContentPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-sidebar-content=\"\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn SidebarFooterPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-sidebar-footer=\"\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn SidebarMenuPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <nav data-rs-sidebar-menu=\"\" role=\"navigation\" class=class>\n {children()}\n </nav>\n }\n}\n\n#[component]\npub fn SidebarMenuItemPrimitive(\n children: Children,\n #[prop(into, default = String::new())] href: String,\n #[prop(default = ActivityState::Inactive)] active: ActivityState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let is_active = active == ActivityState::Active;\n view! {\n <a\n data-rs-sidebar-menu-item=\"\"\n data-rs-activity=active.as_str()\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n href=if disabled.disabled() { \"#\".to_string() } else { href }\n aria-current=if is_active { Some(\"page\") } else { None }\n aria-disabled=disabled.aria_disabled()\n tabindex=if disabled.disabled() { \"-1\" } else { \"0\" }\n class=class\n >\n {children()}\n </a>\n }\n}\n\n#[component]\npub fn SidebarMenuGroupPrimitive(\n children: Children,\n #[prop(into, optional)] label: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-sidebar-menu-group=\"\" role=\"group\" aria-label=label class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn SidebarSeparatorPrimitive(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-sidebar-separator=\"\" role=\"separator\" aria-orientation=\"horizontal\" class=class />\n }\n}\n\n#[component]\npub fn SidebarGroupLabelPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-sidebar-group-label=\"\" role=\"presentation\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn SidebarGroupPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = false)] root: bool,\n #[prop(default = VisibilityState::Open)] state: VisibilityState,\n) -> impl IntoView {\n let state_str = match state {\n VisibilityState::Open => \"expanded\",\n _ => \"collapsed\",\n };\n view! {\n <div\n data-rs-sidebar-group=\"\"\n data-rs-sidebar-group-root=if root { Some(\"true\") } else { None }\n data-rs-state=state_str\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn SidebarGroupTriggerPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <button type=\"button\" data-rs-sidebar-group-toggle=\"\" class=class>\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn SidebarGroupContentPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-sidebar-group-content=\"\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn SidebarTriggerPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] style: String,\n) -> impl IntoView {\n view! {\n <button type=\"button\" data-rs-sidebar-toggle=\"\" aria-expanded=\"false\" class=class style=style>\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn SidebarLabelPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <span data-rs-sidebar-label=\"\" class=class>\n {children()}\n </span>\n }\n}\n\n#[component]\npub fn SidebarIconPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <span data-rs-sidebar-icon=\"\" class=class>\n {children()}\n </span>\n }\n}\n\n#[component]\npub fn SidebarUserPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-sidebar-user=\"\" class=class>\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n SidebarPrimitive, SidebarVariant, SidebarHeaderPrimitive, SidebarContentPrimitive,\n SidebarFooterPrimitive, SidebarMenuPrimitive, SidebarMenuItemPrimitive,\n SidebarMenuGroupPrimitive, SidebarSeparatorPrimitive, SidebarGroupLabelPrimitive,\n SidebarGroupPrimitive, SidebarGroupTriggerPrimitive, SidebarGroupContentPrimitive,\n SidebarLabelPrimitive, SidebarIconPrimitive, SidebarUserPrimitive,\n};\nuse canonrs_core::meta::{VisibilityState, ActivityState, DisabledState};\n\n#[component]\npub fn Sidebar(\n children: Children,\n #[prop(default = VisibilityState::Open)] state: VisibilityState,\n #[prop(default = SidebarVariant::Default)] variant: SidebarVariant,\n #[prop(default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <SidebarPrimitive state=state variant=variant class=class>\n {children()}\n </SidebarPrimitive>\n }\n}\n\n#[component]\npub fn SidebarHeader(\n children: Children,\n #[prop(default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <SidebarHeaderPrimitive class=class>\n {children()}\n </SidebarHeaderPrimitive>\n }\n}\n\n#[component]\npub fn SidebarContent(\n children: Children,\n #[prop(default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <SidebarContentPrimitive class=class>\n {children()}\n </SidebarContentPrimitive>\n }\n}\n\n#[component]\npub fn SidebarFooter(\n children: Children,\n #[prop(default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <SidebarFooterPrimitive class=class>\n {children()}\n </SidebarFooterPrimitive>\n }\n}\n\n#[component]\npub fn SidebarMenu(\n children: Children,\n #[prop(default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <SidebarMenuPrimitive class=class>\n {children()}\n </SidebarMenuPrimitive>\n }\n}\n\n#[component]\npub fn SidebarMenuItem(\n children: Children,\n #[prop(default = String::new())] class: String,\n #[prop(default = String::new())] href: String,\n #[prop(default = ActivityState::Inactive)] active: ActivityState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n) -> impl IntoView {\n view! {\n <SidebarMenuItemPrimitive class=class href=href active=active disabled=disabled>\n {children()}\n </SidebarMenuItemPrimitive>\n }\n}\n\n#[component]\npub fn SidebarMenuGroup(\n children: Children,\n #[prop(default = String::new())] class: String,\n #[prop(into, optional)] label: Option<String>,\n) -> impl IntoView {\n view! {\n <SidebarMenuGroupPrimitive class=class label=label.unwrap_or_default()>\n {children()}\n </SidebarMenuGroupPrimitive>\n }\n}\n\n#[component]\npub fn SidebarSeparator(\n #[prop(default = String::new())] class: String,\n) -> impl IntoView {\n view! { <SidebarSeparatorPrimitive class=class /> }\n}\n\n#[component]\npub fn SidebarGroupLabel(\n children: Children,\n #[prop(default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <SidebarGroupLabelPrimitive class=class>\n {children()}\n </SidebarGroupLabelPrimitive>\n }\n}\n\n#[component]\npub fn SidebarGroup(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = false)] root: bool,\n #[prop(default = false)] hidden: bool,\n #[prop(default = VisibilityState::Open)] state: VisibilityState,\n) -> impl IntoView {\n view! {\n <SidebarGroupPrimitive class=class root=root state=state attr:hidden=hidden.then(|| \"\")>\n {children()}\n </SidebarGroupPrimitive>\n }\n}\n\n#[component]\npub fn SidebarGroupTrigger(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <SidebarGroupTriggerPrimitive class=class>\n {children()}\n </SidebarGroupTriggerPrimitive>\n }\n}\n\n#[component]\npub fn SidebarGroupContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <SidebarGroupContentPrimitive class=class>\n {children()}\n </SidebarGroupContentPrimitive>\n }\n}\n\n#[component]\npub fn SidebarLabel(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <SidebarLabelPrimitive class=class>{children()}</SidebarLabelPrimitive> }\n}\n\n#[component]\npub fn SidebarIcon(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <SidebarIconPrimitive class=class>{children()}</SidebarIconPrimitive> }\n}\n\n#[component]\npub fn SidebarUser(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <SidebarUserPrimitive class=class>{children()}</SidebarUserPrimitive> }\n}\n\n",
"boundary_src": "//! Sidebar Boundary — componentes passthrough + unified boundary\n\nuse leptos::prelude::*;\nuse super::sidebar_ui::{\n SidebarGroup as SidebarGroupUi,\n SidebarGroupTrigger as SidebarGroupTriggerUi,\n SidebarGroupContent as SidebarGroupContentUi,\n Sidebar as SidebarUi,\n SidebarHeader as SidebarHeaderUi,\n SidebarContent as SidebarContentUi,\n SidebarFooter as SidebarFooterUi,\n SidebarMenu as SidebarMenuUi,\n SidebarMenuItem as SidebarMenuItemUi,\n SidebarMenuGroup as SidebarMenuGroupUi,\n SidebarSeparator as SidebarSeparatorUi,\n SidebarGroupLabel as SidebarGroupLabelUi,\n SidebarLabel as SidebarLabelUi,\n SidebarIcon as SidebarIconUi,\n SidebarUser as SidebarUserUi,\n};\npub use canonrs_core::primitives::SidebarTriggerPrimitive as SidebarTrigger;\npub use canonrs_core::primitives::SidebarVariant;\npub use canonrs_core::primitives::BadgeVariant;\npub use canonrs_core::meta::{VisibilityState, ActivityState, DisabledState};\nuse crate::ui::badge::badge_boundary::Badge;\nuse crate::ui::tooltip::tooltip_boundary::{Tooltip, TooltipTrigger, TooltipContent};\nuse crate::ui::command::command_boundary::{Command, CommandItem};\n\npub use super::sidebar_data::{NavBadge, NavItem, NavGroup, SidebarConfig};\n\n// ─── Boundary components ─────────────────────────────────────────────────────\n\n#[component]\npub fn Sidebar(\n children: Children,\n #[prop(default = VisibilityState::Open)] state: VisibilityState,\n #[prop(default = SidebarVariant::Default)] variant: SidebarVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <SidebarUi state=state variant=variant class=class>{children()}</SidebarUi>\n }\n}\n\n#[component]\npub fn SidebarHeader(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <SidebarHeaderUi class=class>{children()}</SidebarHeaderUi> }\n}\n\n#[component]\npub fn SidebarContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <SidebarContentUi class=class>{children()}</SidebarContentUi> }\n}\n\n#[component]\npub fn SidebarFooter(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <SidebarFooterUi class=class>{children()}</SidebarFooterUi> }\n}\n\n#[component]\npub fn SidebarMenu(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <SidebarMenuUi class=class>{children()}</SidebarMenuUi> }\n}\n\n#[component]\npub fn SidebarMenuItem(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] href: String,\n #[prop(default = ActivityState::Inactive)] active: ActivityState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n) -> impl IntoView {\n view! { <SidebarMenuItemUi class=class href=href active=active disabled=disabled>{children()}</SidebarMenuItemUi> }\n}\n\n#[component]\npub fn SidebarMenuGroup(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(optional, into)] label: Option<String>,\n) -> impl IntoView {\n view! { <SidebarMenuGroupUi class=class label=label.unwrap_or_default()>{children()}</SidebarMenuGroupUi> }\n}\n\n#[component]\npub fn SidebarSeparator(\n #[prop(into, default = String::new())] class: String,\n #[prop(default = false)] hidden: bool,\n) -> impl IntoView {\n view! { <SidebarSeparatorUi class=class attr:hidden=hidden.then(|| \"\") /> }\n}\n\n#[component]\npub fn SidebarGroupLabel(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <SidebarGroupLabelUi class=class>{children()}</SidebarGroupLabelUi> }\n}\n\n#[component]\npub fn SidebarGroup(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = false)] root: bool,\n #[prop(default = false)] hidden: bool,\n #[prop(default = VisibilityState::Open)] state: VisibilityState,\n) -> impl IntoView {\n view! { <SidebarGroupUi class=class root=root state=state hidden=hidden>{children()}</SidebarGroupUi> }\n}\n\n#[component]\npub fn SidebarGroupTrigger(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <SidebarGroupTriggerUi class=class>{children()}</SidebarGroupTriggerUi> }\n}\n\n#[component]\npub fn SidebarGroupContent(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <SidebarGroupContentUi class=class>{children()}</SidebarGroupContentUi> }\n}\n\n#[component]\npub fn SidebarLabel(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <SidebarLabelUi class=class>{children()}</SidebarLabelUi> }\n}\n\n#[component]\npub fn SidebarIcon(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <SidebarIconUi class=class>{children()}</SidebarIconUi> }\n}\n\n#[component]\npub fn SidebarUser(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <SidebarUserUi class=class>{children()}</SidebarUserUi> }\n}\n\n\n// ─── Unified Boundary ────────────────────────────────────────────────────────\n\n#[component]\npub fn SidebarUnifiedBoundary(config: SidebarConfig) -> impl IntoView {\n let tooltips = config.tooltips;\n let pinnable = config.pinnable;\n let user_name = config.user_name;\n let user_email = config.user_email;\n let groups = config.groups.clone();\n let search = config.search;\n let state = config.state;\n let variant = config.variant;\n\n let search_items: Vec<(String, String, String)> = groups.iter()\n .flat_map(|g| g.items.iter())\n .map(|item| (item.icon.to_string(), item.label.to_string(), item.href.to_string()))\n .collect();\n\n let has_separator = groups.len() > 1;\n\n view! {\n <Sidebar state=state variant=variant>\n <SidebarHeader>\n <div style=\"display:flex; align-items:center; justify-content:space-between; width:100%;\">\n <SidebarUser>\n <SidebarLabel>{user_name}</SidebarLabel>\n <SidebarLabel>{user_email}</SidebarLabel>\n </SidebarUser>\n <div style=\"display:flex; align-items:center; gap:var(--space-xs);\">\n <SidebarTrigger>\"⇔\"</SidebarTrigger>\n <button\n type=\"button\"\n data-rs-sidebar-pin-toggle=\"\"\n hidden=(!pinnable)\n style=\"padding: var(--space-xs); background: transparent; border: none; cursor: pointer; font-size: var(--font-size-md);\"\n >\n \"📍\"\n </button>\n </div>\n </div>\n </SidebarHeader>\n\n <SidebarContent>\n <div data-rs-sidebar-search=\"\" hidden=(!search)>\n <Command placeholder=\"Search...\".to_string()>\n {search_items.into_iter().map(|(icon, label, href)| {\n let label2 = label.clone();\n view! {\n <CommandItem value=href>\n <SidebarIcon>{icon}</SidebarIcon>\n <SidebarLabel>{label2}</SidebarLabel>\n </CommandItem>\n }\n }).collect::<Vec<_>>()}\n </Command>\n </div>\n <SidebarMenu>\n {groups.into_iter().enumerate().map(|(i, group)| {\n let items = group.items.clone();\n let items2 = items.clone();\n let collapsible = group.collapsible;\n let label = group.label.to_string();\n let label2 = label.clone();\n let icon = group.icon.to_string();\n view! {\n <div>\n <SidebarGroup root=true hidden=(!collapsible)>\n <SidebarGroupTrigger>\n <SidebarIcon>{icon.clone()}</SidebarIcon>\n <SidebarLabel>{label}</SidebarLabel>\n <span data-rs-sidebar-group-chevron=\"\">\"▼\"</span>\n </SidebarGroupTrigger>\n <SidebarGroupContent>\n {render_items(items, tooltips)}\n </SidebarGroupContent>\n </SidebarGroup>\n <div hidden=collapsible>\n <SidebarGroupLabel>{label2}</SidebarGroupLabel>\n {render_items(items2, tooltips)}\n </div>\n <SidebarSeparator hidden=(!has_separator || i >= 1) />\n </div>\n }\n }).collect::<Vec<_>>()}\n </SidebarMenu>\n </SidebarContent>\n\n <SidebarFooter>\n <SidebarLabel>\"© 2026 CanonRS\"</SidebarLabel>\n </SidebarFooter>\n </Sidebar>\n }\n}\n\nfn render_items(items: Vec<NavItem>, tooltips: bool) -> impl IntoView {\n items.into_iter().map(|item| {\n let has_children = !item.children.is_empty();\n let active = if item.active { ActivityState::Active } else { ActivityState::Inactive };\n let children = item.children.clone();\n let badge = item.badge.clone();\n let label = item.label.to_string();\n let label2 = label.clone();\n let label3 = label.clone();\n let label4 = label.clone();\n let icon = item.icon.to_string();\n let icon2 = icon.clone();\n let icon3 = icon.clone();\n let href = item.href.to_string();\n let href2 = href.clone();\n let has_badge = badge.is_some();\n let badge_variant = badge.as_ref().map(|b| b.variant.clone()).unwrap_or_default();\n let badge_variant2 = badge_variant.clone();\n let badge_label = badge.as_ref().map(|b| b.label.to_string()).unwrap_or_default();\n let badge_label2 = badge_label.clone();\n\n view! {\n <div>\n <SidebarGroup state=VisibilityState::Closed hidden=(!has_children)>\n <SidebarGroupTrigger>\n <SidebarIcon>{icon}</SidebarIcon>\n <SidebarLabel>{label}</SidebarLabel>\n <span data-rs-sidebar-group-chevron=\"\">\"▼\"</span>\n </SidebarGroupTrigger>\n <SidebarGroupContent>\n {render_items(children, tooltips)}\n </SidebarGroupContent>\n </SidebarGroup>\n <div hidden=has_children>\n <div hidden=tooltips>\n <SidebarMenuItem href=href.clone() active=active>\n <SidebarIcon>{icon2.clone()}</SidebarIcon>\n <SidebarLabel>{label2.clone()}</SidebarLabel>\n <Badge variant=badge_variant.clone() hidden=(!has_badge)>{badge_label.clone()}</Badge>\n </SidebarMenuItem>\n </div>\n <div hidden=(!tooltips)>\n <Tooltip>\n <TooltipTrigger>\n <SidebarMenuItem href=href2 active=active>\n <SidebarIcon>{icon3}</SidebarIcon>\n <SidebarLabel>{label3}</SidebarLabel>\n <Badge variant=badge_variant2 hidden=(!has_badge)>{badge_label2}</Badge>\n </SidebarMenuItem>\n </TooltipTrigger>\n <TooltipContent>{label4}</TooltipContent>\n </Tooltip>\n </div>\n </div>\n </div>\n }\n }).collect::<Vec<_>>()\n}\n\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{SidebarTriggerPrimitive as SidebarTrigger, SidebarVariant, BadgeVariant}; \n\npub const SIDEBAR_API: ComponentApi = ComponentApi {\n id: \"sidebar\",\n description: \"Sidebar navigation component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"state\", kind: PropType::String, required: false, default: Some(\"open\"), description: \"Loading or visibility state\" },\n PropDef { name: \"variant\", kind: PropType::Enum(&[\"default\", \"rail\"]), required: false, default: Some(\"default\"), description: \"Visual variant of the component\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const SIDEBARHEADER_API: ComponentApi = ComponentApi {\n id: \"sidebar-header\",\n description: \"Sidebar navigation component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const SIDEBARCONTENT_API: ComponentApi = ComponentApi {\n id: \"sidebar-content\",\n description: \"Sidebar navigation component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const SIDEBARFOOTER_API: ComponentApi = ComponentApi {\n id: \"sidebar-footer\",\n description: \"Sidebar navigation component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const SIDEBARMENU_API: ComponentApi = ComponentApi {\n id: \"sidebar-menu\",\n description: \"Sidebar navigation component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const SIDEBARMENUITEM_API: ComponentApi = ComponentApi {\n id: \"sidebar-menu-item\",\n description: \"Sidebar navigation component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n PropDef { name: \"href\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Navigation target URL\" },\n PropDef { name: \"active\", kind: PropType::String, required: false, default: Some(\"inactive\"), description: \"Active/selected state\" },\n PropDef { name: \"disabled\", kind: PropType::String, required: false, default: Some(\"enabled\"), description: \"Whether the component is disabled\" },\n ],\n};\n\npub const SIDEBARMENUGROUP_API: ComponentApi = ComponentApi {\n id: \"sidebar-menu-group\",\n description: \"Sidebar navigation component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n PropDef { name: \"label\", kind: PropType::String, required: false, default: None, description: \"Accessible label text\" },\n ],\n};\n\npub const SIDEBARSEPARATOR_API: ComponentApi = ComponentApi {\n id: \"sidebar-separator\",\n description: \"Sidebar navigation component\",\n props: &[\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n PropDef { name: \"hidden\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n ],\n};\n\npub const SIDEBARGROUPLABEL_API: ComponentApi = ComponentApi {\n id: \"sidebar-group-label\",\n description: \"Sidebar navigation component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const SIDEBARGROUP_API: ComponentApi = ComponentApi {\n id: \"sidebar-group\",\n description: \"Sidebar navigation component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n PropDef { name: \"root\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"hidden\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"state\", kind: PropType::String, required: false, default: Some(\"open\"), description: \"Loading or visibility state\" },\n ],\n};\n\npub const SIDEBARGROUPTRIGGER_API: ComponentApi = ComponentApi {\n id: \"sidebar-group-trigger\",\n description: \"Sidebar navigation component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const SIDEBARGROUPCONTENT_API: ComponentApi = ComponentApi {\n id: \"sidebar-group-content\",\n description: \"Sidebar navigation component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const SIDEBARLABEL_API: ComponentApi = ComponentApi {\n id: \"sidebar-label\",\n description: \"Sidebar navigation component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const SIDEBARICON_API: ComponentApi = ComponentApi {\n id: \"sidebar-icon\",\n description: \"Sidebar navigation component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const SIDEBARUSER_API: ComponentApi = ComponentApi {\n id: \"sidebar-user\",\n description: \"Sidebar navigation component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const SIDEBARUNIFIEDBOUNDARY_API: ComponentApi = ComponentApi {\n id: \"sidebar-unified-boundary\",\n description: \"Sidebar navigation component\",\n props: &[\n PropDef { name: \"config\", kind: PropType::String, required: true, default: None, description: \"Prop value\" },\n ],\n};\n\n",
"preview_src": "//! SidebarPreviewUnified — 10 demos via SidebarConfig\n\nuse leptos::prelude::*;\nuse super::sidebar_boundary::{SidebarConfig, NavGroup, NavItem, SidebarUnifiedBoundary, VisibilityState, SidebarVariant, BadgeVariant};\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\nuse canonrs_core::primitives::layout::grid::{GridPrimitive as Grid, GridCols, GridGap};\n\nfn nav_items_basic() -> Vec<NavGroup> {\n vec![\n NavGroup::new(\"Navigation\", vec![\n NavItem::new(\"📊\", \"Dashboard\", \"/dashboard\").active(),\n NavItem::new(\"📁\", \"Projects\", \"/projects\"),\n NavItem::new(\"✓\", \"Tasks\", \"/tasks\"),\n ]),\n NavGroup::new(\"Settings\", vec![\n NavItem::new(\"👤\", \"Profile\", \"/profile\"),\n NavItem::new(\"⚙\", \"Preferences\", \"/preferences\"),\n ]),\n ]\n}\n\nfn nav_items_with_badges() -> Vec<NavGroup> {\n vec![\n NavGroup::new(\"Navigation\", vec![\n NavItem::new(\"📊\", \"Dashboard\", \"/dashboard\").active(),\n NavItem::new(\"📁\", \"Projects\", \"/projects\").badge(\"12\", BadgeVariant::Primary),\n NavItem::new(\"✓\", \"Tasks\", \"/tasks\").badge(\"5\", BadgeVariant::Destructive),\n NavItem::new(\"💬\", \"Messages\", \"/messages\").badge(\"3\", BadgeVariant::Warning),\n ]),\n NavGroup::new(\"Settings\", vec![\n NavItem::new(\"🔔\", \"Notifications\", \"/notifications\").badge(\"New\", BadgeVariant::Success),\n NavItem::new(\"👤\", \"Profile\", \"/profile\"),\n ]),\n ]\n}\n\nfn nav_items_collapsible() -> Vec<NavGroup> {\n vec![\n NavGroup::new(\"Navigation\", vec![\n NavItem::new(\"📊\", \"Dashboard\", \"/dashboard\").active(),\n NavItem::new(\"📁\", \"Projects\", \"/projects\"),\n NavItem::new(\"✓\", \"Tasks\", \"/tasks\"),\n ]).icon(\"🧭\").collapsible(),\n NavGroup::new(\"Settings\", vec![\n NavItem::new(\"👤\", \"Profile\", \"/profile\"),\n NavItem::new(\"⚙\", \"Preferences\", \"/preferences\"),\n ]).icon(\"⚙\").collapsible(),\n ]\n}\n\nfn nav_items_multi_level() -> Vec<NavGroup> {\n vec![\n NavGroup::new(\"Navigation\", vec![\n NavItem::new(\"📊\", \"Dashboard\", \"/dashboard\").active(),\n NavItem::new(\"📁\", \"Projects\", \"/projects\").children(vec![\n NavItem::new(\"\", \"Frontend\", \"/projects/frontend\").children(vec![\n NavItem::new(\"\", \"Components\", \"/projects/frontend/components\"),\n NavItem::new(\"\", \"Pages\", \"/projects/frontend/pages\"),\n ]),\n NavItem::new(\"\", \"Backend\", \"/projects/backend\").children(vec![\n NavItem::new(\"\", \"API\", \"/projects/backend/api\"),\n NavItem::new(\"\", \"Database\", \"/projects/backend/database\"),\n ]),\n NavItem::new(\"\", \"Documentation\", \"/projects/docs\"),\n ]),\n NavItem::new(\"✓\", \"Tasks\", \"/tasks\"),\n ]),\n ]\n}\n\npub fn config_for(demo: &str) -> SidebarConfig {\n match demo {\n \"simple\" => SidebarConfig { groups: nav_items_basic(), ..Default::default() },\n \"basic\" => SidebarConfig { groups: nav_items_collapsible(), ..Default::default() },\n \"tooltips\" => SidebarConfig { groups: nav_items_basic(), tooltips: true, ..Default::default() },\n \"multilevel\" => SidebarConfig { groups: nav_items_multi_level(), ..Default::default() },\n \"search\" => SidebarConfig { groups: nav_items_basic(), search: true, ..Default::default() },\n \"rail\" => SidebarConfig {\n groups: nav_items_basic(),\n state: VisibilityState::Closed,\n variant: SidebarVariant::Rail,\n tooltips: false,\n ..Default::default()\n },\n \"pinnable\" => SidebarConfig { groups: nav_items_basic(), pinnable: true, ..Default::default() },\n \"badges\" => SidebarConfig { groups: nav_items_with_badges(), ..Default::default() },\n \"groups\" => SidebarConfig { groups: nav_items_collapsible(), ..Default::default() },\n \"responsive\" => SidebarConfig { groups: nav_items_basic(), responsive: true, ..Default::default() },\n _ => SidebarConfig::default(),\n }\n}\n\n#[component]\npub fn SidebarPreviewUnified(#[prop(into)] demo: String) -> impl IntoView {\n let config = config_for(&demo);\n view! { <SidebarUnifiedBoundary config=config /> }\n}\n\n#[component]\npub fn SidebarShowcasePreview() -> impl IntoView {\n let demos: &[(&str, &str)] = &[\n (\"simple\", \"Simple\"),\n (\"basic\", \"Collapsible Groups\"),\n (\"badges\", \"With Badges\"),\n (\"search\", \"With Search\"),\n (\"multilevel\", \"Multi-level\"),\n (\"tooltips\", \"With Tooltips\"),\n (\"rail\", \"Rail Mode\"),\n (\"pinnable\", \"Pinnable\"),\n (\"groups\", \"Groups\"),\n (\"responsive\", \"Responsive\"),\n ];\n\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Xl>\n <Grid cols=GridCols::Two gap=GridGap::Lg>\n {demos.iter().map(|(demo, label)| {\n let config = config_for(demo);\n view! {\n <Stack direction=StackDirection::Vertical gap=StackGap::Sm>\n <span data-rs-showcase-label=\"\">{*label}</span>\n <div data-rs-showcase-preview-stage=\"\" style=\"height: 400px; overflow: hidden; border: 1px solid var(--theme-surface-border); border-radius: var(--radius-lg); position: relative;\">\n <SidebarUnifiedBoundary config=config />\n </div>\n </Stack>\n }\n }).collect::<Vec<_>>()}\n </Grid>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Sidebar — 10 variants: simple, collapsible, badges, search, multilevel, tooltips, rail, pinnable, groups, responsive.\"\n </p>\n </Stack>\n }\n}\n",
"block": [],
"blocks_primitives": [
"flex"
]
},
{
"id": "skeleton",
"label": "Skeleton",
"category": "Feedback",
"description": "Loading skeleton placeholder",
"keywords": "",
"pain": "Loading placeholders inconsistent and lack accessibility semantics",
"promise": "Skeleton state and variant standardized via structure",
"why": "SkeletonPrimitive encodes variant and loading state with aria-busy. This guarantees consistent placeholder rendering and accessibility feedback.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"skeleton\"></div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Skeleton variant=SkeletonVariant::Text />\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"loading states",
"content placeholders"
],
"related": [
"progress",
"spinner",
"pulse",
"loading_overlay",
"doc_progress"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "progress",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Skeleton Primitive - HTML puro\n\nuse leptos::prelude::*;\n\n#[derive(Clone, Copy, PartialEq, Default)]\npub enum SkeletonVariant {\n #[default]\n Rectangle,\n Text,\n Circle,\n}\nimpl SkeletonVariant {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Rectangle => \"rectangle\",\n Self::Text => \"text\",\n Self::Circle => \"circle\",\n }\n }\n}\n\n#[component]\npub fn SkeletonPrimitive(\n children: Children,\n #[prop(default = SkeletonVariant::Rectangle)] variant: SkeletonVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_skl = crate::infra::uid::generate(\"skl\");\n view! {\n <div\n data-rs-skeleton=\"\"\n data-rs-uid=uid_skl\n data-rs-variant=variant.as_str()\n aria-busy=\"true\"\n aria-live=\"polite\"\n class=class\n >\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::SkeletonPrimitive;\npub use canonrs_core::primitives::SkeletonVariant;\n\n#[component]\npub fn Skeleton(\n #[prop(optional)] children: Option<Children>,\n #[prop(default = SkeletonVariant::Rectangle)] variant: SkeletonVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <SkeletonPrimitive variant=variant class=class>\n {children.map(|c| c())}\n </SkeletonPrimitive>\n }\n}\n\n",
"boundary_src": "//! Skeleton Island — Canon Rule #340\n//! Passthrough only. Zero logic, zero transformation.\n\nuse leptos::prelude::*;\nuse super::skeleton_ui::Skeleton as SkeletonUi;\npub use canonrs_core::primitives::SkeletonVariant;\n\n#[component]\npub fn Skeleton(\n #[prop(default = SkeletonVariant::Rectangle)] variant: SkeletonVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <SkeletonUi variant=variant class=class /> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{SkeletonVariant}; \n\npub const SKELETON_API: ComponentApi = ComponentApi {\n id: \"skeleton\",\n description: \"Loading skeleton placeholder\",\n props: &[\n PropDef { name: \"variant\", kind: PropType::Enum(&[\"rectangle\", \"text\", \"circle\"]), required: false, default: Some(\"rectangle\"), description: \"Visual variant of the component\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::skeleton_boundary::{Skeleton, SkeletonVariant};\n\n#[component]\npub fn SkeletonShowcasePreview() -> impl IntoView {\n view! {\n <div data-rs-showcase-preview-hero=\"\">\n <div data-rs-showcase-preview-stage=\"\">\n <div style=\"display:flex;flex-direction:column;gap:var(--space-sm);width:100%;\">\n <Skeleton variant=SkeletonVariant::Rectangle />\n <Skeleton variant=SkeletonVariant::Text />\n <Skeleton variant=SkeletonVariant::Circle />\n </div>\n </div>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Placeholder de carregamento com variantes padronizadas via enum.\"\n </p>\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"Variants\"</span>\n <div data-rs-showcase-preview-row=\"\" style=\"display:flex;flex-direction:column;gap:var(--space-sm);width:100%;\">\n <Skeleton variant=SkeletonVariant::Rectangle />\n <Skeleton variant=SkeletonVariant::Text />\n <Skeleton variant=SkeletonVariant::Circle />\n </div>\n </div>\n </div>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack",
"grid"
]
},
{
"id": "slider",
"label": "Slider",
"category": "Form",
"description": "Range slider input",
"keywords": "",
"pain": "Sliders allow invalid values and break accessibility attributes",
"promise": "Value clamped and ARIA attributes enforced automatically",
"why": "SliderPrimitive clamps value within min/max and maps percent and aria-valuenow. Track and thumb are structurally defined. This guarantees consistent interaction behavior.\n",
"before": "// ❌ Typical\nview! {\n <input type=\"range\" value=\"200\" />\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Slider min=0.0 max=100.0 value=50.0 />\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"volume control",
"range selection"
],
"related": [
"select",
"combobox",
"radio",
"radio_group",
"color_picker"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "select",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Slider Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::DisabledState;\n\n#[component]\npub fn SliderPrimitive(\n children: Children,\n #[prop(default = 0.0)] min: f64,\n #[prop(default = 100.0)] max: f64,\n #[prop(default = 1.0)] step: f64,\n #[prop(default = 0.0)] value: f64,\n #[prop(into, default = \"horizontal\".to_string())] orientation: String,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_sl = crate::infra::uid::generate(\"sl\");\n let safe_value = if value.is_nan() { min } else { value };\n let safe_min = if min.is_nan() { 0.0 } else { min };\n let safe_max = if max.is_nan() || max <= safe_min { safe_min + 100.0 } else { max };\n let clamped_value = safe_value.clamp(safe_min, safe_max);\n let percent = ((clamped_value - safe_min) / (safe_max - safe_min)) * 100.0;\n view! {\n <div\n data-rs-slider=\"\"\n data-rs-uid=uid_sl\n data-rs-interaction=\"gesture\"\n data-rs-orientation=orientation.clone()\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n data-rs-min=safe_min.to_string()\n data-rs-max=safe_max.to_string()\n data-rs-value=clamped_value.to_string()\n data-rs-percent=percent.to_string()\n data-rs-step=step.to_string()\n role=\"slider\"\n aria-valuemin=min.to_string()\n aria-valuemax=max.to_string()\n aria-valuenow=clamped_value.to_string()\n aria-orientation=orientation\n aria-disabled=disabled.aria_disabled()\n tabindex=if disabled == DisabledState::Disabled { \"-1\" } else { \"0\" }\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn SliderTrackPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-slider-track=\"\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn SliderRangePrimitive(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-slider-range=\"\" class=class />\n }\n}\n\n#[component]\npub fn SliderThumbPrimitive(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-slider-thumb=\"\" tabindex=\"0\" aria-label=\"Slider thumb\" class=class />\n }\n}\n\n#[component]\npub fn SliderMarksPrimitive(\n #[prop(default = 0.0)] min: f64,\n #[prop(default = 100.0)] max: f64,\n #[prop(default = 10.0)] step: f64,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let safe_step = if step <= 0.0 { 10.0 } else { step };\n let count = ((max - min) / safe_step).round() as usize + 1;\n let marks: Vec<f64> = (0..count)\n .map(|i| min + i as f64 * safe_step)\n .filter(|v| *v <= max + 1e-9)\n .collect();\n view! {\n <div data-rs-slider-marks=\"\" class=class>\n {marks.into_iter().map(|v| {\n let pct = ((v - min) / (max - min)) * 100.0;\n let style = format!(\"left: {:.4}%\", pct);\n view! { <span data-rs-slider-mark=\"\" style=style /> }\n }).collect::<Vec<_>>()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::Orientation;\nuse canonrs_core::primitives::{\n SliderPrimitive, SliderTrackPrimitive,\n SliderRangePrimitive, SliderThumbPrimitive, SliderMarksPrimitive,\n};\nuse canonrs_core::meta::DisabledState;\n\n#[component]\npub fn Slider(\n #[prop(default = 0.0)] min: f64,\n #[prop(default = 100.0)] max: f64,\n #[prop(default = 1.0)] step: f64,\n #[prop(default = 50.0)] value: f64,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(default = Orientation::Horizontal)] orientation: Orientation,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <SliderPrimitive\n min={min}\n max={max}\n step={step}\n value={value}\n orientation=orientation.as_str().to_string()\n disabled=disabled\n class={class}\n >\n <SliderTrackPrimitive>\n <SliderRangePrimitive />\n <SliderThumbPrimitive />\n </SliderTrackPrimitive>\n </SliderPrimitive>\n }\n}\n\n\n#[component]\npub fn SliderWithMarks(\n #[prop(default = 0.0)] min: f64,\n #[prop(default = 100.0)] max: f64,\n #[prop(default = 10.0)] step: f64,\n #[prop(default = 50.0)] value: f64,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(default = Orientation::Horizontal)] orientation: Orientation,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <SliderPrimitive\n min={min}\n max={max}\n step={step}\n value={value}\n orientation=orientation.as_str().to_string()\n disabled=disabled\n class={class}\n >\n <SliderTrackPrimitive>\n <SliderRangePrimitive />\n <SliderThumbPrimitive />\n </SliderTrackPrimitive>\n <SliderMarksPrimitive min={min} max={max} step={step} />\n </SliderPrimitive>\n }\n}\n",
"boundary_src": "//! Slider Island — Canon Rule passthrough\nuse leptos::prelude::*;\nuse super::slider_ui::{\n Slider as SliderUi,\n SliderWithMarks as SliderWithMarksUi\n};\nuse canonrs_core::meta::DisabledState;\nuse canonrs_core::Orientation;\n\n#[component]\npub fn Slider(\n #[prop(default = 0.0)] min: f64,\n #[prop(default = 100.0)] max: f64,\n #[prop(default = 1.0)] step: f64,\n #[prop(default = 50.0)] value: f64,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(default = Orientation::Horizontal)] orientation: Orientation,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <SliderUi min=min max=max step=step value=value disabled=disabled orientation=orientation class=class />\n }\n}\n\n#[component]\npub fn SliderWithMarks(\n #[prop(default = 0.0)] min: f64,\n #[prop(default = 100.0)] max: f64,\n #[prop(default = 10.0)] step: f64,\n #[prop(default = 50.0)] value: f64,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(default = Orientation::Horizontal)] orientation: Orientation,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <SliderWithMarksUi min=min max=max step=step value=value disabled=disabled orientation=orientation class=class />\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const SLIDER_API: ComponentApi = ComponentApi {\n id: \"slider\",\n description: \"Range slider input\",\n props: &[\n PropDef { name: \"min\", kind: PropType::Number, required: false, default: Some(\"0.0\"), description: \"Prop value\" },\n PropDef { name: \"max\", kind: PropType::Number, required: false, default: Some(\"100.0\"), description: \"Prop value\" },\n PropDef { name: \"step\", kind: PropType::Number, required: false, default: Some(\"1.0\"), description: \"Prop value\" },\n PropDef { name: \"value\", kind: PropType::Number, required: false, default: Some(\"50.0\"), description: \"Current value\" },\n PropDef { name: \"disabled\", kind: PropType::String, required: false, default: Some(\"enabled\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"orientation\", kind: PropType::String, required: false, default: Some(\"horizontal\"), description: \"Horizontal or vertical orientation\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const SLIDERWITHMARKS_API: ComponentApi = ComponentApi {\n id: \"slider-with-marks\",\n description: \"Range slider input\",\n props: &[\n PropDef { name: \"min\", kind: PropType::Number, required: false, default: Some(\"0.0\"), description: \"Prop value\" },\n PropDef { name: \"max\", kind: PropType::Number, required: false, default: Some(\"100.0\"), description: \"Prop value\" },\n PropDef { name: \"step\", kind: PropType::Number, required: false, default: Some(\"10.0\"), description: \"Prop value\" },\n PropDef { name: \"value\", kind: PropType::Number, required: false, default: Some(\"50.0\"), description: \"Current value\" },\n PropDef { name: \"disabled\", kind: PropType::String, required: false, default: Some(\"enabled\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"orientation\", kind: PropType::String, required: false, default: Some(\"horizontal\"), description: \"Horizontal or vertical orientation\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::slider_boundary::{Slider, SliderWithMarks};\nuse canonrs_core::meta::DisabledState;\n\n#[component]\npub fn SliderShowcasePreview() -> impl IntoView {\n view! {\n <div data-rs-showcase-preview-hero=\"\">\n <div data-rs-showcase-preview-stage=\"\">\n <Slider value=50.0 />\n </div>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Drag to set value — interaction-driven, DOM as source of truth.\"\n </p>\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"Steps\"</span>\n <div data-rs-showcase-preview-row=\"\">\n <Slider step=10.0 value=30.0 />\n </div>\n </div>\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"Disabled\"</span>\n <div data-rs-showcase-preview-row=\"\">\n <Slider value=60.0 disabled=DisabledState::Disabled />\n </div>\n </div>\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"With Marks\"</span>\n <div data-rs-showcase-preview-row=\"\">\n <SliderWithMarks step=10.0 value=40.0 />\n </div>\n </div>\n </div>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "spinner",
"label": "Spinner",
"category": "Feedback",
"description": "Loading spinner",
"keywords": "",
"pain": "Loading indicators lack consistent size and accessibility state",
"promise": "Spinner state and size strictly controlled via enums",
"why": "SpinnerPrimitive encodes size and LoadingState into ARIA attributes. aria-busy and role=status are enforced. This guarantees accessible loading indicators.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"spinner\"></div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Spinner size=SpinnerSize::Large />\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"loading indicators",
"async feedback"
],
"related": [
"progress",
"skeleton",
"pulse",
"loading_overlay",
"doc_progress"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "progress",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Spinner Primitive - Loading indicator\n\nuse leptos::prelude::*;\nuse crate::meta::LoadingState;\n\n#[derive(Debug, Clone, Copy, PartialEq, Default)]\npub enum SpinnerSize {\n Small,\n #[default]\n Medium,\n Large,\n}\nimpl SpinnerSize {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Small => \"small\",\n Self::Medium => \"medium\",\n Self::Large => \"large\",\n }\n }\n}\n\n#[component]\npub fn SpinnerPrimitive(\n #[prop(default = SpinnerSize::Medium)] size: SpinnerSize,\n #[prop(default = LoadingState::Loading)] state: LoadingState,\n #[prop(into, default = \"Loading\".to_string())] aria_label: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_spn = crate::infra::uid::generate(\"spn\");\n view! {\n <svg\n data-rs-spinner=\"\"\n data-rs-uid=uid_spn\n data-rs-size=size.as_str()\n data-rs-loading=state.as_str()\n role=\"status\"\n aria-label=aria_label\n aria-busy=state.aria_busy()\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n class=class\n >\n <path d=\"M21 12a9 9 0 1 1-6.219-8.56\" />\n </svg>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::SpinnerPrimitive;\nuse canonrs_core::meta::LoadingState;\npub use canonrs_core::primitives::SpinnerSize;\n\n#[component]\npub fn Spinner(\n #[prop(default = SpinnerSize::Medium)] size: SpinnerSize,\n #[prop(default = false)] paused: bool,\n #[prop(into, default = \"Loading\".to_string())] aria_label: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let state = if paused { LoadingState::Idle } else { LoadingState::Loading };\n view! {\n <SpinnerPrimitive\n size=size\n state=state\n aria_label=aria_label\n class=class\n />\n }\n}\n\n",
"boundary_src": "use leptos::prelude::*;\nuse super::spinner_ui::Spinner as SpinnerUi;\npub use canonrs_core::primitives::SpinnerSize;\n\n#[component]\npub fn Spinner(\n #[prop(default = SpinnerSize::Medium)] size: SpinnerSize,\n #[prop(default = false)] paused: bool,\n #[prop(into, default = \"Loading\".to_string())] aria_label: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <SpinnerUi size=size paused=paused aria_label=aria_label class=class />\n};\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{SpinnerSize}; \n\npub const SPINNER_API: ComponentApi = ComponentApi {\n id: \"spinner\",\n description: \"Loading spinner\",\n props: &[\n PropDef { name: \"size\", kind: PropType::Enum(&[\"small\", \"medium\", \"large\"]), required: false, default: Some(\"medium\"), description: \"Size variant of the component\" },\n PropDef { name: \"paused\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Whether spinner animation is paused\" },\n PropDef { name: \"aria_label\", kind: PropType::String, required: false, default: Some(\"Loading\"), description: \"Accessible label for screen readers\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::spinner_boundary::{Spinner, SpinnerSize};\n\n#[component]\npub fn SpinnerShowcasePreview() -> impl IntoView {\n view! {\n <div data-rs-showcase-preview-hero=\"\">\n <div data-rs-showcase-preview-stage=\"\">\n <Spinner />\n </div>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Loading state enforced by primitive.\"\n </p>\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"Sizes\"</span>\n <div data-rs-showcase-preview-row=\"\" style=\"display:flex;align-items:center;gap:var(--space-md);\">\n <Spinner size=SpinnerSize::Small />\n <Spinner size=SpinnerSize::Medium />\n <Spinner size=SpinnerSize::Large />\n </div>\n </div>\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"Paused\"</span>\n <div data-rs-showcase-preview-row=\"\">\n <Spinner paused=true />\n </div>\n </div>\n </div>\n }\n}\n",
"block": [],
"blocks_primitives": [
"center"
]
},
{
"id": "stat",
"label": "Stat",
"category": "Display",
"description": "Metric stat display",
"keywords": "",
"pain": "Metrics displayed without consistent structure or alignment",
"promise": "Metric layout and semantics enforced via structured primitives",
"why": "StatPrimitive enforces composition of value, label and optional delta. Size, alignment and trend are encoded via attributes. This guarantees consistent KPI display.\n",
"before": "// ❌ Typical\nview! {\n <div>\n <h1>\"100\"</h1>\n <p>\"Users\"</p>\n </div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Stat>\n <StatValue>\"100\"</StatValue>\n <StatLabel>\"Users\"</StatLabel>\n </Stat>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"dashboards",
"analytics"
],
"related": [
"avatar",
"icon",
"logo",
"code_block",
"markdown",
"chart",
"inline_meta",
"kbd",
"badge",
"carousel"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "content_display",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Stat Primitive - Number + label display\n\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum StatSize {\n Sm,\n #[default]\n Md,\n Lg,\n}\nimpl StatSize {\n pub fn as_str(&self) -> &'static str {\n match self { Self::Sm => \"sm\", Self::Md => \"md\", Self::Lg => \"lg\" }\n }\n}\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum StatTrend {\n #[default]\n Neutral,\n Increase,\n Decrease,\n}\nimpl StatTrend {\n pub fn as_str(&self) -> &'static str {\n match self { Self::Neutral => \"neutral\", Self::Increase => \"increase\", Self::Decrease => \"decrease\" }\n }\n}\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum StatAlign {\n #[default]\n Start,\n Center,\n End,\n}\nimpl StatAlign {\n pub fn as_str(&self) -> &'static str {\n match self { Self::Start => \"start\", Self::Center => \"center\", Self::End => \"end\" }\n }\n}\n\nuse leptos::prelude::*;\n\n#[component]\npub fn StatPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_sta = crate::infra::uid::generate(\"sta\");\n view! {\n <div\n data-rs-stat=\"\"\n data-rs-uid=uid_sta\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn StatValuePrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <span data-rs-stat-value=\"\" class=class>\n {children()}\n </span>\n }\n}\n\n#[component]\npub fn StatLabelPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <span data-rs-stat-label=\"\" class=class>\n {children()}\n </span>\n }\n}\n\n#[component]\npub fn StatDeltaPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <span data-rs-stat-delta=\"\" class=class>\n {children()}\n </span>\n }\n}\n\n#[component]\npub fn StatIconPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <span data-rs-stat-icon=\"\" aria-hidden=\"true\" class=class>\n {children()}\n </span>\n }\n}\n\n#[component]\npub fn StatHeaderPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-stat-header=\"\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn StatBodyPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-stat-body=\"\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn StatWrapperPrimitive(\n children: Children,\n #[prop(default = StatSize::Md)] size: StatSize,\n #[prop(default = StatTrend::Neutral)] trend: StatTrend,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-stat-wrapper=\"\"\n data-rs-size=size.as_str()\n data-rs-trend=trend.as_str()\n class=class\n >\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n StatSize, StatTrend, StatAlign,\n StatPrimitive, StatValuePrimitive, StatLabelPrimitive,\n StatDeltaPrimitive, StatIconPrimitive, StatHeaderPrimitive, StatBodyPrimitive, StatWrapperPrimitive,\n};\nuse canonrs_core::LoadingState;\n\n#[component]\npub fn Stat(children: Children, #[prop(default = StatSize::Md)] size: StatSize, #[prop(default = StatAlign::Start)] align: StatAlign, #[prop(optional)] trend: Option<StatTrend>, #[prop(default = LoadingState::Idle)] loading: LoadingState, #[prop(default = String::new())] class: String) -> impl IntoView {\n view! {\n <StatPrimitive class=class>\n <StatWrapperPrimitive size=size trend=trend.unwrap_or_default()>\n {children()}\n </StatWrapperPrimitive>\n </StatPrimitive>\n }\n}\n#[component]\npub fn StatHeader(children: Children, #[prop(default = String::new())] class: String) -> impl IntoView {\n view! { <StatHeaderPrimitive class=class>{children()}</StatHeaderPrimitive> }\n}\n#[component]\npub fn StatBody(children: Children, #[prop(default = String::new())] class: String) -> impl IntoView {\n view! { <StatBodyPrimitive class=class>{children()}</StatBodyPrimitive> }\n}\n#[component]\npub fn StatValue(children: Children, #[prop(default = String::new())] class: String) -> impl IntoView {\n view! { <StatValuePrimitive class=class>{children()}</StatValuePrimitive> }\n}\n#[component]\npub fn StatLabel(children: Children, #[prop(default = String::new())] class: String) -> impl IntoView {\n view! { <StatLabelPrimitive class=class>{children()}</StatLabelPrimitive> }\n}\n#[component]\npub fn StatDelta(children: Children, #[prop(default = String::new())] class: String) -> impl IntoView {\n view! { <StatDeltaPrimitive class=class>{children()}</StatDeltaPrimitive> }\n}\n#[component]\npub fn StatIcon(children: Children, #[prop(default = String::new())] class: String) -> impl IntoView {\n view! { <StatIconPrimitive class=class>{children()}</StatIconPrimitive> }\n}\n",
"boundary_src": "//! Stat Island — Canon Rule #340\n//! Passthrough only. Zero logic, zero transformation.\n\nuse leptos::prelude::*;\nuse super::stat_ui::{\n Stat as StatUi,\n StatHeader as StatHeaderUi,\n StatBody as StatBodyUi,\n StatValue as StatValueUi,\n StatLabel as StatLabelUi,\n StatDelta as StatDeltaUi,\n StatIcon as StatIconUi,\n};\npub use canonrs_core::primitives::{StatSize, StatAlign, StatTrend};\nuse canonrs_core::meta::LoadingState;\n\n#[component]\npub fn Stat(\n children: Children,\n #[prop(default = StatSize::Md)] size: StatSize,\n #[prop(default = StatAlign::Start)] align: StatAlign,\n #[prop(default = StatTrend::Neutral)] trend: StatTrend,\n #[prop(default = LoadingState::Idle)] loading: LoadingState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <StatUi size=size align=align trend=trend loading=loading class=class>{children()}</StatUi> }\n}\n\n#[component]\npub fn StatHeader(children: Children, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <StatHeaderUi class=class>{children()}</StatHeaderUi> }\n}\n\n#[component]\npub fn StatBody(children: Children, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <StatBodyUi class=class>{children()}</StatBodyUi> }\n}\n\n#[component]\npub fn StatValue(children: Children, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <StatValueUi class=class>{children()}</StatValueUi> }\n}\n\n#[component]\npub fn StatLabel(children: Children, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <StatLabelUi class=class>{children()}</StatLabelUi> }\n}\n\n#[component]\npub fn StatDelta(children: Children, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <StatDeltaUi class=class>{children()}</StatDeltaUi> }\n}\n\n#[component]\npub fn StatIcon(children: Children, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <StatIconUi class=class>{children()}</StatIconUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{StatSize, StatAlign, StatTrend}; \n\npub const STAT_API: ComponentApi = ComponentApi {\n id: \"stat\",\n description: \"Metric stat display\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"size\", kind: PropType::Enum(&[\"sm\", \"md\", \"lg\"]), required: false, default: Some(\"md\"), description: \"Size variant of the component\" },\n PropDef { name: \"align\", kind: PropType::Enum(&[\"start\", \"center\", \"end\"]), required: false, default: Some(\"start\"), description: \"Flex or stack align-items\" },\n PropDef { name: \"trend\", kind: PropType::Enum(&[\"neutral\", \"increase\", \"decrease\"]), required: false, default: Some(\"neutral\"), description: \"Trend direction: increase, decrease or neutral\" },\n PropDef { name: \"loading\", kind: PropType::String, required: false, default: Some(\"idle\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const STATHEADER_API: ComponentApi = ComponentApi {\n id: \"stat-header\",\n description: \"Metric stat display\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const STATVALUE_API: ComponentApi = ComponentApi {\n id: \"stat-value\",\n description: \"Metric stat display\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const STATDELTA_API: ComponentApi = ComponentApi {\n id: \"stat-delta\",\n description: \"Metric stat display\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::stat_boundary::{\n Stat, StatHeader, StatBody,\n StatValue, StatLabel, StatDelta, StatIcon,\n};\nuse super::{StatSize, StatAlign, StatTrend};\n\n#[component]\npub fn StatShowcasePreview() -> impl IntoView {\n view! {\n <div data-rs-showcase-preview-hero=\"\">\n <div data-rs-showcase-preview-stage=\"\">\n <Stat size=StatSize::Lg trend=StatTrend::Increase>\n <StatHeader>\n <StatIcon>\"📈\"</StatIcon>\n <StatLabel>\"Total Revenue\"</StatLabel>\n </StatHeader>\n <StatBody>\n <StatValue>\"$89,432\"</StatValue>\n <StatDelta>\"+18.2%\"</StatDelta>\n </StatBody>\n </Stat>\n </div>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Metric layout and semantics enforced via structured primitives.\"\n </p>\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"Trend variants\"</span>\n <div data-rs-showcase-preview-row=\"\">\n <Stat trend=StatTrend::Increase>\n <StatLabel>\"Active Users\"</StatLabel>\n <StatBody>\n <StatValue>\"2,350\"</StatValue>\n <StatDelta>\"+12%\"</StatDelta>\n </StatBody>\n </Stat>\n <Stat trend=StatTrend::Decrease>\n <StatLabel>\"Bounce Rate\"</StatLabel>\n <StatBody>\n <StatValue>\"3.2%\"</StatValue>\n <StatDelta>\"-0.5%\"</StatDelta>\n </StatBody>\n </Stat>\n <Stat trend=StatTrend::Neutral>\n <StatLabel>\"Sessions\"</StatLabel>\n <StatBody>\n <StatValue>\"1,024\"</StatValue>\n <StatDelta>\"0%\"</StatDelta>\n </StatBody>\n </Stat>\n </div>\n </div>\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"Size variants\"</span>\n <div data-rs-showcase-preview-row=\"\">\n <Stat size=StatSize::Sm>\n <StatLabel>\"Small\"</StatLabel>\n <StatValue>\"123\"</StatValue>\n </Stat>\n <Stat size=StatSize::Md>\n <StatLabel>\"Medium\"</StatLabel>\n <StatValue>\"4,567\"</StatValue>\n </Stat>\n <Stat size=StatSize::Lg>\n <StatLabel>\"Large\"</StatLabel>\n <StatValue>\"89,432\"</StatValue>\n </Stat>\n </div>\n </div>\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"Align + Icon\"</span>\n <div data-rs-showcase-preview-row=\"\">\n <Stat align=StatAlign::Center>\n <StatLabel>\"Centered\"</StatLabel>\n <StatValue>\"999\"</StatValue>\n </Stat>\n <Stat>\n <StatHeader>\n <StatIcon>\"💰\"</StatIcon>\n <StatLabel>\"Sales\"</StatLabel>\n </StatHeader>\n <StatValue>\"$12,234\"</StatValue>\n </Stat>\n </div>\n </div>\n </div>\n }\n}\n",
"block": [],
"blocks_primitives": [
"grid"
]
},
{
"id": "status_dot",
"label": "Status Dot",
"category": "Display",
"description": "Status indicator dot",
"keywords": "",
"pain": "Status indicators mix semantic feedback with presence states",
"promise": "Presence states strictly separated from semantic feedback",
"why": "StatusDotVariant encodes only presence states like online or busy. ARIA labels are derived automatically. This guarantees correct semantic usage.\n",
"before": "// ❌ Typical\nview! {\n <span class=\"green-dot\"></span>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <StatusDot variant=StatusDotVariant::Online />\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"user presence",
"chat apps"
],
"related": [
"toast",
"alert",
"banner",
"callout",
"inline_notice"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "feedback",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//!\n//! StatusDot Primitive - User Presence Indicator\n//!\n//! Domain: User presence/availability ONLY\n//! NOT semantic feedback (success/error/warning)\n//! Use Badge for semantic states\n//! Use InlineNotice for feedback states\n\nuse leptos::prelude::*;\n\n#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Default)]\npub enum StatusDotVariant {\n Online,\n #[default]\n Offline,\n Away,\n Busy,\n DoNotDisturb,\n}\n\nimpl StatusDotVariant {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Online => \"online\",\n Self::Offline => \"offline\",\n Self::Away => \"away\",\n Self::Busy => \"busy\",\n Self::DoNotDisturb => \"do-not-disturb\",\n }\n }\n\n pub fn state(&self) -> &'static str {\n match self {\n Self::Online => \"active\",\n _ => \"inactive\",\n }\n }\n pub fn aria_label(&self) -> &'static str {\n match self {\n Self::Online => \"Online\",\n Self::Offline => \"Offline\",\n Self::Away => \"Away\",\n Self::Busy => \"Busy\",\n Self::DoNotDisturb => \"Do not disturb\",\n }\n }\n}\n\n#[component]\npub fn StatusDotPrimitive(\n children: Children,\n #[prop(default = StatusDotVariant::Offline)] variant: StatusDotVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_sd = crate::infra::uid::generate(\"sd\");\n view! {\n <span\n data-rs-status-dot=\"\"\n data-rs-uid=uid_sd\n data-rs-interaction=\"init\"\n data-rs-variant=variant.as_str()\n role=\"img\"\n aria-label=variant.aria_label()\n class=class\n >\n {children()}\n </span>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::StatusDotPrimitive;\npub use canonrs_core::primitives::StatusDotVariant;\n\n#[component]\npub fn StatusDot(\n children: Children,\n #[prop(default = StatusDotVariant::Offline)] variant: StatusDotVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <StatusDotPrimitive variant=variant class=class>\n {children()}\n </StatusDotPrimitive>\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! StatusDot Island — Canon Rule #340 (zero-logic boundary)\n\nuse leptos::prelude::*;\nuse super::status_dot_ui::StatusDot as StatusDotUi;\npub use canonrs_core::primitives::StatusDotVariant;\n\n#[component]\npub fn StatusDot(\n #[prop(default = StatusDotVariant::Offline)] variant: StatusDotVariant,\n #[prop(into, optional)] label: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <span data-rs-status-dot-wrapper=\"\" style=\"display:inline-flex;align-items:center;gap:var(--space-xs);\">\n <StatusDotUi variant=variant class=class>\n <span></span>\n </StatusDotUi>\n {label.map(|l| view! { <span data-rs-status-dot-label=\"\">{l\n};</span> })}\n </span>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{StatusDotVariant}; \n\npub const STATUSDOT_API: ComponentApi = ComponentApi {\n id: \"status-dot\",\n description: \"Status indicator dot\",\n props: &[\n PropDef { name: \"variant\", kind: PropType::Enum(&[\"online\", \"offline\", \"away\", \"busy\", \"do-not-disturb\"]), required: false, default: Some(\"offline\"), description: \"Visual variant of the component\" },\n PropDef { name: \"label\", kind: PropType::String, required: false, default: None, description: \"Accessible label text\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::status_dot_boundary::{StatusDot, StatusDotVariant};\n\n#[component]\npub fn StatusDotShowcasePreview() -> impl IntoView {\n view! {\n <div data-rs-showcase-preview-hero=\"\">\n <div data-rs-showcase-preview-stage=\"\">\n <div style=\"display:flex;flex-direction:column;gap:var(--space-sm);\">\n <StatusDot variant=StatusDotVariant::Online label=\"Online\" />\n <StatusDot variant=StatusDotVariant::Away label=\"Away\" />\n <StatusDot variant=StatusDotVariant::Busy label=\"Busy\" />\n <StatusDot variant=StatusDotVariant::DoNotDisturb label=\"Do not disturb\" />\n <StatusDot variant=StatusDotVariant::Offline label=\"Offline\" />\n </div>\n </div>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Presence states strictly separated from semantic feedback.\"\n </p>\n </div>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "switch",
"label": "Switch",
"category": "Form",
"description": "Toggle switch on off",
"keywords": "",
"pain": "Toggle inputs desync visual state and checked value",
"promise": "Toggle state mapped directly to DOM and interaction state",
"why": "SwitchPrimitive maps SelectionState to checked and data attributes. Disabled state is enforced consistently. This guarantees reliable toggle behavior.\n",
"before": "// ❌ Typical\nview! {\n <input type=\"checkbox\" checked />\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Switch checked=true>\"On\"</Switch>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"settings",
"toggles"
],
"related": [
"toggle",
"toggle_group"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "toggle",
"primitive_src": "#![allow(unused_variables)]\n//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Switch Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::{SelectionState, DisabledState};\n\n\n#[component]\npub fn SwitchPrimitive(\n children: Children,\n #[prop(default = SelectionState::Unselected)] checked: SelectionState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] name: String,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_sw = crate::infra::uid::generate(\"sw\");\n let is_checked = checked == SelectionState::Selected;\n let _is_disabled = disabled == DisabledState::Disabled;\n let aria_checked = if is_checked { \"true\" } else { \"false\" };\n view! {\n <label\n data-rs-switch=\"\"\n data-rs-uid=uid_sw\n data-rs-interaction=\"init\"\n data-rs-selection=if checked == SelectionState::Selected { Some(\"selected\") } else { None }\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n aria-disabled=disabled.aria_disabled()\n class=class\n >\n <input\n type=\"checkbox\"\n role=\"switch\"\n data-rs-switch-input=\"\"\n name=if name.is_empty() { None } else { Some(name) }\n value=value\n checked=is_checked\n aria-checked=aria_checked\n tabindex=\"0\"\n />\n {children()}\n </label>\n }\n}\n\n#[component]\npub fn SwitchThumbPrimitive(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <span data-rs-switch-thumb=\"\" class=class /> }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{SwitchPrimitive, SwitchThumbPrimitive};\nuse canonrs_core::meta::{SelectionState, DisabledState};\n\n#[component]\npub fn Switch(\n children: Children,\n #[prop(default = false)] checked: bool,\n #[prop(default = false)] disabled: bool,\n #[prop(into, default = String::new())] name: String,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let checked_state = if checked { SelectionState::Selected } else { SelectionState::Unselected };\n let disabled_state = if disabled { DisabledState::Disabled } else { DisabledState::Enabled };\n view! {\n <SwitchPrimitive\n checked=checked_state\n disabled=disabled_state\n name=name\n value=value\n class=class\n >\n <SwitchThumbPrimitive />\n {children()}\n </SwitchPrimitive>\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! Switch Island — Canon Rule #340 (zero-logic boundary)\n\nuse leptos::prelude::*;\nuse super::switch_ui::Switch as SwitchUi;\n\n#[component]\npub fn Switch(\n #[prop(default = false)] checked: bool,\n #[prop(default = false)] disabled: bool,\n #[prop(into, default = String::new())] name: String,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <SwitchUi checked=checked disabled=disabled name=name value=value class=class>\n \"\"\n </SwitchUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const SWITCH_API: ComponentApi = ComponentApi {\n id: \"switch\",\n description: \"Toggle switch on off\",\n props: &[\n PropDef { name: \"checked\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Whether the component is checked\" },\n PropDef { name: \"disabled\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"name\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Form field name\" },\n PropDef { name: \"value\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Current value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::switch_boundary::Switch;\n\n#[component]\npub fn SwitchShowcasePreview() -> impl IntoView {\n view! {\n <div data-rs-showcase-preview-hero=\"\">\n <div data-rs-showcase-preview-stage=\"\">\n <Switch />\n <Switch checked=true />\n </div>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Toggle state mapped directly to DOM and interaction state.\"\n </p>\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"States\"</span>\n <div data-rs-showcase-preview-row=\"\">\n <div style=\"display:flex;flex-direction:column;align-items:center;gap:var(--space-xs)\">\n <Switch />\n <span data-rs-showcase-preview-label=\"\">\"Off\"</span>\n </div>\n <div style=\"display:flex;flex-direction:column;align-items:center;gap:var(--space-xs)\">\n <Switch checked=true />\n <span data-rs-showcase-preview-label=\"\">\"On\"</span>\n </div>\n <div style=\"display:flex;flex-direction:column;align-items:center;gap:var(--space-xs)\">\n <Switch disabled=true />\n <span data-rs-showcase-preview-label=\"\">\"Disabled Off\"</span>\n </div>\n <div style=\"display:flex;flex-direction:column;align-items:center;gap:var(--space-xs)\">\n <Switch checked=true disabled=true />\n <span data-rs-showcase-preview-label=\"\">\"Disabled On\"</span>\n </div>\n </div>\n </div>\n </div>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "table",
"label": "Table",
"category": "Data",
"description": "HTML table component",
"keywords": "",
"pain": "Tables lack consistent state handling and accessibility attributes",
"promise": "Table state, sorting and selection enforced structurally",
"why": "TablePrimitive encodes state, striped and hoverable behavior. Rows and headers derive selection and sort semantics. This guarantees consistent data table behavior.\n",
"before": "// ❌ Typical\nview! {\n <table>\n <tr><td>\"Data\"</td></tr>\n </table>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Table>\n <TableBody>\n <TableRow>\n <TableCell>\"Data\"</TableCell>\n </TableRow>\n </TableBody>\n </Table>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"data grids",
"reports"
],
"related": [
"data_table",
"virtual_list",
"empty_table",
"tree",
"list_item"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "data",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Table Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::SelectionState;\n\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum TableState {\n #[default]\n Idle,\n Loading,\n Empty,\n Error,\n}\nimpl TableState {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Idle => \"idle\",\n Self::Loading => \"loading\",\n Self::Empty => \"empty\",\n Self::Error => \"error\",\n }\n }\n}\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum SortDirection {\n #[default]\n None,\n Ascending,\n Descending,\n}\nimpl SortDirection {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::None => \"none\",\n Self::Ascending => \"ascending\",\n Self::Descending => \"descending\",\n }\n }\n pub fn aria_sort(&self) -> Option<&'static str> {\n match self {\n Self::None => None,\n Self::Ascending => Some(\"ascending\"),\n Self::Descending => Some(\"descending\"),\n }\n }\n}\n\n#[component]\npub fn TableWrapperPrimitive(\n children: Children,\n #[prop(into, optional)] aria_label: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-table-wrapper=\"\"\n role=\"region\"\n aria-label=aria_label\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn TablePrimitive(\n children: Children,\n #[prop(default = TableState::Idle)] state: TableState,\n #[prop(default = false)] striped: bool,\n #[prop(default = false)] hoverable: bool,\n #[prop(default = false)] sheet_context: bool,\n #[prop(into, optional)] aria_label: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_tbl = crate::infra::uid::generate(\"tbl\");\n view! {\n <table\n data-rs-table=\"\"\n data-rs-uid=uid_tbl\n data-rs-interaction=\"init\"\n data-rs-activity=state.as_str()\n data-rs-striped={striped.then_some(\"\")}\n data-rs-hoverable={hoverable.then_some(\"\")}\n data-rs-table-context={sheet_context.then_some(\"\")}\n aria-busy={if state == TableState::Loading { Some(\"true\") } else { None }}\n aria-label=aria_label\n class=class\n tabindex=\"-1\"\n >\n {children()}\n </table>\n }\n}\n\n#[component]\npub fn TableHeaderPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <thead data-rs-table-header=\"\" class=class>\n {children()}\n </thead>\n }\n}\n\n#[component]\npub fn TableBodyPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <tbody data-rs-table-body=\"\" class=class>\n {children()}\n </tbody>\n }\n}\n\n#[component]\npub fn TableFooterPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <tfoot data-rs-table-footer=\"\" class=class>\n {children()}\n </tfoot>\n }\n}\n\n#[component]\npub fn TableRowPrimitive(\n children: Children,\n #[prop(default = SelectionState::Unselected)] selected: SelectionState,\n #[prop(into, default = TextProp::from(\"\"))] class: TextProp,\n #[prop(into, default = String::new())] href: String,\n #[prop(into, default = String::new())] row_action: String,\n #[prop(into, default = String::new())] row_label: String,\n #[prop(into, default = String::new())] row_meta: String,\n) -> impl IntoView {\n let action_attr = if !row_action.is_empty() { Some(row_action.clone()) } else if !href.is_empty() { Some(\"navigate\".to_string()) } else { None };\n let href_attr = if href.is_empty() { None } else { Some(href) };\n let row_label = if row_label.is_empty() { None } else { Some(row_label) };\n let row_meta = if row_meta.is_empty() { None } else { Some(row_meta) };\n view! {\n <tr\n data-rs-table-row=\"\"\n data-rs-selection=if selected == SelectionState::Selected { Some(\"selected\") } else { None }\n tabindex=\"0\"\n data-rs-action=action_attr\n data-rs-href=href_attr\n data-rs-label=row_label\n data-rs-meta=row_meta\n role=\"row\"\n aria-selected=if selected == SelectionState::Selected { Some(\"true\") } else { None }\n class=move || class.get().to_string()\n >\n {children()}\n </tr>\n }\n}\n\n#[component]\npub fn TableHeadPrimitive(\n children: Children,\n #[prop(default = SortDirection::None)] sort: SortDirection,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <th\n data-rs-table-head=\"\"\n data-rs-sort=sort.as_str()\n scope=\"col\"\n role=\"columnheader\"\n aria-sort=sort.aria_sort()\n class=class\n >\n {children()}\n </th>\n }\n}\n\n#[component]\npub fn TableCellPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = false)] copyable: bool,\n #[prop(default = false)] truncate: bool,\n) -> impl IntoView {\n view! {\n <td\n data-rs-table-cell=\"\"\n data-rs-copyable={copyable.then(|| \"\")}\n data-rs-truncate={truncate.then(|| \"\")}\n class=class\n >\n {children()}\n </td>\n }\n}\n\n#[component]\npub fn TableCaptionPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <caption data-rs-table-caption=\"\" class=class>\n {children()}\n </caption>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n TablePrimitive, TableHeaderPrimitive,\n TableBodyPrimitive, TableFooterPrimitive, TableRowPrimitive,\n TableHeadPrimitive, TableCellPrimitive, TableCaptionPrimitive,\n SortDirection,\n};\nuse canonrs_core::meta::SelectionState;\npub use canonrs_core::primitives::TableState;\n\n#[component]\npub fn Table(\n #[prop(optional)] children: Option<Children>,\n #[prop(default = TableState::Idle)] state: TableState,\n #[prop(default = false)] striped: bool,\n #[prop(default = false)] hoverable: bool,\n #[prop(into, optional)] aria_label: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <TablePrimitive state=state striped=striped hoverable=hoverable aria_label=aria_label.unwrap_or_default() class=class>\n {children.map(|c| c())}\n </TablePrimitive>\n }\n}\n\n#[component]\npub fn TableHeader(\n #[prop(optional)] children: Option<Children>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <TableHeaderPrimitive class=class>\n {children.map(|c| c())}\n </TableHeaderPrimitive>\n }\n}\n\n#[component]\npub fn TableBody(\n #[prop(optional)] children: Option<Children>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <TableBodyPrimitive class=class>\n {children.map(|c| c())}\n </TableBodyPrimitive>\n }\n}\n\n#[component]\npub fn TableFooter(\n #[prop(optional)] children: Option<Children>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <TableFooterPrimitive class=class>\n {children.map(|c| c())}\n </TableFooterPrimitive>\n }\n}\n\n#[component]\npub fn TableRow(\n #[prop(optional)] children: Option<Children>,\n #[prop(default = SelectionState::Unselected)] selected: SelectionState,\n #[prop(into, default = TextProp::from(\"\"))] class: TextProp,\n #[prop(into, optional)] href: Option<String>,\n #[prop(into, optional)] row_action: Option<String>,\n #[prop(into, optional)] row_label: Option<String>,\n #[prop(into, optional)] row_meta: Option<String>,\n) -> impl IntoView {\n let href = href.unwrap_or_default();\n let row_action = row_action.unwrap_or_default();\n let row_label = row_label.unwrap_or_default();\n let row_meta = row_meta.unwrap_or_default();\n view! {\n <TableRowPrimitive selected=selected class=class href=href row_action=row_action row_label=row_label row_meta=row_meta>\n {children.map(|c| c())}\n </TableRowPrimitive>\n }\n}\n\n#[component]\npub fn TableHead(\n #[prop(optional)] children: Option<Children>,\n #[prop(default = SortDirection::None)] sort: SortDirection,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <TableHeadPrimitive sort=sort class=class>\n {children.map(|c| c())}\n </TableHeadPrimitive>\n }\n}\n\n#[component]\npub fn TableCell(\n #[prop(optional)] children: Option<Children>,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = false)] copyable: bool,\n #[prop(default = false)] truncate: bool,\n) -> impl IntoView {\n view! {\n <TableCellPrimitive class=class copyable=copyable truncate=truncate>\n {children.map(|c| c())}\n </TableCellPrimitive>\n }\n}\n\n#[component]\npub fn TableCaption(\n #[prop(optional)] children: Option<Children>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <TableCaptionPrimitive class=class>\n {children.map(|c| c())}\n </TableCaptionPrimitive>\n }\n}\n",
"boundary_src": "//! @canon-level: strict\n//! Table Boundary — Canon Rule #340 (zero-logic passthrough)\n\nuse leptos::prelude::*;\nuse super::table_ui::{\n Table as TableUi,\n TableHeader as TableHeaderUi,\n TableBody as TableBodyUi,\n TableRow as TableRowUi,\n TableHead as TableHeadUi,\n TableCell as TableCellUi,\n TableFooter as TableFooterUi,\n TableCaption as TableCaptionUi,\n};\npub use canonrs_core::primitives::SortDirection;\nuse canonrs_core::meta::SelectionState;\npub use super::table_ui::TableState;\n\n#[component]\npub fn Table(\n children: Children,\n #[prop(default = TableState::Idle)] state: TableState,\n #[prop(default = false)] striped: bool,\n #[prop(default = false)] hoverable: bool,\n #[prop(into, optional)] aria_label: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <TableUi state=state striped=striped hoverable=hoverable aria_label=aria_label.unwrap_or_default() class=class>{children()}</TableUi> }\n}\n\n#[component]\npub fn TableHeader(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <TableHeaderUi class=class>{children()}</TableHeaderUi> }\n}\n\n#[component]\npub fn TableBody(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <TableBodyUi class=class>{children()}</TableBodyUi> }\n}\n\n#[component]\npub fn TableFooter(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <TableFooterUi class=class>{children()}</TableFooterUi> }\n}\n\n#[component]\npub fn TableRow(\n children: Children,\n #[prop(default = SelectionState::Unselected)] selected: SelectionState,\n #[prop(into, optional)] href: Option<String>,\n #[prop(into, default = TextProp::from(\"\"))] class: TextProp,\n) -> impl IntoView {\n let href = href.unwrap_or_default();\n view! { <TableRowUi selected=selected href=href class=class>{children()}</TableRowUi> }\n}\n\n#[component]\npub fn TableHead(\n children: Children,\n #[prop(default = SortDirection::None)] sort: SortDirection,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <TableHeadUi sort=sort class=class>{children()}</TableHeadUi> }\n}\n\n#[component]\npub fn TableCell(\n children: Children,\n #[prop(default = false)] copyable: bool,\n #[prop(default = false)] truncate: bool,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <TableCellUi copyable=copyable truncate=truncate class=class>{children()}</TableCellUi> }\n}\n\n#[component]\npub fn TableCaption(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <TableCaptionUi class=class>{children()}</TableCaptionUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{SortDirection}; \n\npub const TABLE_API: ComponentApi = ComponentApi {\n id: \"table\",\n description: \"HTML table component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"state\", kind: PropType::Enum(&[\"idle\", \"loading\", \"empty\", \"error\"]), required: false, default: Some(\"idle\"), description: \"Loading or visibility state\" },\n PropDef { name: \"striped\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"hoverable\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"aria_label\", kind: PropType::String, required: false, default: None, description: \"Accessible label for screen readers\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const TABLEHEADER_API: ComponentApi = ComponentApi {\n id: \"table-header\",\n description: \"HTML table component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const TABLEBODY_API: ComponentApi = ComponentApi {\n id: \"table-body\",\n description: \"HTML table component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const TABLEFOOTER_API: ComponentApi = ComponentApi {\n id: \"table-footer\",\n description: \"HTML table component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const TABLEROW_API: ComponentApi = ComponentApi {\n id: \"table-row\",\n description: \"HTML table component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"selected\", kind: PropType::String, required: false, default: Some(\"unselected\"), description: \"Prop value\" },\n PropDef { name: \"href\", kind: PropType::String, required: false, default: None, description: \"Navigation target URL\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"from\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const TABLEHEAD_API: ComponentApi = ComponentApi {\n id: \"table-head\",\n description: \"HTML table component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"sort\", kind: PropType::Enum(&[\"none\", \"ascending\", \"descending\"]), required: false, default: Some(\"none\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const TABLECELL_API: ComponentApi = ComponentApi {\n id: \"table-cell\",\n description: \"HTML table component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"copyable\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"truncate\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const TABLECAPTION_API: ComponentApi = ComponentApi {\n id: \"table-caption\",\n description: \"HTML table component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::table_boundary::{Table, TableHeader, TableBody, TableRow, TableHead, TableCell};\nuse crate::blocks::data_table::DataTableBlock;\nuse crate::ui::sheet::sheet_boundary::Sheet;\nuse canonrs_core::slot;\nuse canonrs_core::meta::SelectionState;\n\n#[component]\npub fn TableShowcasePreview() -> impl IntoView {\n view! {\n <DataTableBlock\n body=slot!(|| view! {\n <div data-rs-table-context=\"\" data-rs-uid=\"table-ctx-1\" data-rs-interaction=\"init\">\n <Table hoverable=true>\n <TableHeader>\n <TableRow>\n <TableHead>\"Name\"</TableHead>\n <TableHead>\"Role\"</TableHead>\n <TableHead>\"Status\"</TableHead>\n <TableHead>\"Email\"</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n <TableRow attr:data-rs-action=\"open-sheet\" attr:data-rs-label=\"Alice\" attr:data-rs-meta=\"Engineer · Active\">\n <TableCell copyable=true>\"Alice\"</TableCell>\n <TableCell>\"Engineer\"</TableCell>\n <TableCell>\"Active\"</TableCell>\n <TableCell truncate=true>\"alice@example.com · alice.secondary@company.org\"</TableCell>\n </TableRow>\n <TableRow attr:data-rs-action=\"open-sheet\" attr:data-rs-label=\"Bob\" attr:data-rs-meta=\"Designer · Active\">\n <TableCell copyable=true>\"Bob\"</TableCell>\n <TableCell>\"Designer\"</TableCell>\n <TableCell>\"Active\"</TableCell>\n <TableCell truncate=true>\"bob@example.com · bob.work@company.org\"</TableCell>\n </TableRow>\n <TableRow attr:data-rs-action=\"open-sheet\" attr:data-rs-label=\"Carol\" attr:data-rs-meta=\"Manager · Inactive\">\n <TableCell copyable=true>\"Carol\"</TableCell>\n <TableCell>\"Manager\"</TableCell>\n <TableCell>\"Inactive\"</TableCell>\n <TableCell truncate=true>\"carol@example.com · carol.backup@company.org\"</TableCell>\n </TableRow>\n <TableRow attr:data-rs-action=\"open-sheet\" attr:data-rs-label=\"Dave\" attr:data-rs-meta=\"DevOps · Active\" selected=SelectionState::Selected>\n <TableCell copyable=true>\"Dave\"</TableCell>\n <TableCell>\"DevOps\"</TableCell>\n <TableCell>\"Active\"</TableCell>\n <TableCell truncate=true>\"dave@example.com · dave.ops@company.org\"</TableCell>\n </TableRow>\n <TableRow attr:data-rs-action=\"open-sheet\" attr:data-rs-label=\"Eve\" attr:data-rs-meta=\"QA · Active\">\n <TableCell copyable=true>\"Eve\"</TableCell>\n <TableCell>\"QA\"</TableCell>\n <TableCell>\"Active\"</TableCell>\n <TableCell truncate=true>\"eve@example.com · eve.testing@company.org\"</TableCell>\n </TableRow>\n </TableBody>\n </Table>\n <Sheet trigger_label=\"\" close_label=\"Close\" />\n </div>\n }.into_any())\n />\n <p data-rs-showcase-preview-anchor=\"\">\n \"Click any row to open a detail sheet. Copy cells and truncation included.\"\n </p>\n <DataTableBlock\n body=slot!(|| view! {\n <Table striped=true hoverable=true>\n <TableHeader>\n <TableRow>\n <TableHead>\"Name\"</TableHead>\n <TableHead>\"Score\"</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n <TableRow><TableCell>\"Alice\"</TableCell><TableCell>\"98\"</TableCell></TableRow>\n <TableRow><TableCell>\"Bob\"</TableCell><TableCell>\"87\"</TableCell></TableRow>\n <TableRow><TableCell>\"Carol\"</TableCell><TableCell>\"75\"</TableCell></TableRow>\n <TableRow selected=SelectionState::Selected><TableCell>\"Dave\"</TableCell><TableCell>\"92\"</TableCell></TableRow>\n <TableRow><TableCell>\"Eve\"</TableCell><TableCell>\"81\"</TableCell></TableRow>\n </TableBody>\n </Table>\n }.into_any())\n />\n }\n}\n",
"block": [
"data_table_block"
],
"blocks_primitives": [
"container"
]
},
{
"id": "table_of_contents",
"label": "Table of Contents",
"category": "Navigation",
"description": "Document table of contents",
"keywords": "",
"pain": "TOC structures inconsistent and hard to sync with document hierarchy",
"promise": "TOC hierarchy and state derived from structured data model",
"why": "TocPrimitive encodes mode and item states with hierarchical structure. SSR rendering ensures deterministic output. This guarantees consistent navigation.\n",
"before": "// ❌ Typical\nview! {\n <ul>\n <li><a href=\"#a\">\"A\"</a></li>\n </ul>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <TableOfContents items=items />\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"docs navigation",
"long pages"
],
"related": [
"tabs"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "tabs",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! TableOfContents Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::VisibilityState;\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum TocMode {\n #[default]\n Simple,\n Expand,\n Nested,\n}\n\nimpl TocMode {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Simple => \"simple\",\n Self::Expand => \"expand\",\n Self::Nested => \"nested\",\n }\n }\n}\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum TocItemState {\n #[default]\n Idle,\n Active,\n Ancestor,\n}\n\nimpl TocItemState {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Idle => \"idle\",\n Self::Active => \"active\",\n Self::Ancestor => \"ancestor\",\n }\n }\n}\n\n#[component]\npub fn TocPrimitive(\n children: Children,\n #[prop(default = TocMode::Simple)] mode: TocMode,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_toc = crate::infra::uid::generate(\"toc\");\n view! {\n <nav\n data-rs-toc=\"\"\n data-rs-uid=uid_toc\n data-rs-interaction=\"init\"\n data-rs-mode=mode.as_str()\n aria-label=\"Table of contents\"\n class=class\n >\n {children()}\n </nav>\n }\n}\n\n#[component]\npub fn TocTitlePrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <p data-rs-toc-title=\"\" class=class>\n {children()}\n </p>\n }\n}\n\n#[component]\npub fn TocListPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ul data-rs-toc-list=\"\" class=class>\n {children()}\n </ul>\n }\n}\n\n#[component]\npub fn TocSubtreePrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ul\n data-rs-toc-subtree=\"\"\n data-rs-navigation=state.as_str()\n class=class\n >\n {children()}\n </ul>\n }\n}\n\n#[component]\npub fn TocItemPrimitive(\n children: Children,\n #[prop(into)] data_level: String,\n #[prop(into, default = String::new())] data_target: String,\n #[prop(default = TocItemState::Idle)] state: TocItemState,\n #[prop(default = false)] is_child: bool,\n #[prop(default = false)] has_children: bool,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <li\n data-rs-toc-item=\"\"\n data-rs-level=data_level\n data-rs-target=data_target\n data-rs-navigation=state.as_str()\n data-rs-child=if is_child { \"true\" } else { \"false\" }\n data-rs-has-children=if has_children { \"true\" } else { \"false\" }\n class=class\n >\n {children()}\n </li>\n }\n}\n\n#[component]\npub fn TocLinkPrimitive(\n children: Children,\n #[prop(into, default = String::new())] href: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <a data-rs-toc-link=\"\" href=href class=class>\n {children()}\n </a>\n }\n}\n\n#[component]\npub fn TocExpandButtonPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let open = state == VisibilityState::Open;\n view! {\n <button\n type=\"button\"\n data-rs-toc-expand-btn=\"\"\n data-rs-navigation=state.as_str()\n aria-expanded=if open { \"true\" } else { \"false\" }\n class=class\n >\n {children()}\n </button>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::structural::toc_item::{TocItemRowPrimitive, TocExpandIconPrimitive};\nuse canonrs_core::TocItem;\nuse canonrs_core::primitives::table_of_contents::*;\n#[cfg(feature = \"ssr\")]\nuse canonrs_core::VisibilityState;\n\n#[component]\npub fn TableOfContents(items: Vec<TocItem>, #[prop(default = TocMode::Simple)] mode: TocMode, #[prop(into, default = String::new())] class: String, #[prop(into, default = \"On this page\".to_string())] title: String) -> impl IntoView {\n #[cfg(feature = \"ssr\")]\n {\n view! {\n <TocPrimitive class=class mode=mode>\n <TocTitlePrimitive>{title}</TocTitlePrimitive>\n {match mode {\n TocMode::Simple => render_simple(items).into_any(),\n TocMode::Expand => render_expand(items).into_any(),\n TocMode::Nested => render_nested(items).into_any(),\n }}\n </TocPrimitive>\n }.into_any()\n }\n #[cfg(not(feature = \"ssr\"))]\n {\n let _ = (items, mode, class, title);\n view! { <TocPrimitive class=String::new() mode=TocMode::Simple>{()}</TocPrimitive> }.into_any()\n }\n}\n\n#[cfg(feature = \"ssr\")]\nfn render_simple(items: Vec<TocItem>) -> impl IntoView {\n view! {\n <TocListPrimitive>\n {items.into_iter().map(|item| view! {\n <TocItemPrimitive data_level=item.level.to_string() data_target=item.id.clone() state=TocItemState::Idle is_child=false has_children=false>\n <TocLinkPrimitive href=format!(\"#{}\", item.id)>{item.text}</TocLinkPrimitive>\n </TocItemPrimitive>\n }).collect::<Vec<_>>()}\n </TocListPrimitive>\n }\n}\n\n#[cfg(feature = \"ssr\")]\nfn render_expand(items: Vec<TocItem>) -> impl IntoView {\n view! {\n <TocListPrimitive>\n {items.into_iter().map(|item| {\n let is_child = item.level > 1;\n view! {\n <TocItemPrimitive data_level=item.level.to_string() data_target=item.id.clone() state=TocItemState::Idle is_child=is_child has_children=false>\n <TocLinkPrimitive href=format!(\"#{}\", item.id)>{item.text}</TocLinkPrimitive>\n </TocItemPrimitive>\n }\n }).collect::<Vec<_>>()}\n </TocListPrimitive>\n }\n}\n\n#[cfg(feature = \"ssr\")]\nfn render_nested(items: Vec<TocItem>) -> impl IntoView {\n let tree = build_tree(items);\n view! { <TocListPrimitive>{render_tree_nodes(tree)}</TocListPrimitive> }\n}\n\n#[derive(Clone)]\n#[cfg(feature = \"ssr\")]\nstruct TocNode { item: TocItem, children: Vec<TocNode> }\n\n#[cfg(feature = \"ssr\")]\nfn build_tree(items: Vec<TocItem>) -> Vec<TocNode> {\n let flat: Vec<TocNode> = items.into_iter().map(|item| TocNode { item, children: Vec::new() }).collect();\n let n = flat.len();\n let mut parent: Vec<Option<usize>> = vec![None; n];\n let mut stack: Vec<(u8, usize)> = Vec::new();\n for i in 0..n {\n let level = flat[i].item.level;\n while stack.last().map(|(l, _)| *l >= level).unwrap_or(false) { stack.pop(); }\n if let Some(&(_, p)) = stack.last() { parent[i] = Some(p); }\n stack.push((level, i));\n }\n let mut nodes: Vec<Option<TocNode>> = flat.into_iter().map(Some).collect();\n for i in (0..n).rev() {\n if let Some(p) = parent[i] { let child = nodes[i].take().unwrap(); nodes[p].as_mut().unwrap().children.insert(0, child); }\n }\n nodes.into_iter().enumerate().filter(|(i, _)| parent[*i].is_none()).filter_map(|(_, n)| n).collect()\n}\n\n#[cfg(feature = \"ssr\")]\nfn render_tree_nodes(nodes: Vec<TocNode>) -> Vec<AnyView> {\n nodes.into_iter().map(|node| -> AnyView {\n let has_children = !node.children.is_empty();\n let item = node.item;\n let children = node.children;\n view! {\n <TocItemPrimitive data_level=item.level.to_string() data_target=item.id.clone() state=TocItemState::Idle is_child=false has_children=has_children>\n <TocItemRowPrimitive>\n {has_children.then(|| view! { <TocExpandButtonPrimitive><TocExpandIconPrimitive/></TocExpandButtonPrimitive> }.into_any())}\n <TocLinkPrimitive href=format!(\"#{}\", item.id)>{item.text}</TocLinkPrimitive>\n </TocItemRowPrimitive>\n {has_children.then(|| view! {\n <TocSubtreePrimitive state=VisibilityState::Closed>{render_tree_nodes(children)}</TocSubtreePrimitive>\n }.into_any())}\n </TocItemPrimitive>\n }.into_any()\n }).collect()\n}\n",
"boundary_src": "//! @canon-level: strict\n//! TableOfContents Boundary — Canon Rule #340 (zero-logic boundary)\n\nuse leptos::prelude::*;\nuse super::table_of_contents_ui::TableOfContents as TableOfContentsUi;\npub use canonrs_core::TocItem;\nuse canonrs_core::primitives::table_of_contents::TocMode;\n\n#[component]\npub fn TableOfContents(\n items: Vec<TocItem>,\n #[prop(into, default = String::from(\"On this page\"))] title: String,\n #[prop(optional)] mode: Option<TocMode>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let toc_mode = mode.unwrap_or(TocMode::Simple);\n view! { <TableOfContentsUi items=items title=title mode=toc_mode class=class /> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{TocMode}; \n\npub const TABLEOFCONTENTS_API: ComponentApi = ComponentApi {\n id: \"table-of-contents\",\n description: \"Document table of contents\",\n props: &[\n PropDef { name: \"items\", kind: PropType::String, required: true, default: None, description: \"Prop value\" },\n PropDef { name: \"title\", kind: PropType::String, required: false, default: Some(\"On this page\"), description: \"Title slot or text\" },\n PropDef { name: \"mode\", kind: PropType::Enum(&[\"simple\", \"expand\", \"nested\"]), required: false, default: None, description: \"Operational mode of the component\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::table_of_contents_boundary::TableOfContents;\nuse super::table_of_contents_boundary::TocItem;\nuse canonrs_core::primitives::table_of_contents::TocMode;\nuse canonrs_core::primitives::layout::stack::{StackPrimitive as Stack, StackDirection, StackGap};\nuse crate::ui::scroll_area::scroll_area_boundary::ScrollArea;\n\nfn demo_items() -> Vec<TocItem> {\n vec![\n TocItem::new(\"toc-intro\".into(), \"Introduction\".into(), 1),\n TocItem::new(\"toc-setup\".into(), \"Setup\".into(), 1),\n TocItem::new(\"toc-install\".into(), \"Installation\".into(), 2),\n TocItem::new(\"toc-config\".into(), \"Configuration\".into(), 2),\n TocItem::new(\"toc-env\".into(), \"Environment\".into(), 3),\n TocItem::new(\"toc-usage\".into(), \"Usage\".into(), 1),\n TocItem::new(\"toc-examples\".into(),\"Examples\".into(), 2),\n TocItem::new(\"toc-api\".into(), \"API Reference\".into(), 1),\n TocItem::new(\"toc-props\".into(), \"Props\".into(), 2),\n TocItem::new(\"toc-events\".into(), \"Events\".into(), 2),\n ]\n}\n\n#[component]\npub fn TableOfContentsShowcasePreview() -> impl IntoView {\n view! {\n <div data-rs-showcase-preview-hero=\"\">\n <p data-rs-showcase-preview-anchor=\"\">\n \"TOC hierarchy and state derived from structured data model.\"\n </p>\n <div data-rs-showcase-preview-stage=\"\" style=\"padding:0\">\n <div style=\"display:grid;grid-template-columns:1fr 1fr 1fr 2fr;gap:var(--space-md);height:400px;width:100%\">\n <div style=\"display:flex;flex-direction:column;gap:var(--space-xs)\">\n <span data-rs-showcase-preview-label=\"\">\"Simple\"</span>\n <TableOfContents mode=TocMode::Simple title=\"On this page\" items=demo_items() />\n </div>\n <div style=\"display:flex;flex-direction:column;gap:var(--space-xs)\">\n <span data-rs-showcase-preview-label=\"\">\"Expand\"</span>\n <TableOfContents mode=TocMode::Expand title=\"On this page\" items=demo_items() />\n </div>\n <div style=\"display:flex;flex-direction:column;gap:var(--space-xs)\">\n <span data-rs-showcase-preview-label=\"\">\"Nested\"</span>\n <TableOfContents mode=TocMode::Nested title=\"On this page\" items=demo_items() />\n </div>\n <div style=\"height:400px;display:flex;flex-direction:column\">\n <span data-rs-showcase-preview-label=\"\">\"Content\"</span>\n <div style=\"flex:1;min-height:0\">\n <ScrollArea>\n <Stack direction=StackDirection::Vertical gap=StackGap::Xl>\n <section>\n <h2 id=\"toc-intro\">\"Introduction\"</h2>\n <p>\"Introduction content goes here. This section covers the basic concepts and goals of the library. Understanding the foundation will help you get the most out of CanonRS.\"</p>\n <p>\"CanonRS is built on a strict contract between primitives, tokens, and interaction modules. Each layer has a single responsibility and clear boundaries.\"</p>\n </section>\n <section>\n <h2 id=\"toc-setup\">\"Setup\"</h2>\n <p>\"Before you begin, make sure you have Rust and the WASM toolchain installed. The setup process is straightforward and takes only a few minutes.\"</p>\n <p>\"You will also need to configure your build pipeline to output WASM artifacts. Follow the steps below carefully to avoid common pitfalls.\"</p>\n </section>\n <section>\n <h3 id=\"toc-install\">\"Installation\"</h3>\n <p>\"Install via cargo add canonrs. You can also add it manually to your Cargo.toml. Make sure to enable the ssr feature for server-side rendering support.\"</p>\n <p>\"After installation, run cargo build to verify everything compiles correctly.\"</p>\n </section>\n <section>\n <h3 id=\"toc-config\">\"Configuration\"</h3>\n <p>\"Configure tokens, themes and behavior. The token system is the foundation of all visual decisions. Start by selecting a base theme and customize from there.\"</p>\n <p>\"All configuration is done through CSS custom properties. No JavaScript configuration is required.\"</p>\n </section>\n <section>\n <h4 id=\"toc-env\">\"Environment\"</h4>\n <p>\"Set the CANON_ENV variable to production before deploying. This enables optimized output and disables debug logging. Make sure all required environment variables are set.\"</p>\n </section>\n <section>\n <h2 id=\"toc-usage\">\"Usage\"</h2>\n <p>\"Import components from canonrs::ui. Each component follows the same pattern: primitive, UI, boundary. Use the boundary in your application code.\"</p>\n <p>\"Components are designed to be composable. You can nest them freely as long as you follow the contract defined by each primitive.\"</p>\n </section>\n <section>\n <h3 id=\"toc-examples\">\"Examples\"</h3>\n <p>\"See the examples directory for working demos of each component. Each example is self-contained and can be run independently. Use them as a starting point for your own implementation.\"</p>\n </section>\n <section>\n <h2 id=\"toc-api\">\"API Reference\"</h2>\n <p>\"Full API reference documentation. Every component, primitive, and token is documented here. Use the search to find what you need quickly.\"</p>\n <p>\"The API is versioned and follows semantic versioning. Breaking changes are announced in the changelog.\"</p>\n </section>\n <section>\n <h3 id=\"toc-props\">\"Props\"</h3>\n <p>\"Each component accepts a set of typed props defined by its primitive. Props are validated at compile time by the Rust type system. Optional props have sensible defaults.\"</p>\n </section>\n <section>\n <h3 id=\"toc-events\">\"Events\"</h3>\n <p>\"Components emit events via data-rs-action attributes. Listen for these in your interaction modules. Events bubble up through the DOM following standard browser conventions.\"</p>\n </section>\n </Stack>\n </ScrollArea>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "tabs",
"label": "Tabs",
"category": "Navigation",
"description": "Tabbed navigation",
"keywords": "",
"pain": "Tabs require manual state sync between triggers and panels",
"promise": "Active state governs trigger and content without manual wiring",
"why": "TabsPrimitive uses ActivityState to synchronize triggers and panels. ARIA roles and visibility are derived automatically. This guarantees consistent tab behavior.\n",
"before": "// ❌ Typical\nview! {\n <button>\"Tab\"</button>\n <div>\"Content\"</div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Tabs>\n <TabsList>\n <TabsTrigger value=\"a\" active=true>\"Tab\"</TabsTrigger>\n </TabsList>\n <TabsContent value=\"a\" active=true>\"Content\"</TabsContent>\n </Tabs>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"panel navigation",
"settings"
],
"related": [
"table_of_contents"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "tabs",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Tabs Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::{ActivityState, DisabledState};\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum TabsOrientation {\n #[default]\n Horizontal,\n Vertical,\n}\nimpl TabsOrientation {\n pub fn as_str(&self) -> &'static str {\n match self { Self::Horizontal => \"horizontal\", Self::Vertical => \"vertical\" }\n }\n}\n\n\n#[component]\npub fn TabsPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(optional)] node_ref: Option<NodeRef<leptos::html::Div>>,\n) -> impl IntoView {\n let uid_tab = crate::infra::uid::generate(\"tab\");\n view! {\n <div\n data-rs-tabs=\"\"\n data-rs-uid=uid_tab\n data-rs-interaction=\"nav\"\n class=class\n node_ref=node_ref.unwrap_or_default()\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn TabsListPrimitive(\n children: Children,\n #[prop(default = TabsOrientation::Horizontal)] orientation: TabsOrientation,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-tabs-list=\"\"\n role=\"tablist\"\n aria-orientation=orientation.as_str()\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn TabsTriggerPrimitive(\n children: Children,\n #[prop(into)] value: String,\n #[prop(default = ActivityState::Inactive)] active: ActivityState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let is_disabled = disabled == DisabledState::Disabled;\n view! {\n <button\n type=\"button\"\n role=\"tab\"\n data-rs-tabs-trigger=\"\"\n data-rs-value=value\n data-rs-activity=active.as_str()\n data-rs-disabled=if is_disabled { Some(\"disabled\") } else { None }\n aria-selected=active.aria_selected()\n aria-disabled=if is_disabled { \"true\" } else { \"false\" }\n class=class\n >\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn TabsContentPrimitive(\n children: Children,\n #[prop(into)] value: String,\n #[prop(default = ActivityState::Inactive)] active: ActivityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-tabs-content=\"\"\n data-rs-value=value\n data-rs-activity=active.as_str()\n role=\"tabpanel\"\n hidden=active.hidden()\n class=class\n >\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::meta::{ActivityState, DisabledState};\nuse canonrs_core::primitives::{\n TabsPrimitive, TabsListPrimitive,\n TabsTriggerPrimitive, TabsContentPrimitive,\n};\n\n#[component]\npub fn Tabs(\n children: Children,\n #[prop(optional)] node_ref: Option<NodeRef<leptos::html::Div>>,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] default_value: String,\n) -> impl IntoView {\n view! {\n <TabsPrimitive class=class attr:data-rs-default-tab=default_value node_ref=node_ref.unwrap_or_default()>\n {children()}\n </TabsPrimitive>\n }\n}\n\n#[component]\npub fn TabsList(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <TabsListPrimitive class=class>\n {children()}\n </TabsListPrimitive>\n }\n}\n\n#[component]\npub fn TabsTrigger(\n children: Children,\n #[prop(into)] value: String,\n #[prop(default = ActivityState::Inactive)] active: ActivityState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <TabsTriggerPrimitive value=value active=active disabled=disabled class=class>\n {children()}\n </TabsTriggerPrimitive>\n }\n}\n\n#[component]\npub fn TabsContent(\n children: Children,\n #[prop(into)] value: String,\n #[prop(default = ActivityState::Inactive)] active: ActivityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <TabsContentPrimitive value=value active=active class=class>\n {children()}\n </TabsContentPrimitive>\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! Tabs Boundary — Canon Rule #341 (zero-logic boundary)\n//! CR-342 v4.0.0: interaction delegated to canonrs-interactions-nav\n\nuse leptos::prelude::*;\nuse super::tabs_ui::{Tabs, TabsList, TabsTrigger as TabsTriggerUi, TabsContent as TabsContentUi};\nuse canonrs_core::meta::{ActivityState, DisabledState};\n\n#[component]\npub fn TabsRoot(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] default_value: String,\n) -> impl IntoView {\n view! { <Tabs class=class default_value=default_value>{children()}</Tabs> }\n}\n\n#[component]\npub fn TabsListBoundary(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <TabsList class=class>{children()}</TabsList> }\n}\n\n#[component]\npub fn TabsTrigger(\n children: Children,\n #[prop(into)] value: String,\n #[prop(default = ActivityState::Inactive)] active: ActivityState,\n #[prop(default = false)] disabled: bool,\n) -> impl IntoView {\n let dis = if disabled { DisabledState::Disabled } else { DisabledState::Enabled };\n view! { <TabsTriggerUi value=value active=active disabled=dis>{children()}</TabsTriggerUi> }\n}\n\n#[component]\npub fn TabsContent(\n children: Children,\n #[prop(into)] value: String,\n #[prop(default = ActivityState::Inactive)] active: ActivityState,\n) -> impl IntoView {\n view! { <TabsContentUi value=value active=active>{children()}</TabsContentUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const TABSROOT_API: ComponentApi = ComponentApi {\n id: \"tabs-root\",\n description: \"Tabbed navigation\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n PropDef { name: \"default_value\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Default selected tab value\" },\n ],\n};\n\npub const TABSLISTBOUNDARY_API: ComponentApi = ComponentApi {\n id: \"tabs-list-boundary\",\n description: \"Tabbed navigation\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const TABSTRIGGER_API: ComponentApi = ComponentApi {\n id: \"tabs-trigger\",\n description: \"Tabbed navigation\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"value\", kind: PropType::String, required: true, default: None, description: \"Current value\" },\n PropDef { name: \"active\", kind: PropType::String, required: false, default: Some(\"inactive\"), description: \"Active/selected state\" },\n PropDef { name: \"disabled\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Whether the component is disabled\" },\n ],\n};\n\npub const TABSCONTENT_API: ComponentApi = ComponentApi {\n id: \"tabs-content\",\n description: \"Tabbed navigation\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"value\", kind: PropType::String, required: true, default: None, description: \"Current value\" },\n PropDef { name: \"active\", kind: PropType::String, required: false, default: Some(\"inactive\"), description: \"Active/selected state\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::tabs_boundary::{TabsRoot, TabsListBoundary, TabsTrigger, TabsContent};\n\n#[component]\npub fn TabsShowcasePreview() -> impl IntoView {\n view! {\n <div data-rs-showcase-preview-hero=\"\">\n <div data-rs-showcase-preview-stage=\"\">\n <TabsRoot>\n <TabsListBoundary>\n <TabsTrigger value=\"overview\">\"Overview\"</TabsTrigger>\n <TabsTrigger value=\"api\">\"API\"</TabsTrigger>\n <TabsTrigger value=\"examples\">\"Examples\"</TabsTrigger>\n </TabsListBoundary>\n <TabsContent value=\"overview\">\"Overview content — structure drives state.\"</TabsContent>\n <TabsContent value=\"api\">\"API reference content.\"</TabsContent>\n <TabsContent value=\"examples\">\"Examples content.\"</TabsContent>\n </TabsRoot>\n </div>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Tab selection governed by DOM — SSR-safe, hydration-safe.\"\n </p>\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"With disabled tab\"</span>\n <div data-rs-showcase-preview-row=\"\">\n <TabsRoot>\n <TabsListBoundary>\n <TabsTrigger value=\"x\">\"Active\"</TabsTrigger>\n <TabsTrigger value=\"y\" disabled=true>\"Disabled\"</TabsTrigger>\n <TabsTrigger value=\"z\">\"Normal\"</TabsTrigger>\n </TabsListBoundary>\n <TabsContent value=\"x\">\"Active content.\"</TabsContent>\n <TabsContent value=\"y\">\"Disabled content.\"</TabsContent>\n <TabsContent value=\"z\">\"Normal content.\"</TabsContent>\n </TabsRoot>\n </div>\n </div>\n </div>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "textarea",
"label": "Textarea",
"category": "Form",
"description": "Multi-line text input",
"keywords": "",
"pain": "Textarea misses required, readonly and aria attributes consistency",
"promise": "All form states mapped directly to DOM and ARIA",
"why": "TextareaPrimitive encodes disabled, readonly and required into both DOM and ARIA attributes. Label and description linkage is explicit. This guarantees accessible multi-line input behavior.\n",
"before": "// ❌ Typical\nview! {\n <textarea placeholder=\"Type...\"></textarea>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Textarea placeholder=\"Type...\" required=true />\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"comments",
"descriptions"
],
"related": [
"form",
"input",
"input_group",
"input_otp",
"field",
"label",
"checkbox",
"form_error_summary"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "form",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Textarea Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::DisabledState;\n\n#[component]\npub fn TextareaPrimitive(\n #[prop(into, default = String::new())] class: String,\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] placeholder: String,\n #[prop(into, default = String::new())] name: String,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(default = false)] readonly: bool,\n #[prop(default = false)] required: bool,\n #[prop(into, optional)] aria_labelledby: Option<String>,\n #[prop(into, optional)] aria_describedby: Option<String>,\n #[prop(optional)] rows: Option<u32>,\n) -> impl IntoView {\n let uid_ta2 = crate::infra::uid::generate(\"ta2\");\n view! {\n <textarea\n data-rs-textarea=\"\"\n data-rs-uid=uid_ta2\n data-rs-interaction=\"init\"\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n data-rs-readonly={if readonly { Some(\"\") } else { None }}\n data-rs-required={if required { Some(\"\") } else { None }}\n prop:value=value\n placeholder={if placeholder.is_empty() { None } else { Some(placeholder) }}\n name={if name.is_empty() { None } else { Some(name) }}\n disabled=disabled.disabled()\n aria-disabled=disabled.aria_disabled()\n readonly=readonly\n aria-readonly={if readonly { Some(\"true\") } else { None }}\n aria-required={if required { Some(\"true\") } else { None }}\n aria-labelledby=aria_labelledby\n aria-describedby=aria_describedby\n rows=rows\n class=class\n />\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::TextareaPrimitive;\nuse canonrs_core::meta::DisabledState;\n\n#[component]\npub fn Textarea(\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] placeholder: String,\n #[prop(into, default = String::new())] name: String,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(default = false)] readonly: bool,\n #[prop(default = false)] required: bool,\n #[prop(optional)] aria_labelledby: Option<String>,\n #[prop(optional)] aria_describedby: Option<String>,\n #[prop(optional)] rows: Option<u32>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <TextareaPrimitive\n value=value\n placeholder=placeholder\n name=name\n disabled=disabled\n readonly=readonly\n required=required\n aria_labelledby=aria_labelledby.unwrap_or_default()\n aria_describedby=aria_describedby.unwrap_or_default()\n rows=rows.unwrap_or(3)\n class=class\n />\n }\n}\n\n",
"boundary_src": "//! Textarea Island — Canon Rule #340\n//! Passthrough only. Zero logic, zero transformation.\n\nuse leptos::prelude::*;\nuse super::textarea_ui::Textarea as TextareaUi;\nuse canonrs_core::meta::DisabledState;\n\n#[component]\npub fn Textarea(\n #[prop(into, default = String::new())] value: String,\n #[prop(into, default = String::new())] placeholder: String,\n #[prop(into, default = String::new())] name: String,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(default = false)] readonly: bool,\n #[prop(default = false)] required: bool,\n #[prop(into, default = String::new())] aria_labelledby: String,\n #[prop(into, default = String::new())] aria_describedby: String,\n #[prop(default = 3u32)] rows: u32,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <TextareaUi\n value=value\n placeholder=placeholder\n name=name\n disabled=disabled\n readonly=readonly\n required=required\n aria_labelledby=aria_labelledby\n aria_describedby=aria_describedby\n rows=rows\n class=class\n />\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const TEXTAREA_API: ComponentApi = ComponentApi {\n id: \"textarea\",\n description: \"Multi-line text input\",\n props: &[\n PropDef { name: \"value\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Current value\" },\n PropDef { name: \"placeholder\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Placeholder text\" },\n PropDef { name: \"name\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Form field name\" },\n PropDef { name: \"disabled\", kind: PropType::String, required: false, default: Some(\"enabled\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"readonly\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"required\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"aria_labelledby\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Prop value\" },\n PropDef { name: \"aria_describedby\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Prop value\" },\n PropDef { name: \"rows\", kind: PropType::Number, required: false, default: Some(\"3u32\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use super::textarea_boundary::Textarea;\nuse leptos::prelude::*;\nuse canonrs_core::meta::DisabledState;\n\n#[component]\npub fn TextareaShowcasePreview() -> impl IntoView {\n view! {\n <div data-rs-showcase-preview-hero=\"\">\n <div data-rs-showcase-preview-stage=\"\">\n <Textarea placeholder=\"Type here...\" rows=3 />\n </div>\n <p data-rs-showcase-preview-anchor=\"\">\n \"All form states mapped directly to DOM and ARIA.\"\n </p>\n\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"Variants\"</span>\n <div data-rs-preview-dev-grid=\"\">\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"Default\"</span>\n <Textarea placeholder=\"Default\" rows=2 />\n </div>\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"Success\"</span>\n <Textarea placeholder=\"Success\" rows=2 />\n </div>\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"Warning\"</span>\n <Textarea placeholder=\"Warning\" rows=2 />\n </div>\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"Error\"</span>\n <Textarea placeholder=\"Error\" rows=2 />\n </div>\n </div>\n </div>\n\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"Rows\"</span>\n <div data-rs-preview-dev-grid=\"\">\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"2 rows\"</span>\n <Textarea placeholder=\"2 rows\" rows=2 />\n </div>\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"5 rows\"</span>\n <Textarea placeholder=\"5 rows\" rows=5 />\n </div>\n </div>\n </div>\n\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"States\"</span>\n <div data-rs-preview-dev-grid=\"\">\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"Readonly\"</span>\n <Textarea placeholder=\"Readonly\" readonly=true rows=2 />\n </div>\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"Disabled\"</span>\n <Textarea placeholder=\"Disabled\" disabled=DisabledState::Disabled rows=2 />\n </div>\n </div>\n </div>\n\n </div>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "toast",
"label": "Toast",
"category": "Feedback",
"description": "Toast notification message",
"keywords": "",
"pain": "Notifications misuse urgency, role and lifecycle behavior",
"promise": "Variant enforces correct role and aria-live automatically",
"why": "ToastVariant defines role and aria-live behavior per type. Lifecycle and visibility are encoded structurally. This guarantees correct notification semantics and timing.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"toast error\">\"Error\"</div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Toast variant=ToastVariant::Error>\n <ToastTitle>\"Error\"</ToastTitle>\n </Toast>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"system notifications",
"user feedback"
],
"related": [
"alert",
"banner",
"callout",
"inline_notice",
"status_dot"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "feedback",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Toast Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::VisibilityState;\n\n#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Default)]\npub enum ToastVariant {\n #[default]\n Default,\n Success,\n Warning,\n Error,\n Info,\n}\n\nimpl ToastVariant {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Default => \"default\",\n Self::Success => \"success\",\n Self::Warning => \"warning\",\n Self::Error => \"error\",\n Self::Info => \"info\",\n }\n }\n pub fn aria_live(&self) -> &'static str {\n match self { Self::Error | Self::Warning => \"assertive\", _ => \"polite\" }\n }\n pub fn role(&self) -> &'static str {\n match self { Self::Error | Self::Warning => \"alert\", _ => \"status\" }\n }\n}\n\n#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Default)]\npub enum ToastLifecycle {\n #[default]\n Open,\n Closing,\n Closed,\n}\n\nimpl ToastLifecycle {\n pub fn as_str(&self) -> &'static str {\n match self { Self::Open => \"open\", Self::Closing => \"closing\", Self::Closed => \"closed\" }\n }\n}\n\n#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Default)]\npub enum ToastPosition {\n #[default]\n TopRight,\n TopLeft,\n BottomRight,\n BottomLeft,\n TopCenter,\n BottomCenter,\n}\n\nimpl ToastPosition {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::TopRight => \"top-right\",\n Self::TopLeft => \"top-left\",\n Self::BottomRight => \"bottom-right\",\n Self::BottomLeft => \"bottom-left\",\n Self::TopCenter => \"top-center\",\n Self::BottomCenter => \"bottom-center\",\n }\n }\n}\n\n#[component]\npub fn ToastPrimitive(\n children: Children,\n #[prop(default = ToastVariant::Default)] variant: ToastVariant,\n #[prop(default = VisibilityState::Open)] state: VisibilityState,\n #[prop(default = ToastLifecycle::Open)] lifecycle: ToastLifecycle,\n #[prop(into, optional)] title_id: Option<String>,\n #[prop(into, optional)] description_id: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_ts = crate::infra::uid::generate(\"ts\");\n view! {\n <div\n data-rs-toast=\"\"\n data-rs-uid=uid_ts\n data-rs-interaction=\"init\"\n data-rs-variant=variant.as_str()\n data-rs-state=state.as_str()\n data-rs-lifecycle=lifecycle.as_str()\n role=variant.role()\n aria-live=variant.aria_live()\n aria-atomic=\"true\"\n aria-labelledby=title_id\n aria-describedby=description_id\n hidden=state.hidden()\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ToastViewportPrimitive(\n children: Children,\n #[prop(default = ToastPosition::TopRight)] position: ToastPosition,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-toast-viewport=\"\"\n data-rs-position=position.as_str()\n aria-label=\"Notifications\"\n role=\"region\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ToastTitlePrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-toast-title=\"\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ToastDescriptionPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-toast-description=\"\" class=class>\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ToastActionPrimitive(\n children: Children,\n #[prop(into)] aria_label: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-toast-action=\"\"\n aria-label=aria_label\n class=class\n >\n {children()}\n </button>\n }\n}\n\n#[component]\npub fn ToastClosePrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <button\n type=\"button\"\n data-rs-toast-close=\"\"\n aria-label=\"Close notification\"\n class=class\n >\n {children()}\n </button>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n ToastPrimitive, ToastViewportPrimitive,\n ToastTitlePrimitive, ToastDescriptionPrimitive,\n ToastActionPrimitive, ToastClosePrimitive,\n};\npub use canonrs_core::primitives::ToastVariant;\n\n#[component]\npub fn Toast(\n children: Children,\n #[prop(default = ToastVariant::Default)] variant: ToastVariant,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ToastPrimitive variant=variant class=class>\n {children()}\n </ToastPrimitive>\n }\n}\n\n#[component]\npub fn ToastViewport(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ToastViewportPrimitive class=class>\n {children()}\n </ToastViewportPrimitive>\n }\n}\n\n#[component]\npub fn ToastTitle(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ToastTitlePrimitive class=class>\n {children()}\n </ToastTitlePrimitive>\n }\n}\n\n#[component]\npub fn ToastDescription(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ToastDescriptionPrimitive class=class>\n {children()}\n </ToastDescriptionPrimitive>\n }\n}\n\n#[component]\npub fn ToastAction(\n children: Children,\n #[prop(into, default = String::new())] aria_label: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ToastActionPrimitive aria_label=aria_label class=class>\n {children()}\n </ToastActionPrimitive>\n }\n}\n\n#[component]\npub fn ToastClose(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ToastClosePrimitive class=class>\n {children()}\n </ToastClosePrimitive>\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! Toast Island — Canon Rule #340 (zero-logic boundary)\n\nuse leptos::prelude::*;\nuse super::toast_ui::{\n Toast as ToastUi,\n ToastViewport as ToastViewportUi\n};\npub use canonrs_core::primitives::ToastVariant;\n\n#[allow(unused_variables)]\n#[component]\npub fn Toast(\n #[prop(into, optional)] title: Option<String>,\n #[prop(into, optional)] description: Option<String>,\n #[prop(default = ToastVariant::Default)] variant: ToastVariant,\n #[prop(default = 5000u32)] duration_ms: u32,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ToastUi variant=variant class=class>\n {title.map(|t| view! { <p data-rs-toast-title=\"\">{t}</p> })}\n {description.map(|d| view! { <p data-rs-toast-description=\"\">{d}</p> })}\n </ToastUi>\n }\n}\n\n#[component]\npub fn ToastViewport(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <ToastViewportUi class=class>{children()}</ToastViewportUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{ToastVariant}; \n\npub const TOAST_API: ComponentApi = ComponentApi {\n id: \"toast\",\n description: \"Toast notification message\",\n props: &[\n PropDef { name: \"title\", kind: PropType::String, required: false, default: None, description: \"Title slot or text\" },\n PropDef { name: \"description\", kind: PropType::String, required: false, default: None, description: \"Description slot or text\" },\n PropDef { name: \"variant\", kind: PropType::Enum(&[\"default\", \"success\", \"warning\", \"error\", \"info\"]), required: false, default: Some(\"default\"), description: \"Visual variant of the component\" },\n PropDef { name: \"duration_ms\", kind: PropType::Number, required: false, default: Some(\"5000u32\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const TOASTVIEWPORT_API: ComponentApi = ComponentApi {\n id: \"toast-viewport\",\n description: \"Toast notification message\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::toast_boundary::{Toast, ToastVariant};\n\n#[component]\npub fn ToastShowcasePreview() -> impl IntoView {\n view! {\n <div data-rs-showcase-preview-hero=\"\">\n <div data-rs-showcase-preview-stage=\"\">\n <div style=\"display:flex;flex-direction:column;gap:var(--space-sm);\">\n <Toast title=\"Notification\" description=\"Your settings have been saved.\" duration_ms=60000 />\n <Toast variant=ToastVariant::Success title=\"Success\" description=\"File uploaded.\" duration_ms=60000 />\n <Toast variant=ToastVariant::Error title=\"Error\" description=\"Something went wrong.\" duration_ms=60000 />\n <Toast variant=ToastVariant::Warning title=\"Warning\" description=\"Session expires soon.\" duration_ms=60000 />\n </div>\n </div>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Variant enforces correct role and aria-live automatically.\"\n </p>\n </div>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "toggle",
"label": "Toggle",
"category": "Form",
"description": "Toggle button",
"keywords": "",
"pain": "Toggle buttons desync pressed state and visual representation",
"promise": "Pressed state mapped directly to DOM and interaction state",
"why": "TogglePrimitive maps ToggleState to data-rs-state and checkbox checked state. Disabled and aria-label are enforced. This guarantees consistent toggle behavior.\n",
"before": "// ❌ Typical\nview! {\n <button class=\"active\">\"On\"</button>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Toggle pressed=true>\"On\"</Toggle>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"formatting tools",
"feature toggles"
],
"related": [
"switch",
"toggle_group"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "toggle",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Toggle Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::{ToggleState, DisabledState};\n\n\n#[component]\npub fn TogglePrimitive(\n children: Children,\n #[prop(default = ToggleState::Off)] pressed: ToggleState,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] aria_label: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_tog = crate::infra::uid::generate(\"tog\");\n view! {\n <label\n data-rs-toggle=\"\"\n data-rs-uid=uid_tog\n data-rs-interaction=\"init\"\n data-rs-pressed=pressed.as_str()\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n aria-label=if aria_label.is_empty() { None } else { Some(aria_label) }\n aria-disabled=disabled.aria_disabled()\n class=class\n >\n <input\n type=\"checkbox\"\n data-rs-toggle-input=\"\"\n checked=pressed.as_str() == \"on\"\n tabindex=\"-1\"\n />\n <span data-rs-toggle-content=\"\">\n {children()}\n </span>\n </label>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::TogglePrimitive;\n\n#[component]\npub fn Toggle(children: Children, #[prop(into, default = String::new())] class: String, #[prop(default = false)] pressed: bool, #[prop(default = false)] disabled: bool, #[prop(into, default = String::new())] aria_label: String) -> impl IntoView {\n view! {\n <TogglePrimitive class=class pressed=pressed.into() disabled=disabled.into() aria_label=aria_label>\n {children()}\n </TogglePrimitive>\n }\n}\n",
"boundary_src": "//! @canon-level: strict\n//! Toggle Island — Canon Rule #340 (zero-logic boundary)\n\nuse leptos::prelude::*;\nuse super::toggle_ui::Toggle as ToggleUi;\n\n#[component]\npub fn Toggle(\n children: Children,\n #[prop(default = false)] pressed: bool,\n #[prop(default = false)] disabled: bool,\n #[prop(into, default = String::new())] aria_label: String,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ToggleUi pressed=pressed disabled=disabled aria_label=aria_label class=class>\n {children()}\n </ToggleUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const TOGGLE_API: ComponentApi = ComponentApi {\n id: \"toggle\",\n description: \"Toggle button\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"pressed\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"disabled\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"aria_label\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Accessible label for screen readers\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::toggle_boundary::Toggle;\n\n#[component]\npub fn ToggleShowcasePreview() -> impl IntoView {\n view! {\n <div data-rs-showcase-preview-hero=\"\">\n <div data-rs-showcase-preview-stage=\"\">\n <Toggle><span>{\"Bold\"}</span></Toggle>\n <Toggle pressed=true><span>{\"Italic\"}</span></Toggle>\n <Toggle><span>{\"Underline\"}</span></Toggle>\n </div>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Pressed state mapped directly to DOM and interaction state.\"\n </p>\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"States\"</span>\n <div data-rs-showcase-preview-row=\"\">\n <Toggle><span>{\"Unpressed\"}</span></Toggle>\n <Toggle pressed=true><span>{\"Pressed\"}</span></Toggle>\n <Toggle disabled=true><span>{\"Disabled\"}</span></Toggle>\n <Toggle pressed=true disabled=true><span>{\"Disabled On\"}</span></Toggle>\n </div>\n </div>\n </div>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "toggle_group",
"label": "Toggle Group",
"category": "Action",
"description": "Group of toggle buttons",
"keywords": "",
"pain": "Grouped toggles lack exclusivity and shared disabled state",
"promise": "Group behavior and selection mode enforced structurally",
"why": "ToggleGroupPrimitive encodes multiple selection and disabled state at container level. Child toggles inherit behavior. This guarantees consistent grouped interactions.\n",
"before": "// ❌ Typical\nview! {\n <div>\n <button>\"A\"</button>\n <button>\"B\"</button>\n </div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <ToggleGroup multiple=false>\n <Toggle>\"A\"</Toggle>\n </ToggleGroup>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"toolbars",
"option groups"
],
"related": [
"switch",
"toggle"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "toggle",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! ToggleGroup Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::DisabledState;\n\n#[component]\npub fn ToggleGroupPrimitive(\n children: Children,\n #[prop(default = false)] multiple: bool,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(into, default = String::new())] class: String,\n #[prop(optional)] node_ref: Option<NodeRef<leptos::html::Div>>,\n) -> impl IntoView {\n let uid_tg = crate::infra::uid::generate(\"tg\");\n let is_disabled = disabled == DisabledState::Disabled;\n\n view! {\n <div\n data-rs-toggle-group=\"\"\n data-rs-uid=uid_tg\n data-rs-interaction=\"selection\"\n data-rs-multiple=if multiple { \"true\" } else { \"false\" }\n data-rs-disabled=if is_disabled { Some(\"disabled\") } else { None }\n role=\"group\"\n aria-disabled=if is_disabled { \"true\" } else { \"false\" }\n class=class\n node_ref=node_ref.unwrap_or_default()\n >\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::meta::DisabledState;\nuse canonrs_core::primitives::ToggleGroupPrimitive;\n\n#[component]\npub fn ToggleGroup(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(default = false)] multiple: bool,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(optional)] node_ref: Option<NodeRef<leptos::html::Div>>,\n) -> impl IntoView {\n view! {\n <ToggleGroupPrimitive class=class multiple=multiple disabled=disabled node_ref=node_ref.unwrap_or_default()>\n {children()}\n </ToggleGroupPrimitive>\n }\n}\n",
"boundary_src": "//! ToggleGroup Island — Canon Rule passthrough\nuse leptos::prelude::*;\nuse super::toggle_group_ui::ToggleGroup as ToggleGroupUi;\n\n#[component]\npub fn ToggleGroup(\n children: Children,\n #[prop(default = false)] multiple: bool,\n #[prop(default = false)] disabled: bool,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let state = if disabled { \"disabled\"\n} else { \"\" };\n view! {\n <ToggleGroupUi multiple=multiple class=class attr:data-rs-state=state>\n {children()}\n </ToggleGroupUi>\n }\n}\n\n#[component]\npub fn ToggleGroupItem(\n children: Children,\n #[prop(into, default = String::new())] value: String,\n #[prop(default = false)] on: bool,\n #[prop(default = false)] disabled: bool,\n) -> impl IntoView {\n let state = if disabled {\n format!(\"{} disabled\", if on { \"on\" } else { \"off\" })\n } else {\n if on { \"on\".into() } else { \"off\".into() }\n };\n view! {\n <button\n type=\"button\"\n data-rs-toggle=\"\"\n data-rs-component=\"Toggle\"\n data-rs-value=value\n data-rs-state=state\n aria-pressed=if on { \"true\" } else { \"false\" }\n role=\"button\"\n >\n {children()}\n </button>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const TOGGLEGROUP_API: ComponentApi = ComponentApi {\n id: \"toggle-group\",\n description: \"Group of toggle buttons\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"multiple\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"disabled\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Whether the component is disabled\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const TOGGLEGROUPITEM_API: ComponentApi = ComponentApi {\n id: \"toggle-group-item\",\n description: \"Group of toggle buttons\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"value\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Current value\" },\n PropDef { name: \"on\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"disabled\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Whether the component is disabled\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::toggle_group_boundary::{ToggleGroup, ToggleGroupItem};\n\n#[component]\npub fn ToggleGroupShowcasePreview() -> impl IntoView {\n view! {\n <div data-rs-showcase-preview-hero=\"\">\n <div data-rs-showcase-preview-stage=\"\">\n <ToggleGroup>\n <ToggleGroupItem value=\"left\" on=true>\"Left\"</ToggleGroupItem>\n <ToggleGroupItem value=\"center\">\"Center\"</ToggleGroupItem>\n <ToggleGroupItem value=\"right\">\"Right\"</ToggleGroupItem>\n </ToggleGroup>\n </div>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Group behavior and selection mode enforced structurally.\"\n </p>\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"Multiple selection\"</span>\n <div data-rs-showcase-preview-row=\"\">\n <ToggleGroup multiple=true>\n <ToggleGroupItem value=\"bold\" on=true>\"Bold\"</ToggleGroupItem>\n <ToggleGroupItem value=\"italic\" on=true>\"Italic\"</ToggleGroupItem>\n <ToggleGroupItem value=\"underline\">\"Underline\"</ToggleGroupItem>\n <ToggleGroupItem value=\"strike\">\"Strike\"</ToggleGroupItem>\n </ToggleGroup>\n </div>\n </div>\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"Disabled\"</span>\n <div data-rs-showcase-preview-row=\"\">\n <ToggleGroup disabled=true>\n <ToggleGroupItem value=\"a\">\"Option A\"</ToggleGroupItem>\n <ToggleGroupItem value=\"b\">\"Option B\"</ToggleGroupItem>\n </ToggleGroup>\n </div>\n </div>\n </div>\n }\n}\n",
"block": [],
"blocks_primitives": [
"flex",
"stack"
]
},
{
"id": "toolbar",
"label": "Toolbar",
"category": "Layout",
"description": "Action toolbar component",
"keywords": "",
"pain": "Action groups lack orientation and accessibility semantics",
"promise": "Toolbar role and orientation enforced via contract",
"why": "ToolbarPrimitive encodes orientation and role=\"toolbar\". ARIA labeling is explicit. This guarantees accessible grouping of actions.\n",
"before": "// ❌ Typical\nview! {\n <div class=\"toolbar\">\n <button>\"Bold\"</button>\n </div>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Toolbar aria_label=\"Editor\">\n <button>\"Bold\"</button>\n </Toolbar>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"editors",
"action bars"
],
"related": [
"card",
"resizable",
"scroll_area",
"aspect_ratio",
"page_header",
"separator"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift"
],
"pillar": "layout",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Toolbar Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum ToolbarOrientation {\n #[default]\n Horizontal,\n Vertical,\n}\n\nimpl ToolbarOrientation {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Horizontal => \"horizontal\",\n Self::Vertical => \"vertical\",\n }\n }\n}\n\n#[component]\npub fn ToolbarPrimitive(\n children: Children,\n #[prop(into, default = String::new())] uid: String,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, optional)] aria_label: Option<String>,\n #[prop(default = ToolbarOrientation::Horizontal)] orientation: ToolbarOrientation,\n) -> impl IntoView {\n let uid = if uid.is_empty() { crate::infra::uid::generate(\"tb2\") } else { uid };\n view! {\n <div\n data-rs-toolbar=\"\"\n data-rs-uid=uid\n data-rs-interaction=\"nav\"\n data-rs-variant=orientation.as_str()\n role=\"toolbar\"\n aria-label=aria_label\n aria-orientation=orientation.as_str()\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ToolbarSeparatorPrimitive(\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-toolbar-separator=\"\"\n role=\"separator\"\n aria-orientation=\"vertical\"\n class=class\n />\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{ToolbarPrimitive, ToolbarSeparatorPrimitive, ToolbarOrientation};\n\n#[component]\npub fn Toolbar(\n children: Children,\n #[prop(into, default = String::new())] uid: String,\n #[prop(into)] aria_label: String,\n #[prop(default = ToolbarOrientation::Horizontal)] orientation: ToolbarOrientation,\n #[prop(default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ToolbarPrimitive\n uid=uid\n class=class\n aria_label=aria_label\n orientation=orientation\n >\n {children()}\n </ToolbarPrimitive>\n }\n}\n\n#[component]\npub fn ToolbarSeparator(\n #[prop(default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ToolbarSeparatorPrimitive class=class />\n }\n}\n",
"boundary_src": "//! Toolbar Island — Canon Rule passthrough\nuse leptos::prelude::*;\nuse super::toolbar_ui::{\n Toolbar as ToolbarUi,\n ToolbarSeparator as ToolbarSeparatorUi\n};\npub use canonrs_core::primitives::ToolbarOrientation;\n\n#[component]\npub fn Toolbar(\n children: Children,\n #[prop(into, default = String::new())] uid: String,\n #[prop(into, default = String::from(\"Toolbar\"))] aria_label: String,\n #[prop(default = ToolbarOrientation::Horizontal)] orientation: ToolbarOrientation,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <ToolbarUi uid=uid aria_label=aria_label orientation=orientation class=class>\n {children()}\n </ToolbarUi>\n }\n}\n\n#[component]\npub fn ToolbarItem(\n children: Children,\n #[prop(into, default = String::new())] value: String,\n #[prop(default = false)] disabled: bool,\n) -> impl IntoView {\n view! {\n <div\n data-rs-toolbar-item=\"\"\n data-rs-value=value\n tabindex=\"-1\"\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn ToolbarSeparator() -> impl IntoView {\n view! { <ToolbarSeparatorUi /> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{ToolbarOrientation}; \n\npub const TOOLBAR_API: ComponentApi = ComponentApi {\n id: \"toolbar\",\n description: \"Action toolbar component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"uid\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Prop value\" },\n PropDef { name: \"aria_label\", kind: PropType::String, required: false, default: Some(\"Toolbar\"), description: \"Accessible label for screen readers\" },\n PropDef { name: \"orientation\", kind: PropType::Enum(&[\"horizontal\", \"vertical\"]), required: false, default: Some(\"horizontal\"), description: \"Horizontal or vertical orientation\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const TOOLBARITEM_API: ComponentApi = ComponentApi {\n id: \"toolbar-item\",\n description: \"Action toolbar component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"value\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Current value\" },\n PropDef { name: \"disabled\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Whether the component is disabled\" },\n ],\n};\n\npub const TOOLBARSEPARATOR_API: ComponentApi = ComponentApi {\n id: \"toolbar-separator\",\n description: \"Action toolbar component\",\n props: &[\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::toolbar_boundary::{Toolbar, ToolbarItem, ToolbarSeparator, ToolbarOrientation};\n\n#[component]\npub fn ToolbarShowcasePreview() -> impl IntoView {\n view! {\n <div data-rs-showcase-preview-hero=\"\">\n <div data-rs-showcase-preview-stage=\"\" style=\"width:100%;\">\n <Toolbar aria_label=\"Text formatting\">\n <ToolbarItem value=\"bold\">\"B\"</ToolbarItem>\n <ToolbarItem value=\"italic\">\"I\"</ToolbarItem>\n <ToolbarItem value=\"underline\">\"U\"</ToolbarItem>\n <ToolbarSeparator />\n <ToolbarItem value=\"left\">\"←\"</ToolbarItem>\n <ToolbarItem value=\"center\">\"↔\"</ToolbarItem>\n <ToolbarItem value=\"right\">\"→\"</ToolbarItem>\n <ToolbarSeparator />\n <ToolbarItem value=\"link\">\"🔗\"</ToolbarItem>\n </Toolbar>\n </div>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Toolbar role and orientation enforced via contract.\"\n </p>\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"Vertical\"</span>\n <div data-rs-showcase-preview-row=\"\">\n <Toolbar aria_label=\"Actions\" orientation=ToolbarOrientation::Vertical>\n <ToolbarItem value=\"cut\">\"✂\"</ToolbarItem>\n <ToolbarItem value=\"copy\">\"⎘\"</ToolbarItem>\n <ToolbarItem value=\"paste\">\"📋\"</ToolbarItem>\n <ToolbarSeparator />\n <ToolbarItem value=\"delete\">\"🗑\"</ToolbarItem>\n </Toolbar>\n </div>\n </div>\n </div>\n }\n}\n",
"block": [],
"blocks_primitives": [
"flex"
]
},
{
"id": "tooltip",
"label": "Tooltip",
"category": "Overlay",
"description": "Hover tooltip",
"keywords": "",
"pain": "Tooltip requires id wiring and loses sync between trigger and content",
"promise": "Trigger-content relationship enforced without manual wiring",
"why": "TooltipPrimitive uses visibility state and DOM structure instead of ids. aria-describedby is optional but supported. This guarantees consistent overlay behavior.\n",
"before": "// ❌ Typical\nview! {\n <span title=\"Info\">\"Hover\"</span>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Tooltip>\n <TooltipTrigger>\"Hover\"</TooltipTrigger>\n <TooltipContent>\"Info\"</TooltipContent>\n </Tooltip>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"hints",
"help text"
],
"related": [
"dialog",
"alert_dialog",
"drawer",
"sheet",
"modal",
"confirm_dialog",
"hover_card",
"popover"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "overlay",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Tooltip Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::VisibilityState;\n\n#[derive(Debug, Clone, Copy, PartialEq, Default)]\npub enum TooltipSide {\n #[default]\n Top,\n Bottom,\n Left,\n Right,\n}\n\nimpl TooltipSide {\n pub fn as_str(&self) -> &'static str {\n match self {\n Self::Top => \"top\",\n Self::Bottom => \"bottom\",\n Self::Left => \"left\",\n Self::Right => \"right\",\n }\n }\n}\n\n\n#[component]\npub fn TooltipPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_tip = crate::infra::uid::generate(\"tip\");\n view! {\n <div\n data-rs-tooltip=\"\"\n data-rs-uid=uid_tip\n data-rs-interaction=\"init\"\n data-rs-state=state.as_str()\n class=class\n >\n {children()}\n </div>\n }\n}\n\n// Trigger é span — tooltip funciona em qualquer elemento\n#[component]\npub fn TooltipTriggerPrimitive(\n children: Children,\n #[prop(into, optional)] tooltip_id: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <span\n data-rs-tooltip-trigger=\"\"\n aria-describedby=tooltip_id\n tabindex=\"0\"\n class=class\n >\n {children()}\n </span>\n }\n}\n\n#[component]\npub fn TooltipContentPrimitive(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(default = TooltipSide::Top)] side: TooltipSide,\n #[prop(default = true)] arrow: bool,\n #[prop(into, optional)] tooltip_id: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-tooltip-content=\"\"\n data-rs-state=state.as_str()\n data-rs-side=side.as_str()\n id=tooltip_id\n role=\"tooltip\"\n class=class\n >\n {children()}\n {arrow.then(|| view! { <span data-rs-tooltip-arrow=\"\" /> })}\n </div>\n }\n}\n\n#[component]\npub fn TooltipProviderPrimitive(\n children: Children,\n #[prop(default = 400)] delay_open: u32,\n #[prop(default = 100)] delay_close: u32,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-tooltip-provider=\"\"\n data-rs-delay-open=delay_open.to_string()\n data-rs-delay-close=delay_close.to_string()\n class=class\n >\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n TooltipProviderPrimitive, TooltipPrimitive,\n TooltipTriggerPrimitive, TooltipContentPrimitive, TooltipSide,\n};\nuse canonrs_core::meta::VisibilityState;\n\n#[component]\npub fn TooltipProvider(\n children: Children,\n #[prop(default = 400)] delay_open: u32,\n #[prop(default = 100)] delay_close: u32,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <TooltipProviderPrimitive delay_open=delay_open delay_close=delay_close class=class>\n {children()}\n </TooltipProviderPrimitive>\n }\n}\n\n#[component]\npub fn Tooltip(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <TooltipPrimitive state=state class=class>\n {children()}\n </TooltipPrimitive>\n }\n}\n\n#[component]\npub fn TooltipTrigger(\n children: Children,\n #[prop(into, optional)] tooltip_id: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <TooltipTriggerPrimitive tooltip_id=tooltip_id.unwrap_or_default() class=class>\n {children()}\n </TooltipTriggerPrimitive>\n }\n}\n\n#[component]\npub fn TooltipContent(\n children: Children,\n #[prop(default = VisibilityState::Closed)] state: VisibilityState,\n #[prop(default = TooltipSide::Top)] side: TooltipSide,\n #[prop(into, optional)] tooltip_id: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <TooltipContentPrimitive state=state side=side tooltip_id=tooltip_id.unwrap_or_default() class=class>\n {children()}\n </TooltipContentPrimitive>\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! Tooltip Island — Canon Rule #340 (zero-logic boundary)\n//! CR-342 v3.0.0: interaction delegated to canonrs-interactions-overlay\n\nuse leptos::prelude::*;\nuse super::tooltip_ui::{\n TooltipProvider as TooltipProviderUi,\n Tooltip as TooltipUi,\n TooltipTrigger as TooltipTriggerUi,\n TooltipContent as TooltipContentUi\n};\npub use canonrs_core::primitives::TooltipSide;\n\n#[component]\npub fn TooltipProvider(\n children: Children,\n #[prop(default = 400)] delay_open: u32,\n #[prop(default = 100)] delay_close: u32,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <TooltipProviderUi delay_open=delay_open delay_close=delay_close class=class>{children()}</TooltipProviderUi> }\n}\n\n#[component]\npub fn Tooltip(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <TooltipUi class=class>{children()}</TooltipUi> }\n}\n\n#[component]\npub fn TooltipTrigger(\n children: Children,\n #[prop(into, optional)] tooltip_id: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let tooltip_id = tooltip_id.unwrap_or_default();\n view! { <TooltipTriggerUi tooltip_id=tooltip_id class=class>{children()}</TooltipTriggerUi> }\n}\n\n#[component]\npub fn TooltipContent(\n children: Children,\n #[prop(default = TooltipSide::Top)] side: TooltipSide,\n #[prop(into, optional)] tooltip_id: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let tooltip_id = tooltip_id.unwrap_or_default();\n view! { <TooltipContentUi side=side tooltip_id=tooltip_id class=class>{children()}</TooltipContentUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\n// imports: use canonrs::primitives::{TooltipSide}; \n\npub const TOOLTIPPROVIDER_API: ComponentApi = ComponentApi {\n id: \"tooltip-provider\",\n description: \"Hover tooltip\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"delay_open\", kind: PropType::Number, required: false, default: Some(\"400\"), description: \"Delay in ms before opening\" },\n PropDef { name: \"delay_close\", kind: PropType::Number, required: false, default: Some(\"100\"), description: \"Delay in ms before closing\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const TOOLTIP_API: ComponentApi = ComponentApi {\n id: \"tooltip\",\n description: \"Hover tooltip\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const TOOLTIPTRIGGER_API: ComponentApi = ComponentApi {\n id: \"tooltip-trigger\",\n description: \"Hover tooltip\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"tooltip_id\", kind: PropType::String, required: false, default: None, description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const TOOLTIPCONTENT_API: ComponentApi = ComponentApi {\n id: \"tooltip-content\",\n description: \"Hover tooltip\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"side\", kind: PropType::Enum(&[\"top\", \"bottom\", \"left\", \"right\"]), required: false, default: Some(\"top\"), description: \"Tooltip or popover side\" },\n PropDef { name: \"tooltip_id\", kind: PropType::String, required: false, default: None, description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::tooltip_boundary::{TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, TooltipSide};\n\n#[component]\npub fn TooltipShowcasePreview() -> impl IntoView {\n view! {\n <div data-rs-showcase-preview-hero=\"\">\n <div data-rs-showcase-preview-stage=\"\">\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger tooltip_id=\"tt-1\">\"Hover me\"</TooltipTrigger>\n <TooltipContent tooltip_id=\"tt-1\">\"Tooltip content\"</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n </div>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Tooltip appears on hover and focus with configurable delay.\"\n </p>\n <div data-rs-showcase-preview-section=\"\">\n <span data-rs-showcase-preview-label=\"\">\"Sides\"</span>\n <div data-rs-showcase-preview-row=\"\">\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger tooltip_id=\"tt-top\">\"Top\"</TooltipTrigger>\n <TooltipContent side=TooltipSide::Top tooltip_id=\"tt-top\">\"Opens above\"</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger tooltip_id=\"tt-bottom\">\"Bottom\"</TooltipTrigger>\n <TooltipContent side=TooltipSide::Bottom tooltip_id=\"tt-bottom\">\"Opens below\"</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n <TooltipProvider>\n <Tooltip>\n <TooltipTrigger tooltip_id=\"tt-right\">\"Right\"</TooltipTrigger>\n <TooltipContent side=TooltipSide::Right tooltip_id=\"tt-right\">\"Opens right\"</TooltipContent>\n </Tooltip>\n </TooltipProvider>\n </div>\n </div>\n </div>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "tree",
"label": "Tree",
"category": "Display",
"description": "Tree view component",
"keywords": "",
"pain": "Tree structures lose selection, expansion and focus consistency",
"promise": "Hierarchy state fully governed via structured attributes",
"why": "TreePrimitive and TreeItemPrimitive encode selection, focus and expansion via data attributes. ARIA roles are enforced. This guarantees accessible hierarchical navigation.\n",
"before": "// ❌ Typical\nview! {\n <ul>\n <li>\"Item\"</li>\n </ul>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <Tree>\n <TreeItem has_children=true>\"Item\"</TreeItem>\n </Tree>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"file explorers",
"nested navigation"
],
"related": [
"table",
"data_table",
"virtual_list",
"empty_table",
"list_item"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "data",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! Tree Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\nuse crate::meta::{SelectionState, DisabledState, ActivityState};\n\n#[derive(Clone, Copy, PartialEq, Default, Debug)]\npub enum TreeSelectionMode {\n #[default]\n Single,\n Multiple,\n}\nimpl TreeSelectionMode {\n pub fn as_str(&self) -> &'static str {\n match self { Self::Single => \"single\", Self::Multiple => \"multiple\" }\n }\n pub fn aria_multiselectable(&self) -> Option<&'static str> {\n match self { Self::Multiple => Some(\"true\"), Self::Single => None }\n }\n}\n\n\n\n\n\n\n\n\n\n\n#[component]\npub fn TreePrimitive(\n children: Children,\n #[prop(default = TreeSelectionMode::Single)] selection: TreeSelectionMode,\n #[prop(into, optional)] aria_label: Option<String>,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_tr = crate::infra::uid::generate(\"tr\");\n view! {\n <div\n data-rs-tree=\"\"\n data-rs-uid=uid_tr\n data-rs-interaction=\"selection\"\n data-rs-selection=selection.as_str()\n role=\"tree\"\n aria-label=aria_label\n aria-multiselectable=selection.aria_multiselectable()\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn TreeItemPrimitive(\n children: Children,\n #[prop(default = SelectionState::Unselected)] selected: SelectionState,\n #[prop(default = ActivityState::Inactive)] focused: ActivityState,\n #[prop(default = false)] expanded: bool,\n #[prop(default = DisabledState::Enabled)] disabled: DisabledState,\n #[prop(default = false)] has_children: bool,\n #[prop(default = 0u8)] depth: u8,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let tabindex = if focused.as_str() == \"active\" { \"0\" } else { \"-1\" };\n let depth_str = depth.to_string();\n view! {\n <div\n data-rs-tree-item=\"\"\n data-rs-selection=if selected == SelectionState::Selected { Some(\"selected\") } else { None }\n data-rs-focused=focused.as_str()\n data-rs-expanded={if has_children { Some(if expanded { \"true\" } else { \"false\" }) } else { None }}\n data-rs-disabled=if disabled.disabled() { Some(\"disabled\") } else { None }\n data-rs-depth=depth_str\n role=\"treeitem\"\n tabindex=tabindex\n aria-selected=if selected == SelectionState::Selected { Some(\"true\") } else { None }\n aria-expanded={if has_children { Some(if expanded { \"true\" } else { \"false\" }) } else { None }}\n aria-disabled=disabled.aria_disabled()\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn TreeGroupPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div data-rs-tree-group=\"\" role=\"group\" class=class>\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{\n TreePrimitive, TreeItemPrimitive, TreeGroupPrimitive,\n };\n\n#[component]\npub fn Tree(children: Children, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <TreePrimitive class=class>{children()}</TreePrimitive> }\n}\n#[component]\npub fn TreeItem(children: Children, #[prop(default = false)] has_children: bool, #[prop(default = 0u8)] depth: u8, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! {\n <TreeItemPrimitive has_children=has_children depth=depth class=class>\n {if has_children {\n view! { <span data-rs-tree-toggle=\"\"/> }.into_any()\n } else {\n view! { <span data-rs-tree-indent=\"\"/> }.into_any()\n }}\n <span data-rs-tree-icon=\"\"/>\n {children()}\n </TreeItemPrimitive>\n }\n}\n#[component]\npub fn TreeGroup(children: Children, #[prop(into, default = String::new())] class: String) -> impl IntoView {\n view! { <TreeGroupPrimitive class=class>{children()}</TreeGroupPrimitive> }\n}\n",
"boundary_src": "//! Tree Island — Canon Rule passthrough\nuse leptos::prelude::*;\nuse super::tree_ui::{\n Tree as TreeUi,\n TreeItem as TreeItemUi,\n TreeGroup as TreeGroupUi\n};\n\n#[component]\npub fn Tree(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <TreeUi class=class>{children()}</TreeUi> }\n}\n\n#[component]\npub fn TreeItem(\n children: Children,\n #[prop(default = false)] has_children: bool,\n #[prop(default = 0u8)] depth: u8,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <TreeItemUi has_children=has_children depth=depth class=class>{children()}</TreeItemUi> }\n}\n\n#[component]\npub fn TreeGroup(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! { <TreeGroupUi class=class>{children()}</TreeGroupUi> }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const TREE_API: ComponentApi = ComponentApi {\n id: \"tree\",\n description: \"Tree view component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const TREEITEM_API: ComponentApi = ComponentApi {\n id: \"tree-item\",\n description: \"Tree view component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"has_children\", kind: PropType::Bool, required: false, default: Some(\"false\"), description: \"Prop value\" },\n PropDef { name: \"depth\", kind: PropType::String, required: false, default: Some(\"0u8\"), description: \"Prop value\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\npub const TREEGROUP_API: ComponentApi = ComponentApi {\n id: \"tree-group\",\n description: \"Tree view component\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"class\", kind: PropType::String, required: false, default: Some(\"\"), description: \"Additional CSS class names\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::tree_boundary::{Tree, TreeItem, TreeGroup};\n\n#[component]\npub fn TreeShowcasePreview() -> impl IntoView {\n view! {\n <div data-rs-showcase-preview-hero=\"\">\n <div data-rs-showcase-preview-stage=\"\">\n <Tree>\n <TreeItem depth=0>\"Documents\"</TreeItem>\n <TreeItem has_children=true depth=0>\"Projects\"</TreeItem>\n <TreeGroup>\n <TreeItem depth=1>\"canonrs\"</TreeItem>\n <TreeItem depth=1>\"monorepo\"</TreeItem>\n </TreeGroup>\n <TreeItem depth=0>\"Settings\"</TreeItem>\n </Tree>\n </div>\n <p data-rs-showcase-preview-anchor=\"\">\n \"Tree structure governed by DOM — keyboard nav and expand/collapse via data-rs-expanded.\"\n </p>\n </div>\n }\n}\n",
"block": [],
"blocks_primitives": [
"stack"
]
},
{
"id": "virtual_list",
"label": "Virtual List",
"category": "Display",
"description": "Virtualized list for large datasets",
"keywords": "",
"pain": "Large lists render all items causing performance degradation",
"promise": "List virtualization enforced structurally for large datasets",
"why": "VirtualListPrimitive encodes list structure and item indexing. Viewport and content separation enable virtualization logic. This guarantees scalable rendering.\n",
"before": "// ❌ Typical\nview! {\n <ul>\n {items.map(|i| view! { <li>{i}</li> })}\n </ul>\n}\n",
"after": "// ✅ CanonRS\nview! {\n <VirtualList items_count=1000 item_height=40.0>\n \"Item\"\n </VirtualList>\n}\n",
"rules": [
"CR-001",
"CR-004"
],
"use_cases": [
"logs",
"large datasets"
],
"related": [
"table",
"data_table",
"empty_table",
"tree",
"list_item"
],
"badges": [
"SSR Safe",
"Hydration Safe",
"Token Driven",
"Deterministic API",
"Zero Drift",
"Island Architecture"
],
"pillar": "data",
"primitive_src": "//! @canon-level: strict\n//! @canon-owner: primitives-team\n//! VirtualList Primitive - HTML puro + ARIA\n\nuse leptos::prelude::*;\n\n#[component]\npub fn VirtualListPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n let uid_vl = crate::infra::uid::generate(\"vl\");\n view! {\n <div\n data-rs-virtual-list=\"\"\n data-rs-uid=uid_vl\n data-rs-interaction=\"data\"\n role=\"list\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn VirtualListViewportPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n #[prop(into, optional)] aria_label: Option<String>,\n) -> impl IntoView {\n view! {\n <div\n data-rs-virtual-list-viewport=\"\"\n role=\"presentation\"\n aria-label=aria_label\n tabindex=\"0\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn VirtualListContentPrimitive(\n children: Children,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-virtual-list-content=\"\"\n role=\"presentation\"\n class=class\n >\n {children()}\n </div>\n }\n}\n\n#[component]\npub fn VirtualListItemPrimitive(\n children: Children,\n #[prop(default = 0usize)] index: usize,\n #[prop(into, default = String::new())] class: String,\n) -> impl IntoView {\n view! {\n <div\n data-rs-virtual-list-item=\"\"\n data-rs-index=index.to_string()\n role=\"listitem\"\n aria-setsize=\"-1\"\n aria-posinset={(index + 1).to_string()}\n class=class\n >\n {children()}\n </div>\n }\n}\n",
"ui_src": "#![allow(unreachable_pub, dead_code)]\n\nuse leptos::prelude::*;\nuse canonrs_core::primitives::{VirtualListPrimitive, VirtualListViewportPrimitive, ScrollOrientation};\nuse crate::ui::scroll_area::scroll_area_boundary::ScrollArea;\n\n#[component]\npub fn VirtualList(\n #[prop(into)] items_count: usize,\n #[prop(into, default = 40.0f64)] item_height: f64,\n #[prop(into, default = 400u32)] height: u32,\n #[prop(into, default = String::new())] class: String,\n children: Children,\n) -> impl IntoView {\n view! {\n <ScrollArea\n orientation=ScrollOrientation::Vertical\n attr:style=format!(\"height:{}px;\", height)\n >\n <VirtualListPrimitive\n class=class\n attr:data-items-count=items_count.to_string()\n attr:data-item-height=item_height.to_string()\n >\n <VirtualListViewportPrimitive>\n {children()}\n </VirtualListViewportPrimitive>\n </VirtualListPrimitive>\n </ScrollArea>\n }\n}\n\n",
"boundary_src": "//! @canon-level: strict\n//! VirtualList Island — bootstrap only, delegates to interaction engine\n\nuse leptos::prelude::*;\nuse super::virtual_list_ui::VirtualList as VirtualListUi;\n\n#[component]\npub fn VirtualList(\n #[prop(into)] items_count: usize,\n #[prop(into, default = 40.0f64)] item_height: f64,\n #[prop(into, default = 400u32)] height: u32,\n children: Children,\n) -> impl IntoView {\n view! {\n <VirtualListUi items_count=items_count item_height=item_height height=height>\n {children()}\n </VirtualListUi>\n }\n}\n",
"api_src": "// AUTO-GENERATED by build.rs — do not edit manually.\n// Source: *_boundary.rs + builder.yaml\nuse crate::catalog_types::{ComponentApi, PropDef, PropType};\n\npub const VIRTUALLIST_API: ComponentApi = ComponentApi {\n id: \"virtual-list\",\n description: \"Virtualized list for large datasets\",\n props: &[\n PropDef { name: \"children\", kind: PropType::Children, required: true, default: None, description: \"Child elements\" },\n PropDef { name: \"items_count\", kind: PropType::Number, required: true, default: None, description: \"Prop value\" },\n PropDef { name: \"item_height\", kind: PropType::Number, required: false, default: Some(\"40.0f64\"), description: \"Prop value\" },\n PropDef { name: \"height\", kind: PropType::Number, required: false, default: Some(\"400u32\"), description: \"Prop value\" },\n ],\n};\n\n",
"preview_src": "use leptos::prelude::*;\nuse super::virtual_list_boundary::VirtualList;\nuse canonrs_core::primitives::layout::container::{ContainerPrimitive as Container, ContainerSize};\n\n#[component]\npub fn VirtualListShowcasePreview() -> impl IntoView {\n view! {\n <div data-rs-showcase-preview-hero=\"\">\n <div data-rs-showcase-preview-stage=\"\">\n <Container size=ContainerSize::Sm>\n <VirtualList items_count=10000usize item_height=40.0f64>\n \"\"\n </VirtualList>\n </Container>\n </div>\n <p data-rs-showcase-preview-anchor=\"\">\n \"List virtualization enforced structurally for large datasets.\"\n </p>\n </div>\n }\n}\n",
"block": [],
"blocks_primitives": [
"container"
]
}
]