Overlays
Surfaces that float above the page or expand inline: modal dialogs over native <dialog>, disclosure wrappers over <details>, and the toast notifications API for transient feedback.
Modal
cui-modal is a thin behavioral wrapper around the native
<dialog> element. The dialog stays in light DOM, so you get the top
layer, focus trap, ESC handling, and form method="dialog" support for free.
The component adds declarative triggers, programmatic open() /
close(value?), and cancelable events.
A native <form method="dialog"> auto-closes the dialog when a
button inside is clicked. Each button's value attribute becomes the
returnValue on the close event. No JavaScript wiring required to capture
the choice.
API reference
| Attribute / property | Purpose |
|---|---|
open |
Reflects the modal's open state. Setting or removing toggles via the API. |
dismissible="false" |
Blocks ESC and backdrop-click dismissal. Programmatic close still works. |
modal.open() |
Opens the modal (delegates to dialog.showModal()). |
modal.close(value?) |
Closes, optionally setting returnValue. |
modal.toggle() |
Opens if closed, closes if open. |
modal.isOpen |
Boolean — current state. |
modal.returnValue |
The value set by the most recent close. |
modal.dialog |
Reference to the inner <dialog> element. |
| Markup attribute / class | Purpose |
|---|---|
data-cui-modal-target="<id>" |
On any clickable element. Opens the modal with that id. |
data-cui-modal-close[="<value>"] |
On any element inside a modal. Closes with optional returnValue. |
.cui-dialog · .cui-surface |
Both on the <dialog>. .cui-dialog handles
sizing, centering, and the backdrop; compose .cui-surface for the
card chrome (background, border, radius, shadow, padding). |
.cui-dialog-header · .cui-dialog-title ·
.cui-dialog-close · .cui-dialog-body ·
.cui-dialog-actions
|
Optional structural classes. |
data-size="sm | md | lg | full" |
On the <dialog>. Width preset. |
data-variant="sheet-end" |
On the <dialog>. Side-sheet drawer variant. |
| Event | Detail / behavior |
|---|---|
cui-modal-open |
Fired after the dialog opens. |
cui-modal-close |
{ returnValue }. Fired after the dialog closes. |
cui-modal-cancel |
Cancelable. Fired when ESC is pressed. Call preventDefault() to
block the close. |
| Variable | Default |
|---|---|
--cui-dialog-bg |
--cui-color-surface-raised |
--cui-dialog-border |
--cui-color-border |
--cui-dialog-radius |
--cui-radius-2 |
--cui-dialog-padding |
--cui-space-5 |
--cui-dialog-max-inline-size |
28rem |
--cui-dialog-shadow |
0 16px 48px rgba(0, 0, 0, 0.18) |
--cui-dialog-backdrop |
rgba(0, 0, 0, 0.4) |
Disclosure
cui-disclosure wraps a native <details> element and adds
consistent styling, animation, an attribute-reflected open state, and a
cui-disclosure-toggle event. Markup stays in light DOM, so headings remain
crawlable and the component still works without JavaScript.
Use the native name attribute on the inner <details> to
create an accordion — opening one sibling collapses the others.
How does Combat UI ship CSS?
Components inject their shadow styles through constructable stylesheets and
expose light-DOM hooks via the cui-* class system. Import
cui/index.css once at the app root, then drop components anywhere.
Does it work without JavaScript?
Yes. The component progressively enhances the native
<details>. Without JS the summary still toggles open and
closed; with JS you also get the open attribute on the host, custom events,
and an imperative API.
How do I make an accordion?
Give each inner <details> the same name
attribute. The browser will close siblings automatically when one opens.
Can I style the chevron?
Override --cui-disclosure-marker-color or replace the
::after rule in your own stylesheet.
Inline ghost disclosure
Use data-variant="ghost" for a borderless treatment that nests
inside other surfaces — table of contents, API references, etc.
Pairs with prose
The summary inherits text styles so it sits naturally beside paragraphs.
Remote-controlled disclosure
Any element with data-cui-disclosure-target="<id>"
toggles the matching disclosure. Add
data-cui-disclosure-action="open" or
"close" to force a specific state.
API reference
| Attribute / property | Purpose |
|---|---|
open |
Reflects the open state. Setting or removing toggles via the API. |
disclosure.open() |
Opens the inner <details>. |
disclosure.close() |
Closes the inner <details>. |
disclosure.toggle() |
Toggles the open state. |
disclosure.isOpen |
Boolean — current state. |
disclosure.details |
Reference to the inner <details> element. |
| Markup attribute / class | Purpose |
|---|---|
.cui-disclosure · .cui-surface |
Both on the <details> for a default card — compose
.cui-surface for the chrome. data-variant="ghost"
needs no surface. |
.cui-disclosure-body |
Optional wrapper around the disclosure content for consistent padding. |
.cui-stack |
Container that lays out adjacent disclosures with consistent spacing
(replaces the former .cui-disclosure-group). |
data-variant="ghost" |
On the <details>. Borderless treatment for in-flow disclosures. |
data-variant="stacked" |
On the .cui-stack. Joins adjacent disclosures with shared borders. |
data-tone="subtle" |
Muted surface treatment. |
name="<group>" |
Native attribute on <details>. Siblings sharing a name behave as an
accordion. |
data-cui-disclosure-target="<id>" |
On any clickable element. Toggles the matching disclosure. |
data-cui-disclosure-action="open | close" |
On a trigger. Forces the state instead of toggling. |
| Event | Detail / behavior |
|---|---|
cui-disclosure-toggle |
{ open }. Fires after the inner <details> toggles. |
| Variable | Default |
|---|---|
--cui-disclosure-bg |
--cui-color-surface-raised |
--cui-disclosure-color |
--cui-color |
--cui-disclosure-border |
--cui-color-border |
--cui-disclosure-radius |
--cui-radius-2 |
--cui-disclosure-padding-block |
--cui-space-3 |
--cui-disclosure-padding-inline |
--cui-space-4 |
--cui-disclosure-marker-color |
--cui-color-muted |
--cui-disclosure-hover-bg |
--cui-color-surface-muted |
Notifications
Two flavors share the same .cui-alert CSS: static inline alerts sit in the page
flow and need no JavaScript; floating toasts are created programmatically via the
toast() function and slotted into a cui-toast-region that
auto-creates itself on the first call. A data-cui-dismiss attribute on any
button inside a .cui-alert removes its alert.
Heads up
Maintenance starts at 22:00 UTC.
Saved.
Your changes are live.
Be careful
This branch has unsaved migrations.
Failed
Couldn't reach the upstream service. Retrying in 30s.
No JavaScript required for styling. The dismiss button works as soon as the toast module
is loaded — one global click listener handles [data-cui-dismiss] for any
.cui-alert on the page.
A cui-toast-region auto-mounts on first call. Place
<cui-toast-region placement="end-end"></cui-toast-region> in
your body to control the container yourself.
Default outline tone. Surface background with an accent stripe.
data-tone="filled". Subtle accent-tinted background.
data-tone="solid". Full accent background, high-contrast text.
Critical
Solid tone draws the eye for blocking errors.
API reference
| API | Purpose |
|---|---|
toast(options) |
Show a toast. Returns { element, dismiss(), update(opts) }. |
toast.info(msg, opts?) · .success · .warning ·
.danger
|
Sugar — sets variant for you. |
handle.dismiss() |
Cancel the auto-dismiss timer and remove the toast (with the exit animation). |
handle.update(opts) |
Patch any option (message, title, variant, tone, duration). Resets the timer. |
| Option | Default · Purpose |
|---|---|
message |
Required. Body text. |
title |
Optional bold heading. |
variant |
"info". One of info | success | warning | danger.
warning and danger render with role="alert".
|
tone |
None. filled or solid for heavier emphasis. |
duration |
4000 ms. Set 0 for sticky. |
placement |
"end-end" (bottom right). Six positions: start | end on the
block axis × start | center | end on the inline axis. |
| Markup attribute / class | Purpose |
|---|---|
.cui-alert · .cui-surface |
Container. Both classes on the element — compose .cui-surface
for chrome; the alert keeps its grid layout: icon · body · dismiss. |
.cui-alert-icon · .cui-alert-title ·
.cui-alert-message · .cui-alert-dismiss
|
Bespoke / JS-hook slots that keep a class. The body is a
.cui-stack and an actions row a .cui-cluster. |
data-variant="info | success | warning | danger" |
Sets the accent color and icon. |
data-tone="filled | solid" |
Visual weight. Omitted = outline tone. |
data-cui-dismiss |
On any element inside a .cui-alert. Clicks remove the alert with the exit
animation. |
cui-alert-dismiss event |
Bubbles from a .cui-alert after it's removed. |
| Variable | Default |
|---|---|
--cui-alert-bg |
--cui-color-surface-raised |
--cui-alert-color |
--cui-color |
--cui-alert-border |
--cui-color-border |
--cui-alert-accent |
Per-variant --cui-color-info | success | warning | danger |
--cui-alert-radius |
--cui-radius-2 |
--cui-toast-z-index |
1000 |
--cui-toast-padding |
--cui-space-4 |
--cui-toast-gap |
--cui-space-2 |