Theme
Version
GitHub PyPI Discord
On this page

Menu

Use CMenu for a temporary application-command collection. It supports native links, grouped commands, check/radio choices, and nested submenus with direct focus, typeahead, touch-safe activation, and logical placement.

Use CPopover for arbitrary controls, forms, or explanatory content. Menu items accept text and decorative content, not nested interactive controls.

Open the archive menu to see commands, navigation, a submenu, a separator, and destructive emphasis together.

Menu at a glance
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class MenuAtAGlance(Component):
    template = """
      <section class="archive-menu-demo">
        <p>Enchanted archive</p>
        <c-CMenu>
          <c-fill name="activator" data="{ activator_attrs, activator_disabled }">
            <c-CButton c-disabled="activator_disabled" c-attrs="activator_attrs">Open archive menu</c-CButton>
          </c-fill>
          <c-fill name="default">
            <c-CMenuItem value="rename">Rename folio</c-CMenuItem>
            <c-CMenuItem href="#moon-catalog">Open moon catalog</c-CMenuItem>
            <c-CMenuSubmenu value="send-to">
              <c-fill name="label">Send to collection</c-fill>
              <c-fill name="default">
                <c-CMenuItem value="astronomy">Astronomy</c-CMenuItem>
                <c-CMenuItem value="mythology">Mythology</c-CMenuItem>
              </c-fill>
            </c-CMenuSubmenu>
            <c-CMenuSeparator />
            <c-CMenuItem value="banish" intent="danger">Banish folio</c-CMenuItem>
          </c-fill>
        </c-CMenu>
      </section>
    """

    css = """
      :where(.archive-menu-demo) {
        display: grid;
        gap: 0.75rem;
        justify-items: start;
        min-block-size: 17rem;
        color: CanvasText;
        font-family: ui-sans-serif, system-ui, sans-serif;
      }

      :where(.archive-menu-demo > p) {
        margin: 0;
        color: light-dark(#7a4b18, #e8bd76);
        font-size: 0.75rem;
        font-weight: 700;
        letter-spacing: 0.08em;
        text-transform: uppercase;
      }
    """


preview = MenuAtAGlance()

preview  # noqa: B018

Compose a Menu

Provide exactly one native Button through activator. Put Menu-family declarations directly in the default slot.

<c-CMenu>
  <c-fill name="activator" data="{ activator_attrs, activator_disabled }">
    <c-CButton c-disabled="activator_disabled" c-attrs="activator_attrs">Open archive</c-CButton>
  </c-fill>
  <c-fill name="default">
    <c-CMenuItem value="rename">Rename folio</c-CMenuItem>
    <c-CMenuItem href="/catalog">Open catalog</c-CMenuItem>
    <c-CMenuSeparator />
    <c-CMenuItem value="delete" intent="danger">Delete folio</c-CMenuItem>
  </c-fill>
</c-CMenu>

Forward both activator fields. activator_attrs carries relationships and the anchor; activator_disabled goes through CButton's disabled input. A native button also sets type="button" directly.

For Python composition, supply one component whose output contains the direct declarations. Transparent components may generate declarations when they add no wrapper or other output.

CMenuItem, CMenuCheckboxItem, CMenuRadioGroup, CMenuRadioItem, CMenuGroup, CMenuSeparator, and CMenuSubmenu are not standalone.

Give a command value when the root onAction callback should identify it. Anonymous commands use native @click. Supplying href renders a real anchor and preserves navigation, link context menus, and browser behavior.

Commands and links
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class MenuCommandsAndLinks(Component):
    template = """
      <section
        class="archive-command-demo"
        x-data="{lastAction: 'none'}"
      >
        <c-CMenu
          $c-props="{
            onAction: (value) => lastAction = value,
          }"
        >
          <c-fill name="activator" data="{ activator_attrs, activator_disabled }">
            <c-CButton c-disabled="activator_disabled" c-attrs="activator_attrs">Folio actions</c-CButton>
          </c-fill>
          <c-fill name="default">
            <c-CMenuItem value="duplicate">Duplicate folio</c-CMenuItem>
            <c-CMenuItem @click="lastAction = 'annotate'">Add annotation</c-CMenuItem>
            <c-CMenuItem href="#restricted-shelf">Visit restricted shelf</c-CMenuItem>
          </c-fill>
        </c-CMenu>
        <output x-text="`Last command: ${lastAction}`">Last command: none</output>
      </section>
    """

    css = """
      :where(.archive-command-demo) {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 1rem;
        min-block-size: 14rem;
        color: CanvasText;
        font-family: ui-sans-serif, system-ui, sans-serif;
      }

      :where(.archive-command-demo output) {
        color: light-dark(#66451f, #dec08f);
      }
    """


preview = MenuCommandsAndLinks()

preview  # noqa: B018

Links do not call onAction. Disabled links temporarily omit href and never navigate.

Add item content

Use start, default, description, and end for icons, the visible label, supporting text, and shortcuts. Only the default label names the item; the description is exposed separately.

Item content
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class MenuItemContent(Component):
    template = """
      <section class="archive-content-demo">
        <c-CMenu>
          <c-fill name="activator" data="{ activator_attrs, activator_disabled }">
            <c-CButton c-disabled="activator_disabled" c-attrs="activator_attrs">Catalog tools</c-CButton>
          </c-fill>
          <c-fill name="default">
            <c-CMenuItem value="search">
              <c-fill name="start"><c-CIcon name="search" /></c-fill>
              <c-fill name="default">Search illuminated texts</c-fill>
              <c-fill name="description">Find titles, scribes, and sigils.</c-fill>
              <c-fill name="end"><kbd>⌘ K</kbd></c-fill>
            </c-CMenuItem>
            <c-CMenuItem value="bookmark">
              <c-fill name="start"><c-CIcon name="star" /></c-fill>
              <c-fill name="default">Mark this passage</c-fill>
              <c-fill name="end"><kbd>M</kbd></c-fill>
            </c-CMenuItem>
          </c-fill>
        </c-CMenu>
      </section>
    """

    css = """
      :where(.archive-content-demo) {
        min-block-size: 15rem;
        color: CanvasText;
        font-family: ui-sans-serif, system-ui, sans-serif;
      }

      :where(.archive-content-demo kbd) {
        padding: 0.1rem 0.35rem;
        border: 1px solid color-mix(in srgb, CanvasText 20%, transparent);
        border-radius: 0.3rem;
        font: inherit;
        font-size: 0.75rem;
      }
    """


preview = MenuItemContent()

