Menu

Organize related actions into items, groups, and submenus.

Live preview
Interact with the component to see results here.
View code
HTML
<div style="display:inline-flex;align-self:center;margin:70px 0">
  <sy-button>Action menu<sy-menu
      trigger="click"
      position="bottomLeft"
      direction="right"
      id="sample"
    >
      <sy-menu-item value="edit">Edit</sy-menu-item>
      <sy-menu-item value="copy">Duplicate</sy-menu-item>
      <sy-menu-item value="archive" disabled>Archive</sy-menu-item>
    </sy-menu>
  </sy-button>
</div>

Usage guide

#

Provide commands or a context menu for a specific target. Use Navigation for app-wide navigation.

Command groups

Group related items with menu-group and give each item a value.

Nested commands

Use menu-sub for nested commands. Use checkable to display selection state.

Examples

#

Action list

Selecting an item closes the menu and emits the selection result.

View code
HTML
<div style="display:inline-flex;align-self:center;margin:70px 0">
  <sy-button>Action menu<sy-menu trigger="click" id="sample">
      <sy-menu-item value="edit">Edit</sy-menu-item>
      <sy-menu-item value="copy">Duplicate</sy-menu-item>
      <sy-menu-item value="archive" disabled>Archive</sy-menu-item>
    </sy-menu>
  </sy-button>
</div>

Menu groups

Use headings to separate different kinds of actions.

View code
HTML
<div style="display:inline-flex;align-self:center;margin:70px 0">
  <sy-button>Action menu<sy-menu trigger="click" id="sample">
      <sy-menu-group title="Project">
        <sy-menu-item value="edit">Edit</sy-menu-item>
        <sy-menu-item value="copy">Duplicate</sy-menu-item>
        <sy-menu-item value="archive" disabled>Archive</sy-menu-item>
      </sy-menu-group>
      <sy-menu-group title="Help">
        <sy-menu-item value="guide">Usage guide</sy-menu-item>
      </sy-menu-group>
    </sy-menu>
  </sy-button>
</div>

Submenu

Group multi-step actions in a nested list.

View code
HTML
<div style="display:inline-flex;align-self:center;margin:70px 0">
  <sy-button>Action menu<sy-menu trigger="click" id="sample">
      <sy-menu-item value="edit">Edit</sy-menu-item>
      <sy-menu-item value="copy">Duplicate</sy-menu-item>
      <sy-menu-item value="archive" disabled>Archive</sy-menu-item>
      <sy-menu-sub title="Export" trigger="click">
        <sy-menu-item value="csv">CSV</sy-menu-item>
        <sy-menu-item value="json">JSON</sy-menu-item>
      </sy-menu-sub>
    </sy-menu>
  </sy-button>
</div>

Checklist

Keep the checked state of multiple options.

View code
HTML
<div style="display:inline-flex;align-self:center;margin:70px 0">
  <sy-button>Action menu<sy-menu
      trigger="click"
      checkable
      id="sample"
    >
      <sy-menu-item value="edit">Edit</sy-menu-item>
      <sy-menu-item value="copy">Duplicate</sy-menu-item>
      <sy-menu-item value="archive" disabled>Archive</sy-menu-item>
    </sy-menu>
  </sy-button>
</div>

Context menu

Right-click the trigger to open the menu.

View code
HTML
<div style="display:inline-flex;align-self:center;margin:70px 0">
  <sy-button>Action menu<sy-menu trigger="contextmenu" id="sample">
      <sy-menu-item value="edit">Edit</sy-menu-item>
      <sy-menu-item value="copy">Duplicate</sy-menu-item>
      <sy-menu-item value="archive" disabled>Archive</sy-menu-item>
    </sy-menu>
  </sy-button>
</div>

Control menus from the app

Call methods to open, close, and clear selection.

View code
HTML
<div style="display:inline-flex;align-self:center;margin:70px 0">
  <sy-button>Action menu<sy-menu trigger="click" id="sample">
      <sy-menu-item value="edit">Edit</sy-menu-item>
      <sy-menu-item value="copy">Duplicate</sy-menu-item>
      <sy-menu-item value="archive" disabled>Archive</sy-menu-item>
    </sy-menu>
  </sy-button>
