@slotted/styles 0.0.0-stage → 0.1.0-beta.1

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.
Files changed (254) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +10 -2
  3. package/package.json +276 -3
  4. package/src/accordion/accordion.css +96 -0
  5. package/src/accordion/accordion.tokens.json +14 -0
  6. package/src/action-bar/action-bar.css +455 -0
  7. package/src/action-bar/action-bar.tokens.json +50 -0
  8. package/src/action-sheet/action-sheet.css +462 -0
  9. package/src/action-sheet/action-sheet.tokens.json +59 -0
  10. package/src/alert/alert.css +764 -0
  11. package/src/alert/alert.tokens.json +77 -0
  12. package/src/app-shell/app-shell.css +1899 -0
  13. package/src/app-shell/app-shell.tokens.json +88 -0
  14. package/src/aspect-ratio/aspect-ratio.css +131 -0
  15. package/src/aspect-ratio/aspect-ratio.tokens.json +1 -0
  16. package/src/attachment/attachment.css +906 -0
  17. package/src/attachment/attachment.tokens.json +85 -0
  18. package/src/avatar/avatar.css +598 -0
  19. package/src/avatar/avatar.tokens.json +64 -0
  20. package/src/avatar-group/avatar-group.css +268 -0
  21. package/src/avatar-group/avatar-group.tokens.json +16 -0
  22. package/src/badge/badge.css +462 -0
  23. package/src/badge/badge.tokens.json +58 -0
  24. package/src/breadcrumb/breadcrumb.css +441 -0
  25. package/src/breadcrumb/breadcrumb.tokens.json +50 -0
  26. package/src/button/button.css +754 -0
  27. package/src/button/button.tokens.json +72 -0
  28. package/src/button-group/button-group.css +455 -0
  29. package/src/button-group/button-group.tokens.json +11 -0
  30. package/src/calendar/calendar.css +846 -0
  31. package/src/calendar/calendar.tokens.json +67 -0
  32. package/src/card/card.css +602 -0
  33. package/src/card/card.tokens.json +68 -0
  34. package/src/carousel/carousel.css +860 -0
  35. package/src/carousel/carousel.tokens.json +62 -0
  36. package/src/cascader/cascader.css +1320 -0
  37. package/src/cascader/cascader.tokens.json +103 -0
  38. package/src/chart/chart.css +1473 -0
  39. package/src/chart/chart.tokens.json +107 -0
  40. package/src/checkbox/checkbox.css +1011 -0
  41. package/src/checkbox/checkbox.tokens.json +102 -0
  42. package/src/checkbox-group/checkbox-group.css +70 -0
  43. package/src/checkbox-group/checkbox-group.tokens.json +6 -0
  44. package/src/code-block/code-block.css +555 -0
  45. package/src/code-block/code-block.tokens.json +69 -0
  46. package/src/collapsible/collapsible.css +546 -0
  47. package/src/collapsible/collapsible.tokens.json +55 -0
  48. package/src/color-picker/color-picker.css +607 -0
  49. package/src/color-picker/color-picker.tokens.json +58 -0
  50. package/src/column-manager/column-manager.css +452 -0
  51. package/src/column-manager/column-manager.tokens.json +43 -0
  52. package/src/combobox/combobox.css +1048 -0
  53. package/src/combobox/combobox.tokens.json +112 -0
  54. package/src/command-palette/command-palette.css +1000 -0
  55. package/src/command-palette/command-palette.tokens.json +75 -0
  56. package/src/container/container.css +198 -0
  57. package/src/container/container.tokens.json +18 -0
  58. package/src/context-menu/context-menu.css +104 -0
  59. package/src/context-menu/context-menu.tokens.json +11 -0
  60. package/src/currency-field/currency-field.css +787 -0
  61. package/src/currency-field/currency-field.tokens.json +80 -0
  62. package/src/dashboard-grid/dashboard-grid.css +748 -0
  63. package/src/dashboard-grid/dashboard-grid.tokens.json +67 -0
  64. package/src/data-grid/data-grid.css +2484 -0
  65. package/src/data-grid/data-grid.tokens.json +100 -0
  66. package/src/data-list/data-list.css +455 -0
  67. package/src/data-list/data-list.tokens.json +35 -0
  68. package/src/date-field/date-field.css +560 -0
  69. package/src/date-field/date-field.tokens.json +80 -0
  70. package/src/date-picker/date-picker.css +420 -0
  71. package/src/date-picker/date-picker.tokens.json +49 -0
  72. package/src/date-range-picker/date-range-picker.css +983 -0
  73. package/src/date-range-picker/date-range-picker.tokens.json +109 -0
  74. package/src/datetime-field/datetime-field.css +850 -0
  75. package/src/datetime-field/datetime-field.tokens.json +91 -0
  76. package/src/datetime-picker/datetime-picker.css +843 -0
  77. package/src/datetime-picker/datetime-picker.tokens.json +78 -0
  78. package/src/datetime-range-picker/datetime-range-picker.css +1375 -0
  79. package/src/datetime-range-picker/datetime-range-picker.tokens.json +135 -0
  80. package/src/description-list/description-list.css +675 -0
  81. package/src/description-list/description-list.tokens.json +66 -0
  82. package/src/detail-panel/detail-panel.css +329 -0
  83. package/src/detail-panel/detail-panel.tokens.json +33 -0
  84. package/src/dialog/dialog.css +968 -0
  85. package/src/dialog/dialog.tokens.json +66 -0
  86. package/src/divider/divider.css +131 -0
  87. package/src/divider/divider.tokens.json +18 -0
  88. package/src/drawer/drawer.css +1303 -0
  89. package/src/drawer/drawer.tokens.json +75 -0
  90. package/src/entity-picker/entity-picker.css +1367 -0
  91. package/src/entity-picker/entity-picker.tokens.json +118 -0
  92. package/src/event-calendar/event-calendar.css +2148 -0
  93. package/src/event-calendar/event-calendar.tokens.json +87 -0
  94. package/src/field/field.css +707 -0
  95. package/src/field/field.tokens.json +58 -0
  96. package/src/field-group/field-group.css +226 -0
  97. package/src/field-group/field-group.tokens.json +33 -0
  98. package/src/filter-bar/filter-bar.css +1306 -0
  99. package/src/filter-bar/filter-bar.tokens.json +78 -0
  100. package/src/form/form.css +218 -0
  101. package/src/form/form.tokens.json +32 -0
  102. package/src/fullscreen/fullscreen.css +376 -0
  103. package/src/fullscreen/fullscreen.tokens.json +50 -0
  104. package/src/grid/grid.css +173 -0
  105. package/src/grid/grid.tokens.json +20 -0
  106. package/src/highlight/highlight.css +135 -0
  107. package/src/highlight/highlight.tokens.json +16 -0
  108. package/src/hover-card/hover-card.css +93 -0
  109. package/src/hover-card/hover-card.tokens.json +17 -0
  110. package/src/icon/icon.css +136 -0
  111. package/src/icon/icon.tokens.json +14 -0
  112. package/src/image/image.css +449 -0
  113. package/src/image/image.tokens.json +27 -0
  114. package/src/import-export-center/import-export-center.css +1175 -0
  115. package/src/import-export-center/import-export-center.tokens.json +84 -0
  116. package/src/inline/inline.css +149 -0
  117. package/src/inline/inline.tokens.json +14 -0
  118. package/src/inline-message/inline-message.css +396 -0
  119. package/src/inline-message/inline-message.tokens.json +19 -0
  120. package/src/kbd/kbd.css +490 -0
  121. package/src/kbd/kbd.tokens.json +40 -0
  122. package/src/link/link.css +237 -0
  123. package/src/link/link.tokens.json +25 -0
  124. package/src/list/list.css +240 -0
  125. package/src/list/list.tokens.json +42 -0
  126. package/src/list-item/list-item.css +937 -0
  127. package/src/list-item/list-item.tokens.json +114 -0
  128. package/src/listbox/listbox.css +674 -0
  129. package/src/listbox/listbox.tokens.json +72 -0
  130. package/src/loading-overlay/loading-overlay.css +730 -0
  131. package/src/loading-overlay/loading-overlay.tokens.json +48 -0
  132. package/src/markdown/markdown.css +168 -0
  133. package/src/markdown/markdown.tokens.json +18 -0
  134. package/src/menu/menu.css +817 -0
  135. package/src/menu/menu.tokens.json +78 -0
  136. package/src/menubar/menubar.css +170 -0
  137. package/src/menubar/menubar.tokens.json +32 -0
  138. package/src/message/message.css +1453 -0
  139. package/src/message/message.tokens.json +85 -0
  140. package/src/message-scroller/message-scroller.css +371 -0
  141. package/src/message-scroller/message-scroller.tokens.json +47 -0
  142. package/src/multi-select/multi-select.css +1427 -0
  143. package/src/multi-select/multi-select.tokens.json +114 -0
  144. package/src/navigation-menu/navigation-menu.css +1029 -0
  145. package/src/navigation-menu/navigation-menu.tokens.json +70 -0
  146. package/src/notification-center/notification-center.css +856 -0
  147. package/src/notification-center/notification-center.tokens.json +84 -0
  148. package/src/number-field/number-field.css +794 -0
  149. package/src/number-field/number-field.tokens.json +80 -0
  150. package/src/otp-input/otp-input.css +525 -0
  151. package/src/otp-input/otp-input.tokens.json +69 -0
  152. package/src/page/page.css +179 -0
  153. package/src/page/page.tokens.json +20 -0
  154. package/src/page-header/page-header.css +1181 -0
  155. package/src/page-header/page-header.tokens.json +71 -0
  156. package/src/pagination/pagination.css +500 -0
  157. package/src/pagination/pagination.tokens.json +56 -0
  158. package/src/popover/popover.css +526 -0
  159. package/src/popover/popover.tokens.json +60 -0
  160. package/src/progress-indicator/progress-indicator.css +1584 -0
  161. package/src/progress-indicator/progress-indicator.tokens.json +50 -0
  162. package/src/prompt-composer/prompt-composer.css +680 -0
  163. package/src/prompt-composer/prompt-composer.tokens.json +99 -0
  164. package/src/prose/prose.css +733 -0
  165. package/src/prose/prose.tokens.json +79 -0
  166. package/src/radio-group/radio-group.css +778 -0
  167. package/src/radio-group/radio-group.tokens.json +103 -0
  168. package/src/rating/rating.css +1071 -0
  169. package/src/rating/rating.tokens.json +88 -0
  170. package/src/result-state/result-state.css +980 -0
  171. package/src/result-state/result-state.tokens.json +63 -0
  172. package/src/runtime-properties.mjs +321 -0
  173. package/src/saved-views/saved-views.css +394 -0
  174. package/src/saved-views/saved-views.tokens.json +41 -0
  175. package/src/scroll-area/scroll-area.css +481 -0
  176. package/src/scroll-area/scroll-area.tokens.json +15 -0
  177. package/src/section/section.css +177 -0
  178. package/src/section/section.tokens.json +39 -0
  179. package/src/segmented-control/segmented-control.css +564 -0
  180. package/src/segmented-control/segmented-control.tokens.json +67 -0
  181. package/src/select/select.css +1593 -0
  182. package/src/select/select.tokens.json +110 -0
  183. package/src/side-navigation/side-navigation.css +1096 -0
  184. package/src/side-navigation/side-navigation.tokens.json +87 -0
  185. package/src/skeleton/skeleton.css +431 -0
  186. package/src/skeleton/skeleton.tokens.json +29 -0
  187. package/src/slider/slider.css +792 -0
  188. package/src/slider/slider.tokens.json +75 -0
  189. package/src/sparkline/sparkline.css +282 -0
  190. package/src/sparkline/sparkline.tokens.json +45 -0
  191. package/src/spinner/spinner.css +224 -0
  192. package/src/spinner/spinner.tokens.json +8 -0
  193. package/src/split-button/split-button.css +253 -0
  194. package/src/split-button/split-button.tokens.json +12 -0
  195. package/src/splitter/splitter.css +336 -0
  196. package/src/splitter/splitter.tokens.json +24 -0
  197. package/src/stack/stack.css +128 -0
  198. package/src/stack/stack.tokens.json +14 -0
  199. package/src/stat-card/stat-card.css +641 -0
  200. package/src/stat-card/stat-card.tokens.json +95 -0
  201. package/src/status-indicator/status-indicator.css +481 -0
  202. package/src/status-indicator/status-indicator.tokens.json +38 -0
  203. package/src/stepper/stepper.css +629 -0
  204. package/src/stepper/stepper.tokens.json +72 -0
  205. package/src/switch/switch.css +703 -0
  206. package/src/switch/switch.tokens.json +75 -0
  207. package/src/table/table.css +1482 -0
  208. package/src/table/table.tokens.json +69 -0
  209. package/src/table-of-contents/table-of-contents.css +533 -0
  210. package/src/table-of-contents/table-of-contents.tokens.json +53 -0
  211. package/src/tabs/tabs.css +998 -0
  212. package/src/tabs/tabs.tokens.json +82 -0
  213. package/src/tag/tag.css +768 -0
  214. package/src/tag/tag.tokens.json +85 -0
  215. package/src/tags-input/tags-input.css +757 -0
  216. package/src/tags-input/tags-input.tokens.json +84 -0
  217. package/src/text-field/text-field.css +900 -0
  218. package/src/text-field/text-field.tokens.json +87 -0
  219. package/src/textarea/textarea.css +365 -0
  220. package/src/textarea/textarea.tokens.json +59 -0
  221. package/src/time-field/time-field.css +719 -0
  222. package/src/time-field/time-field.tokens.json +86 -0
  223. package/src/time-picker/time-picker.css +665 -0
  224. package/src/time-picker/time-picker.tokens.json +66 -0
  225. package/src/time-range-picker/time-range-picker.css +1287 -0
  226. package/src/time-range-picker/time-range-picker.tokens.json +134 -0
  227. package/src/timeline/timeline.css +1596 -0
  228. package/src/timeline/timeline.tokens.json +96 -0
  229. package/src/toast/toast.css +943 -0
  230. package/src/toast/toast.tokens.json +90 -0
  231. package/src/toggle/toggle.css +290 -0
  232. package/src/toggle/toggle.tokens.json +54 -0
  233. package/src/toggle-group/toggle-group.css +463 -0
  234. package/src/toggle-group/toggle-group.tokens.json +54 -0
  235. package/src/toolbar/toolbar.css +434 -0
  236. package/src/toolbar/toolbar.tokens.json +49 -0
  237. package/src/tooltip/tooltip.css +368 -0
  238. package/src/tooltip/tooltip.tokens.json +39 -0
  239. package/src/top-navigation/top-navigation.css +1287 -0
  240. package/src/top-navigation/top-navigation.tokens.json +91 -0
  241. package/src/tour/tour.css +680 -0
  242. package/src/tour/tour.tokens.json +67 -0
  243. package/src/transfer/transfer.css +571 -0
  244. package/src/transfer/transfer.tokens.json +49 -0
  245. package/src/tree/tree.css +1000 -0
  246. package/src/tree/tree.tokens.json +80 -0
  247. package/src/typography/typography.css +211 -0
  248. package/src/typography/typography.tokens.json +31 -0
  249. package/src/upload/upload.css +1706 -0
  250. package/src/upload/upload.tokens.json +123 -0
  251. package/src/visually-hidden/visually-hidden.css +52 -0
  252. package/src/visually-hidden/visually-hidden.tokens.json +1 -0
  253. package/src/wizard/wizard.css +740 -0
  254. package/src/wizard/wizard.tokens.json +63 -0
