# shadwire 0.2.0 — component reference Every component below installs with `shadwire add `. Helpers are the ui_* methods; each component's own helper module installs with it, so only the components you install define helpers. ## button — Button Displays a button or a link styled as a button. When to use: Any clickable action. Use tag: :a for a link that looks like a button, size: :icon for icon-only. For a set of related actions use button-group; for a menu of actions use dropdown-menu. Install: `shadwire add button` ### Ui::ButtonComponent — `ui_button` variants: default | destructive | outline | secondary | ghost | link sizes: default | sm | lg | icon props: variant: :default, size: :default, tag: :button, disabled: false, focusable_when_disabled: false, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_button(variant: :outline, size: :sm) { "Save" } %> ``` ```erb <%= ui_button(size: :icon, "aria-label": "Add") { ui_icon("plus") } %> ``` --- ## badge — Badge A small inline label for status, counts, or categories. When to use: Non-interactive status or metadata next to content. For a clickable pill use button with size: :sm; for a keyboard key use kbd. Install: `shadwire add badge` ### Ui::BadgeComponent — `ui_badge` variants: default | secondary | destructive | outline props: variant: :default, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_badge(variant: :secondary) { "Beta" } %> ``` --- ## card — Card A container for grouping related content with header, body, and footer slots. When to use: Grouping related content into a bordered surface. For a compact list row use item; for a placeholder state use empty. Install: `shadwire add card` ### Ui::CardComponent — `ui_card` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Card::HeaderComponent — `ui_card_header` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Card::TitleComponent — `ui_card_title` props: tag_name: :h3, class_name: nil, **attrs (free HTML attributes) ### Ui::Card::DescriptionComponent — `ui_card_description` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Card::ContentComponent — `ui_card_content` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Card::FooterComponent — `ui_card_footer` props: class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_card do %> <%= ui_card_header do %> <%= ui_card_title { "Team" } %> <%= ui_card_description { "Manage members." } %> <% end %> <%= ui_card_content { "Body" } %> <%= ui_card_footer { ui_button { "Save" } } %> <% end %> ``` --- ## alert — Alert A callout for important inline messages. When to use: Persistent inline messages inside the page. For a message that interrupts the user use alert-dialog; for transient feedback use sonner. Install: `shadwire add alert` ### Ui::AlertComponent — `ui_alert` variants: default | destructive props: variant: :default, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_alert(variant: :destructive) { "Your session expired." } %> ``` --- ## separator — Separator A thin rule that visually divides content. When to use: Dividing sections or menu groups. Inside a menu prefer that menu's own separator (ui_dropdown_menu_separator, ui_context_menu_separator). Install: `shadwire add separator` ### Ui::SeparatorComponent — `ui_separator` props: orientation: :horizontal, decorative: nil, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_separator %> ``` --- ## avatar — Avatar A user or entity image with a text fallback. When to use: Representing a person or account. Always supply fallback for when the image fails to load. Install: `shadwire add avatar` ### Ui::AvatarComponent — `ui_avatar` props: src: nil, alt: "", fallback: nil, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_avatar(src: user.avatar_url, alt: user.name, fallback: "JD") %> ``` --- ## accordion — Accordion Vertically stacked sections that expand and collapse. When to use: Several collapsible sections, typically FAQs. For a single collapsible region use collapsible; for switching between peer views use tabs. Install: `shadwire add accordion` Requires Stimulus (importmap with eager loading). ### Ui::AccordionComponent — `ui_accordion` props: multiple: false, default_value: nil, orientation: :vertical, disabled: false, loop_focus: true, class_name: nil, **attrs (free HTML attributes) ### Ui::Accordion::ItemComponent — `ui_accordion_item` props: value: nil, disabled: false, class_name: nil, **attrs (free HTML attributes) ### Ui::Accordion::HeaderComponent — `ui_accordion_header` props: tag: :h3, class_name: nil, **attrs (free HTML attributes) ### Ui::Accordion::TriggerComponent — `ui_accordion_trigger` props: disabled: false, class_name: nil, **attrs (free HTML attributes) ### Ui::Accordion::ContentComponent — `ui_accordion_content` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Accordion::PanelComponent — `ui_accordion_panel` ### Ui::IconComponent — `ui_icon` sizes: sm | default | lg | xl props: size: :default, label: nil, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_accordion(multiple: false) do %> <%= ui_accordion_item(value: "one") do %> <%= ui_accordion_trigger { "Is it accessible?" } %> <%= ui_accordion_content { "Yes." } %> <% end %> <% end %> ``` --- ## scroll-area — Scroll Area A scrollable region with styled scrollbars. When to use: Constraining a long list or panel to a fixed height with consistent scrollbars across browsers. Install: `shadwire add scroll-area` Requires Stimulus (importmap with eager loading). ### Ui::ScrollAreaComponent — `ui_scroll_area` props: scrollbars: [ :vertical ], class_name: nil, **attrs (free HTML attributes) ### Ui::ScrollArea::ScrollbarComponent — `ui_scroll_bar` props: orientation: :vertical, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_scroll_area(class: "h-72 w-48 rounded-md border") { "Long content" } %> ``` --- ## icon — Icon A Lucide icon with shadcn-style size variants. When to use: Any icon. Names are Lucide kebab-case. Decorative by default (aria-hidden); pass label: when the icon carries meaning on its own. Install: `shadwire add icon` ### Ui::IconComponent — `ui_icon` sizes: sm | default | lg | xl props: size: :default, label: nil, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_icon("chevron-down", size: :sm) %> ``` ```erb <%= ui_icon("trash-2", label: "Delete") %> ``` --- ## input — Input A single-line text field. When to use: Free-text entry. For multi-line use textarea; with an addon or button attached use input-group; wrap in field for a label and error. Install: `shadwire add input` ### Ui::InputComponent — `ui_input` props: type: :text, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_input(type: :email, name: "email", placeholder: "you@example.com") %> ``` --- ## label — Label An accessible caption bound to a form control. When to use: Labelling a standalone control. Inside a field block prefer ui_field_label, which wires the description and error for you. Install: `shadwire add label` ### Ui::LabelComponent — `ui_label` props: class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_label(for: "email") { "Email" } %> ``` --- ## textarea — Textarea A multi-line text field. When to use: Free-text entry spanning several lines. For one line use input. Install: `shadwire add textarea` ### Ui::TextareaComponent — `ui_textarea` props: class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_textarea(name: "bio", rows: 4, placeholder: "About you") %> ``` --- ## checkbox — Checkbox A binary control for opting in or selecting many items. When to use: Independent on/off choices, or multi-select in a list. For a single setting that applies immediately use switch; for one-of-many use radio-group. Install: `shadwire add checkbox` ### Ui::CheckboxComponent — `ui_checkbox` props: checked: false, disabled: false, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_checkbox(name: "terms", value: "1") %> ``` --- ## radio-group — Radio Group A set of mutually exclusive options. When to use: Picking exactly one of a few visible options. For many options use select; for a compact toolbar-style choice use toggle-group. Install: `shadwire add radio-group` ### Ui::RadioGroupComponent — `ui_radio_group` props: class_name: nil, **attrs (free HTML attributes) ### Ui::RadioGroup::ItemComponent — `ui_radio_group_item` props: name: (required), value: (required), checked: false, disabled: false, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_radio_group(name: "plan", value: "pro") do %> <%= ui_radio_group_item(value: "free") %> <%= ui_radio_group_item(value: "pro") %> <% end %> ``` --- ## switch — Switch A toggle for a setting that takes effect immediately. When to use: On/off settings, especially ones that apply instantly. For a value submitted with a form use checkbox. Install: `shadwire add switch` ### Ui::SwitchComponent — `ui_switch` props: checked: false, disabled: false, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_switch(name: "notifications", checked: true) %> ``` --- ## skeleton — Skeleton A placeholder block shown while content loads. When to use: Preserving layout during loading. For an indeterminate inline busy indicator use spinner; for measurable progress use progress. Install: `shadwire add skeleton` ### Ui::SkeletonComponent — `ui_skeleton` props: class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_skeleton(class: "h-4 w-[250px]") %> ``` --- ## progress — Progress A bar showing completion toward a known total. When to use: Progress you can measure. When the duration is unknown use spinner; when you are waiting on content use skeleton. Install: `shadwire add progress` ### Ui::ProgressComponent — `ui_progress` props: value: 0, max: 100, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_progress(value: 60) %> ``` --- ## table — Table A styled HTML table with header, body, and footer sections. When to use: Presenting rows of data you render yourself. For built-in sorting, filtering, pagination and row selection use data-table. Install: `shadwire add table` ### Ui::TableComponent — `ui_table` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Table::HeaderComponent — `ui_table_header` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Table::BodyComponent — `ui_table_body` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Table::FooterComponent — `ui_table_footer` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Table::RowComponent — `ui_table_row` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Table::HeadComponent — `ui_table_head` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Table::CellComponent — `ui_table_cell` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Table::CaptionComponent — `ui_table_caption` props: class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_table do %> <%= ui_table_header do %> <%= ui_table_row { ui_table_head { "Name" } } %> <% end %> <%= ui_table_body do %> <%= ui_table_row { ui_table_cell { "Ada" } } %> <% end %> <% end %> ``` --- ## breadcrumb — Breadcrumb A trail showing the current page's position in the hierarchy. When to use: Showing where the user is in a nested structure. For moving between top-level sections use navigation-menu. Install: `shadwire add breadcrumb` ### Ui::BreadcrumbComponent — `ui_breadcrumb` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Breadcrumb::ListComponent — `ui_breadcrumb_list` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Breadcrumb::ItemComponent — `ui_breadcrumb_item` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Breadcrumb::LinkComponent — `ui_breadcrumb_link` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Breadcrumb::PageComponent — `ui_breadcrumb_page` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Breadcrumb::SeparatorComponent — `ui_breadcrumb_separator` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Breadcrumb::EllipsisComponent — `ui_breadcrumb_ellipsis` props: class_name: nil, **attrs (free HTML attributes) ### Ui::IconComponent — `ui_icon` sizes: sm | default | lg | xl props: size: :default, label: nil, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_breadcrumb do %> <%= ui_breadcrumb_list do %> <%= ui_breadcrumb_item { ui_breadcrumb_link(href: "/") { "Home" } } %> <%= ui_breadcrumb_separator %> <%= ui_breadcrumb_item { ui_breadcrumb_page { "Settings" } } %> <% end %> <% end %> ``` --- ## pagination — Pagination Navigation controls for moving between pages of results. When to use: Paging through server-rendered result sets. The data-table component brings its own pagination. Install: `shadwire add pagination` ### Ui::PaginationComponent — `ui_pagination` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Pagination::ContentComponent — `ui_pagination_content` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Pagination::ItemComponent — `ui_pagination_item` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Pagination::LinkComponent — `ui_pagination_link` props: active: false, size: :icon, class_name: nil, **attrs (free HTML attributes) ### Ui::Pagination::PreviousComponent — `ui_pagination_previous` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Pagination::NextComponent — `ui_pagination_next` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Pagination::EllipsisComponent — `ui_pagination_ellipsis` props: class_name: nil, **attrs (free HTML attributes) ### Ui::ButtonComponent — `ui_button` variants: default | destructive | outline | secondary | ghost | link sizes: default | sm | lg | icon props: variant: :default, size: :default, tag: :button, disabled: false, focusable_when_disabled: false, class_name: nil, **attrs (free HTML attributes) ### Ui::IconComponent — `ui_icon` sizes: sm | default | lg | xl props: size: :default, label: nil, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_pagination do %> <%= ui_pagination_content do %> <%= ui_pagination_item { ui_pagination_previous(href: "?page=1") } %> <%= ui_pagination_item { ui_pagination_link(href: "?page=2") { "2" } } %> <%= ui_pagination_item { ui_pagination_next(href: "?page=3") } %> <% end %> <% end %> ``` --- ## tabs — Tabs Switches between peer views in the same space. When to use: Alternate views of comparable content. Triggers must live inside ui_tabs_list. For expanding sections that can be open at once use accordion. Install: `shadwire add tabs` Requires Stimulus (importmap with eager loading). ### Ui::TabsComponent — `ui_tabs` props: default_value: nil, class_name: nil, **attrs (free HTML attributes) ### Ui::Tabs::ListComponent — `ui_tabs_list` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Tabs::TriggerComponent — `ui_tabs_trigger` props: value: (required), disabled: false, class_name: nil, **attrs (free HTML attributes) ### Ui::Tabs::ContentComponent — `ui_tabs_content` props: value: (required), class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_tabs(default_value: "account") do %> <%= ui_tabs_list do %> <%= ui_tabs_trigger(value: "account") { "Account" } %> <%= ui_tabs_trigger(value: "password") { "Password" } %> <% end %> <%= ui_tabs_content(value: "account") { "Account settings" } %> <% end %> ``` --- ## dialog — Dialog A modal window overlaying the page. When to use: Focused tasks that interrupt the current flow. For a destructive confirmation use alert-dialog; for a side panel use sheet; for a bottom sheet use drawer. Always include ui_dialog_title, visually hidden if needed. Install: `shadwire add dialog` Requires Stimulus (importmap with eager loading). ### Ui::DialogComponent — `ui_dialog` props: close_on_backdrop: true, class_name: nil, **attrs (free HTML attributes) ### Ui::Dialog::TriggerComponent — `ui_dialog_trigger` props: variant: :default, size: :default, class_name: nil, **attrs (free HTML attributes) ### Ui::Dialog::ContentComponent — `ui_dialog_content` props: show_close_button: true, class_name: nil, **attrs (free HTML attributes) ### Ui::Dialog::HeaderComponent — `ui_dialog_header` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Dialog::FooterComponent — `ui_dialog_footer` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Dialog::TitleComponent — `ui_dialog_title` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Dialog::DescriptionComponent — `ui_dialog_description` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Dialog::CloseComponent — `ui_dialog_close` props: variant: :outline, size: :default, class_name: nil, **attrs (free HTML attributes) ### Ui::ButtonComponent — `ui_button` variants: default | destructive | outline | secondary | ghost | link sizes: default | sm | lg | icon props: variant: :default, size: :default, tag: :button, disabled: false, focusable_when_disabled: false, class_name: nil, **attrs (free HTML attributes) ### Ui::IconComponent — `ui_icon` sizes: sm | default | lg | xl props: size: :default, label: nil, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_dialog do %> <%= ui_dialog_trigger { ui_button { "Open" } } %> <%= ui_dialog_content do %> <%= ui_dialog_header do %> <%= ui_dialog_title { "Edit profile" } %> <%= ui_dialog_description { "Update your details." } %> <% end %> <%= ui_dialog_footer { ui_button { "Save" } } %> <% end %> <% end %> ``` --- ## alert-dialog — Alert Dialog A modal that interrupts the user to confirm a consequential action. When to use: Confirming destructive or irreversible actions — it cannot be dismissed by clicking outside. For ordinary modal content use dialog. Install: `shadwire add alert-dialog` Requires Stimulus (importmap with eager loading). ### Ui::AlertDialogComponent — `ui_alert_dialog` props: class_name: nil, **attrs (free HTML attributes) ### Ui::AlertDialog::TriggerComponent — `ui_alert_dialog_trigger` props: variant: :default, size: :default, class_name: nil, **attrs (free HTML attributes) ### Ui::AlertDialog::ContentComponent — `ui_alert_dialog_content` props: class_name: nil, **attrs (free HTML attributes) ### Ui::AlertDialog::HeaderComponent — `ui_alert_dialog_header` props: class_name: nil, **attrs (free HTML attributes) ### Ui::AlertDialog::FooterComponent — `ui_alert_dialog_footer` props: class_name: nil, **attrs (free HTML attributes) ### Ui::AlertDialog::TitleComponent — `ui_alert_dialog_title` props: class_name: nil, **attrs (free HTML attributes) ### Ui::AlertDialog::DescriptionComponent — `ui_alert_dialog_description` props: class_name: nil, **attrs (free HTML attributes) ### Ui::AlertDialog::ActionComponent — `ui_alert_dialog_action` props: variant: :default, size: :default, class_name: nil, **attrs (free HTML attributes) ### Ui::AlertDialog::CancelComponent — `ui_alert_dialog_cancel` props: variant: :outline, size: :default, class_name: nil, **attrs (free HTML attributes) ### Ui::ButtonComponent — `ui_button` variants: default | destructive | outline | secondary | ghost | link sizes: default | sm | lg | icon props: variant: :default, size: :default, tag: :button, disabled: false, focusable_when_disabled: false, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_alert_dialog do %> <%= ui_alert_dialog_trigger { ui_button(variant: :destructive) { "Delete" } } %> <%= ui_alert_dialog_content do %> <%= ui_alert_dialog_header do %> <%= ui_alert_dialog_title { "Are you sure?" } %> <%= ui_alert_dialog_description { "This cannot be undone." } %> <% end %> <%= ui_alert_dialog_footer do %> <%= ui_alert_dialog_cancel { "Cancel" } %> <%= ui_alert_dialog_action { "Delete" } %> <% end %> <% end %> <% end %> ``` --- ## sheet — Sheet A panel that slides in from an edge of the screen. When to use: Secondary content or forms alongside the page, anchored to an edge. For a centred modal use dialog; for app navigation use sidebar. Install: `shadwire add sheet` Requires Stimulus (importmap with eager loading). ### Ui::SheetComponent — `ui_sheet` props: close_on_backdrop: true, class_name: nil, **attrs (free HTML attributes) ### Ui::Sheet::TriggerComponent — `ui_sheet_trigger` props: variant: :default, size: :default, class_name: nil, **attrs (free HTML attributes) ### Ui::Sheet::ContentComponent — `ui_sheet_content` props: side: :right, show_close_button: true, class_name: nil, **attrs (free HTML attributes) ### Ui::Sheet::HeaderComponent — `ui_sheet_header` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Sheet::FooterComponent — `ui_sheet_footer` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Sheet::TitleComponent — `ui_sheet_title` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Sheet::DescriptionComponent — `ui_sheet_description` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Sheet::CloseComponent — `ui_sheet_close` props: variant: :outline, size: :default, class_name: nil, **attrs (free HTML attributes) ### Ui::ButtonComponent — `ui_button` variants: default | destructive | outline | secondary | ghost | link sizes: default | sm | lg | icon props: variant: :default, size: :default, tag: :button, disabled: false, focusable_when_disabled: false, class_name: nil, **attrs (free HTML attributes) ### Ui::IconComponent — `ui_icon` sizes: sm | default | lg | xl props: size: :default, label: nil, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_sheet do %> <%= ui_sheet_trigger { ui_button(variant: :outline) { "Open" } } %> <%= ui_sheet_content(side: :right) do %> <%= ui_sheet_header { ui_sheet_title { "Filters" } } %> <% end %> <% end %> ``` --- ## tooltip — Tooltip A short hint shown on hover or focus. When to use: Naming an icon-only control or adding a brief hint. Never put interactive content inside — use popover or hover-card for that. Install: `shadwire add tooltip` Requires Stimulus (importmap with eager loading). ### Ui::TooltipComponent — `ui_tooltip` props: open_delay: 300, class_name: nil, **attrs (free HTML attributes) ### Ui::Tooltip::TriggerComponent — `ui_tooltip_trigger` props: variant: :outline, size: :default, class_name: nil, **attrs (free HTML attributes) ### Ui::Tooltip::ContentComponent — `ui_tooltip_content` props: side: :top, class_name: nil, **attrs (free HTML attributes) ### Ui::ButtonComponent — `ui_button` variants: default | destructive | outline | secondary | ghost | link sizes: default | sm | lg | icon props: variant: :default, size: :default, tag: :button, disabled: false, focusable_when_disabled: false, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_tooltip do %> <%= ui_tooltip_trigger { ui_button(size: :icon) { ui_icon("info") } } %> <%= ui_tooltip_content { "More information" } %> <% end %> ``` --- ## popover — Popover A floating panel anchored to a trigger. When to use: Rich or interactive content anchored to a control, opened by click. For a plain hint use tooltip; for a list of actions use dropdown-menu. Install: `shadwire add popover` Requires Stimulus (importmap with eager loading). ### Ui::PopoverComponent — `ui_popover` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Popover::TriggerComponent — `ui_popover_trigger` props: variant: :outline, size: :default, class_name: nil, **attrs (free HTML attributes) ### Ui::Popover::ContentComponent — `ui_popover_content` props: side: :bottom, align: :center, class_name: nil, **attrs (free HTML attributes) ### Ui::ButtonComponent — `ui_button` variants: default | destructive | outline | secondary | ghost | link sizes: default | sm | lg | icon props: variant: :default, size: :default, tag: :button, disabled: false, focusable_when_disabled: false, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_popover do %> <%= ui_popover_trigger { ui_button(variant: :outline) { "Open" } } %> <%= ui_popover_content { "Panel content" } %> <% end %> ``` --- ## dropdown-menu — Dropdown Menu A menu of actions or options triggered by a button. When to use: Row actions, overflow menus, and account menus. For right-click use context-menu; for an application menu bar use menubar; for choosing a form value use select. Install: `shadwire add dropdown-menu` Requires Stimulus (importmap with eager loading). ### Ui::DropdownMenuComponent — `ui_dropdown_menu` props: class_name: nil, **attrs (free HTML attributes) ### Ui::DropdownMenu::TriggerComponent — `ui_dropdown_menu_trigger` props: variant: :outline, size: :default, class_name: nil, **attrs (free HTML attributes) ### Ui::DropdownMenu::ContentComponent — `ui_dropdown_menu_content` props: side: :bottom, align: :start, class_name: nil, **attrs (free HTML attributes) ### Ui::DropdownMenu::ItemComponent — `ui_dropdown_menu_item` props: inset: false, variant: :default, disabled: false, tag: :button, class_name: nil, **attrs (free HTML attributes) ### Ui::DropdownMenu::LabelComponent — `ui_dropdown_menu_label` props: inset: false, class_name: nil, **attrs (free HTML attributes) ### Ui::DropdownMenu::SeparatorComponent — `ui_dropdown_menu_separator` props: class_name: nil, **attrs (free HTML attributes) ### Ui::DropdownMenu::GroupComponent — `ui_dropdown_menu_group` props: class_name: nil, **attrs (free HTML attributes) ### Ui::DropdownMenu::ShortcutComponent — `ui_dropdown_menu_shortcut` props: class_name: nil, **attrs (free HTML attributes) ### Ui::ButtonComponent — `ui_button` variants: default | destructive | outline | secondary | ghost | link sizes: default | sm | lg | icon props: variant: :default, size: :default, tag: :button, disabled: false, focusable_when_disabled: false, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_dropdown_menu do %> <%= ui_dropdown_menu_trigger { ui_button(variant: :outline) { "Open" } } %> <%= ui_dropdown_menu_content do %> <%= ui_dropdown_menu_label { "My account" } %> <%= ui_dropdown_menu_separator %> <%= ui_dropdown_menu_item { "Profile" } %> <% end %> <% end %> ``` --- ## select — Select A styled control for choosing one option from a list. When to use: Choosing a single form value from many options. For a searchable list use combobox; for the plain browser control use native-select; for a few visible options use radio-group. Install: `shadwire add select` Requires Stimulus (importmap with eager loading). ### Ui::SelectComponent — `ui_select` props: name: nil, value: nil, placeholder: nil, disabled: false, class_name: nil, **attrs (free HTML attributes) ### Ui::Select::TriggerComponent — `ui_select_trigger` sizes: default | sm props: size: :default, disabled: false, class_name: nil, **attrs (free HTML attributes) ### Ui::Select::ValueComponent — `ui_select_value` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Select::ContentComponent — `ui_select_content` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Select::ItemComponent — `ui_select_item` props: value: (required), disabled: false, class_name: nil, **attrs (free HTML attributes) ### Ui::Select::GroupComponent — `ui_select_group` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Select::LabelComponent — `ui_select_label` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Select::SeparatorComponent — `ui_select_separator` props: class_name: nil, **attrs (free HTML attributes) ### Ui::IconComponent — `ui_icon` sizes: sm | default | lg | xl props: size: :default, label: nil, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_select(name: "role", placeholder: "Select a role") do %> <%= ui_select_trigger { ui_select_value } %> <%= ui_select_content do %> <%= ui_select_item(value: "admin") { "Admin" } %> <%= ui_select_item(value: "member") { "Member" } %> <% end %> <% end %> ``` --- ## sidebar — Sidebar A composable, collapsible application sidebar. When to use: Primary application navigation. Wrap the app in ui_sidebar_provider and put page content in ui_sidebar_inset. For a temporary edge panel use sheet. Install: `shadwire add sidebar` Requires Stimulus (importmap with eager loading). ### Ui::SidebarComponent — `ui_sidebar` props: side: :left, variant: :sidebar, collapsible: :offcanvas, class_name: nil, **attrs (free HTML attributes) ### Ui::Sidebar::ProviderComponent — `ui_sidebar_provider` props: default_open: true, cookie_name: "sidebar_state", class_name: nil, **attrs (free HTML attributes) ### Ui::Sidebar::TriggerComponent — `ui_sidebar_trigger` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Sidebar::RailComponent — `ui_sidebar_rail` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Sidebar::InsetComponent — `ui_sidebar_inset` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Sidebar::InputComponent — `ui_sidebar_input` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Sidebar::HeaderComponent — `ui_sidebar_header` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Sidebar::FooterComponent — `ui_sidebar_footer` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Sidebar::SeparatorComponent — `ui_sidebar_separator` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Sidebar::ContentComponent — `ui_sidebar_content` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Sidebar::GroupComponent — `ui_sidebar_group` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Sidebar::GroupLabelComponent — `ui_sidebar_group_label` props: tag: :div, class_name: nil, **attrs (free HTML attributes) ### Ui::Sidebar::GroupActionComponent — `ui_sidebar_group_action` props: tag: :button, class_name: nil, **attrs (free HTML attributes) ### Ui::Sidebar::GroupContentComponent — `ui_sidebar_group_content` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Sidebar::MenuComponent — `ui_sidebar_menu` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Sidebar::MenuItemComponent — `ui_sidebar_menu_item` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Sidebar::MenuButtonComponent — `ui_sidebar_menu_button` variants: default | outline sizes: default | sm | lg props: is_active: false, variant: :default, size: :default, tag: :button, tooltip: nil, class_name: nil, **attrs (free HTML attributes) ### Ui::Sidebar::MenuActionComponent — `ui_sidebar_menu_action` props: show_on_hover: false, tag: :button, class_name: nil, **attrs (free HTML attributes) ### Ui::Sidebar::MenuBadgeComponent — `ui_sidebar_menu_badge` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Sidebar::MenuSkeletonComponent — `ui_sidebar_menu_skeleton` props: show_icon: false, class_name: nil, **attrs (free HTML attributes) ### Ui::Sidebar::MenuSubComponent — `ui_sidebar_menu_sub` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Sidebar::MenuSubItemComponent — `ui_sidebar_menu_sub_item` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Sidebar::MenuSubButtonComponent — `ui_sidebar_menu_sub_button` sizes: sm | md props: is_active: false, size: :md, tag: :a, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_sidebar_provider do %> <%= ui_sidebar do %> <%= ui_sidebar_content do %> <%= ui_sidebar_group do %> <%= ui_sidebar_menu { ui_sidebar_menu_item { ui_sidebar_menu_button { "Home" } } } %> <% end %> <% end %> <% end %> <%= ui_sidebar_inset { ui_sidebar_trigger } %> <% end %> ``` --- ## sidebar-01 — Sidebar 01 A documentation-style dashboard layout with a collapsible sidebar, version switcher, and search. When to use: Scaffolding a full dashboard page rather than assembling the sidebar yourself. Install the sidebar component instead when you want to compose your own layout. Install: `shadwire add sidebar-01` ```erb <%= render Ui::Blocks::Sidebar01::PageComponent.new %> ``` --- ## aspect-ratio — Aspect Ratio Constrains its content to a fixed width-to-height ratio. When to use: Keeping images, video, or embeds at a consistent shape while they scale. Install: `shadwire add aspect-ratio` ### Ui::AspectRatioComponent — `ui_aspect_ratio` props: ratio: 1, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_aspect_ratio(ratio: 16.0 / 9) { image_tag("cover.jpg", class: "size-full object-cover") } %> ``` --- ## spinner — Spinner An indeterminate loading indicator. When to use: Waits of unknown length, including inside a button. For measurable progress use progress; for content placeholders use skeleton. Install: `shadwire add spinner` ### Ui::SpinnerComponent — `ui_spinner` props: label: "Carregando", class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_button(disabled: true) { ui_spinner + " Saving" } %> ``` --- ## kbd — Kbd Displays a keyboard key or shortcut. When to use: Documenting keyboard shortcuts. Inside a menu row prefer that menu's shortcut helper, such as ui_dropdown_menu_shortcut. Install: `shadwire add kbd` ### Ui::KbdComponent — `ui_kbd` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Kbd::GroupComponent — `ui_kbd_group` props: class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_kbd_group { ui_kbd { "⌘" } + ui_kbd { "K" } } %> ``` --- ## empty — Empty A placeholder for when there is nothing to show. When to use: Empty lists, no search results, or first-run states. For loading use skeleton. Install: `shadwire add empty` ### Ui::EmptyComponent — `ui_empty` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Empty::HeaderComponent — `ui_empty_header` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Empty::MediaComponent — `ui_empty_media` variants: default | icon props: variant: :default, class_name: nil, **attrs (free HTML attributes) ### Ui::Empty::TitleComponent — `ui_empty_title` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Empty::DescriptionComponent — `ui_empty_description` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Empty::ContentComponent — `ui_empty_content` props: class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_empty do %> <%= ui_empty_header do %> <%= ui_empty_media { ui_icon("inbox") } %> <%= ui_empty_title { "No messages" } %> <%= ui_empty_description { "You are all caught up." } %> <% end %> <% end %> ``` --- ## item — Item A compact row with media, text, and trailing actions. When to use: List rows such as settings, files, or people. For a larger bordered surface use card. Install: `shadwire add item` ### Ui::ItemComponent — `ui_item` variants: default | outline | muted sizes: default | sm props: tag: :div, variant: :default, size: :default, class_name: nil, **attrs (free HTML attributes) ### Ui::Item::GroupComponent — `ui_item_group` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Item::MediaComponent — `ui_item_media` variants: default | icon | image props: variant: :default, class_name: nil, **attrs (free HTML attributes) ### Ui::Item::ContentComponent — `ui_item_content` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Item::TitleComponent — `ui_item_title` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Item::DescriptionComponent — `ui_item_description` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Item::ActionsComponent — `ui_item_actions` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Item::HeaderComponent — `ui_item_header` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Item::FooterComponent — `ui_item_footer` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Item::SeparatorComponent — `ui_item_separator` props: class_name: nil, **attrs (free HTML attributes) ### Ui::SeparatorComponent — `ui_separator` props: orientation: :horizontal, decorative: nil, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_item do %> <%= ui_item_media { ui_icon("file") } %> <%= ui_item_content do %> <%= ui_item_title { "Report.pdf" } %> <%= ui_item_description { "2.4 MB" } %> <% end %> <%= ui_item_actions { ui_button(variant: :ghost, size: :icon) { ui_icon("download") } } %> <% end %> ``` --- ## input-group — Input Group An input with attached addons, text, or buttons. When to use: Prefixes, suffixes, or buttons attached to a field. Use ui_input_group_input or ui_input_group_textarea inside, never a bare input. Install: `shadwire add input-group` ### Ui::InputGroupComponent — `ui_input_group` props: class_name: nil, **attrs (free HTML attributes) ### Ui::InputGroup::AddonComponent — `ui_input_group_addon` props: align: :inline_start, class_name: nil, **attrs (free HTML attributes) ### Ui::InputGroup::TextComponent — `ui_input_group_text` props: class_name: nil, **attrs (free HTML attributes) ### Ui::InputGroup::InputComponent — `ui_input_group_input` props: type: :text, class_name: nil, **attrs (free HTML attributes) ### Ui::InputGroup::TextareaComponent — `ui_input_group_textarea` props: class_name: nil, **attrs (free HTML attributes) ### Ui::InputGroup::ButtonComponent — `ui_input_group_button` variants: default | outline | secondary | ghost sizes: xs | sm | icon_xs | icon_sm props: variant: :ghost, size: :xs, type: "button", class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_input_group do %> <%= ui_input_group_addon { ui_icon("search") } %> <%= ui_input_group_input(name: "q", placeholder: "Search") %> <% end %> ``` --- ## button-group — Button Group Related buttons joined into a single control. When to use: Segmenting related actions such as previous/next. For selecting among options use toggle-group. Install: `shadwire add button-group` ### Ui::ButtonGroupComponent — `ui_button_group` props: orientation: :horizontal, class_name: nil, **attrs (free HTML attributes) ### Ui::ButtonGroup::TextComponent — `ui_button_group_text` props: tag: :div, class_name: nil, **attrs (free HTML attributes) ### Ui::ButtonGroup::SeparatorComponent — `ui_button_group_separator` props: orientation: :vertical, class_name: nil, **attrs (free HTML attributes) ### Ui::SeparatorComponent — `ui_separator` props: orientation: :horizontal, decorative: nil, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_button_group do %> <%= ui_button(variant: :outline) { "Prev" } %> <%= ui_button_group_separator %> <%= ui_button(variant: :outline) { "Next" } %> <% end %> ``` --- ## field — Field Form field layout with label, description, and error message. When to use: The standard wrapper for every form control — never lay out form rows with bare divs. Group related fields with ui_field_group, and related checkboxes or radios with ui_field_set plus ui_field_legend. Install: `shadwire add field` ### Ui::FieldComponent — `ui_field` props: orientation: :vertical, invalid: false, class_name: nil, **attrs (free HTML attributes) ### Ui::Field::SetComponent — `ui_field_set` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Field::LegendComponent — `ui_field_legend` variants: legend | label props: variant: :legend, class_name: nil, **attrs (free HTML attributes) ### Ui::Field::GroupComponent — `ui_field_group` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Field::ContentComponent — `ui_field_content` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Field::LabelComponent — `ui_field_label` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Field::TitleComponent — `ui_field_title` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Field::DescriptionComponent — `ui_field_description` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Field::SeparatorComponent — `ui_field_separator` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Field::ErrorComponent — `ui_field_error` props: errors: nil, class_name: nil, **attrs (free HTML attributes) ### Ui::SeparatorComponent — `ui_separator` props: orientation: :horizontal, decorative: nil, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_field do %> <%= ui_field_label(for: "email") { "Email" } %> <%= ui_input(type: :email, id: "email", name: "email") %> <%= ui_field_description { "We never share it." } %> <%= ui_field_error { "Enter a valid email." } %> <% end %> ``` --- ## native-select — Native Select The browser's native select element, styled to match. When to use: When you want native mobile pickers and no JavaScript. For a styled listbox use select; for search use combobox. Install: `shadwire add native-select` ### Ui::NativeSelectComponent — `ui_native_select` props: class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_native_select(name: "role") do %> <% end %> ``` --- ## collapsible — Collapsible A single region that expands and collapses. When to use: One show/hide region. For several coordinated sections use accordion. Install: `shadwire add collapsible` Requires Stimulus (importmap with eager loading). ### Ui::CollapsibleComponent — `ui_collapsible` props: open: false, class_name: nil, **attrs (free HTML attributes) ### Ui::Collapsible::TriggerComponent — `ui_collapsible_trigger` props: open: false, class_name: nil, **attrs (free HTML attributes) ### Ui::Collapsible::ContentComponent — `ui_collapsible_content` props: open: false, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_collapsible do %> <%= ui_collapsible_trigger { ui_button(variant: :ghost) { "Toggle" } } %> <%= ui_collapsible_content { "Hidden content" } %> <% end %> ``` --- ## toggle — Toggle A two-state button that stays pressed. When to use: A single on/off control in a toolbar, such as bold. For several related toggles use toggle-group; for a settings switch use switch. Install: `shadwire add toggle` Requires Stimulus (importmap with eager loading). ### Ui::ToggleComponent — `ui_toggle` variants: default | outline sizes: default | sm | lg props: variant: :default, size: :default, pressed: false, disabled: false, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_toggle(pressed: true, "aria-label": "Bold") { ui_icon("bold") } %> ``` --- ## toggle-group — Toggle Group A set of toggle buttons acting as one control. When to use: Choosing between two to seven options in a compact toolbar. For form semantics use radio-group; for actions use button-group. Install: `shadwire add toggle-group` Requires Stimulus (importmap with eager loading). ### Ui::ToggleGroupComponent — `ui_toggle_group` props: type: :single, variant: :default, size: :default, orientation: :horizontal, class_name: nil, **attrs (free HTML attributes) ### Ui::ToggleGroup::ItemComponent — `ui_toggle_group_item` variants: default | outline sizes: default | sm | lg props: value: nil, variant: :default, size: :default, pressed: false, disabled: false, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_toggle_group(type: :single) do %> <%= ui_toggle_group_item(value: "left") { ui_icon("align-left") } %> <%= ui_toggle_group_item(value: "center") { ui_icon("align-center") } %> <% end %> ``` --- ## slider — Slider Selects a numeric value from a range by dragging. When to use: Imprecise numeric input such as volume or price. For an exact number use input with type: :number. Install: `shadwire add slider` Requires Stimulus (importmap with eager loading). ### Ui::SliderComponent — `ui_slider` props: min: 0, max: 100, step: 1, value: 0, name: nil, label: nil, disabled: false, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_slider(name: "volume", min: 0, max: 100, value: 50) %> ``` --- ## hover-card — Hover Card A rich preview card shown on hover. When to use: Previewing linked content, such as a user profile. For a short hint use tooltip; for click-triggered content use popover. Install: `shadwire add hover-card` Requires Stimulus (importmap with eager loading). ### Ui::HoverCardComponent — `ui_hover_card` props: open_delay: 700, close_delay: 300, class_name: nil, **attrs (free HTML attributes) ### Ui::HoverCard::TriggerComponent — `ui_hover_card_trigger` props: tag: :a, class_name: nil, **attrs (free HTML attributes) ### Ui::HoverCard::ContentComponent — `ui_hover_card_content` props: side: :bottom, align: :center, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_hover_card do %> <%= ui_hover_card_trigger { link_to "@ada", "#" } %> <%= ui_hover_card_content { "Ada Lovelace — mathematician" } %> <% end %> ``` --- ## input-otp — Input OTP A segmented field for one-time passcodes. When to use: Verification codes split into boxes. For ordinary text use input. Install: `shadwire add input-otp` Requires Stimulus (importmap with eager loading). ### Ui::InputOtpComponent — `ui_input_otp` props: name: nil, value: nil, class_name: nil, **attrs (free HTML attributes) ### Ui::InputOtp::GroupComponent — `ui_input_otp_group` props: class_name: nil, **attrs (free HTML attributes) ### Ui::InputOtp::SlotComponent — `ui_input_otp_slot` props: value: nil, inputmode: "numeric", disabled: false, class_name: nil, **attrs (free HTML attributes) ### Ui::InputOtp::SeparatorComponent — `ui_input_otp_separator` props: class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_input_otp(name: "code") do %> <%= ui_input_otp_group do %> <%= ui_input_otp_slot(index: 0) %> <%= ui_input_otp_slot(index: 1) %> <% end %> <% end %> ``` --- ## drawer — Drawer A panel that slides up from the bottom of the screen. When to use: Mobile-friendly overlays anchored to the bottom edge. For a side panel use sheet; for a centred modal use dialog. Install: `shadwire add drawer` Requires Stimulus (importmap with eager loading). ### Ui::DrawerComponent — `ui_drawer` props: close_on_backdrop: true, class_name: nil, **attrs (free HTML attributes) ### Ui::Drawer::TriggerComponent — `ui_drawer_trigger` props: variant: :outline, size: :default, class_name: nil, **attrs (free HTML attributes) ### Ui::Drawer::ContentComponent — `ui_drawer_content` props: side: :bottom, class_name: nil, **attrs (free HTML attributes) ### Ui::Drawer::HeaderComponent — `ui_drawer_header` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Drawer::FooterComponent — `ui_drawer_footer` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Drawer::TitleComponent — `ui_drawer_title` props: tag_name: :h2, class_name: nil, **attrs (free HTML attributes) ### Ui::Drawer::DescriptionComponent — `ui_drawer_description` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Drawer::CloseComponent — `ui_drawer_close` props: variant: :outline, size: :default, class_name: nil, **attrs (free HTML attributes) ### Ui::ButtonComponent — `ui_button` variants: default | destructive | outline | secondary | ghost | link sizes: default | sm | lg | icon props: variant: :default, size: :default, tag: :button, disabled: false, focusable_when_disabled: false, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_drawer do %> <%= ui_drawer_trigger { ui_button(variant: :outline) { "Open" } } %> <%= ui_drawer_content do %> <%= ui_drawer_header { ui_drawer_title { "Filters" } } %> <% end %> <% end %> ``` --- ## context-menu — Context Menu A menu opened by right-clicking a region. When to use: Right-click actions on an element. For a button-triggered menu use dropdown-menu. Install: `shadwire add context-menu` Requires Stimulus (importmap with eager loading). ### Ui::ContextMenuComponent — `ui_context_menu` props: class_name: nil, **attrs (free HTML attributes) ### Ui::ContextMenu::TriggerComponent — `ui_context_menu_trigger` props: class_name: nil, **attrs (free HTML attributes) ### Ui::ContextMenu::ContentComponent — `ui_context_menu_content` props: class_name: nil, **attrs (free HTML attributes) ### Ui::ContextMenu::ItemComponent — `ui_context_menu_item` props: inset: false, variant: :default, disabled: false, tag: :button, class_name: nil, **attrs (free HTML attributes) ### Ui::ContextMenu::LabelComponent — `ui_context_menu_label` props: inset: false, class_name: nil, **attrs (free HTML attributes) ### Ui::ContextMenu::SeparatorComponent — `ui_context_menu_separator` props: class_name: nil, **attrs (free HTML attributes) ### Ui::ContextMenu::GroupComponent — `ui_context_menu_group` props: class_name: nil, **attrs (free HTML attributes) ### Ui::ContextMenu::ShortcutComponent — `ui_context_menu_shortcut` props: class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_context_menu do %> <%= ui_context_menu_trigger { "Right-click here" } %> <%= ui_context_menu_content do %> <%= ui_context_menu_item { "Copy" } %> <% end %> <% end %> ``` --- ## menubar — Menubar A horizontal application menu bar. When to use: Desktop-style application menus such as File and Edit. For a single button menu use dropdown-menu; for site navigation use navigation-menu. Install: `shadwire add menubar` Requires Stimulus (importmap with eager loading). ### Ui::MenubarComponent — `ui_menubar` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Menubar::MenuComponent — `ui_menubar_menu` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Menubar::TriggerComponent — `ui_menubar_trigger` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Menubar::ContentComponent — `ui_menubar_content` props: align: :start, class_name: nil, **attrs (free HTML attributes) ### Ui::Menubar::ItemComponent — `ui_menubar_item` props: inset: false, variant: :default, disabled: false, tag: :button, class_name: nil, **attrs (free HTML attributes) ### Ui::Menubar::SeparatorComponent — `ui_menubar_separator` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Menubar::LabelComponent — `ui_menubar_label` props: inset: false, class_name: nil, **attrs (free HTML attributes) ### Ui::Menubar::ShortcutComponent — `ui_menubar_shortcut` props: class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_menubar do %> <%= ui_menubar_menu do %> <%= ui_menubar_trigger { "File" } %> <%= ui_menubar_content { ui_menubar_item { "New" } } %> <% end %> <% end %> ``` --- ## navigation-menu — Navigation Menu Site navigation with optional rich dropdown panels. When to use: Primary site navigation in a header. For application menus use menubar; for showing location use breadcrumb. Install: `shadwire add navigation-menu` Requires Stimulus (importmap with eager loading). ### Ui::NavigationMenuComponent — `ui_navigation_menu` props: class_name: nil, **attrs (free HTML attributes) ### Ui::NavigationMenu::ListComponent — `ui_navigation_menu_list` props: class_name: nil, **attrs (free HTML attributes) ### Ui::NavigationMenu::ItemComponent — `ui_navigation_menu_item` props: class_name: nil, **attrs (free HTML attributes) ### Ui::NavigationMenu::TriggerComponent — `ui_navigation_menu_trigger` props: class_name: nil, **attrs (free HTML attributes) ### Ui::NavigationMenu::ContentComponent — `ui_navigation_menu_content` props: class_name: nil, **attrs (free HTML attributes) ### Ui::NavigationMenu::LinkComponent — `ui_navigation_menu_link` props: active: false, class_name: nil, **attrs (free HTML attributes) ### Ui::IconComponent — `ui_icon` sizes: sm | default | lg | xl props: size: :default, label: nil, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_navigation_menu do %> <%= ui_navigation_menu_list do %> <%= ui_navigation_menu_item do %> <%= ui_navigation_menu_trigger { "Products" } %> <%= ui_navigation_menu_content { ui_navigation_menu_link(href: "/a") { "Analytics" } } %> <% end %> <% end %> <% end %> ``` --- ## command — Command A searchable, keyboard-driven list of commands. When to use: Command palettes and searchable pickers. Put it inside dialog for a ⌘K palette; combine with popover for a combobox. Install: `shadwire add command` Requires Stimulus (importmap with eager loading). ### Ui::CommandComponent — `ui_command` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Command::InputComponent — `ui_command_input` props: placeholder: nil, class_name: nil, **attrs (free HTML attributes) ### Ui::Command::ListComponent — `ui_command_list` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Command::EmptyComponent — `ui_command_empty` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Command::GroupComponent — `ui_command_group` props: heading: nil, class_name: nil, **attrs (free HTML attributes) ### Ui::Command::ItemComponent — `ui_command_item` props: value: nil, disabled: false, class_name: nil, **attrs (free HTML attributes) ### Ui::Command::SeparatorComponent — `ui_command_separator` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Command::ShortcutComponent — `ui_command_shortcut` props: class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_command do %> <%= ui_command_input(placeholder: "Type a command…") %> <%= ui_command_list do %> <%= ui_command_empty { "No results." } %> <%= ui_command_group(heading: "Suggestions") { ui_command_item { "Calendar" } } %> <% end %> <% end %> ``` --- ## calendar — Calendar A month grid for selecting a date or a date range. When to use: Showing an inline month view, for a single date or a range. For a date field that opens the grid in a popover use date-picker. Install: `shadwire add calendar` Requires Stimulus (importmap with eager loading). ### Ui::CalendarComponent — `ui_calendar` props: month: nil, selected: nil, from: nil, to: nil, min: nil, max: nil, name: nil, end_name: nil, mode: :single, number_of_months: 1, caption_layout: :label, year_range: nil, dir: nil, week_start: 0, month_names: nil, day_names: nil, class_name: nil, **attrs (free HTML attributes) ```erb <%# Inline it draws no frame of its own — a popover would. Add one here. %> <%= ui_calendar(name: "starts_on", selected: Date.current, class: "border") %> ``` ```erb <%# Range: two hidden inputs, two months side by side. %> <%= ui_calendar( mode: :range, selected: Date.current..(Date.current + 7), number_of_months: 2, name: "trip[starts_on]", end_name: "trip[ends_on]" ) %> ``` ```erb <%# Month and year selects instead of a label. min:/max: disable the days outside the range and bound the year select. %> <%= ui_calendar(name: "born_on", caption_layout: :dropdown, max: Date.current) %> ``` ```erb <%# Mirrored grid and localized labels. %> <%= ui_calendar( name: "date", dir: :rtl, month_names: I18n.t("date.month_names").compact, day_names: I18n.t("date.abbr_day_names") ) %> ``` --- ## resizable — Resizable Panels the user can resize by dragging a handle. When to use: Split layouts with user-adjustable panes, such as a list and detail view. Install: `shadwire add resizable` Requires Stimulus (importmap with eager loading). ### Ui::ResizablePanelGroupComponent — `ui_resizable_panel_group` props: direction: :horizontal, class_name: nil, **attrs (free HTML attributes) ### Ui::ResizablePanelComponent — `ui_resizable_panel` props: default_size: nil, class_name: nil, **attrs (free HTML attributes) ### Ui::ResizableHandleComponent — `ui_resizable_handle` props: with_handle: false, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_resizable_panel_group(direction: :horizontal) do %> <%= ui_resizable_panel { "List" } %> <%= ui_resizable_handle %> <%= ui_resizable_panel { "Detail" } %> <% end %> ``` --- ## carousel — Carousel A horizontally or vertically swipeable set of slides. When to use: Browsing images or cards one at a time. For a scrollable region without slide semantics use scroll-area. Install: `shadwire add carousel` Requires Stimulus (importmap with eager loading). ### Ui::CarouselComponent — `ui_carousel` props: orientation: :horizontal, class_name: nil, **attrs (free HTML attributes) ### Ui::Carousel::ContentComponent — `ui_carousel_content` props: orientation: :horizontal, class_name: nil, **attrs (free HTML attributes) ### Ui::Carousel::ItemComponent — `ui_carousel_item` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Carousel::PreviousComponent — `ui_carousel_previous` props: class_name: nil, **attrs (free HTML attributes) ### Ui::Carousel::NextComponent — `ui_carousel_next` props: class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_carousel do %> <%= ui_carousel_content do %> <%= ui_carousel_item { "Slide 1" } %> <%= ui_carousel_item { "Slide 2" } %> <% end %> <%= ui_carousel_previous %> <%= ui_carousel_next %> <% end %> ``` --- ## combobox — Combobox A searchable single-select built from popover, command, and button. When to use: Choosing one option from a long list where typing to filter matters. For short lists use select; for the native control use native-select. This item installs its parts rather than a Ui::ComboboxComponent — compose them yourself. Install: `shadwire add combobox` ```erb <%= ui_popover do %> <%= ui_popover_trigger { ui_button(variant: :outline) { "Select framework…" } } %> <%= ui_popover_content do %> <%= ui_command do %> <%= ui_command_input(placeholder: "Search…") %> <%= ui_command_list { ui_command_item { "Rails" } } %> <% end %> <% end %> <% end %> ``` --- ## date-picker — Date Picker A date field that opens a calendar in a popover. When to use: Picking a date in a form: as a button, as a typed text field, as a range, or as a date of birth. For an always-visible month grid use calendar. There is no Ui::DatePickerComponent — this item installs the parts plus the ui-date-picker controller that keeps the field and the calendar in step, and you compose them. Install: `shadwire add date-picker` Requires Stimulus (importmap with eager loading). ```erb <%# Basic. The calendar owns the value; ui-date-picker only writes the picked date into the label and closes the popover. %>
<%= ui_popover do %> <%= ui_popover_trigger(variant: :outline, class: "w-[212px] justify-between font-normal") do %> Pick a date <%= ui_icon("chevron-down", class: "opacity-50") %> <% end %> <%= ui_popover_content(align: :start, class: "w-auto! p-0!") do %> <%= ui_calendar(name: "due_on") %> <% end %> <% end %>
``` ```erb <%# Labelled. The trigger is a plain button, so id:/for: tie it to the label. %> <%= ui_field(class: "w-48", data: { controller: "ui-date-picker" }) do %> <%= ui_field_label(for: "checkin") { "Date" } %> <%# ...the same Popover, with ui_popover_trigger(id: "checkin", ...) %> <% end %> ``` ```erb <%# Range. Same shell, range calendar: name: is the start, end_name: the end. The popover closes on the second pick, not the first. %> <%= ui_calendar( mode: :range, selected: Date.current..(Date.current + 20), number_of_months: 2, name: "stay[from]", end_name: "stay[to]" ) %> ``` ```erb <%# Date of birth. Month/year selects for the decade-long jump, and max: keeps the future out — it bounds the year select too. %> <%= ui_calendar(name: "profile[born_on]", caption_layout: :dropdown, max: Date.current) %> ``` ```erb <%# Text input. What is typed is parsed into the calendar, what is picked comes back formatted. Down arrow opens the popover. %> <%= ui_field(class: "w-60", data: { controller: "ui-date-picker" }) do %> <%= ui_field_label(for: "subscribed-on") { "Subscription date" } %> <%= ui_input_group do %> <%= ui_input_group_input(id: "subscribed-on", autocomplete: "off", data: { ui_date_picker_target: "input" }) %> <%= ui_input_group_addon(align: :inline_end) do %> <%# Popover + Calendar, triggered by ui_popover_trigger(variant: :ghost, size: :icon, class: "size-6! rounded-sm") %> <% end %> <% end %> <% end %> ``` ```erb <%# Date and time. The picker owns the day; a native time input owns the clock. %> <%= ui_input(type: :time, name: "meeting[at]", step: 1, value: "10:30:00", class: "appearance-none bg-background [&::-webkit-calendar-picker-indicator]:hidden") %> ``` ```erb <%# Natural language. Accepts "today", "in 2 weeks", "next friday". The phrase table lives in the Stimulus controller this item installs — extend it for the locales you ship. %> <%= ui_field(data: { controller: "ui-date-picker", ui_date_picker_natural_language_value: "true" }) do %> <%# ...input-group as above, plus an echo of the parsed date: %> <%= ui_field_description do %> Publishing on . <% end %> <% end %> ``` ```erb <%# RTL. dir: :rtl mirrors the grid, the nav arrows and the ← → keys. %> <%= ui_calendar( name: "date", dir: :rtl, month_names: I18n.t("date.month_names").compact, day_names: I18n.t("date.abbr_day_names") ) %> ``` --- ## sonner — Sonner Transient toast notifications. When to use: Brief confirmations that do not need acknowledgement. For messages that stay on the page use alert; for ones requiring a decision use alert-dialog. Install: `shadwire add sonner` Requires Stimulus (importmap with eager loading). ### Ui::SonnerComponent — `ui_sonner` props: position: :"bottom-right", duration: 4000, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_sonner %> ``` --- ## chart — Chart Chart.js charts using the Shadwire theme tokens. When to use: Visualising series data. Pins chart.js via importmap and needs Stimulus. For plain tabular numbers use table. Install: `shadwire add chart` Requires Stimulus (importmap with eager loading). ### Ui::ChartComponent — `ui_chart` props: type: :bar, data: {}, options: {}, label: nil, class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_chart(type: :bar, data: { labels: %w[Jan Feb], datasets: [{ label: "Sales", data: [12, 19] }] }) %> ``` --- ## data-table — Data Table A table with sorting, filtering, pagination, and row selection. When to use: Tabular data that needs interaction out of the box. When you only need markup and styling use table. Install: `shadwire add data-table` Requires Stimulus (importmap with eager loading). ### Ui::DataTableComponent — `ui_data_table` props: columns: (required), rows: (required), filter_key: nil, filter_placeholder: "Filtrar…", page_size: 5, selectable: true, empty_text: "Sem resultados.", class_name: nil, **attrs (free HTML attributes) ```erb <%= ui_data_table(columns: [{ key: :name, label: "Name" }], rows: @users.map { |u| { name: u.name } }, filter_key: :name) %> ```