Liking cljdoc? Tell your friends :D

x-select

A styled, accessible select input component that wraps a native <select> element inside shadow DOM. Options are provided as light DOM children and automatically synced into the internal select on slot change.

Tag

<x-select name="country" placeholder="Choose a country">
  <option value="us">United States</option>
  <option value="gb">United Kingdom</option>
  <option value="ca">Canada</option>
</x-select>

Observed Attributes

AttributeTypeDefaultDescription
disabledboolean (presence)falseDisables the select; prevents interaction
requiredboolean (presence)falseMarks the field as required for form validation
size"sm" | "md" | "lg""md"Visual size variant
placeholderstringPlaceholder option shown when no value selected
valuestringCurrently selected value (consumer-controlled)
namestringForm field name forwarded to the internal select

Properties

PropertyTypeReflects Attribute
disabledbooleandisabled
requiredbooleanrequired
valuestringvalue

Events

EventBubblesComposedCancelableDetail
select-changeyesyesno{ value: string, label: string }

The component does not auto-set the value attribute on change. The consumer controls value by reacting to select-change and setting it explicitly if desired.

Slots

SlotDescription
(default)<option> and <optgroup> elements. These are cloned into the internal <select> on slotchange.

CSS Custom Properties

PropertyDefault (light)Default (dark)
--x-select-height-sm2rem
--x-select-height-md2.5rem
--x-select-height-lg3rem
--x-select-radius0.5rem
--x-select-font-size-sm0.75rem
--x-select-font-size-md0.875rem
--x-select-font-size-lg1rem
--x-select-padding-inline0.75rem
--x-select-bg#ffffff#1f2937
--x-select-bg-disabled#f8fafc#111827
--x-select-fg#0f172a#f1f5f9
--x-select-fg-disabled#94a3b8
--x-select-placeholder-fg#94a3b8
--x-select-border#cbd5e1#374151
--x-select-border-hover#94a3b8#4b5563
--x-select-border-focus#3b82f6#60a5fa
--x-select-chevron#64748b#94a3b8
--x-select-focus-ring#93c5fd
--x-select-shadow0 1px 2px rgba(15,23,42,0.06)
--x-select-transition-duration140ms

CSS Parts

PartElementDescription
wrapper<div>The styled container; receives size/disabled data attrs
select<select>The native select element
chevron<span>The dropdown arrow icon

Accessibility

  • The internal <select> uses native browser accessibility — keyboard navigation, screen reader announcements, and OS-level option pickers are provided for free.
  • When no name attribute is present, aria-label is set on the internal <select> using the placeholder text (or "select" as fallback).
  • Disabled state is communicated natively via <select disabled>.
  • Focus ring is applied via :focus-within on the wrapper using --x-select-focus-ring.

Theming

Dark mode is handled automatically via @media (prefers-color-scheme: dark) inside the component's shadow styles. Override any CSS custom property on the host to customize:

x-select {
  --x-select-border-focus: #a855f7;
  --x-select-focus-ring: #d8b4fe;
}

Motion

Transitions on the wrapper respect @media (prefers-reduced-motion: reduce) — all transitions are set to none when reduced motion is preferred.

Usage Examples

Basic

<x-select name="role" placeholder="Select a role">
  <option value="admin">Admin</option>
  <option value="editor">Editor</option>
  <option value="viewer">Viewer</option>
</x-select>

Consumer-controlled value

<x-select id="my-select" name="plan" value="pro">
  <option value="free">Free</option>
  <option value="pro">Pro</option>
  <option value="enterprise">Enterprise</option>
</x-select>

<script>
  const sel = document.getElementById('my-select');
  sel.addEventListener('select-change', e => {
    // Consumer decides whether to persist the selection
    sel.setAttribute('value', e.detail.value);
    console.log('Selected:', e.detail.value, e.detail.label);
  });
</script>

Disabled

<x-select disabled name="region">
  <option value="us">United States</option>
</x-select>

Sizes

<x-select size="sm" name="sm-select">
  <option value="a">Small</option>
</x-select>

<x-select size="md" name="md-select">
  <option value="a">Medium (default)</option>
</x-select>

<x-select size="lg" name="lg-select">
  <option value="a">Large</option>
</x-select>

Option groups

<x-select name="city" placeholder="Choose a city">
  <optgroup label="North America">
    <option value="nyc">New York</option>
    <option value="la">Los Angeles</option>
  </optgroup>
  <optgroup label="Europe">
    <option value="lon">London</option>
    <option value="par">Paris</option>
  </optgroup>
</x-select>

Rendering Invariants

  • DOM = f(attributes) — render reads only from HTML attributes, never from mutable state.
  • The value attribute is never auto-set by the component on user interaction.
  • Options are cloned (not moved) from light DOM into the shadow <select>, so the light DOM children remain intact.
  • The placeholder <option> is always the first child of the internal <select> and is never removed.

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close