preview  # noqa: B018

Keep every item region to noninteractive phrasing content. Set text_value when the visible label does not produce concise typeahead text.

Control visibility and configuration

Server inputs are passed in Python through <c-CMenu ... /> attributes or a CMenu(...) composition call. Client inputs are passed in the browser through $c-props="{...}".

Control Menu visibility
Customize example
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class ControlledMenu(Component):
    template = """
      <section
        class="archive-controlled-demo"
        x-data="{open: false, disabled: false, locked: false, size: 'md', lastReason: 'none'}"
        @citry-ui-preview-controls.window="Object.assign($data, $event.detail)"
      >
        <c-CButton size="sm" @click="open = !open">Toggle from owner</c-CButton>
        <c-CMenu
          $c-props="{
            open,
            disabled,
            size,
            onOpenChange: (nextOpen, detail) => {
              lastReason = detail.reason;
              if (!locked) open = nextOpen;
            },
          }"
        >
          <c-fill name="activator" data="{ activator_attrs, activator_disabled }">
            <c-CButton c-disabled="activator_disabled" c-attrs="activator_attrs">Controlled grimoire</c-CButton>
          </c-fill>
          <c-fill name="default">
            <c-CMenuItem value="translate">Translate runes</c-CMenuItem>
            <c-CMenuItem value="restore">Restore missing page</c-CMenuItem>
          </c-fill>
        </c-CMenu>
        <output x-text="`Last request: ${lastReason}`">Last request: none</output>
      </section>
    """

    css = """
      :where(.archive-controlled-demo) {
        display: grid;
        gap: 1rem;
        min-block-size: 17rem;
        color: CanvasText;
        font-family: ui-sans-serif, system-ui, sans-serif;
      }

    """


preview_controls = (
    {
        "name": "size",
        "label": "Size",
        "type": "select",
        "default": "md",
        "options": (("sm", "Small"), ("md", "Medium"), ("lg", "Large")),
    },
    {
        "name": "disabled",
        "label": "Disabled",
        "type": "checkbox",
        "default": False,
    },
    {
        "name": "locked",
        "label": "Decline visibility requests",
        "type": "checkbox",
        "default": False,
    },
)


preview = ControlledMenu()

preview  # noqa: B018

A Boolean client open owns visibility. Omit it or pass null to release control from the current committed state. onOpenChange reports requests; forced ancestor/modal/disabled closes cannot be rejected.

Add application choices

Checkbox and radio items model application preferences, not native Form controls. They contribute no FormData and emit no native input/change event.

Menu choices
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class MenuChoices(Component):
    template = """
      <section
        class="archive-choice-demo"
        x-data
        x-init="Alpine.store('archiveMenuChoices', {glow: 'mixed', script: 'elvish'})"
      >
        <c-CMenu c-close_on_select="False">
          <c-fill name="activator" data="{ activator_attrs, activator_disabled }">
            <c-CButton c-disabled="activator_disabled" c-attrs="activator_attrs">Reading preferences</c-CButton>
          </c-fill>
          <c-fill name="default">
            <c-CMenuCheckboxItem
              value="glow"
              checked="mixed"
              $c-props="{
                checked: $store.archiveMenuChoices.glow,
                onCheckedChange: (value) => $store.archiveMenuChoices.glow = value,
              }"
            >
              Glow around enchanted passages
            </c-CMenuCheckboxItem>
            <c-CMenuSeparator />
            <c-CMenuRadioGroup
              value="elvish"
              $c-props="{
                value: $store.archiveMenuChoices.script,
                onValueChange: (value) => $store.archiveMenuChoices.script = value,
              }"
            >
              <c-fill name="label">Translation script</c-fill>
              <c-fill name="default">
                <c-CMenuRadioItem value="elvish">Elvish</c-CMenuRadioItem>
                <c-CMenuRadioItem value="draconic">Draconic</c-CMenuRadioItem>
                <c-CMenuRadioItem value="celestial">Celestial</c-CMenuRadioItem>
              </c-fill>
            </c-CMenuRadioGroup>
          </c-fill>
        </c-CMenu>
        <output
          x-text="`Glow: ${$store.archiveMenuChoices.glow}; script: ${$store.archiveMenuChoices.script}`"
        ></output>
      </section>
    """

    css = """
      :where(.archive-choice-demo) {
        display: grid;
        gap: 1rem;
        min-block-size: 17rem;
        color: CanvasText;
        font-family: ui-sans-serif, system-ui, sans-serif;
      }
    """


preview = MenuChoices()

preview  # noqa: B018

Checkboxes support false, true, and "mixed"; activating mixed requests true. A radio group owns one value. Set close_on_select=False when readers should make several choices before leaving the Menu.

Group commands

CMenuGroup owns a visible accessible label. CMenuSeparator divides adjacent command families. Radio groups have their own optional label.

Groups and separators
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class MenuGroups(Component):
    template = """
      <section class="archive-group-demo">
        <c-CMenu>
          <c-fill name="activator" data="{ activator_attrs, activator_disabled }">
            <c-CButton c-disabled="activator_disabled" c-attrs="activator_attrs">Archive sections</c-CButton>
          </c-fill>
          <c-fill name="default">
            <c-CMenuGroup>
              <c-fill name="label">Public halls</c-fill>
              <c-fill name="default">
                <c-CMenuItem value="maps">Star maps</c-CMenuItem>
                <c-CMenuItem value="herbals">Moonlit herbals</c-CMenuItem>
              </c-fill>
            </c-CMenuGroup>
            <c-CMenuSeparator />
            <c-CMenuGroup>
              <c-fill name="label">Restricted vaults</c-fill>
              <c-fill name="default">
                <c-CMenuItem value="prophecies">Sealed prophecies</c-CMenuItem>
                <c-CMenuItem value="curses" disabled>Curses under glass</c-CMenuItem>
              </c-fill>
            </c-CMenuGroup>
          </c-fill>
        </c-CMenu>
      </section>
    """

    css = """
      :where(.archive-group-demo) {
        min-block-size: 18rem;
        color: CanvasText;
        font-family: ui-sans-serif, system-ui, sans-serif;
      }
    """


preview = MenuGroups()

preview  # noqa: B018

Do not put separators first, last, or consecutively. Generic groups cannot be nested inside generic groups.

Nest submenus

CMenuSubmenu is one item plus another Menu surface. Give it a stable value, a label fill, and direct declarations in its default fill.

