Chip
A row of compact, removable entries that shares one value and one tab stop.
Chip is a row of compact entries the user can drop one by one: the tags on a post, the filters on a search, the people on an invitation. The row is one tab stop, the arrow keys move inside it, and Backspace removes whatever the user is standing on.
Glasgow
Bath
Nuremberg
Munich
<template>
<div class="flex flex-col items-start gap-4">
<Chip.Group v-model="cities" aria-label="Cities" :class="group">
<template #default="{ entries, empty }">
<Chip.Root
v-for="entry in entries"
:key="entry.key"
:class="chip"
:value="entry.value"
>
<svg class="size-4 shrink-0" viewBox="0 0 16 16" aria-hidden="true">
<path
d="M8 14s4.5-4.2 4.5-7.5a4.5 4.5 0 1 0-9 0C3.5 9.8 8 14 8 14Z"
fill="none"
stroke="currentColor"
stroke-width="1.5"
stroke-linejoin="round"
/>
<circle cx="8" cy="6.5" r="1.5" fill="currentColor" />
</svg>
<span class="truncate">{{ entry.label }}</span>
<Chip.Remove :class="remove">
<svg class="size-4" viewBox="0 0 16 16" aria-hidden="true">
<path
d="m4.5 4.5 7 7m0-7-7 7"
fill="none"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
/>
</svg>
</Chip.Remove>
</Chip.Root>
<span v-if="empty" class="type-component-sm text-surface-muted">
Nothing left to remove.
</span>
</template>
</Chip.Group>
<Button :class="reset" @click="cities = [...defaults]">Reset</Button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Button } from '@maas/mirror/vue/Button'
import { Chip } from '@maas/mirror/vue/Chip'
const defaults = ['Glasgow', 'Bath', 'Nuremberg', 'Munich']
const cities = ref([...defaults])
const group = 'flex min-h-8 flex-wrap items-center gap-1.5'
const chip = [
'inline-flex h-8 max-w-full shrink-0 items-center gap-1.5 px-1.5',
'rounded-component-sm border-2 border-[transparent] type-component-md',
'bg-primary-subtle text-primary-on-subtle',
'transition-all duration-100 ease-linear',
'outline-4 outline-transparent focus-visible:focus-ring',
'hover:bg-primary-subtle-hover active:bg-primary-subtle-active',
'data-[disabled=true]:bg-disabled-subtle data-[disabled=true]:text-disabled-on-subtle',
].join(' ')
const remove = [
'flex size-6 shrink-0 items-center justify-center -mr-1',
'rounded-component-compact-sm border-2 border-[transparent]',
'transition-all duration-100 ease-linear',
'outline-4 outline-transparent focus-visible:focus-ring',
'hover:opacity-70 active:opacity-50',
].join(' ')
const reset = [
'inline-flex h-9 items-center justify-center px-3.5',
'rounded-component-md border-2 border-[transparent] type-component-sm',
'bg-primary-solid text-primary-on-solid',
'transition-all duration-100 ease-linear',
'outline-4 outline-transparent focus-visible:focus-ring',
'hover:bg-primary-solid-hover active:bg-primary-solid-active',
].join(' ')
</script>
Usage guidelines
- Only one kind of chip needs a component of its own: the removable collection. The other kinds are compositions of components that already exist. The Chip types section below has all four.should
- Reach for
Chip.Groupwhen the entries can be dropped and the set is the value. When the entries are only a display of a selection made somewhere else, useCombobox.ChipGroupinstead: it reads the combobox’s own value and hands focus back to the input.should - The row is one tab stop, so keep it short enough to walk through with the arrow keys. Give a long collection a
ScrollAreaor a search instead.should - The remove buttons sit outside the tab order: Backspace on a focused chip is the keyboard route, and a second stop per chip would double the length of the row for anyone tabbing through the page.may
- Chips ship unstyled. The preview above and the recipes below use the same utility classes as the rest of the documentation; there is no chip-specific token.may