@antadesign/anta 0.3.18 → 0.3.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -242
- package/dist/anta_helpers.d.ts +2 -2
- package/dist/components/Banner.d.ts +3 -3
- package/dist/components/Button.d.ts +1 -1
- package/dist/components/Calendar.d.ts +11 -12
- package/dist/components/Card.d.ts +1 -1
- package/dist/components/Card.js +1 -1
- package/dist/components/Checkbox.d.ts +8 -9
- package/dist/components/Dialog.d.ts +1 -1
- package/dist/components/Expander.d.ts +5 -6
- package/dist/components/Input.d.ts +6 -3
- package/dist/components/Input.js +3 -1
- package/dist/components/InputAutocomplete.d.ts +8 -8
- package/dist/components/InputDate.css +1 -0
- package/dist/components/InputDate.d.ts +1 -0
- package/dist/components/InputDate.js +5 -5
- package/dist/components/InputTime.d.ts +3 -3
- package/dist/components/InputTime.js +1 -1
- package/dist/components/Menu.d.ts +7 -3
- package/dist/components/Menu.js +3 -2
- package/dist/components/MenuItem.d.ts +1 -1
- package/dist/components/MenuItem.js +1 -1
- package/dist/components/Progress.d.ts +1 -1
- package/dist/components/RadioGroup.d.ts +12 -13
- package/dist/components/Select.d.ts +32 -38
- package/dist/components/Select.js +9 -13
- package/dist/components/SelectFaceted.d.ts +28 -33
- package/dist/components/SelectFaceted.js +29 -23
- package/dist/components/Slider.d.ts +5 -5
- package/dist/components/Switch.d.ts +4 -4
- package/dist/components/Tabs.d.ts +7 -7
- package/dist/components/Title.d.ts +1 -1
- package/dist/components/Toaster.d.ts +1 -1
- package/dist/components/Tooltip.d.ts +1 -1
- package/dist/components/select-parts.css +1 -0
- package/dist/elements/a-card.css +1 -1
- package/dist/elements/a-dialog.css +1 -1
- package/dist/elements/a-expander.css +1 -1
- package/dist/elements/a-expander.d.ts +1 -0
- package/dist/elements/a-expander.js +12 -2
- package/dist/elements/a-input.css +1 -1
- package/dist/elements/a-menu.js +12 -0
- package/dist/elements/a-progress.css +1 -1
- package/dist/elements/a-slider.css +1 -1
- package/dist/elements/a-tabs.css +1 -1
- package/dist/elements/a-tabs.d.ts +3 -0
- package/dist/elements/a-tabs.js +40 -1
- package/dist/elements/a-tooltip.css +1 -1
- package/dist/elements/a-tooltip.d.ts +1 -1
- package/dist/elements/a-tooltip.js +1 -1
- package/dist/elements/index.d.ts +1 -1
- package/dist/general_types.d.ts +24 -22
- package/dist/index.d.ts +3 -3
- package/dist/jsx-runtime.d.ts +9 -0
- package/docs/accessibility.md +34 -0
- package/docs/changelog.md +561 -0
- package/docs/colors.md +42 -0
- package/docs/comparison.md +28 -0
- package/docs/components/banner.md +267 -0
- package/docs/components/button.md +603 -0
- package/docs/components/card.md +321 -0
- package/docs/components/checkbox.md +296 -0
- package/docs/components/dialog.md +314 -0
- package/docs/components/expander.md +526 -0
- package/docs/components/icon.md +155 -0
- package/docs/components/input-autocomplete.md +100 -0
- package/docs/components/input-date.md +401 -0
- package/docs/components/input-time.md +174 -0
- package/docs/components/input.md +610 -0
- package/docs/components/loader.md +139 -0
- package/docs/components/menu.md +511 -0
- package/docs/components/progress.md +182 -0
- package/docs/components/radio.md +368 -0
- package/docs/components/select-faceted.md +451 -0
- package/docs/components/select.md +926 -0
- package/docs/components/slider.md +147 -0
- package/docs/components/switch.md +198 -0
- package/docs/components/tabs.md +471 -0
- package/docs/components/tag.md +185 -0
- package/docs/components/text.md +148 -0
- package/docs/components/title.md +146 -0
- package/docs/components/toaster.md +313 -0
- package/docs/components/tooltip.md +177 -0
- package/docs/credits.md +18 -0
- package/docs/index.md +52 -0
- package/docs/install-config.md +240 -0
- package/docs/normalization.md +102 -0
- package/docs/overview.md +10 -0
- package/docs/packages/stickers.md +187 -0
- package/docs/packages/table.md +153 -0
- package/docs/theming.md +13 -0
- package/package.json +10 -6
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# Dialog
|
|
2
|
+
|
|
3
|
+
A modal dialog: content that opens above a dimmed overlay and holds focus until
|
|
4
|
+
it's dismissed. The `Dialog` wrapper renders an `<a-dialog>` whose shadow DOM
|
|
5
|
+
wraps a native `<dialog>`, so the top layer (which escapes any `overflow` or
|
|
6
|
+
stacking context on the page), the focus trap, focus return on close, the
|
|
7
|
+
backdrop, and Esc all come from the platform.
|
|
8
|
+
|
|
9
|
+
It has three optional zones: a `header`, the body (`children`), and a `footer`,
|
|
10
|
+
plus a close button in the top-right corner. Set `position` to make it an edge
|
|
11
|
+
drawer instead.
|
|
12
|
+
|
|
13
|
+
## Zones
|
|
14
|
+
|
|
15
|
+
`header`, `children` (the body), and `footer` are the three zones. The body
|
|
16
|
+
scrolls when it overflows the available height; the header and footer stay
|
|
17
|
+
pinned. Each zone is optional: drop `header` for a chromeless sheet, or `footer`
|
|
18
|
+
when there are no actions. The footer lays its content out in a right-aligned
|
|
19
|
+
row.
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
const [open, setOpen] = useState(false)
|
|
23
|
+
|
|
24
|
+
<Button onClick={() => setOpen(true)}>Open dialog</Button>
|
|
25
|
+
<Dialog
|
|
26
|
+
open={open}
|
|
27
|
+
onStateChange={(_e, { next }) => setOpen(next)}
|
|
28
|
+
header={<Title level={4}>Enable telemetry?</Title>}
|
|
29
|
+
footer={
|
|
30
|
+
<>
|
|
31
|
+
<Button onClick={() => setOpen(false)}>Not now</Button>
|
|
32
|
+
<Button priority="primary" onClick={() => setOpen(false)}>Enable</Button>
|
|
33
|
+
</>
|
|
34
|
+
}
|
|
35
|
+
>
|
|
36
|
+
<Text>Anonymous usage data helps us find the bugs that matter.</Text>
|
|
37
|
+
</Dialog>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Position
|
|
41
|
+
|
|
42
|
+
`position` places the dialog. `center` (the default) is a modal centered on the
|
|
43
|
+
viewport, keeping a 32px gap from every edge. `left`, `right`, `top`, and
|
|
44
|
+
`bottom` turn it into an edge drawer: `left` / `right` run the full viewport
|
|
45
|
+
height and slide in from the side; `top` / `bottom` span the full width and slide
|
|
46
|
+
down / up. A drawer sits flush against its edge but keeps 32px of overlay on the
|
|
47
|
+
far side, so the backdrop stays visible however wide or tall you make it. Drawers
|
|
48
|
+
square their corners against the edges they touch. `fullscreen` fills the whole
|
|
49
|
+
viewport: no gap, no radius.
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
<Dialog position="left" open={open} onStateChange={…}>…</Dialog> {/* full-height left drawer */}
|
|
53
|
+
<Dialog position="right" open={open} onStateChange={…}>…</Dialog> {/* full-height right drawer */}
|
|
54
|
+
<Dialog position="top" open={open} onStateChange={…}>…</Dialog> {/* full-width top drawer */}
|
|
55
|
+
<Dialog position="bottom" open={open} onStateChange={…}>…</Dialog> {/* full-width bottom sheet */}
|
|
56
|
+
<Dialog position="fullscreen" open={open} onStateChange={…}>…</Dialog> {/* edge-to-edge, no gap */}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
A drawer's thickness is the axis it slides along: a `left` / `right` drawer's
|
|
60
|
+
width (380px by default), a `top` / `bottom` drawer's height (its content). The
|
|
61
|
+
cross axis fills the viewport, so the far-side space is `100vw`/`100dvh` minus
|
|
62
|
+
that thickness. Resize it with a position-scoped `::part(dialog)` rule (see
|
|
63
|
+
[Styling](#styling)).
|
|
64
|
+
|
|
65
|
+
## Rounding corners
|
|
66
|
+
|
|
67
|
+
The centered modal is rounded by default (10px) and drawers are square. `round`
|
|
68
|
+
overrides that. A **side keyword** rounds only that edge's two corners, which
|
|
69
|
+
pairs with a drawer's exposed edge: a bottom sheet takes `round="top"`, a right
|
|
70
|
+
drawer `round="left"`.
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
<Dialog round={0}>…</Dialog> {/* unrounded centered modal */}
|
|
74
|
+
<Dialog position="bottom" round="top">…</Dialog> {/* round the sheet's top edge */}
|
|
75
|
+
<Dialog position="left" round="right">…</Dialog> {/* round a left drawer's inner edge */}
|
|
76
|
+
<Dialog round={24}>…</Dialog> {/* all corners, 24px */}
|
|
77
|
+
<Dialog round="12px 12px 0 0">…</Dialog> {/* any border-radius shorthand */}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
A `number` or CSS string sets the amount on all corners; the string may be a full
|
|
81
|
+
`border-radius` shorthand, so `round="12px 12px 0 0"` rounds just the top by 12px
|
|
82
|
+
(a side keyword with a custom amount). Bare `round` rounds all corners at the
|
|
83
|
+
default 10px, handy to round an otherwise-square drawer. Authoring the raw
|
|
84
|
+
`<a-dialog>`, a single length works through the typed `attr()` (`round="20px"`);
|
|
85
|
+
shorthands go through the wrapper's `round` prop.
|
|
86
|
+
|
|
87
|
+
## Close button
|
|
88
|
+
|
|
89
|
+
`closable` controls one thing: whether the top-right ✕ is present (on by
|
|
90
|
+
default). The ✕ is just one way to close the dialog, alongside Esc, a backdrop
|
|
91
|
+
click, any `data-dialog-close` / footer action, and your own code. So
|
|
92
|
+
`closable={false}` only removes the ✕ (say, when the footer already carries a
|
|
93
|
+
Cancel button); the dialog stays closable through those other paths. The ✕
|
|
94
|
+
itself is a keyboard-focusable `Button` that requests a close like Esc and the
|
|
95
|
+
backdrop do.
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
<Dialog closable={false} header="Rename file" footer={…}>…</Dialog>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Dismiss
|
|
102
|
+
|
|
103
|
+
"Dismissible" means light-dismiss: a backdrop click or Esc closes the dialog,
|
|
104
|
+
both on by default. `persistent` turns only that off. The dialog stays closable
|
|
105
|
+
through explicit controls (the ✕ when `closable`, a footer action, your own
|
|
106
|
+
code); `persistent` doesn't make it un-closable, it stops an accidental click or
|
|
107
|
+
stray Esc from dismissing it. Reach for it on an alert or destructive confirm
|
|
108
|
+
that should be answered deliberately.
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
<Dialog
|
|
112
|
+
persistent
|
|
113
|
+
header="Delete account?"
|
|
114
|
+
footer={
|
|
115
|
+
<>
|
|
116
|
+
<Button onClick={cancel}>Cancel</Button>
|
|
117
|
+
<Button priority="primary" tone="critical" onClick={confirm}>Delete</Button>
|
|
118
|
+
</>
|
|
119
|
+
}
|
|
120
|
+
>
|
|
121
|
+
<Text>This permanently removes your account and all its data.</Text>
|
|
122
|
+
</Dialog>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
A backdrop click dismisses only when the press **and** the release both land on
|
|
126
|
+
the backdrop. Selecting text and releasing outside the dialog won't close it,
|
|
127
|
+
and neither will clicking an option in a `Menu` or `Select` whose dropdown
|
|
128
|
+
overflows the dialog's box: that click lands on the popover. So a dialog can
|
|
129
|
+
hold interactive content that spills past its edges.
|
|
130
|
+
|
|
131
|
+
## Triggers
|
|
132
|
+
|
|
133
|
+
Give the dialog a unique `name`, and any element with `data-dialog-open="{name}"`
|
|
134
|
+
opens it while `data-dialog-close="{name}"` closes it, with no state or handler.
|
|
135
|
+
This helps when the trigger sits far from the dialog in the markup. It's for
|
|
136
|
+
hand-authored or vanilla usage; controlled mode (below) ignores `name` and you
|
|
137
|
+
drive `open` yourself.
|
|
138
|
+
|
|
139
|
+
```html
|
|
140
|
+
<a-button tabindex="0" data-dialog-open="settings">Settings</a-button>
|
|
141
|
+
|
|
142
|
+
<a-dialog name="settings">
|
|
143
|
+
<h2 slot="header">Settings</h2>
|
|
144
|
+
<p>…</p>
|
|
145
|
+
<a-button tabindex="0" slot="footer" data-dialog-close="settings">Done</a-button>
|
|
146
|
+
</a-dialog>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Controlled vs uncontrolled
|
|
150
|
+
|
|
151
|
+
Open state follows Anta's shared **state contract**. Leave it *uncontrolled*
|
|
152
|
+
(seed with `defaultOpen`, or drive it with the `name` triggers) and the dialog
|
|
153
|
+
owns open/close. Pass `open` + `onStateChange` to *control* it: the dialog
|
|
154
|
+
follows `open` only, and every user dismiss (Esc, backdrop, close button)
|
|
155
|
+
*requests* a change that you apply to accept or ignore to reject. The wrapper
|
|
156
|
+
takes booleans and the element takes a `state` string enum; the wrapper maps
|
|
157
|
+
between them:
|
|
158
|
+
|
|
159
|
+
| | `<Dialog>` (JSX) | `<a-dialog>` (element) |
|
|
160
|
+
|---|---|---|
|
|
161
|
+
| **Uncontrolled** (element owns it) | `defaultOpen` / `name` triggers | `default-state="open" \| "closed"` / `name` |
|
|
162
|
+
| **Controlled** (you own it) | `open` + `onStateChange` | `state="open" \| "closed"` + `statechange` |
|
|
163
|
+
| **Change event** | `onStateChange(event, { next, prev })`, booleans | `statechange`, a `CustomEvent<{ next, prev }>` of `'open'` / `'closed'` |
|
|
164
|
+
|
|
165
|
+
`statechange` is **cancelable** and fires *before* the dialog applies the
|
|
166
|
+
change. Uncontrolled, call `event.preventDefault()` to veto a dismiss (e.g.
|
|
167
|
+
confirm before discarding unsaved edits). Controlled, the element never
|
|
168
|
+
self-applies; apply `detail.next` to `open` to accept, or do nothing to reject.
|
|
169
|
+
|
|
170
|
+
In controlled mode the element keeps the dialog in sync with your `open` value,
|
|
171
|
+
even against a native close it didn't route (a `<form method="dialog">` submit,
|
|
172
|
+
or calling `close()` on the underlying `<dialog>`). Such a close re-asserts your
|
|
173
|
+
state: the dialog reopens if `open` is still true. Drive closing through your
|
|
174
|
+
state, setting `open` to `false` in `onStateChange`.
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
// controlled
|
|
178
|
+
const [open, setOpen] = useState(false)
|
|
179
|
+
<Button onClick={() => setOpen(true)}>Open</Button>
|
|
180
|
+
<Dialog open={open} onStateChange={(_e, { next }) => setOpen(next)}>…</Dialog>
|
|
181
|
+
|
|
182
|
+
// uncontrolled, veto the close while there are unsaved changes
|
|
183
|
+
<Dialog defaultOpen onStateChange={(e, { next }) => {
|
|
184
|
+
if (!next && unsaved) e.preventDefault()
|
|
185
|
+
}}>…</Dialog>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Focus
|
|
189
|
+
|
|
190
|
+
On open, focus goes to the **dialog itself** rather than a control inside it. So
|
|
191
|
+
the user Tabs to reach the first field, focus never lands on the ✕ or a
|
|
192
|
+
destructive button, and it works the same with or without a close button or
|
|
193
|
+
focusable content. A screen reader announces the dialog by its `header` text
|
|
194
|
+
(mirrored to the dialog's accessible name), and the ✕ is the last tab stop.
|
|
195
|
+
|
|
196
|
+
To focus a specific control on open instead (a search field, say), put
|
|
197
|
+
`autofocus` on it; the dialog honors that and skips the default. On close, focus
|
|
198
|
+
returns to whatever opened the dialog, from the native `<dialog>`.
|
|
199
|
+
|
|
200
|
+
```tsx
|
|
201
|
+
<Dialog header="Rename" footer={…}>
|
|
202
|
+
<Input label="Name" autofocus /> {/* focus the field on open */}
|
|
203
|
+
</Dialog>
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Props
|
|
207
|
+
|
|
208
|
+
| Prop | Type | Default | Description |
|
|
209
|
+
|------|------|---------|-------------|
|
|
210
|
+
| `children?` | ReactNode | — | The dialog body. Scrolls when it overflows the available height. |
|
|
211
|
+
| `closable?` | boolean | true | Whether the top-right ✕ button is present. It's one way to close the dialog,
|
|
212
|
+
alongside Esc, the backdrop, a `data-dialog-close` / footer action, and your
|
|
213
|
+
own code; `false` removes the ✕ without making the dialog unclosable. |
|
|
214
|
+
| `defaultOpen?` | boolean | — | Initial open state for the uncontrolled case (read once on mount). |
|
|
215
|
+
| `footer?` | ReactNode | — | Footer content, usually the action buttons. Rendered in the bottom zone as
|
|
216
|
+
a right-aligned row (wraps under pressure). Omit for none. |
|
|
217
|
+
| `header?` | ReactNode | — | Header content: a title, or any node. Rendered in the top zone. Omit for a
|
|
218
|
+
chromeless dialog with only a body. |
|
|
219
|
+
| `name?` | string | — | Uncontrolled trigger name. Any element with `data-dialog-open="{name}"`
|
|
220
|
+
opens this dialog, `data-dialog-close="{name}"` closes it. A convenience for
|
|
221
|
+
triggers rendered elsewhere; ignored in controlled mode. |
|
|
222
|
+
| `onStateChange?` | (event, detail) => void | — | Fired before the open state changes, on every user open or dismiss.
|
|
223
|
+
`event` is the cancelable `statechange`: call `event.preventDefault()` to
|
|
224
|
+
veto an *uncontrolled* transition. `detail.next` is the requested open
|
|
225
|
+
state, `detail.prev` the current one (booleans). In controlled mode, apply
|
|
226
|
+
`detail.next` to `open` to accept, or do nothing to reject. |
|
|
227
|
+
| `open?` | boolean | — | Controlled open state. When provided, the consumer owns open/close: the
|
|
228
|
+
dialog only follows this prop, and every user dismiss (Esc, backdrop, close
|
|
229
|
+
button) *requests* a change via `onStateChange` (reject by not updating).
|
|
230
|
+
Leave undefined for uncontrolled. |
|
|
231
|
+
| `persistent?` | boolean | — | Turn off light-dismiss: a backdrop click and Esc no longer close the dialog.
|
|
232
|
+
It stays closable through explicit controls (the ✕ when `closable`, a footer
|
|
233
|
+
action, your own code); `persistent` isn't "un-closable", it stops an
|
|
234
|
+
accidental click or stray Esc from dismissing. For an alert / confirm that
|
|
235
|
+
should be answered deliberately. Omit for the default dismissible behavior. |
|
|
236
|
+
| `position?` | 'center' \| 'left' \| 'right' \| 'top' \| 'bottom' \| 'fullscreen' | 'center' | Placement. `center` (the default) is a centered modal; `left` / `right` /
|
|
237
|
+
`top` / `bottom` turn it into an edge drawer (full height for left/right,
|
|
238
|
+
full width for top/bottom); `fullscreen` fills the whole viewport with no
|
|
239
|
+
edge gap or corner radius. |
|
|
240
|
+
| `round?` | boolean \| number \| 'top' \| 'right' \| 'bottom' \| 'left' \| (string & {}) | — | Corner radius. A side keyword — `'top'` / `'right'` / `'bottom'` / `'left'` —
|
|
241
|
+
rounds only that edge's two corners (at `--dialog-radius`), which pairs with
|
|
242
|
+
a drawer's exposed edge (a bottom sheet → `'top'`, a right drawer → `'left'`).
|
|
243
|
+
A `number` (px) or CSS string rounds all corners at that value; the string
|
|
244
|
+
may be a full `border-radius` shorthand (`'12px 12px 0 0'`). `true` rounds all
|
|
245
|
+
corners at `--dialog-radius`. Omit for the position default: `center` is
|
|
246
|
+
rounded, drawers and `fullscreen` are square. |
|
|
247
|
+
|
|
248
|
+
Use the web component directly when you are not using React or Preact and a native control does not fit.
|
|
249
|
+
|
|
250
|
+
Slots hold the header, footer, and close control. Add `tabindex="0"` to every
|
|
251
|
+
raw `<a-button>`.
|
|
252
|
+
|
|
253
|
+
```html
|
|
254
|
+
<a-button role="button" tabindex="0" data-dialog-open="invite"><a-button-label>Invite teammates</a-button-label></a-button>
|
|
255
|
+
<a-dialog name="invite">
|
|
256
|
+
<a-title slot="header" level="4" role="heading" aria-level="4">Invite teammates</a-title>
|
|
257
|
+
<a-text>Send an invite link to your team.</a-text>
|
|
258
|
+
<a-button slot="footer" role="button" tabindex="0" data-dialog-close="invite"><a-button-label>Cancel</a-button-label></a-button>
|
|
259
|
+
<a-button slot="footer" role="button" tabindex="0" priority="primary" data-dialog-close="invite"><a-button-label>Send</a-button-label></a-button>
|
|
260
|
+
<a-button slot="close" role="button" tabindex="0" priority="tertiary" size="large"
|
|
261
|
+
aria-label="Close" data-custom-event="closerequest"><a-icon shape="x" aria-hidden="true"></a-icon></a-button>
|
|
262
|
+
</a-dialog>
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Reach for the props first: **`position`** (modal, drawer edge, or fullscreen),
|
|
266
|
+
**`closable`** (the ✕), **`persistent`** (light-dismiss off), **`round`** (corner
|
|
267
|
+
radius). For everything else, style the shadow **parts** in plain CSS.
|
|
268
|
+
|
|
269
|
+
`<a-dialog>` exposes `::part(dialog)` (the surface), `::part(header)`,
|
|
270
|
+
`::part(body)`, `::part(footer)`, `::part(close)`, and `::part(dialog)::backdrop`
|
|
271
|
+
(the overlay). A `::part` rule wins over the element's internal styles with no
|
|
272
|
+
`!important`, so the surface, size, radius, shadow, overlay, and zone padding are
|
|
273
|
+
all reachable.
|
|
274
|
+
|
|
275
|
+
**Scope the rule to a position (or a class).** A bare `a-dialog::part(dialog)`
|
|
276
|
+
hits every dialog, drawers included, where a `width` override would collapse a
|
|
277
|
+
bottom sheet off its full-bleed edge. Target the one you mean: a class for a
|
|
278
|
+
specific dialog, `[position="right"]` for a drawer edge, `:not([position])` for
|
|
279
|
+
the centered modal (`center` carries no `position` attribute).
|
|
280
|
+
|
|
281
|
+
```css
|
|
282
|
+
/* one dialog, via a class — nothing else is touched */
|
|
283
|
+
a-dialog.styled-dialog::part(dialog) {
|
|
284
|
+
width: 640px;
|
|
285
|
+
border-radius: 20px;
|
|
286
|
+
background: var(--bg-2);
|
|
287
|
+
}
|
|
288
|
+
a-dialog.styled-dialog::part(dialog)::backdrop {
|
|
289
|
+
background: color-mix(in oklch, black 60%, transparent);
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
More recipes. The centered modal's max size is the viewport minus a 32px edge
|
|
294
|
+
gap; set `max-width` / `max-height` on the surface to change it. A drawer's
|
|
295
|
+
thickness is the axis it slides along, so scope it to that edge and the cross
|
|
296
|
+
axis stays full-bleed.
|
|
297
|
+
|
|
298
|
+
```css
|
|
299
|
+
/* centered modal: size + the viewport edge gap (max-*) */
|
|
300
|
+
a-dialog:not([position])::part(dialog) { width: 720px; max-width: 90vw; max-height: 85dvh; }
|
|
301
|
+
|
|
302
|
+
/* a drawer's thickness — scoped to its edge */
|
|
303
|
+
a-dialog[position="right"]::part(dialog) { width: 480px; }
|
|
304
|
+
a-dialog[position="bottom"]::part(dialog) { height: 60dvh; }
|
|
305
|
+
|
|
306
|
+
/* zone padding, shadow, overlay (unscoped → every dialog) */
|
|
307
|
+
a-dialog::part(header) { padding: 24px 24px 12px; font-size: 20px; }
|
|
308
|
+
a-dialog::part(footer) { justify-content: space-between; }
|
|
309
|
+
a-dialog::part(dialog) { box-shadow: 0 20px 60px color-mix(in oklch, black 30%, transparent); }
|
|
310
|
+
a-dialog::part(dialog)::backdrop { background: color-mix(in oklch, black 50%, transparent); }
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
If you override the surface, shadow, or overlay, add a `.dark` rule to match —
|
|
314
|
+
the built-in defaults track light / dark, a fixed override won't.
|