Nested command menus
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class NestedMenus(Component):
    template = """
      <section class="archive-submenu-demo">
        <c-CMenu>
          <c-fill name="activator" data="{ activator_attrs, activator_disabled }">
            <c-CButton c-disabled="activator_disabled" c-attrs="activator_attrs">Choose a collection</c-CButton>
          </c-fill>
          <c-fill name="default">
            <c-CMenuSubmenu value="skies">
              <c-fill name="label">Celestial archives</c-fill>
              <c-fill name="default">
                <c-CMenuItem value="constellations">Constellations</c-CMenuItem>
                <c-CMenuSubmenu value="moons">
                  <c-fill name="label">Moon records</c-fill>
                  <c-fill name="default">
                    <c-CMenuItem value="silver">Silver moon</c-CMenuItem>
                    <c-CMenuItem value="ember">Ember moon</c-CMenuItem>
                  </c-fill>
                </c-CMenuSubmenu>
              </c-fill>
            </c-CMenuSubmenu>
            <c-CMenuSubmenu value="seas">
              <c-fill name="label">Sunken archives</c-fill>
              <c-fill name="default">
                <c-CMenuItem value="tides">Tide almanacs</c-CMenuItem>
                <c-CMenuItem value="leviathans">Leviathan sightings</c-CMenuItem>
              </c-fill>
            </c-CMenuSubmenu>
          </c-fill>
        </c-CMenu>
      </section>
    """

    css = """
      :where(.archive-submenu-demo) {
        display: grid;
        place-items: start center;
        min-block-size: 22rem;
        padding-inline: 5rem;
        color: CanvasText;
        font-family: ui-sans-serif, system-ui, sans-serif;
      }
    """


preview = NestedMenus()

preview  # noqa: B018

Arrow direction follows text direction. Pointer intent uses the submenu's actual collision-resolved geometry. Deep nesting works, but one level is usually easier to scan and operate.

Keyboard and typeahead

Arrow Down/Up moves direct focus, Home/End reaches the edges, and printable characters perform buffered prefix matching. Repeating one character cycles matching labels. loop controls wrapping.

Keyboard and typeahead
Customize example
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class MenuKeyboard(Component):
    template = """
      <section
        class="archive-keyboard-demo"
        x-data="{loop: true, close: false}"
        @citry-ui-preview-controls.window="Object.assign($data, $event.detail)"
      >
        <c-CMenu
          c-close_on_select="False"
          $c-props="{loop, closeOnSelect: close}"
        >
          <c-fill name="activator" data="{ activator_attrs, activator_disabled }">
            <c-CButton c-disabled="activator_disabled" c-attrs="activator_attrs">Browse spell index</c-CButton>
          </c-fill>
          <c-fill name="default">
            <c-CMenuItem value="aegis">Aegis</c-CMenuItem>
            <c-CMenuItem value="alchemy">Alchemy</c-CMenuItem>
            <c-CMenuItem value="astral">Astral projection</c-CMenuItem>
            <c-CMenuItem value="binding">Binding</c-CMenuItem>
            <c-CMenuItem value="blessing">Blessing</c-CMenuItem>
            <c-CMenuItem value="conjuring">Conjuring</c-CMenuItem>
            <c-CMenuItem value="divination">Divination</c-CMenuItem>
            <c-CMenuItem value="enchantment">Enchantment</c-CMenuItem>
            <c-CMenuItem value="illusion">Illusion</c-CMenuItem>
            <c-CMenuItem value="restoration">Restoration</c-CMenuItem>
            <c-CMenuItem value="warding">Warding</c-CMenuItem>
          </c-fill>
        </c-CMenu>
      </section>
    """

    css = """
      :where(.archive-keyboard-demo) {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 1rem;
        min-block-size: 20rem;
        color: CanvasText;
        font-family: ui-sans-serif, system-ui, sans-serif;
      }

      :where(.archive-keyboard-demo) {
        --cui-menu-max-block-size: 13rem;
      }
    """


preview_controls = (
    {
        "name": "loop",
        "label": "Loop navigation",
        "type": "checkbox",
        "default": True,
    },
    {
        "name": "close",
        "label": "Close on action",
        "type": "checkbox",
        "default": False,
    },
)


preview = MenuKeyboard()

preview  # noqa: B018

Escape closes one submenu or the root. Tab closes the whole tree and continues normal page order. Disabled items remain discoverable by Menu navigation but never activate.

Disable Menu safely

Menu disabled and native disabled fieldset ancestry are authoritative. Buttons inside the Menu always use type=button, so commands never submit an enclosing Form.

Disabled Menu and native Forms
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class MenuDisabledAndForms(Component):
    template = """
      <section
        class="archive-disabled-demo"
        x-data="{locked: true, submits: 0}"
      >
        <c-CButton size="sm" @click="locked = !locked">
          Toggle archive seal
        </c-CButton>
        <form @submit.prevent="submits += 1">
          <fieldset :disabled="locked">
            <legend>Archive desk</legend>
            <c-CMenu>
              <c-fill name="activator" data="{ activator_attrs, activator_disabled }">
                <c-CButton c-disabled="activator_disabled" c-attrs="activator_attrs">Desk commands</c-CButton>
              </c-fill>
              <c-fill name="default">
                <c-CMenuItem value="catalog">Catalog folio</c-CMenuItem>
                <c-CMenuItem value="sealed" disabled>Break royal seal</c-CMenuItem>
              </c-fill>
            </c-CMenu>
            <button type="submit">Submit native form</button>
          </fieldset>
        </form>
        <output x-text="`Form submits: ${submits}`">Form submits: 0</output>
      </section>
    """

    css = """
      :where(.archive-disabled-demo) {
        display: grid;
        gap: 0.75rem;
        min-block-size: 18rem;
        color: CanvasText;
        font-family: ui-sans-serif, system-ui, sans-serif;
      }

      :where(.archive-disabled-demo fieldset) {
        display: flex;
        gap: 0.75rem;
        align-items: center;
        padding: 1rem;
        border: 1px solid color-mix(in srgb, CanvasText 20%, transparent);
        border-radius: 0.75rem;
      }
    """


preview = MenuDisabledAndForms()

preview  # noqa: B018

Place the surface

Choose one of six logical block placements. match_width follows the activator only up to the viewport-safe maximum. Submenus prefer logical inline-end, flip inline, then use a centered block fallback when neither side is usable.

