J3W1 / DESIGN SYSTEM
Select
A themed single-choice combobox or multiple-choice list backed by a native select for forms and the no-JavaScript fallback.
stable · LIGHT DOM · 1.1.0
Theme-owned combobox/listbox presentation backed by the original native select for FormData, constraints, defaults and reset. The native control is visible only before enhancement or without JavaScript.
Live variants
default
New windows open here.
<j3w1-select id="example-select-default"><div class="select">
<label class="select-label" for="example-select-default-sel-workspace">Workspace <span class="select-required" aria-hidden="true">*</span><span class="select-required sr-only"> required</span></label>
<div class="select-root">
<select class="select-control" id="example-select-default-sel-workspace" aria-describedby="example-select-default-sel-workspace-help example-select-default-sel-workspace-message" name="example-select-default-sel-workspace">
<option value="">Choose a workspace</option>
<option value="1" selected="">1: terminal</option>
<option value="2">2: editor</option>
<option value="3">3: browser</option>
</select>
<svg class="select-chevron" aria-hidden="true" focusable="false" width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="square"><path d="M4 6.5l4 4 4-4"></path></svg>
</div>
<p class="select-help" id="example-select-default-sel-workspace-help">New windows open here.</p>
<p class="select-message" id="example-select-default-sel-workspace-message">✕ Choose a workspace.</p>
</div></j3w1-select>with-groups
Fallbacks use recorded size-adjust metrics.
<j3w1-select id="example-select-with-groups"><div class="select">
<label class="select-label" for="example-select-with-groups-sel-font">Font <span class="select-required" aria-hidden="true">*</span><span class="select-required sr-only"> required</span></label>
<div class="select-root">
<select class="select-control" id="example-select-with-groups-sel-font" aria-describedby="example-select-with-groups-sel-font-help example-select-with-groups-sel-font-message" name="example-select-with-groups-sel-font">
<optgroup label="Installed">
<option value="scp" selected="">SauceCodePro NFM</option>
<option value="cascadia">Cascadia Mono</option>
</optgroup>
<optgroup label="Fallbacks">
<option value="consolas">Consolas</option>
<option value="dejavu">DejaVu Sans Mono</option>
</optgroup>
</select>
<svg class="select-chevron" aria-hidden="true" focusable="false" width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="square"><path d="M4 6.5l4 4 4-4"></path></svg>
</div>
<p class="select-help" id="example-select-with-groups-sel-font-help">Fallbacks use recorded size-adjust metrics.</p>
<p class="select-message" id="example-select-with-groups-sel-font-message">✕ That font is not installed.</p>
</div></j3w1-select>multiple
Click or Space toggles an option; Shift+arrows selects a range. The native fallback uses Ctrl/Command+click.
<j3w1-select id="example-select-multiple"><div class="select">
<label class="select-label" for="example-select-multiple-sel-outputs">Outputs <span class="select-required" aria-hidden="true">*</span><span class="select-required sr-only"> required</span></label>
<div class="select-root">
<select class="select-control select-control-multiple" id="example-select-multiple-sel-outputs" multiple="" size="4" aria-describedby="example-select-multiple-sel-outputs-help example-select-multiple-sel-outputs-message" name="example-select-multiple-sel-outputs">
<option value="dp1" selected="">DP-1 (2560×1440)</option>
<option value="dp2" selected="">DP-2 (2560×1440)</option>
<option value="hdmi">HDMI-1 (1920×1080)</option>
<option value="edp">eDP-1 (laptop)</option>
</select>
</div>
<p class="select-help" id="example-select-multiple-sel-outputs-help">Click or Space toggles an option; Shift+arrows selects a range. The native fallback uses Ctrl/Command+click.</p>
<p class="select-message" id="example-select-multiple-sel-outputs-message">✕ Choose at least one output.</p>
</div></j3w1-select>Install and register
Use the exact version. If npm publication is pending, install the release tarball from the installation guide.
npm install --save-exact @j3w1/ui@1.1.0import '@j3w1/ui/tokens.css';
import '@j3w1/ui/styles/select.css';
import '@j3w1/ui/register/select';Insert the maintained markup above after loading these imports. Its native children provide accessible names and form behavior; the empty custom tag alone does not create a control.
Copy the complete implementation
npx --package=@j3w1/ui@1.1.0 j3w1-ui copy select --out ./vendor/j3w1/selectThe CLI verifies every file before writing and refuses overwrites. Serve the output over HTTP and open index.html. Keep its modules, CSS, tokens and licence notices together.
Public API
attributes
| Name | Type / signature | Meaning |
|---|---|---|
disabled | CustomEvent | Disable the native controls owned by this instance. |
loading | CustomEvent | Mark the primary control busy and prevent duplicate activation. |
value | CustomEvent | Forwards value to the primary native control. |
required | CustomEvent | Forwards required to the primary native control. |
readonly | CustomEvent | Forwards readonly to the primary native control. |
checked | CustomEvent | Forwards checked to the primary native control. |
properties
| Name | Type / signature | Meaning |
|---|---|---|
disabled | boolean | Delegates disabled state to native children. |
name | string | Native control name; application form keys remain explicit. |
value | string | Primary native value; assignment does not fabricate user events. |
checked | boolean | Native checkbox state. |
required | boolean | Native required constraint. |
readOnly | boolean | Native readonly property. |
form | HTMLFormElement | null | Owning native form. Read only. |
values | string[] | Selected values of a multiple native select. |
methods
| Name | Type / signature | Meaning |
|---|---|---|
refresh | (): void | Reconnect behavior after replacing native child structure. |
focus | (options?: FocusOptions): void | Focus the first usable native control. |
checkValidity | (): boolean | Check owned native constraints. |
reportValidity | (): boolean | Report the first invalid owned control. |
clear | (): void | Clear an editable primary input and emit native input/change. |
events
| Name | Type / signature | Meaning |
|---|---|---|
j3w1-change | CustomEvent | Native change from an owned control, alongside the original native event. |
Dependencies and verification
Registered components: none. Style dependencies: none.
Package presence establishes an available implementation. Actual passes depend on the tested artifact and environment. Read specification execution evidence; read packed-consumer evidence; inspect the complete state matrix.
Full implementation manifest, API and token dependency closure
Canonical specification
Purpose
Choosing one option, or several, from a fixed list. D-025 requires theme-owned choice surfaces in web applications. The native select remains the form-value and constraint source. A searchable or asynchronous list is the separate combobox component.
Anatomy
Label above; a theme-owned trigger and listbox for single choices, or an
always-visible multiselectable listbox. Each selected option has a check mark
and the canonical selection fill. The hidden native <select> retains its
name, options, current values and defaults. Help and validation are linked
to the visible control. Without JavaScript the original native control is visible.
States
| State | Visual | Non-colour channel |
|---|---|---|
| default | 1px color.border.control on color.surface.input; value color.text.default; chevron color.icon.default | the chevron |
| hover | background → color.interaction.hover.bg; border unchanged | cursor: default; hover-capable pointers only |
| focus-visible | border → color.border.active; ring 1px dashed color.interaction.focus.ring at −2px | the ring |
| required | * after the label in color.status.danger.text plus visually hidden “required” | required; the mark |
| invalid | border 2px color.status.danger.border; message with the ✕ glyph | border width 1 → 2px; glyph; aria-invalid |
| invalid+focus-visible | the 2px danger border and the ring in color.interaction.focus.ring-container at −4px | double boundary |
| disabled | text and chevron color.text.disabled; border color.border.disabled; background color.interaction.disabled.bg | disabled; cursor: not-allowed |
Inside the theme-owned list, a selected option is
color.interaction.selection.bg with color.interaction.selection.text and
group labels are color.text.muted on color.surface.raised. Precedence:
disabled > invalid > hover; focus-visible is always drawn.
Keyboard
The single control follows the select-only combobox pattern: arrows open and move the active option, Home/End reach the ends, typing finds an option, and Enter/Space commits. Escape and Tab close without committing. Multiple choices use arrows, Space or click to toggle, Shift with arrows for a range, and Ctrl/Command+A to select or clear all enabled choices. No modifier is required for ordinary multiple selection. The native fallback uses host keyboard behavior.
Accessibility
A programmatic label is required; the first option is a real choice or a
placeholder option that is disabled and selected with text such as
“Choose a workspace”, never an empty string. aria-invalid="true" only after
interaction or submit. Help and message through aria-describedby. The
themed popup uses the declared contrast roles. The visible control receives
the label, required/invalid state and descriptions. A failed native constraint
focuses that visible control and exposes a theme-owned error. Disabled options,
optgroups and fieldsets remain unavailable. Current values and defaults survive
reconnection, programmatic updates and form reset. Automated evidence records
its actual environments; it does not imply a manual screen-reader pass.
Portability
Use the packaged behavior and complete copy closure for web implementations. The supported enhancement can apply the same renderer to existing selects without moving them or duplicating form values. Native platforms keep the mappings listed above; no-JavaScript pages expose the native fallback.
Non-examples
An unthemed browser popup presented as a fully themed implementation. A background-image chevron in a literal colour. Rounded corners. A select whose first option is blank. A select that triggers navigation on change. Invalid shown by colour alone. A glow instead of the ring. Disabling by opacity.