Theme
Version
GitHub PyPI Discord
On this page

Popover

Use CPopover for compact interactive content that belongs beside one Button without blocking the rest of the page. It enters the browser top layer, so it escapes clipping while keeping its original DOM, theme, and Form relationships.

Popover at a glance

Open each Button to compare concise content, a description, an explicit action, and trigger-width matching.

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

citry.register_library(citry_ui)


class PopoverAtAGlance(Component):
    template = """
      <section class="popover-sampler">
        <c-CPopover placement="bottom-start">
          <c-fill name="activator" data="{ activator_attrs }">
            <c-CButton c-attrs="activator_attrs">
              Europa
            </c-CButton>
          </c-fill>
          <c-fill name="title">Europa</c-fill>
          <c-fill name="default">An ocean world beneath fractured ice.</c-fill>
        </c-CPopover>
        <c-CPopover placement="top">
          <c-fill name="activator" data="{ activator_attrs }">
            <c-CButton variant="outline" c-attrs="activator_attrs">
              Enceladus
            </c-CButton>
          </c-fill>
          <c-fill name="title">Enceladus</c-fill>
          <c-fill name="description">Saturn II</c-fill>
          <c-fill name="default">Bright plumes erupt above its south pole.</c-fill>
        </c-CPopover>
        <c-CPopover placement="bottom-end" match_width>
          <c-fill name="activator" data="{ activator_attrs }">
            <c-CButton variant="ghost" c-attrs="activator_attrs">
              Titan atmosphere
            </c-CButton>
          </c-fill>
          <c-fill name="title">Titan</c-fill>
          <c-fill name="default">A dense nitrogen sky conceals methane lakes.</c-fill>
          <c-fill name="actions" data="{ close_attrs }">
            <c-CButton size="sm" c-attrs="close_attrs">
              Mark explored
            </c-CButton>
          </c-fill>
        </c-CPopover>
      </section>
    """

    css = """
      :where(.popover-sampler) {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 0.75rem;
        min-block-size: 12rem;
        padding-block: 2rem;
      }
    """


preview = PopoverAtAGlance()

preview  # noqa: B018

Build a Popover

Provide an activator, visible title, and body. Spread activator_attrs onto one native Button. CButton renders the required Button when href is omitted.

Inspect a moon
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class MoonInspector(Component):
    template = """
      <section class="moon-inspector">
        <p>Jovian system</p>
        <h2>Four worlds orbit a striped giant</h2>
        <c-CPopover>
          <c-fill name="activator" data="{ activator_attrs }">
            <c-CButton c-attrs="activator_attrs">
              Inspect Europa
            </c-CButton>
          </c-fill>
          <c-fill name="title">
            Europa
          </c-fill>
          <c-fill name="description">
            Jupiter II ยท mean radius 1,560.8 km
          </c-fill>
          <c-fill name="default">
            Its fractured water-ice crust may cover a global saltwater ocean.
          </c-fill>
        </c-CPopover>
      </section>
    """

    css = """
      :where(.moon-inspector) {
        display: grid;
        gap: 0.75rem;
        justify-items: start;
        max-inline-size: 42rem;
        color: CanvasText;
        font-family: ui-sans-serif, system-ui, sans-serif;
      }

      :where(.moon-inspector h2, .moon-inspector p) {
        margin: 0;
      }

      :where(.moon-inspector > p) {
        color: light-dark(#4338ca, #a5b4fc);
        font-size: 0.75rem;
        font-weight: 700;
        letter-spacing: 0.08em;
        text-transform: uppercase;
      }
    """


preview = MoonInspector()

preview  # noqa: B018
<c-CPopover>
  <c-fill name="activator" data="{ activator_attrs }">
    <c-CButton c-attrs="activator_attrs">
      Inspect Europa
    </c-CButton>
  </c-fill>
  <c-fill name="title">
    Europa
  </c-fill>
  <c-fill name="description">
    Jupiter II ยท mean radius 1,560.8 km
  </c-fill>
  <c-fill name="default">
    Its fractured water-ice crust may cover a global ocean.
  </c-fill>
</c-CPopover>

The title becomes the accessible name. Keep description concise; place structured or lengthy content in the body.

The activator must resolve to exactly one native Button. Do not use an anchor, generic element, or several controls. Disable that Button itself when opening is unavailable so native semantics, styling, and Popover behavior agree.

Add interactive content and actions