</div>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">Open</sy-button>
  <sy-button id="action-1" size="small">Close</sy-button>
  <sy-button id="action-2" size="small">Deselect</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? 'Done';
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.setOpen();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget1 = document.querySelector("#sample");
  document.querySelector('#action-1').addEventListener('click', async () => {
    const result = await actionTarget1.setClose();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget2 = document.querySelector("#sample");
  document.querySelector('#action-2').addEventListener('click', async () => {
    const result = await actionTarget2.clearSelectedItem();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
</script>

Events and payloads

Interact with the component and inspect the latest event name and detail. The code includes subscriptions to events emitted by this component and its children.

View code
HTML
<div style="display:inline-flex;align-self:center;margin:70px 0">
  <sy-button>Action menu<sy-menu
      trigger="click"
      position="bottomLeft"
      direction="right"
      id="sample"
    >
      <sy-menu-item value="edit">Edit</sy-menu-item>
      <sy-menu-item value="copy">Duplicate</sy-menu-item>
      <sy-menu-item value="archive" disabled>Archive</sy-menu-item>
    </sy-menu>
  </sy-button>
</div>
<output id="demo-events" aria-live="polite">Interact with the component to see
  its latest event and payload.</output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? 'Done';
  for (const name of ["opened", "itemSelected", "itemChecked"]) {
    document.addEventListener(name, event => {
      document.querySelector('#demo-events').textContent =
        `${event.target.localName} · ${name}: ${describeResult(event.detail)}`;
    });
  }
</script>

Toggle visibility and menu state

toggle switches the open state; close closes the menu. Delayed closing and trigger-width queries are also available. Enable item selection and inspect the result.

View code
HTML
<div style="display:inline-flex;align-self:center;margin:70px 0">
  <sy-button>Action menu<sy-menu
      trigger="click"
      position="bottomLeft"
      direction="right"
      id="sample"
    >
      <sy-menu-item value="edit">Edit</sy-menu-item>
      <sy-menu-item value="copy">Duplicate</sy-menu-item>
      <sy-menu-item value="archive" disabled>Archive</sy-menu-item>
    </sy-menu>
  </sy-button>
</div>
<div class="demo-actions"
  style="display:flex;flex-wrap:wrap;gap:8px;margin-top:16px"
>
  <sy-button id="action-0" size="small">Toggle open</sy-button>
  <sy-button id="action-1" size="small">Close</sy-button>
  <sy-button id="action-2" size="small">Close with delay</sy-button>
  <sy-button id="action-3" size="small">Trigger width</sy-button>
  <sy-button id="action-4" size="small">Enable item selection</sy-button>
</div>
<output id="demo-result" aria-live="polite"></output>

<script type="module">
  const describeResult = value => JSON.stringify(value, (_key, item) => {
    if (item instanceof File) return {
      name: item.name,
      size: item.size,
      type: item.type
    };
    if (item instanceof Element) return `<${item.localName}>`;
    return item;
  }) ?? 'Done';
  const component1 = document.querySelector("#sample");
  Object.assign(component1, {});
  const actionTarget0 = document.querySelector("#sample");
  document.querySelector('#action-0').addEventListener('click', async () => {
    const result = await actionTarget0.toggle();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget1 = document.querySelector("#sample");
  document.querySelector('#action-1').addEventListener('click', async () => {
    const result = await actionTarget1.close();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget2 = document.querySelector("#sample");
  document.querySelector('#action-2').addEventListener('click', async () => {
    const result = await actionTarget2.delayedMenuClose();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget3 = document.querySelector("#sample");
  document.querySelector('#action-3').addEventListener('click', async () => {
    const result = await actionTarget3.getTriggerWidth();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
  const actionTarget4 = document.querySelector("#sample");
  document.querySelector('#action-4').addEventListener('click', async () => {
    const result = await actionTarget4.setSelectableAllItems();
    document.querySelector('#demo-result').textContent = describeResult(result);
  });
</script>

API reference

#

Hover over a name or focus it with the keyboard to see the code. On touch screens, tap the name.

<sy-menu>

Properties

Attribute / PropertyTypeDefaultDescription
booleanfalseShow the action menu.
booleanfalseSelect multiple items using checkboxes.
'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight''bottomLeft'Corner used to align the menu with its trigger.
'click' | 'hover' | 'contextmenu''hover'User action that opens the list or popup.
'left' | 'right''right'Direction in which the submenu expands.
booleanfalseBlock user interaction and keyboard focus.

Events

EventDetailDescription
CustomEvent<boolean>Emitted when the list opens.
CustomEvent<any>Read the result from the event's detail.
CustomEvent<any>Read the result from the event's detail.

Methods

MethodReturnsDescription
Promise<void>Expand the submenu.
Promise<void>Close the item.
Promise<void>Toggle the current expansion state.
Promise<void>Close the open panel.
Promise<number | null>Returns the trigger element's measured width, or `null` when the menu has no DOM trigger (e.g. inside a dropdown wrapper) or the trigger is not currently rendered. Used by `<sy-menu-item loading>` as the fallback width when there are no non-loading siblings to inherit sizing from.
Promise<void>Close the menu after a delay.
Promise<void>Make every menu item selectable.
Promise<void>Clear the menu item's selection indicator.

Slots

SlotDescription
Component body or child items.

<sy-menu-item>

Properties

Attribute / PropertyTypeDefaultDescription
booleanfalseBlock user interaction and keyboard focus.
string''Current value or item identifier.
booleanfalseSelected or checked state.
booleanfalseAllow an item selection indicator.
booleanfalseShow a checkbox before the item.
booleanfalseIndicate that processing is in progress.

Events

EventDetailDescription
CustomEvent<{ value: string; label: string }>Read the result from the event's detail.
CustomEvent<{ value: string; label: string; checked: boolean }>Read the result from the event's detail.

Slots

SlotDescription
Component body or child items.

<sy-menu-sub>

Properties

Attribute / PropertyTypeDefaultDescription
booleanfalseBlock user interaction and keyboard focus.
booleanfalseShow the submenu.
string''Title that opens the submenu.
'click' | 'hover''hover'User action that opens the list or popup.

Slots

SlotDescription
Component body or child items.

<sy-menu-group>

Properties

Attribute / PropertyTypeDefaultDescription
string''Title of an action group.

Slots

SlotDescription
Component body or child items.