Catalog · Navigation
Submenu
Groups a few links of a header under one button, so the header keeps a short row of links.
Fits best: any site — when a header has more links than fit in one row
<luz-submenu>- No JavaScript
- Light · Dark · High contrast
- 2 known issues
Example
A winery’s header with two submenus: Wines and About us.
Press Wines or About us. From 769 px of window width each opens a box under its button; at 768 px and below the links are in the menu, and each group opens in place, under its button.
Known issues
A submenu is a native <details>, which has no Escape key and does not close when you click elsewhere: it closes when you press its button again or open another one (in browsers that support name on <details>: Chrome and Edge 120, Safari 17.2, Firefox 130; in older ones several can be open at once). From 769 px an open box stays over the top of the page until then, and if you keep pressing Tab after it, a link of the page behind it can be hidden (WCAG 2.2 success criterion 2.4.11, Focus not obscured).
In the menu at 768 px and below this does not happen: the links are in the flow and Escape closes the whole menu. It has not been tested with a screen reader.
Code
Copy the markup
import "@luz-ds/catalog/components/submenu.css";
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@luz-ds/catalog@0.3.0/dist/components/submenu.min.css" integrity="sha384-y+7suWYL2a3Jot2cD9bCKS/ollQ/VHJGqkHQNIHmAEnQh1TyfrypOHFyhNk/3x3H" crossorigin="anonymous">
An item of the list of the catalog’s navbar, in a header such as the site header or the masthead, whose content is a <luz-submenu> around a <details>: its <summary> is the button, and a list of links follows it. No class: the tag is the block, and its parts are found by their place.
Give every submenu of the header the same name, so opening one closes the others.
<nav class="luz-navbar" id="site-nav" aria-label="Main" popover>
<ul class="luz-navbar__list">
<li><a class="luz-navbar__link" href="/" aria-current="page">Home</a></li>
<li>
<luz-submenu>
<details name="site-menu">
<summary>Wines</summary>
<ul>
<li><a class="luz-navbar__link" href="/wines/red/">Red wines</a></li>
<li><a class="luz-navbar__link" href="/wines/white/">White wines</a></li>
</ul>
</details>
</luz-submenu>
</li>
<li><a class="luz-navbar__link" href="/visits/">Visits</a></li>
</ul>
</nav>
Variants and options
There are no variants. The attributes are the browser’s.
| Tag or attribute | Effect |
|---|---|
<luz-submenu> | Around the <details>, with display: contents: it draws no box and adds no role. The <summary> looks like the links beside it, with a chevron; the list of links is indented under it, and from 769 px a box under it |
name | The same value on every submenu of the header: opening one closes the others. Browsers without it leave the others open |
open | Set by the browser. Do not write it in the markup: a submenu open on every page hides the page under it |
How it works
CSS from @luz-ds/catalog and a native <details>. No JavaScript: <luz-submenu> is a tag that no script ever defines.
- The browser opens and closes it, and tells assistive technology whether it is open. A submenu opens on a click, a tap, Enter or Space: never on hover, so it does not open under a pointer passing by, and it works the same on a touch screen.
- At 768 px and below, inside the header’s menu, the links of a group open in place, under their button, indented, and push the next links down.
- From 769 px, where the header’s links are a row, they open in a box under their button, over the page; the page does not move. The box of the last item opens toward the start of the row, so it stays in the window, in either direction of text.
- The chevron is drawn with two borders and has no text: the state is the one the browser exposes.
- It does not use the popover API: a popover sits in the top layer, so it could not open in place inside the menu.
Without JavaScript
Nothing changes: there is no script. The links are plain links.
Keyboard and ARIA
- Order: a closed submenu is one stop, its button. Open, its links come right after it, then the next item of the header.
- Keys: Enter or Space on the button opens and closes it; focus stays on the button. Tab and Shift+Tab move through the links. Escape does nothing in a submenu (see Known issues); in the header’s menu at 768 px and below it closes the menu and returns focus to the menu button.
- Names: the button’s name is its text. The browser exposes the expanded state of a
<summary>: write noaria-expanded, norole="menu"and norole="menuitem"; these are links in a navigation landmark, not an application menu. - Current page: put
aria-current="page"on the link of the page, in the submenu too.
Contrast and focus are in the Design tab.
Design
When to use
Use it when the header needs more links than fit in a short row, and some of them belong together: the kinds of wine, the pages about the company.
When not to use
Do not use it for a single link, or to hide pages most visitors need: put those in the row itself.
- Do not nest a submenu in a submenu: two levels hide pages from the visitor and are hard to use with a pointer and a keyboard.
- Do not put actions, forms or settings in it: it holds links to pages. For actions use buttons, or a dropdown.
- If every group has many links, give each group a page of its own and link to it: a long submenu is a page in a box.
It replaces a dropdown navigation built with a script, and the hover menu. Nothing in LUZ is removed.
Do and don't
Writing the content
Name the group with one or two words in sentence case that say what it holds: “Wines”, “About us”.
- Name each link after the page it opens, so it makes sense outside the group: “Red wines”, not “Red”.
- Do not make the group’s name a page of its own as well: the button opens the group, it does not go anywhere. If the group has an overview page, make it the first link: “All wines”.
- Keep the same groups, in the same order, on every page.
Contrast and focus
The button and the links in the row sit on the page; from 769 px the box is a raised surface with a border and a shadow.
| Part | Colour on background | Light | Dark | High contrast | Needs |
|---|---|---|---|---|---|
Button text and chevron | --luz-text-secondary on --luz-surface-page | 6.2:1 | 7.5:1 | 19.3:1 | 4.5:1 |
Button, hovered or open | --luz-text-primary on --luz-surface-page | 15.8:1 | 15.1:1 | 21.0:1 | 4.5:1 |
Link in the box, from 769 px | --luz-text-secondary on --luz-surface | 6.4:1 | 6.9:1 | 19.3:1 | 4.5:1 |
Link in the box, hovered | --luz-text-primary on --luz-surface | 16.5:1 | 14.0:1 | 21.0:1 | 4.5:1 |
Focus ring on the button | --luz-focus-ring on --luz-surface-page | 5.6:1 | 7.7:1 | 21.0:1 | 3:1 |
Focus ring on a link in the box | --luz-focus-ring on --luz-surface | 5.9:1 | 7.2:1 | 21.0:1 | 3:1 |
Measured from the token values of each mode, rounded down. Needs is the WCAG 2.2 AA minimum: 4.5:1 for text, 3:1 for focus rings. In the menu at 768 px and below, the links are on the menu’s page surface, as in the first two rows.
- Focus: core’s 2 px
--luz-focus-ringoutline, 2 px outside the button and each link. - Open, the chevron points up as well as the button’s text getting darker: the state is never only a colour.
- The current page’s link is underlined, as in the row.
Tokens it uses
The 14 tokens that the rules of catalog’s components/submenu.css read, by family. The links in it are the catalog’s navbar links. Each family links to its page, with the values in every mode. In Figma they are variables with the same names: see Tokens in Figma.
- Spacing and sizing
--luz-control-lg,--luz-space-2xs,--luz-space-xs,--luz-space-md,--luz-space-xl- Typography
--luz-font-sans,--luz-text-md- Radius
--luz-radius-md- Shadows
--luz-shadow-md- Layers
--luz-z-overlay
Keyboard and ARIA are in the Code tab.
Last updated · Edit this page · Report a problem