Printer Select

What follows is a rant about a basic HTML element that badly needs modernizing.

Every element on a page has spent the last twenty years becoming more styleable, and then there’s <select>. It’s the office printer of the web. It works, everybody needs one, nobody has ever been happy with it, and any attempt to make it fit the rest of your setup ends with you giving up and accepting whatever it wants to look like.

You can set a font on it. You can put a border on it, mostly. Try to style the list that drops down and you find out that it isn’t really yours. The browser draws it, the operating system has opinions about it, and none of your CSS is invited. Every other part of the form obeys you. This one doesn’t.

So the whole industry writes its way around it. You build a div that pretends to be a select, wire up the keyboard handling, reimplement type-ahead, and reinvent focus management, and now you own a component that a browser was already giving you for free. Except yours is worse, because the browser’s version is accessible by default and yours is accessible only if you were careful, and you weren’t, because nobody is.

Then you load it on a phone. Native selects on iOS open that scroll wheel at the bottom of the screen, which is genuinely good and took Apple real effort to get right. Your custom one is a div. It lags, it fights the soft keyboard, it scrolls the page behind it, and on a bad day it just doesn’t open. You’ve traded a control that was correct everywhere for one that looks right on your laptop.

What I want isn’t complicated. Give me the native element, with its accessibility and its platform behavior intact, and let me style the dropdown. That’s it. That’s the whole request.

The update, and I’m happy to be wrong

I wrote the above at the end of 2023 in a fairly hopeless mood. It got fixed.

Chrome 135 shipped appearance: base-select in April 2025. Opt into it and the <select> becomes stylable, dropdown included, while staying a real select underneath. You get ::picker(select) for the popup, you can put actual markup in your options, and the browser keeps handling keyboard navigation and accessibility. On mobile it still falls back to the native picker, which is the correct call.

select, ::picker(select) {
  appearance: base-select;
}

The catch is the usual one. It’s Chromium first, the other engines are somewhere behind, and until they land you’re writing the fallback path anyway. So in practice a lot of us are still shipping the div. But the standards work is done and the argument is settled, which is more than I expected when I wrote this.

Worth remembering the next time something on the platform seems permanently broken. The <select> element went unchanged for about twenty-five years and then didn’t.