Styling a native select with the Customizable Select API

For over a decade every branded dropdown came down to the same choice: pull in a heavy JavaScript library, or build a fragile thing out of divs and spans held together by z-index guesses. Either way you gave up keyboard navigation and accessibility so that a form field could match the brand colours. The Customizable Select API ends that trade.

Chromium has shipped features that let you treat the native <select> like any other styleable container. This is a change in how the browser handles pickers, not another library. If you have ever watched a dropdown get clipped by a parent container and gone digging through the CSS stacking context, this is the thing that fixes it.

The opt-in: appearance: base-select

The Customizable Select API turns on with one CSS value: appearance: base-select. Browsers render selects natively by default, which is why they resist styling so stubbornly. Setting base-select hands the rendering over to you.

/* The essential opt-in for the Customizable Select API */
select,
::picker(select) {
  appearance: base-select;
}

/* Say goodbye to that hard-coded arrow */
select::picker-icon {
  display: none;
}

It also gives you the ::picker(select) pseudo-element, which is the dropdown menu itself. The picker is not trapped by the parent’s overflow. It behaves like a top layer popover, closer to how Tailwind CSS layouts position things than to anything from the float era.

A curved folder stack

I tried a deliberately silly idea: a stack of folders that fans out in a curve when you click it. That used to mean a React component with custom event listeners. With the Customizable Select API and the new sibling-index() function it is a handful of CSS declarations.

/* Rotating options based on their index */
option {
  --rotation-offset: -4deg;
  rotate: calc(sibling-index() * var(--rotation-offset));
  transform-origin: right calc(sibling-index() * -1.5rem);
  transition: rotate 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* Animate only when open */
select:open option {
  rotate: calc(sibling-index() * -1 * var(--rotation-offset));
}

@starting-style {
  select:open option {
    rotate: 0deg;
  }
}

The @starting-style rule is what makes the animation work at all. It gives the browser values to transition from the moment the element renders, so the options slide in instead of snapping into place.

Overriding the button for a fanned card deck

One gotcha with a custom select: the browser mirrors your selected option into the button area through a virtual <selectedcontent> element. That is helpful most of the time and wrong when you want the button to stay put, like the top card of a deck.

The way around it is to drop an empty <button> straight inside the <select>. The browser then skips generating the default content and leaves you a blank surface for the closed state of the component.

Trigonometry and anchor positioning

A radial emoji picker is possible because the Customizable Select API sits on top of anchor positioning. You can place each option with cos() and sin(), measured from the centre of the select button.

/* Positioning options in a circle */
option {
  position: absolute;
  --angle: calc((sibling-index() - 2) * (360deg / (sibling-count() - 1)) - 90deg);
  top: 50%;
  left: 50%;
  translate: 
    calc(-50% + cos(var(--angle)) * var(--radius)) 
    calc(-50% + sin(var(--angle)) * var(--radius));
}

If the Customizable Select API is eating your dev hours, hand it to me. I have been wrestling with WordPress and front end architecture since the 4.x days.

The takeaway

Is it ready for production? Yes, with one caveat. It is Chromium only for now, and because it is a progressive enhancement it will not break anything. Browsers without support fall back to the plain dropdown, and users still finish their checkout or submit their form. You are swapping a few hours of JavaScript debugging for a few minutes of declarative CSS, and that is a trade worth making.

author avatar
Ahmad Wael
I'm a WordPress and WooCommerce developer with 15+ years of experience building custom e-commerce solutions and plugins. I specialize in PHP development, following WordPress coding standards to deliver clean, maintainable code. Currently, I'm exploring AI and e-commerce by building multi-agent systems and SaaS products that integrate technologies like Google Gemini API with WordPress platforms, approaching every project with a commitment to performance, security, and exceptional user experience.