The body accepts native controls and nested components. Content stays mounted, so edits survive closing and reopening. Spread close_attrs only onto actions that should request closure.

Edit an orbit note
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class InteractivePopover(Component):
    template = """
      <section class="orbit-editor">
        <c-CPopover>
          <c-fill name="activator" data="{ activator_attrs }">
            <c-CButton c-attrs="activator_attrs">
              Adjust orbit note
            </c-CButton>
          </c-fill>
          <c-fill name="title">
            Orbit note
          </c-fill>
          <c-fill name="description">
            Changes stay in the native Form after closing.
          </c-fill>
          <c-fill name="default">
            <form id="orbit-form">
              <label for="orbit-label">Label</label>
              <input id="orbit-label" name="label" value="Perijove pass" />
              <label for="orbit-detail">Detail</label>
              <textarea id="orbit-detail" name="detail">Closest approach before sunrise.</textarea>
            </form>
          </c-fill>
          <c-fill name="actions" data="{ close_attrs }">
            <c-CButton variant="ghost" c-attrs="close_attrs">
              Cancel
            </c-CButton>
            <c-CButton c-attrs="close_attrs">
              Keep note
            </c-CButton>
          </c-fill>
        </c-CPopover>
      </section>
    """

    css = """
      :where(.orbit-editor) {
        min-block-size: 15rem;
        padding-block: 3rem;
      }

      :where(#orbit-form) {
        display: grid;
        gap: 0.5rem;
      }

      :where(#orbit-form input, #orbit-form textarea) {
        box-sizing: border-box;
        inline-size: 100%;
        padding: 0.5rem 0.625rem;
        border: 1px solid color-mix(in srgb, CanvasText 25%, transparent);
        border-radius: 0.5rem;
        background: Canvas;
        color: CanvasText;
        font: inherit;
      }
    """


preview = InteractivePopover()

preview  # noqa: B018
<c-fill name="actions" data="{ close_attrs }">
  <c-CButton variant="ghost" c-attrs="close_attrs">
    Cancel
  </c-CButton>
  <c-CButton c-attrs="close_attrs">
    Keep note
  </c-CButton>
</c-fill>

Popover never closes merely because body content was clicked. This keeps links, inputs, selectors, and nested components predictable.

Control visibility

Client inputs are passed in the browser through $c-props="{...}". Supply a Boolean open to control visibility. onOpenChange reports requests; update the owner value to accept one or leave it unchanged to decline it.

Control Popover visibility
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class ControlledPopover(Component):
    template = """
      <section
        class="controlled-popover"
        x-data="{ open: false, locked: false, lastReason: 'none' }"
      >
        <c-CPopover
          $c-props="{
            open,
            onOpenChange: (nextOpen, detail) => {
              lastReason = detail.reason;
              if (!locked) open = nextOpen;
            },
          }"
        >
          <c-fill name="activator" data="{ activator_attrs }">
            <c-CButton c-attrs="activator_attrs">
              Mission controls
            </c-CButton>
          </c-fill>
          <c-fill name="title">Mission controls</c-fill>
          <c-fill name="default">
            The owner may accept or decline every visibility request.
          </c-fill>
          <c-fill name="actions" data="{ close_attrs }">
            <c-CButton c-attrs="close_attrs">Close panel</c-CButton>
          </c-fill>
        </c-CPopover>
        <label>
          <input type="checkbox" x-model="locked" />
          Decline visibility requests
        </label>
        <output x-text="`Last request: ${lastReason}`"></output>
      </section>
    """

    css = """
      :where(.controlled-popover) {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 1rem;
        min-block-size: 12rem;
        padding-block: 2rem;
      }

      :where(.controlled-popover output) {
        flex-basis: 100%;
        color: color-mix(in srgb, CanvasText 70%, transparent);
      }
    """


preview = ControlledPopover()

preview  # noqa: B018
<c-CPopover
  $c-props="{
    open,
    onOpenChange: (nextOpen, detail) => {
      if (mayApply(nextOpen, detail)) open = nextOpen;
    },
  }"
>
  ...
</c-CPopover>

Without a client open, Popover commits user requests itself and then notifies. Removing the client value or passing null releases control without resetting the current state. Owner commits do not call back.

The callback detail identifies trigger, action, escape, outside, focus-outside, unavoidable external native changes, and safety closures caused by an ancestor or modal. It also includes controlled ownership, whether the close was forced, and the browser source.

