j3w1 / theme

j3w1 / theme4.0.0

j3w1 / command

  • SPEC Overview
  • SPEC Foundations
  • SPEC Components
  • SPEC Patterns
  • SPEC Tokens
  • SPEC Tools
  • SPEC Workbench
  • SPEC Form builder
  • SPEC Task kit
  • SPEC Recipes
  • SPEC Figma bridge
  • SPEC Report an issue
  • SPEC Implement
  • SPEC For agents
  • SPEC Ports
  • SPEC Releases
  • SPEC Specification
  • DEMO Vue application demo

Combobox

A text input that filters a listbox of suggestions; the APG combobox with list autocomplete, rendered with the list open.

stable · light DOM · 4.0.0. Light DOM retains static content, native semantics, label relationships and host composition without duplicating controls.

Live variants

default

  • Cascadia Mono
  • SauceCodePro NFM
  • Source Code Pro
  • No fonts match.

Down opens the list; Enter accepts.

✕ Choose a font from the list.

Markup
<j3w1-combobox id="example-combobox-default"><div class="combobox">
  <label class="combobox-label" for="example-combobox-default-cb-font">Font family</label>
  <div class="combobox-root">
    <input class="combobox-input" id="example-combobox-default-cb-font" type="text" role="combobox" aria-autocomplete="list" aria-expanded="true" aria-controls="example-combobox-default-cb-font-list" autocomplete="off" value="Sauce" placeholder="Type to filter" aria-describedby="example-combobox-default-cb-font-help example-combobox-default-cb-font-message" name="example-combobox-default-cb-font">
    <button class="combobox-toggle" type="button" tabindex="-1" aria-label="Show all fonts"><svg class="combobox-chevron" viewBox="0 0 16 16" width="16" height="16" aria-hidden="true" focusable="false"><path d="M3.5 6 8 10.5 12.5 6" fill="none" stroke="currentColor" stroke-width="1.5"></path></svg></button>
  </div>
  <ul class="combobox-listbox" id="example-combobox-default-cb-font-list" role="listbox" aria-label="Fonts">
    <li class="combobox-option" role="option" id="example-combobox-default-cb-font-opt-1">Cascadia Mono</li>
    <li class="combobox-option" role="option" id="example-combobox-default-cb-font-opt-2" aria-selected="true">SauceCodePro NFM</li>
    <li class="combobox-option" role="option" id="example-combobox-default-cb-font-opt-3">Source Code Pro</li>
    <li class="combobox-empty" role="option" aria-disabled="true">No fonts match.</li>
  </ul>
  <p class="combobox-help" id="example-combobox-default-cb-font-help">Down opens the list; Enter accepts.</p>
  <p class="combobox-message" id="example-combobox-default-cb-font-message">✕ Choose a font from the list.</p>
</div></j3w1-combobox>

with-groups

  • Recent
  • Known
  • No hosts match.

Recent hosts first.

✕ Choose a known host.

Markup
<j3w1-combobox id="example-combobox-with-groups"><div class="combobox">
  <label class="combobox-label" for="example-combobox-with-groups-cb-host">Host</label>
  <div class="combobox-root">
    <input class="combobox-input" id="example-combobox-with-groups-cb-host" type="text" role="combobox" aria-autocomplete="list" aria-expanded="true" aria-controls="example-combobox-with-groups-cb-host-list" autocomplete="off" value="j3" placeholder="Name or address" aria-describedby="example-combobox-with-groups-cb-host-help example-combobox-with-groups-cb-host-message" name="example-combobox-with-groups-cb-host">
    <button class="combobox-toggle" type="button" tabindex="-1" aria-label="Show all hosts"><svg class="combobox-chevron" viewBox="0 0 16 16" width="16" height="16" aria-hidden="true" focusable="false"><path d="M3.5 6 8 10.5 12.5 6" fill="none" stroke="currentColor" stroke-width="1.5"></path></svg></button>
  </div>
  <ul class="combobox-listbox" id="example-combobox-with-groups-cb-host-list" role="listbox" aria-label="Hosts">
    <li class="combobox-group" role="group" aria-labelledby="example-combobox-with-groups-cb-host-g1">
      <span class="combobox-group-heading" id="example-combobox-with-groups-cb-host-g1" role="presentation">Recent</span>
      <ul class="combobox-group-list" role="presentation">
        <li class="combobox-option" role="option" id="example-combobox-with-groups-cb-host-opt-1" aria-selected="true">j3w1.github.io</li>
        <li class="combobox-option" role="option" id="example-combobox-with-groups-cb-host-opt-2">j3w1.local</li>
      </ul>
    </li>
    <li class="combobox-group" role="group" aria-labelledby="example-combobox-with-groups-cb-host-g2">
      <span class="combobox-group-heading" id="example-combobox-with-groups-cb-host-g2" role="presentation">Known</span>
      <ul class="combobox-group-list" role="presentation">
        <li class="combobox-option" role="option" id="example-combobox-with-groups-cb-host-opt-3">j3w1-build.internal</li>
      </ul>
    </li>
    <li class="combobox-empty" role="option" aria-disabled="true">No hosts match.</li>
  </ul>
  <p class="combobox-help" id="example-combobox-with-groups-cb-host-help">Recent hosts first.</p>
  <p class="combobox-message" id="example-combobox-with-groups-cb-host-message">✕ Choose a known host.</p>