Placement, width, and RTL
Customize example
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class MenuPlacement(Component):
    template = """
      <section
        class="archive-placement-demo"
        x-data="{placement: 'bottom-start', rtl: false, match: true}"
        :dir="rtl ? 'rtl' : 'ltr'"
        @citry-ui-preview-controls.window="Object.assign($data, $event.detail)"
      >
        <c-CMenu $c-props="{placement, matchWidth: match}">
          <c-fill name="activator" data="{ activator_attrs, activator_disabled }">
            <c-CButton
              class_="archive-placement-demo__wide"
              c-disabled="activator_disabled"
              c-attrs="activator_attrs"
            >
              A deliberately wide enchanted-volume trigger
            </c-CButton>
          </c-fill>
          <c-fill name="default">
            <c-CMenuItem value="north">Northern shelf</c-CMenuItem>
            <c-CMenuItem value="south">Southern shelf</c-CMenuItem>
            <c-CMenuSubmenu value="hidden-wing">
              <c-fill name="label">Hidden wing</c-fill>
              <c-fill name="default">
                <c-CMenuItem value="mirrors">Hall of mirrors</c-CMenuItem>
              </c-fill>
            </c-CMenuSubmenu>
          </c-fill>
        </c-CMenu>
      </section>
    """

    css = """
      :where(.archive-placement-demo) {
        display: grid;
        gap: 1rem;
        justify-items: center;
        min-block-size: 21rem;
        color: CanvasText;
        font-family: ui-sans-serif, system-ui, sans-serif;
      }

      :where(.archive-placement-demo__wide) {
        inline-size: min(34rem, 150dvi);
      }
    """


preview_controls = (
    {
        "name": "placement",
        "label": "Placement",
        "type": "select",
        "default": "bottom-start",
        "options": (
            ("bottom-start", "Bottom start"),
            ("bottom", "Bottom"),
            ("bottom-end", "Bottom end"),
            ("top-start", "Top start"),
            ("top", "Top"),
            ("top-end", "Top end"),
        ),
    },
    {
        "name": "match",
        "label": "Match activator width",
        "type": "checkbox",
        "default": True,
    },
    {
        "name": "rtl",
        "label": "Right-to-left",
        "type": "checkbox",
        "default": False,
    },
)


preview = MenuPlacement()

preview  # noqa: B018

Choose a size

sm, md, and lg change the whole family’s item geometry.

Menu sizes
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class MenuSizes(Component):
    template = """
      <section class="archive-size-demo">
        <c-CMenu size="sm">
          <c-fill name="activator" data="{ activator_attrs, activator_disabled }">
            <c-CButton size="sm" c-disabled="activator_disabled" c-attrs="activator_attrs">Small</c-CButton>
          </c-fill>
          <c-fill name="default">
            <c-CMenuItem value="index">Pocket index</c-CMenuItem>
            <c-CMenuItem value="notes">Margin notes</c-CMenuItem>
          </c-fill>
        </c-CMenu>
        <c-CMenu size="md">
          <c-fill name="activator" data="{ activator_attrs, activator_disabled }">
            <c-CButton c-disabled="activator_disabled" c-attrs="activator_attrs">Medium</c-CButton>
          </c-fill>
          <c-fill name="default">
            <c-CMenuItem value="index">Reading index</c-CMenuItem>
            <c-CMenuItem value="notes">Scribe notes</c-CMenuItem>
          </c-fill>
        </c-CMenu>
        <c-CMenu size="lg">
          <c-fill name="activator" data="{ activator_attrs, activator_disabled }">
            <c-CButton size="lg" c-disabled="activator_disabled" c-attrs="activator_attrs">Large</c-CButton>
          </c-fill>
          <c-fill name="default">
            <c-CMenuItem value="index">Grand index</c-CMenuItem>
            <c-CMenuItem value="notes">Archivist notes</c-CMenuItem>
          </c-fill>
        </c-CMenu>
      </section>
    """

    css = """
      :where(.archive-size-demo) {
        display: flex;
        flex-wrap: wrap;
        align-items: start;
        gap: 1rem;
        min-block-size: 17rem;
        color: CanvasText;
        font-family: ui-sans-serif, system-ui, sans-serif;
      }
    """


preview = MenuSizes()

preview  # noqa: B018

Customize Menu

Override public variables on an ancestor or one wrapper. Stable part selectors target the surface, item regions, groups, indicators, separators, and submenus.

Theme archive menus
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class CustomizedMenus(Component):
    template = """
      <section class="archive-theme-demo">
        <div class="archive-theme-demo__moon">
          <c-CMenu>
            <c-fill name="activator" data="{ activator_attrs, activator_disabled }">
              <c-CButton c-disabled="activator_disabled" c-attrs="activator_attrs">Moon archive</c-CButton>
            </c-fill>
            <c-fill name="default">
              <c-CMenuItem value="phases">Moon phases</c-CMenuItem>
              <c-CMenuItem value="eclipses">Eclipse records</c-CMenuItem>
            </c-fill>
          </c-CMenu>
        </div>
        <div class="archive-theme-demo__ember">
          <c-CMenu>
            <c-fill name="activator" data="{ activator_attrs, activator_disabled }">
              <c-CButton c-disabled="activator_disabled" c-attrs="activator_attrs">Ember archive</c-CButton>
            </c-fill>
            <c-fill name="default">
              <c-CMenuItem value="dragons">Dragon chronicles</c-CMenuItem>
              <c-CMenuItem value="ashes" intent="danger">Destroy ash record</c-CMenuItem>
            </c-fill>
          </c-CMenu>
        </div>
      </section>
    """

    css = """
      :where(.archive-theme-demo) {
        display: flex;
        flex-wrap: wrap;
        gap: 1.5rem;
        min-block-size: 18rem;
        color: CanvasText;
        font-family: ui-sans-serif, system-ui, sans-serif;
      }

      :where(.archive-theme-demo__moon) {
        --cui-menu-background: light-dark(#f4f2ff, #17142d);
        --cui-menu-border-color: light-dark(#8f83c7, #7065aa);
        --cui-menu-focus-background: light-dark(#4c3e92, #b6a9ff);
        --cui-menu-focus-foreground: light-dark(#ffffff, #17142d);
        --cui-menu-radius: 1rem;
      }

      :where(.archive-theme-demo__ember) {
        --cui-menu-background: light-dark(#fff7ed, #2a1710);
        --cui-menu-border-color: light-dark(#d97706, #f59e0b);
        --cui-menu-focus-background: light-dark(#9a3412, #fdba74);
        --cui-menu-focus-foreground: light-dark(#ffffff, #2a1710);
        --cui-menu-danger-color: light-dark(#991b1b, #fecaca);
        --cui-menu-radius: 0.35rem;
      }
    """


