forms
Select
Native select control styled to match input heights, with a decorative chevron, label, hint, error, and attribute forwarding.
Examples
Basic select
Label, hint, placeholder option, and required state.
Controls access to invoices, stock, and approvals.
<x-ui.select
name="role"
label="Team role"
hint="Controls access to invoices, stock, and approvals."
placeholder="Select a role"
required
class="max-w-sm"
>
<option value="owner">Owner</option>
<option value="finance">Finance</option>
<option value="ops">Operations</option>
<option value="viewer">Viewer</option>
</x-ui.select>
<div class="w-full max-w-sm">
<label for="team-role" class="mb-1.5 block text-sm font-medium text-fg">Team role</label>
<div class="relative">
<select
id="team-role"
name="role"
required
aria-describedby="team-role-hint"
class="h-9 w-full appearance-none rounded-control border border-border bg-surface-raised pl-3.5 pr-9 text-sm text-fg shadow-2xs transition-colors duration-150 hover:border-border-strong focus:border-brand focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface"
>
<option value="" disabled selected>Select a role</option>
<option value="owner">Owner</option>
<option value="finance">Finance</option>
<option value="ops">Operations</option>
<option value="viewer">Viewer</option>
</select>
<span class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2.5 text-fg-subtle" aria-hidden="true">
<svg class="size-4 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 9l6 6 6-6" />
</svg>
</span>
</div>
<p id="team-role-hint" class="mt-1.5 text-xs text-fg-muted leading-relaxed">Controls access to invoices, stock, and approvals.</p>
</div>
Sizes
sm, md, and lg heights matching input.
<div class="flex max-w-sm flex-col gap-4">
<x-ui.select size="sm" name="size_sm" aria-label="Warehouse small">
<option>Gudang A · Jakarta</option>
</x-ui.select>
<x-ui.select size="md" name="size_md" aria-label="Warehouse medium">
<option>Gudang B · Bandung</option>
</x-ui.select>
<x-ui.select size="lg" name="size_lg" aria-label="Warehouse large">
<option>Gudang C · Surabaya</option>
</x-ui.select>
</div>
<div class="flex max-w-sm flex-col gap-4">
<div class="relative">
<select aria-label="Warehouse small" class="h-8 w-full appearance-none rounded-control border border-border bg-surface-raised pl-2.5 pr-8 text-xs text-fg shadow-2xs transition-colors duration-150 hover:border-border-strong focus:border-brand">
<option>Gudang A · Jakarta</option>
</select>
<span class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2.5 text-fg-subtle" aria-hidden="true">
<svg class="size-4 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><path stroke-linecap="round" stroke-linejoin="round" d="M6 9l6 6 6-6" /></svg>
</span>
</div>
<div class="relative">
<select aria-label="Warehouse medium" class="h-9 w-full appearance-none rounded-control border border-border bg-surface-raised pl-3.5 pr-9 text-sm text-fg shadow-2xs transition-colors duration-150 hover:border-border-strong focus:border-brand">
<option>Gudang B · Bandung</option>
</select>
<span class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2.5 text-fg-subtle" aria-hidden="true">
<svg class="size-4 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><path stroke-linecap="round" stroke-linejoin="round" d="M6 9l6 6 6-6" /></svg>
</span>
</div>
<div class="relative">
<select aria-label="Warehouse large" class="h-10 w-full appearance-none rounded-control border border-border bg-surface-raised pl-4 pr-10 text-sm text-fg shadow-2xs transition-colors duration-150 hover:border-border-strong focus:border-brand">
<option>Gudang C · Surabaya</option>
</select>
<span class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2.5 text-fg-subtle" aria-hidden="true">
<svg class="size-4 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><path stroke-linecap="round" stroke-linejoin="round" d="M6 9l6 6 6-6" /></svg>
</span>
</div>
</div>
Validation error
Error text linked through aria-describedby.
Choose IDR or USD before issuing INV-2026-089.
<x-ui.select
name="currency"
label="Settlement currency"
placeholder="Select currency"
error="Choose IDR or USD before issuing INV-2026-089."
class="max-w-sm"
>
<option value="idr">IDR · Rupiah</option>
<option value="usd">USD · US Dollar</option>
<option value="sgd">SGD · Singapore Dollar</option>
</x-ui.select>
<div class="w-full max-w-sm">
<label for="currency-error" class="mb-1.5 block text-sm font-medium text-fg">Settlement currency</label>
<div class="relative">
<select
id="currency-error"
name="currency"
aria-invalid="true"
aria-describedby="currency-error-msg"
class="h-9 w-full appearance-none rounded-control border border-danger bg-surface-raised pl-3.5 pr-9 text-sm text-fg shadow-2xs focus:border-danger focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-danger focus-visible:ring-offset-2 focus-visible:ring-offset-surface"
>
<option value="" disabled selected>Select currency</option>
<option value="idr">IDR · Rupiah</option>
<option value="usd">USD · US Dollar</option>
<option value="sgd">SGD · Singapore Dollar</option>
</select>
<span class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2.5 text-fg-subtle" aria-hidden="true">
<svg class="size-4 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><path stroke-linecap="round" stroke-linejoin="round" d="M6 9l6 6 6-6" /></svg>
</span>
</div>
<p id="currency-error-msg" class="mt-1.5 flex items-start gap-1.5 text-xs text-danger">
<svg class="mt-0.5 size-3.5 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v4m0 4h.01M10.3 4.2 2.8 17.1A2 2 0 0 0 4.5 20h15a2 2 0 0 0 1.7-2.9L13.7 4.2a2 2 0 0 0-3.4 0Z" />
</svg>
<span>Choose IDR or USD before issuing INV-2026-089.</span>
</p>
</div>
Props API
| Prop | Type | Default | Description |
|---|---|---|---|
| size | sm|md|lg | md | Control height aligned with the input archetype. |
| label | string|null | null | Visible label linked via for/id. |
| hint | string|null | null | Helper text referenced by aria-describedby. |
| error | string|null | null | Validation message. Sets aria-invalid and contributes to aria-describedby. |
| placeholder | string|null | null | Disabled empty first option shown before real choices. |
Accessibility notes
Architected with WCAG 2.2 AA patterns in mind. These notes document the exact keyboard and ARIA behavior for this component.
- Uses a native select so keyboard navigation (Tab, arrows, Enter/Space) follows platform behavior.
- Label for, select id, hint, and error are linked through aria-describedby. Errors set aria-invalid="true".
- Pass-through attributes (name, required, disabled, x-model, wire:model) land on the select, not the wrapper.
- The chevron is decorative (aria-hidden) and pointer-events-none so it never steals clicks.
Built by Praesepe
Architecture & custom software partner
Praesepe uses these exact UI primitives to ship SME dashboards, inventory systems, invoicing desks, and internal tools faster, without rebuilding a design system for every client.
- Same Blade + Tailwind 4 + Alpine 3 stack you see in this docs site
- Opinionated for Indonesian SME ops: invoices, stock, roles, approvals
- Architected with WCAG 2.2 AA patterns in mind for keyboard and ARIA