@kerfjs/ui 5.0.0-beta.100 → 5.0.0-beta.101
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 +3 -1
- package/ai/component-catalog.json +214 -18
- package/ai/component-composition.json +216 -8
- package/ai/components/README.md +2 -0
- package/ai/components/backdrop-image.md +84 -0
- package/ai/components/nav-stack.md +6 -1
- package/ai/components/pane.md +11 -1
- package/ai/components/recipe-media-detail.md +57 -0
- package/ai/components/surface-scaffold.md +13 -6
- package/ai/components/tab-navigator.md +6 -1
- package/ai/components/toolbar-control-group.md +1 -0
- package/ai/public-api-signatures-v1.md +356 -32
- package/ai/skill.md +53 -5
- package/ai/webawesome-jsx-signatures-v1.md +1 -1
- package/dist/backdrop-image.d.ts +132 -0
- package/dist/backdrop-image.js +5 -0
- package/dist/backdrop-image.js.map +1 -0
- package/dist/browser/backdrop-image.js +3 -0
- package/dist/catalog-resources.d.ts +2 -2
- package/dist/catalog.d.ts +2 -2
- package/dist/catalog.js +10 -9
- package/dist/catalog.js.map +1 -1
- package/dist/{chunk-QIG4IGUP.js → chunk-2PXZBFEC.js} +3 -1
- package/dist/chunk-2PXZBFEC.js.map +1 -0
- package/dist/{chunk-T4WJ5VGP.js → chunk-3SKAEPPZ.js} +2 -2
- package/dist/{chunk-T4WJ5VGP.js.map → chunk-3SKAEPPZ.js.map} +1 -1
- package/dist/chunk-5NQF35VO.js +67 -0
- package/dist/chunk-5NQF35VO.js.map +1 -0
- package/dist/{chunk-CMETHPKR.js → chunk-6E2H2E5A.js} +29 -8
- package/dist/chunk-6E2H2E5A.js.map +1 -0
- package/dist/{chunk-PIVY32AE.js → chunk-BYFTHA4I.js} +16 -3
- package/dist/chunk-BYFTHA4I.js.map +1 -0
- package/dist/{chunk-HGV7J4T6.js → chunk-EKHDLBRP.js} +1 -1
- package/dist/chunk-EKHDLBRP.js.map +1 -0
- package/dist/{chunk-QROYYGWU.js → chunk-FNZQOUZ5.js} +2 -2
- package/dist/{chunk-QROYYGWU.js.map → chunk-FNZQOUZ5.js.map} +1 -1
- package/dist/{chunk-EOO6PYGF.js → chunk-IXLUZHGT.js} +4 -4
- package/dist/{chunk-EOO6PYGF.js.map → chunk-IXLUZHGT.js.map} +1 -1
- package/dist/{chunk-UMKV7YVQ.js → chunk-LPHU44S3.js} +5 -1
- package/dist/chunk-LPHU44S3.js.map +1 -0
- package/dist/{chunk-INI4WDWC.js → chunk-M5KZ26VV.js} +27 -4
- package/dist/chunk-M5KZ26VV.js.map +1 -0
- package/dist/{chunk-TLMMFQWM.js → chunk-NS762OVM.js} +3 -3
- package/dist/{chunk-TLMMFQWM.js.map → chunk-NS762OVM.js.map} +1 -1
- package/dist/{chunk-CXZXHZTR.js → chunk-OZ6X76G5.js} +34 -1
- package/dist/chunk-OZ6X76G5.js.map +1 -0
- package/dist/{chunk-YOA5W5ZH.js → chunk-PKL6QJKK.js} +31 -3
- package/dist/chunk-PKL6QJKK.js.map +1 -0
- package/dist/chunk-QTSMHH5J.js +85 -0
- package/dist/chunk-QTSMHH5J.js.map +1 -0
- package/dist/chunk-SCDWURBO.js +136 -0
- package/dist/chunk-SCDWURBO.js.map +1 -0
- package/dist/{chunk-RHD7RKGI.js → chunk-SIW64V65.js} +3 -3
- package/dist/{chunk-RHD7RKGI.js.map → chunk-SIW64V65.js.map} +1 -1
- package/dist/chunk-SQ5DH66U.js +37 -0
- package/dist/chunk-SQ5DH66U.js.map +1 -0
- package/dist/{chunk-PYBBYSZ3.js → chunk-YBWQR2OK.js} +5 -5
- package/dist/{chunk-PYBBYSZ3.js.map → chunk-YBWQR2OK.js.map} +1 -1
- package/dist/{chunk-3GLYIIFR.js → chunk-ZYU5WRC3.js} +2 -2
- package/dist/{chunk-3GLYIIFR.js.map → chunk-ZYU5WRC3.js.map} +1 -1
- package/dist/collapsible-panel.js +3 -2
- package/dist/collapsible-panel.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.js +24 -20
- package/dist/list-action-row.js +1 -1
- package/dist/list-header.js +1 -1
- package/dist/list-item.js +1 -1
- package/dist/{nav-stack-VXTumzKa.d.ts → nav-stack-CU_0dKtI.d.ts} +41 -5
- package/dist/nav-stack.d.ts +4 -4
- package/dist/nav-stack.js +2 -1
- package/dist/pane.d.ts +76 -4
- package/dist/pane.js +2 -1
- package/dist/select.js +1 -1
- package/dist/split-view.d.ts +4 -4
- package/dist/split-view.js +3 -2
- package/dist/split-view.js.map +1 -1
- package/dist/state-banner.js +1 -1
- package/dist/styles/backdrop-image.css +241 -0
- package/dist/styles/components/actions/toolbar/toolbar.css +9 -0
- package/dist/styles/components/actions/toolbar-control-group/toolbar-control-group.css +128 -0
- package/dist/styles/components/layout/pane/pane.css +121 -1
- package/dist/styles/components/navigation/nav-stack/nav-stack.css +207 -6
- package/dist/styles/components/navigation/tab-navigator/tab-navigator.css +121 -3
- package/dist/styles/components/surfaces/backdrop-image/backdrop-image.css +241 -0
- package/dist/styles/components/surfaces/surface-scaffold/surface-scaffold.css +166 -2
- package/dist/styles/foundation.css +165 -8
- package/dist/styles/nav-stack.css +207 -6
- package/dist/styles/overlay-sheet.css +211 -0
- package/dist/styles/pane.css +121 -1
- package/dist/styles/styles.css +1 -0
- package/dist/styles/surface-scaffold.css +166 -2
- package/dist/styles/tab-navigator.css +121 -3
- package/dist/styles/toolbar-control-group.css +128 -0
- package/dist/styles/toolbar.css +9 -0
- package/dist/styles/webawesome.css +10 -0
- package/dist/surface-scaffold-register.d.ts +3 -2
- package/dist/surface-scaffold-register.js +113 -10
- package/dist/surface-scaffold-register.js.map +1 -1
- package/dist/surface-scaffold.d.ts +38 -5
- package/dist/surface-scaffold.js +1 -1
- package/dist/tab-navigator.d.ts +43 -3
- package/dist/tab-navigator.js +35 -6
- package/dist/tab-navigator.js.map +1 -1
- package/dist/toolbar-control-group.d.ts +13 -2
- package/dist/toolbar-control-group.js +1 -1
- package/dist/toolbar-text.d.ts +6 -1
- package/dist/toolbar-text.js +1 -1
- package/dist/toolbar.d.ts +1 -0
- package/dist/wire-catalog.d.ts +2 -2
- package/dist/wire-catalog.js +1 -1
- package/dist/wire-nav-stack.js +29 -3
- package/dist/wire-nav-stack.js.map +1 -1
- package/dist/wire-overlay-sheets.d.ts +20 -0
- package/dist/wire-overlay-sheets.js +4 -0
- package/dist/wire-overlay-sheets.js.map +1 -0
- package/dist/wire-scroll-dividers.js +1 -1
- package/dist/wire-token-search-fields.js +1 -1
- package/dist/workbench.d.ts +2 -2
- package/dist/workbench.js +2 -1
- package/docs/accessibility.md +35 -0
- package/docs/app-layouts.md +13 -0
- package/docs/backdrop-image.md +128 -0
- package/docs/component-contract.md +2 -0
- package/docs/component-selection.md +11 -2
- package/docs/design/templates/toolbar-control-group/glass-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/glass.svg +1 -0
- package/docs/design/templates/toolbar-control-group-dark.svg +1 -1
- package/docs/design/templates/toolbar-control-group.svg +1 -1
- package/docs/design/templates.md +8 -0
- package/docs/layout.md +135 -5
- package/docs/manual-test-plan.md +29 -0
- package/docs/nav-stack.md +37 -1
- package/docs/recipes.md +24 -0
- package/docs/surface-scaffold.md +149 -3
- package/docs/tab-navigator.md +36 -0
- package/docs/ux-demo.md +11 -1
- package/llms.txt +42 -4
- package/package.json +13 -1
- package/ux-demo/recipes/loaders.ts +2 -0
- package/ux-demo/recipes/media-detail.tsx +272 -0
- package/dist/chunk-CMETHPKR.js.map +0 -1
- package/dist/chunk-CXZXHZTR.js.map +0 -1
- package/dist/chunk-HGV7J4T6.js.map +0 -1
- package/dist/chunk-INI4WDWC.js.map +0 -1
- package/dist/chunk-PIVY32AE.js.map +0 -1
- package/dist/chunk-QIG4IGUP.js.map +0 -1
- package/dist/chunk-UMKV7YVQ.js.map +0 -1
- package/dist/chunk-YOA5W5ZH.js.map +0 -1
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
height: 100%;
|
|
6
6
|
overflow: hidden;
|
|
7
7
|
flex-direction: column;
|
|
8
|
-
background: var(--kui-color-surface);
|
|
8
|
+
background: var(--_kui-tab-scaffold-background, var(--kui-color-surface));
|
|
9
9
|
/* Safe areas: the scenes and the bar paint through unsafe areas; the bar
|
|
10
10
|
compensates for the bottom and inline edges, and the scenes for the top
|
|
11
11
|
and inline edges (see pane.css for the `--kui-edge-inset-*` context). */
|
|
@@ -55,6 +55,31 @@
|
|
|
55
55
|
background: var(--kui-sunken-panel-background, var(--kui-sunken-surface));
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
/* `background`: the navigator's color paints the root and a docked bar (the
|
|
59
|
+
overlay bar keeps the chrome material); a tab's own color paints its scene
|
|
60
|
+
and replaces its appearance's fill. A nested navigator never inherits the
|
|
61
|
+
color. Forced colors paint the system surface. */
|
|
62
|
+
.kui-tab-scaffold:not([data-background]) {
|
|
63
|
+
--_kui-tab-scaffold-background: initial;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.kui-tab-scaffold__scene[data-kui-tint]:not([data-background]) {
|
|
67
|
+
background: var(--kui-color-surface);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.kui-tab-scaffold__scene[data-background] {
|
|
71
|
+
background: var(--_kui-tab-scaffold-scene-background);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@media (forced-colors: active) {
|
|
75
|
+
.kui-tab-scaffold[data-background],
|
|
76
|
+
.kui-tab-scaffold[data-background]:not([data-bar-presentation="overlay"])
|
|
77
|
+
> .kui-tab-scaffold__bar,
|
|
78
|
+
.kui-tab-scaffold__scene[data-background] {
|
|
79
|
+
background: Canvas;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
58
83
|
/* A scene is its region's scroll owner, so its top inset is padding inside
|
|
59
84
|
the scroller. A scene whose only child is a Pane or layout (usually a
|
|
60
85
|
NavStack) lets that child own the insets instead. */
|
|
@@ -138,7 +163,7 @@
|
|
|
138
163
|
scroll divider. Changing its color never moves the bar or a tab. */
|
|
139
164
|
border-block-start: 1px solid
|
|
140
165
|
var(--_kui-tab-scaffold-bar-divider, transparent);
|
|
141
|
-
background: var(--kui-color-surface);
|
|
166
|
+
background: var(--_kui-tab-scaffold-background, var(--kui-color-surface));
|
|
142
167
|
}
|
|
143
168
|
|
|
144
169
|
.kui-tab-scaffold[data-separator="auto"]:has(
|
|
@@ -161,7 +186,10 @@
|
|
|
161
186
|
> [data-component="pane"][data-appearance="sunken"]
|
|
162
187
|
)
|
|
163
188
|
> .kui-tab-scaffold__bar,
|
|
164
|
-
.kui-tab-scaffold[data-separator="auto"]:not(
|
|
189
|
+
.kui-tab-scaffold[data-separator="auto"]:not(
|
|
190
|
+
[data-chrome-dividers],
|
|
191
|
+
[data-bar-material="clear"]
|
|
192
|
+
)
|
|
165
193
|
> .kui-tab-scaffold__bar[data-scroll-divider*="t"],
|
|
166
194
|
.kui-tab-scaffold[data-separator="auto"][data-chrome-dividers="always"]
|
|
167
195
|
> .kui-tab-scaffold__bar {
|
|
@@ -213,6 +241,96 @@
|
|
|
213
241
|
}
|
|
214
242
|
}
|
|
215
243
|
|
|
244
|
+
/* `barPresentation="clear"`: an overlay bar (the same measuring and edge
|
|
245
|
+
inset as above) that is fully transparent, for a bar over imagery. With
|
|
246
|
+
`scrollEdge="material"` a pseudo-element carrying the chrome material
|
|
247
|
+
fades in (opacity only) while the active scene continues beneath the bar,
|
|
248
|
+
and out once it is scrolled to its end. A clear bar draws no scroll
|
|
249
|
+
divider. */
|
|
250
|
+
.kui-tab-scaffold[data-bar-material="clear"] > .kui-tab-scaffold__bar {
|
|
251
|
+
-webkit-backdrop-filter: none;
|
|
252
|
+
backdrop-filter: none;
|
|
253
|
+
background: transparent;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
:where(.kui-tab-scaffold[data-bar-material="clear"][data-scroll-edge])
|
|
257
|
+
> .kui-tab-scaffold__bar {
|
|
258
|
+
position: relative;
|
|
259
|
+
isolation: isolate;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/* The scroll edge fades in as content scrolls beneath the bar: the wiring
|
|
263
|
+
publishes `--kui-scroll-edge-progress` (0 at rest, 1 once content lies
|
|
264
|
+
`scrollEdgeDistance` beyond the bar) and only the layer's opacity follows
|
|
265
|
+
it; the filter never animates. Unwired, it stays clear. */
|
|
266
|
+
.kui-tab-scaffold[data-bar-material="clear"][data-scroll-edge]
|
|
267
|
+
> .kui-tab-scaffold__bar::before {
|
|
268
|
+
position: absolute;
|
|
269
|
+
z-index: -1;
|
|
270
|
+
content: "";
|
|
271
|
+
inset: 0;
|
|
272
|
+
opacity: var(--kui-scroll-edge-progress, 0);
|
|
273
|
+
pointer-events: none;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.kui-tab-scaffold[data-bar-material="clear"][data-scroll-edge="material"]
|
|
277
|
+
> .kui-tab-scaffold__bar::before {
|
|
278
|
+
-webkit-backdrop-filter: var(--kui-chrome-material-filter);
|
|
279
|
+
backdrop-filter: var(--kui-chrome-material-filter);
|
|
280
|
+
background: var(--kui-chrome-material);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.kui-tab-scaffold[data-bar-material="clear"][data-scroll-edge="surface"]
|
|
284
|
+
> .kui-tab-scaffold__bar::before {
|
|
285
|
+
background: var(--kui-color-surface);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/* Static effects and slow displays snap the edge on while content lies
|
|
289
|
+
beneath the bar instead of tracking the scroll. */
|
|
290
|
+
:root[data-kui-effects="static"]
|
|
291
|
+
.kui-tab-scaffold[data-bar-material="clear"][data-scroll-edge]
|
|
292
|
+
> .kui-tab-scaffold__bar::before {
|
|
293
|
+
opacity: 0;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
:root[data-kui-effects="static"]
|
|
297
|
+
.kui-tab-scaffold[data-bar-material="clear"][data-scroll-edge]
|
|
298
|
+
> .kui-tab-scaffold__bar[data-scroll-divider*="t"]::before {
|
|
299
|
+
opacity: 1;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
@media (update: slow) {
|
|
303
|
+
.kui-tab-scaffold[data-bar-material="clear"][data-scroll-edge]
|
|
304
|
+
> .kui-tab-scaffold__bar::before {
|
|
305
|
+
opacity: 0;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.kui-tab-scaffold[data-bar-material="clear"][data-scroll-edge]
|
|
309
|
+
> .kui-tab-scaffold__bar[data-scroll-divider*="t"]::before {
|
|
310
|
+
opacity: 1;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/* Clear chrome turns opaque where transparency would hurt, like the
|
|
315
|
+
material itself. */
|
|
316
|
+
@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
|
|
317
|
+
.kui-tab-scaffold[data-bar-material="clear"] > .kui-tab-scaffold__bar {
|
|
318
|
+
background: var(--kui-chrome-material);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
@media (forced-colors: active) {
|
|
323
|
+
.kui-tab-scaffold[data-bar-material="clear"] > .kui-tab-scaffold__bar {
|
|
324
|
+
background: Canvas;
|
|
325
|
+
border-block-start-color: CanvasText;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.kui-tab-scaffold[data-bar-material="clear"][data-scroll-edge]
|
|
329
|
+
> .kui-tab-scaffold__bar::before {
|
|
330
|
+
content: none;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
216
334
|
.kui-tab-scaffold__tab {
|
|
217
335
|
display: flex;
|
|
218
336
|
flex: 1 1 0;
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/* An image block at the top of a scrolling Pane or NavStack view. The root
|
|
2
|
+
paints fadeTo, so the image fades into it and the content below sits on the
|
|
3
|
+
same color. It bleeds to the Pane's resolved inline insets (the safe area)
|
|
4
|
+
and hands them back to its overlay and content, so their own items keep
|
|
5
|
+
the ordinary text axis. */
|
|
6
|
+
.kui-backdrop-image {
|
|
7
|
+
--_kui-backdrop-image-height: 20rem;
|
|
8
|
+
--_kui-backdrop-image-fade-to: transparent;
|
|
9
|
+
--_kui-backdrop-image-parallax: 0;
|
|
10
|
+
--_kui-backdrop-image-position: center;
|
|
11
|
+
--_kui-backdrop-image-fit: cover;
|
|
12
|
+
--_kui-backdrop-image-inset-start: var(
|
|
13
|
+
--kui-pane-content-inset-inline-start,
|
|
14
|
+
0px
|
|
15
|
+
);
|
|
16
|
+
--_kui-backdrop-image-inset-end: var(
|
|
17
|
+
--kui-pane-content-inset-inline-end,
|
|
18
|
+
0px
|
|
19
|
+
);
|
|
20
|
+
--_kui-backdrop-image-bleed-block-start: 0px;
|
|
21
|
+
display: flow-root;
|
|
22
|
+
position: relative;
|
|
23
|
+
min-width: 0;
|
|
24
|
+
margin-inline: calc(-1 * var(--_kui-backdrop-image-inset-start))
|
|
25
|
+
calc(-1 * var(--_kui-backdrop-image-inset-end));
|
|
26
|
+
background-color: var(--_kui-backdrop-image-fade-to);
|
|
27
|
+
isolation: isolate;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* `underChrome`: the block runs up beneath the scroller's block-start inset
|
|
31
|
+
(overlay or clear chrome clearance plus the top safe area, as the Pane
|
|
32
|
+
publishes it) and the hero grows by the same amount, so the visible image
|
|
33
|
+
keeps its height below the chrome and the overlay stays where it was. */
|
|
34
|
+
.kui-backdrop-image[data-under-chrome="true"] {
|
|
35
|
+
--_kui-backdrop-image-bleed-block-start: var(
|
|
36
|
+
--kui-pane-content-inset-block-start,
|
|
37
|
+
0px
|
|
38
|
+
);
|
|
39
|
+
margin-block-start: calc(-1 * var(--_kui-backdrop-image-bleed-block-start));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* The view timeline's inset defaults to the scroller's scroll padding, which
|
|
43
|
+
is the chrome clearance this image now runs beneath; measured from there,
|
|
44
|
+
the parallax would start part-way through at rest and shift the image
|
|
45
|
+
down. Measure from the scroller's real edge instead. */
|
|
46
|
+
.kui-backdrop-image[data-under-chrome="true"] .kui-backdrop-image__hero {
|
|
47
|
+
view-timeline-inset: 0px;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* The hero is the scroll-timeline subject: its own block of scroll drives
|
|
51
|
+
the parallax, whatever sits above it and however tall it is. */
|
|
52
|
+
.kui-backdrop-image__hero {
|
|
53
|
+
display: grid;
|
|
54
|
+
position: relative;
|
|
55
|
+
min-height: calc(
|
|
56
|
+
var(--_kui-backdrop-image-height) +
|
|
57
|
+
var(--_kui-backdrop-image-bleed-block-start)
|
|
58
|
+
);
|
|
59
|
+
grid-template: minmax(0, 1fr) / minmax(0, 1fr);
|
|
60
|
+
view-timeline: --kui-backdrop-image block;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.kui-backdrop-image__backdrop {
|
|
64
|
+
position: absolute;
|
|
65
|
+
inset: 0;
|
|
66
|
+
overflow: clip;
|
|
67
|
+
pointer-events: none;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* The fade is a mask on the backdrop, not a painted gradient, so the image
|
|
71
|
+
dissolves into whatever fadeTo is without a visible band. */
|
|
72
|
+
.kui-backdrop-image[data-fade="true"] .kui-backdrop-image__backdrop {
|
|
73
|
+
mask-image: linear-gradient(to bottom, #000 50%, transparent);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* The moving layer: everything that lags the scroll. */
|
|
77
|
+
.kui-backdrop-image__layers {
|
|
78
|
+
position: absolute;
|
|
79
|
+
inset: 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.kui-backdrop-image__media,
|
|
83
|
+
.kui-backdrop-image__blurred {
|
|
84
|
+
display: block;
|
|
85
|
+
width: 100%;
|
|
86
|
+
height: 100%;
|
|
87
|
+
object-fit: var(--_kui-backdrop-image-fit);
|
|
88
|
+
object-position: var(--_kui-backdrop-image-position);
|
|
89
|
+
background-position: var(--_kui-backdrop-image-position);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Progressive blur: a copy over the sharp image, blurred once and hidden
|
|
93
|
+
until the scroll timeline fades it in. A filtered copy overhangs the block
|
|
94
|
+
by its radius on every side, so the blur's transparent fringe falls outside
|
|
95
|
+
the clip instead of letting the sharp edge show through. The dim layer
|
|
96
|
+
darkens toward its peak opacity with it. */
|
|
97
|
+
.kui-backdrop-image__blurred {
|
|
98
|
+
position: absolute;
|
|
99
|
+
inset: 0;
|
|
100
|
+
opacity: 0;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.kui-backdrop-image[data-blur="filter"] .kui-backdrop-image__blurred {
|
|
104
|
+
inset: calc(-1 * var(--_kui-backdrop-image-blur)) auto auto
|
|
105
|
+
calc(-1 * var(--_kui-backdrop-image-blur));
|
|
106
|
+
width: calc(100% + 2 * var(--_kui-backdrop-image-blur));
|
|
107
|
+
height: calc(100% + 2 * var(--_kui-backdrop-image-blur));
|
|
108
|
+
filter: blur(var(--_kui-backdrop-image-blur));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.kui-backdrop-image__dim {
|
|
112
|
+
position: absolute;
|
|
113
|
+
inset: 0;
|
|
114
|
+
background: rgb(0 0 0 / var(--_kui-backdrop-image-dim));
|
|
115
|
+
opacity: 0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.kui-backdrop-image__scrim {
|
|
119
|
+
position: absolute;
|
|
120
|
+
inset: 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.kui-backdrop-image[data-scrim="bottom"] .kui-backdrop-image__scrim {
|
|
124
|
+
background: linear-gradient(to bottom, rgb(0 0 0 / 0) 35%, rgb(0 0 0 / 0.5));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.kui-backdrop-image[data-scrim="full"] .kui-backdrop-image__scrim {
|
|
128
|
+
background: rgb(0 0 0 / 0.35);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.kui-backdrop-image__overlay {
|
|
132
|
+
position: relative;
|
|
133
|
+
min-width: 0;
|
|
134
|
+
align-self: end;
|
|
135
|
+
padding-inline: var(--_kui-backdrop-image-inset-start)
|
|
136
|
+
var(--_kui-backdrop-image-inset-end);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* A scrim is dark in both color schemes, so the content laid over it takes
|
|
140
|
+
the dark scheme: default-tone titles and controls resolve their light
|
|
141
|
+
foregrounds whatever the page's scheme. */
|
|
142
|
+
.kui-backdrop-image:is([data-scrim="bottom"], [data-scrim="full"])
|
|
143
|
+
.kui-backdrop-image__overlay {
|
|
144
|
+
color-scheme: dark;
|
|
145
|
+
color: var(--kui-color-text);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.kui-backdrop-image__content {
|
|
149
|
+
min-width: 0;
|
|
150
|
+
padding-inline: var(--_kui-backdrop-image-inset-start)
|
|
151
|
+
var(--_kui-backdrop-image-inset-end);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* Parallax: as the hero scrolls out past the scroller's start edge, the
|
|
155
|
+
layer slides down by the lag factor of that distance, so the image moves
|
|
156
|
+
slower than the content. The blurred copy and dim layer fade in over the
|
|
157
|
+
same range. Only transform and opacity animate, on a scroll-driven
|
|
158
|
+
timeline the compositor runs; the blur filter is never animated, and there
|
|
159
|
+
is no scroll listener and no background-attachment: fixed. Without
|
|
160
|
+
scroll-driven animations (Firefox today) or with reduced motion the image
|
|
161
|
+
stays static and sharp. */
|
|
162
|
+
@supports (animation-timeline: view()) {
|
|
163
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
164
|
+
.kui-backdrop-image[data-parallax="scroll"] .kui-backdrop-image__layers {
|
|
165
|
+
animation: kui-backdrop-image-parallax linear both;
|
|
166
|
+
animation-timeline: --kui-backdrop-image;
|
|
167
|
+
animation-range: exit-crossing 0% exit-crossing 100%;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/* The blur reaches full strength halfway through the exit, while most
|
|
171
|
+
of the image is still in view; ramping over the whole exit left it
|
|
172
|
+
barely visible before the image had scrolled away. */
|
|
173
|
+
.kui-backdrop-image[data-blur]
|
|
174
|
+
:is(.kui-backdrop-image__blurred, .kui-backdrop-image__dim) {
|
|
175
|
+
animation: kui-backdrop-image-reveal linear both;
|
|
176
|
+
animation-timeline: --kui-backdrop-image;
|
|
177
|
+
animation-range: exit-crossing 0% exit-crossing 50%;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
@keyframes kui-backdrop-image-parallax {
|
|
183
|
+
from {
|
|
184
|
+
transform: none;
|
|
185
|
+
}
|
|
186
|
+
to {
|
|
187
|
+
transform: translateY(calc(var(--_kui-backdrop-image-parallax) * 100%));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
@keyframes kui-backdrop-image-reveal {
|
|
192
|
+
from {
|
|
193
|
+
opacity: 0;
|
|
194
|
+
}
|
|
195
|
+
to {
|
|
196
|
+
opacity: 1;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/* The document-wide static-effects switch and slow-update displays (e-ink,
|
|
201
|
+
print) get the same static, sharp image as reduced motion. */
|
|
202
|
+
:root[data-kui-effects="static"]
|
|
203
|
+
.kui-backdrop-image[data-parallax="scroll"]
|
|
204
|
+
.kui-backdrop-image__layers,
|
|
205
|
+
:root[data-kui-effects="static"]
|
|
206
|
+
.kui-backdrop-image[data-blur]
|
|
207
|
+
:is(.kui-backdrop-image__blurred, .kui-backdrop-image__dim) {
|
|
208
|
+
animation: none;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
@media (update: slow) {
|
|
212
|
+
.kui-backdrop-image[data-parallax="scroll"] .kui-backdrop-image__layers,
|
|
213
|
+
.kui-backdrop-image[data-blur]
|
|
214
|
+
:is(.kui-backdrop-image__blurred, .kui-backdrop-image__dim) {
|
|
215
|
+
animation: none;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* Reduced transparency or increased contrast: no dissolve. The image ends on
|
|
220
|
+
a hard edge over solid fadeTo, and overlay content sits on a solid fadeTo
|
|
221
|
+
band instead of the image, so text chosen for the fade color stays
|
|
222
|
+
legible. */
|
|
223
|
+
@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
|
|
224
|
+
.kui-backdrop-image[data-fade="true"] .kui-backdrop-image__backdrop {
|
|
225
|
+
mask-image: none;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.kui-backdrop-image[data-fade="true"] .kui-backdrop-image__overlay {
|
|
229
|
+
background-color: var(--_kui-backdrop-image-fade-to);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* Over that solid band the overlay keeps the page's scheme. */
|
|
233
|
+
.kui-backdrop-image[data-fade="true"]:is(
|
|
234
|
+
[data-scrim="bottom"],
|
|
235
|
+
[data-scrim="full"]
|
|
236
|
+
)
|
|
237
|
+
.kui-backdrop-image__overlay {
|
|
238
|
+
color-scheme: inherit;
|
|
239
|
+
color: inherit;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
@@ -5,6 +5,21 @@
|
|
|
5
5
|
var(--_kui-dialog-surface-gutter, 1rem)
|
|
6
6
|
);
|
|
7
7
|
--width: min(35rem, calc(100vw - 2rem));
|
|
8
|
+
/* Concentric with a pill ToolbarControlGroup at the body inset: 22px + 8px
|
|
9
|
+
compact, 22px + 16px comfortable. */
|
|
10
|
+
--_kui-dialog-surface-radius: calc(
|
|
11
|
+
var(--kui-layout-pill-radius, calc(1px + 1.3125rem)) +
|
|
12
|
+
var(--kui-wa-surface-inset, 0.5rem)
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
.kui-dialog-surface[data-body-inset="comfortable"] > wa-dialog {
|
|
16
|
+
--_kui-dialog-surface-radius: calc(
|
|
17
|
+
var(--kui-layout-pill-radius, calc(1px + 1.3125rem)) +
|
|
18
|
+
var(--kui-wa-container-inset, 1rem)
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
.kui-dialog-surface > wa-dialog::part(dialog) {
|
|
22
|
+
border-radius: var(--_kui-dialog-surface-radius);
|
|
8
23
|
}
|
|
9
24
|
.kui-dialog-surface[data-size="small"] > wa-dialog {
|
|
10
25
|
--_kui-dialog-surface-width: 25rem;
|
|
@@ -281,7 +296,8 @@
|
|
|
281
296
|
height: 100dvh;
|
|
282
297
|
max-height: 100dvh;
|
|
283
298
|
margin-inline-end: 0;
|
|
284
|
-
border-radius: var(--
|
|
299
|
+
border-radius: var(--_kui-dialog-surface-radius) 0 0
|
|
300
|
+
var(--_kui-dialog-surface-radius);
|
|
285
301
|
}
|
|
286
302
|
.kui-dialog-surface[data-presentation="fullscreen"] > wa-dialog {
|
|
287
303
|
--width: 100vw;
|
|
@@ -293,6 +309,149 @@
|
|
|
293
309
|
max-height: 100dvh;
|
|
294
310
|
border-radius: 0;
|
|
295
311
|
}
|
|
312
|
+
/* A bottom sheet rests on the viewport's block-end edge: square bottom
|
|
313
|
+
corners, the dialog's own radius on top. Its panel runs to the edge, behind
|
|
314
|
+
the home indicator and any virtual keyboard, while a block-end padding
|
|
315
|
+
(the larger of the safe area, the keyboard inset the register measures
|
|
316
|
+
from `visualViewport`, and the VirtualKeyboard API's
|
|
317
|
+
`env(keyboard-inset-height)`) keeps its content above them. `height`,
|
|
318
|
+
`minHeight` and `maxHeight` measure that visible content height; the panel
|
|
319
|
+
is capped at the dynamic viewport minus the top safe area and one gutter. */
|
|
320
|
+
.kui-dialog-surface[data-presentation="bottom-sheet"]
|
|
321
|
+
> wa-dialog::part(dialog) {
|
|
322
|
+
--_kui-dialog-surface-sheet-clearance: max(
|
|
323
|
+
var(--kui-safe-area-block-end, env(safe-area-inset-bottom, 0px)),
|
|
324
|
+
var(--_kui-dialog-surface-keyboard-inset, 0px),
|
|
325
|
+
env(keyboard-inset-height, 0px)
|
|
326
|
+
);
|
|
327
|
+
--_kui-dialog-surface-sheet-cap: max(
|
|
328
|
+
0px,
|
|
329
|
+
min(
|
|
330
|
+
calc(
|
|
331
|
+
var(--_kui-dialog-surface-max-height, 100dvh) +
|
|
332
|
+
var(--_kui-dialog-surface-sheet-clearance)
|
|
333
|
+
),
|
|
334
|
+
calc(
|
|
335
|
+
100dvh -
|
|
336
|
+
var(--kui-safe-area-block-start, env(safe-area-inset-top, 0px)) -
|
|
337
|
+
var(--_kui-dialog-surface-resolved-gutter)
|
|
338
|
+
)
|
|
339
|
+
)
|
|
340
|
+
);
|
|
341
|
+
box-sizing: border-box;
|
|
342
|
+
width: min(
|
|
343
|
+
var(
|
|
344
|
+
--_kui-dialog-surface-preferred-width,
|
|
345
|
+
var(--_kui-dialog-surface-width)
|
|
346
|
+
),
|
|
347
|
+
100%
|
|
348
|
+
);
|
|
349
|
+
max-width: 100%;
|
|
350
|
+
/* Unset (content height) until the register publishes a resized height. */
|
|
351
|
+
height: calc(
|
|
352
|
+
var(--_kui-dialog-surface-sheet-height) +
|
|
353
|
+
var(--_kui-dialog-surface-sheet-clearance)
|
|
354
|
+
);
|
|
355
|
+
max-height: var(--_kui-dialog-surface-sheet-cap);
|
|
356
|
+
/* A modal dialog sits between both block insets, so an auto height would
|
|
357
|
+
stretch to the cap; anchored only at the bottom, a sheet with no
|
|
358
|
+
`height` (and no resized height) opens at its content height. */
|
|
359
|
+
inset-block-start: auto;
|
|
360
|
+
margin: auto auto 0;
|
|
361
|
+
padding-block-end: var(--_kui-dialog-surface-sheet-clearance);
|
|
362
|
+
border-radius: var(--_kui-dialog-surface-radius)
|
|
363
|
+
var(--_kui-dialog-surface-radius) 0 0;
|
|
364
|
+
}
|
|
365
|
+
.kui-dialog-surface[data-presentation="bottom-sheet"][data-height]
|
|
366
|
+
> wa-dialog::part(dialog) {
|
|
367
|
+
height: calc(
|
|
368
|
+
var(
|
|
369
|
+
--_kui-dialog-surface-sheet-height,
|
|
370
|
+
var(--_kui-dialog-surface-height, 100dvh)
|
|
371
|
+
) +
|
|
372
|
+
var(--_kui-dialog-surface-sheet-clearance)
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
/* A resizable sheet keeps a band at its top edge for the grabber the
|
|
376
|
+
register injects: a `role="separator"` that resizes the visible height
|
|
377
|
+
between `minHeight` (default 160px) and the cap by drag or keyboard. */
|
|
378
|
+
.kui-dialog-surface[data-presentation="bottom-sheet"][data-resizable]
|
|
379
|
+
> wa-dialog::part(dialog) {
|
|
380
|
+
min-height: min(
|
|
381
|
+
calc(
|
|
382
|
+
var(--_kui-dialog-surface-min-height, 10rem) +
|
|
383
|
+
var(--_kui-dialog-surface-sheet-clearance)
|
|
384
|
+
),
|
|
385
|
+
var(--_kui-dialog-surface-sheet-cap)
|
|
386
|
+
);
|
|
387
|
+
padding-block-start: 0.75rem;
|
|
388
|
+
}
|
|
389
|
+
.kui-dialog-surface[data-presentation="bottom-sheet"][data-resizable]
|
|
390
|
+
> wa-dialog::part(sheet-handle) {
|
|
391
|
+
position: absolute;
|
|
392
|
+
z-index: 2;
|
|
393
|
+
inset-block-start: 0;
|
|
394
|
+
inset-inline: 0;
|
|
395
|
+
width: 6rem;
|
|
396
|
+
height: 1.25rem;
|
|
397
|
+
margin-inline: auto;
|
|
398
|
+
border-radius: var(--kui-radius-s);
|
|
399
|
+
cursor: ns-resize;
|
|
400
|
+
touch-action: none;
|
|
401
|
+
}
|
|
402
|
+
.kui-dialog-surface[data-presentation="bottom-sheet"][data-resizable]
|
|
403
|
+
> wa-dialog::part(sheet-handle)::before {
|
|
404
|
+
position: absolute;
|
|
405
|
+
inset-block-start: 0.375rem;
|
|
406
|
+
inset-inline: 0;
|
|
407
|
+
width: 2.25rem;
|
|
408
|
+
height: 0.3125rem;
|
|
409
|
+
margin-inline: auto;
|
|
410
|
+
border-radius: 0.15625rem;
|
|
411
|
+
background: var(--kui-color-neutral-border-loud);
|
|
412
|
+
content: "";
|
|
413
|
+
}
|
|
414
|
+
.kui-dialog-surface[data-presentation="bottom-sheet"][data-resizable]
|
|
415
|
+
> wa-dialog::part(sheet-handle):focus-visible {
|
|
416
|
+
outline: var(--kui-focus-ring);
|
|
417
|
+
outline-offset: -3px;
|
|
418
|
+
}
|
|
419
|
+
@media (forced-colors: active) {
|
|
420
|
+
.kui-dialog-surface[data-presentation="bottom-sheet"][data-resizable]
|
|
421
|
+
> wa-dialog::part(sheet-handle)::before {
|
|
422
|
+
background: CanvasText;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
/* The sheet slides up from, and back down to, the bottom edge. Web Awesome
|
|
426
|
+
runs its show, hide and pulse animations through one slot, so the register
|
|
427
|
+
marks the host only while a show (`enter`) or hide (`exit`) runs, and only
|
|
428
|
+
those keyframes change; the hide plays them in reverse. Reduced motion and
|
|
429
|
+
the static effects mode keep Web Awesome's own animation. */
|
|
430
|
+
@keyframes kui-dialog-surface-sheet-slide {
|
|
431
|
+
from {
|
|
432
|
+
translate: 0 100%;
|
|
433
|
+
}
|
|
434
|
+
to {
|
|
435
|
+
translate: 0 0;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
439
|
+
:root:not([data-kui-effects="static"])
|
|
440
|
+
.kui-dialog-surface[data-presentation="bottom-sheet"]:not(
|
|
441
|
+
[data-enter-motion="standard"]
|
|
442
|
+
)
|
|
443
|
+
> wa-dialog[data-kui-sheet-motion="enter"]::part(dialog),
|
|
444
|
+
:root:not([data-kui-effects="static"])
|
|
445
|
+
.kui-dialog-surface[data-presentation="bottom-sheet"]:not(
|
|
446
|
+
[data-exit-motion="standard"]
|
|
447
|
+
)
|
|
448
|
+
> wa-dialog[data-kui-sheet-motion="exit"]::part(dialog) {
|
|
449
|
+
animation-name: kui-dialog-surface-sheet-slide;
|
|
450
|
+
/* A full-height travel needs longer than the dialog's 150ms scale. */
|
|
451
|
+
animation-duration: var(--wa-transition-slow, 300ms);
|
|
452
|
+
animation-timing-function: cubic-bezier(0.32, 0.72, 0, 1);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
296
455
|
/* PopoverDialogSurface: the dialog chrome and bounded geometry for a native
|
|
297
456
|
popover="auto" surface (non-modal, light dismiss) or the same surface in
|
|
298
457
|
flow. The UA popover rules supply the centered top-layer placement. */
|
|
@@ -325,7 +484,12 @@
|
|
|
325
484
|
overflow: auto;
|
|
326
485
|
color: var(--kui-color-text);
|
|
327
486
|
border: 1px solid var(--kui-color-neutral-border-normal);
|
|
328
|
-
|
|
487
|
+
/* Matches the modal DialogSurface corner: a pill ToolbarControlGroup at the
|
|
488
|
+
8px surface inset (22px + 8px). */
|
|
489
|
+
border-radius: calc(
|
|
490
|
+
var(--kui-layout-pill-radius, calc(1px + 1.3125rem)) +
|
|
491
|
+
var(--kui-wa-surface-inset, 0.5rem)
|
|
492
|
+
);
|
|
329
493
|
background: var(--kui-color-surface-raised);
|
|
330
494
|
box-shadow: var(--kui-shadow-l);
|
|
331
495
|
}
|