Choose dismissal behavior

dismissible=True allows Escape, outside pointer, and focus-outside requests. The activator and controls carrying close_attrs always remain explicit paths.

Choose dismissal behavior
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class PopoverDismissal(Component):
    template = """
      <section class="dismissal-samples">
        <c-CPopover>
          <c-fill name="activator" data="{ activator_attrs }">
            <c-CButton c-attrs="activator_attrs">Dismissible</c-CButton>
          </c-fill>
          <c-fill name="title">Dismissible panel</c-fill>
          <c-fill name="default">Escape, outside pointer, or focus outside may close it.</c-fill>
        </c-CPopover>
        <c-CPopover c-dismissible="False">
          <c-fill name="activator" data="{ activator_attrs }">
            <c-CButton variant="outline" c-attrs="activator_attrs">
              Explicit action only
            </c-CButton>
          </c-fill>
          <c-fill name="title">Protected observation</c-fill>
          <c-fill name="default">Outside interaction leaves this panel open.</c-fill>
          <c-fill name="actions" data="{ close_attrs }">
            <c-CButton c-attrs="close_attrs">Acknowledge</c-CButton>
          </c-fill>
        </c-CPopover>
      </section>
    """

    css = """
      :where(.dismissal-samples) {
        display: flex;
        flex-wrap: wrap;
        gap: 0.75rem;
        min-block-size: 12rem;
        padding-block: 2rem;
      }
    """


preview = PopoverDismissal()

preview  # noqa: B018

Use dismissible=False when the user must choose an explicit action. Always provide a clear close path. In controlled mode, declining a passive request keeps the surface open and prevents that request from closing an ancestor.

Place the surface

Server inputs are passed in Python through <c-CPopover ... /> attributes or a CPopover(...) composition call. placement accepts top-start, top, top-end, bottom-start, bottom, or bottom-end. The same client input can change it in the browser. Use client matchWidth or server match_width when the surface should be at least as wide as the Button.

Place a Popover
Customize example
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class PopoverPlacements(Component):
    template = """
      <section
        class="placement-preview"
        x-data="{ placement: 'bottom-start', match_width: false }"
        @citry-ui-preview-controls.window="Object.assign($data, $event.detail)"
      >
        <c-CPopover
          $c-props="{ placement, matchWidth: match_width }"
        >
          <c-fill name="activator" data="{ activator_attrs }">
            <c-CButton c-attrs="activator_attrs">
              Position orbital summary
            </c-CButton>
          </c-fill>
          <c-fill name="title">Orbital summary</c-fill>
          <c-fill name="default">
            Collision fallback may flip the requested side near a viewport edge.
          </c-fill>
        </c-CPopover>
      </section>
    """

    css = """
      :where(.placement-preview) {
        display: grid;
        place-items: center;
        min-block-size: 22rem;
      }
    """


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

preview = PopoverPlacements()

preview  # noqa: B018

Placement is a preference. The browser may flip it near an edge. Start and end are logical, so they follow text direction. Change the activator gap with --cui-popover-offset; use --cui-popover-inline-size or style for width.

Popover uses native top-layer rendering and CSS anchors. It does not teleport under <body>, start a JavaScript geometry loop, or publish a generic placement engine.

Nest Popovers

Nested Popovers are valid inside a body or action. Escape and outside interaction affect only the top open layer.

Nest Popovers
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class NestedPopovers(Component):
    template = """
      <section class="nested-popovers">
        <c-CPopover>
          <c-fill name="activator" data="{ activator_attrs }">
            <c-CButton c-attrs="activator_attrs">Inspect Saturn</c-CButton>
          </c-fill>
          <c-fill name="title">Saturn</c-fill>
          <c-fill name="default">
            <p>Its rings contain countless ice-rich particles.</p>
            <c-CPopover placement="bottom-end">
              <c-fill name="activator" data="{ activator_attrs }">
                <c-CButton size="sm" variant="outline" c-attrs="activator_attrs">
                  Inspect ring gap
                </c-CButton>
              </c-fill>
              <c-fill name="title">Cassini Division</c-fill>
              <c-fill name="default">
                A broad region shaped by orbital resonance with Mimas.
              </c-fill>
            </c-CPopover>
          </c-fill>
        </c-CPopover>
      </section>
    """

    css = """
      :where(.nested-popovers) {
        min-block-size: 14rem;
        padding-block: 3rem;
      }
    """