</div></j3w1-combobox>

no-results

  • No tags match.

Escape clears the filter.

✕ Choose an existing tag.

Markup
<j3w1-combobox id="example-combobox-no-results"><div class="combobox">
  <label class="combobox-label" for="example-combobox-no-results-cb-tag">Tag</label>
  <div class="combobox-root">
    <input class="combobox-input" id="example-combobox-no-results-cb-tag" type="text" role="combobox" aria-autocomplete="list" aria-expanded="true" aria-controls="example-combobox-no-results-cb-tag-list" autocomplete="off" value="zzq" placeholder="Type to filter" aria-describedby="example-combobox-no-results-cb-tag-help example-combobox-no-results-cb-tag-message" name="example-combobox-no-results-cb-tag">
    <button class="combobox-toggle" type="button" tabindex="-1" aria-label="Show all tags"><svg class="combobox-chevron" viewBox="0 0 16 16" width="16" height="16" aria-hidden="true" focusable="false"><path d="M3.5 6 8 10.5 12.5 6" fill="none" stroke="currentColor" stroke-width="1.5"></path></svg></button>
  </div>
  <ul class="combobox-listbox" id="example-combobox-no-results-cb-tag-list" role="listbox" aria-label="Tags">
    <li class="combobox-empty combobox-empty-shown" role="option" aria-disabled="true">No tags match.</li>
  </ul>
  <p class="combobox-help" id="example-combobox-no-results-cb-tag-help">Escape clears the filter.</p>
  <p class="combobox-message" id="example-combobox-no-results-cb-tag-message">✕ Choose an existing tag.</p>
</div></j3w1-combobox>

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@4.0.0
import '@j3w1/ui/tokens.css';
import '@j3w1/ui/styles/combobox.css';
import '@j3w1/ui/register/combobox';

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.

Vue SFC, React, Astro and plain HTML integration →

Copy the complete implementation

npx --package=@j3w1/ui@4.0.0 j3w1-ui copy combobox --out ./vendor/j3w1/combobox

The 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

NameType / signatureMeaning
disabledCustomEventDisable the native controls owned by this instance.
loadingCustomEventMark the primary control busy and prevent duplicate activation.

properties

NameType / signatureMeaning
disabledbooleanDelegates disabled state to native children.
namestringNative control name; application form keys remain explicit.
valuestringTyped or accepted input value.
openbooleanList visibility.

methods

NameType / signatureMeaning
refresh(): voidReconnect behavior after replacing native child structure.
focus(options?: FocusOptions): voidFocus the first usable native control.
show(allOptions?: boolean): voidOpen the list, optionally showing every option.
hide(): voidClose without discarding typed text.

events

NameType / signatureMeaning
j3w1-changeCustomEventAn option was accepted or the value cleared.

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

Combobox combobox

  • Family: Advanced forms
  • Maturity: stable
  • Priority: R1
  • Since: 0.1.0
  • Custom widget: APG combobox with list autocomplete: input role combobox owning a listbox of options — APG pattern
  • specified
  • demonstrated

A text input that filters a listbox of suggestions; the APG combobox with list autocomplete, rendered with the list open.

Report an issue with this component

Tokens

Focus a role to inspect its values; follow its link for persistent usage and source details.

