<script setup lang="ts">
import { AlignCenter, AlignLeft, AlignRight } from '@lucide/vue'
import { ButtonGroup, IconButton } from '@hina-ui/vue'
</script>
<template>
<ButtonGroup label="Alignment">
<IconButton label="Align left" variant="outline">
<AlignLeft />
</IconButton>
<IconButton label="Align center" variant="outline">
<AlignCenter />
</IconButton>
<IconButton label="Align right" variant="outline">
<AlignRight />
</IconButton>
</ButtonGroup>
</template>
Usage
import { ButtonGroup } from '@hina-ui/vue'
Put the buttons straight into the group; joining them is the container’s job. The outer corners at each end are kept, the corners where two buttons meet are dropped, and adjacent borders overlap into one rule rather than sitting side by side.
<script setup lang="ts">
import { Button, ButtonGroup } from '@hina-ui/vue'
</script>
<template>
<ButtonGroup label="View">
<Button variant="outline" tone="neutral">List</Button>
<Button variant="outline" tone="neutral">Grid</Button>
<Button variant="outline" tone="neutral">Timeline</Button>
</ButtonGroup>
</template>
Buttons in a group should share one variant and one tone. Only actions of equal standing belong together; actions of different weight should be placed apart.
Examples
Variants
Variant and tone are set on each button; the container does not assign them.
<script setup lang="ts">
import { Button, ButtonGroup, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack align="center">
<ButtonGroup label="Solid">
<Button variant="solid" tone="accent">Back</Button>
<Button variant="solid" tone="accent">Save</Button>
<Button variant="solid" tone="accent">Next</Button>
</ButtonGroup>
<ButtonGroup label="Soft">
<Button variant="soft" tone="accent">Back</Button>
<Button variant="soft" tone="accent">Save</Button>
<Button variant="soft" tone="accent">Next</Button>
</ButtonGroup>
<ButtonGroup label="Outline">
<Button variant="outline" tone="neutral">Back</Button>
<Button variant="outline" tone="neutral">Save</Button>
<Button variant="outline" tone="neutral">Next</Button>
</ButtonGroup>
</Stack>
</template>
Sizes
Size is likewise set on the buttons. A group must use one size throughout, otherwise the heights of neighbouring buttons will not line up.
<script setup lang="ts">
import { Button, ButtonGroup, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack align="center">
<ButtonGroup label="Small">
<Button size="sm" variant="outline" tone="neutral">Day</Button>
<Button size="sm" variant="outline" tone="neutral">Week</Button>
<Button size="sm" variant="outline" tone="neutral">Month</Button>
</ButtonGroup>
<ButtonGroup label="Medium">
<Button variant="outline" tone="neutral">Day</Button>
<Button variant="outline" tone="neutral">Week</Button>
<Button variant="outline" tone="neutral">Month</Button>
</ButtonGroup>
<ButtonGroup label="Large">
<Button size="lg" variant="outline" tone="neutral">Day</Button>
<Button size="lg" variant="outline" tone="neutral">Week</Button>
<Button size="lg" variant="outline" tone="neutral">Month</Button>
</ButtonGroup>
</Stack>
</template>
Vertical
With orientation="vertical" the group joins vertically, the shared edges move to the top and bottom, and the width of the group is set by its widest item.
<script setup lang="ts">
import { Copy, Scissors, Trash2 } from '@lucide/vue'
import { Button, ButtonGroup } from '@hina-ui/vue'
</script>
<template>
<ButtonGroup label="File actions" orientation="vertical" divider>
<Button variant="soft" tone="neutral">
<template #icon>
<Copy />
</template>
Copy
</Button>
<Button variant="soft" tone="neutral">
<template #icon>
<Scissors />
</template>
Cut
</Button>
<Button variant="soft" tone="neutral">
<template #icon>
<Trash2 />
</template>
Delete
</Button>
</ButtonGroup>
</template>
Full width
With block the group fills the width of its container and the buttons divide the remaining space equally. This form suits dialog footers and mobile layouts.
<script setup lang="ts">
import { Button, ButtonGroup } from '@hina-ui/vue'
</script>
<template>
<ButtonGroup label="Vote" block>
<Button variant="outline" tone="neutral">For</Button>
<Button variant="outline" tone="neutral">Abstain</Button>
<Button variant="outline" tone="neutral">Against</Button>
</ButtonGroup>
</template>
Dividers
When buttons share a background colour, the boundary between them is not obvious. divider adds a thin rule between them, shorter than the full edge, coloured from the current text colour.
<script setup lang="ts">
import { Button, ButtonGroup, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack align="center">
<ButtonGroup label="Without divider">
<Button variant="soft" tone="neutral">Copy</Button>
<Button variant="soft" tone="neutral">Cut</Button>
<Button variant="soft" tone="neutral">Paste</Button>
</ButtonGroup>
<ButtonGroup label="With divider" divider>
<Button variant="soft" tone="neutral">Copy</Button>
<Button variant="soft" tone="neutral">Cut</Button>
<Button variant="soft" tone="neutral">Paste</Button>
</ButtonGroup>
</Stack>
</template>
Mixing in icon buttons
Button and IconButton can be joined in one group; a common pattern is a primary action followed by a dropdown trigger.
<script setup lang="ts">
import { ChevronDown } from '@lucide/vue'
import { Button, ButtonGroup, IconButton } from '@hina-ui/vue'
</script>
<template>
<ButtonGroup label="Publish">
<Button>Publish now</Button>
<IconButton label="More publish options" variant="solid" tone="accent">
<ChevronDown />
</IconButton>
</ButtonGroup>
</template>
Unavailable
The container has no disabled property. To disable a whole group, set disabled on each button; a single button can also be disabled on its own.
<script setup lang="ts">
import { Button, ButtonGroup, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack align="center">
<ButtonGroup label="Whole group disabled">
<Button variant="outline" tone="neutral" disabled>Bold</Button>
<Button variant="outline" tone="neutral" disabled>Italic</Button>
<Button variant="outline" tone="neutral" disabled>Underline</Button>
</ButtonGroup>
<ButtonGroup label="One disabled">
<Button variant="outline" tone="neutral" disabled>Previous</Button>
<Button variant="outline" tone="neutral">Refresh</Button>
<Button variant="outline" tone="neutral">Next</Button>
</ButtonGroup>
</Stack>
</template>
Custom styles
class is appended to the container. To change the corners at each end, set them from the container with child selectors rather than writing them on every button.
<script setup lang="ts">
import { Button, ButtonGroup } from '@hina-ui/vue'
</script>
<template>
<ButtonGroup
label="Sort"
class="[&>*:first-child]:rounded-s-full [&>*:last-child]:rounded-e-full"
>
<Button variant="soft" tone="neutral" class="px-6">Newest</Button>
<Button variant="soft" tone="neutral" class="px-6">Popular</Button>
<Button variant="soft" tone="neutral" class="px-6">Rating</Button>
</ButtonGroup>
</template>
Behaviour
- Buttons in a group no longer scale on press.
- The focused button is raised above the others, so its focus ring is never covered by a neighbour.
- The container is a
role="group"andlabelbecomes the accessible name of the whole group. A vertical group also carriesaria-orientation="vertical".
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | — | Accessible name of the group |
orientation | 'horizontal' | 'vertical' | 'horizontal' | Direction in which buttons are joined |
block | boolean | false | Fill the container and divide space evenly |
divider | boolean | false | Show a rule between adjacent buttons |
class | string | — | Classes appended to the root |
Slots
| Slot | Description |
|---|---|
default | The buttons in the group |