Autocomplete
A text input that suggests as you type, where the text is the value.
Autocomplete is a text field that offers suggestions while the user types, and
the text in the field is the value. Reach for it when anything the user writes
is acceptable and the list is there to save typing. When only a value from the
list will do, that is a Combobox.
Start typing to search
<template>
<div class="flex w-64 flex-col gap-1.5">
<Autocomplete.Root v-model="value" :items="fonts" :options="options">
<Autocomplete.InputGroup
class="group border-surface rounded-component-lg has-[:focus-visible]:focus-ring data-[content-open=true]:focus-ring relative isolate flex h-12 w-full cursor-text items-center justify-between gap-1.5 border-2 px-3.5 outline-4 outline-transparent transition-all duration-100 ease-linear"
>
<label
class="flex max-h-full w-full cursor-text flex-col-reverse justify-center gap-0 px-1 transition-all duration-100 ease-linear group-has-[:focus]:gap-1 group-has-[input:not(:placeholder-shown)]:gap-1 before:absolute before:-inset-0.5 before:-z-10 before:content-['']"
>
<span
class="relative flex h-0 w-full items-end transition-all duration-100 ease-linear group-has-[:focus]:h-[0.9375rem] group-has-[input:not(:placeholder-shown)]:h-[0.9375rem]"
>
<Autocomplete.Input
class="text-primary-solid type-component-lg -my-1.25 box-content block h-[1lh] w-full min-w-0 bg-transparent py-1.25 [line-height:normal]! transition-colors duration-100 ease-linear outline-none placeholder:text-transparent"
placeholder=" "
/>
</span>
<span
class="text-primary-muted type-component-lg group-has-[:focus]:type-component-2xs group-has-[input:not(:placeholder-shown)]:type-component-2xs flex w-full items-center [line-height:normal]! transition-all duration-100 ease-linear"
>
Font
</span>
</label>
<Autocomplete.Clear
class="text-primary-muted hover:text-primary-solid flex shrink-0 items-center transition-all duration-100 ease-linear"
>
<svg class="size-4.5" 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>
</Autocomplete.Clear>
<Autocomplete.Trigger
class="text-primary-muted group-has-[input:not(:placeholder-shown)]:text-primary-solid flex shrink-0 items-center transition-all duration-100 ease-linear"
>
<Autocomplete.Icon
class="flex items-center transition-transform duration-100 ease-linear data-[content-open=true]:rotate-180"
>
<svg class="size-4.5" viewBox="0 0 16 16" aria-hidden="true">
<path
d="m4 6.5 4 4 4-4"
fill="none"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</Autocomplete.Icon>
</Autocomplete.Trigger>
</Autocomplete.InputGroup>
<Autocomplete.Status
v-slot="{ count, listEmpty, contentOpen }"
class="type-component-2xs text-primary-muted px-1"
>
<template v-if="contentOpen">
<template v-if="listEmpty">No matching fonts</template>
<span v-else class="tabular-nums">{{ count }} fonts</span>
</template>
<template v-else>Start typing to search</template>
</Autocomplete.Status>
<Autocomplete.Content
class="rounded-component-2xl bg-primary-inverted shadow-component-high w-(--rack-floating-anchor-width) overflow-y-auto p-1.5 backdrop-blur-[8rem]"
>
<Autocomplete.Collection v-slot="{ item, value: font }">
<Autocomplete.Item
class="rounded-component-md type-component-lg text-primary-on-subtle hover:bg-primary-subtle active:bg-primary-subtle-hover data-[highlighted=true]:bg-primary-subtle data-[disabled=true]:text-disabled-on-subtle flex h-12 w-full items-center justify-start px-5.5 transition-colors duration-100 ease-linear select-none [--mirror-autocomplete-item-cursor:pointer]"
:value="font"
>
<span class="truncate">{{ item }}</span>
</Autocomplete.Item>
</Autocomplete.Collection>
<Autocomplete.Empty
class="type-component-lg text-primary-muted flex h-12 items-center pl-5.5"
>
No matching fonts.
</Autocomplete.Empty>
</Autocomplete.Content>
</Autocomplete.Root>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Autocomplete } from '@maas/mirror/vue/Autocomplete'
const options = {
initial: { highlight: true },
openOnInputClick: true,
teleport: { disabled: true },
floating: { sideOffset: 28 },
}
const fonts = [
'Alte Haas Grotesk',
'Basis Grotesque',
'Bely Display',
'Cardinal Fruit',
'Domaine Text',
'Editorial New',
'Fraunces',
'Gambetta',
'Instrument Serif',
'Louize',
]
const value = ref('')
</script>
Usage guidelines
- Hand the root an
itemsarray and it filters for you. Passfilter: nullwhen the narrowing already happened somewhere else, on a server for instance, and render whatever you were given.should - Make sure the autocomplete has an accessible name, either through a
Labelinside aFieldor anaria-labelon the input.must Autocomplete.Statusis a live region, so keep it mounted outside the popup. A region that appears together with its first text is announced unreliably.mustAutocomplete.Contentteleports to the end of<body>unless you turn that off withoptions.teleport.disabled. Leave it on where the field sits inside anything withoverflow: hidden.should- The content resolves everything but
sideandalignonce, during setup, so change the rest before the popup first renders or key the root to remount it.must