j3w1 / theme

j3w1 / theme4.0.0

j3w1 / command

  • SPEC Overview
  • SPEC Foundations
  • SPEC Components
  • SPEC Patterns
  • SPEC Tokens
  • SPEC Tools
  • SPEC Workbench
  • SPEC Form builder
  • SPEC Task kit
  • SPEC Recipes
  • SPEC Figma bridge
  • SPEC Report an issue
  • SPEC Implement
  • SPEC For agents
  • SPEC Ports
  • SPEC Releases
  • SPEC Specification
  • DEMO Vue application demo

Menu

A menu button that opens a list of actions on the raised surface inside a 1px overlay border; the active item is the selection fill and check items show a glyph.

stable · light DOM · 4.0.0. Light DOM retains static content, native semantics, label relationships and host composition without duplicating controls.

Live variants

default

Markup
<j3w1-menu id="example-menu-default"><div class="menu">
  <button class="menu-button" type="button" id="example-menu-default-menu-file-button" aria-haspopup="menu" aria-expanded="true" aria-controls="example-menu-default-menu-file">File<span class="menu-chevron" aria-hidden="true"><svg viewBox="0 0 16 16" width="12" height="12" focusable="false"><path d="M4 6l4 4 4-4"></path></svg></span></button>
  <ul class="menu-list" role="menu" id="example-menu-default-menu-file" aria-labelledby="example-menu-default-menu-file-button">
    <li role="none"><button class="menu-item" role="menuitem" type="button" tabindex="0"><span class="menu-check" aria-hidden="true">✓</span><span class="menu-label">New window</span><span class="menu-shortcut">Mod+Enter</span></button></li>
    <li role="none"><button class="menu-item menu-item-demo-target" role="menuitem" type="button" tabindex="-1"><span class="menu-check" aria-hidden="true">✓</span><span class="menu-label">Open…</span><span class="menu-shortcut">Mod+O</span></button></li>
    <li role="none"><button class="menu-item" role="menuitem" type="button" tabindex="-1" aria-disabled="true"><span class="menu-check" aria-hidden="true">✓</span><span class="menu-label">Save</span><span class="menu-shortcut">Mod+S</span></button></li>
    <li class="menu-separator" role="separator"></li>
    <li role="none"><button class="menu-item" role="menuitem" type="button" tabindex="-1"><span class="menu-check" aria-hidden="true">✓</span><span class="menu-label">Quit</span><span class="menu-shortcut">Mod+Shift+Q</span></button></li>
  </ul>
</div></j3w1-menu>

with-submenu

Markup
<j3w1-menu id="example-menu-with-submenu"><div class="menu">
  <button class="menu-button" type="button" id="example-menu-with-submenu-menu-view-button" aria-haspopup="menu" aria-expanded="true" aria-controls="example-menu-with-submenu-menu-view">View<span class="menu-chevron" aria-hidden="true"><svg viewBox="0 0 16 16" width="12" height="12" focusable="false"><path d="M4 6l4 4 4-4"></path></svg></span></button>
  <ul class="menu-list" role="menu" id="example-menu-with-submenu-menu-view" aria-labelledby="example-menu-with-submenu-menu-view-button">
    <li role="none"><button class="menu-item" role="menuitem" type="button" tabindex="0"><span class="menu-check" aria-hidden="true">✓</span><span class="menu-label">Reload</span><span class="menu-shortcut">Mod+R</span></button></li>
    <li role="none"><button class="menu-item menu-item-demo-target" role="menuitem" type="button" tabindex="-1" aria-haspopup="menu" aria-expanded="false" aria-controls="example-menu-with-submenu-submenu-0" id="example-menu-with-submenu-submenu-0-trigger"><span class="menu-check" aria-hidden="true">✓</span><span class="menu-label">Density</span><span class="menu-submenu" aria-hidden="true"><svg viewBox="0 0 16 16" width="12" height="12" focusable="false"><path d="M6 4l4 4-4 4"></path></svg></span></button><ul class="menu-list" role="menu" id="example-menu-with-submenu-submenu-0" aria-labelledby="example-menu-with-submenu-submenu-0-trigger" hidden=""><li role="none"><button class="menu-item" type="button" role="menuitemradio" aria-checked="true" tabindex="-1" data-action="submenu-0:first">First option</button></li><li role="none"><button class="menu-item" type="button" role="menuitemradio" aria-checked="false" tabindex="-1" data-action="submenu-0:second">Second option</button></li></ul></li>
    <li role="none"><button class="menu-item" role="menuitem" type="button" tabindex="-1" aria-haspopup="menu" aria-expanded="false" aria-controls="example-menu-with-submenu-submenu-1" id="example-menu-with-submenu-submenu-1-trigger"><span class="menu-check" aria-hidden="true">✓</span><span class="menu-label">Profile</span><span class="menu-submenu" aria-hidden="true"><svg viewBox="0 0 16 16" width="12" height="12" focusable="false"><path d="M6 4l4 4-4 4"></path></svg></span></button><ul class="menu-list" role="menu" id="example-menu-with-submenu-submenu-1" aria-labelledby="example-menu-with-submenu-submenu-1-trigger" hidden=""><li role="none"><button class="menu-item" type="button" role="menuitemradio" aria-checked="true" tabindex="-1" data-action="submenu-1:first">First option</button></li><li role="none"><button class="menu-item" type="button" role="menuitemradio" aria-checked="false" tabindex="-1" data-action="submenu-1:second">Second option</button></li></ul></li>
    <li class="menu-separator" role="separator"></li>
    <li role="none"><button class="menu-item" role="menuitem" type="button" tabindex="-1"><span class="menu-check" aria-hidden="true">✓</span><span class="menu-label">Full screen</span><span class="menu-shortcut">F11</span></button></li>
  </ul>