preview = NestedPopovers()

preview  # noqa: B018

Closing a child returns focus to its child activator and leaves the parent open. Prefer shallow layers; a page section is clearer when content no longer feels compact or locally related.

Choose the right surface

Popover is a named, non-modal dialog with rich interactive content.

  • Use CDialog when a task blocks the page or needs contained focus.
  • Use CMenu for command/choice collection semantics and menu keyboard behavior.
  • Use CTooltip for brief noninteractive text shown by hover and focus.
  • Use CAlert for persistent status or feedback.

Adding a role to Popover content does not turn it into those components; each has different activation, focus, dismissal, and assistive-technology rules.

Theme and customize Popover

Set public --cui-popover-* variables on an ancestor or one surface. Use public part selectors for targeted regions.

Theme Popovers
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class CustomizedPopover(Component):
    template = """
      <section class="custom-popovers">
        <c-CPopover class_="aurora-popover">
          <c-fill name="activator" data="{ activator_attrs }">
            <c-CButton c-attrs="activator_attrs">Aurora palette</c-CButton>
          </c-fill>
          <c-fill name="title">Auroral oval</c-fill>
          <c-fill name="default">Charged particles paint green arcs above the poles.</c-fill>
        </c-CPopover>
        <c-CPopover class_="lunar-popover">
          <c-fill name="activator" data="{ activator_attrs }">
            <c-CButton variant="outline" c-attrs="activator_attrs">Lunar palette</c-CButton>
          </c-fill>
          <c-fill name="title">Lunar highlands</c-fill>
          <c-fill name="default">Ancient pale terrain surrounds younger dark maria.</c-fill>
        </c-CPopover>
      </section>
    """

    css = """
      :where(.custom-popovers) {
        display: flex;
        flex-wrap: wrap;
        gap: 0.75rem;
        min-block-size: 12rem;
        padding-block: 2rem;
      }

      :where(.aurora-popover) {
        --cui-popover-background: light-dark(#ecfdf5, #052e2b);
        --cui-popover-foreground: light-dark(#064e3b, #d1fae5);
        --cui-popover-border-color: light-dark(#6ee7b7, #34d399);
        --cui-popover-radius: 1.25rem;
      }

      :where(.lunar-popover) {
        --cui-popover-background: light-dark(#f8fafc, #172033);
        --cui-popover-foreground: light-dark(#1e293b, #f1f5f9);
        --cui-popover-border-color: light-dark(#94a3b8, #64748b);
        --cui-popover-shadow: 0 1.25rem 2.75rem rgb(15 23 42 / 30%);
      }
    """


preview = CustomizedPopover()

