Icon button
Offer a compact action, such as Search or Close, with an icon and an accessible name for screen readers.
INV-0042
Acme Corp
INV-0043
Birch & Co
INV-0044
Cedar Ltd
<script setup lang="ts">import { Columns3, Copy, Pencil, Plus, SlidersHorizontal, Trash2 } from '@lucide/vue'import { UiStack, UiIconButton, UiButton, UiCard, UiList, UiListItem } from 'grani' const INVOICES = [ { id: 'INV-0042', client: 'Acme Corp' }, { id: 'INV-0043', client: 'Birch & Co' }, { id: 'INV-0044', client: 'Cedar Ltd' },]</script> <template> <UiStack gap="tight" class="w-full max-w-md"> <div class="flex items-center justify-end gap-2"> <UiIconButton :icon="SlidersHorizontal" ariaLabel="Filters" /> <UiIconButton :icon="Columns3" ariaLabel="Columns" /> <UiButton><Plus aria-hidden="true" />New invoice</UiButton> </div> <UiCard :padded="false"> <UiList> <UiListItem v-for="row in INVOICES" :key="row.id" :title="row.id" :description="row.client" > <template #actions> <UiIconButton :icon="Pencil" variant="ghost" :ariaLabel="`Edit ${row.id}`" /> <UiIconButton :icon="Copy" variant="ghost" :ariaLabel="`Duplicate ${row.id}`" /> <UiIconButton :icon="Trash2" variant="ghost" tone="danger" :ariaLabel="`Delete ${row.id}`" /> </template> </UiListItem> </UiList> </UiCard> </UiStack></template>
Also called icon-only button, glyph button.
Usage
<UiIconButton :aria-label="ariaLabel">…</UiIconButton> With GraniResolver in the Vite config, a template needs no import: set it up once.
API
| Name · type | Default | Description |
|---|---|---|
| State | ||
| loadingboolean= false | false | A request is running: spinner instead of the glyph, aria-busy, no clicks; focus stays, as in UiButton. |
| Behavior | ||
| typebutton | submit | reset= "button" | "button" | Native type; submit sends the form the button sits in. |
| disabledReasonstring | Why it cannot be pressed now, while disabled, as UiButton's: it stays in the Tab order, refuses the press, and says the reason in a tooltip and as its description. | |
| pressedboolean | A toggle (pin, bookmark), as UiButton's pressed: aria-pressed, and on, the brand frame and plate. Unset, it is no toggle. A primary square is never one. | |
| Look | ||
| iconComponent | A Lucide icon component, e.g. Pencil from @lucide/vue. | |
| variantoutline | primary | ghost= "outline" | "outline" | Presentation: primary is the brand fill (the arrow of a split button), outline a white square with a border, ghost only the glyph until hovered. |
| tonebrand | neutral | danger= "neutral" | "neutral" | Meaning of the glyph: gray, brand, or a dangerous action. pressed on overrides it with the brand frame. |
| sizexs | sm | md | Step of the control ladder: xs 28px for a dismiss in a corner, sm 36px, md 44px for forms. Unset, the app's size (createGrani({ size })), sm by default. | |
| Form and access | ||
| ariaLabel*string | The accessible name, for a screen reader; no hint shows it, so wrap the button in UiTooltip where a sighted user needs it. Required: the button has no visible text. | |
| disabledboolean | Natively disabled: out of the Tab order, grayed; with disabledReason, focusable and saying why. | |
Attributes are placed by the component itself: its description says where.
When to use another component
| When you need | Take |
|---|---|
| Text | Button |
| Copying a value | Copy button |
| A choice among options | Radio group segmented |
| A primary square as a toggle | Never; it is the arrow of a split button in Button group |