Theme
Version
GitHub PyPI Discord
On this page

Toast

Use CToastRegion once near the end of an application root. It owns a persistent visible queue, polite and assertive announcers, remaining-time pause, action/dismiss semantics, and F6 focus access. Arrival never steals focus.

Toast at a glance

Intent controls presentation; priority independently controls announcement urgency.

Toast at a glance
Show code
from typing import Any

import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class ToastAtAGlance(Component):
    template = """
      <section class="toast-sampler">
        <p>These initial messages demonstrate presentation intent separately from urgency.</p>
        <c-CToastRegion c-items="items" c-duration_ms="0" c-limit="5" />
      </section>
    """

    def template_data(self, kwargs: Any, slots: Any) -> dict[str, object]:  # noqa: ARG002
        return {
            "items": tuple(
                citry_ui.CToastMessage(id=intent, title=title, intent=intent)
                for intent, title in (
                    ("neutral", "Draft retained"),
                    ("info", "Sync started"),
                    ("success", "Field note saved"),
                    ("warn", "Connection is slow"),
                    ("error", "Upload failed"),
                )
            )
        }

    css = ":where(.toast-sampler) { min-block-size:20rem; padding:1rem; }"


preview = ToastAtAGlance()
preview  # noqa: B018

Drive a reactive queue

Pass an Array of plain client message records. A stable id is queue identity. Remove IDs in onDismiss so expired or dismissed messages can later begin a fresh episode.

Add application notifications
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class ReactiveToastQueue(Component):
    template = """
      <section class="toast-example" x-data="{notices: [], next: 1}">
        <c-CButton @click="notices = [...notices, {
          id: `note-${next}`, title: `Observation ${next++} queued`, intent: 'info'
        }]">Add notification</c-CButton>
        <c-CToastRegion $c-props="{
          items: notices,
          onDismiss: id => notices = notices.filter(item => item.id !== id),
        }" />
      </section>
    """
    css = ":where(.toast-example) { min-block-size:16rem; padding:1rem; }"


preview = ReactiveToastQueue()
preview  # noqa: B018
<c-CToastRegion
  $c-props="{
    items: notices,
    onDismiss: (id) => notices = notices.filter(item => item.id !== id),
  }"
/>

Replace and deduplicate by ID

A retained ID updates in place. A material update restarts its lifetime and announces the replacement once; a byte-equivalent snapshot does neither.

Replace a message
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class ToastReplacement(Component):
    template = """
      <section class="toast-example" x-data="{progress: 20}">
        <c-CButton @click="progress = Math.min(100, progress + 20)">Advance upload</c-CButton>
        <c-CToastRegion c-duration_ms="0" $c-props="{items: [{
          id: 'upload', title: `Upload ${progress}% complete`, description: 'Aurora Ridge photos'
        }]}" />
      </section>
    """
    css = ":where(.toast-example) { min-block-size:16rem; padding:1rem; }"


preview = ToastReplacement()
preview  # noqa: B018

Pause remaining lifetime

The default lifetime is eight seconds. Set duration_ms=0 for persistent messages. Hover, focus within, document visibility, and an unrelated modal pause remaining time rather than starting a new timeout.

Pause a timed Toast
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class TimedToast(Component):
    template = """
      <section class="toast-example" x-data="{items: []}">
        <c-CButton @click="items = [{id: crypto.randomUUID(), title: 'Hover or focus to pause'}]">
          Start timed Toast
        </c-CButton>
        <c-CToastRegion c-duration_ms="4000" $c-props="{
          items,
          onDismiss: id => items = items.filter(item => item.id !== id),
        }" />
      </section>
    """
    css = ":where(.toast-example) { min-block-size:16rem; padding:1rem; }"


preview = TimedToast()
preview  # noqa: B018

Add one persistent action

onAction runs before action-caused dismissal. Set closeOnAction: false when the result should remain visible.

Act on a notification
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class PersistentToastAction(Component):
    template = """
      <section class="toast-example" x-data="{items: [], result: 'No action yet'}">
        <c-CButton @click="items = [{
          id: 'offline', title: 'Working offline', actionLabel: 'Retry',
          closeOnAction: false, durationMs: 0, intent: 'warn'
        }]">Show persistent action</c-CButton>
        <output x-text="result"></output>
        <c-CToastRegion $c-props="{
          items,
          onAction: () => result = 'Retry requested',
          onDismiss: id => items = items.filter(item => item.id !== id),
        }" />
      </section>
    """
    css = ":where(.toast-example) { display:grid; gap:.75rem; min-block-size:16rem; padding:1rem; }"


