Skip to main content
xriptxr

Select

Info:Form
18

A styled native dropdown: .xtyle-control chrome, a custom chevron, and a label, with valid and invalid states across three sizes.

dropdown combobox picker options

Live demo

live · @xtyle/astro

Select

Labelled selects

Sizes

States

Pick a region to continue.

In a form

The chosen value posts once under its name, whether it rendered into light DOM or behind a shadow root. required is real constraint validity, so the browser blocks a submit with no plan chosen.

Nothing submitted yet.

Select is a thin, accessible skin over the native <select>. It inherits the shared .xtyle-control chrome (the same fill, border, radius, and focus ring as Field) so it sits flush beside other form controls, then hides the platform arrow and paints its own chevron, colored by focus, invalid, and disabled state.

Options are plain <option> / <optgroup> children passed straight through to the native element, so keyboard navigation, type-ahead, and the OS picker all come for free. A label wires an accessible name, invalid plus error surface validation, and sm / md / lg tune the density. It is form-associated: give it a name and the chosen value posts once, whether the element rendered into light DOM or behind a shadow root; required and invalid reach the form as real constraint validity rather than styling alone.

When to use

How this component composes with the rest of the set.

Inherits .xtyle-control chrome from the base layer, so it lines up pixel-for-pixel with Field and Textarea.
Pair with Field for text inputs and Button for the submit action to build a complete form row.
Options are native <option> / <optgroup> elements. No custom item component required.

Props

10 props, straight from the manifest.

PropTypeDefaultBindingsDescription
label string
html svelte astro
Visible label and accessible name. When empty, falls back to aria-label.
value string
html svelte astro
The selected option's value. Two-way bindable in Svelte.
size Size
sm md lg
md
html svelte astro
Control density.
name string
html svelte astro
Form field name, forwarded to the native select for submission.
invalid boolean false
html svelte astro
Marks the field invalid: danger border, danger chevron, and aria-invalid.
error string
html svelte astro
Validation message shown beneath the control; wired via aria-describedby when invalid.
focusable boolean true
html svelte astro
Set false to keep the control out of sequential focus navigation, for an app driving selection from its own keyboard cursor. The host's own tabindex cannot express this, because focus lands on the inner control rather than the host, and setting it there from outside does not survive the next render.
disabled boolean false
html svelte astro
Disables the native select and mutes the chevron.
required boolean false
html svelte astro
Marks the field required, reflecting required and aria-required onto the native select.
requiredMessage html: required-message string Please select a value.
html svelte astro
The message the browser shows when required is unmet. The platform localizes its own constraint messages and this one is xtyle's, so an app that is not in English should set it.

Events

What the component emits, and what rides along on event.detail. The name in the first column is the one addEventListener takes.

EventDetailBindingsDescription
change { value }
html svelte astro
The chosen option changed.

Appearance

Variants

default

.xtyle-select

The standard control: .xtyle-control chrome with the accent focus ring.

invalid

.xtyle-select--invalid

Validation-failed treatment: danger border and chevron, danger-tinted focus ring.

Sizes

sm

.xtyle-select--sm

Compact.

md

default
.xtyle-select

Default.

lg

.xtyle-select--lg

Large.

States

focus-visible

.xtyle-select__field:focus-visible + .xtyle-select__chevron

Keyboard focus: the .xtyle-control accent ring, with the chevron tinted to match.

invalid-focus

.xtyle-select--invalid .xtyle-select__field:focus-visible

Focus while invalid: the danger border holds and the ring shifts to the danger tint.

disabled

.xtyle-select__field:disabled + .xtyle-select__chevron

Non-interactive: the native control mutes and the chevron drops to the disabled ink.

Anatomy

The named parts that make up the component. A ::part() handle is reachable from your own stylesheet; the class beside it is the component's internal selector, which a shadow boundary keeps to itself.

root

Success:::part(root) .xtyle-select

The vertical stack holding the label, control, and error message.

--space-1 --font-sans

label

Success:::part(label) .xtyle-select__label

The control's visible label, hidden when no label is set.

--text-sm --weight-medium --fg-1 --leading-normal

control

Success:::part(control) .xtyle-select__control

The positioning wrapper that overlays the chevron on the native select.

select

Success:::part(select) .xtyle-select__field

The native <select> carrying the shared .xtyle-control chrome with the platform arrow suppressed.

--space-7

chevron

Success:::part(chevron) .xtyle-select__chevron

The custom dropdown indicator, decorative and pointer-transparent, recolored by state.

--space-3 --fg-2 --accent --duration-fast --ease-standard

error

Success:::part(error) .xtyle-select__error

The validation message shown when invalid and an error string are set.

--text-sm --leading-normal --danger

Tokens & coverage

What the component consumes, checked live against what the algorithm produces.

Success:fully covered 18/18 consumed tokens produced default register: 310 tokens

Live coverage check against the xtyle-default register (derive(xtyleDefault, { anchors })coverComponent(manifest, register)). Every token this component consumes must be a key the algorithm produces.

--accent --border-normal --danger --danger-bg --danger-text --duration-fast --ease-standard --fg-1 --fg-2 --fg-disabled --font-sans --leading-normal --space-1 --space-3 --space-7 --text-lg --text-sm --weight-medium

Slots

default
html svelte astro

The <option> and <optgroup> elements, passed straight through to the native select.

Accessibility

Renders a native <select>, so keyboard navigation, type-ahead, and the OS picker work without any custom scripting.
label provides the accessible name via a for-linked <label>; when absent, an aria-label is required and the html binding warns at runtime when neither is present.
invalid sets aria-invalid="true"; when an error string is present it is linked with aria-describedby.
required reflects both the native required attribute and aria-required for assistive tech.
The chevron is decorative (aria-hidden); selection state lives in the native control, not the visual.
Focus is shown with the shared .xtyle-control ring plus a transparent outline that the forced-colors base rule promotes to a real system outline.

Code

Labels, groups, and validation

A plain labelled select, a grouped large select, and an invalid required select with an error message.

<xtyle-select label="Theme" name="theme" value="auto">
	<option value="auto">Match system</option>
	<option value="light">Light</option>
	<option value="dark">Dark</option>
</xtyle-select>

<xtyle-select label="Plan" size="lg">
	<optgroup label="Personal">
		<option value="free">Free</option>
		<option value="pro">Pro</option>
	</optgroup>
	<optgroup label="Teams">
		<option value="team">Team</option>
		<option value="org">Organization</option>
	</optgroup>
</xtyle-select>

<xtyle-select label="Country" invalid error="Select a country to continue." required>
	<option value="" disabled selected>Choose…</option>
	<option value="us">United States</option>
	<option value="ca">Canada</option>
</xtyle-select>

In a form

A required select inside a <form> with a submit button: the chosen value posts under its name, and leaving it empty blocks the submit.

<form>
	<xtyle-select label="Plan" name="plan" required>
		<option value="" selected>Choose…</option>
		<option value="team">Team</option>
		<option value="pro">Pro</option>
	</xtyle-select>
	<xtyle-button type="submit">Subscribe</xtyle-button>
</form>