June 17, 2026
Multiselect polyfill

What if I told you you could just use a ‘select’ with the ‘multiple’ attribute, without compatibility or usability issues? That would be magic, right? That is why I created a polyfill for native multiselects.

It uses the native multiselect on mobile and a custom one on desktop. Data submission and validation work as expected, and accessibility is built on native form semantics rather than a pile of hand-written ARIA. Using it looks like this:

<label>Multiselect 1</label>
<select multiple required>
    <option>Option 1</option>
    <option>Option 2</option>
    <option>Option 3</option>
    <option>Option 4</option>
</select>

<script src="multiselect.js"></script>
<link rel="stylesheet" href="multiselect.css" />

View the demo and the source

The styling is kept to a minimum, but it includes a custom ‘down arrow’ on the selects. The ‘required’ attribute still triggers native client-side form validation and the component supports the advanced ‘optgroup’ element. On mobile, there is a little ‘plus’ next to the label to switch to the desktop version of the multiselect, in case the mobile detection returns a false positive.

Here are your advantages described:

You write standard HTML

There’s no component API, no config object, no framework binding to memorise. You drop in a select, link a script and a stylesheet, and you’re finished. Because it enhances a real form control rather than replacing it, it behaves identically whether you’re shipping server-rendered PHP, a static site, or a heavy single-page app. The markup is the same and your mental model is the same.

Accessibility leans on native semantics

This is where the approach earns its keep. Because the component enhances real form controls, screen-reader output and keyboard navigation come largely from the browser and operating system, not from ARIA you have to write, test, and defend in an audit. On mobile you’re handing users the picker their phone already ships.

That said, “native” is not the same as “guaranteed accessible” — the native <select multiple> in particular is a control with known rough edges across screen readers and touch. So treat this as a pattern worth scrutinising, not a solved problem. I’d welcome real-world test reports against VoiceOver/iOS, TalkBack, NVDA, and JAWS, and I’ll publish the findings — including where it falls short — rather than claim it’s perfect.

Native validation

Mark the field required and it simply works: the browser’s own validation message blocks submission until something is chosen, in both the mobile and desktop layouts. You get native, localized validation messages without writing or maintaining a single line of error-handling UI.

Submits like any other field

The checkboxes on the desktop are just a controller; they carry no name of their own. They flip the selected state on the real options, so the values arrive at your server under the select’s own name, exactly as a normal form would send them. No hidden inputs to keep in sync, no special parsing, no change to your FormData handling or your existing endpoints. Drop it into a form that already works and that form keeps working.

Renders the same on every device

On mobile it shows “3 selected” — mirroring Chrome on Android, but rendered by the component so iOS Safari and Firefox match too: one behavior instead of three. The fallback can be translated by adding data-summary="{n} geselecteerd". Leave it off for the English default. On modern mobile browsers supporting ‘appearance: base-select’, the native select is shown (with ‘size=1’), assuming these mobile browsers show the summary themselves in your preferred language.

Dark mode support

The component follows the visitor’s light or dark preference automatically — and the reason it was painless to add is the same reason everything else here is painless: native controls already know how to recolour themselves. You don’t author a parallel dark stylesheet, you don’t track a theme variable, you don’t duplicate every rule behind a media query. You simply use the power of native HTML and CSS.

The right UI per device

Touch devices get the native picker; mouse-and-keyboard users get a clean checkbox list. And for the awkward in-between cases — a touchscreen laptop, an iPad with a trackpad — there’s a small + button beside the label that lets the user switch to the checkbox version themselves.

It’s future-proof

Browsers are finally making the native select stylable (appearance: base-select), and the multiple-select variant is on its way. When it lands everywhere, this polyfill becomes a clean no-op you can simply remove — your plain select[multiple] stays exactly as written. That’s progressive enhancement: no lock-in, no rewrite, no tech debt.

Tiny, and licensed for humans

No build step, no dependencies, nothing to bloat your bundle — and it’s released under the WTFPL, so you can copy it, paste it, and ship it without a legal conversation. Using it looks like this, and that’s the whole story:

<label>Multiselect 1</label>
<select multiple required>
    <option>Option 1</option>
    <option>Option 2</option>
    <option>Option 3</option>
    <option>Option 4</option>
</select>

<script src="multiselect.js"></script>
<link rel="stylesheet" href="multiselect.css" />

View the demo and the source

()  Joost van der Schee