@@ -0,0 +1,680 @@
1
+ @layer slotted.components {
2
+ /**
3
+ * A guided walk through an interface, one step at a time: a card that points
4
+ * at a target, and a spotlight that says which one.
5
+ *
6
+ * ┌─ root ─ fixed, the whole viewport, takes no pointer ──────────────┐
7
+ * │ ┌─ backdrop ─ the scrim; takes the pointer on a modal step ─────┐ │
8
+ * │ │ ┌─ spotlight ─ the cutout; its shadow is the scrim ─┐ │ │
9
+ * │ │ └───────────────────────────────────────────────────┘ │ │
10
+ * │ └───────────────────────────────────────────────────────────────┘ │
11
+ * │ ┌─ surface ─ the positioned layer, as popover's root ───────────┐ │
12
+ * │ │ ▪ focus-guard ◆ arrow │ │
13
+ * │ │ ┌─ content ─ role="dialog" ────────────────────────────────┐ │ │
14
+ * │ │ │ media │ │ │
15
+ * │ │ │ header ▸ progress ··························· close │ │ │
16
+ * │ │ │ title · description · body ▸ the only part that scrolls │ │ │
17
+ * │ │ │ footer ▸ skip ·························· prev · next │ │ │
18
+ * │ │ └───────────────────────────────────────────────────────────┘ │ │
19
+ * │ └───────────────────────────────────────────────────────────────┘ │
20
+ * └────────────────────────────────────────────────────────────────────┘
21
+ *
22
+ * The surface is popover's layer and the scrim is dialog's
23
+ * (`docs/history/designs/2026-09-24-tour-design.md` §4.1). The card's
24
+ * geometry, its arrow and its entrance repeat popover's for the reason that
25
+ * family records beside each of them.
26
+ *
27
+ * ── Why the scrim is the spotlight's shadow ──────────────────────────
28
+ *
29
+ * A scrim with a hole cut by `clip-path` has square corners, and a
30
+ * `clip-path: path()` takes no `var()`. A box the size of the cutout, with a
31
+ * spread shadow wide enough to cover any viewport, paints the scrim around a
32
+ * rounded hole and moves with two properties the motion scale can animate.
33
+ * The backdrop holds it and fades it, so the scrim arrives and leaves the way
34
+ * `dialog`'s does (`scrim-motion.test.mjs`).
35
+ */
36
+ [data-ui='tour'] {
37
+ --_scrim: var(--slotted-tour-scrim, var(--slotted-scrim, Canvas));
38
+ --_spotlight-padding: var(--slotted-tour-spotlight-padding, var(--slotted-space-3, 0.375rem));
39
+ --_spotlight-radius: var(--slotted-tour-spotlight-radius, var(--slotted-radius-md, 6px));
40
+ --_gap: var(
41
+ --slotted-tour-gap,
42
+ calc(var(--_spotlight-padding) + var(--slotted-space-4, 0.5rem))
43
+ );
44
+
45
+ --_surface: var(--slotted-tour-surface, var(--slotted-surface-raised, #ffffff));
46
+ --_text: var(--slotted-tour-text, var(--slotted-text, #0f172a));
47
+ --_border-color: var(--slotted-tour-border-color, var(--slotted-border-subtle, #e2e8f0));
48
+ --_border-width: var(--slotted-border-width-thin, 1px);
49
+ --_radius: var(--slotted-tour-radius, var(--slotted-radius-lg, 8px));
50
+ --_shadow: var(--slotted-tour-shadow, var(--slotted-shadow-lg));
51
+ --_arrow-size: var(--slotted-tour-arrow-size, 0.625rem);
52
+ --_arrow-tip-radius: var(--slotted-tour-arrow-tip-radius, var(--slotted-radius-xs, 2px));
53
+
54
+ --_inset-block: var(--slotted-tour-padding-block, var(--slotted-space-6, 1rem));
55
+ --_inset-inline: var(--slotted-tour-padding-inline, var(--slotted-space-6, 1rem));
56
+ --_row-gap: var(--slotted-control-gap-md, 0.5rem);
57
+ --_title-font-size: var(--slotted-font-size-lg, 1rem);
58
+ --_font-size: var(--slotted-font-size-md, 0.875rem);
59
+ --_measure: var(--slotted-tour-measure, 20rem);
60
+
61
+ position: fixed;
62
+ inset: 0;
63
+ z-index: calc(var(--slotted-z-index-overlay, 1000) + var(--slotted-layer-depth, 0));
64
+ isolation: isolate;
65
+ overflow: clip;
66
+ /* The frame is only a frame. The backdrop takes the pointer when the step
67
+ is modal, and the card always does. */
68
+ pointer-events: none;
69
+
70
+ /**
71
+ * The frame's own exit, which is what `@slotted/core/presence` measures
72
+ * before it lets the tour leave the document. With no duration of its own
73
+ * the frame was removed after the fifty-millisecond grace, and the scrim
74
+ * and the card, whose fades are their own, were cut off mid-way: closing
75
+ * read as a cut. `visibility` is discrete — visible for the whole of the
76
+ * arrival, hidden only at the end of the exit — so it paints nothing of its
77
+ * own and only says how long leaving takes, and its `transitionend` is the
78
+ * moment the fades beneath it have finished.
79
+ */
80
+ visibility: hidden;
81
+ transition: visibility var(--slotted-duration-short) var(--slotted-easing-exit);
82
+ }
83
+
84
+ /* Visible at once on the way in: a frame hidden until the end of a transition
85
+ cannot take focus in the frame it opens, and the card is focused then. */
86
+ [data-ui='tour'][data-open] {
87
+ visibility: visible;
88
+ transition: none;
89
+ }
90
+
91
+ [data-ui='tour'][data-size='sm'] {
92
+ --_inset-block: var(--slotted-tour-padding-block-sm, var(--slotted-space-5, 0.75rem));
93
+ --_inset-inline: var(--slotted-tour-padding-inline-sm, var(--slotted-space-5, 0.75rem));
94
+ --_title-font-size: var(--slotted-font-size-md, 0.875rem);
95
+ --_font-size: var(--slotted-font-size-sm, 0.8125rem);
96
+ --_measure: var(--slotted-tour-measure-sm, 16rem);
97
+ }
98
+
99
+ [data-ui='tour'][data-size='lg'] {
100
+ --_inset-block: var(--slotted-tour-padding-block-lg, var(--slotted-space-7, 1.25rem));
101
+ --_inset-inline: var(--slotted-tour-padding-inline-lg, var(--slotted-space-7, 1.25rem));
102
+ --_title-font-size: var(--slotted-font-size-xl, 1.125rem);
103
+ --_measure: var(--slotted-tour-measure-lg, 24rem);
104
+ }
105
+
106
+ /* ── The scrim ────────────────────────────────────────────────────────── */
107
+
108
+ /**
109
+ * With no spotlight — a centred step, or `spotlight={false}` on a modal
110
+ * step — the backdrop paints the scrim itself, as `dialog`'s does. With one,
111
+ * the spotlight's shadow paints it and the backdrop only carries the fade.
112
+ */
113
+ [data-ui='tour'] > [data-part='backdrop'] {
114
+ position: absolute;
115
+ inset: 0;
116
+ overflow: clip;
117
+ background: var(--_scrim);
118
+ opacity: 0;
119
+ transition: opacity var(--slotted-duration-short) var(--slotted-easing-exit);
120
+ }
121
+
122
+ [data-ui='tour'][data-open] > [data-part='backdrop'] {
123
+ opacity: 1;
124
+ transition: opacity var(--slotted-duration-long) var(--slotted-easing-standard);
125
+ }
126
+
127
+ [data-ui='tour'][data-spotlight] > [data-part='backdrop'] {
128
+ background: transparent;
129
+ }
130
+
131
+ /* A modal step takes every press outside the card, the target's included:
132
+ the page beneath is inert, and a press that reached it would do nothing
133
+ a reader could see. */
134
+ [data-ui='tour'][data-modal] > [data-part='backdrop'] {
135
+ pointer-events: auto;
136
+ }
137
+
138
+ /**
139
+ * Physical coordinates, for the reason `logical.test.mjs` records beside its
140
+ * exemption: the rectangle is `getBoundingClientRect()`'s, in viewport
141
+ * pixels from the left and the top.
142
+ *
143
+ * The spread is twice the larger side of the viewport, so the shadow covers
144
+ * it from any position the cutout can take, a target half off screen
145
+ * included.
146
+ */
147
+ [data-ui='tour'] > [data-part='backdrop'] > [data-part='spotlight'] {
148
+ position: absolute;
149
+ left: calc(var(--slotted-tour-target-x, 0px) - var(--_spotlight-padding));
150
+ top: calc(var(--slotted-tour-target-y, 0px) - var(--_spotlight-padding));
151
+ width: calc(var(--slotted-tour-target-width, 0px) + 2 * var(--_spotlight-padding));
152
+ height: calc(var(--slotted-tour-target-height, 0px) + 2 * var(--_spotlight-padding));
153
+ border-radius: var(--_spotlight-radius);
154
+ box-shadow: 0 0 0 200vmax var(--_scrim);
155
+ /* The hole is the spotlight's own ground; `light` and `close` paint it. */
156
+ background-color: transparent;
157
+ }
158
+
159
+ /* ── How the cutout moves ─────────────────────────────────────────────── */
160
+
161
+ /**
162
+ * At rest the cutout has no transition, and follows its target frame by
163
+ * frame: a scroll, a resize, a dialog animating in under it. A transition
164
+ * here made the cutout trail every scroll by a third of a second, and on the
165
+ * first step it flew in from the corner of the page — the place a cutout
166
+ * with no rectangle yet stands — to its target.
167
+ *
168
+ * The binding writes `data-spot` on the root for the three movements a
169
+ * reader's eye should follow, each for the long step on the standard curve,
170
+ * which is the scrim's own arrival: the eye reads the cutout and the scrim
171
+ * as one layer.
172
+ *
173
+ * - `travel`: from one target to the next, so the reader sees where the
174
+ * tour went rather than a light going out in one place and on in another.
175
+ * - `light`: a cutout opening where there was none — the first step, or the
176
+ * step after a centred one. The hole starts in the scrim's ink and clears,
177
+ * so the target is lit rather than revealed by a cut.
178
+ * - `close`: the step after is centred. The hole fills back with the scrim
179
+ * before the cutout goes, and the backdrop, which paints the same ink,
180
+ * takes over without a seam.
181
+ */
182
+ [data-ui='tour'][data-spot='travel'] > [data-part='backdrop'] > [data-part='spotlight'] {
183
+ transition-property: left, top, width, height;
184
+ transition-duration: var(--slotted-duration-long);
185
+ transition-timing-function: var(--slotted-easing-standard);
186
+ }
187
+
188
+ [data-ui='tour'][data-spot='light'] > [data-part='backdrop'] > [data-part='spotlight'] {
189
+ animation: slotted-tour-light var(--slotted-duration-long) var(--slotted-easing-standard);
190
+ }
191
+
192
+ [data-ui='tour'][data-spot='close'] > [data-part='backdrop'] > [data-part='spotlight'] {
193
+ background-color: var(--_scrim);
194
+ transition: background-color var(--slotted-duration-long) var(--slotted-easing-standard);
195
+ }
196
+
197
+ @keyframes slotted-tour-light {
198
+ from {
199
+ background-color: var(--_scrim);
200
+ }
201
+ }
202
+
203
+ /* ── The layer the card stands in ─────────────────────────────────────── */
204
+
205
+ /**
206
+ * `packages/core/src/positioner` writes `left`, `top` and `data-side` on it.
207
+ * Only the block inset is stated, for the reason `tooltip.css` sets out: the
208
+ * positioner writes `left` and never `right`, and `inset-inline-start`
209
+ * resolves to `right` in a right-to-left document.
210
+ */
211
+ [data-ui='tour'] > [data-part='surface'] {
212
+ position: absolute;
213
+ inset-block-start: 0;
214
+
215
+ box-sizing: border-box;
216
+ padding: var(--_gap);
217
+ inline-size: max-content;
218
+ max-inline-size: min(var(--_measure), var(--slotted-positioner-available-width, 100vw));
219
+
220
+ pointer-events: none;
221
+
222
+ /**
223
+ * The card arrives and leaves by its ink alone, as `dialog`'s panel does,
224
+ * and for the reason that panel records (DG4): a box of text moved by a
225
+ * fraction of a pixel has every letter resampled on every frame of the
226
+ * move, and the words lose their edges and get them back. The card slid
227
+ * four pixels towards its target for one iteration and read as careless
228
+ * beside the cutout, which is the movement that tells the eye where to
229
+ * look; the card only has to appear where the cutout has landed.
230
+ *
231
+ * Leaving is the short step on the exit curve — a reader who pressed Next
232
+ * is not waiting for the old card — and arriving the long step on the
233
+ * standard curve, with the scrim on opening and after the cutout on every
234
+ * step after.
235
+ */
236
+ opacity: 0;
237
+ transition: opacity var(--slotted-duration-short) var(--slotted-easing-exit);
238
+ }
239
+
240
+ /**
241
+ * `data-ready` is the binding's: the card of the step on screen has been
242
+ * placed — measured by the positioner, or centred — and is not leaving. A
243
+ * card between two steps, still at the old target with the new words, is
244
+ * never drawn.
245
+ */
246
+ [data-ui='tour'][data-open] > [data-part='surface'][data-ready]:not([data-leaving]) {
247
+ opacity: 1;
248
+ transition: opacity var(--slotted-duration-long) var(--slotted-easing-standard);
249
+ }
250
+
251
+ [data-ui='tour'] > [data-part='surface'][data-detached] {
252
+ visibility: hidden;
253
+ }
254
+
255
+ /**
256
+ * A step with no target, or one whose target could not be found, stands in
257
+ * the middle of the viewport as `dialog`'s panel does. Auto margins on an
258
+ * absolutely placed box with all four insets at zero centre it without a
259
+ * transform, which the entrance owns.
260
+ */
261
+ [data-ui='tour'] > [data-part='surface'][data-placement='centered'] {
262
+ inset: 0;
263
+ margin: auto;
264
+ block-size: max-content;
265
+ max-inline-size: min(var(--_measure), 100%);
266
+ max-block-size: 100%;
267
+ }
268
+
269
+ /**
270
+ * A phone's notch, its rounded corners and its home indicator are outside
271
+ * the safe area, and a centred card must not stand under them. `env()`
272
+ * answers zero everywhere else.
273
+ */
274
+ @supports (inset: env(safe-area-inset-top)) {
275
+ [data-ui='tour'] > [data-part='surface'][data-placement='centered'] {
276
+ inset: env(safe-area-inset-top, 0) env(safe-area-inset-right, 0)
277
+ env(safe-area-inset-bottom, 0) env(safe-area-inset-left, 0);
278
+ }
279
+ }
280
+
281
+ /**
282
+ * On a compact viewport — below the library's 600-pixel size class — the
283
+ * binding writes `data-compact`, turns a lateral side into a vertical one,
284
+ * and the card takes the width the positioner says is left rather than its
285
+ * measure: a 20-rem card beside a button does not fit in 375 pixels, and
286
+ * one narrower than the screen leaves a sliver of page on either side
287
+ * that nobody can use.
288
+ */
289
+ [data-ui='tour'][data-compact] > [data-part='surface'][data-placement='anchored'] {
290
+ /* Capped, so layout="compact" forced on a wide screen is a wide card and
291
+ not a banner across the page. */
292
+ --_compact-measure: var(--slotted-tour-measure-compact, 32rem);
293
+ inline-size: min(var(--slotted-positioner-available-width, 100vw), var(--_compact-measure));
294
+ max-inline-size: min(var(--slotted-positioner-available-width, 100vw), var(--_compact-measure));
295
+ }
296
+
297
+ [data-ui='tour'] > [data-part='surface'][data-placement='centered'] > [data-part='arrow'] {
298
+ display: none;
299
+ }
300
+
301
+ [data-ui='tour'] > [data-part='surface'] > [data-part='focus-guard'] {
302
+ position: absolute;
303
+ inline-size: 1px;
304
+ block-size: 1px;
305
+ padding: 0;
306
+ overflow: hidden;
307
+ opacity: 0;
308
+ outline: none;
309
+ pointer-events: none;
310
+ }
311
+
312
+ /* ── The card ─────────────────────────────────────────────────────────── */
313
+
314
+ [data-ui='tour'] > [data-part='surface'] > [data-part='content'] {
315
+ box-sizing: border-box;
316
+ display: flex;
317
+ flex-direction: column;
318
+ max-inline-size: 100%;
319
+ max-block-size: calc(var(--slotted-positioner-available-height, 100vh) - 2 * var(--_gap));
320
+
321
+ border: var(--_border-width) solid var(--_border-color);
322
+ border-radius: var(--_radius);
323
+ background: var(--_surface);
324
+ color: var(--_text);
325
+ box-shadow: var(--_shadow);
326
+ /* `clip`, as popover's: the media's corners follow the card's, and the
327
+ card is no scroll container of its own. */
328
+ overflow: clip;
329
+
330
+ font-family: var(--slotted-font-family-sans, ui-sans-serif, system-ui, sans-serif);
331
+ font-size: var(--_font-size);
332
+ line-height: var(--slotted-line-height-md, 1.5);
333
+ text-align: start;
334
+
335
+ pointer-events: auto;
336
+ outline: none;
337
+ }
338
+
339
+ [data-ui='tour'] > [data-part='surface'][data-placement='centered'] > [data-part='content'] {
340
+ max-block-size: calc(100vh - 2 * var(--_gap));
341
+ }
342
+
343
+ [data-ui='tour'] > [data-part='surface'] > [data-part='content']:focus-visible {
344
+ outline: var(--slotted-focus-ring-width, 2px) solid
345
+ var(--slotted-focus-ring-color, currentColor);
346
+ outline-offset: calc(-1 * var(--slotted-focus-ring-width, 2px));
347
+ }
348
+
349
+ /**
350
+ * The picture that opens a step. Full bleed, so it reads as the card's own
351
+ * top rather than as an image placed inside it; its height is its own, and
352
+ * a consumer who wants a fixed frame writes `aspect-ratio` on it.
353
+ */
354
+ [data-ui='tour'] [data-part='content'] > [data-part='media'] {
355
+ display: block;
356
+ flex: none;
357
+ inline-size: 100%;
358
+ max-inline-size: 100%;
359
+ block-size: auto;
360
+ object-fit: cover;
361
+ border-block-end: var(--_border-width) solid var(--_border-color);
362
+ }
363
+
364
+ [data-ui='tour'] [data-part='content'] > [data-part='media'] > :is(img, video, svg) {
365
+ display: block;
366
+ inline-size: 100%;
367
+ block-size: auto;
368
+ }
369
+
370
+ /**
371
+ * Where the reader is, and the way out. The row is one control tall, so the
372
+ * close control sits in it without stretching it, and the progress text is
373
+ * centred on the same line.
374
+ */
375
+ [data-ui='tour'] [data-part='content'] > [data-part='header'] {
376
+ display: flex;
377
+ flex: none;
378
+ /* `start`, so a progress text a translation wraps keeps the close control
379
+ level with its first line; the text's own inset centres that line on
380
+ the control. */
381
+ align-items: flex-start;
382
+ gap: var(--_row-gap);
383
+ min-block-size: var(--slotted-control-height-sm, 30px);
384
+ padding-block-start: calc(var(--_inset-block) - var(--slotted-space-3, 0.375rem));
385
+ padding-inline: var(--_inset-inline)
386
+ calc(var(--_inset-inline) - var(--slotted-space-3, 0.375rem));
387
+ }
388
+
389
+ [data-ui='tour'] [data-part='content'] > [data-part='header'] > [data-part='progress'] {
390
+ flex: 1 1 auto;
391
+ min-inline-size: 0;
392
+ margin: 0;
393
+ font-size: var(--slotted-font-size-xs, 0.75rem);
394
+ font-weight: var(--slotted-font-weight-medium, 500);
395
+ line-height: var(--slotted-line-height-xs, 1.4);
396
+ color: var(--slotted-text-muted, #5b6b7f);
397
+ font-variant-numeric: tabular-nums;
398
+ /* Half of what the close control is taller than one line of this text,
399
+ above and below, so the first line sits on the control's centre. */
400
+ padding-block: calc((var(--slotted-control-height-sm, 30px) - 1lh) / 2);
401
+ /* "2 of 5" in a right-to-left page reads "of 5 2" without it: the numbers
402
+ are weak characters and take the paragraph's direction. */
403
+ unicode-bidi: plaintext;
404
+ }
405
+
406
+ [data-ui='tour'] [data-part='content'] > [data-part='header'] > [data-part='close'] {
407
+ flex: none;
408
+ display: inline-flex;
409
+ align-items: center;
410
+ justify-content: center;
411
+ box-sizing: border-box;
412
+ inline-size: var(--slotted-control-height-sm, 30px);
413
+ block-size: var(--slotted-control-height-sm, 30px);
414
+ margin-inline-start: auto;
415
+ padding: 0;
416
+
417
+ border: 0;
418
+ border-radius: var(--slotted-control-radius, var(--slotted-radius-sm, 4px));
419
+ background: transparent;
420
+ color: var(--slotted-text-muted, #5b6b7f);
421
+ cursor: pointer;
422
+
423
+ transition:
424
+ background-color var(--slotted-control-transition-duration, var(--slotted-duration-short))
425
+ var(--slotted-control-transition-easing, var(--slotted-easing-standard)),
426
+ color var(--slotted-control-transition-duration, var(--slotted-duration-short))
427
+ var(--slotted-control-transition-easing, var(--slotted-easing-standard));
428
+ }
429
+
430
+ [data-ui='tour']
431
+ [data-part='content']
432
+ > [data-part='header']
433
+ > [data-part='close']:enabled:hover {
434
+ background: var(--slotted-tone-secondary-subtle-hover, rgb(15 23 42 / 0.06));
435
+ color: var(--_text);
436
+ }
437
+
438
+ [data-ui='tour']
439
+ [data-part='content']
440
+ > [data-part='header']
441
+ > [data-part='close']:enabled:active {
442
+ background: var(--slotted-tone-secondary-subtle-active, rgb(15 23 42 / 0.1));
443
+ }
444
+
445
+ [data-ui='tour']
446
+ [data-part='content']
447
+ > [data-part='header']
448
+ > [data-part='close']:focus-visible {
449
+ outline: var(--slotted-focus-ring-width, 2px) solid
450
+ var(--slotted-focus-ring-color, currentColor);
451
+ outline-offset: calc(-1 * var(--slotted-focus-ring-width, 2px));
452
+ }
453
+
454
+ [data-ui='tour'] [data-part='content'] > [data-part='title'] {
455
+ flex: none;
456
+ margin: 0;
457
+ padding-inline: var(--_inset-inline);
458
+ padding-block-start: var(--slotted-space-2, 0.25rem);
459
+ font-family: var(
460
+ --slotted-font-family-heading,
461
+ var(--slotted-font-family-sans, ui-sans-serif, system-ui, sans-serif)
462
+ );
463
+ font-size: var(--_title-font-size);
464
+ font-weight: var(--slotted-font-weight-semibold, 600);
465
+ line-height: var(--slotted-line-height-lg, 1.5);
466
+ color: var(--_text);
467
+ overflow-wrap: anywhere;
468
+ unicode-bidi: plaintext;
469
+ }
470
+
471
+ /* A card with no header — the consumer composed their own — starts at the
472
+ title, and the title takes the header's inset. */
473
+ [data-ui='tour'] [data-part='content'] > [data-part='title']:first-child,
474
+ [data-ui='tour'] [data-part='content'] > [data-part='media'] + [data-part='title'] {
475
+ padding-block-start: var(--_inset-block);
476
+ }
477
+
478
+ [data-ui='tour'] [data-part='content'] > [data-part='description'] {
479
+ flex: none;
480
+ margin: 0;
481
+ padding-inline: var(--_inset-inline);
482
+ padding-block-start: var(--slotted-space-2, 0.25rem);
483
+ color: var(--slotted-text-muted, #5b6b7f);
484
+ overflow-wrap: anywhere;
485
+ unicode-bidi: plaintext;
486
+ }
487
+
488
+ /**
489
+ * The step's own content, and the only part that scrolls. The shadow at each
490
+ * edge is popover's, for the reason recorded there.
491
+ */
492
+ [data-ui='tour'] [data-part='content'] > [data-part='body'] {
493
+ flex: 1 1 auto;
494
+ min-block-size: 0;
495
+ overflow-x: hidden;
496
+ overflow-y: auto;
497
+ overflow-inline: hidden;
498
+ overflow-block: auto;
499
+ overscroll-behavior: contain;
500
+ overflow-wrap: anywhere;
501
+ unicode-bidi: plaintext;
502
+ margin-block-start: var(--_row-gap);
503
+ padding-inline: var(--_inset-inline);
504
+
505
+ background:
506
+ linear-gradient(var(--_surface) 30%, transparent) center top / 100%
507
+ var(--slotted-space-4, 0.5rem) no-repeat local,
508
+ linear-gradient(transparent, var(--_surface) 70%) center bottom / 100%
509
+ var(--slotted-space-4, 0.5rem) no-repeat local,
510
+ radial-gradient(
511
+ farthest-side at 50% 0,
512
+ color-mix(in oklab, var(--_text) 12%, transparent),
513
+ transparent
514
+ )
515
+ center top / 100% var(--slotted-space-3, 0.375rem) no-repeat scroll,
516
+ radial-gradient(
517
+ farthest-side at 50% 100%,
518
+ color-mix(in oklab, var(--_text) 12%, transparent),
519
+ transparent
520
+ )
521
+ center bottom / 100% var(--slotted-space-3, 0.375rem) no-repeat scroll;
522
+ }
523
+
524
+ /**
525
+ * Skip at the start, where it is out of the way of the hand moving forward;
526
+ * back and next together at the end, next last.
527
+ *
528
+ * When the row cannot hold all three, back and next must stay a pair and
529
+ * skip must be the one that leaves. A plain wrapping row breaks at the last
530
+ * item that does not fit, which put next on a line of its own under back
531
+ * the first time a translation lengthened the labels. So the line is filled
532
+ * from the end: `order` makes next, back, skip the sequence the row fills,
533
+ * `row-reverse` draws that sequence from the inline end, and `wrap-reverse`
534
+ * stacks the line skip was pushed onto above the pair. The document order,
535
+ * which is the tab order, is still skip, back, next, and so is the picture.
536
+ */
537
+ [data-ui='tour'] [data-part='content'] > [data-part='footer'] {
538
+ display: flex;
539
+ flex: none;
540
+ flex-direction: row-reverse;
541
+ flex-wrap: wrap-reverse;
542
+ align-items: center;
543
+ justify-content: flex-start;
544
+ gap: var(--_row-gap);
545
+ padding-block: var(--_inset-block);
546
+ padding-inline: var(--_inset-inline);
547
+ }
548
+
549
+ [data-ui='tour'] [data-part='content'] > [data-part='footer'] > [data-part='next'] {
550
+ order: 1;
551
+ }
552
+
553
+ [data-ui='tour'] [data-part='content'] > [data-part='footer'] > [data-part='prev'] {
554
+ order: 2;
555
+ }
556
+
557
+ [data-ui='tour'] [data-part='content'] > [data-part='footer'] > [data-part='skip'] {
558
+ order: 3;
559
+ margin-inline-end: auto;
560
+ /* A ghost button's label sits a button's padding in from its edge; pulled
561
+ back by that padding, the word lines up with the title above it. */
562
+ margin-inline-start: calc(-1 * var(--slotted-control-padding-inline-sm, 10px));
563
+ }
564
+
565
+ /* ── The arrow ────────────────────────────────────────────────────────── */
566
+
567
+ /**
568
+ * popover's, in physical properties and over the card, for the two reasons
569
+ * `tooltip.css` sets out.
570
+ */
571
+ [data-ui='tour'] > [data-part='surface'] > [data-part='arrow'] {
572
+ /**
573
+ * Where the arrow may stand: on the straight run of the card's edge. The
574
+ * positioner keeps it `arrowPadding` from the edge of the **layer**, and
575
+ * the card stands a `gap` inside the layer — the spotlight's padding and a
576
+ * step of space, more than the padding — so beside a target in a corner
577
+ * the arrow landed past the card's rounded corner, and half of the rotated
578
+ * square that the card normally hides stood outside it: a whole diamond.
579
+ * Clamped here, the arrow always meets a straight edge, whatever padding,
580
+ * gap or radius a theme sets; it is the tip that may then be off the
581
+ * target's centre by a few pixels, which reads, where a diamond does not.
582
+ */
583
+ --_arrow-min: calc(var(--_gap) + var(--_radius));
584
+ --_arrow-max-x: calc(100% - var(--_gap) - var(--_radius) - var(--_arrow-size));
585
+ --_arrow-x: clamp(
586
+ var(--_arrow-min),
587
+ var(--slotted-positioner-arrow-x, 0px),
588
+ var(--_arrow-max-x)
589
+ );
590
+ --_arrow-y: clamp(
591
+ var(--_arrow-min),
592
+ var(--slotted-positioner-arrow-y, 0px),
593
+ calc(100% - var(--_gap) - var(--_radius) - var(--_arrow-size))
594
+ );
595
+ position: absolute;
596
+ z-index: 1;
597
+ inline-size: var(--_arrow-size);
598
+ block-size: var(--_arrow-size);
599
+ rotate: 45deg;
600
+ background: var(--_surface);
601
+ border: 0 solid var(--_border-color);
602
+ }
603
+
604
+ [data-ui='tour'] > [data-part='surface'][data-side='top'] > [data-part='arrow'] {
605
+ left: var(--_arrow-x);
606
+ top: auto;
607
+ bottom: calc(var(--_gap) - var(--_arrow-size) / 2);
608
+ border-right-width: var(--_border-width);
609
+ border-bottom-width: var(--_border-width);
610
+ border-bottom-right-radius: var(--_arrow-tip-radius);
611
+ }
612
+
613
+ [data-ui='tour'] > [data-part='surface'][data-side='bottom'] > [data-part='arrow'] {
614
+ left: var(--_arrow-x);
615
+ top: calc(var(--_gap) - var(--_arrow-size) / 2);
616
+ bottom: auto;
617
+ border-top-width: var(--_border-width);
618
+ border-left-width: var(--_border-width);
619
+ border-top-left-radius: var(--_arrow-tip-radius);
620
+ }
621
+
622
+ [data-ui='tour'] > [data-part='surface'][data-side='left'] > [data-part='arrow'] {
623
+ top: var(--_arrow-y);
624
+ left: auto;
625
+ right: calc(var(--_gap) - var(--_arrow-size) / 2);
626
+ border-top-width: var(--_border-width);
627
+ border-right-width: var(--_border-width);
628
+ border-top-right-radius: var(--_arrow-tip-radius);
629
+ }
630
+
631
+ [data-ui='tour'] > [data-part='surface'][data-side='right'] > [data-part='arrow'] {
632
+ top: var(--_arrow-y);
633
+ left: calc(var(--_gap) - var(--_arrow-size) / 2);
634
+ right: auto;
635
+ border-bottom-width: var(--_border-width);
636
+ border-left-width: var(--_border-width);
637
+ border-bottom-left-radius: var(--_arrow-tip-radius);
638
+ }
639
+
640
+ /**
641
+ * The arrow is painted over the card to hide the card's border under its
642
+ * base, so it also hides the focus ring there: the first capture of a
643
+ * focused card showed the ring broken where the arrow meets it. While the
644
+ * card holds focus the arrow's two outer edges are drawn as the ring, and
645
+ * the ring runs round the point instead of stopping at it.
646
+ */
647
+ [data-ui='tour']
648
+ > [data-part='surface']:has(> [data-part='content']:focus-visible)
649
+ > [data-part='arrow'] {
650
+ --_border-width: var(--slotted-focus-ring-width, 2px);
651
+ border-color: var(--slotted-focus-ring-color, currentColor);
652
+ }
653
+
654
+ /**
655
+ * The Angular parts are directives on the consumer's own elements and cannot
656
+ * remove their host, so a part with nothing to show — a step with no
657
+ * description, Back on the first step — writes `hidden`. Every part here
658
+ * sets its own `display`, which outranks the user agent's rule for the
659
+ * attribute, so the attribute is restated last.
660
+ */
661
+ [data-ui='tour'] [data-part='content'] [data-part][hidden] {
662
+ display: none;
663
+ }
664
+
665
+ @media (forced-colors: active) {
666
+ /*
667
+ * Forced colours drop every `box-shadow`, and the scrim is one: the page
668
+ * stays in full view and the cutout would mark nothing. The outline is
669
+ * what forced colours keep, so the target is ringed in the system's
670
+ * highlight instead of being lit.
671
+ */
672
+ [data-ui='tour'] > [data-part='backdrop'] > [data-part='spotlight'] {
673
+ outline: var(--slotted-focus-ring-width, 2px) solid Highlight;
674
+ }
675
+
676
+ [data-ui='tour'][data-spotlight] > [data-part='backdrop'] {
677
+ background: transparent;
678
+ }
679
+ }
680
+ }