Menus
tier 2 — classes
One dropdown panel style, for a short list of actions opened from a control, and the
value trigger that opens one. The header's user menu is the same .menu class
— there is not a second panel style for the frame. A panel of content is a
popover.
role="menu"
That role promises arrow-key navigation
and a roving tabindex this does not implement; the items are ordinary links and buttons that
tab. Put aria-expanded on the trigger and leave the panel unlabelled by role.
Anchored under a trigger
Open it — the demo is live. .menu-anchor wraps the trigger and
the panel and declares the anchor-name the panel positions against, so the
panel lands under the trigger with nothing measured. data-menu-toggle on the
trigger hands it to sednaUi.menu — one panel open at a time, a click outside or
on an item closing it, Escape closing it and returning focus to the trigger — and
the closed state is the hidden attribute rather than a class,
which is what takes the panel out of the tab order as well as out of the layout; an app
holding the state in C# leaves the attribute off and renders hidden itself. The
panel lines up with the anchor's trailing edge and sizes to its widest row, floored
at 220px — add .menu--start to the panel for a trigger near the leading edge.
A menu is for actions: a list of values is a <select> or a
segmented control, a list of places is the sidebar.
<div style="padding-bottom:210px">
<div class="menu-anchor">
<button class="btn" type="button" data-menu-toggle aria-expanded="false">
<i class="ri-more-2-fill"></i> Actions
</button>
<div class="menu menu--start" hidden>
<span class="menu-label">This order</span>
<button class="menu-item" type="button">
<i class="ri-user-shared-line"></i> Reassign<span class="menu-item-note">R</span>
</button>
<button class="menu-item" type="button">
<i class="ri-time-line"></i> Snooze<span class="menu-item-note">S</span>
</button>
<button class="menu-item" type="button" aria-disabled="true">
<i class="ri-git-merge-line"></i> Merge
</button>
<hr class="menu-sep" />
<a class="menu-item" href="#"><i class="ri-external-link-line"></i> Open in the warehouse</a>
<button class="menu-item menu-item--danger" type="button">
<i class="ri-delete-bin-line"></i> Discard reservation
</button>
</div>
</div>
</div>
A value trigger in a table cell
Live — open one, then scroll the table sideways.
.value-trigger shows the current value of something and opens a menu to
change it — a status, an assignee, an access level. Reach for it where a
<select> would be wrong because the options need icons or a description;
where a plain list of values would do, use a <select>.
--value-trigger-measure is how much of the value the control keeps room for,
10ch by default, and .col-control on the th and the
td gives the column its floor — see Tables.
| Member | Access | Last seen |
|---|---|---|
| Jordan Weiss | 4 min ago | |
| Priya Nair | 2 days ago |
<div class="sedna-scroll-x" tabindex="0" role="group" aria-label="Repository access">
<table class="table">
<thead>
<tr>
<th>Member</th>
<th class="col-control">Access</th>
<th class="col-num">Last seen</th>
</tr>
</thead>
<tbody>
<tr>
<td>Jordan Weiss</td>
<td class="col-control">
<div class="menu-anchor">
<button class="value-trigger" type="button" data-menu-toggle aria-expanded="false"
aria-label="Access level for Jordan Weiss">
<span>Maintainer</span>
</button>
<div class="menu menu--start" hidden>
<span class="menu-label">Access level</span>
<button class="menu-item" type="button"><i class="ri-eye-line"></i> Viewer</button>
<button class="menu-item" type="button"><i class="ri-git-pull-request-line"></i> Contributor</button>
<button class="menu-item" type="button"><i class="ri-shield-star-line"></i> Maintainer</button>
</div>
</div>
</td>
<td class="col-num col-muted">4 min ago</td>
</tr>
<tr>
<td>Priya Nair</td>
<td class="col-control">
<div class="menu-anchor">
<button class="value-trigger" type="button" data-menu-toggle aria-expanded="false"
aria-label="Access level for Priya Nair">
<span>Viewer</span>
</button>
<div class="menu menu--start" hidden>
<span class="menu-label">Access level</span>
<button class="menu-item" type="button"><i class="ri-eye-line"></i> Viewer</button>
<button class="menu-item" type="button"><i class="ri-git-pull-request-line"></i> Contributor</button>
<button class="menu-item" type="button"><i class="ri-shield-star-line"></i> Maintainer</button>
</div>
</div>
</td>
<td class="col-num col-muted">2 days ago</td>
</tr>
</tbody>
</table>
</div>
Dismissing it
There is no JavaScript behind this. Put .menu-scrim immediately
before the panel — a transparent full-viewport element that catches the
click which closes the menu, and coming first in the markup is what puts the panel above it.
Handle Escape on the wrapper as well, or the menu can only be closed with a
pointer.
<div class="menu-anchor" @onkeydown="HandleKeyDown">
<button class="btn" type="button" aria-expanded="@_open" @onclick="Toggle">Actions</button>
@if (_open)
{
<div class="menu-scrim" @onclick="Close"></div>
<div class="menu">
<button class="menu-item" type="button">Reassign</button>
</div>
}
</div>