Components / Layout & Navigation / Menubar

Menubar beta

The desktop menu bar: File, Edit, View — menus that open on click and slide sideways while open, with the shortcuts showing.

Preview
notes.txt — Editor

Open File, then slide across to Edit — the bar follows, like a desktop.

<div style="border:1px solid var(--k-line);border-radius:14px;overflow:hidden;background:var(--k-bg)">
  <div style="display:flex;align-items:center;gap:7px;padding:10px 13px;border-bottom:1px solid var(--k-line)">
    <span style="width:11px;height:11px;border-radius:50%;background:var(--k-zinc-300)"></span>
    <span style="width:11px;height:11px;border-radius:50%;background:var(--k-zinc-300)"></span>
    <span style="width:11px;height:11px;border-radius:50%;background:var(--k-zinc-300)"></span>
    <span style="font-size:12.5px;color:var(--k-ink-3);margin-left:8px">notes.txt — Editor</span>
  </div>
  <div style="padding:6px 8px;min-height:190px">
    <div class="k-mbar" data-mbar role="menubar" aria-label="Preview">
      <span class="k-mbar-item"><button class="k-mbar-btn" type="button" aria-expanded="false" aria-haspopup="true">File</button><div class="k-mbar-menu" role="menu"><button class="k-mbar-mi" type="button" role="menuitem"><span class="ck"></span>New window<span class="sc">Ctrl N</span></button><button class="k-mbar-mi" type="button" role="menuitem"><span class="ck"></span>Open…<span class="sc">Ctrl O</span></button><div class="k-mbar-sep"></div><button class="k-mbar-mi" type="button" role="menuitem"><span class="ck"></span>Close window<span class="sc"></span></button><button class="k-mbar-mi" type="button" role="menuitem"><span class="ck"></span>Quit<span class="sc">Ctrl Q</span></button></div></span>
      <span class="k-mbar-item"><button class="k-mbar-btn" type="button" aria-expanded="false" aria-haspopup="true">Edit</button><div class="k-mbar-menu" role="menu"><button class="k-mbar-mi" type="button" role="menuitem"><span class="ck"></span>Undo<span class="sc">Ctrl Z</span></button><button class="k-mbar-mi" type="button" role="menuitem"><span class="ck"></span>Redo<span class="sc">Ctrl ⇧ Z</span></button><div class="k-mbar-sep"></div><button class="k-mbar-mi" type="button" role="menuitem"><span class="ck"></span>Cut<span class="sc">Ctrl X</span></button><button class="k-mbar-mi" type="button" role="menuitem"><span class="ck"></span>Copy<span class="sc">Ctrl C</span></button><button class="k-mbar-mi" type="button" role="menuitem"><span class="ck"></span>Paste<span class="sc">Ctrl V</span></button></div></span>
      <span class="k-mbar-item"><button class="k-mbar-btn" type="button" aria-expanded="false" aria-haspopup="true">View</button><div class="k-mbar-menu" role="menu"><button class="k-mbar-mi" type="button" role="menuitem"><span class="ck"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="square" stroke-linejoin="bevel" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span>Sidebar<span class="sc"></span></button><button class="k-mbar-mi" type="button" role="menuitem"><span class="ck"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="square" stroke-linejoin="bevel" aria-hidden="true"><path d="m5 12.5 4.5 4.5L19 7.5"/></svg></span>Status bar<span class="sc"></span></button><div class="k-mbar-sep"></div><button class="k-mbar-mi" type="button" role="menuitem"><span class="ck"></span>Zoom in<span class="sc">Ctrl +</span></button><button class="k-mbar-mi" type="button" role="menuitem"><span class="ck"></span>Zoom out<span class="sc">Ctrl −</span></button></div></span>
      <span class="k-mbar-item"><button class="k-mbar-btn" type="button" aria-expanded="false" aria-haspopup="true">Help</button><div class="k-mbar-menu" role="menu"><button class="k-mbar-mi" type="button" role="menuitem"><span class="ck"></span>Keyboard shortcuts<span class="sc"></span></button><button class="k-mbar-mi" type="button" role="menuitem"><span class="ck"></span>About<span class="sc"></span></button></div></span>
    </div>
    <p style="font-size:12.5px;color:var(--k-ink-3);margin-top:12px;padding:0 6px">Open File, then slide across to Edit — the bar follows, like a desktop.</p>
  </div>
