Connection lost. Reconnecting… attempt 1 of 8
Paused. Your work is held on the server.
Could not reconnect.
This session has expired on the server.
An unhandled error has occurred.
Sedna.UI
v0.17.0 · main

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.

Do not add 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>