<!-- 1. Include styles & script -->
<link rel="stylesheet" href="kselect.css">
<script src="kselect.js"></script>

<!-- 2. Initialize -->
<script>
  Kselect.init('select');  // all selects on page
</script>
🔍
Single Select — Searchable
A standard single-value select with live search filtering. Includes an optional clear button.
Value:
☑️
Multi Select with Checkboxes
Multi-value select with checkbox indicators. Selected values appear as removable tags inline in the control.
Values:
🔢
Selection Limit — maxSelect
Cap the number of items a user can pick. When the limit is hit, remaining options dim out and an amber notice appears in the dropdown so the user knows why.
Selected:
📦
Collapse selected to a summary — summarizeSelected
Show a summary like "{n} selected" instead of a row of tags. Set to 'auto' (the default) to collapse only when the tags would wrap, 'off' to always show every tag, or a number to collapse once the count exceeds it. The label template is configurable via summarizeSelectedText. This demo uses a threshold of 1 with a custom label — pick one item to see the tag, two or more to see "{n} Channels Applied".
Selected:
📂
Optgroups — Collapsible
Options organised into labelled groups. Click the group header to collapse/expand. Search automatically expands matching groups.
Selected:
📁
Optgroups — Collapsed by Default
Same structure but groups start collapsed. Useful when you have many groups and want a compact initial view.
Value:
🔒
Disabled States
The whole control can be disabled, or individual options can be marked disabled.
🔄
Dynamic Sync
The underlying <select> is updated programmatically. Dispatch kselect:sync to force the widget to reflect changes.
// After modifying the select DOM: const evt = new Event('kselect:sync'); selectEl.dispatchEvent(evt);
🎨
Custom Themes via CSS Variables
Override CSS custom properties on the wrapper element to create any look.
/* Override on the wrapper or a parent: */ .my-theme { --ks-color-border-focus: #10b981; --ks-color-option-selected: #10b981; --ks-color-tag-bg: #ecfdf5; --ks-color-tag-text: #059669; /* ... and more */ }
Select All Checkbox
A "Select all" row at the top of the dropdown. It reflects checked / indeterminate / unchecked state as items are toggled individually.
Selected:
☑️
Select All per Optgroup
Each optgroup gets its own "Select all" row. Both global and per-group select-all can be combined.
Selected:
📡
Events
Listen for native change and custom kselect:change, kselect:open, kselect:close events.
Waiting for events…
selectEl.addEventListener('kselect:change', e => { console.log('new value:', ks.getValue()); });
🔔
Native change event — alert test
A plain addEventListener('change', …) on the underlying <select> — no kselect API involved. Select an item to see if the native event fires.
📱
Native picker on touch devices
Set nativeOnMobile: true to fall back to the OS picker on phones and tablets. Open this page on a phone to see the difference — on desktop you still get the full custom widget.
Selected:
const ks = Kselect.init('#native-mobile-demo', {
  nativeOnMobile: true,
})[0];

// All API methods still work on either device
ks.getValue();
ks.setValue('cherry');
📱
Native picker on touch devices (multi)
nativeOnMobile: true works for multi-selects too. On phones, users get the OS-native multi-select UI (a stacked list on iOS); on desktop, the full custom widget with checkboxes and tag pills.
Selected:
const ks = Kselect.init('#native-mobile-multi-demo', {
  nativeOnMobile: true,
})[0];

// getValue() returns an array for multi selects, in either mode
ks.getValue();          // → ['cheese', 'olives']
ks.setValue(['cheese', 'mushrooms']);
HTML Option Labels — allowHtml: true
With allowHtml enabled (the default), HTML markup inside <option> tags is rendered live — bold text, colours, flags, icons, whatever you put in.
Selected:
🔒
HTML Option Labels — allowHtml: false
With allowHtml: false, HTML tags are shown as literal text. Useful when option content comes from user input or untrusted sources.
Selected: