Single Select
Populate a single select from button options. close-on-select closes the flyout and scroll-to-selected keeps the selection visible.
Code
The following example assumes that you have imported the component and set up the theme.
<div class="select-container"> <label for="countries-select-trigger">Select your country:</label>
<sv-select class="countries-select" close-on-select scroll-to-selected> <sv-flyout class="select-flyout"> <!-- Trigger --> <button id="countries-select-trigger" type="button" class="trigger" slot="trigger"> <!-- The selected option will be cloned into here --> <span data-select-clone></span>
<!-- Show this when no option is selected --> <span style="flex: 1; text-align: start" data-select-no-value>Please select a country</span>
<svg class="option-icon" aria-hidden="false" viewBox="0 -960 960 960" xmlns="http://www.w3.org/2000/svg"> <path d="M480-360 280-560h400L480-360Z"></path> </svg> </button> </sv-flyout> </sv-select></div>
<!-- Option template -->6 collapsed lines
<template id="select-option-template"> <button type="button" class="option" value="{value}"> <img alt="" aria-hidden="true" src="" /> <span></span> </button></template>
<!-- Dynamic data --><script type="module">38 collapsed lines
import { countriesData } from "./countries-data";
/** @type {Flyout} */ const countriesFlyout = document.querySelector(".select-flyout");
/** @type {HTMLTemplateElement} */ const optionTemplate = document.querySelector("#select-option-template");
const flagsDir = new URL("@assets/flags/", import.meta.url);
// Filling the flyout with options elements for (const country of countriesData) { /** @type {DocumentFragment} */ const clone = optionTemplate.content.cloneNode(true);
/** @type {HTMLButtonElement} */ const button = clone.querySelector(".option");
/** @type {HTMLImageElement} */ const img = clone.querySelector("img");
/** @type {HTMLSpanElement} */ const label = clone.querySelector("span");
// option button button.classList.add("option"); button.value = country.code;
// country flag image img.src = `${flagsDir.href}${country.code}.${_img.png}`; img.loading = "lazy"; img.alt = country.label_en;
// country label label.textContent = country.label_en;
countriesFlyout.append(button); }</script>
<style> .select-container {5 collapsed lines
display: flex; flex-direction: column; gap: 0.5em; max-inline-size: 350px; margin: auto;
.option {12 collapsed lines
display: flex; gap: 0.5em; align-items: center; padding: 0.5em; font-family: inherit; font-size: 1rem; color: var(--sv-gray-2); cursor: pointer; background-color: transparent; border: none; border-radius: var(--sv-bdr-rad-md); transition: background-color 200ms;
&[aria-selected="true"] { color: var(--sv-accent-content); background-color: var(--sv-accent); }
&[disabled] { cursor: not-allowed; filter: saturate(40%) brightness(50%); }
@media (hover: hover) and (pointer: fine) { &:not([disabled], [aria-selected="true"]):hover { background-color: var(--sv-gray-5); } } }
.trigger {13 collapsed lines
display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding: 0.5em; font-family: inherit; font-size: 1rem; color: var(--sv-gray-2); cursor: pointer; background-color: var(--sv-gray-6); border: solid var(--sv-bdr-sz-sm) var(--sv-gray-5); border-radius: var(--sv-bdr-rad-md); transition: border-color 200ms ease;
.option-icon {5 collapsed lines
inline-size: 1.5em; block-size: 1.5em; fill: currentcolor; rotate: 90deg; transition: rotate 200ms ease; }
.option { padding: 0; } }
sv-flyout::part(content) { /* Match the flyout’s inline size to the anchor (trigger). */ inline-size: var(--sv-anchor-width);
/* Define the smallest useful flyout for this list. When a position has less space, the anchor tries the next fallback. */ min-block-size: 200px;
/* Let long content shrink and scroll so the current position can fit. */ max-block-size: min(var(--sv-anchor-available-height), 500px); }
sv-flyout:state(--open) { .trigger { border: solid var(--sv-bdr-sz-sm) var(--sv-accent); }
.option-icon { rotate: 0deg; } }
.countries-select {8 collapsed lines
.option { gap: 1em;
img { inline-size: 1.5em; block-size: 1.5em; } } } }</style>