css web-dev

2026・03・19

2026-03-19 Fleeting Notes

21:52 - CSS Customizable Selects: Fanned Card Deck Effect

Link: https://css-tricks.com/abusing-customizable-selects/

The Setup

<select>
  <button></button>  <!-- Prevents <selectedcontent> auto-insertion -->
  <option class="red" value="QH">
    <span class="rank">Q</span>
    <span class="suit">♥</span>
  </option>
  <!-- more options -->
</select>

The empty <button> inside the <select> stops the browser from mirroring the selected option in the button area—keeps the button showing the “deck back” instead of the selected card.

Enable Customization

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

Hide the Dropdown Container

::picker(select) {
  background: transparent;
  border: none;
  box-shadow: none;
  overflow: visible;
  position-area: center center;
  inset: 0;
}

Fan Layout with sibling-index() & sibling-count()

option {
  --card-fan-rotation: 7deg;
  --card-fan-spread: -11vmin;
  --option-index: calc(sibling-index() - 1);
  --center: calc(sibling-count() / 2);
  --offset-from-center: calc(var(--option-index) - var(--center));

  rotate: calc(var(--offset-from-center) * var(--card-fan-rotation));
  translate: calc(var(--offset-from-center) * var(--card-fan-spread)) 0;
  transform-origin: center 75vmin;
}

How it works:

Animate the Fan Opening

@property --card-fan-rotation {
  syntax: '<angle>';
  inherits: false;
  initial-value: 7deg;
}

option {
  --card-fan-rotation: 0deg;
  transition: --card-fan-rotation 0.2s ease-out;
}

@starting-style {
  select:open option {
    --card-fan-rotation: 0deg;
  }
}

select:open option {
  --card-fan-rotation: initial;
}

Key: @starting-style lets you animate from 0deg when options appear—otherwise transitions don’t trigger on element appearance.