</div>
/* KATA Menubar — the desktop bar, with the shortcuts showing. */
.k-mbar{display:flex;align-items:center;gap:2px;font-family:var(--k-font);color:var(--k-ink);position:relative;background:var(--k-bg)}
.k-mbar-item{position:relative}
.k-mbar-btn{padding:6px 11px;border:0;background:none;font-family:inherit;font-size:13px;color:var(--k-ink-2);border-radius:7px;cursor:pointer}
.k-mbar-btn:hover{background:var(--k-bg-soft);color:var(--k-ink)}
.k-mbar-item.open .k-mbar-btn{background:var(--k-bg-soft);color:var(--k-ink)}
.k-mbar-menu{position:absolute;inset-inline-start:0;top:calc(100% + 4px);min-width:228px;background:var(--k-bg);border:1px solid var(--k-line);border-radius:11px;box-shadow:0 18px 40px -12px rgba(16,24,40,.25);padding:5px;display:none;z-index:60}
.k-mbar-item.open .k-mbar-menu{display:block}
.k-mbar-mi{display:flex;align-items:center;gap:9px;width:100%;padding:7px 9px;border:0;background:none;font-family:inherit;font-size:13px;color:var(--k-ink);border-radius:7px;cursor:pointer;text-align:start}
.k-mbar-mi:hover{background:var(--k-bg-soft)}
.k-mbar-mi .ck{width:15px;flex:none;color:var(--k-ink)}
.k-mbar-mi .sc{margin-inline-start:auto;font-size:11.5px;color:var(--k-ink-3);font-family:var(--k-mono,monospace);padding-inline-start:22px}
.k-mbar-sep{height:1px;background:var(--k-line);margin:5px 8px}
.k-mbar-mi[disabled]{opacity:.45;pointer-events:none}
// KATA Menubar — click opens; while open, crossing the bar switches menus.
document.querySelectorAll('[data-mbar]').forEach(function (bar) {
  var items = Array.prototype.slice.call(bar.querySelectorAll('.k-mbar-item'));
  var anyOpen = false;
  function set(it, open) {
    it.classList.toggle('open', open);
    it.querySelector('.k-mbar-btn').setAttribute('aria-expanded', open ? 'true' : 'false');
  }
  function closeAll() { items.forEach(function (it) { set(it, false); }); anyOpen = false; }
  items.forEach(function (it) {
    var btn = it.querySelector('.k-mbar-btn');
    btn.addEventListener('click', function () {
      var was = it.classList.contains('open');
      closeAll();
      if (!was) { set(it, true); anyOpen = true; }
    });
    btn.addEventListener('pointerenter', function () { if (anyOpen) { closeAll(); set(it, true); anyOpen = true; } });
    it.querySelectorAll('.k-mbar-mi').forEach(function (mi) { mi.addEventListener('click', closeAll); });
  });
  document.addEventListener('click', function (e) { if (!bar.contains(e.target)) closeAll(); });
  document.addEventListener('keydown', function (e) { if (e.key === 'Escape') closeAll(); });
});

Installation

CLI

node kata-ui.mjs add menubar

Writes the part’s CSS and JS into your project. New to the CLI? See the CLI doc.

Manual

  1. Load tokens.css once, before any part.
  2. Copy the CSS tab into your stylesheet.
  3. Copy the HTML where it belongs. The part has a script — include it after the markup; it initializes itself.

Usage

For editor-shaped and tool-shaped apps — anywhere commands outnumber buttons. Menus carry the shortcuts on the right so the bar doubles as documentation. While one menu is open, moving across the bar switches menus, the way every desktop taught its users to expect.

Accessibility

Buttons carry aria-expanded; menu items are menuitem buttons, Escape closes and returns focus, and clicking away closes. Full arrow-key menu navigation is the app’s layer — the part guarantees the mouse and the basics.

Anatomy

The public surface of this part, read from its own source. Class names are the API — the CSS tab above is the full contract.

.k-mbarclass
.k-mbar-itemclass
.k-mbar-btnclass
.openclass
.k-mbar-menuclass
.k-mbar-miclass
.ckclass
.scclass
.k-mbar-sepclass
data-mbardata hook
aria-labelARIA
aria-expandedARIA
aria-haspopupARIA
aria-hiddenARIA

Related parts