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

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,449 @@
1
+ @layer slotted.components {
2
+ /**
3
+ * ── The image ──────────────────────────────────────────────────────────
4
+ *
5
+ * A picture, and what stands in for it before it arrives and after it
6
+ * fails. The `<img>` is the platform's and keeps every attribute the
7
+ * platform gives it; the family adds the two layers under it, a fit, a
8
+ * focal point, a corner and an edge, and the frame it composes from
9
+ * `aspect-ratio` when a ratio was decided.
10
+ *
11
+ * Two attributes on every selector that reaches the root, always. The
12
+ * layers are descendants rather than children, because a frame from
13
+ * `aspect-ratio` may stand between the root and them; a rule for a layer
14
+ * therefore names the family and the part, never the depth.
15
+ *
16
+ * Nothing is reserved without a decision. A root with no ratio and an
17
+ * `<img>` with no `width`/`height` has no height until the picture reports,
18
+ * which is the platform's own behaviour and the honest one: a family that
19
+ * guessed a ratio would guess it wrong for half its consumers. `width` and
20
+ * `height` on the element, or `ratio` on the root, are the two ways to
21
+ * decide, and both reserve the box before the first byte arrives.
22
+ */
23
+ [data-ui='image'][data-part='root'] {
24
+ --_fit: cover;
25
+ --_position: var(--slotted-image-position, center);
26
+ --_radius: var(--slotted-image-radius, var(--slotted-radius-none, 0px));
27
+ /**
28
+ * The edge is drawn inside the box as an inset ring, so switching
29
+ * `bordered` on moves nothing; it is transparent until asked for, and the
30
+ * interactive root paints it on hover whether or not it was asked for.
31
+ */
32
+ --_edge: transparent;
33
+ --_edge-width: var(--slotted-image-border-width, var(--slotted-border-width-thin, 1px));
34
+ /**
35
+ * The ground under a picture that is not there yet, and under one that
36
+ * never came. `surface-sunken` is the one surface a step below the page
37
+ * in every scheme, so the reserved box reads as a hole the picture will
38
+ * fill rather than as a blank.
39
+ */
40
+ --_ground: var(--slotted-image-placeholder-background, var(--slotted-surface-sunken));
41
+ /**
42
+ * The ground has an edge of its own, because in the `high-contrast`
43
+ * scheme `surface-sunken` is the page's own white and a reserved box with
44
+ * no edge is no box at all: the loading tile vanished and the fallback
45
+ * glyph floated on nothing (measured in the high-contrast capture). It is
46
+ * the same subtle edge `avatar` gives a loaded photograph, and it leaves
47
+ * with the ground, so a picture that arrived carries no edge it did not
48
+ * ask for.
49
+ */
50
+ --_ground-edge: inset 0 0 0 var(--_edge-width) var(--slotted-border-subtle, ButtonBorder);
51
+
52
+ /**
53
+ * Nothing behind a picture that arrived, so a `contain` fit shows the
54
+ * page through the letterbox rather than a ground the consumer did not
55
+ * ask for. A product that wants one — logotypes on a neutral tile — sets
56
+ * the token, and the fallback and placeholder keep their own.
57
+ */
58
+ background-color: var(--slotted-image-background, transparent);
59
+ border-radius: var(--_radius);
60
+ box-sizing: border-box;
61
+ /**
62
+ * A grid of one cell: the picture, the placeholder and the fallback all
63
+ * take it, and the last painted is on top, which is the order they are
64
+ * written in and the reason no layer carries a `z-index`. A frame from
65
+ * `aspect-ratio` between the root and the layers is a grid of one cell
66
+ * of its own, so the layering holds at either depth.
67
+ */
68
+ display: grid;
69
+ grid-template-columns: minmax(0, 1fr);
70
+ grid-template-rows: 1fr;
71
+ /**
72
+ * `intrinsic` shrinks the box to the picture and never past the
73
+ * container: the picture is its own size, and the layers are the same
74
+ * size as the picture rather than the size of the column it is in.
75
+ */
76
+ inline-size: fit-content;
77
+ isolation: isolate;
78
+ max-inline-size: 100%;
79
+ min-inline-size: 0;
80
+ /**
81
+ * The corner is cut on the root and inherited by nothing: a rounded box
82
+ * whose children reach its edge clips them here, which is what rounds
83
+ * the photograph, the ground and the blurred preview together. `clip`
84
+ * rather than `hidden` because the box is not a scroll container and
85
+ * must not become one for a focus ring or a caption.
86
+ */
87
+ overflow: clip;
88
+ position: relative;
89
+ /**
90
+ * `render` can make the root an `<a>`, and the browser writes a line
91
+ * under an anchor's content; a `<button>` draws two pixels of `outset`.
92
+ * The family states its own edge, so both are replaced rather than reset.
93
+ */
94
+ appearance: none;
95
+ border: 0;
96
+ padding: 0;
97
+ text-decoration: none;
98
+ }
99
+
100
+ /**
101
+ * Every direct child takes the one cell, the frame from `aspect-ratio`
102
+ * included. The layers name their cell themselves, but the frame is
103
+ * another family's root and this sheet does not reach into it; without
104
+ * this rule the frame was auto-placed into a second row under the edge
105
+ * pseudo-element, which had taken the first cell explicitly, and the edge
106
+ * stood 0.016 pixels tall above the picture instead of over it (measured
107
+ * with hover forced on the `interactive` scene).
108
+ */
109
+ [data-ui='image'][data-part='root'] > * {
110
+ grid-area: 1 / 1;
111
+ min-inline-size: 0;
112
+ }
113
+
114
+ /* ── Layout ───────────────────────────────────────────────────────────── */
115
+
116
+ /**
117
+ * `fill` is Next's and NgOptimizedImage's: the box is the parent's in both
118
+ * axes and `fit` decides what the picture does about it. It is also the
119
+ * layout a decided ratio implies, since the frame is then the box. A parent
120
+ * with no height of its own resolves the block size to `auto`, and the
121
+ * picture keeps its intrinsic height; the family does not measure the
122
+ * parent to do otherwise.
123
+ */
124
+ [data-ui='image'][data-part='root'][data-layout='fill'] {
125
+ block-size: 100%;
126
+ inline-size: 100%;
127
+ }
128
+
129
+ /**
130
+ * `framed` is the layout a decided ratio implies: the width is the
131
+ * column's and the height is the frame's, and nothing else. It is not
132
+ * `fill`, because a list item stretched to its row is a parent with a
133
+ * definite height, and `block-size: 100%` there took the whole row and
134
+ * pushed the caption under the picture out of the item (measured in the
135
+ * first capture of the `in-context` scene).
136
+ */
137
+ [data-ui='image'][data-part='root'][data-layout='framed'] {
138
+ inline-size: 100%;
139
+ }
140
+
141
+ /* ── Fit: what the picture does about a box that is not its shape ─────── */
142
+
143
+ /**
144
+ * The five values `object-fit` has, on a private property the picture and
145
+ * the blurred preview both read, so the preview is cropped exactly where
146
+ * the picture will be. `cover` is stated in the base rule.
147
+ */
148
+ [data-ui='image'][data-part='root'][data-fit='contain'] {
149
+ --_fit: contain;
150
+ }
151
+
152
+ [data-ui='image'][data-part='root'][data-fit='fill'] {
153
+ --_fit: fill;
154
+ }
155
+
156
+ [data-ui='image'][data-part='root'][data-fit='none'] {
157
+ --_fit: none;
158
+ }
159
+
160
+ [data-ui='image'][data-part='root'][data-fit='scale-down'] {
161
+ --_fit: scale-down;
162
+ }
163
+
164
+ /* ── Position: the focal point the crop keeps ─────────────────────────── */
165
+
166
+ /**
167
+ * `object-position` has no logical keywords, so `start` and `end` are the
168
+ * family's: they resolve to the physical side the writing direction puts
169
+ * them on, which is what a portrait cropped to its subject needs when the
170
+ * page is mirrored. `center` is stated in the base rule; `custom` is the
171
+ * token written on the instance, read there.
172
+ */
173
+ [data-ui='image'][data-part='root'][data-position='top'] {
174
+ --_position: top;
175
+ }
176
+
177
+ [data-ui='image'][data-part='root'][data-position='bottom'] {
178
+ --_position: bottom;
179
+ }
180
+
181
+ [data-ui='image'][data-part='root'][data-position='start'] {
182
+ --_position: left;
183
+ }
184
+
185
+ [data-ui='image'][data-part='root'][data-position='end'] {
186
+ --_position: right;
187
+ }
188
+
189
+ [data-ui='image'][data-part='root'][data-position='start']:dir(rtl) {
190
+ --_position: right;
191
+ }
192
+
193
+ [data-ui='image'][data-part='root'][data-position='end']:dir(rtl) {
194
+ --_position: left;
195
+ }
196
+
197
+ /* ── Radius ───────────────────────────────────────────────────────────── */
198
+
199
+ [data-ui='image'][data-part='root'][data-radius='sm'] {
200
+ --_radius: var(--slotted-image-radius, var(--slotted-radius-sm, 4px));
201
+ }
202
+
203
+ [data-ui='image'][data-part='root'][data-radius='md'] {
204
+ --_radius: var(--slotted-image-radius, var(--slotted-radius-md, 6px));
205
+ }
206
+
207
+ [data-ui='image'][data-part='root'][data-radius='lg'] {
208
+ --_radius: var(--slotted-image-radius, var(--slotted-radius-lg, 8px));
209
+ }
210
+
211
+ /* ── The edge ─────────────────────────────────────────────────────────── */
212
+
213
+ /**
214
+ * A photograph of a white wall on a white page has no boundary, and
215
+ * `border-subtle` is the token that separates one surface from another
216
+ * everywhere else. Drawn as the inset ring the base rule already holds, so
217
+ * the picture is the same size with the edge as without it.
218
+ */
219
+ [data-ui='image'][data-part='root'][data-bordered] {
220
+ --_edge: var(--slotted-border-subtle, ButtonBorder);
221
+ }
222
+
223
+ /**
224
+ * The edge is a pseudo-element over the layers rather than an inset shadow
225
+ * on the root, because an inset shadow is painted under the children and
226
+ * the picture covered it whole (measured in the first capture of the
227
+ * `in-context` scene). It takes the one cell like every layer, is generated
228
+ * last and so painted last, and takes no pointer, so a press on the edge
229
+ * is a press on the link the root may be.
230
+ */
231
+ [data-ui='image'][data-part='root']::after {
232
+ border-radius: inherit;
233
+ box-shadow: inset 0 0 0 var(--_edge-width) var(--_edge);
234
+ content: '';
235
+ grid-area: 1 / 1;
236
+ pointer-events: none;
237
+ }
238
+
239
+ /* ── The picture ──────────────────────────────────────────────────────── */
240
+
241
+ /**
242
+ * `<picture>` is the platform's element for art direction and it is inline
243
+ * by default, with no box a percentage could resolve against. `contents`
244
+ * takes its box away, so the `<img>` inside it is the grid item and every
245
+ * rule below reaches it the same way with sources and without.
246
+ */
247
+ [data-ui='image'] [data-part='picture'] {
248
+ display: contents;
249
+ }
250
+
251
+ /**
252
+ * Always rendered, never taken out of the DOM: the `alt` is the accessible
253
+ * name in every status, which is what keeps a broken picture announced as
254
+ * what it was meant to show. It lies over the ground at opacity nought and
255
+ * rises to one when the load reports, so a photograph that arrives four
256
+ * hundred milliseconds after the page fades in rather than snapping. The
257
+ * theme collapses the duration under `prefers-reduced-motion`.
258
+ *
259
+ * In `intrinsic` the picture is its own size and shrinks to the box; in
260
+ * `fill` and inside a frame it is the box, and `--_fit` decides the crop.
261
+ * The rule names the root part, which is what puts it above the frame's
262
+ * own `contain` — `aspect-ratio` reserves without cropping, and this
263
+ * family is the one that decides to.
264
+ */
265
+ [data-ui='image'][data-part='root'] [data-part='image'] {
266
+ block-size: auto;
267
+ display: block;
268
+ grid-area: 1 / 1;
269
+ max-inline-size: 100%;
270
+ min-inline-size: 0;
271
+ object-fit: var(--_fit);
272
+ object-position: var(--_position);
273
+ opacity: 0;
274
+ transition: opacity var(--slotted-duration-short) var(--slotted-easing-standard);
275
+ }
276
+
277
+ [data-ui='image'][data-part='root'][data-layout='fill'] [data-part='image'],
278
+ [data-ui='image'][data-part='root'][data-layout='framed'] [data-part='image'] {
279
+ block-size: 100%;
280
+ inline-size: 100%;
281
+ }
282
+
283
+ [data-ui='image'][data-part='root'][data-status='loaded'] [data-part='image'] {
284
+ opacity: 1;
285
+ }
286
+
287
+ /**
288
+ * A picture that failed has no size of its own, and Chrome lays the broken
289
+ * element out as its `alt` text, so a root that reserved nothing took the
290
+ * width of the sentence (measured in the `error` scene). The element stays
291
+ * in the tree with its name and takes no box; the fallback is the box.
292
+ */
293
+ [data-ui='image'][data-part='root'][data-status='error'] [data-part='image'] {
294
+ block-size: 0;
295
+ inline-size: 0;
296
+ min-block-size: 0;
297
+ }
298
+
299
+ /* ── The placeholder: before the picture ──────────────────────────────── */
300
+
301
+ /**
302
+ * The layer under the picture while it is on its way, and while nothing was
303
+ * asked for. It fills the box the picture will fill — which is nothing at
304
+ * all when nothing was reserved, and that is the point — and it leaves as
305
+ * the picture arrives, on the same clock, so the two cross rather than
306
+ * flicker. It never takes a pointer: a press on a loading thumbnail is a
307
+ * press on the link the root may be.
308
+ */
309
+ [data-ui='image'][data-part='root'] [data-part='placeholder'] {
310
+ background-color: var(--_ground);
311
+ block-size: 100%;
312
+ box-shadow: var(--_ground-edge);
313
+ /**
314
+ * The blurred preview below is drawn a little larger than the box, and
315
+ * the layer clips it here rather than leaving it to the root: a frame
316
+ * between the root and this layer would otherwise report the preview as
317
+ * content that left the box (`tools/harness/audit.mjs` read 295 pixels in
318
+ * 284 on the reference page). `clip` rather than `hidden`, so the layer
319
+ * is not a scroll container.
320
+ */
321
+ overflow: clip;
322
+ display: grid;
323
+ grid-area: 1 / 1;
324
+ inline-size: 100%;
325
+ min-inline-size: 0;
326
+ pointer-events: none;
327
+ transition: opacity var(--slotted-duration-short) var(--slotted-easing-standard);
328
+ }
329
+
330
+ [data-ui='image'][data-part='root'][data-placeholder='none'] [data-part='placeholder'] {
331
+ background-color: transparent;
332
+ box-shadow: none;
333
+ }
334
+
335
+ /**
336
+ * The low-quality preview, scaled and blurred where the picture will be. It
337
+ * is an `<img>` of its own rather than a background, so it reads the same
338
+ * `fit` and `position` as the picture and lands on the same pixels. The
339
+ * blur feathers its own edge to transparent, which would show the ground
340
+ * as a soft frame around the preview; it is drawn a little larger than the
341
+ * box and the root's clip takes the feathered margin off. The radius is the
342
+ * blur's, in a length rather than a scale step, because a scale step is a
343
+ * distance between things and this is how far a pixel bleeds.
344
+ */
345
+ [data-ui='image'][data-part='root'][data-placeholder='blur'] [data-part='placeholder'] > img {
346
+ block-size: 100%;
347
+ display: block;
348
+ filter: blur(var(--slotted-image-placeholder-blur, 1.25rem));
349
+ inline-size: 100%;
350
+ object-fit: var(--_fit);
351
+ object-position: var(--_position);
352
+ transform: scale(1.08);
353
+ }
354
+
355
+ [data-ui='image'][data-part='root'][data-status='loaded'] [data-part='placeholder'] {
356
+ opacity: 0;
357
+ }
358
+
359
+ [data-ui='image'][data-part='root'][data-status='error'] [data-part='placeholder'] {
360
+ visibility: hidden;
361
+ }
362
+
363
+ /* ── The fallback: after the picture failed ───────────────────────────── */
364
+
365
+ /**
366
+ * Painted only in `error`, and only then in the flow, so a consumer's own
367
+ * content in it — a retry, a logotype — is neither focusable nor announced
368
+ * while the picture is fine. Given a minimum of its own because a picture
369
+ * that reserved nothing and then failed has no box, and a glyph needs one.
370
+ */
371
+ [data-ui='image'][data-part='root'] [data-part='fallback'] {
372
+ /**
373
+ * The mark a picture leaves behind: the binding draws `image-off` in an
374
+ * `Icon` unless the consumer projected content of their own, which
375
+ * replaces it rather than sitting beside it. The size is set on the slot,
376
+ * so an icon the consumer projects without a size is drawn at the same
377
+ * step. It is a token with a length behind it rather than a step of the
378
+ * control icon scale, whose largest step is eighteen pixels and reads as a
379
+ * speck in a box of any size a picture takes.
380
+ */
381
+ --slotted-icon-size: var(--slotted-image-glyph-size, var(--slotted-space-9, 2rem));
382
+
383
+ display: none;
384
+ }
385
+
386
+ [data-ui='image'][data-part='root'][data-status='error'] [data-part='fallback'] {
387
+ background-color: var(--_ground);
388
+ block-size: 100%;
389
+ box-shadow: var(--_ground-edge);
390
+ color: var(--slotted-text-muted, GrayText);
391
+ display: grid;
392
+ grid-area: 1 / 1;
393
+ inline-size: 100%;
394
+ min-block-size: var(--slotted-image-fallback-min-size, var(--slotted-space-12, 4rem));
395
+ min-inline-size: var(--slotted-image-fallback-min-size, var(--slotted-space-12, 4rem));
396
+ place-items: center;
397
+ }
398
+
399
+ /* ── Interaction ──────────────────────────────────────────────────────── */
400
+
401
+ /**
402
+ * A picture is not a command, and paints nothing at rest. These three
403
+ * selectors are the ones the browser itself treats as interactive — a
404
+ * thumbnail `render` made a link or a button — and they are written apart
405
+ * rather than gathered into `:is()` because `packages/styles/src/states.test.mjs`
406
+ * reads one comma-separated selector at a time.
407
+ *
408
+ * Hover draws the edge in the strong step, which holds 3:1 against every
409
+ * surface in every scheme and does not touch the photograph: a hover that
410
+ * dims or lifts a picture says the wrong thing in the dark scheme, where
411
+ * lighter is nearer. The family declares no disabled state of its own —
412
+ * that belongs to the control that holds the picture — but a `<button
413
+ * disabled>` is the platform's, and a weak state holds itself back for it.
414
+ */
415
+ [data-ui='image'][data-part='root']:any-link,
416
+ button[data-ui='image'][data-part='root'],
417
+ [data-ui='image'][data-part='root'][tabindex] {
418
+ cursor: pointer;
419
+ }
420
+
421
+ [data-ui='image'][data-part='root']:any-link:not([data-disabled]):hover,
422
+ button[data-ui='image'][data-part='root']:not(:disabled):hover,
423
+ [data-ui='image'][data-part='root'][tabindex]:not([data-disabled]):hover {
424
+ --_edge: var(--slotted-border-strong, CanvasText);
425
+ /* Two pixels rather than one: a hairline over a photograph's own bright
426
+ edge is not a hover anyone sees, and the ring is inset, so the width
427
+ moves nothing. */
428
+ --_edge-width: var(--slotted-border-width-thick, 2px);
429
+ }
430
+
431
+ /**
432
+ * Painted whatever the element turned out to be, so the ring does not go
433
+ * missing the first time a consumer makes a picture a link. Drawn outside
434
+ * the box at the theme's offset, over whatever the picture is on.
435
+ */
436
+ [data-ui='image'][data-part='root']:focus-visible {
437
+ outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
438
+ outline-offset: var(--slotted-focus-ring-offset, 2px);
439
+ }
440
+
441
+ /**
442
+ * ── Forced colours ─────────────────────────────────────────────────────
443
+ *
444
+ * The theme maps the ground to `Canvas` and the edge to `ButtonBorder`, so
445
+ * nothing here needs restating for them. The picture is exempt by the user
446
+ * agent's own rule: `forced-color-adjust` does not apply to images, and a
447
+ * photograph reduced to two system colours is not a photograph.
448
+ */
449
+ }
@@ -0,0 +1,27 @@
1
+ [
2
+ "--slotted-border-strong",
3
+ "--slotted-border-subtle",
4
+ "--slotted-border-width-thick",
5
+ "--slotted-border-width-thin",
6
+ "--slotted-duration-short",
7
+ "--slotted-easing-standard",
8
+ "--slotted-focus-ring-color",
9
+ "--slotted-focus-ring-offset",
10
+ "--slotted-focus-ring-width",
11
+ "--slotted-image-background",
12
+ "--slotted-image-border-width",
13
+ "--slotted-image-fallback-min-size",
14
+ "--slotted-image-glyph-size",
15
+ "--slotted-image-placeholder-background",
16
+ "--slotted-image-placeholder-blur",
17
+ "--slotted-image-position",
18
+ "--slotted-image-radius",
19
+ "--slotted-radius-lg",
20
+ "--slotted-radius-md",
21
+ "--slotted-radius-none",
22
+ "--slotted-radius-sm",
23
+ "--slotted-space-12",
24
+ "--slotted-space-9",
25
+ "--slotted-surface-sunken",
26
+ "--slotted-text-muted"
27
+ ]