0.0.3

Menu

A pure vanilla JS implementation of Zag JS Menu Opens in a new window

An accessible dropdown and context menu that is used to display a list of actions or options that a user can choose.


Anatomy

The Menu component consists of the following data-part

trigger indicator positioner content separator item item-group item-group-label

It can include multiple separator, item, item-group and item-group-label parts.

data-value is required on all item parts

<div class="menu menu-js">
                  <button data-part="trigger" class="button ">
                  Menu
                  <span data-part="indicator">
                  <svg class="button__icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                  <path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"></path>
                  </svg>
                  </span>
                  </button>
                  <div data-part="positioner">
                  <ul data-part="content">
                  <li data-part="item" data-value="account">
                  Account
                  </li>
                  <li data-part="item" data-value="profile">
                  Profile
                  </li>
                  <div data-part="separator"></div>
                  <li data-part="item" data-value="logout">
                  Logout
                  </li>
                  </ul>
                  </div>
                  </div>
                  <div class="menu menu-js">
                  <div data-part="context-trigger">
                  <div>Context menu</div>
                  </div>
                  <div data-part="positioner">
                  <ul data-part="content">
                  <li data-part="item" data-value="account">
                  Account
                  </li>
                  <li data-part="item" data-value="profile">
                  Profile
                  </li>
                  <div data-part="separator"></div>
                  <li data-part="item" data-value="logout">
                  Logout
                  </li>
                  </ul>
                  </div>
                  </div>

Group Items

The Menu items can be grouped with a label to identify each group

Please note you must provide and id to item-group and item-group-label and not a value

<div class="menu menu-js" data-highlighted-value="profile">
                  <button data-part="trigger" class="button ">
                  Menu
                  <span data-part="indicator">
                  <svg class="button__icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                  <path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"></path>
                  </svg>
                  </span>
                  </button>
                  <div data-part="positioner">
                  <div data-part="content">
                  <div data-part="item-group-label" data-id="account">Account</div>
                  <div data-part="item-group" data-id="account">
                  <li data-part="item" data-value="account-profile">
                  Profile
                  </li>
                  <li data-part="item" data-value="account-settings">
                  Settings
                  </li>
                  </div>
                  <div data-part="separator"></div>
                  <div data-part="item-group-label" data-id="account">Feed</div>
                  <div data-part="item-group" data-id="feed">
                  <li data-part="item" data-value="feed-new">
                  New
                  </li>
                  <li data-part="item" data-value="account-favorite">
                  Favorite
                  </li>
                  </div>
                  </div>
                  </div>
                  </div>
                  <div class="menu menu-js" data-highlighted-value="profile">
                  <div data-part="context-trigger">
                  <div>Context menu</div>
                  </div>
                  <div data-part="positioner">
                  <div data-part="content">
                  <div data-part="item-group-label" data-id="account">Account</div>
                  <div data-part="item-group" data-id="account">
                  <li data-part="item" data-value="account-profile">
                  Profile
                  </li>
                  <li data-part="item" data-value="account-settings">
                  Settings
                  </li>
                  </div>
                  <div data-part="separator"></div>
                  <div data-part="item-group-label" data-id="account">Feed</div>
                  <div data-part="item-group" data-id="feed">
                  <li data-part="item" data-value="feed-new">
                  New
                  </li>
                  <li data-part="item" data-value="account-favorite">
                  Favorite
                  </li>
                  </div>
                  </div>
                  </div>
                  </div>

Data attributes

Each menu can be set with different settings with the following data-attribute.