preview = CustomizedMenus()

preview  # noqa: B018

Every styled family member exposes top-level class_ and style on its documented root. Unlayered consumer CSS overrides Citry UI defaults; named layers follow the site-wide layer-order contract.

Compose with other overlays

Menu, Popover, Tooltip, and Dialog share one logical layer coordinator. Closing an ancestor closes descendant submenus first. Opening an unrelated modal Dialog suppresses outside anchored layers and gives the Dialog Escape/focus ownership.

Overlay ownership and cleanup
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class MenuLifecycle(Component):
    template = """
      <section class="archive-lifecycle-demo" x-data>
        <c-CPopover>
          <c-fill name="activator" data="{ activator_attrs }">
            <c-CButton c-attrs="activator_attrs">Open reading room</c-CButton>
          </c-fill>
          <c-fill name="title">Reading room</c-fill>
          <c-fill name="default">
            <c-CMenu>
              <c-fill name="activator" data="{ activator_attrs, activator_disabled }">
                <c-CButton c-disabled="activator_disabled" c-attrs="activator_attrs">Nested folio menu</c-CButton>
              </c-fill>
              <c-fill name="default">
                <c-CMenuItem value="inspect">Inspect binding</c-CMenuItem>
                <c-CMenuSubmenu value="editions">
                  <c-fill name="label">Other editions</c-fill>
                  <c-fill name="default">
                    <c-CMenuItem value="first">First edition</c-CMenuItem>
                  </c-fill>
                </c-CMenuSubmenu>
              </c-fill>
            </c-CMenu>
          </c-fill>
        </c-CPopover>
        <c-CButton @click="$refs.vault.showModal()">Open modal vault</c-CButton>
        <dialog x-ref="vault" aria-labelledby="vault-title">
          <h2 id="vault-title">Royal vault</h2>
          <p>Opening this modal closes unrelated anchored layers.</p>
          <button type="button" @click="$refs.vault.close()">Close vault</button>
        </dialog>
      </section>
    """

    css = """
      :where(.archive-lifecycle-demo) {
        display: flex;
        flex-wrap: wrap;
        gap: 1rem;
        min-block-size: 20rem;
        color: CanvasText;
        font-family: ui-sans-serif, system-ui, sans-serif;
      }

      :where(.archive-lifecycle-demo dialog) {
        max-inline-size: min(26rem, calc(100dvi - 2rem));
        padding: 1.25rem;
        border: 1px solid color-mix(in srgb, CanvasText 24%, transparent);
        border-radius: 0.85rem;
        background: Canvas;
        color: CanvasText;
      }

      :where(.archive-lifecycle-demo dialog::backdrop) {
        background: rgb(15 23 42 / 45%);
      }
    """


preview = MenuLifecycle()

preview  # noqa: B018

Trust boundary

Text is escaped. Values are plain, nonempty canonical strings; generated IDs do not expose raw values. href remains a trusted application URL boundary. Attribute maps reject owned semantics, focus, visibility, anchoring, structural Alpine directives, and Citry runtime namespaces. Use Popover when item content needs links, Buttons, inputs, editing, or independent Tab stops.

API reference

Inputs

CMenu server inputs

Server inputs are passed in a template through <c-CMenu ... /> or in Python through CMenu(...).

InputTypeDefaultEffect
idstr | NonegeneratedSets the Menu surface and activator relationship identity.
openboolFalseSets initial visibility and the uncontrolled fallback.
disabledboolFalseDisables the activator and force-closes the tree. Native disabled fieldset ancestry also applies.
loopboolTrueWraps arrow navigation and typeahead matching.
placement"top-start" | "top" | "top-end" | "bottom-start" | "bottom" | "bottom-end" (CMenuPlacement)"bottom-start"Sets the preferred logical root placement.
match_widthboolFalseMatches the activator width up to the viewport-safe maximum.
close_on_selectboolTrueSets the default command and choice close policy.
size"sm" | "md" | "lg" (CMenuSize)"md"Sets item geometry for the whole tree.
class_str | Mapping[str, bool] | Sequence[CClassValue] | None (CClassValue)NoneAdds surface classes and merges with attrs.
stylestr | Mapping[str, str | int | float | bool | None] | Sequence[CStyleValue] | None (CStyleValue)NoneAdds surface styles; private anchor ownership merges last.
attrsMapping[str, object] | NoneNoneAdds allowed native, ARIA, Alpine, and data attributes to the Menu surface.

CMenu client inputs

Client inputs are passed in the browser through the $c-props="{ ... }" attribute on <c-CMenu />.

InputTypeOmitted behaviorEffect
openboolean | nullReleases control from committed state. null has the same effect.Controls root visibility while supplied as a Boolean.
disabledbooleanUses the server input.Controls local disabledness; native fieldset disabledness remains authoritative.
loopbooleanUses the server input.Controls keyboard wrapping.
placementsix logical placement strings (CMenuPlacement)Uses the server input.Controls requested placement.
matchWidthbooleanUses the server input.Controls clamped activator-width matching.
closeOnSelectbooleanUses the server input.Controls the tree default close policy.
size"sm" | "md" | "lg" (CMenuSize)Uses the server input.Controls tree geometry.
onOpenChangefunctionDoes not notify a visibility callback.Receives visibility requests and forced close notices.
onActionfunctionDoes not notify a root action callback.Receives valued command and choice activations.

CMenuItem server inputs

Server inputs are passed in a template through <c-CMenuItem ... /> or in Python through CMenuItem(...).

InputTypeDefaultEffect
valuestr | NoneNoneSupplies optional canonical command identity for root onAction; rejected with href.
hrefstr | NoneNoneRenders a real anchor and preserves native navigation.
disabledboolFalseMakes the item focusable but inactive.
close_on_selectbool | NoneNoneOverrides the root close policy when supplied.
intent"default" | "danger" (CMenuIntent)"default"Sets visual emphasis.
text_valuestr | NoneNoneOverrides label-derived typeahead text.
class_CClassValue | None (CClassValue)NoneAdds semantic-root classes.
styleCStyleValue | None (CStyleValue)NoneAdds semantic-root styles.
attrsMapping[str, object] | NoneNoneAdds allowed semantic-root attributes.

CMenuItem client inputs

Client inputs are passed in the browser through the $c-props="{ ... }" attribute on <c-CMenuItem />.