</div></j3w1-menu>

with-checks

Markup
<j3w1-menu id="example-menu-with-checks"><div class="menu">
  <button class="menu-button" type="button" id="example-menu-with-checks-menu-options-button" aria-haspopup="menu" aria-expanded="true" aria-controls="example-menu-with-checks-menu-options">Options<span class="menu-chevron" aria-hidden="true"><svg viewBox="0 0 16 16" width="12" height="12" focusable="false"><path d="M4 6l4 4 4-4"></path></svg></span></button>
  <ul class="menu-list" role="menu" id="example-menu-with-checks-menu-options" aria-labelledby="example-menu-with-checks-menu-options-button">
    <li role="none"><button class="menu-item" role="menuitemcheckbox" type="button" tabindex="0" aria-checked="true"><span class="menu-check" aria-hidden="true">✓</span><span class="menu-label">Line numbers</span></button></li>
    <li role="none"><button class="menu-item menu-item-demo-target" role="menuitemcheckbox" type="button" tabindex="-1" aria-checked="false"><span class="menu-check" aria-hidden="true">✓</span><span class="menu-label">Wrap lines</span></button></li>
    <li role="none"><button class="menu-item" role="menuitemcheckbox" type="button" tabindex="-1" aria-checked="true"><span class="menu-check" aria-hidden="true">✓</span><span class="menu-label">Render whitespace</span></button></li>
    <li class="menu-separator" role="separator"></li>
    <li role="none"><button class="menu-item" role="menuitemcheckbox" type="button" tabindex="-1" aria-checked="false" aria-disabled="true"><span class="menu-check" aria-hidden="true">✓</span><span class="menu-label">Minimap</span></button></li>
  </ul>
</div></j3w1-menu>

Install and register

Use the exact version. If npm publication is pending, install the release tarball from the installation guide.

npm install --save-exact @j3w1/ui@4.0.0
import '@j3w1/ui/tokens.css';
import '@j3w1/ui/styles/menu.css';
import '@j3w1/ui/register/menu';

Insert the maintained markup above after loading these imports. Its native children provide accessible names and form behavior; the empty custom tag alone does not create a control.

Vue SFC, React, Astro and plain HTML integration →

Copy the complete implementation

npx --package=@j3w1/ui@4.0.0 j3w1-ui copy menu --out ./vendor/j3w1/menu

The CLI verifies every file before writing and refuses overwrites. Serve the output over HTTP and open index.html. Keep its modules, CSS, tokens and licence notices together.

Public API

attributes

NameType / signatureMeaning
disabledCustomEventDisable the native controls owned by this instance.
loadingCustomEventMark the primary control busy and prevent duplicate activation.

properties

NameType / signatureMeaning
disabledbooleanDelegates disabled state to native children.
namestringNative control name; application form keys remain explicit.
openbooleanMenu visibility.

methods

