<script setup lang="ts">
import { Bookmark, Download, Pencil, Share2, Trash2 } from '@lucide/vue'
import { IconButton, Inline } from '@hina-ui/vue'
</script>
<template>
<Inline>
<IconButton label="Edit">
<Pencil />
</IconButton>
<IconButton label="Bookmark">
<Bookmark />
</IconButton>
<IconButton label="Share">
<Share2 />
</IconButton>
<IconButton label="Download">
<Download />
</IconButton>
<IconButton label="Delete" tone="danger">
<Trash2 />
</IconButton>
</Inline>
</template>
Usage
import { IconButton } from '@hina-ui/vue'
label is required and serves as both the accessible name and the tooltip text. The default slot holds the icon.
<script setup lang="ts">
import { Search } from '@lucide/vue'
import { IconButton } from '@hina-ui/vue'
</script>
<template>
<IconButton label="Search">
<Search />
</IconButton>
</template>
Toolbars, the end of table rows and the top corner of cards are where space is tight and this component fits; actions with visible text still use Button.
Examples
Variants and tones
Variants and tones are identical to Button, but the defaults differ. IconButton defaults to ghost and neutral, because it usually appears in dense interfaces where it should not stand out.
<script setup lang="ts">
import { Settings, Trash2 } from '@lucide/vue'
import { IconButton, Inline } from '@hina-ui/vue'
</script>
<template>
<Inline>
<IconButton label="Settings" variant="solid" tone="accent">
<Settings />
</IconButton>
<IconButton label="Settings" variant="soft">
<Settings />
</IconButton>
<IconButton label="Settings" variant="outline">
<Settings />
</IconButton>
<IconButton label="Settings">
<Settings />
</IconButton>
<IconButton label="Delete" variant="soft" tone="danger">
<Trash2 />
</IconButton>
</Inline>
</template>
Sizes
Three sizes. The button is square, with a side equal to the height of a Button at the same step.
<script setup lang="ts">
import { Star } from '@lucide/vue'
import { IconButton, Inline } from '@hina-ui/vue'
</script>
<template>
<Inline align="center">
<IconButton label="Small" size="sm" variant="outline">
<Star />
</IconButton>
<IconButton label="Medium" size="md" variant="outline">
<Star />
</IconButton>
<IconButton label="Large" size="lg" variant="outline">
<Star />
</IconButton>
</Inline>
</template>
States
With loading the button shows a spinner and refuses clicks; with disabled it is unavailable. The size stays the same while loading.
<script setup lang="ts">
import { ref } from 'vue'
import { RotateCw, Trash2 } from '@lucide/vue'
import { IconButton, Inline } from '@hina-ui/vue'
const refreshing = ref(false)
function refresh() {
refreshing.value = true
setTimeout(() => (refreshing.value = false), 2000)
}
</script>
<template>
<Inline>
<IconButton label="Refresh" variant="outline" :loading="refreshing" @click="refresh">
<RotateCw />
</IconButton>
<IconButton label="Delete" variant="outline" tone="danger" disabled>
<Trash2 />
</IconButton>
</Inline>
</template>
Tooltip
side sets where the tooltip appears, defaulting to above. Setting tooltip to false keeps the accessible name without showing a tooltip.
<script setup lang="ts">
import { Copy, Download, Heart, Search } from '@lucide/vue'
import { IconButton, Inline } from '@hina-ui/vue'
</script>
<template>
<Inline>
<IconButton label="Tooltip above" variant="outline">
<Search />
</IconButton>
<IconButton label="Tooltip on the right" side="right" variant="outline">
<Copy />
</IconButton>
<IconButton label="Tooltip below" side="bottom" variant="outline">
<Download />
</IconButton>
<IconButton label="No tooltip" :tooltip="false" variant="outline">
<Heart />
</IconButton>
</Inline>
</template>
The tooltip relies on a TooltipProvider at the application root. Without one the button still works, only without the tooltip, and aria-label is unaffected.
Accessibility
labelis required and becomes thearia-labelthat screen readers announce.- Icons are invisible to screen readers, so never drop
labeland rely on the icon alone to carry meaning. - A loading button carries
aria-busy, which screen readers announce as busy.
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | — | Required. Accessible name, also the tooltip text |
tooltip | boolean | true | Whether to show a tooltip on hover |
side | 'top' | 'right' | 'bottom' | 'left' | 'top' | Where the tooltip appears |
as | string | Component | 'button' | Element or component to render |
asChild | boolean | false | Render nothing, merging into the single child |
variant | 'solid' | 'soft' | 'outline' | 'ghost' | 'link' | 'ghost' | Visual style |
tone | 'accent' | 'neutral' | 'danger' | 'neutral' | Semantic tone |
size | 'xs' | 'sm' | 'md' | 'lg' | 'md' | Size |
type | 'button' | 'submit' | 'reset' | 'button' | Native button type |
pill | boolean | false | Render as a circle |
loading | boolean | false | Whether it is loading |
disabled | boolean | false | Whether it is disabled |
class | string | — | Classes appended to the root |
Slots
| Slot | Description |
|---|---|
default | The icon, replaced by a spinner while loading |