A right-click style menu with shortcuts, where hovering Share slides a submenu out beside it. The submenu keeps a small hover bridge over the gap, so moving diagonally into it works.
Also known as: css submenu on hover, multi level dropdown css, context menu css.
<ul class="hfx-context-submenu">
<li><a href="#"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="8" y="8" width="12" height="12" rx="2"/><path d="M16 8V5.5A1.5 1.5 0 0 0 14.5 4h-9A1.5 1.5 0 0 0 4 5.5v9A1.5 1.5 0 0 0 5.5 16H8"/></svg>Copy<kbd>⌘C</kbd></a></li>
<li><a href="#"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="4" y="4" width="16" height="16" rx="2.5"/><path d="M12 8.5v7M8.5 12h7"/></svg>Duplicate<kbd>⌘D</kbd></a></li>
<li class="hfx-context-submenu-parent">
<a href="#" aria-haspopup="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3v12M7.5 7.5 12 3l4.5 4.5M5 13v5.5A1.5 1.5 0 0 0 6.5 20h11a1.5 1.5 0 0 0 1.5-1.5V13"/></svg>Share<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m9 6 6 6-6 6"/></svg></a>
<ul class="hfx-context-submenu-sub">
<li><a href="#">Email</a></li>
<li><a href="#">Messages</a></li>
<li><a href="#">Copy link</a></li>
</ul>
</li>
<li><a href="#"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7h16M10 11v6M14 11v6M6 7l1 12.5A1.5 1.5 0 0 0 8.5 21h7a1.5 1.5 0 0 0 1.5-1.5L18 7M9 7V4.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 .5.5V7"/></svg>Delete<kbd>⌫</kbd></a></li>
</ul>.hfx-context-submenu {
--c: var(--hfx-color, #3b82f6);
font-size: var(--hfx-size, 16px);
width: 11em;
margin: 0 8.6em 0 0; /* room for the submenu in this preview */
font-family: system-ui, sans-serif;
}
.hfx-context-submenu,
.hfx-context-submenu-sub {
padding: 0.3em;
list-style: none;
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: 0.7em;
background: #fff;
box-shadow: 0 0.8em 1.6em -0.6em rgba(15, 23, 42, 0.35);
color: #1f2937;
}
.hfx-context-submenu a {
display: flex;
align-items: center;
gap: 0.6em;
padding: 0.45em 0.6em;
border-radius: 0.45em;
color: inherit;
font-size: 0.9em;
font-weight: 500;
text-decoration: none;
transition: background-color 0.15s ease, color 0.15s ease;
}
.hfx-context-submenu a svg {
flex: none;
width: 1.1em;
height: 1.1em;
color: #6b7280;
transition: color 0.15s ease, transform 0.25s cubic-bezier(0.3, 0.7, 0.4, 1);
}
.hfx-context-submenu kbd,
.hfx-context-submenu a svg + svg { margin-left: auto; }
.hfx-context-submenu kbd {
color: #9ca3af;
font-family: inherit;
font-size: 0.85em;
}
.hfx-context-submenu a:is(:hover, :focus-visible),
.hfx-context-submenu-parent:is(:hover, :focus-within) > a {
background: color-mix(in srgb, var(--c) 13%, transparent);
color: var(--c);
}
.hfx-context-submenu a:is(:hover, :focus-visible) svg,
.hfx-context-submenu-parent:is(:hover, :focus-within) > a svg { color: var(--c); }
.hfx-context-submenu-parent { position: relative; }
.hfx-context-submenu-sub {
position: absolute;
top: -0.35em;
left: calc(100% + 0.5em);
width: 8em;
margin: 0;
opacity: 0;
visibility: hidden;
transform: translateX(-0.5em) scale(0.97);
transform-origin: 0 1em;
transition: opacity 0.2s ease, transform 0.25s cubic-bezier(0.3, 0.7, 0.4, 1), visibility 0s 0.25s;
}
.hfx-context-submenu-sub::before {
content: "";
position: absolute;
top: 0;
bottom: 0;
right: 100%;
width: 0.8em;
}
.hfx-context-submenu-parent:is(:hover, :focus-within) > a svg + svg { transform: translateX(0.2em); }
.hfx-context-submenu-parent:is(:hover, :focus-within) .hfx-context-submenu-sub {
opacity: 1;
visibility: visible;
transform: none;
transition-delay: 0s;
}
@media (prefers-reduced-motion: reduce) {
.hfx-context-submenu, .hfx-context-submenu *, .hfx-context-submenu::before, .hfx-context-submenu::after, .hfx-context-submenu *::before, .hfx-context-submenu *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}--hfx-color on the element or a parent.
Currently previewing #3b82f6.--hfx-size. Every other dimension is in
em, so the whole thing scales together. Currently previewing 16px.transition or animation
shorthand. Staggered delays are relative, so they keep their rhythm.