NameType / signatureMeaning
refresh(): voidReconnect behavior after replacing native child structure.
focus(options?: FocusOptions): voidFocus the first usable native control.
show(): voidOpen and focus the first menu item.
hide(focus?: boolean): voidClose all menus, optionally restoring the trigger.

events

NameType / signatureMeaning
j3w1-actionCustomEventAn enabled action or check item was invoked.

Dependencies and verification

Registered components: none. Style dependencies: none.

Package presence establishes an available implementation. Actual passes depend on the tested artifact and environment. Read specification execution evidence; read packed-consumer evidence; inspect the complete state matrix.

Full implementation manifest, API and token dependency closure

Canonical specification

Menu menu

  • Family: Navigation
  • Maturity: stable
  • Priority: R1
  • Since: 0.1.0
  • Custom widget: APG menu button (button with aria-haspopup="menu") opening a menu of menuitem, menuitemcheckbox and separator — APG pattern
  • specified
  • demonstrated
  • test implemented

A menu button that opens a list of actions on the raised surface inside a 1px overlay border; the active item is the selection fill and check items show a glyph.

Report an issue with this component

Tokens

Focus a role to inspect its values; follow its link for persistent usage and source details.

PartRoleValueUse
button.textcolor.text.defaultapproveduse#e99499D-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills.
button.bgcolor.surface.defaultapproveduse#0d0707D-041 surface.default: color.primitive.modern.panel.
button.bordercolor.border.controlapproveduse#c81a1aD-041 owner correction: red control and floating-frame borders, distinct from rose text.
button.bg-hovercolor.interaction.hover.bgapproveduse#1c1010D-041 interaction.hover.bg: color.primitive.modern.hover.
button.bg-opencolor.interaction.pressed.bgobserveduse#420f0cPressed fill.
button.border-opencolor.border.activeobserveduse#e53935Active or focused control border.
button.ringcolor.interaction.focus.ringobserveduse#e539351px dashed focus ring on controls and rows.
surface.bgcolor.surface.raisedapproveduse#140b0bD-041 surface.raised: color.primitive.modern.raised.
surface.bordercolor.border.overlayapproveduse#c81a1aD-041 owner correction: red control and floating-frame borders, distinct from rose text.
item.textcolor.text.defaultapproveduse#e99499D-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills.
item.bg-hovercolor.interaction.hover.bg-strongapproveduse#1c1010D-041 interaction.hover.bg-strong: color.primitive.modern.hover.
item.text-hovercolor.text.link-hoverobserveduse#f4eeeeLink text on hover, over interaction.hover.bg-strong.
item.text-disabledcolor.text.disabledapproveduse#a3676bD-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills.
active.bgcolor.interaction.selection.bgapproveduse#531310Selected row, tab, workspace; D-027 recessed the fill, leaving the 2px indicator and the check to carry selection.
active.textcolor.interaction.selection.textobserveduse#f4eeeeText on the selection.
active.ringcolor.interaction.focus.ringobserveduse#e539351px dashed focus ring on controls and rows.
check.glyphcolor.text.brightapproveduse#ffa2a7D-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills.
shortcut.textcolor.text.mutedapproveduse#d4868bD-041 owner correction: rose is the default UI and reading foreground; near-white is reserved for document headings, highlights and contrast-critical fills.
separator.colorcolor.border.dividerapproveduse#482b2bD-041 border.divider: color.primitive.modern.divider.
chevron.colorcolor.icon.decorativeapproveduse#986f72D-041 icon.decorative: color.primitive.modern.control.

Purpose

A short list of commands behind one button. Choosing a value is select or combobox; moving between views is tabs or the sidebar. Running an action closes the menu; a check item toggles and leaves it open.

Anatomy

Parts as listed in the anatomy. The active item, reached by pointer or arrow key, takes the selection fill.

States

StateVisualNon-colour channel
defaultbutton at rest; list on color.surface.raised in the color.border.overlay frame—
hoveritem → color.interaction.hover.bg-strong, text color.text.link-hovercursor: pointer
focus-visible1px dashed color.interaction.focus.ring at −2px on the button and the active itemthe ring
openbutton color.interaction.pressed.bg with 1px color.border.active; list shownaria-expanded="true"; list present
closedbutton at rest; list hiddenaria-expanded="false"; list absent from the tree
disableditem text color.text.disabled; no hover; still reachable by arrow keys when aria-disabledaria-disabled="true"; cursor: not-allowed
checked✓ in color.text.bright in the gutteraria-checked="true"; the glyph
selectedactive item filled color.interaction.selection.bg with color.interaction.selection.textfocus on the item; aria-activedescendant where used

