Open demo

CSS code

HTML
<svg width="0" height="0" style="position:absolute" aria-hidden="true" focusable="false">
  <symbol id="i-chev-down" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></symbol>
  <symbol id="i-updown" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="m8 9 4-4 4 4M8 15l4 4 4-4"/></symbol>
  <symbol id="i-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="m5 12.5 4.5 4.5L19 7.5"/></symbol>
  <symbol id="i-folder" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M3.5 7a1.5 1.5 0 0 1 1.5-1.5h4.3l2 2.5H19a1.5 1.5 0 0 1 1.5 1.5v8.5A1.5 1.5 0 0 1 19 19.5H5A1.5 1.5 0 0 1 3.5 18z"/></symbol>
  <symbol id="i-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M10 14a4 4 0 0 0 5.7 0l3.1-3.1a4 4 0 0 0-5.7-5.7L11.5 6.8"/><path d="M14 10a4 4 0 0 0-5.7 0l-3.1 3.1a4 4 0 0 0 5.7 5.7l1.6-1.6"/></symbol>
  <symbol id="i-card" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="5.5" width="18" height="13" rx="2"/><path d="M3 10h18M7 15h3"/></symbol>
  <symbol id="i-plus" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12h14"/></symbol>
  <symbol id="i-user" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="8" r="4"/><path d="M4.5 20.5c.8-4 3.7-6 7.5-6s6.7 2 7.5 6"/></symbol>
  <symbol id="i-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="8.5" r="3.5"/><path d="M2.5 20c.6-3.6 3.2-5.5 6.5-5.5s5.9 1.9 6.5 5.5"/><path d="M16 5.2a3.3 3.3 0 0 1 0 6.6M18 14.8c2 .6 3.2 2.3 3.5 5.2"/></symbol>
  <symbol id="i-sliders" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7h9M17 7h3M4 17h3M11 17h9"/><circle cx="15" cy="7" r="2"/><circle cx="9" cy="17" r="2"/></symbol>
  <symbol id="i-keyboard" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="2.5" y="6" width="19" height="12" rx="2"/><path d="M6.5 10h.01M10 10h.01M13.5 10h.01M17 10h.01M7.5 14h9"/></symbol>
  <symbol id="i-help" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8.5"/><path d="M9.6 9.5a2.5 2.5 0 0 1 4.8 1c0 1.7-2.4 2.2-2.4 3.7"/><path d="M12 17.3h.01"/></symbol>
  <symbol id="i-logout" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 4h4.5A1.5 1.5 0 0 1 20 5.5v13a1.5 1.5 0 0 1-1.5 1.5H14"/><path d="M9.5 16.5 5 12l4.5-4.5M5 12h10"/></symbol>
  <symbol id="i-sparkle" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4 12.8 9.2 18 11l-5.2 1.8L11 18l-1.8-5.2L4 11l5.2-1.8z"/><path d="M18.5 3.5v3M17 5h3"/></symbol>
  <symbol id="i-bell" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M6 16.5V11a6 6 0 0 1 12 0v5.5l1.5 1.5h-15z"/><path d="M10 20.5a2 2 0 0 0 4 0"/></symbol>