PartRoleValueUse
root.bgcolor.surface.inputapproveduse#000000Form-control fill.
root.bordercolor.border.controlapproveduse#c81a1aD-041 owner correction: red control and floating-frame borders, distinct from rose text.
root.bg-hovercolor.interaction.hover.bgapproveduse#1c1010D-041 interaction.hover.bg: color.primitive.modern.hover.
root.border-focuscolor.border.activeobserveduse#e53935Active or focused control border.
root.ringcolor.interaction.focus.ringobserveduse#e539351px dashed focus ring on controls and rows.
root.ring-invalidcolor.interaction.focus.ring-containerobserveduse#ffa2a7Focus colour for containers and explicitly mapped filled controls (D-015); component rules determine ring geometry.
root.border-invalidcolor.status.danger.borderobserveduse#e53935Danger border, 2px on invalid inputs.
root.border-disabledcolor.border.disabledapproveduse#482b2bD-041 border.disabled: color.primitive.modern.divider.
root.bg-disabledcolor.interaction.disabled.bgapproveduse#0d0707D-041 interaction.disabled.bg: color.primitive.modern.panel.
input.textcolor.text.defaultapproveduse#e99499D-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills.
input.text-disabledcolor.text.disabledapproveduse#a3676bD-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills.
input.placeholdercolor.text.placeholderapproveduse#bd787dD-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills.
input.caretcolor.code.caretapproveduse#ffa2a7D-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills.
toggle.textcolor.action.tertiary.textapproveduse#e99499D-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills.
toggle.bg-hovercolor.action.tertiary.hover-bgobserveduse#1c0a09Ghost and icon button hover.
label.textcolor.text.brightapproveduse#ffa2a7D-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills.
listbox.bgcolor.surface.raisedapproveduse#140b0bD-041 surface.raised: color.primitive.modern.raised.
listbox.bordercolor.border.overlayapproveduse#c81a1aD-041 owner correction: red control and floating-frame borders, distinct from rose text.
option.textcolor.text.defaultapproveduse#e99499D-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills.
option.bg-hovercolor.interaction.hover.bg-strongapproveduse#1c1010D-041 interaction.hover.bg-strong: color.primitive.modern.hover.
option.text-hovercolor.text.link-hoverobserveduse#f4eeeeLink text on hover, over interaction.hover.bg-strong.
option.bg-selectedcolor.interaction.selection.bgapproveduse#531310Selected row, tab, workspace; D-027 recessed the fill, leaving the 2px indicator and the check to carry selection.
option.text-selectedcolor.interaction.selection.textobserveduse#f4eeeeText on the selection.
option.ring-selectedcolor.interaction.focus.ringobserveduse#e539351px dashed focus ring on controls and rows.
group.textcolor.text.subtleapproveduse#bd787dD-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills.
group.bordercolor.border.dividerapproveduse#482b2bD-041 border.divider: color.primitive.modern.divider.
empty.textcolor.text.mutedapproveduse#d4868bD-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills.
help.textcolor.text.mutedapproveduse#d4868bD-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills.
message.textcolor.status.danger.textobserveduse#f07178D-041 readable error text on neutral surfaces and native warning tints.

State matrix

Every declared state and combination, statically rendered; the default cell is live. Sizes: compact, comfortable (switch with the density control above).

StateFlat listGrouped optionsNo results
default
  • Cascadia Mono
  • SauceCodePro NFM
  • Source Code Pro
  • No fonts match.

Down opens the list; Enter accepts.

✕ Choose a font from the list.

  • Recent
  • Known
  • No hosts match.

Recent hosts first.

✕ Choose a known host.

  • No tags match.

Escape clears the filter.

✕ Choose an existing tag.

hover
focus-visible
open
closed
selected
invalid
invalid+focus-visible
disabled
no-results

Purpose

A text input completed from a list of suggestions, for large or open-ended sets (fonts, hosts, tags); a closed set of a dozen values is select. The demonstration renders the list open so every option state shows without a script.

Anatomy

Parts as listed in the anatomy; help and validation message behave as in text-field.

States

StateVisualNon-colour channel
default1px color.border.control on color.surface.input; list on color.surface.raised, 1px color.border.overlay—
hoverbox → color.interaction.hover.bg; hovered option → color.interaction.hover.bg-strong with color.text.link-hovercursor: text on the box, default on the list
focus-visiblebox border → color.border.active; 1px dashed color.interaction.focus.ring at −2pxthe ring
openlist below the box; toggle glyph rotated 180°aria-expanded="true"; toggle glyph
closedno list; box unchangedaria-expanded="false"; nothing below the box
selectedactive option filled color.interaction.selection.bg with color.interaction.selection.text, 1px dashed color.interaction.focus.ring ring at −2pxaria-selected="true"; aria-activedescendant; the ring
invalid2px color.status.danger.border; message with ✕border 1 → 2px; glyph; aria-invalid
invalid+focus-visible2px danger border and color.interaction.focus.ring-container ring at −4pxdouble boundary
disabledcolor.text.disabled, color.border.disabled, color.interaction.disabled.bg; list never opensdisabled; cursor: not-allowed
no-resultsonly the empty row, color.text.muted italicaria-disabled row; italic; polite zero-match announcement

