Wednesday, March 11, 2026

Abusing Customizable Selects | CSS-Tips


Net browsers ship new options on a regular basis, however what enjoyable is it if we will’t construct foolish and enjoyable issues with them?

On this article, let’s go over a couple of demos that I’ve made through the use of the brand new customizable ingredient continues to be a                  

You’ll discover that we’ve used components contained in the components, to wrap every folder title. That’s going to be helpful for styling the chosen folder title later. Regardless that that is only a , having the ability to do that is fairly an enormous change from what was beforehand doable.

That’s as a result of, up till very not too long ago, s may solely include textual content, as a result of that’s the one factor that would seem inside choices of a choose. The HTML parser has now been relaxed to permit for lots extra HTML components to be embedded in choices. Browsers that don’t assist customizable selects will simply ignore these additional components and show the textual content solely.

So, right here’s what our stack of folders appears to be like like to this point:

Subsequent up, and that is a very powerful factor you’ll wish to do to decide into the customizable choose characteristic: let’s reset the default look of the choose and its dropdown half, through the use of the ::picker() pseudo-element:

choose,
::picker(choose) {
  look: base-select;
}

This CSS rule does loads for us: it unlocks full styling capabilities for the whole choose, together with its button, dropdown, and choices. With out this opt-in, you get a typical choose.

Now let’s model the choose, beginning with its button half. First, we’ll eliminate the picker icon through the use of the brand new ::picker-icon pseudo-element to cover it:

choose::picker-icon {
  show: none;
}

Subsequent, let’s add a bit extra kinds to create a nice-looking button:

choose {
  background: linear-gradient(
    135deg,
    rgba(40, 40, 50, 0.4) 0%,
    rgba(60, 60, 70, 0.25) 50%,
    rgba(50, 50, 60, 0.35) 100%
  );
  backdrop-filter: blur(12px) saturate(180%);
  box-shadow:
    0 8px 32px rgba(0, 0, 0, 0.2),
    inset 0 1px 1px rgba(255, 255, 255, 0.15),
    inset 0 -1px 1px rgba(0, 0, 0, 0.1);
  border: 1px strong rgba(255, 255, 255, 0.2);
  coloration: white;
  min-inline-size: 12rem;
}

And right here is our new choose button:

A custom select button with an opaque background, a folder icon, and a text label called Music.

Now let’s flip our consideration to the dropdown half since that is the place the magic occurs.

In a choose, the dropdown incorporates all of the choices and seems once you click on on the button. Lots of browser default kinds apply to it already to set its place, background-color, margin, and extra. So, we’ll need to disable and override a bunch of stuff.

In our demo, we don’t need the dropdown to be seen in any respect. As an alternative, we would like every particular person choice (every folder on this case) to look as if floating above the web page, and not using a container ingredient.

To do that, let’s use the ::picker(choose) pseudo-element to set our kinds:

::picker(choose) {
  background: clear;
  border: none;
  box-shadow: none;
  overflow: seen;
}

And with this, the dropdown isn’t seen anymore and it now not constrains the choices or clips them in the event that they overflow the dropdown space.

This offers us the next enhancements:

A select element with expanded options formatted as text in a single vertical list. An option called music is selected and represents the top picker button which is styled with a folder icon to the left of the text label.

It’s now time to show our consideration to the choice components. First, let’s substitute the checkmark icon with a little bit disc icon as a substitute through the use of the ::checkmark pseudo-element:

choice::checkmark {
  content material: "●";
  coloration: #222;
}

This pseudo-element makes it straightforward to alter the form, the colour, and even the scale of the checkmark.

Let’s additionally add a further pseudo-element to every choice, through the use of choice::earlier than, to show a folder emoji subsequent to every choice. And, with a pinch extra CSS nice tuning, we find yourself with this:

A vertical column of folder icons expanded as options from a select element. Each folder includes a label on the right.

We now have an inventory of folders which floats on high of the web page once we click on the choose button. It really works like another choose, too, both with the mouse, or with the keyboard, so we will simply thank the browser for sustaining the accessibility of the enter whereas we’re having enjoyable with CSS.

Let’s now apply some CSS transformation to make the stack of folders a little bit curvy, so it appears to be like cooler.

To attain this, we’ll want yet one more piece of recent CSS syntax which, sadly, isn’t but broadly out there: the sibling-index() operate. This operate returns the index of the ingredient inside its siblings. The sibling-count() operate additionally exists, and it returns the overall variety of siblings, however we gained’t want it right here.

Gaining access to the index of the present ingredient inside its siblings implies that we will model every choice relying on its place inside the choose dropdown. That is precisely what we have to make the choices seem at a regularly bigger angle.

Right here is the code:

choice {
  --rotation-offset: -4deg;
  rotate: calc(sibling-index() * var(--rotation-offset));
}

On this code snippet, we first create a customized property known as --rotation-offset, which defines the angle by which every choice ought to rotate, with respect to the earlier choice. We then use this with the rotate property, multiplying its worth by sibling-index(). That means, the primary choice is rotated by -4 levels, the second by -8 levels, the third by -12 levels, and so forth.

Now, that’s not sufficient by itself to create the phantasm of a curved stack of folders as a result of every folder rotates round its personal level of origin, which is situated within the top-left nook of every folder by default. Proper now, we get this:

A single column of folder icons with labels on the right. Each folder is slightly rotated more as the list goes down.

Let’s use the transform-origin property to set a shared level of origin round which all choices will rotate. As a result of transform-origin is relative to every particular person ingredient, we have to use the sibling-index() operate once more to maneuver all origin factors up and to the appropriate so that they’re all in the identical spot:

choice {
  --rotation-offset: -4deg;
  rotate: calc(sibling-index() * var(--rotation-offset));
  transform-origin: proper calc(sibling-index() * -1.5rem);
}

And with this, we get the next outcome:

A vertical column of folders with labels on the right fanned out and curving towards the right.

The ultimate step is to animate the choices. It appears to be like nice as it’s, however we would like the stack of folders to get regularly curved till it reaches its closing form. That’ll make it a lore extra vigorous and enjoyable to work together with.

Let’s reset the choice’s rotation by default, and apply a transition with a pleasant elastic easing operate:

choice {
  rotate: 0deg;
  transition: rotate 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

And now, let’s apply the appropriate rotation angle solely when the choose is open:

choose:open choice {
  rotate: calc(sibling-index() * -1 *  var(--rotation-offset));
}

Sadly, the above just isn’t sufficient. By default, CSS transitions usually are not triggered when a component seems, which is the case for our choices. Fortunately, there’s a repair for this problem: the @starting-style at-rule. This at-rule lets us outline the preliminary state of the choices, making it doable for the transition to play proper when the choices seem:

@starting-style {
  choose:open choice {
    rotate: 0deg;
  }
}

Yet one more factor to make it even nicer. Let’s delay every transition relative to the earlier one to make it appear like every folder is available in barely after the one earlier than it. To attain this, let’s use the sibling-index() operate as soon as extra, as a multiplier to a brief transition delay:

choice {
  transition-delay: calc((sibling-index() - 1) * 0.01s);
}

We now have an animated, curved, stack of folders carried out with a opening tag:

This empty

Related Articles

Latest Articles