Select
hc-select styles a native <select> element. The underlying
element keeps every native behaviour — keyboard navigation, form
submission, the OS native picker on mobile, screen-reader semantics
— while the closed state is fully restyled to match the rest of the
HC form-control family.
Browser baseline
Section titled “Browser baseline”Works in every browser that supports <select> (every browser). The
expanded dropdown stays browser-native, which keeps the experience
consistent with the user’s platform.
A modern follow-up — appearance: base-select — will eventually let
you style the open picker too without giving up the native
control. See Native-forward: styling the open picker
below for the current status and how to opt in today.
Basic example
Section titled “Basic example”<select class="hc-select" name="country"> <option value="">Choose a country…</option> <option value="jp">Japan</option> <option value="us">United States</option> <option value="gb">United Kingdom</option></select>Pair with hc-field to
get a visible label and helper text on top of the select.
Variants
Section titled “Variants”data-variant accepts success, warning, and error for
non-default border colors. The default (omitted) variant uses the
neutral border.
<select class="hc-select" data-variant="error" aria-invalid="true">…</select>Set aria-invalid="true" together with data-variant="error" so
assistive tech also recognises the field as invalid.
data-size accepts sm, md (default), and lg. Sizes follow the
same --hc-control-* scale as hc-button / hc-input, so
data-density="compact" shrinks them consistently with the other
controls.
<select class="hc-select" data-size="sm">…</select><select class="hc-select">…</select><select class="hc-select" data-size="lg">…</select>Disabled
Section titled “Disabled”Use the native disabled attribute when the control should not be
interactive at all. aria-disabled="true" is the focusable variant
for the rare case the control must remain in the tab order.
<select class="hc-select" disabled> <option>Disabled select</option></select>htmx usage
Section titled “htmx usage”hc-select participates in form submissions like any native
<select>. For instant filters, listen to change:
<select class="hc-select" name="status" data-hx-get="/items" data-hx-trigger="change" data-hx-target="#items" data-hx-include="this"> <option value="">Any status</option> <option value="open">Open</option> <option value="closed">Closed</option></select>For server-side validation feedback, return the select with
aria-invalid="true" and a paired hc-field message.
Accessibility
Section titled “Accessibility”- Always associate the select with a label. The easiest pattern is
to put it inside an
hc-fieldwith a<label for>. Usearia-labelonly when no visible label is appropriate (e.g. a toolbar select). - The first option is the visible default — use a placeholder like
<option value="">Choose…</option>when nothing is preselected so the empty value is distinguishable from a real choice on the server. - The native
<select>already handlesEnter/Space/Arrowkeys. Do not add custom keyboard handlers; you would only conflict with the browser’s built-in behaviour. - The chevron is drawn via
background-imagewith a hardcoded neutral stroke color, mirroring the convention used byhc-checkbox/hc-radio. It is purely decorative — screen readers do not announce it.
Theming tokens
Section titled “Theming tokens”Component tokens (in component.tokens.json):
| Token path | Purpose |
|---|---|
select.height / padding-x | Sizing (inherits density via --hc-control-*). |
select.radius / font-size / bg / fg / border | Surface, text, and resting border. |
select.focus-border | Border when focused. |
select.error-border / success-border / warning-border | Variant borders. |
select.disabled-bg | Background when disabled. |
select.chevron-size | Chevron icon dimension. |
select.sm.* / lg.* | Dedicated sm / lg overrides. |
CSS variables
Section titled “CSS variables”Show the generated CSS variables
--hc-select-height | -padding-x | -radius | -font-size--hc-select-bg | -fg | -border--hc-select-focus-border | -error-border | -success-border | -warning-border--hc-select-disabled-bg--hc-select-chevron-size--hc-select-sm-height | -sm-padding-x | -sm-font-size--hc-select-lg-height | -lg-padding-x | -lg-font-size--hc-color-focus-ring (inherited from data-color)Native-forward: styling the open picker
Section titled “Native-forward: styling the open picker”A native <select> gives you keyboard navigation, form participation,
the OS picker on mobile, and screen-reader semantics for free — but
historically the open dropdown could not be styled. The
customizable <select>
work changes that: opting an element into appearance: base-select
lets you style the picker — including the
::picker(select)
pseudo-element and each <option> — while keeping a real native
control. The picker renders in the top layer (like a popover) and can
be placed with CSS anchor positioning. See the
Open UI explainer
for the full surface.
It is fully progressive: a browser that doesn’t understand
appearance: base-select simply ignores it and keeps the native
dropdown. If you want the customizable path on supporting browsers
today, opt in per-instance:
<select class="hc-select" name="country" style="appearance: base-select"> <button type="button"> <selectedcontent></selectedcontent> </button> <option value="">Choose a country…</option> <option value="jp">Japan</option></select>Need a fully styled dropdown that works everywhere right now? Use
hc-combobox — the JS
escape hatch that renders an ARIA listbox in light DOM. HC tracks the
customizable-<select> rollout and will add a first-class, declarative
opt-in to hc-select once the feature reaches Baseline, so the native
path can replace the JS one where styling the open picker is the only
reason to reach for hc-combobox.