Documents v0.9.0 · previews run the real runtime · experimental features are marked exp

Context menu

Right-click anywhere on the desk to open a custom menu where you clicked.

Live previewreal runtime

Right-click anywhere on the desk, then pick an item. From the keyboard, focus the desk with Tab and press Shift+F10 or the Menu key, then Tab into the items; Escape closes the menu.

How it works

  • :on()lines 12, 20, 25, 32…An event rule: when the event happens on the matched element (or inside it, for bubbling events), the rule’s state mutations run. The cascade picks one winning mutation per state, per trigger and per element.
  • prevent()line 12Event modifiers for :on(): prevent() cancels the browser's default for the event, stop() ends the event at the element so ancestors do not see it.
  • event()lines 15, 16Inside a mutation of an :on() rule: one field of the current event, such as event(local-x), event(key) or event(value), read once as a typed value.
  • focus:line 46Moves focus to the matched element when its value goes from false to true because of a user action. In 0.9 this declaration is the focus request: focus: when(--open).

A custom context menu with no script. A contextmenu rule cancels the browser’s own menu with prevent(), stores where the right-click happened, and opens the menu there. Clicks, Escape and a press outside close it.

The position is an event-time value: event(local-x) and event(local-y) are read once, at the right-click, and clamped with element(width) and element(height) so the menu stays inside the desk.

Native CSS alternative

None. CSS cannot cancel the native menu or open an element at a position. The popover attribute gives light dismiss, but not a position or a right-click trigger.

Limitations

  • Focus does not move into the menu when it opens. Focus requests are armed only by click, keydown, input, change, pointerup and submit, and a right-click is a contextmenu event. Tab moves into the items.
  • Arrow keys do not move between items: there is no action that moves focus to a selector. Use Tab and Shift+Tab.
  • A menu opened from the keyboard appears where the browser reports the event, usually the desk’s top-left corner.

Implementation

2 files · 48 lines
style.cssreactive
/* ---- Reactive CSS -------------------------------------------------------------------------- */.ctx {  --open: state(false);  --x: state(0px);  --y: state(0px);  --action: state("none");  --restore: state(false);}/* Right-click, or the Menu key: prevent() keeps the browser's own menu closed.   The position is read once, at the event, and kept inside the desk. */.ctx .desk:on(contextmenu, prevent()) {  --open: set(true);  --restore: set(false);  --x: set(clamp(0px, event(local-x), calc(element(width) - 164px)));  --y: set(clamp(0px, event(local-y), calc(element(height) - 150px)));}/* A click on the desk closes the menu. */.ctx .desk:on(click) {  --open: set(false);}/* An item records its action, closes the menu and sends focus back to the desk. */.ctx .menu button:on(click) {  --action: set(attr(data-action));  --open: set(false);  --restore: set(true);}/* Escape from the desk or from an item. */.ctx .stage:on(keydown(Escape)) {  --open: set(false);  --restore: set(true);}/* A press elsewhere on the page closes it too, without taking focus. */.ctx:on(outside(pointerdown)) {  --open: set(false);}.ctx:when(not --open) .menu { display: none; }.ctx .menu { translate: var(--x) var(--y); }/* Escape or an item returns focus to the desk. */.ctx .desk { focus: when(--restore); }.ctx .last { content: var(--action); }
state()Function · hover any red tokenMakes each matching element the owner of an independent state with an initial value.Reference →

Source: site/src/catalog/api/context-menu/. style.css from its Reactive CSS divider; the layout above it and the shared api.css run but are not shown.

Related

State toggle

One boolean state that a button flips, and styles that follow it.

toggle():when()

Form mirror

Type in the field and the preview follows; the Clear button resets both.

experimentalstate()bind():on()event()content