DDI

Menus

Menus

CollapsibleMenu

CollapsibleMenu moves the items that don’t fit into an overflow popover, opened by a “Meny” button. Use it for responsive navigation bars and headers. Its items can be any elements: links, buttons or VerticalMenus.

Resize the window to see items move into the overflow menu
---
import { CollapsibleMenu } from "@dynamic-type/ddi/server";
---

<header class="x-flow items:center p bg rounded inline-size:100cqi">
    <div class="p bg:alt rounded">Logo</div>
    <CollapsibleMenu
        class="flex:1"
        buttonClass="bg:alt"
        overflowItemsClass="rounded drop-shadow"
    >
        <a href="#item-1" class="button tertiary">Item 1</a>
        <a href="#item-2" class="button tertiary">Item 2</a>
        <a href="#item-3" class="button tertiary">Item 3</a>
        <a href="#item-4" class="button tertiary">Item 4</a>
        <a href="#item-5" class="button tertiary">Item 5</a>
        <a href="#item-6" class="button tertiary">Item 6</a>
        <a href="#item-7" class="button tertiary">Item 7</a>
        <a href="#item-8" class="button tertiary">Item 8</a>
    </CollapsibleMenu>
</header>

Set collapseAll to move every item into the overflow menu as soon as one of them doesn’t fit.

collapseAll: all items or none
---
import { CollapsibleMenu } from "@dynamic-type/ddi/server";
---

<header class="x-flow items:center p bg rounded inline-size:100cqi">
    <div class="p bg:alt rounded">Logo</div>
    <CollapsibleMenu class="flex:1" collapseAll>
        <a href="#item-1" class="button tertiary">Item 1</a>
        <a href="#item-2" class="button tertiary">Item 2</a>
        <a href="#item-3" class="button tertiary">Item 3</a>
        <a href="#item-4" class="button tertiary">Item 4</a>
        <a href="#item-5" class="button tertiary">Item 5</a>
        <a href="#item-6" class="button tertiary">Item 6</a>
        <a href="#item-7" class="button tertiary">Item 7</a>
        <a href="#item-8" class="button tertiary">Item 8</a>
    </CollapsibleMenu>
</header>

Fold order and an icons-only button

Items fold from the right. Mark an item data-ddi-collapse-last to keep it out of the overflow menu as long as there is room for it: it folds only after every other item has. With collapseAll, all the other items fold at once, then the collapse-last items fold one at a time from the right.

With buttonIcons, the button shows copies of the folded items’ icons (the child marked data-ddi-menu-icon) and no text. buttonLabel becomes its aria-label.

An item that opens a popover with data-ddi-popovertarget opens it from the overflow button while it is folded. That also applies to a script calling item.click(). The overflow menu closes, and the popover is placed with the button’s buttonAlign and offsets.

Search folds last; the button shows the folded icons
Logo
John 3
Settings
---
import { CollapsibleMenu, Popover } from "@dynamic-type/ddi/server";

const tools = [
    { name: "Compare", d: "M4 6h16M4 12h16M4 18h16" },
    {
        name: "Search",
        d: "M11 4a7 7 0 1 0 0 14a7 7 0 0 0 0-14ZM16 16l4 4",
        last: true,
    },
    {
        name: "Settings",
        d: "M12 4v16M4 12h16",
        popover: "icons-example-settings",
    },
    {
        name: "Account",
        d: "M12 4a4 4 0 1 0 0 8a4 4 0 0 0 0-8ZM4 20a8 8 0 0 1 16 0",
    },
];
---

<header class="x-flow items:center p bg rounded inline-size:100cqi">
    <div class="p bg:alt rounded">Logo</div>
    <div class="flex:1">John 3</div>
    <CollapsibleMenu
        collapseAll
        buttonIcons
        buttonLabel="Menu"
        buttonAlign="top-center"
    >
        {
            tools.map(({ name, d, last, popover }) => (
                <button
                    class="tertiary y-flow items:center"
                    data-ddi-collapse-last={last}
                    data-ddi-popovertarget={popover}
                >
                    <svg
                        data-ddi-menu-icon
                        viewBox="0 0 24 24"
                        width="20"
                        height="20"
                        fill="none"
                        stroke="currentColor"
                        stroke-width="2"
                    >
                        <path d={d} />
                    </svg>
                    {name}
                </button>
            ))
        }
    </CollapsibleMenu>
</header>

<Popover id="icons-example-settings" class="p bg rounded drop-shadow">
    Settings
</Popover>

When it re-measures

The menu re-measures whenever the menu, its parent, an item or the button changes size. That covers text size, a changed label and a hidden menu being shown. It also re-measures on window resize and once web fonts have loaded. Each measurement restores every item to the row, measures it and folds again in the same frame, so a wider row brings items back.

It never folds while a pointer is down in the menu or while the overflow menu is open. It waits for the pointer to lift or the menu to close.

For changes no observer sees, call menu.refresh() or dispatch ddi:collapsible-refresh on the menu.

Items keep their size in the row (flex-shrink: 0), and the menu takes whatever room its parent gives it. In a flex row where another item shrinks too, flex shrinks both together, so the menu starts folding before the other item has given up its room. To make the other item shrink first, give it a large flex-shrink and a min-inline-size (e.g. flex: 0 1000 auto), and the menu flex: 0 1 auto; min-inline-size: 0.

Items with display: none are not measured and never folded.

Props

Attributes

Events and methods

Slots

VerticalMenu

VerticalMenu is a button that opens its items in a popover. Use it for submenus and grouped actions.

Basic vertical menu
---
import { VerticalMenu } from "@dynamic-type/ddi/server";
---

<VerticalMenu name="Menu items">
    <a href="#item-1" class="button tertiary">Item 1</a>
    <a href="#item-2" class="button tertiary">Item 2</a>
    <a href="#item-3" class="button tertiary">Item 3</a>
</VerticalMenu>
Styled with buttonClass and itemsClass
---
import { VerticalMenu } from "@dynamic-type/ddi/server";
---

<VerticalMenu
    name="Account"
    buttonClass="bg:alt rounded"
    itemsClass="rounded drop-shadow"
>
    <a href="#dashboard" class="button tertiary">Dashboard</a>
    <a href="#settings" class="button tertiary">Settings</a>
    <a href="#profile" class="button tertiary">Profile</a>
    <a href="#logout" class="button tertiary">Log out</a>
</VerticalMenu>

Props

Slots

Combining the two

When a CollapsibleMenu moves a VerticalMenu into its overflow menu, the vertical menu is unwrapped: its items are placed directly in the overflow menu. When it comes back into the row, it is wrapped again. With unwrapPrefix, each item keeps the submenu’s name, so “Design” becomes “Services – Design”.

Resize the window until Services moves into the overflow menu
---
import { CollapsibleMenu, VerticalMenu } from "@dynamic-type/ddi/server";
---

<header class="x-flow items:center p bg rounded inline-size:100cqi">
    <div class="p bg:alt rounded">Logo</div>
    <CollapsibleMenu class="flex:1" overflowItemsClass="rounded drop-shadow">
        <a href="#item-1" class="button tertiary">Item 1</a>
        <a href="#item-2" class="button tertiary">Item 2</a>
        <VerticalMenu name="Services" unwrapPrefix>
            <a href="#design" class="button tertiary">Design</a>
            <a href="#development" class="button tertiary">Development</a>
            <a href="#consulting" class="button tertiary">Consulting</a>
        </VerticalMenu>
        <a href="#item-3" class="button tertiary">Item 3</a>
        <a href="#item-4" class="button tertiary">Item 4</a>
    </CollapsibleMenu>
</header>