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:
sibling-index()= position (1, 2, 3…)sibling-count()= total options--offset-from-center= distance from middle card (negative left, positive right)- Each card rotates by7deg × its offset from center
transform-origin: center 75vminmakes cards rotate around a shared pivot below them
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.