preview = PersistentToastAction()
preview  # noqa: B018

Limit the visible stack

Only the first limit unsuppressed messages render, announce, and run timers. Queued records start when promoted.

Queue beyond the visible limit
Show code
from typing import Any

import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class ToastVisibleLimit(Component):
    template = """
      <section class="toast-example">
        <p>Dismiss a visible message to promote the queued third item.</p>
        <c-CToastRegion c-items="items" c-limit="2" c-duration_ms="0" />
      </section>
    """

    def template_data(self, kwargs: Any, slots: Any) -> dict[str, object]:  # noqa: ARG002
        return {
            "items": tuple(
                citry_ui.CToastMessage(id=f"queue-{index}", title=f"Queue item {index}") for index in range(1, 4)
            )
        }

    css = ":where(.toast-example) { min-block-size:16rem; padding:1rem; }"


preview = ToastVisibleLimit()
preview  # noqa: B018

Reach notifications with F6

Unmodified F6 moves from the application to the first presented Toast. F6 inside returns to the recorded element. Tab remains ordinary and is never trapped.

Use the F6 focus route
Show code
from typing import Any

import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class ToastFocusAccess(Component):
    template = """
      <section class="toast-example">
        <p>Focus this page, then press F6 to enter the notification and F6 again to return.</p>
        <c-CButton>Focus before F6</c-CButton>
        <c-CToastRegion c-items="items" c-duration_ms="0" />
      </section>
    """

    def template_data(self, kwargs: Any, slots: Any) -> dict[str, object]:  # noqa: ARG002
        return {"items": (citry_ui.CToastMessage(id="f6", title="F6 reaches this message"),)}

    css = ":where(.toast-example) { min-block-size:16rem; padding:1rem; }"


preview = ToastFocusAccess()
preview  # noqa: B018

Pause behind a modal

A global Region becomes hidden, inert, and paused while an unrelated native modal is open. Use CAlert inside the modal for feedback that must be immediate there.

Keep modal feedback local
Show code
import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class ToastModalPause(Component):
    template = """
      <section class="toast-example" x-data="{items: [{id:'global', title:'Global queue waits', durationMs:0}]}">
        <c-CDialog>
          <c-fill name="activator" data="{ activator_attrs }">
            <c-CButton c-attrs="activator_attrs">Open modal task</c-CButton>
          </c-fill>
          <c-fill name="title">Modal-local feedback</c-fill>
          <c-fill name="default">
            <c-CAlert intent="info">Use Alert for immediate feedback inside this task.</c-CAlert>
          </c-fill>
        </c-CDialog>
        <c-CToastRegion $c-props="{items}" />
      </section>
    """
    css = ":where(.toast-example) { min-block-size:16rem; padding:1rem; }"


preview = ToastModalPause()
preview  # noqa: B018

Choose a logical corner

Placements are block-start-start, block-start-end, block-end-start, and block-end-end. Logical edges follow direction and writing mode.

Place Toasts in RTL
Show code
from typing import Any

import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class ToastPlacementRtl(Component):
    template = """
      <section class="toast-example" dir="rtl">
        <p>Logical start follows this RTL context.</p>
        <c-CToastRegion c-items="items" placement="block-end-start" c-duration_ms="0" />
      </section>
    """

    def template_data(self, kwargs: Any, slots: Any) -> dict[str, object]:  # noqa: ARG002
        return {"items": (citry_ui.CToastMessage(id="rtl", title="Logical start placement"),)}

    css = ":where(.toast-example) { min-block-size:16rem; padding:1rem; }"


preview = ToastPlacementRtl()
preview  # noqa: B018

Customize the surface

Use documented variables and part selectors. Unlayered application CSS wins; safe-area, narrow viewport, forced-colors, and print behavior stay owned.

Customize Toast
Show code
from typing import Any

import citry_ui
from citry import Component, citry

citry.register_library(citry_ui)


