Components Buttons

Petal Pro is the full SaaS app this is built for

Auth, billing, admin, and Claude Code integration included. One purchase, unlimited projects.

Buttons

Triggers an action. Five variants, plus a semantic range for when the action carries meaning.
Variants

Five fill styles, one per action weight: solid carries the primary action, soft and light tint the surface (soft adapts its tint to dark mode, light stays light in both), outline and ghost recede to chrome. inverted and shadow still render but are legacy - prefer outline.

heex
<div class="flex flex-wrap items-center justify-center gap-3">
<.button>Solid</.button>
<.button variant="soft">Soft</.button>
<.button variant="light">Light</.button>
<.button variant="outline">Outline</.button>
<.button variant="ghost">Ghost</.button>
</div>
Semantic colours

For actions that carry meaning: info, success, warning and danger tint every variant - solid shown here; soften the signal with variant="soft" or "outline". primary, secondary and gray cover everything else.

heex
<div class="flex flex-wrap items-center justify-center gap-3">
<.button color="info">Info</.button>
<.button color="success">Success</.button>
<.button color="warning">Warning</.button>
<.button color="danger">Danger</.button>
</div>
Sizes

Five sizes, xs to xl, md the default. Padding, type and icon scale together, so a size change never needs a class override.

heex
<div class="flex flex-wrap items-center justify-center gap-3">
<.button size="xs">Extra small</.button>
<.button size="sm">Small</.button>
<.button size="md">Medium</.button>
<.button size="lg">Large</.button>
<.button size="xl">Extra large</.button>
</div>
States

icon renders a Heroicon beside the label; icon_placement="right" moves it after, for arrows and continues. loading swaps the icon's side for a spinner so the button never reflows mid-submit, and disabled mutes the button and blocks the click.

heex
<div class="flex flex-wrap items-center justify-center gap-3">
<.button>Default</.button>
<.button icon="hero-rocket-launch">With icon</.button>
<.button icon="hero-arrow-right" icon_placement="right">Continue</.button>
<.button loading>Loading</.button>
<.button disabled>Disabled</.button>
</div>
Icon button

size="icon" renders a square, md-height button for a lone glyph - toolbars, composers, close affordances. A glyph is not a name, so pair it with an aria-label. Every variant and colour applies.

heex
<div class="flex flex-wrap items-center justify-center gap-3">
<.button size="icon" aria-label="Submit">
<.icon name="hero-arrow-up" class="w-5 h-5" />
</.button>
<.button variant="outline" size="icon" aria-label="Add">
<.icon name="hero-plus" class="w-5 h-5" />
</.button>
<.button variant="ghost" size="icon" aria-label="Settings">
<.icon name="hero-cog-6-tooth" class="w-5 h-5" />
</.button>
</div>
Properties
<.button>
Attribute Type Default Description
class any nil CSS class
color string "primary" button color
one of: "primary", "secondary", "info", "success", "warning", "danger", "gray", "pure_white", "white", "light", "dark"
disabled boolean false indicates a disabled state
icon any nil name of a Heroicon rendered beside the label
icon_placement string "left" which side of the label the icon sits on - and where the loading spinner appears, so the layout doesn't jump when a button enters its loading state
one of: "left", "right"
label string nil labels your button
link_type string "button"
one of: "a", "live_patch", "live_redirect", "button"
loading boolean false indicates a loading state
radius string nil button border radius; when unset the --pc-radius theme token applies
one of: nil, "none", "sm", "md", "lg", "xl", "full"
rest global
size string "md" button sizes; icon renders a square, md-height icon button (pair with an aria-label)
one of: "xs", "sm", "md", "lg", "xl", "icon"
to string nil link path
variant string "solid" button variant. solid/soft/light/outline/ghost are the supported fill styles (soft adapts its tint to dark mode; light stays light in both modes). inverted and shadow are legacy, slated for removal in a future major - prefer outline, and compose effects (border_beam / shine_border) for flourish
one of: "solid", "soft", "light", "outline", "inverted", "shadow", "ghost"
with_icon boolean false legacy: icon spacing is part of the base button now; kept for compatibility, removed in 5.0
:inner_block slot slot
<.icon_button>
Attribute Type Default Description
class any nil CSS class
color string "gray"
one of: "primary", "secondary", "info", "success", "warning", "danger", "gray"
disabled boolean false indicates a disabled state
link_type string "button"
one of: "a", "live_patch", "live_redirect", "button"
loading boolean false indicates a loading state
radius string "full" button radius
one of: "none", "sm", "md", "lg", "xl", "full"
rest global
size string "sm"
one of: "xs", "sm", "md", "lg", "xl"
to string nil link path
tooltip string nil tooltip text
with_icon boolean false adds some icon base classes
:inner_block slot slot