<div class="menu menu-js" data-aria-label="Demo menu" data-close-on-select="false" data-composite="true" data-default-highlighted-value="profile" data-highlighted-value="profile" data-default-open="true" data-dir="ltr" data-loop-focus="false" data-typehead="true" data-placement="top-end" data-strategy="absolute" data-flip="true" data-hide-when-detached="true" data-gutter="1" data-arrow-padding="1" data-overflow-padding="1" data-slide="true" data-fit-viewport="true" data-overlap="true" data-same-width="true">
                  <button data-part="trigger" class="button ">
                  Menu
                  <span data-part="indicator">
                  <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                  <path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"></path>
                  </svg>
                  </span>
                  </button>
                  <div data-part="positioner">
                  <ul data-part="content">
                  <li data-part="item" data-value="account">
                  Account
                  </li>
                  <li data-part="item" data-value="profile">
                  Profile
                  </li>
                  <div data-part="separator"></div>
                  <li data-part="item" data-value="logout">
                  Logout
                  </li>
                  </ul>
                  </div>
                  </div>
                  <div class="menu menu-js" data-aria-label="Demo menu" data-close-on-select="false" data-composite="true" data-default-highlighted-value="profile" data-highlighted-value="profile" data-default-open="true" data-dir="ltr" data-loop-focus="false" data-typehead="true" data-placement="top-end" data-strategy="absolute" data-flip="true" data-hide-when-detached="true" data-gutter="1" data-arrow-padding="1" data-overflow-padding="1" data-slide="true" data-fit-viewport="true" data-overlap="true" data-same-width="true">
                  <div data-part="context-trigger">
                  <div>Context menu</div>
                  </div>
                  <div data-part="positioner">
                  <ul data-part="content">
                  <li data-part="item" data-value="account">
                  Account
                  </li>
                  <li data-part="item" data-value="profile">
                  Profile
                  </li>
                  <div data-part="separator"></div>
                  <li data-part="item" data-value="logout">
                  Logout
                  </li>
                  </ul>
                  </div>
                  </div>

Callbacks

Each Menu component can receive callbacks that can be used to respond to user interaction with custom behavior.

You must add a custom id for the menu and a event listener for your event name


                      document.getElementById("my-callback-menu")
                      ?.addEventListener("my-callback-menu-event", (event) => {
                        console.log("Received event:", (event as CustomEvent).detail);
                      });
                  
<div id="my-callback-menu" class="menu menu-js" data-on-open-change="my-callback-menu-event">
                  <button data-part="trigger" class="button ">
                  Menu
                  <span data-part="indicator">
                  <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                  <path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"></path>
                  </svg>
                  </span>
                  </button>
                  <div data-part="positioner">
                  <ul data-part="content">
                  <li data-part="item" data-value="account">
                  Account
                  </li>
                  <li data-part="item" data-value="profile">
                  Profile
                  </li>
                  <div data-part="separator"></div>
                  <li data-part="item" data-value="logout">
                  Logout
                  </li>
                  </ul>
                  </div>
                  </div>

Open your browser's console to see the events received when the menu is open or closed


RTL

RTL support for menu

<div dir="rtl">
                  <div class="menu menu-js" data-dir="rtl">
                  <button data-part="trigger" class="button ">
                  قائمة طعام
                  <span data-part="indicator">
                  <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                  <path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"></path>
                  </svg>
                  </span>
                  </button>
                  <div data-part="positioner">
                  <ul data-part="content">
                  <li data-part="item" data-value="account">
                  الحساب
                  </li>
                  <li data-part="item" data-value="profile">
                  الملف الشخصي
                  </li>
                  <div data-part="separator"></div>
                  <li data-part="item" data-value="logout">
                  تسجيل الخروج
                  </li>
                  </ul>
                  </div>
                  </div>
                  </div>
                  <div dir="rtl">
                  <div class="menu menu-js" data-dir="rtl">
                  <div data-part="context-trigger">
                  <div>قائمة السياق</div>
                  </div>
                  <div data-part="positioner">
                  <ul data-part="content">
                  <li data-part="item" data-value="account">
                  الحساب
                  </li>
                  <li data-part="item" data-value="profile">
                  الملف الشخصي
                  </li>
                  <div data-part="separator"></div>
                  <li data-part="item" data-value="logout">
                  تسجيل الخروج
                  </li>
                  </ul>
                  </div>
                  </div>
                  </div>

Custom (Tailwind)

If you are using Tailwind styling, you can customize each menu and its parts with Tailwind utilities

<div class="menu menu-js">
                  <button data-part="trigger" class="button button--brand">
                  Menu
                  <span data-part="indicator" class="text-red-700">
                  <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
                  <path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"></path>
                  </svg>
                  </span>
                  </button>
                  <div data-part="positioner">
                  <ul data-part="content" class="bg-blue-100">
                  <li data-part="item" data-value="account" class="border-yellow-100">
                  Account
                  </li>
                  <li data-part="item" data-value="profile" class="border-yellow-100 p-3">
                  Profile
                  </li>
                  <div data-part="separator"></div>
                  <li data-part="item" data-value="logout" class="border-red-100 my-2">
                  Logout
                  </li>
                  </ul>
                  </div>
                  </div>

Templates

Menu components is available in the followng templates

Listbox Scrollbar