Skip to content

Chip

A row of compact, removable entries that shares one value and one tab stop.

View source View as Markdown

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
App.vue
<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.Group when the entries can be dropped and the set is the value. When the entries are only a display of a selection made somewhere else, use Combobox.ChipGroup instead: 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 ScrollArea or 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