InputTypeOmitted behaviorEffect
disabledbooleanUses the server input.Controls inactive behavior and aria-disabled.
closeOnSelectboolean | nullInherits the root policy. null has the same effect.Controls per-item close behavior.
intent"default" | "danger" (CMenuIntent)Uses the server input.Controls emphasis and data-intent.
textValuestring | nullUses the server fallback.Controls typeahead text; a null fallback may read current label text.

CMenuCheckboxItem server inputs

Server inputs are passed in a template through <c-CMenuCheckboxItem ... /> or in Python through CMenuCheckboxItem(...).

InputTypeDefaultEffect
valuestrrequiredSets unique canonical choice identity.
checkedbool | "mixed" (CMenuChecked)FalseSets initial checked state.
disabledboolFalseMakes the item focusable but inactive.
close_on_selectbool | NoneNoneOverrides the root close policy.
text_valuestr | NoneNoneOverrides label-derived typeahead text.
class_CClassValue | None (CClassValue)NoneAdds item classes.
styleCStyleValue | None (CStyleValue)NoneAdds item styles.
attrsMapping[str, object] | NoneNoneAdds allowed item attributes.

CMenuCheckboxItem client inputs

Client inputs are passed in the browser through the $c-props="{ ... }" attribute on <c-CMenuCheckboxItem />.

InputTypeOmitted behaviorEffect
checkedboolean | "mixed" | null (CMenuChecked)Releases control from committed state. null has the same effect.Controls checked state.
disabledbooleanUses the server input.Controls inactive behavior.
closeOnSelectboolean | nullInherits the root policy.Controls per-item close behavior.
textValuestring | nullUses the server fallback.Controls typeahead text.
onCheckedChangefunctionDoes not notify a checked callback.Receives requested checked values.

CMenuRadioGroup server inputs

Server inputs are passed in a template through <c-CMenuRadioGroup ... /> or in Python through CMenuRadioGroup(...).

InputTypeDefaultEffect
valuestrrequiredSets the required initial direct-radio selection.
class_CClassValue | None (CClassValue)NoneAdds group classes.
styleCStyleValue | None (CStyleValue)NoneAdds group styles.
attrsMapping[str, object] | NoneNoneAdds allowed group attributes.

CMenuRadioGroup client inputs

Client inputs are passed in the browser through the $c-props="{ ... }" attribute on <c-CMenuRadioGroup />.

InputTypeOmitted behaviorEffect
valuestring | nullReleases control from committed state. null has the same effect.Controls the selected radio value.
onValueChangefunctionDoes not notify a value callback.Receives activation and structural-removal value requests.

CMenuRadioItem server inputs

Server inputs are passed in a template through <c-CMenuRadioItem ... /> or in Python through CMenuRadioItem(...).

InputTypeDefaultEffect
valuestrrequiredSets canonical identity unique in the radio group.
disabledboolFalseMakes the item focusable but inactive.
close_on_selectbool | NoneNoneOverrides the root close policy.
text_valuestr | NoneNoneOverrides label-derived typeahead text.
class_CClassValue | None (CClassValue)NoneAdds item classes.
styleCStyleValue | None (CStyleValue)NoneAdds item styles.
attrsMapping[str, object] | NoneNoneAdds allowed item attributes.

CMenuRadioItem client inputs

Client inputs are passed in the browser through the $c-props="{ ... }" attribute on <c-CMenuRadioItem />.

InputTypeOmitted behaviorEffect
disabledbooleanUses the server input.Controls inactive behavior.
closeOnSelectboolean | nullInherits the root policy.Controls per-item close behavior.
textValuestring | nullUses the server fallback.Controls typeahead text.

CMenuGroup server inputs

Server inputs are passed in a template through <c-CMenuGroup ... /> or in Python through CMenuGroup(...).

InputTypeDefaultEffect
class_CClassValue | None (CClassValue)NoneAdds group classes.
styleCStyleValue | None (CStyleValue)NoneAdds group styles.
attrsMapping[str, object] | NoneNoneAdds allowed group attributes.

CMenuSeparator server inputs

Server inputs are passed in a template through <c-CMenuSeparator ... /> or in Python through CMenuSeparator(...).

InputTypeDefaultEffect
class_CClassValue | None (CClassValue)NoneAdds separator classes.
styleCStyleValue | None (CStyleValue)NoneAdds separator styles.
attrsMapping[str, object] | NoneNoneAdds allowed separator attributes.

CMenuSubmenu server inputs

Server inputs are passed in a template through <c-CMenuSubmenu ... /> or in Python through CMenuSubmenu(...).

InputTypeDefaultEffect
valuestrrequiredSets a canonical path segment unique at its menu level.
disabledboolFalseMakes the trigger inactive and force-closes its child.
intent"default" | "danger" (CMenuIntent)"default"Sets trigger emphasis.
text_valuestr | NoneNoneOverrides label-derived typeahead text.
class_CClassValue | None (CClassValue)NoneAdds neutral-wrapper classes.
styleCStyleValue | None (CStyleValue)NoneAdds neutral-wrapper styles inherited by the child surface.
attrsMapping[str, object] | NoneNoneAdds allowed neutral-wrapper attributes.
trigger_attrsMapping[str, object] | NoneNoneAdds allowed submenu-trigger attributes.
menu_attrsMapping[str, object] | NoneNoneAdds allowed child Menu-surface attributes.

CMenuSubmenu client inputs

Client inputs are passed in the browser through the $c-props="{ ... }" attribute on <c-CMenuSubmenu />.

InputTypeOmitted behaviorEffect
disabledbooleanUses the server input.Controls inactive behavior and child closure.
intent"default" | "danger" (CMenuIntent)Uses the server input.Controls trigger emphasis.
textValuestring | nullUses the server fallback.Controls typeahead text.

Slots

Slots are passed as nested content or <c-fill> tags in a template, or through the slots={...} argument in Python.

CMenu slots

SlotRequiredDataFallback
activatoryes{activator_attrs: dict[str, object], activator_disabled: bool} (CMenuActivatorSlotData)none
defaultyes{} (CMenuDefaultSlotData)none

CMenuItem slots

SlotRequiredDataFallback
startno{} (CMenuItemStartSlotData)omitted
defaultyes{} (CMenuItemDefaultSlotData)none
descriptionno{} (CMenuItemDescriptionSlotData)omitted
endno{} (CMenuItemEndSlotData)omitted

CMenuCheckboxItem slots

SlotRequiredDataFallback
startno{} (CMenuItemStartSlotData)omitted
defaultyes{} (CMenuItemDefaultSlotData)none
descriptionno{} (CMenuItemDescriptionSlotData)omitted
endno{} (CMenuItemEndSlotData)omitted

