Forms
Interactive form controls for Strata CSS — custom select with every variant developers need.
Installation
npm install @strata-packages/forms
Usage
Standalone
<link rel="stylesheet" href="node_modules/@strata-packages/forms/forms.css">
<script src="node_modules/@strata-packages/forms/forms.js"></script>
Available as StrataForms.
With Strata CSS
The forms package is bundled into Strata's component build — do not load forms.js separately when using the full Strata bundle.
Available as Strata.Forms.
Quick Start
Declarative — auto-inits on DOMContentLoaded
<select data-st-select name="service">
<option value="">Choose a service…</option>
<option value="hair">Hair Styling</option>
<option value="nails">Nails</option>
</select>
Programmatic
const sel = StrataForms.select('#mySelect', options)
Select Variants
All variants are composable — combine any set of options together.
Single select (default)
StrataForms.select('#el', { placeholder: 'Choose…' })
Multi-select with chips
<select data-st-select data-st-multi name="services[]" multiple>
StrataForms.select('#el', { multiSelect: true, placeholder: 'Choose services…' })
Selected items render as removable chips in the trigger.
Multi-select with max items
StrataForms.select('#el', { multiSelect: true, maxItems: 3 })
maxDisplay — fixed-height chip trigger
Caps visible chips at N, shows +N badge for the rest.
StrataForms.select('#el', { multiSelect: true, maxDisplay: 3 })
Searchable
<select data-st-select data-st-searchable>
StrataForms.select('#el', { searchable: true })
Clearable
Shows a × button when a value is selected.
<select data-st-select data-st-clearable>
Grouped options
Reads <optgroup> from the native select automatically — no config needed.
<select data-st-select>
<optgroup label="Hair">
<option value="cut">Hair Cut</option>
<option value="color">Hair Colour</option>
</optgroup>
<optgroup label="Nails">
<option value="mani">Manicure</option>
</optgroup>
</select>
Creatable
User can type a new value and add it. Requires searchable: true.
<select data-st-select data-st-multi data-st-searchable data-st-creatable multiple>
StrataForms.select('#el', { multiSelect: true, searchable: true, creatable: true })
Avatar / custom render
StrataForms.select('#el', {
renderOption: opt => `
<img src="${opt.dataset.avatar}" class="st-sel-avatar">
<span><strong>${opt.text}</strong><small>${opt.dataset.role}</small></span>`,
renderValue: opt => `<img src="${opt.dataset.avatar}" class="st-sel-avatar"> ${opt.text}`,
})
Async / remote options
StrataForms.select('#el', {
searchable: true,
loadOptions: (query, callback) => {
fetch(`/api/search?q=${query}`)
.then(r => r.json())
.then(items => callback(items)) // items: [{ value, text }]
},
})
Auto-width
Dropdown expands to fit content. Flips alignment automatically at viewport edge.
<select data-st-select data-st-auto-width data-st-max-width="320">
StrataForms.select('#el', { autoWidth: true, maxWidth: 320 })
Checkbox select
Dropdown stays open while ticking. Includes Select All and group-level checkboxes.
<select data-st-select data-st-checkboxes data-st-checkbox-display="count" multiple>
StrataForms.select('#el', {
checkboxes: true,
checkboxDisplay: 'count', // 'chips' | 'count' | 'list'
selectAll: true,
})
checkboxDisplay values:
chips— removable chip tags (default)count—"3 of 6 selected"list—"Hair, Nails, Facial"
All Options
| Option | Type | Data attribute | Description |
|---|---|---|---|
placeholder |
string | data-st-placeholder |
Trigger text when nothing selected |
multiSelect |
bool | data-st-multi |
Multiple selection mode |
searchable |
bool | data-st-searchable |
Search input in dropdown |
clearable |
bool | data-st-clearable |
× clear button |
creatable |
bool | data-st-creatable |
Add new options by typing |
maxItems |
number | data-st-max-items |
Cap multi-select count |
maxDisplay |
number | data-st-max-display |
Cap visible chips, show +N badge |
checkboxes |
bool | data-st-checkboxes |
Checkbox UI, dropdown stays open |
checkboxDisplay |
string | data-st-checkbox-display |
chips / count / list |
selectAll |
bool | data-st-no-select-all to disable |
Select All checkbox header |
autoWidth |
bool | data-st-auto-width |
Expand dropdown to content width |
maxWidth |
number | data-st-max-width |
Cap auto-width expansion |
renderOption |
function | JS only | Custom HTML per option in dropdown |
renderValue |
function | JS only | Custom HTML for selected value / chip |
loadOptions |
function | JS only | Async option loading |
Methods
const sel = StrataForms.select('#el', options)
sel.open()
sel.close()
sel.setValue('hair') // single — set by value
sel.setValues(['hair','nails']) // multi — set multiple
sel.getValue() // single → string; multi → string[]
sel.clear() // deselect all
sel.destroy() // remove custom select, restore native
Events
document.addEventListener('st:select:change', e => {
// e.detail: { select, value, text, values, index }
// value / text: string (single) or first item (multi)
// values: always an array — use this for multi
})
document.addEventListener('st:select:open', e => { /* e.detail: { select } */ })
document.addEventListener('st:select:close', e => { /* e.detail: { select } */ })
Backend Compatibility
- Native
<select>stays in the DOM — form submission works with any backend nameattribute preserved —$_POST['service']orrequest.POST['service']works normally- Multi-select: use
name="services[]"(PHP) orname="services"(Django / Rails) requiredattribute triggers visible error state on the custom trigger when form validation fails- Pre-selected values:
<option selected>is respected at init time
CSS Tokens
Override per-instance, per-context, or per-theme — no !important needed.
/* Per theme */
[data-st-theme="dark"] .st-select-trigger {
--st-select-bg: #1e1e2e;
--st-select-border: #45475a;
}
/* Per instance */
<select style="--st-select-height: 3rem" data-st-select>
Known Limitations
renderOption/renderValueoutput is set viainnerHTML— sanitise untrusted data before passingloadOptionsdoes not debounce internally — add your own debounce wrapper if needed- Dynamic
<option>elements added after init are not reflected — calldestroy()then re-init to refresh
License
MIT © Aftab Ibrahim Kazi