preview  # noqa: B018
.aurora-popover {
  --cui-popover-background: light-dark(#ecfdf5, #052e2b);
  --cui-popover-foreground: light-dark(#064e3b, #d1fae5);
  --cui-popover-border-color: light-dark(#6ee7b7, #34d399);
  --cui-popover-radius: 1.25rem;
}

class_, style, and attrs target the Popover surface. The activator remains owned by its own component. Unlayered consumer CSS overrides Citry UI defaults; named layers follow the site-wide layer-order contract.

The documented variables, selectors, and reflected attributes are public CSS API. .cui-* classes, --_cui-* variables, host markup, initialization markers, and anchor names are private.

Keyboard, focus, and forms

Opening focuses [autofocus], then the first tabbable descendant, then the surface itself. Popover does not trap Tab: the rest of the page remains available. Leaving a dismissible surface closes it after focus reaches the new destination.

Escape closes only the top open layer. Trigger, action, and Escape closure return focus to the activator when focus was inside. Outside closure preserves the browser's new focus destination.

Controls inside Popover retain native Form owners, values, reset, validation, and FormData behavior. Closing does not reset them because content stays in its original DOM and remains mounted.

Support narrow viewports and RTL

Use long RTL content
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class ResponsivePopover(Component):
    template = """
      <section class="responsive-popover" dir="rtl">
        <c-CPopover
          placement="bottom-start"
          style="--cui-popover-inline-size: 28rem"
        >
          <c-fill name="activator" data="{ activator_attrs }">
            <c-CButton c-attrs="activator_attrs">ุงูุชุญ ุณุฌู„ ุงู„ู…ุฑูŠุฎ</c-CButton>
          </c-fill>
          <c-fill name="title">ุณุฌู„ ุงู„ู…ุฑูŠุฎ ุงู„ุทูˆูŠู„</c-fill>
          <c-fill name="description">ู…ุญุชูˆู‰ ูŠุฎุชุจุฑ ุงู„ุงุชุฌุงู‡ ูˆุงู„ุนุฑุถ ุงู„ุถูŠู‚</c-fill>
          <c-fill name="default">
            <p>ูŠุจู‚ู‰ ุงู„ุณุทุญ ุฏุงุฎู„ ู…ุณุงุญุฉ ุงู„ุนุฑุถ ูˆูŠุชูŠุญ ุงู„ุชู…ุฑูŠุฑ ุนู†ุฏ ุงู„ุญุงุฌุฉ.</p>
            <p>OlympusMonsSummitTraverseObservationIdentifier2026</p>
            <p>ู‡ุจุทุช ุงู„ู…ุฑูƒุจุฉ ู‚ุฑุจ ุณู‡ู„ ุตุฎุฑูŠ ูˆุงุณุนุŒ ุซู… ุจุฏุฃุช ู‚ูŠุงุณ ุงู„ุบุจุงุฑ ูˆุงู„ุฑูŠุงุญ.</p>
          </c-fill>
        </c-CPopover>
      </section>
    """

    css = """
      :where(.responsive-popover) {
        min-block-size: 14rem;
        padding-block: 3rem;
      }
    """


preview = ResponsivePopover()

preview  # noqa: B018

Logical dimensions, viewport maxima, wrapping, and body scrolling keep content reachable at narrow widths and high zoom. The surface follows surrounding light/dark scope even in the top layer. Forced colors preserve its boundary; reduced-motion users receive immediate transitions.

Without JavaScript, an initially closed Popover stays hidden. An initially open Popover renders readable content in document flow, then activation upgrades it to the top layer.

API reference

Inputs

CPopover server inputs

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

InputTypeDefaultEffect
idstr | NonegeneratedSets native identity and title, description, and activator relationships.
openboolFalseSets the server-visible initial state and uncontrolled fallback. A valid client open input controls later state.
dismissibleboolTruePermits Escape, outside-pointer, and focus-outside close requests. Trigger and explicit actions remain available when false.
placement"top-start" | "top" | "top-end" | "bottom-start" | "bottom" | "bottom-end" (CPopoverPlacement)"bottom-start"Sets the preferred logical placement. Collision fallback may choose a different rendered side.
match_widthboolFalseMakes the Popover at least as wide as its activator.
class_str | Mapping[str, bool] | Sequence[CClassValue] | None (CClassValue)NoneAdds surface classes and merges them with attrs.
stylestr | Mapping[str, str | int | float | bool | None] | Sequence[CStyleValue] | None (CStyleValue)NoneAdds surface inline styles and merges them with attrs; Citry retains anchor ownership.
attrsMapping[str, object] | NoneNoneAdds allowed native, ARIA, Alpine, and data attributes to the Popover surface. Owned presence, semantics, focus, and relationships are rejected.

CPopover client inputs

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

InputTypeOmitted behaviorEffect
openboolean | nullReleases control and preserves the current committed state. null has the same effect.Controls visible state while supplied as a Boolean. An invalid value reports once and releases control from the current state.
dismissiblebooleanUses the server input.Controls passive Escape, outside-pointer, and focus-outside dismissal.
placement"top-start" | "top" | "top-end" | "bottom-start" | "bottom" | "bottom-end" (CPopoverPlacement)Uses the server input.Controls requested placement and data-placement.
matchWidthbooleanUses the server input.Controls trigger-width matching and data-match-width.
onOpenChangefunctionDoes not notify a component callback.Receives trigger, explicit-action, passive-dismissal, and external-native visibility requests.

Slots

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

CPopover slots

SlotRequiredDataFallback
activatoryes{activator_attrs: dict[str, object]} (CPopoverActivatorSlotData)none
titleyes{} (CPopoverTitleSlotData)none
descriptionno{} (CPopoverDescriptionSlotData)Omitted, with no aria-describedby.
defaultyes{} (CPopoverDefaultSlotData)none
actionsno{close_attrs: dict[str, object]} (CPopoverActionsSlotData)omitted

Events

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

CPopover events

EventSignatureTrigger and timingDetailControlled and cancellation behavior
onOpenChange(requestedOpen: boolean, detail: CPopoverOpenChangeDetail) => void (CPopoverOpenChangeDetail)An owned activator, explicit action, Escape, outside pointer, focus outside, or external native Popover operation requests a different state.{reason: "trigger" | "action" | "escape" | "outside" | "focus-outside" | "native" | "ancestor" | "modal", controlled: boolean, forced: boolean, source: Element | EventTarget | null} (CPopoverOpenChangeDetail)Uncontrolled requests commit before notification. Controlled requests wait for the owner. Owner commits do not notify.

Methods

-

CSS

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

CPopover CSS variables

Apply these variables to CPopover or one of its ancestors.

VariableTypePurposeDefault
--cui-popover-backgroundcolorSurface background.Canvas
--cui-popover-foregroundcolorSurface text and inherited control context.CanvasText
--cui-popover-border-colorcolorSurface boundary.Subtle CanvasText mix.
--cui-popover-border-widthlengthBoundary width.1px
--cui-popover-radiuslengthSurface corner radius.0.75rem
--cui-popover-shadowshadowTop-layer elevation.0 1rem 3rem rgb(15 23 42 / 22%)
--cui-popover-inline-sizelengthPreferred surface width.20rem
--cui-popover-max-inline-sizelengthMaximum responsive width.calc(100dvi - 1rem)
--cui-popover-max-block-sizelengthMaximum responsive height.calc(100dvb - 1rem)
--cui-popover-paddinglengthRegion inline and edge padding.1rem
--cui-popover-gaplengthGap between header, body, and actions.0.75rem
--cui-popover-offsetlengthGap between activator and surface.0.5rem
--cui-popover-durationtimeEntry and exit duration; reduced motion resolves to zero.140ms
--cui-popover-easingeasingEntry and exit easing.cubic-bezier(0.2, 0.8, 0.2, 1)
--cui-popover-focus-colorcolorSurface fallback focus outline.Highlight

Attributes

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

CPopover attributes

AttributeElementTypeMeaning
popoverSurface"manual"Uses native top-layer presence while Citry owns dismissal and control.
roleSurface"dialog"Identifies the named non-modal interactive surface.
aria-labelledbySurfaceIDREFReferences the required visible title.
aria-describedbySurfaceIDREF | absentReferences the optional concise description.
data-openSurfacepresent | absentMirrors logical visible ownership; absent during exit.
data-placementSurfacesix placement strings (CPopoverPlacement)Mirrors requested placement, not the collision fallback result.
data-match-widthSurfacepresent | absentIndicates trigger-width matching.
aria-haspopupActivator Button"dialog"Announces the kind of surface controlled by the Button.
aria-controlsActivator ButtonIDREFReferences the Popover surface.
aria-expandedActivator Button"true" | "false"Mirrors logical open state.

Selectors

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

CPopover selectors

SelectorElementPurpose
[data-citry-ui-part="popover"]SurfaceSemantic root, visual surface, and attrs destination.
[data-citry-ui-part="header"]HeaderTitle and optional-description layout.
[data-citry-ui-part="title"]TitleRequired visible accessible name.
[data-citry-ui-part="description"]DescriptionOptional concise supporting text.
[data-citry-ui-part="body"]BodyRequired rich interactive content region.
[data-citry-ui-part="actions"]ActionsOptional explicit-action row.

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]
CPopoverPlacementLiteral["top-start", "top", "top-end", "bottom-start", "bottom", "bottom-end"]

CPopoverActivatorSlotData

FieldTypeDefaultMeaning
activator_attrsdict[str, object]-Owned trigger marker, CSS anchor, aria-haspopup, aria-controls, and synchronized aria-expanded.

CPopoverTitleSlotData

Empty dataclass: {}.

CPopoverDescriptionSlotData

Empty dataclass: {}.

CPopoverDefaultSlotData

Empty dataclass: {}.

CPopoverActionsSlotData

FieldTypeDefaultMeaning
close_attrsdict[str, object]-Marks an explicit action control as a close request.

CPopoverOpenChangeDetail

FieldTypeDefaultMeaning
reason"trigger" | "action" | "escape" | "outside" | "focus-outside" | "native" | "ancestor" | "modal"-Source of the requested visibility change.
controlledboolean-Whether a valid client open Boolean currently owns state.
forcedboolean-Whether structural or modal safety required the component to close regardless of controlled ownership.
sourceElement | EventTarget | null-Browser source associated with the request.

Translation keys

-