unselect2
It quacks like a dropdown and it walks like a dropdown. It is not a dropdown — it is a real <select> wearing better clothes. The element you author stays in the page, keeps its name, and remains the single thing the form submits.
Nothing on this page touches a chain. There is no contract, no address, no transaction and no wallet — this is a browser component and a plain form post. The eight demos beside it on the demos page are contracts on Base Sepolia; this one is not, and its card carries no chain badge for that reason.
The receipt
The native <select> cannot be styled. That is not an oversight in browsers, it is the design: the control is drawn by the operating system, which is also why it behaves correctly everywhere without anyone writing code. Taking that appearance back normally means replacing the element with a pile of <div>s, and the bill for that arrives in four parts.
- Form semantics. A replacement submits nothing on its own, so it needs a hidden input shadowing its state, and every bug in that mirror is a wrong value posted silently. Not paid here: the
<select>is never detached from the form, soFormData, the submitted payload and the reset behavior are the browser's own. - Constraint validation.
requiredstops working the moment the real control leaves the page, and the usual workaround re-implements validity in script. Not paid here: the element is hidden withclip-pathrather thandisplay:none, precisely becausedisplay:noneremoves an element from constraint validation in some engines.requiredis still the browser's. - Keyboard behavior. Arrow keys, Home/End, page-step, and type-ahead that cycles on a repeated letter are what a native select does, and a replacement usually ships two of those. Paid, but paid in full: the component implements the ARIA 1.2 select-only combobox pattern, keeps focus on one element and moves an
aria-activedescendant, and skips disabled rows with every movement key. - Zero-JavaScript operation. A
<div>widget is nothing at all without script. Not paid here: with JavaScript off this page is a plain form that still works, which you can check without taking our word for it — the section below says how.
The reason none of the first three lands on the visitor is the same reason in each case: the component enhances rather than replaces. It builds a styleable surface, puts it in front of the real element, and leaves the element to keep doing its job. The payload never stops being the browser's.
What it does on a touchscreen
It enhances there too, and that is a trade rather than a free win — so it is worth stating plainly. There is no touch branch anywhere in the component: no pointer-type test in the script, none in the stylesheet. A phone or tablet therefore gets the same panel a mouse gets, and the platform's own picker does not appear — no iOS wheel, no Android bottom sheet, and none of the affordances those bring with them. What survives is everything the receipt above is about: the value still lives in the <select>, the form still submits the same payload, and the panel is operable by touch. What is given up is the picker a phone user already knows. If that trade is the wrong one for a particular field, the enhancement is opt-in per element — leave data-unselect2 off and that field stays exactly as the browser drew it.
Try it
Three fields: two required, one optional, one of them grouped, and one row that is shown but cannot be chosen. Open a field with the mouse, or focus it and use the arrow keys, Home, End, or just start typing a letter.
How to check the claims above
- That the real element is still there: inspect any field and look for the
<select>next to the generateddiv.us2. It is hidden, not removed, and it still carries itsname. - That validation is the browser's: submit without choosing a plan. The browser stops it before a request is made. Then turn JavaScript off and submit again — the server stops it, and says so beside the field.
- That the server does not trust the browser: the last plan row is disabled and cannot be picked in the panel. A hand-built request that posts it anyway is refused, because the rule lives on the server rather than in the markup.
- That no-JavaScript is real: disable JavaScript for this page and reload. The form still submits and still answers.
Provenance
The component is vendored into this site as two files copied verbatim — no package manager, no build step, no bundler. The copy is pinned to commit 419ba67 of the unselect2 repository, and a test in this repo hashes the vendored bytes against that commit, so an edit made here instead of upstream fails the build rather than shipping quietly.