</svg>
<div><span class="lbl">Avatar menu with a profile header</span>
<div class="stage" style="--stage-h:420px">
<div class="ac-wrap ac-bar"><span class="ac-brand"><i></i>Northwind</span><button class="ac-icon" type="button" aria-label="Notifications"><svg aria-hidden="true"><use href="#i-bell"/></svg></button><div class="ac">
<button class="ac-avatar" id="ac-a-btn" type="button" aria-label="Account menu for Maya Okafor" aria-haspopup="menu" aria-expanded="false" aria-controls="ac-a"><span class="ac-av" aria-hidden="true">MO</span></button>
<div class="ac-menu end" role="menu" id="ac-a" aria-label="Account" hidden>
<div role="group" aria-labelledby="ac-a-h">
<div class="ac-head" id="ac-a-h"><span class="ac-av lg" aria-hidden="true">MO</span><span><b>Maya Okafor</b><small>[email protected]</small></span></div>
<a href="#profile" class="ac-item" role="menuitem" tabindex="-1"><svg aria-hidden="true"><use href="#i-user"/></svg>Your profile</a>
<a href="#settings" class="ac-item" role="menuitem" tabindex="-1" aria-keyshortcuts="Meta+,"><svg aria-hidden="true"><use href="#i-sliders"/></svg><span>Settings</span><kbd class="ac-kbd" aria-hidden="true">⌘,</kbd></a>
<a href="#billing" class="ac-item" role="menuitem" tabindex="-1"><svg aria-hidden="true"><use href="#i-card"/></svg><span>Billing</span><span class="ac-badge">Pro</span></a>
</div>
<div class="ac-sep" role="separator"></div>
<button type="button" class="ac-item" role="menuitem" tabindex="-1" aria-keyshortcuts="Shift+?"><svg aria-hidden="true"><use href="#i-keyboard"/></svg><span>Keyboard shortcuts</span><kbd class="ac-kbd" aria-hidden="true">?</kbd></button>
<a href="#help" class="ac-item" role="menuitem" tabindex="-1"><svg aria-hidden="true"><use href="#i-help"/></svg>Help centre</a>
<div class="ac-sep" role="separator"></div>
<a href="#sign-out" class="ac-item" role="menuitem" tabindex="-1"><svg aria-hidden="true"><use href="#i-logout"/></svg>Sign out</a>
</div>
</div></div>
</div></div>
<div><span class="lbl">Workspace switcher</span>
<div class="stage" style="--stage-h:420px">
<div class="ac-wrap">
<div class="ac block">
<button class="ac-switch" id="ac-b-btn" type="button" aria-haspopup="menu" aria-expanded="false" aria-controls="ac-b"><span class="ac-logo ac-teal" aria-hidden="true">N</span><span><b>Northwind Studio</b><small>Pro plan · 14 members</small></span><svg aria-hidden="true"><use href="#i-updown"/></svg><span class="sr-only">, switch workspace</span></button>
<div class="ac-menu full" role="menu" id="ac-b" aria-labelledby="ac-b-btn" hidden>
<div role="group" aria-labelledby="ac-b-h">
<div class="ac-group-lbl" id="ac-b-h">Workspaces</div>
<a href="#northwind" class="ac-item" role="menuitem" tabindex="-1" aria-current="true"><span class="ac-logo ac-teal" aria-hidden="true">N</span><span>Northwind Studio</span><svg class="ac-tick" aria-hidden="true"><use href="#i-check"/></svg></a>
<a href="#fieldnotes" class="ac-item" role="menuitem" tabindex="-1"><span class="ac-logo ac-amber" aria-hidden="true">F</span>Fieldnotes Press</a>
<a href="#personal" class="ac-item" role="menuitem" tabindex="-1"><span class="ac-logo ac-slate" aria-hidden="true">P</span>Personal</a>
</div>
<div class="ac-sep" role="separator"></div>
<a href="#new-workspace" class="ac-item" role="menuitem" tabindex="-1"><svg aria-hidden="true"><use href="#i-plus"/></svg>Create workspace</a>
<a href="#join" class="ac-item" role="menuitem" tabindex="-1"><svg aria-hidden="true"><use href="#i-link"/></svg>Join with an invite code</a>
</div>
</div></div>
</div></div>
<div><span class="lbl">Profile card with plan usage</span>
<div class="stage" style="--stage-h:420px">
<div class="ac-wrap">
<div class="ac">
<button class="ac-pill" id="ac-c-btn" type="button" aria-haspopup="menu" aria-expanded="false" aria-controls="ac-c"><span class="ac-av blue" aria-hidden="true">DP</span>Dev Patel<svg aria-hidden="true"><use href="#i-chev-down"/></svg></button>
<div class="ac-menu card" role="menu" id="ac-c" aria-labelledby="ac-c-btn" hidden>
<div role="group" aria-labelledby="ac-c-h">
<div class="ac-head" id="ac-c-h"><span class="ac-av lg blue" aria-hidden="true">DP</span><span><b>Dev Patel</b><small>[email protected]</small></span><span class="ac-plan">Business</span></div>
<a href="#storage" class="ac-item usage" role="menuitem" tabindex="-1"><svg aria-hidden="true"><use href="#i-folder"/></svg><span class="ac-use"><span class="ac-use-row"><span>Storage</span><span>7.2 of 10 GB</span></span><span class="ac-meter" aria-hidden="true"><i style="width:72%"></i></span></span></a>
<a href="#upgrade" class="ac-item accent" role="menuitem" tabindex="-1"><svg aria-hidden="true"><use href="#i-sparkle"/></svg>Upgrade plan</a>
</div>
<div class="ac-sep" role="separator"></div>
<a href="#team" class="ac-item" role="menuitem" tabindex="-1"><svg aria-hidden="true"><use href="#i-users"/></svg><span>Team members</span><span class="ac-badge">6</span></a>
<a href="#account" class="ac-item" role="menuitem" tabindex="-1"><svg aria-hidden="true"><use href="#i-sliders"/></svg>Account settings</a>
<div class="ac-sep" role="separator"></div>
<a href="#sign-out" class="ac-item" role="menuitem" tabindex="-1"><svg aria-hidden="true"><use href="#i-logout"/></svg>Sign out</a>
</div>
</div></div>
</div></div>
CSS
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
/* Account menus: menu buttons (aria-haspopup="menu") with a profile header that labels the first group. */
.ac-wrap{--ac-ink:#15201e;--ac-muted:#56625f;--ac-line:#e1e7e6;--ac-hover:#eef5f3;--ac-accent:#0f766e;--ac-tint:#dff3ef;font-size:.875rem}
.ac{position:relative;display:inline-block}
.ac.block{display:block;max-width:280px}
.ac-icon{display:grid;place-items:center;width:36px;height:36px;padding:0;border:0;border-radius:10px;background:none;color:var(--ac-muted);cursor:pointer;transition:background-color .15s}
.ac-icon:hover{background:var(--ac-hover);color:var(--ac-ink)}
.ac-icon svg{width:20px;height:20px}
.ac-avatar{position:relative;padding:0;border:0;border-radius:50%;background:none;font:inherit;cursor:pointer}
.ac-avatar .ac-av{box-shadow:0 0 0 2px #fff,0 0 0 3px var(--ac-line);transition:box-shadow .15s}
.ac-avatar[aria-expanded="true"] .ac-av{box-shadow:0 0 0 2px #fff,0 0 0 4px var(--ac-accent)}
.ac-avatar::after{content:"";position:absolute;right:-1px;bottom:-1px;width:11px;height:11px;border:2px solid #fff;border-radius:50%;background:#12b76a}
.ac-switch{display:flex;align-items:center;gap:10px;width:100%;padding:8px 10px 8px 8px;border:1px solid #d3dbd9;border-radius:12px;background:#fff;color:var(--ac-ink);
  font:inherit;text-align:left;cursor:pointer;box-shadow:0 1px 2px rgba(15,40,36,.05);transition:border-color .15s,box-shadow .15s}
.ac-switch:hover{border-color:#b9c5c2}
.ac-switch[aria-expanded="true"]{border-color:var(--ac-accent);box-shadow:0 0 0 3px rgba(15,118,110,.15)}
.ac-switch b{display:block;font-weight:700;line-height:1.3}
.ac-switch small{display:block;color:var(--ac-muted);font-size:.75rem;line-height:1.4}
.ac-switch>svg{width:16px;height:16px;margin-left:auto;flex:none;color:var(--ac-muted)}
.ac-pill{display:inline-flex;align-items:center;gap:9px;height:44px;padding:0 12px 0 4px;border:1px solid #d3dbd9;border-radius:999px;background:#fff;color:var(--ac-ink);
  font:inherit;font-weight:600;cursor:pointer;box-shadow:0 1px 2px rgba(15,40,36,.05);transition:background-color .15s}
.ac-pill:hover,.ac-pill[aria-expanded="true"]{background:var(--ac-hover)}
.ac-pill .ac-av{width:34px;height:34px}
.ac-pill>svg{width:16px;height:16px;color:var(--ac-muted);transition:rotate .2s}
.ac-pill[aria-expanded="true"]>svg{rotate:180deg}
.ac-wrap button:not([role]):focus-visible{outline:2px solid var(--ac-accent);outline-offset:3px}

.ac-menu{position:absolute;z-index:20;top:calc(100% + 8px);left:0;min-width:250px;padding:6px;background:#fff;border:1px solid var(--ac-line);border-radius:14px;
  box-shadow:0 2px 6px rgba(15,40,36,.06),0 22px 44px -14px rgba(15,40,36,.28);animation:ac-in .16s cubic-bezier(.2,.8,.3,1)}
.ac-menu[hidden]{display:none}
@keyframes ac-in{from{opacity:0;translate:0 -6px}}
.ac-item{display:flex;align-items:center;gap:10px;width:100%;padding:8px 10px;border:0;border-radius:9px;background:none;color:var(--ac-ink);font:inherit;font-weight:500;
  line-height:1.4;text-align:left;text-decoration:none;white-space:nowrap;cursor:pointer}
.ac-item>svg{width:17px;height:17px;flex:none;color:var(--ac-muted)}
.ac-item:hover,.ac-item:focus{background:var(--ac-hover);outline:none}
.ac-item:focus-visible{outline:2px solid var(--ac-accent);outline-offset:-2px}
.ac-kbd{margin-left:auto;padding-left:18px;color:var(--ac-muted);font:500 .75rem ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
.ac-sep{height:1px;margin:6px -6px;background:var(--ac-line)}
.ac-group-lbl{padding:6px 10px 4px;color:var(--ac-muted);font-size:.6875rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase}

.ac-av{flex:none;display:grid;place-items:center;width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,#b45309,#be185d);color:#fff;font-size:.75rem;font-weight:700;letter-spacing:.02em}
.ac-av.lg{width:42px;height:42px;font-size:.85rem}
.ac-av.blue{background:linear-gradient(135deg,#0f766e,#1d4ed8)}
.ac-bar{display:flex;align-items:center;gap:6px;padding:7px 8px 7px 14px;background:#fff;border:1px solid var(--ac-line);border-radius:14px;box-shadow:0 1px 2px rgba(15,40,36,.05)}
.ac-brand{display:flex;align-items:center;gap:8px;margin-right:auto;color:var(--ac-ink);font-weight:800;letter-spacing:-.01em}
.ac-brand i{width:22px;height:22px;border-radius:7px;background:linear-gradient(135deg,#14b8a6,#0f766e)}
.ac-logo{flex:none;display:grid;place-items:center;width:34px;height:34px;border-radius:9px;color:#fff;font-size:.85rem;font-weight:800}
.ac-teal{background:linear-gradient(135deg,#0d9488,#115e59)}
.ac-amber{background:linear-gradient(135deg,#b45309,#92400e)}
.ac-slate{background:linear-gradient(135deg,#64748b,#334155)}
.ac-menu.end{left:auto;right:0}
.ac-menu.full{width:100%}
.ac-menu.card{width:292px;max-width:calc(100vw - 72px)}
.ac-item .ac-logo{width:26px;height:26px;border-radius:7px;font-size:.72rem}
.ac-item .ac-tick{width:17px;height:17px;margin-left:auto;color:var(--ac-accent)}
.ac-item.accent,.ac-item.accent>svg{color:var(--ac-accent);font-weight:600}
.ac-badge{margin-left:auto;padding:1px 8px;border-radius:999px;background:var(--ac-tint);color:#115e56;font-size:.7rem;font-weight:700;line-height:1.6}
.ac-head{display:flex;align-items:center;gap:12px;margin:-6px -6px 6px;padding:14px 16px;border-bottom:1px solid var(--ac-line);white-space:normal}
.ac-head b{display:block;color:var(--ac-ink);font-size:.875rem;font-weight:700;line-height:1.3}
.ac-head small{display:block;color:var(--ac-muted);font-size:.8rem;line-height:1.4}
.ac-plan{margin-left:auto;align-self:flex-start;padding:2px 8px;border-radius:6px;background:#15201e;color:#fff;font-size:.68rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase}
.ac-use{flex:1;min-width:0}
.ac-use-row{display:flex;justify-content:space-between;gap:12px}
.ac-use-row span:last-child{color:var(--ac-muted);font-size:.8rem}
.ac-meter{display:block;height:6px;margin-top:6px;border-radius:999px;background:#dfe7e5;overflow:hidden}
.ac-meter i{display:block;height:100%;border-radius:inherit;background:linear-gradient(90deg,#14b8a6,#0f766e)}
.ac-item.usage{align-items:flex-start}
.ac-item.usage>svg{margin-top:2px}

@media (prefers-reduced-motion:reduce){.ac-menu{animation:none}.ac-wrap *{transition:none}}
JavaScript
// Menu button (WAI-ARIA APG pattern). A trigger has aria-haspopup="menu", aria-expanded and aria-controls="<menu id>".
// Click, Enter, Space or ArrowDown opens the menu (ArrowUp opens it on the last item). Inside: ArrowUp/ArrowDown move,
// Home/End jump, typing a letter jumps to the next item that starts with it, Esc closes and returns focus to the trigger,
// Tab or a click outside closes. Choosing an item closes the menu; checkbox items stay open so you can tick several.
(() => {
  const OPEN = '[aria-haspopup="menu"][aria-expanded="true"]';
  const menuOf = t => document.getElementById(t.getAttribute('aria-controls'));
  const ownerOf = m => m && document.querySelector(`[aria-controls="${m.id}"][aria-expanded="true"]`);
  const itemsOf = m => [...m.querySelectorAll('[role^="menuitem"]')]
    .filter(i => i.closest('[role="menu"]') === m && i.getAttribute('aria-disabled') !== 'true');

  function close(t, focus) {
    const m = menuOf(t);
    m.querySelectorAll(OPEN).forEach(s => close(s));
    t.setAttribute('aria-expanded', 'false');
    m.hidden = true;
    if (focus) t.focus();
  }
  function open(t, at) {
    const level = t.closest('[role="menu"]');
    document.querySelectorAll(OPEN).forEach(o => { if (o !== t && o.closest('[role="menu"]') === level) close(o); });
    t.setAttribute('aria-expanded', 'true');
    menuOf(t).hidden = false;
    if (at !== undefined) itemsOf(menuOf(t)).at(at)?.focus();
  }
  const closeAll = target => document.querySelectorAll(OPEN).forEach(t => {
    if (!t.closest('[role="menu"]') && !t.contains(target) && !menuOf(t).contains(target)) close(t);
  });

  document.addEventListener('click', e => {
    const t = e.target.closest('[aria-haspopup="menu"][aria-controls]:not([data-context])');
    if (t) {
      if (!t.closest('[role="menu"]') && t.getAttribute('aria-expanded') === 'true') close(t);
      else open(t, e.detail === 0 ? 0 : undefined); // keyboard clicks (Enter/Space) move focus into the menu
      return;
    }
    const item = e.target.closest('[role^="menuitem"]');
    if (item && item.getAttribute('aria-disabled') !== 'true') {
      const role = item.getAttribute('role');
      if (role === 'menuitemcheckbox') { item.setAttribute('aria-checked', item.getAttribute('aria-checked') !== 'true'); return; }
      if (role === 'menuitemradio') {
        const set = item.closest('[role="group"]') || item.closest('[role="menu"]');
        set.querySelectorAll('[role="menuitemradio"]').forEach(r => r.setAttribute('aria-checked', r === item));
      }
      let t = ownerOf(item.closest('[role="menu"]'));
      while (t && t.closest('[role="menu"]')) t = ownerOf(t.closest('[role="menu"]'));
      if (t) close(t, true);
      return;
    }
    closeAll(e.target);
  });

  document.addEventListener('keydown', e => {
    const t = e.target.closest('[aria-haspopup="menu"][aria-controls]:not([data-context])');
    if (t && !t.closest('[role="menu"]')) {
      if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); open(t, e.key === 'ArrowDown' ? 0 : -1); }
      else if (e.key === 'Escape' && t.getAttribute('aria-expanded') === 'true') close(t);
      return;
    }
    const m = e.target.closest('[role="menu"]');
    if (!m) return;
    const items = itemsOf(m), i = items.indexOf(e.target), owner = ownerOf(m);
    let next;
    switch (e.key) {
      case 'ArrowDown': next = items[(i + 1) % items.length]; break;
      case 'ArrowUp': next = items.at(i <= 0 ? -1 : i - 1); break;
      case 'Home': next = items[0]; break;
      case 'End': next = items.at(-1); break;
      case 'Escape': if (owner) { e.preventDefault(); close(owner, true); } return;
      case 'Tab': closeAll(null); return;
      case ' ': if (e.target.tagName === 'A') { e.preventDefault(); e.target.click(); } return;
      default:
        if (e.key.length !== 1 || e.ctrlKey || e.metaKey || e.altKey) return;
        next = [...items.slice(i + 1), ...items.slice(0, i + 1)]
          .find(x => x.textContent.trim().toLowerCase().startsWith(e.key.toLowerCase()));
    }
    e.preventDefault();
    next?.focus();
  });
})();

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS, Bootstrap 5.3.8 JS bundle

HTML
<svg width="0" height="0" style="position:absolute" aria-hidden="true" focusable="false">
  <symbol id="i-chev-down" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></symbol>
  <symbol id="i-updown" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="m8 9 4-4 4 4M8 15l4 4 4-4"/></symbol>
  <symbol id="i-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="m5 12.5 4.5 4.5L19 7.5"/></symbol>
  <symbol id="i-folder" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M3.5 7a1.5 1.5 0 0 1 1.5-1.5h4.3l2 2.5H19a1.5 1.5 0 0 1 1.5 1.5v8.5A1.5 1.5 0 0 1 19 19.5H5A1.5 1.5 0 0 1 3.5 18z"/></symbol>
  <symbol id="i-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M10 14a4 4 0 0 0 5.7 0l3.1-3.1a4 4 0 0 0-5.7-5.7L11.5 6.8"/><path d="M14 10a4 4 0 0 0-5.7 0l-3.1 3.1a4 4 0 0 0 5.7 5.7l1.6-1.6"/></symbol>
  <symbol id="i-card" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="5.5" width="18" height="13" rx="2"/><path d="M3 10h18M7 15h3"/></symbol>
  <symbol id="i-plus" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12h14"/></symbol>
  <symbol id="i-user" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="8" r="4"/><path d="M4.5 20.5c.8-4 3.7-6 7.5-6s6.7 2 7.5 6"/></symbol>
  <symbol id="i-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="8.5" r="3.5"/><path d="M2.5 20c.6-3.6 3.2-5.5 6.5-5.5s5.9 1.9 6.5 5.5"/><path d="M16 5.2a3.3 3.3 0 0 1 0 6.6M18 14.8c2 .6 3.2 2.3 3.5 5.2"/></symbol>
  <symbol id="i-sliders" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7h9M17 7h3M4 17h3M11 17h9"/><circle cx="15" cy="7" r="2"/><circle cx="9" cy="17" r="2"/></symbol>
  <symbol id="i-keyboard" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="2.5" y="6" width="19" height="12" rx="2"/><path d="M6.5 10h.01M10 10h.01M13.5 10h.01M17 10h.01M7.5 14h9"/></symbol>
  <symbol id="i-help" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8.5"/><path d="M9.6 9.5a2.5 2.5 0 0 1 4.8 1c0 1.7-2.4 2.2-2.4 3.7"/><path d="M12 17.3h.01"/></symbol>
  <symbol id="i-logout" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 4h4.5A1.5 1.5 0 0 1 20 5.5v13a1.5 1.5 0 0 1-1.5 1.5H14"/><path d="M9.5 16.5 5 12l4.5-4.5M5 12h10"/></symbol>
  <symbol id="i-sparkle" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4 12.8 9.2 18 11l-5.2 1.8L11 18l-1.8-5.2L4 11l5.2-1.8z"/><path d="M18.5 3.5v3M17 5h3"/></symbol>
  <symbol id="i-bell" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M6 16.5V11a6 6 0 0 1 12 0v5.5l1.5 1.5h-15z"/><path d="M10 20.5a2 2 0 0 0 4 0"/></symbol>
</svg>
<div><span class="lbl">Avatar menu with a profile header</span>
<div class="stage" style="--stage-h:420px">
<div class="ac-wrap ac-bar"><span class="ac-brand"><i></i>Northwind</span><button class="btn ac-icon" type="button" aria-label="Notifications"><svg aria-hidden="true"><use href="#i-bell"/></svg></button><div class="dropdown ac">
<button class="btn ac-avatar" id="ac-a-btn" type="button" data-bs-toggle="dropdown" data-bs-offset="0,8" aria-label="Account menu for Maya Okafor" aria-haspopup="menu" aria-expanded="false" aria-controls="ac-a"><span class="ac-av" aria-hidden="true">MO</span></button>
<div class="dropdown-menu ac-menu dropdown-menu-end" role="menu" id="ac-a" aria-label="Account">
<div role="group" aria-labelledby="ac-a-h">
<div class="dropdown-header ac-head" id="ac-a-h"><span class="ac-av lg" aria-hidden="true">MO</span><span><b>Maya Okafor</b><small>[email protected]</small></span></div>
<a href="#profile" class="dropdown-item ac-item" role="menuitem" tabindex="-1"><svg aria-hidden="true"><use href="#i-user"/></svg>Your profile</a>
<a href="#settings" class="dropdown-item ac-item" role="menuitem" tabindex="-1" aria-keyshortcuts="Meta+,"><svg aria-hidden="true"><use href="#i-sliders"/></svg><span>Settings</span><kbd class="ac-kbd" aria-hidden="true">⌘,</kbd></a>
<a href="#billing" class="dropdown-item ac-item" role="menuitem" tabindex="-1"><svg aria-hidden="true"><use href="#i-card"/></svg><span>Billing</span><span class="ac-badge">Pro</span></a>
</div>
<hr class="dropdown-divider">
<button type="button" class="dropdown-item ac-item" role="menuitem" tabindex="-1" aria-keyshortcuts="Shift+?"><svg aria-hidden="true"><use href="#i-keyboard"/></svg><span>Keyboard shortcuts</span><kbd class="ac-kbd" aria-hidden="true">?</kbd></button>
<a href="#help" class="dropdown-item ac-item" role="menuitem" tabindex="-1"><svg aria-hidden="true"><use href="#i-help"/></svg>Help centre</a>
<hr class="dropdown-divider">
<a href="#sign-out" class="dropdown-item ac-item" role="menuitem" tabindex="-1"><svg aria-hidden="true"><use href="#i-logout"/></svg>Sign out</a>
</div>
</div></div>
</div></div>
<div><span class="lbl">Workspace switcher</span>
<div class="stage" style="--stage-h:420px">
<div class="ac-wrap">
<div class="dropdown ac block">
<button class="btn ac-switch" id="ac-b-btn" type="button" data-bs-toggle="dropdown" data-bs-offset="0,8" aria-haspopup="menu" aria-expanded="false" aria-controls="ac-b"><span class="ac-logo ac-teal" aria-hidden="true">N</span><span><b>Northwind Studio</b><small>Pro plan · 14 members</small></span><svg aria-hidden="true"><use href="#i-updown"/></svg><span class="visually-hidden">, switch workspace</span></button>
<div class="dropdown-menu ac-menu full" role="menu" id="ac-b" aria-labelledby="ac-b-btn">
<div role="group" aria-labelledby="ac-b-h">
<div class="dropdown-header ac-group-lbl" id="ac-b-h">Workspaces</div>
<a href="#northwind" class="dropdown-item ac-item" role="menuitem" tabindex="-1" aria-current="true"><span class="ac-logo ac-teal" aria-hidden="true">N</span><span>Northwind Studio</span><svg class="ac-tick" aria-hidden="true"><use href="#i-check"/></svg></a>
<a href="#fieldnotes" class="dropdown-item ac-item" role="menuitem" tabindex="-1"><span class="ac-logo ac-amber" aria-hidden="true">F</span>Fieldnotes Press</a>
<a href="#personal" class="dropdown-item ac-item" role="menuitem" tabindex="-1"><span class="ac-logo ac-slate" aria-hidden="true">P</span>Personal</a>
</div>
<hr class="dropdown-divider">
<a href="#new-workspace" class="dropdown-item ac-item" role="menuitem" tabindex="-1"><svg aria-hidden="true"><use href="#i-plus"/></svg>Create workspace</a>
<a href="#join" class="dropdown-item ac-item" role="menuitem" tabindex="-1"><svg aria-hidden="true"><use href="#i-link"/></svg>Join with an invite code</a>
</div>
</div></div>
</div></div>
<div><span class="lbl">Profile card with plan usage</span>
<div class="stage" style="--stage-h:420px">
<div class="ac-wrap">
<div class="dropdown ac">
<button class="btn ac-pill" id="ac-c-btn" type="button" data-bs-toggle="dropdown" data-bs-offset="0,8" aria-haspopup="menu" aria-expanded="false" aria-controls="ac-c"><span class="ac-av blue" aria-hidden="true">DP</span>Dev Patel<svg aria-hidden="true"><use href="#i-chev-down"/></svg></button>
<div class="dropdown-menu ac-menu card" role="menu" id="ac-c" aria-labelledby="ac-c-btn">
<div role="group" aria-labelledby="ac-c-h">
<div class="dropdown-header ac-head" id="ac-c-h"><span class="ac-av lg blue" aria-hidden="true">DP</span><span><b>Dev Patel</b><small>[email protected]</small></span><span class="ac-plan">Business</span></div>
<a href="#storage" class="dropdown-item ac-item usage" role="menuitem" tabindex="-1"><svg aria-hidden="true"><use href="#i-folder"/></svg><span class="ac-use"><span class="ac-use-row"><span>Storage</span><span>7.2 of 10 GB</span></span><span class="ac-meter" aria-hidden="true"><i style="width:72%"></i></span></span></a>
<a href="#upgrade" class="dropdown-item ac-item accent" role="menuitem" tabindex="-1"><svg aria-hidden="true"><use href="#i-sparkle"/></svg>Upgrade plan</a>
</div>
<hr class="dropdown-divider">
<a href="#team" class="dropdown-item ac-item" role="menuitem" tabindex="-1"><svg aria-hidden="true"><use href="#i-users"/></svg><span>Team members</span><span class="ac-badge">6</span></a>
<a href="#account" class="dropdown-item ac-item" role="menuitem" tabindex="-1"><svg aria-hidden="true"><use href="#i-sliders"/></svg>Account settings</a>
<hr class="dropdown-divider">
<a href="#sign-out" class="dropdown-item ac-item" role="menuitem" tabindex="-1"><svg aria-hidden="true"><use href="#i-logout"/></svg>Sign out</a>
</div>
</div></div>
</div></div>
CSS
/* Bootstrap .dropdown restyled through --bs-dropdown-* variables; the header labels the first group. */
.ac-wrap{--ac-ink:#15201e;--ac-muted:#56625f;--ac-line:#e1e7e6;--ac-hover:#eef5f3;--ac-accent:#0f766e;--ac-tint:#dff3ef;font-size:.875rem}
.ac.block{display:block;max-width:280px}
.ac-icon{--bs-btn-padding-x:0;--bs-btn-padding-y:0;--bs-btn-border-width:0;--bs-btn-border-radius:10px;--bs-btn-color:var(--ac-muted);--bs-btn-hover-color:var(--ac-ink);--bs-btn-hover-bg:var(--ac-hover);
  display:grid;place-items:center;width:36px;height:36px}
.ac-icon svg{width:20px;height:20px}
.ac-avatar{--bs-btn-padding-x:0;--bs-btn-padding-y:0;--bs-btn-border-width:0;--bs-btn-border-radius:50%;--bs-btn-active-border-color:transparent;position:relative}
.ac-avatar .ac-av{box-shadow:0 0 0 2px #fff,0 0 0 3px var(--ac-line);transition:box-shadow .15s}
.ac-avatar.show .ac-av{box-shadow:0 0 0 2px #fff,0 0 0 4px var(--ac-accent)}
.ac-avatar::after{content:"";position:absolute;right:-1px;bottom:-1px;width:11px;height:11px;border:2px solid #fff;border-radius:50%;background:#12b76a}
.ac-switch{--bs-btn-padding-x:8px;--bs-btn-padding-y:8px;--bs-btn-color:var(--ac-ink);--bs-btn-bg:#fff;--bs-btn-border-color:#d3dbd9;--bs-btn-border-radius:12px;
  --bs-btn-hover-color:var(--ac-ink);--bs-btn-hover-bg:#fff;--bs-btn-hover-border-color:#b9c5c2;--bs-btn-active-color:var(--ac-ink);--bs-btn-active-bg:#fff;
  --bs-btn-active-border-color:var(--ac-accent);--bs-btn-font-size:.875rem;display:flex;align-items:center;gap:10px;width:100%;padding-right:10px;text-align:left;
  box-shadow:0 1px 2px rgba(15,40,36,.05)}
.ac-switch.show{box-shadow:0 0 0 3px rgba(15,118,110,.15)}
.ac-switch b{display:block;font-weight:700;line-height:1.3}
.ac-switch small{display:block;color:var(--ac-muted);font-size:.75rem;line-height:1.4}
.ac-switch>svg{width:16px;height:16px;margin-left:auto;flex:none;color:var(--ac-muted)}
.ac-pill{--bs-btn-padding-x:4px;--bs-btn-padding-y:0;--bs-btn-font-size:.875rem;--bs-btn-font-weight:600;--bs-btn-color:var(--ac-ink);--bs-btn-bg:#fff;--bs-btn-border-color:#d3dbd9;
  --bs-btn-border-radius:999px;--bs-btn-hover-color:var(--ac-ink);--bs-btn-hover-bg:var(--ac-hover);--bs-btn-hover-border-color:#d3dbd9;--bs-btn-active-color:var(--ac-ink);
  --bs-btn-active-bg:var(--ac-hover);--bs-btn-active-border-color:#d3dbd9;display:inline-flex;align-items:center;gap:9px;height:44px;padding-right:12px;box-shadow:0 1px 2px rgba(15,40,36,.05)}
.ac-pill .ac-av{width:34px;height:34px}
.ac-pill>svg{width:16px;height:16px;color:var(--ac-muted);transition:rotate .2s}
.ac-pill.show>svg{rotate:180deg}
.ac-wrap .btn:focus-visible{outline:2px solid var(--ac-accent);outline-offset:3px;box-shadow:none}

.ac-menu{--bs-dropdown-min-width:250px;--bs-dropdown-padding-x:6px;--bs-dropdown-padding-y:6px;--bs-dropdown-font-size:.875rem;--bs-dropdown-color:var(--ac-ink);
  --bs-dropdown-border-color:var(--ac-line);--bs-dropdown-border-radius:14px;--bs-dropdown-divider-bg:var(--ac-line);--bs-dropdown-divider-margin-y:6px;
  --bs-dropdown-link-color:var(--ac-ink);--bs-dropdown-link-hover-color:var(--ac-ink);--bs-dropdown-link-hover-bg:var(--ac-hover);
  --bs-dropdown-link-active-color:var(--ac-ink);--bs-dropdown-link-active-bg:var(--ac-hover);--bs-dropdown-item-padding-x:10px;--bs-dropdown-item-padding-y:8px;
  --bs-dropdown-item-border-radius:9px;--bs-dropdown-header-color:var(--ac-muted);--bs-dropdown-header-padding-x:10px;--bs-dropdown-header-padding-y:4px;
  box-shadow:0 2px 6px rgba(15,40,36,.06),0 22px 44px -14px rgba(15,40,36,.28)}
.ac-menu.show{animation:ac-in .16s cubic-bezier(.2,.8,.3,1)}
@keyframes ac-in{from{opacity:0;translate:0 -6px}}
.ac-menu .dropdown-divider{margin-inline:-6px;opacity:1}
.ac-item{display:flex;align-items:center;gap:10px;font-weight:500;line-height:1.4}
.ac-item>svg{width:17px;height:17px;flex:none;color:var(--ac-muted)}
.ac-item:focus{outline:none}
.ac-item:focus-visible{outline:2px solid var(--ac-accent);outline-offset:-2px}
.ac-kbd{margin-left:auto;padding:0 0 0 18px;background:none;color:var(--ac-muted);font:500 .75rem ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
.ac-menu .ac-group-lbl{padding-top:6px;font-size:.6875rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase}

.ac-av{flex:none;display:grid;place-items:center;width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,#b45309,#be185d);color:#fff;font-size:.75rem;font-weight:700;letter-spacing:.02em}
.ac-av.lg{width:42px;height:42px;font-size:.85rem}
.ac-av.blue{background:linear-gradient(135deg,#0f766e,#1d4ed8)}
.ac-bar{display:flex;align-items:center;gap:6px;padding:7px 8px 7px 14px;background:#fff;border:1px solid var(--ac-line);border-radius:14px;box-shadow:0 1px 2px rgba(15,40,36,.05)}
.ac-brand{display:flex;align-items:center;gap:8px;margin-right:auto;color:var(--ac-ink);font-weight:800;letter-spacing:-.01em}
.ac-brand i{width:22px;height:22px;border-radius:7px;background:linear-gradient(135deg,#14b8a6,#0f766e)}
.ac-logo{flex:none;display:grid;place-items:center;width:34px;height:34px;border-radius:9px;color:#fff;font-size:.85rem;font-weight:800}
.ac-teal{background:linear-gradient(135deg,#0d9488,#115e59)}
.ac-amber{background:linear-gradient(135deg,#b45309,#92400e)}
.ac-slate{background:linear-gradient(135deg,#64748b,#334155)}
.ac-menu.end{left:auto;right:0}
.ac-menu.full{width:100%}
.ac-menu.card{width:292px;max-width:calc(100vw - 72px)}
.ac-item .ac-logo{width:26px;height:26px;border-radius:7px;font-size:.72rem}
.ac-item .ac-tick{width:17px;height:17px;margin-left:auto;color:var(--ac-accent)}
.ac-item.accent,.ac-item.accent>svg{color:var(--ac-accent);font-weight:600}
.ac-badge{margin-left:auto;padding:1px 8px;border-radius:999px;background:var(--ac-tint);color:#115e56;font-size:.7rem;font-weight:700;line-height:1.6}
.ac-head{display:flex;align-items:center;gap:12px;margin:-6px -6px 6px;padding:14px 16px;border-bottom:1px solid var(--ac-line);white-space:normal}
.ac-head b{display:block;color:var(--ac-ink);font-size:.875rem;font-weight:700;line-height:1.3}
.ac-head small{display:block;color:var(--ac-muted);font-size:.8rem;line-height:1.4}
.ac-plan{margin-left:auto;align-self:flex-start;padding:2px 8px;border-radius:6px;background:#15201e;color:#fff;font-size:.68rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase}
.ac-use{flex:1;min-width:0}
.ac-use-row{display:flex;justify-content:space-between;gap:12px}
.ac-use-row span:last-child{color:var(--ac-muted);font-size:.8rem}
.ac-meter{display:block;height:6px;margin-top:6px;border-radius:999px;background:#dfe7e5;overflow:hidden}
.ac-meter i{display:block;height:100%;border-radius:inherit;background:linear-gradient(90deg,#14b8a6,#0f766e)}
.ac-item.usage{align-items:flex-start}
.ac-item.usage>svg{margin-top:2px}

@media (prefers-reduced-motion:reduce){.ac-menu.show{animation:none}.ac-wrap *{transition:none}}

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<svg width="0" height="0" style="position:absolute" aria-hidden="true" focusable="false">
  <symbol id="i-chev-down" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></symbol>
  <symbol id="i-updown" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="m8 9 4-4 4 4M8 15l4 4 4-4"/></symbol>
  <symbol id="i-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="m5 12.5 4.5 4.5L19 7.5"/></symbol>
  <symbol id="i-folder" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M3.5 7a1.5 1.5 0 0 1 1.5-1.5h4.3l2 2.5H19a1.5 1.5 0 0 1 1.5 1.5v8.5A1.5 1.5 0 0 1 19 19.5H5A1.5 1.5 0 0 1 3.5 18z"/></symbol>
  <symbol id="i-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M10 14a4 4 0 0 0 5.7 0l3.1-3.1a4 4 0 0 0-5.7-5.7L11.5 6.8"/><path d="M14 10a4 4 0 0 0-5.7 0l-3.1 3.1a4 4 0 0 0 5.7 5.7l1.6-1.6"/></symbol>
  <symbol id="i-card" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="5.5" width="18" height="13" rx="2"/><path d="M3 10h18M7 15h3"/></symbol>
  <symbol id="i-plus" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12h14"/></symbol>
  <symbol id="i-user" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="8" r="4"/><path d="M4.5 20.5c.8-4 3.7-6 7.5-6s6.7 2 7.5 6"/></symbol>
  <symbol id="i-users" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="8.5" r="3.5"/><path d="M2.5 20c.6-3.6 3.2-5.5 6.5-5.5s5.9 1.9 6.5 5.5"/><path d="M16 5.2a3.3 3.3 0 0 1 0 6.6M18 14.8c2 .6 3.2 2.3 3.5 5.2"/></symbol>
  <symbol id="i-sliders" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7h9M17 7h3M4 17h3M11 17h9"/><circle cx="15" cy="7" r="2"/><circle cx="9" cy="17" r="2"/></symbol>
  <symbol id="i-keyboard" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="2.5" y="6" width="19" height="12" rx="2"/><path d="M6.5 10h.01M10 10h.01M13.5 10h.01M17 10h.01M7.5 14h9"/></symbol>
  <symbol id="i-help" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="8.5"/><path d="M9.6 9.5a2.5 2.5 0 0 1 4.8 1c0 1.7-2.4 2.2-2.4 3.7"/><path d="M12 17.3h.01"/></symbol>
  <symbol id="i-logout" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 4h4.5A1.5 1.5 0 0 1 20 5.5v13a1.5 1.5 0 0 1-1.5 1.5H14"/><path d="M9.5 16.5 5 12l4.5-4.5M5 12h10"/></symbol>
  <symbol id="i-sparkle" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4 12.8 9.2 18 11l-5.2 1.8L11 18l-1.8-5.2L4 11l5.2-1.8z"/><path d="M18.5 3.5v3M17 5h3"/></symbol>
  <symbol id="i-bell" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M6 16.5V11a6 6 0 0 1 12 0v5.5l1.5 1.5h-15z"/><path d="M10 20.5a2 2 0 0 0 4 0"/></symbol>
</svg>
<div><span class="mb-3.5 block text-[.72rem] tracking-[.12em] text-[#5b6474] uppercase">Avatar menu with a profile header</span>
<div class="relative min-h-[420px] rounded-[18px] border border-[#dfe3eb] bg-[#f8f9fc] bg-[radial-gradient(#d5dae4_1px,transparent_1px)] bg-size-[18px_18px] p-7 max-[600px]:p-5">
<div class="flex items-center gap-1.5 rounded-[14px] border border-[#e1e7e6] bg-white py-[7px] pr-2 pl-3.5 text-[.875rem] shadow-[0_1px_2px_rgba(15,40,36,.05)]"><span class="mr-auto flex items-center gap-2 font-extrabold tracking-[-.01em] text-[#15201e]"><i class="size-[22px] rounded-[7px] bg-[linear-gradient(135deg,#14b8a6,#0f766e)]"></i>Northwind</span><button class="grid size-9 cursor-pointer place-items-center rounded-[10px] border-0 bg-transparent p-0 text-[#56625f] transition-colors duration-150 hover:bg-[#eef5f3] hover:text-[#15201e] focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-[#0f766e] motion-reduce:transition-none" type="button" aria-label="Notifications"><svg class="size-5" aria-hidden="true"><use href="#i-bell"/></svg></button><div class="relative inline-block">
<button class="group relative cursor-pointer rounded-full border-0 bg-transparent p-0 after:absolute after:-right-px after:-bottom-px after:size-[11px] after:rounded-full after:border-2 after:border-white after:bg-[#12b76a] after:content-[''] focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-[#0f766e]" id="ac-a-btn" type="button" aria-label="Account menu for Maya Okafor" aria-haspopup="menu" aria-expanded="false" aria-controls="ac-a"><span class="grid size-9 flex-none place-items-center rounded-full bg-[linear-gradient(135deg,#b45309,#be185d)] text-[.75rem] font-bold tracking-[.02em] text-white shadow-[0_0_0_2px_#fff,0_0_0_3px_#e1e7e6] transition-shadow duration-150 group-aria-expanded:shadow-[0_0_0_2px_#fff,0_0_0_4px_#0f766e] motion-reduce:transition-none" aria-hidden="true">MO</span></button>
<div class="absolute top-[calc(100%+8px)] left-0 z-20 min-w-[250px] rounded-[14px] border border-[#e1e7e6] bg-white p-1.5 shadow-[0_2px_6px_rgba(15,40,36,.06),0_22px_44px_-14px_rgba(15,40,36,.28)] animate-menu-in motion-reduce:animate-none right-0 left-auto" role="menu" id="ac-a" aria-label="Account" hidden>
<div role="group" aria-labelledby="ac-a-h">
<div class="-mx-1.5 -mt-1.5 mb-1.5 flex items-center gap-3 border-b border-[#e1e7e6] px-4 py-3.5" id="ac-a-h"><span class="grid size-[42px] flex-none place-items-center rounded-full bg-[linear-gradient(135deg,#b45309,#be185d)] text-[.85rem] font-bold tracking-[.02em] text-white" aria-hidden="true">MO</span><span><b class="block text-[.875rem] leading-[1.3] font-bold text-[#15201e]">Maya Okafor</b><small class="block text-[.8rem] leading-[1.4] text-[#56625f]">[email protected]</small></span></div>
<a href="#profile" class="flex w-full cursor-pointer items-center gap-2.5 rounded-[9px] border-0 bg-transparent px-2.5 py-2 text-left leading-[1.4] font-medium whitespace-nowrap no-underline hover:bg-[#eef5f3] focus:bg-[#eef5f3] focus:outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[#0f766e] text-[#15201e]" role="menuitem" tabindex="-1"><svg class="size-[17px] flex-none text-[#56625f]" aria-hidden="true"><use href="#i-user"/></svg>Your profile</a>
<a href="#settings" class="flex w-full cursor-pointer items-center gap-2.5 rounded-[9px] border-0 bg-transparent px-2.5 py-2 text-left leading-[1.4] font-medium whitespace-nowrap no-underline hover:bg-[#eef5f3] focus:bg-[#eef5f3] focus:outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[#0f766e] text-[#15201e]" role="menuitem" tabindex="-1" aria-keyshortcuts="Meta+,"><svg class="size-[17px] flex-none text-[#56625f]" aria-hidden="true"><use href="#i-sliders"/></svg><span>Settings</span><kbd class="ml-auto pl-[18px] font-mono text-[.75rem] font-medium text-[#56625f]" aria-hidden="true">⌘,</kbd></a>
<a href="#billing" class="flex w-full cursor-pointer items-center gap-2.5 rounded-[9px] border-0 bg-transparent px-2.5 py-2 text-left leading-[1.4] font-medium whitespace-nowrap no-underline hover:bg-[#eef5f3] focus:bg-[#eef5f3] focus:outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[#0f766e] text-[#15201e]" role="menuitem" tabindex="-1"><svg class="size-[17px] flex-none text-[#56625f]" aria-hidden="true"><use href="#i-card"/></svg><span>Billing</span><span class="ml-auto rounded-full bg-[#dff3ef] px-2 py-px text-[.7rem] leading-[1.6] font-bold text-[#115e56]">Pro</span></a>
</div>
<div class="-mx-1.5 my-1.5 h-px bg-[#e1e7e6]" role="separator"></div>
<button type="button" class="flex w-full cursor-pointer items-center gap-2.5 rounded-[9px] border-0 bg-transparent px-2.5 py-2 text-left leading-[1.4] font-medium whitespace-nowrap no-underline hover:bg-[#eef5f3] focus:bg-[#eef5f3] focus:outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[#0f766e] text-[#15201e]" role="menuitem" tabindex="-1" aria-keyshortcuts="Shift+?"><svg class="size-[17px] flex-none text-[#56625f]" aria-hidden="true"><use href="#i-keyboard"/></svg><span>Keyboard shortcuts</span><kbd class="ml-auto pl-[18px] font-mono text-[.75rem] font-medium text-[#56625f]" aria-hidden="true">?</kbd></button>
<a href="#help" class="flex w-full cursor-pointer items-center gap-2.5 rounded-[9px] border-0 bg-transparent px-2.5 py-2 text-left leading-[1.4] font-medium whitespace-nowrap no-underline hover:bg-[#eef5f3] focus:bg-[#eef5f3] focus:outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[#0f766e] text-[#15201e]" role="menuitem" tabindex="-1"><svg class="size-[17px] flex-none text-[#56625f]" aria-hidden="true"><use href="#i-help"/></svg>Help centre</a>
<div class="-mx-1.5 my-1.5 h-px bg-[#e1e7e6]" role="separator"></div>
<a href="#sign-out" class="flex w-full cursor-pointer items-center gap-2.5 rounded-[9px] border-0 bg-transparent px-2.5 py-2 text-left leading-[1.4] font-medium whitespace-nowrap no-underline hover:bg-[#eef5f3] focus:bg-[#eef5f3] focus:outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[#0f766e] text-[#15201e]" role="menuitem" tabindex="-1"><svg class="size-[17px] flex-none text-[#56625f]" aria-hidden="true"><use href="#i-logout"/></svg>Sign out</a>
</div>
</div></div>
</div></div>
<div><span class="mb-3.5 block text-[.72rem] tracking-[.12em] text-[#5b6474] uppercase">Workspace switcher</span>
<div class="relative min-h-[420px] rounded-[18px] border border-[#dfe3eb] bg-[#f8f9fc] bg-[radial-gradient(#d5dae4_1px,transparent_1px)] bg-size-[18px_18px] p-7 max-[600px]:p-5">
<div class="relative block max-w-[280px] text-[.875rem]">
<button class="flex w-full cursor-pointer items-center gap-2.5 rounded-xl border border-[#d3dbd9] bg-white py-2 pr-2.5 pl-2 text-left text-[#15201e] shadow-[0_1px_2px_rgba(15,40,36,.05)] transition-[border-color,box-shadow] duration-150 hover:border-[#b9c5c2] aria-expanded:border-[#0f766e] aria-expanded:shadow-[0_0_0_3px_rgba(15,118,110,.15)] focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-[#0f766e] motion-reduce:transition-none" id="ac-b-btn" type="button" aria-haspopup="menu" aria-expanded="false" aria-controls="ac-b"><span class="grid size-[34px] rounded-[9px] text-[.85rem] flex-none place-items-center bg-[linear-gradient(135deg,#0d9488,#115e59)] font-extrabold text-white" aria-hidden="true">N</span><span><b class="block leading-[1.3] font-bold">Northwind Studio</b><small class="block text-[.75rem] leading-[1.4] text-[#56625f]">Pro plan · 14 members</small></span><svg class="ml-auto size-4 flex-none text-[#56625f]" aria-hidden="true"><use href="#i-updown"/></svg><span class="sr-only">, switch workspace</span></button>
<div class="absolute top-[calc(100%+8px)] left-0 z-20 min-w-[250px] rounded-[14px] border border-[#e1e7e6] bg-white p-1.5 shadow-[0_2px_6px_rgba(15,40,36,.06),0_22px_44px_-14px_rgba(15,40,36,.28)] animate-menu-in motion-reduce:animate-none w-full" role="menu" id="ac-b" aria-labelledby="ac-b-btn" hidden>
<div role="group" aria-labelledby="ac-b-h">
<div class="px-2.5 pt-1.5 pb-1 text-[.6875rem] font-bold tracking-[.07em] text-[#56625f] uppercase" id="ac-b-h">Workspaces</div>
<a href="#northwind" class="flex w-full cursor-pointer items-center gap-2.5 rounded-[9px] border-0 bg-transparent px-2.5 py-2 text-left leading-[1.4] font-medium whitespace-nowrap no-underline hover:bg-[#eef5f3] focus:bg-[#eef5f3] focus:outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[#0f766e] text-[#15201e]" role="menuitem" tabindex="-1" aria-current="true"><span class="grid size-[26px] rounded-[7px] text-[.72rem] flex-none place-items-center bg-[linear-gradient(135deg,#0d9488,#115e59)] font-extrabold text-white" aria-hidden="true">N</span><span>Northwind Studio</span><svg class="ml-auto size-[17px] text-[#0f766e]" aria-hidden="true"><use href="#i-check"/></svg></a>
<a href="#fieldnotes" class="flex w-full cursor-pointer items-center gap-2.5 rounded-[9px] border-0 bg-transparent px-2.5 py-2 text-left leading-[1.4] font-medium whitespace-nowrap no-underline hover:bg-[#eef5f3] focus:bg-[#eef5f3] focus:outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[#0f766e] text-[#15201e]" role="menuitem" tabindex="-1"><span class="grid size-[26px] rounded-[7px] text-[.72rem] flex-none place-items-center bg-[linear-gradient(135deg,#b45309,#92400e)] font-extrabold text-white" aria-hidden="true">F</span>Fieldnotes Press</a>
<a href="#personal" class="flex w-full cursor-pointer items-center gap-2.5 rounded-[9px] border-0 bg-transparent px-2.5 py-2 text-left leading-[1.4] font-medium whitespace-nowrap no-underline hover:bg-[#eef5f3] focus:bg-[#eef5f3] focus:outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[#0f766e] text-[#15201e]" role="menuitem" tabindex="-1"><span class="grid size-[26px] rounded-[7px] text-[.72rem] flex-none place-items-center bg-[linear-gradient(135deg,#64748b,#334155)] font-extrabold text-white" aria-hidden="true">P</span>Personal</a>
</div>
<div class="-mx-1.5 my-1.5 h-px bg-[#e1e7e6]" role="separator"></div>
<a href="#new-workspace" class="flex w-full cursor-pointer items-center gap-2.5 rounded-[9px] border-0 bg-transparent px-2.5 py-2 text-left leading-[1.4] font-medium whitespace-nowrap no-underline hover:bg-[#eef5f3] focus:bg-[#eef5f3] focus:outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[#0f766e] text-[#15201e]" role="menuitem" tabindex="-1"><svg class="size-[17px] flex-none text-[#56625f]" aria-hidden="true"><use href="#i-plus"/></svg>Create workspace</a>
<a href="#join" class="flex w-full cursor-pointer items-center gap-2.5 rounded-[9px] border-0 bg-transparent px-2.5 py-2 text-left leading-[1.4] font-medium whitespace-nowrap no-underline hover:bg-[#eef5f3] focus:bg-[#eef5f3] focus:outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[#0f766e] text-[#15201e]" role="menuitem" tabindex="-1"><svg class="size-[17px] flex-none text-[#56625f]" aria-hidden="true"><use href="#i-link"/></svg>Join with an invite code</a>
</div>
</div>
</div></div>
<div><span class="mb-3.5 block text-[.72rem] tracking-[.12em] text-[#5b6474] uppercase">Profile card with plan usage</span>
<div class="relative min-h-[420px] rounded-[18px] border border-[#dfe3eb] bg-[#f8f9fc] bg-[radial-gradient(#d5dae4_1px,transparent_1px)] bg-size-[18px_18px] p-7 max-[600px]:p-5">
<div class="relative inline-block text-[.875rem]">
<button class="group inline-flex h-11 cursor-pointer items-center gap-[9px] rounded-full border border-[#d3dbd9] bg-white pr-3 pl-1 font-semibold text-[#15201e] shadow-[0_1px_2px_rgba(15,40,36,.05)] transition-colors duration-150 hover:bg-[#eef5f3] aria-expanded:bg-[#eef5f3] focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-[#0f766e] motion-reduce:transition-none" id="ac-c-btn" type="button" aria-haspopup="menu" aria-expanded="false" aria-controls="ac-c"><span class="grid size-[34px] flex-none place-items-center rounded-full bg-[linear-gradient(135deg,#0f766e,#1d4ed8)] text-[.75rem] font-bold tracking-[.02em] text-white" aria-hidden="true">DP</span>Dev Patel<svg class="size-4 text-[#56625f] transition-[rotate] duration-200 group-aria-expanded:rotate-180 motion-reduce:transition-none" aria-hidden="true"><use href="#i-chev-down"/></svg></button>
<div class="absolute top-[calc(100%+8px)] left-0 z-20 min-w-[250px] rounded-[14px] border border-[#e1e7e6] bg-white p-1.5 shadow-[0_2px_6px_rgba(15,40,36,.06),0_22px_44px_-14px_rgba(15,40,36,.28)] animate-menu-in motion-reduce:animate-none w-[292px] max-w-[calc(100vw-72px)]" role="menu" id="ac-c" aria-labelledby="ac-c-btn" hidden>
<div role="group" aria-labelledby="ac-c-h">
<div class="-mx-1.5 -mt-1.5 mb-1.5 flex items-center gap-3 border-b border-[#e1e7e6] px-4 py-3.5" id="ac-c-h"><span class="grid size-[42px] flex-none place-items-center rounded-full bg-[linear-gradient(135deg,#0f766e,#1d4ed8)] text-[.85rem] font-bold tracking-[.02em] text-white" aria-hidden="true">DP</span><span><b class="block text-[.875rem] leading-[1.3] font-bold text-[#15201e]">Dev Patel</b><small class="block text-[.8rem] leading-[1.4] text-[#56625f]">[email protected]</small></span><span class="ml-auto self-start rounded-md bg-[#15201e] px-2 py-0.5 text-[.68rem] font-bold tracking-[.04em] text-white uppercase">Business</span></div>
<a href="#storage" class="flex w-full cursor-pointer items-center gap-2.5 rounded-[9px] border-0 bg-transparent px-2.5 py-2 text-left leading-[1.4] font-medium whitespace-nowrap no-underline hover:bg-[#eef5f3] focus:bg-[#eef5f3] focus:outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[#0f766e] text-[#15201e] items-start [&>svg]:mt-0.5" role="menuitem" tabindex="-1"><svg class="size-[17px] flex-none text-[#56625f]" aria-hidden="true"><use href="#i-folder"/></svg><span class="min-w-0 flex-1"><span class="flex justify-between gap-3"><span>Storage</span><span class="text-[.8rem] text-[#56625f]">7.2 of 10 GB</span></span><span class="mt-1.5 block h-1.5 overflow-hidden rounded-full bg-[#dfe7e5]" aria-hidden="true"><i class="block h-full w-[72%] rounded-full bg-[linear-gradient(90deg,#14b8a6,#0f766e)]"></i></span></span></a>
<a href="#upgrade" class="flex w-full cursor-pointer items-center gap-2.5 rounded-[9px] border-0 bg-transparent px-2.5 py-2 text-left leading-[1.4] font-medium whitespace-nowrap no-underline hover:bg-[#eef5f3] focus:bg-[#eef5f3] focus:outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[#0f766e] text-[#15201e] font-semibold text-[#0f766e]! [&>svg]:text-[#0f766e]!" role="menuitem" tabindex="-1"><svg class="size-[17px] flex-none text-[#56625f]" aria-hidden="true"><use href="#i-sparkle"/></svg>Upgrade plan</a>
</div>
<div class="-mx-1.5 my-1.5 h-px bg-[#e1e7e6]" role="separator"></div>
<a href="#team" class="flex w-full cursor-pointer items-center gap-2.5 rounded-[9px] border-0 bg-transparent px-2.5 py-2 text-left leading-[1.4] font-medium whitespace-nowrap no-underline hover:bg-[#eef5f3] focus:bg-[#eef5f3] focus:outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[#0f766e] text-[#15201e]" role="menuitem" tabindex="-1"><svg class="size-[17px] flex-none text-[#56625f]" aria-hidden="true"><use href="#i-users"/></svg><span>Team members</span><span class="ml-auto rounded-full bg-[#dff3ef] px-2 py-px text-[.7rem] leading-[1.6] font-bold text-[#115e56]">6</span></a>
<a href="#account" class="flex w-full cursor-pointer items-center gap-2.5 rounded-[9px] border-0 bg-transparent px-2.5 py-2 text-left leading-[1.4] font-medium whitespace-nowrap no-underline hover:bg-[#eef5f3] focus:bg-[#eef5f3] focus:outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[#0f766e] text-[#15201e]" role="menuitem" tabindex="-1"><svg class="size-[17px] flex-none text-[#56625f]" aria-hidden="true"><use href="#i-sliders"/></svg>Account settings</a>
<div class="-mx-1.5 my-1.5 h-px bg-[#e1e7e6]" role="separator"></div>
<a href="#sign-out" class="flex w-full cursor-pointer items-center gap-2.5 rounded-[9px] border-0 bg-transparent px-2.5 py-2 text-left leading-[1.4] font-medium whitespace-nowrap no-underline hover:bg-[#eef5f3] focus:bg-[#eef5f3] focus:outline-none focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[#0f766e] text-[#15201e]" role="menuitem" tabindex="-1"><svg class="size-[17px] flex-none text-[#56625f]" aria-hidden="true"><use href="#i-logout"/></svg>Sign out</a>
</div>
</div>
</div></div>
CSS
@theme {
  --animate-menu-in: menu-in .16s cubic-bezier(.2,.8,.3,1);
  @keyframes menu-in { from { opacity: 0; translate: 0 -6px; } }
}
JavaScript
// Menu button (WAI-ARIA APG pattern). A trigger has aria-haspopup="menu", aria-expanded and aria-controls="<menu id>".
// Click, Enter, Space or ArrowDown opens the menu (ArrowUp opens it on the last item). Inside: ArrowUp/ArrowDown move,
// Home/End jump, typing a letter jumps to the next item that starts with it, Esc closes and returns focus to the trigger,
// Tab or a click outside closes. Choosing an item closes the menu; checkbox items stay open so you can tick several.
(() => {
  const OPEN = '[aria-haspopup="menu"][aria-expanded="true"]';
  const menuOf = t => document.getElementById(t.getAttribute('aria-controls'));
  const ownerOf = m => m && document.querySelector(`[aria-controls="${m.id}"][aria-expanded="true"]`);
  const itemsOf = m => [...m.querySelectorAll('[role^="menuitem"]')]
    .filter(i => i.closest('[role="menu"]') === m && i.getAttribute('aria-disabled') !== 'true');

  function close(t, focus) {
    const m = menuOf(t);
    m.querySelectorAll(OPEN).forEach(s => close(s));
    t.setAttribute('aria-expanded', 'false');
    m.hidden = true;
    if (focus) t.focus();
  }
  function open(t, at) {
    const level = t.closest('[role="menu"]');
    document.querySelectorAll(OPEN).forEach(o => { if (o !== t && o.closest('[role="menu"]') === level) close(o); });
    t.setAttribute('aria-expanded', 'true');
    menuOf(t).hidden = false;
    if (at !== undefined) itemsOf(menuOf(t)).at(at)?.focus();
  }
  const closeAll = target => document.querySelectorAll(OPEN).forEach(t => {
    if (!t.closest('[role="menu"]') && !t.contains(target) && !menuOf(t).contains(target)) close(t);
  });

  document.addEventListener('click', e => {
    const t = e.target.closest('[aria-haspopup="menu"][aria-controls]:not([data-context])');
    if (t) {
      if (!t.closest('[role="menu"]') && t.getAttribute('aria-expanded') === 'true') close(t);
      else open(t, e.detail === 0 ? 0 : undefined); // keyboard clicks (Enter/Space) move focus into the menu
      return;
    }
    const item = e.target.closest('[role^="menuitem"]');
    if (item && item.getAttribute('aria-disabled') !== 'true') {
      const role = item.getAttribute('role');
      if (role === 'menuitemcheckbox') { item.setAttribute('aria-checked', item.getAttribute('aria-checked') !== 'true'); return; }
      if (role === 'menuitemradio') {
        const set = item.closest('[role="group"]') || item.closest('[role="menu"]');
        set.querySelectorAll('[role="menuitemradio"]').forEach(r => r.setAttribute('aria-checked', r === item));
      }
      let t = ownerOf(item.closest('[role="menu"]'));
      while (t && t.closest('[role="menu"]')) t = ownerOf(t.closest('[role="menu"]'));
      if (t) close(t, true);
      return;
    }
    closeAll(e.target);
  });

  document.addEventListener('keydown', e => {
    const t = e.target.closest('[aria-haspopup="menu"][aria-controls]:not([data-context])');
    if (t && !t.closest('[role="menu"]')) {
      if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { e.preventDefault(); open(t, e.key === 'ArrowDown' ? 0 : -1); }
      else if (e.key === 'Escape' && t.getAttribute('aria-expanded') === 'true') close(t);
      return;
    }
    const m = e.target.closest('[role="menu"]');
    if (!m) return;
    const items = itemsOf(m), i = items.indexOf(e.target), owner = ownerOf(m);
    let next;
    switch (e.key) {
      case 'ArrowDown': next = items[(i + 1) % items.length]; break;
      case 'ArrowUp': next = items.at(i <= 0 ? -1 : i - 1); break;
      case 'Home': next = items[0]; break;
      case 'End': next = items.at(-1); break;
      case 'Escape': if (owner) { e.preventDefault(); close(owner, true); } return;
      case 'Tab': closeAll(null); return;
      case ' ': if (e.target.tagName === 'A') { e.preventDefault(); e.target.click(); } return;
      default:
        if (e.key.length !== 1 || e.ctrlKey || e.metaKey || e.altKey) return;
        next = [...items.slice(i + 1), ...items.slice(0, i + 1)]
          .find(x => x.textContent.trim().toLowerCase().startsWith(e.key.toLowerCase()));
    }
    e.preventDefault();
    next?.focus();
  });
})();

About this set

Three account menus for the top right corner of an app. Avatar menu with a profile header sits in a small app bar: the avatar has an online dot, and the menu opens with the person’s name and email above Your profile, Settings, Billing with a Pro badge, Keyboard shortcuts, Help centre and Sign out. Workspace switcher is a full-width button showing the current workspace, plan and member count; its menu lists workspaces with a tick on the current one, then Create workspace and Join with an invite code. Profile card with plan usage is a pill with avatar and name whose menu shows the plan, a storage meter that doubles as a link, an Upgrade plan item and the team size. The profile header is the visible label of the first group, so a screen reader announces the name and email on entering the menu. The current workspace is marked with aria-current rather than colour alone, and the meter bar is decorative because the item text already says 7.2 of 10 GB. Menus align to the end of their trigger where they would otherwise run off the page.

What’s included

  • Avatar trigger with a status dot and an accessible name
  • Profile header that labels the first menu group
  • Workspace switcher with aria-current on the active workspace
  • Plan badge, count badge and a storage meter item
  • End-aligned menu for triggers at the right edge

Accessibility

The avatar-only trigger is named "Account menu for Maya Okafor", and every trigger exposes aria-haspopup, aria-expanded and aria-controls. The profile header labels the first role="group", the current workspace carries aria-current, and decorative avatars and the meter bar are hidden from assistive technology. Items and triggers have visible focus outlines, keyboard handling follows the WAI-ARIA menu button pattern, and animations stop under prefers-reduced-motion.

Customise it

Change the --ac-* custom properties on .ac-wrap for ink, lines, hover and the teal accent, and the gradients on .ac-av and .ac-logo for avatars. Bootstrap uses the same properties plus --bs-dropdown-* on .ac-menu; in Tailwind edit the hex values in the classes.