Github

Select

Native <select class="select"> is the default. There is no wrapper. When you need a styled listbox, paste the markup first. Copy src/components/ui/Place.svelte (see Popover) and use .select-trigger plus a .menu popover (data-match="anchor" matches the trigger width).

Native

<div class="field">
	<label class="label" for="fruit">Fruit</label>
	<select class="select" id="fruit">
		<option value="">Select</option>
		<option>Apple</option>
		<option>Banana</option>
		<option>Orange</option>
	</select>
</div>

Custom listbox

Markup

<button class="select-trigger" type="button" popovertarget="fruit-list" aria-haspopup="listbox" aria-expanded="false" data-placeholder="">Select a fruit</button>
<div id="fruit-list" popover class="menu" role="listbox" tabindex="-1" data-match="anchor">
	<button class="menu-item select-option" type="button" role="option" aria-selected="false">
		<span class="menu-check"></span> Apple
	</button>
	<button class="menu-item select-option" type="button" role="option" aria-selected="false">
		<span class="menu-check"></span> Banana
	</button>
</div>

Imported

<script>
	import { on_menu_toggle, menu_keydown } from './ui/Place.svelte';
	let value = $state('');
	const label = $derived(value || 'Select a fruit');

	function pick(event, next) {
		if (event.currentTarget.getAttribute('aria-disabled') === 'true')
			return;
		value = next;
		event.currentTarget.closest('[popover]')?.hidePopover();
	}
</script>

<button
	class="select-trigger"
	type="button"
	popovertarget="fruit-list"
	aria-haspopup="listbox"
	aria-expanded="false"
	data-placeholder={value ? undefined : ''}
>{label}</button>
<div id="fruit-list" popover class="menu" role="listbox" tabindex="-1" data-match="anchor" ontoggle={on_menu_toggle} onkeydown={menu_keydown}>
	<button class="menu-item select-option" type="button" role="option" aria-selected={value === 'apple'} onclick={(event) => pick(event, 'apple')}>
		<span class="menu-check"></span> Apple
	</button>
	<button class="menu-item select-option" type="button" role="option" aria-selected={value === 'banana'} onclick={(event) => pick(event, 'banana')}>
		<span class="menu-check"></span> Banana
	</button>
</div>