Precedence: disabled > selected > hover; focus-visible is always drawn.

Keyboard

As in the key table; focus never rests on a separator.

Accessibility

The list is aria-labelledby the button. Disabled items use aria-disabled so they stay reachable and announced. Shortcut hints are text, not aria-keyshortcuts alone. Text meets 4.5:1; frame and ring 3:1. Items are at least 24 CSS pixels tall in compact.

Portability

A framed box of rows: the active row a fill, the frame a border, the ring an outline. Native menus keep their keyboard model and take the surface, frame and fill colours. A host that cannot draw the frame in the overlay colour uses the strong border colour and records the deviation.

Non-examples

A shadow under the menu. Rounded menu or items. An active item marked by text colour alone. A hover fill unlike every other menu in the product. Icons in a plain menuitem’s check gutter. A menu that stays open after an action. Links styled as menu items without role="menuitem". Disabling by opacity.

Contrast report

17 declared pairs measured on resolved sRGB values with the WCAG 2.x formula; 0 waived (decorative only), 0 failing. A pass is a design target for the named role, not an application-wide conformance claim.

PairStateKindForegroundBackgroundRatioMinResult
menu default textdefaulttextcolor.text.default #e99499color.surface.raised #140b0b8.464.5pass
menu default borderdefaultuicolor.border.overlay #c81a1acolor.surface.raised #140b0b3.343pass
menu hover texthovertextcolor.text.link-hover #f4eeeecolor.interaction.hover.bg-strong #1c101016.194.5pass
menu focus-visible textfocus-visibletextcolor.text.default #e99499color.surface.default #0d07078.724.5pass
menu focus-visible borderfocus-visibleuicolor.border.control #c81a1acolor.surface.default #0d07073.443pass
menu focus-visible outlinefocus-visibleuicolor.interaction.focus.ring #e53935color.surface.default #0d07074.733pass
menu open textopentextcolor.text.default #e99499color.interaction.pressed.bg #420f0c7.024.5pass
menu open borderopenuicolor.border.active #e53935color.interaction.pressed.bg #420f0c3.813pass
menu closed textclosedtextcolor.text.default #e99499color.surface.default #0d07078.724.5pass
menu closed bordercloseduicolor.border.control #c81a1acolor.surface.default #0d07073.443pass
menu checked textcheckedtextcolor.text.bright #ffa2a7color.surface.raised #140b0b10.144.5pass
menu selected textselectedtextcolor.interaction.selection.text #f4eeeecolor.interaction.selection.bg #53131012.474.5pass
menu selected outlineselecteduicolor.interaction.focus.ring #e53935color.interaction.selection.bg #5313103.383pass
shortcut hint on the raised surface—textcolor.text.muted #d4868bcolor.surface.raised #140b0b7.014.5pass
disabled item text (exempt; house floor 3:1)disableduicolor.text.disabled #a3676bcolor.surface.raised #140b0b4.353pass
item separator (decorative; role separator carries the grouping)—uicolor.border.divider #482b2bcolor.surface.raised #140b0b1.531pass
submenu chevron (decorative; aria-haspopup carries the meaning)—uicolor.icon.decorative #986f72color.surface.raised #140b0b4.481pass

Keys

KeyAction
Enter / Space / DownOn the button, opens the menu and focuses the first item; Up opens it and focuses the last.
Down / UpMoves to the next or previous item and wraps.
Home / EndMoves to the first or last item.
Right / LeftOpens a submenu and focuses its first item; Left closes the submenu and returns to the parent item.
Enter / SpaceActivates the focused item; a check item toggles and the menu stays open.
EscapeCloses the menu and returns focus to the button.
TabCloses the menu and moves focus to the next tab stop after the button.
A-ZMoves focus to the next item starting with the typed character.

For agents

Component JSON ·Implementation brief ·Build a task kit ·

Related: button toolbar command-palette popover select

Stress fixtures: FX-LONG FX-320 FX-ZOOM-200 FX-I18N FX-RTL FX-RM FX-HC FX-STATE-MATRIX