A dropdown a keyboard can hold open
CaveatHover-only menus lock keyboards out; :focus-within keeps the panel open while anything inside it holds focus, so Tab walks straight through. Two traps: the menu snaps shut the instant focus leaves, and a pixel of empty space between trigger and panel breaks the hover path — bridge the gap with padding on the panel, never margin. Hide it with visibility, not display, so the tab from trigger to first item has something to land in.
FallbackKeep :hover alongside: iOS Safari does not hand buttons focus on tap, so a focus-only menu never opens on touch. For anything holding forms or many items, graduate to a real disclosure with aria-expanded.
<div class="menu">
<button>Workspace</button>
<ul>
<li><a href="#">Settings</a></li>
</ul>
</div>
.menu { position: relative; }
.menu ul {
position: absolute;
top: 100%;
left: 0;
/* the bridge over the gap: padding,
never margin, or hover has a hole */
padding-top: 8px;
/* not display: the tab out of the
trigger needs somewhere to land */
visibility: hidden;
}
/* open while anything inside has focus */
.menu:hover ul,
.menu:focus-within ul { visibility: visible; }
Published
Questions
Why does my CSS dropdown close before the mouse reaches it?
There is a gap between the trigger and the panel, and while the pointer crosses it the menu is no longer hovered. Position the panel flush against the trigger and create the visual space with padding-top on the panel, which still counts as part of the hovered element. A margin does not.
Why does my focus-within dropdown not open on iPhone?
iOS Safari does not give a button focus when you tap it, so :focus-within never matches. Keep a :hover rule next to it: a tap triggers hover on touch devices, and the menu opens.
Is a :focus-within dropdown accessible enough?
For a short list of links, it lets keyboard users Tab through the menu. It exposes no open or closed state to screen readers, and Escape does not close it. For forms or long menus, use a button that toggles aria-expanded with a script.