Icons
Icons come from lucide-rails, the Rails port of Lucide, which is the icon set shadcn/ui uses. ui_icon wraps lucide_icon and adds shadcn's sizes.
The application needs the lucide-rails gem.
shadwire init adds it, and
bin/shadwire status --json lists it under gems.
Names are Lucide's, in kebab-case
You can browse the names at lucide.dev/icons.
Sizes
| Size | Class | Typical use |
|---|---|---|
:sm |
size-3 |
Inside badges, supporting text and breadcrumbs. |
:default |
size-4 |
Buttons, menu items, fields. |
:lg |
size-5 |
Headings, tiles, empty states. |
:xl |
size-6 |
Illustrations and feature callouts. |
Use these sizes rather than class: "w-3 h-3", so your icons change
along with everything else if the scale is ever adjusted.
Icons are decorative by default
ui_icon renders aria-hidden="true". That is what
you want next to a text label, because the text already names the control.
When the icon is the only content, it has to provide the name. Pass
label:, which sets role="img" and
aria-label and drops the aria-hidden:
Putting the label on the button instead of the icon works just as well. What doesn't work is leaving both without one.
There is no icon: prop
No component takes an icon as an argument. As in shadcn/ui, you put the icon in the content.
ButtonComponent has [&_svg] utilities that size
and space a nested icon, so it needs no extra class. Use size: :icon
for a button with only an icon.
Join the icon and the text with safe_join rather than building a
string. ui_icon("save") + " Save" also works, because the icon is
already HTML-safe, but safe_join makes the intent clear and escapes
interpolated text properly.