Skip to content
Praesepe UI

forms

Select

Native select control styled to match input heights, with a decorative chevron, label, hint, error, and attribute forwarding.

Copied!

Examples

Basic select

Label, hint, placeholder option, and required state.

Copied!

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.

Copied!
<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.

Copied!

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

Component props
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

Talk to the studio

WhatsApp: TODO Email the studio Visit Praesepe