Precedence when several apply: disabled > invalid > selected > hover; focus-visible is always drawn on the box, and the selection ring on the option.

Keyboard

As in the key table. Typing filters on every keystroke; the match count is announced through a polite live region, not by re-reading the list.

Accessibility

The listbox is named (aria-label, or aria-labelledby the field label); options are role="option", aria-selected on the active one only; groups are role="group" labelled by their heading. The empty row is a disabled option so the list is never empty. Text meets 4.5:1; list border and selection ring 3:1.

Portability

Background, border and outline express everything; the toggle rotation is a transform inside the motion budget. A toolkit with a native completion popup uses it, mapping the option fill to the selection roles and the list border to the popover frame. Without aria-activedescendant, real focus moves to the option and the deviation is recorded.

Non-examples

A select restyled as a combobox. DOM focus moving into the list on Down. A list floating with a shadow or rounded frame. The active option marked by text colour alone. A no-results state that silently shows the full list. Opening on hover. A list that slides or fades in for longer than 150 ms.

Contrast report

23 declared pairs measured on resolved sRGB values with the WCAG 2.x formula; 0 waived (decorative only), 0 failing. A pass is a design target for the named role, not an application-wide conformance claim.

PairStateKindForegroundBackgroundRatioMinResult
combobox default textdefaulttextcolor.text.default #e99499color.surface.input #0000009.164.5pass
combobox default borderdefaultuicolor.border.control #c81a1acolor.surface.input #0000003.623pass
combobox hover texthovertextcolor.text.link-hover #f4eeeecolor.interaction.hover.bg-strong #1c101016.194.5pass
combobox focus-visible textfocus-visibletextcolor.text.default #e99499color.surface.input #0000009.164.5pass
combobox focus-visible borderfocus-visibleuicolor.border.active #e53935color.surface.input #0000004.973pass
combobox focus-visible outlinefocus-visibleuicolor.interaction.focus.ring #e53935color.surface.input #0000004.973pass
combobox open textopentextcolor.text.default #e99499color.surface.raised #140b0b8.464.5pass
combobox open borderopenuicolor.border.overlay #c81a1acolor.surface.raised #140b0b3.343pass
combobox selected textselectedtextcolor.interaction.selection.text #f4eeeecolor.interaction.selection.bg #53131012.474.5pass
combobox selected outlineselecteduicolor.interaction.focus.ring #e53935color.interaction.selection.bg #5313103.383pass
combobox invalid textinvalidtextcolor.text.default #e99499color.surface.input #0000009.164.5pass
combobox invalid borderinvaliduicolor.status.danger.border #e53935color.surface.input #0000004.973pass
combobox invalid+focus-visible textinvalid+focus-visibletextcolor.text.default #e99499color.surface.input #0000009.164.5pass
combobox invalid+focus-visible borderinvalid+focus-visibleuicolor.status.danger.border #e53935color.surface.input #0000004.973pass
combobox invalid+focus-visible outlineinvalid+focus-visibleuicolor.interaction.focus.ring-container #ffa2a7color.surface.input #00000010.983pass
combobox no-results textno-resultstextcolor.text.muted #d4868bcolor.surface.raised #140b0b7.014.5pass
combobox no-results borderno-resultsuicolor.border.overlay #c81a1acolor.surface.raised #140b0b3.343pass
group heading on the listbox—textcolor.text.subtle #bd787dcolor.surface.raised #140b0b5.684.5pass
validation message on the canvas—textcolor.status.danger.text #f07178color.surface.canvas #0000007.344.5pass
help text on the canvas—textcolor.text.muted #d4868bcolor.surface.canvas #0000007.584.5pass
disabled text (exempt; house floor 3:1)disableduicolor.text.disabled #a3676bcolor.interaction.disabled.bg #0d07074.483pass
disabled border (exempt)disableduicolor.border.disabled #482b2bcolor.interaction.disabled.bg #0d07071.581pass
group separator (decorative)—uicolor.border.divider #482b2bcolor.surface.raised #140b0b1.531pass

Keys

KeyAction
Down / UpOpens the list if closed; moves the active option, wrapping at either end; the input keeps DOM focus.
EnterAccepts the active option into the input and closes the list.
EscapeCloses the list; a second Escape clears the input.
Alt+Down / Alt+UpOpens or closes the list without moving the active option.
Home / EndMoves the caret inside the input, never the active option.
TabLeaves the control; an open list closes and the typed text stays.

For agents

Component JSON ·Implementation brief ·Build a task kit ·

Related: text-field select multiselect search-field menu command-palette

Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-OVERFLOW FX-STATE-MATRIX