Skip to content

OverflowMenu

<caos-overflow-menu>

overflow of secondary actions behind a single trigger

May be placed on a record page, an app’s home page, a page inside an app and an action on a record.

Name Attribute Description Type Default Required
items property only The actions, as { value, label, disabled?, danger?, separator? }. A separator entry draws a rule and is skipped by the keyboard; a danger entry is drawn in the danger ink; a disabled entry is drawn faded, refused, and stepped over by the arrow keys. json none declared Optional
label label, markup only What this menu is called. Becomes the accessible name of both the trigger and the menu it opens — “Row actions”, “More order actions”. Read once, on connect. text More actions Optional
align align, markup only Set to “start” to open the panel to the RIGHT of the trigger, for a trigger sitting at the left of its row. Anything else, including leaving it off, right-aligns the panel to the trigger — the correct behaviour for the row-end position this piece is normally in. Read live on every reposition, so it may be changed after placement. text — recognises start; anything else reads as end none declared Optional
Name When it fires What it carries
caos-menu-select An enabled item is activated, by pointer or by Enter or Space. The menu closes itself and returns focus to the trigger; DOING the thing is the host’s job. { value } — the value of the item that was chosen.

Nothing goes inside this piece.

Design token What it controls
--caos-layer-dropdown Which layer the floating menu asks for. A NAME rather than a stacking number, so the platform can resolve it against where the menu actually sits — the same menu belongs below a dialog on a page and above it inside one. Falls back to the value the sibling panels carry until the token ships.
--caos-shadow-float The elevation of the floating menu — it is the one floating surface.
--caos-color-surface The fill of the menu panel.
--caos-color-border The outline of the panel, and the rule a separator draws.
--caos-radius The corner radius of the panel and of the trigger.
--caos-color-surface-2 The highlight behind a hovered or focused item, and the open trigger.
--caos-color-danger The ink of an item marked danger.
--caos-color-danger-bg The highlight behind a hovered danger item.
--caos-color-danger-text The ink of a danger item while it is hovered.
--caos-focus-ring The ring drawn on the keyboard-focused trigger.

Nothing else drives this piece by calling it.

Name Type What it tells you
open boolean Whether the menu panel is showing.
Part Which piece of it When it is there
::part(trigger) the control that opens the menu Always

A set of actions that are all secondary, or the tail of a set whose first one or two earned a place in the open. Reach for it wherever repeating three buttons on every row of a table would drown the rows.

  • Button — The action is the one people came to do. Anything primary put behind this trigger is an action nobody finds. Note that this piece never replaces Button — it holds actions Button would draw.
  • ActionBar — The actions deserve to be visible and laid out in the open. That one arranges a row; this one collapses a set behind one control. They pair — this goes in that bar’s overflow slot — so the question is which actions go where, not which component to use.
  • PicklistField — The list is a VALUE being chosen rather than an action being taken. Choosing from this menu does something; it does not set anything.
  • ObjectCaret — The dropdown belongs to a tab in the workspace frame and lists what can be done with an object. That is a frame part and is not placed on a page.

The trigger is a real button carrying aria-haspopup="menu" and a live aria-expanded, and the panel is a role="menu" of role="menuitem"s, so the whole pattern is announced correctly without the author doing anything. The full keyboard model is handled: Enter, Space and Arrow Down open on the first item, Arrow Up opens on the last, Arrow Up/Down and Home/End roam within the menu, Enter and Space activate, Escape closes and returns focus to the trigger, Tab and Shift+Tab stay inside the open menu and cycle its items, and a pointer press outside closes without moving focus. Disabled items carry aria-disabled and are skipped rather than focused and refused. Because the panel lives in its own shadow tree at the end of the document, it cannot be pointed at from the trigger, so it carries the trigger’s own name instead. Left to the author: give the menu a label. Every unlabelled one on a page is announced as “More actions”, which on a table of forty rows names nothing.

The ordinary case: three actions behind one trigger at the end of a row, the last of them destructive and drawn in the danger ink.

{
"id": "example",
"section": "Actions on a row",
"columns": 1,
"items": [
{
"id": "overflow_menu_1",
"type": "component",
"key": "overflow_menu",
"inputs": {
"label": "Row actions",
"items": [
{
"value": "edit",
"label": "Edit"
},
{
"value": "clone",
"label": "Clone"
},
{
"value": "delete",
"label": "Delete",
"danger": true
}
]
}
}
]
}
<caos-overflow-menu id="overflowMenu1" label="Row actions"></caos-overflow-menu>
// What an attribute cannot hold, set as a property — the same values the preview is drawn with.
const overflowMenu1 = document.getElementById('overflowMenu1');
overflowMenu1.items = [
{
"value": "edit",
"label": "Edit"
},
{
"value": "clone",
"label": "Clone"
},
{
"value": "delete",
"label": "Delete",
"danger": true
}
];

A longer set divided by a separator, with an action that is unavailable — faded, refused to the pointer, and stepped over by the arrow keys rather than focused and then rejected.

{
"id": "example",
"section": "Grouped, with one refused",
"columns": 1,
"items": [
{
"id": "overflow_menu_1",
"type": "component",
"key": "overflow_menu",
"inputs": {
"label": "Order actions",
"items": [
{
"value": "export",
"label": "Export as PDF"
},
{
"value": "share",
"label": "Share with the account"
},
{
"value": "revise",
"label": "Start a revision"
},
{
"value": "approve",
"label": "Approve",
"disabled": true
},
{
"value": "sep",
"label": "",
"separator": true
},
{
"value": "delete",
"label": "Delete order",
"danger": true
}
]
}
}
]
}
<caos-overflow-menu id="overflowMenu1" label="Order actions"></caos-overflow-menu>
// What an attribute cannot hold, set as a property — the same values the preview is drawn with.
const overflowMenu1 = document.getElementById('overflowMenu1');
overflowMenu1.items = [
{
"value": "export",
"label": "Export as PDF"
},
{
"value": "share",
"label": "Share with the account"
},
{
"value": "revise",
"label": "Start a revision"
},
{
"value": "approve",
"label": "Approve",
"disabled": true
},
{
"value": "sep",
"label": "",
"separator": true
},
{
"value": "delete",
"label": "Delete order",
"danger": true
}
];

The same menu on a trigger that is NOT at the end of its row — the panel is told to open to the right of the trigger instead of back across it.

{
"id": "example",
"section": "Opening the other way",
"columns": 1,
"items": [
{
"id": "overflow_menu_1",
"type": "component",
"key": "overflow_menu",
"inputs": {
"label": "Section actions",
"align": "start",
"items": [
{
"value": "rename",
"label": "Rename section"
},
{
"value": "move",
"label": "Move up"
},
{
"value": "remove",
"label": "Remove section",
"danger": true
}
]
}
}
]
}
<caos-overflow-menu id="overflowMenu1" label="Section actions" align="start"></caos-overflow-menu>
// What an attribute cannot hold, set as a property — the same values the preview is drawn with.
const overflowMenu1 = document.getElementById('overflowMenu1');
overflowMenu1.items = [
{
"value": "rename",
"label": "Rename section"
},
{
"value": "move",
"label": "Move up"
},
{
"value": "remove",
"label": "Remove section",
"danger": true
}
];