Recipient Field
A To field where every chosen person becomes a tag. data-combobox-label keeps the email out of the tags, a custom filter matches it, and ::part(chip) styles the tags.
No one in your contacts matches
Code
The following example assumes that you have imported the components and set up the theme.
<div class="recipient-field">37 collapsed lines
<label for="recipients">To</label>
<sv-combobox id="recipients" class="recipients" multiple name="to" placeholder="Add people"> <sv-flyout> <button type="button" class="contact" aria-selected="true" value="maya@example.com"> <span data-combobox-label>Maya Chen</span> <span class="email">maya@example.com</span> </button>
<button type="button" class="contact" value="omar@example.com"> <span data-combobox-label>Omar Haddad</span> <span class="email">omar@example.com</span> </button>
<button type="button" class="contact" value="lena@example.com"> <span data-combobox-label>Lena Fischer</span> <span class="email">lena@example.com</span> </button>
<button type="button" class="contact" value="ravi@example.com"> <span data-combobox-label>Ravi Patel</span> <span class="email">ravi@example.com</span> </button>
<button type="button" class="contact" value="sofia@example.com"> <span data-combobox-label>Sofia Marino</span> <span class="email">sofia@example.com</span> </button>
<button type="button" class="contact" value="jonas@example.com"> <span data-combobox-label>Jonas Berg</span> <span class="email">jonas@example.com</span> </button>
<p class="empty" data-combobox-no-results>No one in your contacts matches</p> </sv-flyout> </sv-combobox></div>
<script type="application/x-typescript" module>3 collapsed lines
const recipients = document.querySelector<Combobox>(".recipients")!;
recipients.filter = (option, query) => option.textContent.toLowerCase().includes(query.toLowerCase());</script>
<style>54 collapsed lines
.recipient-field { display: grid; gap: 0.5em; inline-size: min(100%, 24rem); margin-inline: auto; color: var(--sv-gray-2);
label { justify-self: start; cursor: pointer; }
.recipients::part(chip) { padding: 0.25em 0.4em 0.25em 0.6em; color: var(--sv-gray-1); background-color: color-mix(in srgb, var(--sv-accent) 18%, transparent); border-radius: var(--sv-bdr-rad-sm); transition: background-color 160ms ease; }
.recipients::part(chip):hover { background-color: color-mix(in srgb, var(--sv-accent) 30%, transparent); }
.recipients::part(chip-remove) { fill: currentcolor; }
.contact { display: grid; gap: 0.2em; }
.email { font-size: 0.8em; color: var(--sv-gray-3); }
.contact[aria-selected="true"] .email { color: inherit; }
.empty { padding: 0.5em; margin: 0; color: var(--sv-gray-3); }
@media (prefers-reduced-motion: reduce) { .recipients::part(chip) { transition: none; } } }</style>