class CustomizedToast(Component):
    template = """
      <section class="toast-theme">
        <c-CToastRegion class_="polar-toast" c-items="items" c-duration_ms="0" />
      </section>
    """

    def template_data(self, kwargs: Any, slots: Any) -> dict[str, object]:  # noqa: ARG002
        return {
            "items": (
                citry_ui.CToastMessage(
                    id="polar",
                    title="Polar archive synchronized",
                    description="A scheme-aware brand adaptation.",
                    intent="success",
                ),
            )
        }

    css = """
      :where(.toast-theme) { color-scheme:light dark; min-block-size:16rem; padding:1rem; }
      :where(.polar-toast) {
        --cui-toast-background: light-dark(#eef8fb, #102a34);
        --cui-toast-foreground: light-dark(#17343e, #e6f7fb);
        --cui-toast-border-color: light-dark(#76b7c7, #5ea5b6);
        --cui-toast-radius: 1.25rem;
      }
    """


preview = CustomizedToast()
preview  # noqa: B018

Composition boundaries

Toast is brief global feedback, not a task surface, form-error relationship, arbitrary card renderer, or dismissible overlay. Use CAlert for persistent rich content, CDialog/CDrawer for tasks, and Field/Form errors beside their controls. V1 deliberately has no slots, imperative service, swipe, portal, or multi-action layout.

API reference

Inputs

CToastRegion server inputs

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

InputTypeDefaultEffect
itemsSequence[CToastMessage]"()"Ordered initial queue copied and validated once per render.
idstr | NonegeneratedSets exact Region identity and generated message relationships.
labelnon-empty str"Notifications"Names the Region.
messagesCToastMessages | None (CToastMessages)NoneOverrides catalog-backed dismiss and action-announcement patterns per field.
placement"block-start-start" | "block-start-end" | "block-end-start" | "block-end-end" (CToastPlacement)"block-end-end"Selects a logical viewport corner.
limitint (1..10)3Limits simultaneously presented messages.
duration_msint8000Sets default lifetime; zero is persistent and nonzero values are 1000..120000 milliseconds.
pause_on_hoverboolTruePauses remaining time while the viewport is hovered.
pause_on_focusboolTruePauses remaining time while focus is inside.
pause_on_hiddenboolTruePauses remaining time while the owner document is hidden.
class_str | Mapping[str, bool] | Sequence[CClassValue] | None (CClassValue)NoneMerges consumer classes onto the Region.
stylestr | Mapping[str, str | int | float | bool | None] | Sequence[CStyleValue] | None (CStyleValue)NoneMerges consumer inline styles onto the Region.
attrsMapping[str, object] | NoneNoneAdds allowed native and data attributes without replacing owned semantics, focus, live regions, or structure.

CToastRegion client inputs

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

InputTypeOmitted behaviorEffect
itemsArray<CToastClientMessage>Uses the copied server snapshot.Reconciles the complete ordered queue by canonical ID.
placement"block-start-start" | "block-start-end" | "block-end-start" | "block-end-end" (CToastPlacement)Uses the server fallback.Updates logical viewport placement.
limitinteger (1..10)Uses the server fallback.Changes visible capacity and promotes or queues messages.
durationMsintegerUses the server fallback.Sets default lifetime for messages without an override.
pauseOnHoverbooleanUses the server fallback.Controls hover pause.
pauseOnFocusbooleanUses the server fallback.Controls focus pause.
pauseOnHiddenbooleanUses the server fallback.Controls document-visibility pause.
onDismissfunctionDoes not notify a component callback.Receives timeout, explicit-dismiss, and action-dismiss completion.
onActionfunctionDoes not notify a component callback.Receives the optional action before action-caused dismissal.

Slots

-

Events

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

CToastRegion events

EventSignatureTrigger and timingDetailControlled and cancellation behavior
onDismiss(id: string, detail: CToastDismissDetail) => void (CToastDismissDetail)A presented message expires, is explicitly dismissed, or closes after its action.{reason: "timeout" | "dismiss" | "action", source: Element, message: CToastClientMessage} (CToastDismissDetail)Fires once after runtime removal; a producer should remove the ID to end suppression.
onAction(id: string, detail: CToastActionDetail) => void (CToastActionDetail)The optional action Button activates.{source: HTMLButtonElement, message: CToastClientMessage} (CToastActionDetail)Fires before an action-caused dismissal; stale work stops if the callback removes the Region.

Methods

-

CSS

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

CToastRegion CSS variables

Apply these variables to CToastRegion or one of its ancestors.

