Buttons & forms
Push button
Pure CSSPress it. The edge is a generated box-shadow stack that collapses on :active.
Hover or focus the button: items swing down from their top edge, one after another.
Your edited version
rotateX(-90deg) with transform-origin: top, plus opacity: 0.:hover / :focus of the menu they rotate to 0°.transition-delay: calc(var(--i) * 80ms) opens them one after another.perspective on the list gives the swing its depth.rotateX(-90deg) → 0 with origin toptransition-delay from --itabindex + :focus for keyboards<div class="menu" tabindex="0">
<span>Menu ▾</span>
<ul>
<li style="--i:0">Profile</li>
<li style="--i:1">Projects</li>
<li style="--i:2">Settings</li>
<li style="--i:3">Sign out</li>
</ul>
</div>
body {
place-items: start center;
padding-top: 40px;
}
.menu {
position: relative;
width: 200px;
cursor: pointer;
font-family: system-ui;
}
.menu > span {
display: block;
padding: 12px 16px;
border-radius: 10px;
color: #fff;
font-weight: 800;
background: linear-gradient(120deg, #8b6cff, #ff4d9d);
}
.menu ul {
position: absolute;
inset: 100% 0 auto;
margin: 4px 0 0;
padding: 0;
list-style: none;
perspective: 500px;
}
.menu li {
padding: 10px 16px;
background: #141830;
border: 1px solid #2a3054;
opacity: 0;
transform-origin: top center;
transform: rotateX(-90deg);
pointer-events: none; /* folded items must not catch the pointer */
transition: transform 0.35s cubic-bezier(0.3, 1.4, 0.5, 1), opacity 0.2s;
transition-delay: calc((3 - var(--i)) * 50ms); /* closing: bottom-up */
}
.menu:hover li,
.menu:focus li {
opacity: 1;
pointer-events: auto;
transform: rotateX(0deg);
transition-delay: calc(var(--i) * 80ms); /* opening: top-down */
}
.d-dropdown {
position: relative;
align-self: start;
width: 170px;
margin-top: 26px;
cursor: pointer;
> span {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 14px;
border-radius: 10px;
background: linear-gradient(120deg, var(--accent), var(--hot));
color: #fff;
font-weight: 800;
}
ul {
position: absolute;
inset: 100% 0 auto;
margin: 4px 0 0;
padding: 0;
list-style: none;
perspective: 500px;
}
// --i is 0…3 from the markup
li {
padding: 8px 14px;
background: var(--surface-solid);
border: 1px solid var(--border);
font-size: 14px;
opacity: 0;
pointer-events: none; // folded items must not catch the pointer
transform-origin: top center;
transform: rotateX(-90deg);
transition:
transform 0.35s cubic-bezier(0.3, 1.4, 0.5, 1),
opacity 0.2s;
// closing: last item first
transition-delay: calc((3 - var(--i)) * 50ms);
}
&:hover li,
&:focus li {
opacity: 1;
pointer-events: auto;
transform: rotateX(0deg);
// opening: first item first
transition-delay: calc(var(--i) * 80ms);
}
}