CMenuRadioItem slots

SlotRequiredDataFallback
startno{} (CMenuItemStartSlotData)omitted
defaultyes{} (CMenuItemDefaultSlotData)none
descriptionno{} (CMenuItemDescriptionSlotData)omitted
endno{} (CMenuItemEndSlotData)omitted

CMenuRadioGroup slots

SlotRequiredDataFallback
labelno{} (CMenuRadioGroupLabelSlotData)omitted
defaultyes{} (CMenuRadioGroupDefaultSlotData)none

CMenuGroup slots

SlotRequiredDataFallback
labelyes{} (CMenuGroupLabelSlotData)none
defaultyes{} (CMenuGroupDefaultSlotData)none

CMenuSubmenu slots

SlotRequiredDataFallback
startno{} (CMenuSubmenuStartSlotData)omitted
labelyes{} (CMenuSubmenuLabelSlotData)none
descriptionno{} (CMenuSubmenuDescriptionSlotData)omitted
endno{} (CMenuSubmenuEndSlotData)Built-in direction-aware chevron.
defaultyes{} (CMenuSubmenuDefaultSlotData)none

Events

Component events are callback inputs supplied through $c-props. Native browser events remain available through Alpine @... attributes.

CMenu events

EventSignatureTrigger and timingDetailControlled and cancellation behavior
onOpenChange(requestedOpen: boolean, detail: CMenuOpenChangeDetail) => void (CMenuOpenChangeDetail)A visibility request occurs or a forced safety close changes effective open state.{reason, controlled, forced, source} (CMenuOpenChangeDetail)Uncontrolled requests commit before notification; controlled requests wait except forced closes.
onAction(value: string, detail: CMenuActionDetail) => void (CMenuActionDetail)An enabled valued command or choice activates.{kind, item, event, path} (CMenuActionDetail)Fires once after a choice-specific request and before the close request. Links and anonymous commands do not fire it.

CMenuCheckboxItem events

EventSignatureTrigger and timingDetailControlled and cancellation behavior
onCheckedChange(requestedChecked: boolean, detail: CMenuCheckedChangeDetail) => void (CMenuCheckedChangeDetail)An enabled checkbox item activates.{checked, previousChecked, controlled, item, event, path} (CMenuCheckedChangeDetail)Controlled items wait for owner acceptance.

CMenuRadioGroup events

EventSignatureTrigger and timingDetailControlled and cancellation behavior
onValueChange(requestedValue: string, detail: CMenuRadioChangeDetail) => void (CMenuRadioChangeDetail)A different enabled radio activates or the selected radio is structurally removed in either ownership mode.{value, previousValue, reason, controlled, item, event, path} (CMenuRadioChangeDetail)Controlled groups wait for owner acceptance.

Methods

-

CSS

CSS variables to theme the components. Set them on an ancestor or the component itself.

CMenu CSS variables

Apply these variables to CMenu or one of its ancestors.