VariableTypePurposeDefault
--cui-toast-inline-offsetlengthLogical viewport inline offset.1rem
--cui-toast-block-offsetlengthLogical viewport block offset.1rem
--cui-toast-gaplengthVisible stack gap.0.75rem
--cui-toast-widthlengthPreferred visible width.22rem
--cui-toast-backgroundcolorMessage background.Canvas
--cui-toast-foregroundcolorMessage foreground and controls.CanvasText
--cui-toast-border-colorcolorMessage boundary.Subtle CanvasText mix.
--cui-toast-shadowshadowMessage elevation.0 1rem 3rem rgb(15 23 42 / 22%)
--cui-toast-radiuslengthMessage corners.0.75rem
--cui-toast-paddinglengthMessage padding.1rem
--cui-toast-accentcolorNeutral message accent.currentColor
--cui-toast-z-indexintegerNonmodal application stacking hint.1000

Attributes

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

CToastRegion attributes

AttributeElementTypeMeaning
data-placementRegionlogical placementMirrors the effective viewport corner.
data-pausedRegionpresent | absentPresent while timers are paused by hover, focus, visibility, or modality.
data-intentToastneutral | info | success | warn | errorMirrors presentation intent.
data-priorityToastpolite | assertiveMirrors announcement urgency.

Selectors

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

CToastRegion selectors

SelectorElementPurpose
[data-citry-ui-part="region"]Region sectionViewport and attrs destination.
[data-citry-ui-part="announcer-polite"]Hidden live regionSerialized polite announcements.
[data-citry-ui-part="announcer-assertive"]Hidden live regionSerialized assertive announcements.
[data-citry-ui-part="toast"]Presented groupFocusable message surface.
[data-citry-ui-part="content"]Content wrapperTitle and optional description.
[data-citry-ui-part="title"]TitleVisible accessible name.
[data-citry-ui-part="description"]DescriptionOptional relationship text.
[data-citry-ui-part="actions"]Controls wrapperOptional action and dismissal layout.
[data-citry-ui-part="action"]ButtonOptional one-action control.
[data-citry-ui-part="dismiss"]ButtonExplicit message dismissal.

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]
CToastIntentLiteral["neutral", "info", "success", "warn", "error"]
CToastPlacementLiteral["block-start-start", "block-start-end", "block-end-start", "block-end-end"]
CToastPriorityLiteral["polite", "assertive"]

CToastMessage

FieldTypeDefaultMeaning
idstr-Unique canonical queue identity.
titlestr-Visible and accessible plain-text name.
descriptionstr | None-Optional plain supporting text.
intentCToastIntent-Presentation intent independent from urgency.
priorityCToastPriority-Polite or assertive announcement channel.
duration_msint | None-Optional per-message lifetime; zero is persistent.
action_labelstr | None-Optional one-action Button label.
close_on_actionbool-Whether action completion dismisses the message.
dismissiblebool-Whether explicit dismissal is available.

CToastMessages

FieldTypeDefaultMeaning
dismiss_labelstr | NoneNoneOverrides the catalog-backed dismiss pattern and must contain {title}.
action_announcementstr | NoneNoneOverrides the catalog-backed action announcement and must contain {action_label}.

CToastClientMessage

FieldTypeDefaultMeaning
idstring-Unique canonical queue identity.
titlestring-Visible and accessible plain-text name.
descriptionstring | null-Optional supporting text.
intentCToastIntent-Presentation intent.
priorityCToastPriority-Announcement channel.
durationMsinteger | null-Optional lifetime override.
actionLabelstring | null-Optional one-action label.
closeOnActionboolean-Whether action dismisses.
dismissibleboolean-Whether explicit dismissal is available.

CToastDismissDetail

FieldTypeDefaultMeaning
reason"timeout" | "dismiss" | "action"-Runtime removal reason.
sourceElement-Browser source associated with dismissal.
messageCToastClientMessage-Canonical public message snapshot.

CToastActionDetail

FieldTypeDefaultMeaning
sourceHTMLButtonElement-Activated action Button.
messageCToastClientMessage-Canonical public message snapshot.

Translation keys

Catalog keys used by this family. An explicit component input or slot listed in Override takes precedence over the catalog for that instance.

CToastRegion translation keys

KeyPurposeVariablesOverrideBrowser updates
citry-ui-toast-regionNames the notification region.Nonelabel input$c-tr updates aria-label.
citry-ui-toast-dismissNames each toast dismiss control.title: strmessages.dismiss_label$c-tr handles initial items; i18n.bind() handles browser-created items.
citry-ui-toast-action-availableAnnounces that the toast exposes an action.action_label: strmessages.action_announcementOne-shot i18n.tr() when the toast is added.