VariableTypePurposeDefault
--cui-menu-backgroundcolorMenu surfaces.Canvas
--cui-menu-foregroundcolorItem text.CanvasText
--cui-menu-muted-colorcolorDescriptions, labels, and shortcuts.color-mix(in srgb, current foreground 72%, transparent)
--cui-menu-border-colorcolorSurface and separator boundaries.color-mix(in srgb, CanvasText 18%, transparent)
--cui-menu-border-widthlengthSurface boundary width.1px
--cui-menu-radiuslengthSurface corners.0.75rem
--cui-menu-shadowshadowRoot elevation.0 0.75rem 2rem rgb(15 23 42 / 18%)
--cui-menu-submenu-shadowshadowNested elevation.0 1rem 2.5rem rgb(15 23 42 / 22%)
--cui-menu-inline-sizelengthPreferred width.14rem
--cui-menu-min-inline-sizelengthMinimum useful inline submenu corridor.10rem
--cui-menu-max-inline-sizelengthViewport-safe width.calc(100dvi - 1rem)
--cui-menu-max-block-sizelengthScroll limit.min(24rem, calc(100dvb - 1rem))
--cui-menu-paddinglengthSurface edge spacing.0.375rem
--cui-menu-item-block-sizelengthItem minimum height.Size-derived.
--cui-menu-item-padding-inlinelengthItem inline spacing.Size-derived.
--cui-menu-item-gaplengthItem-region gap.0.625rem
--cui-menu-item-radiuslengthItem corners.0.5rem
--cui-menu-hover-backgroundcolorPointer hover fill.color-mix(in srgb, CanvasText 8%, transparent)
--cui-menu-focus-backgroundcolorFocused item fill.light-dark(#175cd3, #84adff)
--cui-menu-focus-foregroundcolorFocused item content.light-dark(#ffffff, #101828)
--cui-menu-focus-outline-colorcolorFocus-visible outline.light-dark(#175cd3, #84adff)
--cui-menu-danger-colorcolorDestructive item content.light-dark(#b42318, #fda29b)
--cui-menu-disabled-opacitynumberDisabled content opacity.0.5
--cui-menu-offsetlengthRoot anchor gap.0.375rem
--cui-menu-submenu-offsetlengthNested anchor gap.0.25rem
--cui-menu-durationtimeEntry and exit duration.120ms
--cui-menu-easingeasingEntry and exit easing.cubic-bezier(0.2, 0.8, 0.2, 1)

Attributes

HTML attributes defined on the components that you can refer to for CSS, inspection, and testing. Read-only.

CMenu attributes

AttributeElementTypeMeaning
popoverMenu surface"manual"Native top-layer presence with Citry dismissal.
roleMenu surface"menu"Application Menu composite.
aria-labelledbyMenu surfaceactivator IDREFNames the Menu from its Button.
data-openMenu surfacepresent | absentMirrors logical open ownership.
data-placementMenu surfacesix requested placement strings (CMenuPlacement)Requested logical placement, not collision result.
data-match-widthMenu surfacepresent | absentIndicates clamped activator-width matching.
data-sizeMenu surface"sm" | "md" | "lg" (CMenuSize)Effective item geometry.
aria-haspopupActivator Button"menu"Announces the controlled popup kind.
aria-controlsActivator ButtonIDREFReferences the Menu surface.
aria-expandedActivator Button"true" | "false"Mirrors logical open state.

CMenuItem attributes

AttributeElementTypeMeaning
roleItem root"menuitem"Command or native-link semantics.
aria-labelledbyItem rootowned label IDREFExact visible accessible name.
aria-describedbyItem rootdescription IDREF | absentOptional separate description.
aria-disabledItem root"true" | absentFocusable inactive item.
data-disabledItem rootpresent | absentDisabled styling mirror.
data-intentItem root"default" | "danger" (CMenuIntent)Visual emphasis.

CMenuCheckboxItem attributes

AttributeElementTypeMeaning
roleItem Button"menuitemcheckbox"Checkable command semantics.
aria-checkedItem Button"false" | "true" | "mixed"Effective checked value.
data-checkedItem Button"false" | "true" | "mixed"Styling mirror.

CMenuRadioItem attributes

AttributeElementTypeMeaning
roleItem Button"menuitemradio"Exclusive choice semantics.
aria-checkedItem Button"false" | "true"Effective group selection.
data-checkedItem Button"false" | "true"Styling mirror.

CMenuGroup attributes

AttributeElementTypeMeaning
roleGroup root"group"Owns grouped direct Menu items.
aria-labelledbyGroup rootgroup-label IDREFNames the group from its visible label.

CMenuRadioGroup attributes

AttributeElementTypeMeaning
roleRadio-group root"group"Owns exclusive radio items.
aria-labelledbyRadio-group rootlabel IDREF | absentNames the group when a label is supplied.

CMenuSeparator attributes

AttributeElementTypeMeaning
roleHorizontal rule"separator"Divides vertically stacked item families.

CMenuSubmenu attributes

AttributeElementTypeMeaning
roleWrapper / trigger / surface"none" / "menuitem" / "menu"Keeps the trigger and immediate sibling child Menu relationship.
aria-haspopupSubmenu trigger"menu"Announces the child Menu.
aria-controlsSubmenu triggerchild Menu IDREFReferences the child surface.
aria-expandedSubmenu trigger"true" | "false"Mirrors child open state.
data-openWrapper / child surfacepresent | absentMirrors logical child open ownership.

Selectors

Selectors for the DOM nodes in the components that you can use for CSS, inspection, and testing.

CMenu selectors

SelectorElementPurpose
[data-citry-ui-part="menu"]Root or submenu surfacePopover presence and collection focus.
[data-citry-ui-part="menu-item"]Command/link/check/radio semantic rootItem styling.
[data-citry-ui-part="menu-item-start"]Decorative start wrapperStart-region layout.
[data-citry-ui-part="menu-item-label"]Visible item labelLayout and exact accessible-name target.
[data-citry-ui-part="menu-item-description"]Optional descriptionSupporting text and accessible description.
[data-citry-ui-part="menu-item-end"]Decorative end wrapperShortcut/end-region layout.
[data-citry-ui-part="menu-choice-indicator"]Decorative choice indicatorChecked/radio marker.
[data-citry-ui-part="menu-group"]Labelled group rootGroup layout.
[data-citry-ui-part="menu-group-label"]Visible group labelExact group name and layout.
[data-citry-ui-part="menu-radio-group"]Radio-group rootExclusive choice grouping.
[data-citry-ui-part="menu-separator"]Horizontal separatorCollection division.
[data-citry-ui-part="menu-submenu"]Neutral submenu wrapperTrigger/surface ownership and inherited customization.
[data-citry-ui-part="menu-submenu-trigger"]Submenu ButtonChild Menu activation and placement anchor.

Interfaces

Aliases and data shapes referenced above.

Input type aliases

InterfaceDefinition
CClassValuestr | Mapping[str, bool] | Sequence[CClassValue]
CStyleValuestr | Mapping[str, str | int | float | bool | None] | Sequence[CStyleValue]
CMenuPlacementLiteral["top-start", "top", "top-end", "bottom-start", "bottom", "bottom-end"]
CMenuSizeLiteral["sm", "md", "lg"]
CMenuIntentLiteral["default", "danger"]
CMenuCheckedbool | Literal["mixed"]

CMenuActivatorSlotData

FieldTypeDefaultMeaning
activator_attrsdict[str, object]-Owned trigger marker, anchor identity, and synchronized ARIA relationships.
activator_disabledbool-Server-owned disabled value to forward through the activator component input.

CMenuDefaultSlotData

Empty dataclass: {}.

CMenuItemStartSlotData

Empty dataclass: {}.

CMenuItemDefaultSlotData

Empty dataclass: {}.

CMenuItemDescriptionSlotData

Empty dataclass: {}.

CMenuItemEndSlotData

Empty dataclass: {}.

CMenuGroupLabelSlotData

Empty dataclass: {}.

CMenuGroupDefaultSlotData

Empty dataclass: {}.

CMenuRadioGroupLabelSlotData

Empty dataclass: {}.

CMenuRadioGroupDefaultSlotData

Empty dataclass: {}.

CMenuSubmenuStartSlotData

Empty dataclass: {}.

CMenuSubmenuLabelSlotData

Empty dataclass: {}.

CMenuSubmenuDescriptionSlotData

Empty dataclass: {}.

CMenuSubmenuEndSlotData

Empty dataclass: {}.

CMenuSubmenuDefaultSlotData

Empty dataclass: {}.

CMenuOpenChangeDetail

FieldTypeDefaultMeaning
reason"trigger" | "escape" | "outside" | "focus-outside" | "tab" | "action" | "native" | "disabled" | "ancestor"-Cause of the requested or forced visibility change.
controlledboolean-Whether a valid client Boolean owns desired state.
forcedboolean-Whether native/structural safety overrides owner rejection.
sourceElement | EventTarget | null-Browser source associated with the change.

CMenuActionDetail

FieldTypeDefaultMeaning
kind"command" | "checkbox" | "radio"-Activated semantic item kind.
itemElement-Activated item root.
eventEvent-Native activation event.
pathlist[str]-Canonical ancestor-submenu path from the root.

CMenuCheckedChangeDetail

FieldTypeDefaultMeaning
checkedboolean-Requested checked value; activation moves mixed to true and otherwise negates the prior state.
previousCheckedboolean | "mixed" (CMenuChecked)-Prior effective value.
controlledboolean-Whether a valid client value owns state.
itemElement-Activated checkbox item.
eventEvent-Native activation event.
pathlist[str]-Canonical ancestor-submenu path.

CMenuRadioChangeDetail

FieldTypeDefaultMeaning
valuestring-Requested radio value.
previousValuestring-Prior selected value.
reason"activation" | "removal"-Request source.
controlledboolean-Whether a valid client value owns state.
itemElement | null-Activated item or null for structural removal.
eventEvent | null-Native activation event or null for removal.
pathlist[str]-Canonical ancestor-submenu path.

Translation keys

-