@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,95 @@
1
+ [
2
+ "--slotted-border-subtle",
3
+ "--slotted-border-width-thin",
4
+ "--slotted-control-icon-size-md",
5
+ "--slotted-control-icon-size-sm",
6
+ "--slotted-font-family-sans",
7
+ "--slotted-font-size-2xl",
8
+ "--slotted-font-size-3xl",
9
+ "--slotted-font-size-4xl",
10
+ "--slotted-font-size-md",
11
+ "--slotted-font-size-sm",
12
+ "--slotted-font-size-xl",
13
+ "--slotted-font-size-xs",
14
+ "--slotted-font-weight-medium",
15
+ "--slotted-font-weight-semibold",
16
+ "--slotted-line-height-2xl",
17
+ "--slotted-line-height-3xl",
18
+ "--slotted-line-height-4xl",
19
+ "--slotted-line-height-md",
20
+ "--slotted-line-height-sm",
21
+ "--slotted-line-height-xl",
22
+ "--slotted-line-height-xs",
23
+ "--slotted-radius-lg",
24
+ "--slotted-radius-none",
25
+ "--slotted-shadow-md",
26
+ "--slotted-space-1",
27
+ "--slotted-space-11",
28
+ "--slotted-space-12",
29
+ "--slotted-space-2",
30
+ "--slotted-space-3",
31
+ "--slotted-space-4",
32
+ "--slotted-space-5",
33
+ "--slotted-space-6",
34
+ "--slotted-space-7",
35
+ "--slotted-space-8",
36
+ "--slotted-space-9",
37
+ "--slotted-sparkline-block-size",
38
+ "--slotted-sparkline-block-size-lg",
39
+ "--slotted-sparkline-block-size-sm",
40
+ "--slotted-stat-card-background",
41
+ "--slotted-stat-card-background-subtle",
42
+ "--slotted-stat-card-border-color",
43
+ "--slotted-stat-card-border-width",
44
+ "--slotted-stat-card-color",
45
+ "--slotted-stat-card-color-muted",
46
+ "--slotted-stat-card-delta-font-size",
47
+ "--slotted-stat-card-delta-font-size-lg",
48
+ "--slotted-stat-card-delta-font-size-sm",
49
+ "--slotted-stat-card-indicator-size",
50
+ "--slotted-stat-card-indicator-size-lg",
51
+ "--slotted-stat-card-label-font-size",
52
+ "--slotted-stat-card-label-font-size-lg",
53
+ "--slotted-stat-card-label-font-size-sm",
54
+ "--slotted-stat-card-label-line-height",
55
+ "--slotted-stat-card-label-line-height-lg",
56
+ "--slotted-stat-card-label-line-height-sm",
57
+ "--slotted-stat-card-min-inline-size",
58
+ "--slotted-stat-card-padding-block",
59
+ "--slotted-stat-card-padding-block-lg",
60
+ "--slotted-stat-card-padding-block-sm",
61
+ "--slotted-stat-card-padding-inline",
62
+ "--slotted-stat-card-padding-inline-lg",
63
+ "--slotted-stat-card-padding-inline-sm",
64
+ "--slotted-stat-card-prose-font-size",
65
+ "--slotted-stat-card-prose-font-size-lg",
66
+ "--slotted-stat-card-prose-line-height",
67
+ "--slotted-stat-card-prose-line-height-lg",
68
+ "--slotted-stat-card-radius",
69
+ "--slotted-stat-card-region-gap",
70
+ "--slotted-stat-card-region-gap-lg",
71
+ "--slotted-stat-card-region-gap-sm",
72
+ "--slotted-stat-card-shadow",
73
+ "--slotted-stat-card-shadow-elevated",
74
+ "--slotted-stat-card-trend-gap",
75
+ "--slotted-stat-card-trend-gap-lg",
76
+ "--slotted-stat-card-trend-gap-sm",
77
+ "--slotted-stat-card-trend-inline-size",
78
+ "--slotted-stat-card-trend-inline-size-lg",
79
+ "--slotted-stat-card-trend-inline-size-sm",
80
+ "--slotted-stat-card-value-font-size",
81
+ "--slotted-stat-card-value-font-size-lg",
82
+ "--slotted-stat-card-value-font-size-sm",
83
+ "--slotted-stat-card-value-line-height",
84
+ "--slotted-stat-card-value-line-height-lg",
85
+ "--slotted-stat-card-value-line-height-sm",
86
+ "--slotted-surface-raised",
87
+ "--slotted-surface-sunken",
88
+ "--slotted-text",
89
+ "--slotted-text-muted",
90
+ "--slotted-tone-accent-text",
91
+ "--slotted-tone-danger-text",
92
+ "--slotted-tone-secondary-text",
93
+ "--slotted-tone-success-text",
94
+ "--slotted-tone-warning-text"
95
+ ]
@@ -0,0 +1,481 @@
1
+ @layer slotted.components {
2
+ /**
3
+ * ── The indicator ──────────────────────────────────────────────────────
4
+ *
5
+ * A mark and the word it stands for: the operational state of something the
6
+ * application observes and this family only reports. It is never a command
7
+ * of its own: it answers a pointer only when a consumer renders the root as a
8
+ * link, and it holds no state of its own — a component that moved its own
9
+ * status would be describing a system it cannot see.
10
+ *
11
+ * `align-items: flex-start` rather than `center`, and that is the decision
12
+ * the whole box is built around. A status is often a sentence — “Waiting for
13
+ * approval from the security team” — and this family lets it wrap where
14
+ * `badge` clips its own label to one line. A centred mark beside four lines
15
+ * of text floats in the middle of a paragraph; the mark is given a box one
16
+ * line tall instead, so it sits on the first line however far the words run.
17
+ *
18
+ * The words take the page's own colour. The tone is on the mark, which is
19
+ * what keeps a column of statuses in a table as readable as the column
20
+ * beside it — the answer Spectrum, Chakra and Ant Design all reached, and
21
+ * the opposite of the coloured pill Primer and Atlassian draw.
22
+ */
23
+ [data-ui='status-indicator'][data-part='root'] {
24
+ /**
25
+ * Type and glyph come from the control scales, the only ones the theme
26
+ * restates per density, so a status shrinks with the row it sits in. The
27
+ * gap comes from the space scale: a density step inside a distance this
28
+ * small would be sub-pixel, and density already moves the type.
29
+ */
30
+ --_font-size: var(--slotted-control-font-size-md, 13px);
31
+ --_glyph-size: var(--slotted-control-icon-size-md, 16px);
32
+ --_gap: var(--slotted-space-4, 0.5rem);
33
+ --_line-height: var(--slotted-line-height-md, 1.5);
34
+
35
+ /**
36
+ * The disc is a whole number of pixels from the space scale: 8 at `md` and
37
+ * `lg`, 6 at `sm`. It was one cap tall, 9.48 pixels at `md`, and a
38
+ * fractional disc lands its edge across two device pixels at 1×. ReUI draws
39
+ * the same light at 6 pixels beside 14-pixel type (0.43 em); 8 beside 13 is
40
+ * 0.62 em, which keeps the tone legible in a table cell without
41
+ * outweighing the word. Measured in
42
+ * `docs/history/designs/2026-09-25-status-indicator-reui-dossier.md` § 3.
43
+ */
44
+ --_dot-size: var(--slotted-space-4, 0.5rem);
45
+ --_mark-size: var(--_dot-size);
46
+ --_rim-width: var(
47
+ --slotted-status-indicator-mark-border-width,
48
+ var(--slotted-border-width-thin, 1px)
49
+ );
50
+ /**
51
+ * The rim is the fill itself unless the fill cannot hold the edge alone.
52
+ * Every tone's fill clears WCAG 2.2 1.4.11's 3:1 against the surface in
53
+ * light and dark except `warning`, whose yellow is 1.53:1 on white; that
54
+ * one status sets `--_edge` to the tone's border role. The rim was the
55
+ * tone's text role on every status, and a second colour around the fill
56
+ * drew a bullseye: brown around yellow, near-black around grey, and a pale
57
+ * ring around every tone in dark (dossier § 3).
58
+ */
59
+ --_rim: var(--slotted-status-indicator-mark-border-color, var(--_edge, var(--_solid)));
60
+ /**
61
+ * The halo: a band of the dot's own colour at a fifth of its strength,
62
+ * drawn outside the disc with `box-shadow` so it takes no room in the
63
+ * line. It is what separates a status light from a bullet — the disc
64
+ * reads as lit rather than printed — and it costs no contrast, because
65
+ * the disc inside it is unchanged. 3 pixels at `md` and `lg`, 2 at `sm`
66
+ * (dossier § 3). The gap to the words is 8 at `md` so the halo never
67
+ * reaches closer than 5 pixels to the first letter.
68
+ */
69
+ --_halo: color-mix(in oklab, var(--_rim) 20%, transparent);
70
+ --_halo-width: calc(var(--slotted-space-1, 0.125rem) * 1.5);
71
+
72
+ /**
73
+ * What the ring is drawn against. The pulse hangs off the root rather
74
+ * than off the mark: when the icon mark was a `mask-image`, the mask
75
+ * clipped the element's pseudo-elements with it, and a ring behind a
76
+ * triangle was cut into the shape of that triangle and then disappeared.
77
+ * Photographed at 300% before it was moved here, and kept here so the
78
+ * ring never depends on how a mark is drawn. Rounded to a whole pixel, the
79
+ * rule `alert` applies to its own mark, so the disc never straddles one.
80
+ */
81
+ --_pulse-inset: round(nearest, (1lh - var(--_mark-size)) / 2, 1px);
82
+
83
+ align-items: flex-start;
84
+ /**
85
+ * The words keep the page's colour when `render` makes the root an `<a>`;
86
+ * without this the user agent paints the label in its link blue.
87
+ */
88
+ color: inherit;
89
+ display: inline-flex;
90
+ font-family: var(
91
+ --slotted-status-indicator-font-family,
92
+ var(--slotted-control-font-family, sans-serif)
93
+ );
94
+ font-size: var(--_font-size);
95
+ line-height: var(--_line-height);
96
+ max-inline-size: 100%;
97
+ /**
98
+ * The indicator never widens the box that holds it. Its own minimum is the
99
+ * longest word of a label it did not choose, and a grid or flex parent
100
+ * sizes its track from that minimum — so a container digest with nothing
101
+ * to break on pushed a 220-pixel column out to 420 and painted past its
102
+ * own edge. Zero here hands the decision back to the container, and the
103
+ * label breaks the token at whatever width it is given.
104
+ */
105
+ min-inline-size: 0;
106
+ position: relative;
107
+ /**
108
+ * `render` can make the root an `<a>`, and the rule a browser writes on
109
+ * every anchor would put a line under a status at rest. The linked root
110
+ * below draws its own underline, hidden until the pointer arrives.
111
+ */
112
+ text-decoration: none;
113
+ /* Written inside a sentence, the indicator sits on the text's centre. */
114
+ vertical-align: middle;
115
+ }
116
+
117
+ /* ── Status: the tone each state speaks in ────────────────────────────
118
+ *
119
+ * Six values, and the map is `specs/components/status-indicator/contract.json`
120
+ * rather than a decision taken here: the stylesheet, both bindings and the
121
+ * contract test all read the same table. The glyph each status draws is in
122
+ * the same contract, and the bindings render it; the sheet only colours it.
123
+ *
124
+ * `neutral` and `unknown` share `secondary` on purpose, so the difference
125
+ * between “nothing is happening” and “nobody knows” is never carried by
126
+ * colour. The filled disc and the dashed ring say it in `high-contrast` and
127
+ * in greyscale as well as in `light`.
128
+ */
129
+
130
+ [data-ui='status-indicator'][data-part='root'][data-status='neutral'] {
131
+ /**
132
+ * The one neutral the theme calibrates against the surface rather than
133
+ * against a control, and the only fill that makes this dot read as filled.
134
+ *
135
+ * `--slotted-tone-secondary-solid` is the surface's own colour in all
136
+ * three schemes, and its `solid-hover` step — which `badge` reaches for on
137
+ * a chip the size of a word — is one shade off it. Read at 420 pixels on a
138
+ * nine-pixel disc, that was a rim around nothing: `neutral` and `unknown`
139
+ * both read as hollow, and hollow is the word this family reserved for
140
+ * “nobody knows”. `--slotted-border-strong` is held to 3:1 against the
141
+ * surface it separates, which is exactly what a filled neutral mark needs
142
+ * and the reason `badge` rims its own dot with it.
143
+ */
144
+ --_solid: var(--slotted-border-strong);
145
+ --_text: var(--slotted-tone-secondary-text);
146
+ }
147
+
148
+ [data-ui='status-indicator'][data-part='root'][data-status='info'] {
149
+ --_solid: var(--slotted-tone-accent-solid);
150
+ --_text: var(--slotted-tone-accent-text);
151
+ }
152
+
153
+ [data-ui='status-indicator'][data-part='root'][data-status='success'] {
154
+ --_solid: var(--slotted-tone-success-solid);
155
+ --_text: var(--slotted-tone-success-text);
156
+ }
157
+
158
+ /**
159
+ * The one fill that cannot hold its edge: `#facc15` is 1.53:1 on the light
160
+ * surface. The tone's border role is the same hue a step darker, 2.94:1 in
161
+ * light, the fill itself in dark and black in `high-contrast` — an edge that
162
+ * reads as the dot's own rim rather than as a second colour around it.
163
+ */
164
+ [data-ui='status-indicator'][data-part='root'][data-status='warning'] {
165
+ --_edge: var(--slotted-tone-warning-border);
166
+ --_solid: var(--slotted-tone-warning-solid);
167
+ --_text: var(--slotted-tone-warning-text);
168
+ }
169
+
170
+ [data-ui='status-indicator'][data-part='root'][data-status='danger'] {
171
+ --_solid: var(--slotted-tone-danger-solid);
172
+ --_text: var(--slotted-tone-danger-text);
173
+ }
174
+
175
+ /**
176
+ * A ring with nothing inside, drawn in the neutral dot's own fill so the two
177
+ * grey statuses carry the same weight and differ only by being hollow. It
178
+ * has no halo of its own; the ring is drawn where the halo of a lit dot
179
+ * ends, below.
180
+ */
181
+ [data-ui='status-indicator'][data-part='root'][data-status='unknown'] {
182
+ --_edge: var(--slotted-border-strong);
183
+ --_halo: transparent;
184
+ --_solid: transparent;
185
+ --_text: var(--slotted-tone-secondary-text);
186
+ }
187
+
188
+ /**
189
+ * Which of the two marks the box is sized for, decided on the root rather
190
+ * than on the mark. Both the mark and the ring that leaves it read the
191
+ * measurement, and the ring is a pseudo-element of the root: declaring it on
192
+ * the mark left the ring the size of a dot around a sixteen-pixel glyph, and
193
+ * one and three quarter pixels above where it belonged.
194
+ */
195
+ [data-ui='status-indicator'][data-part='root'][data-mark='icon'] {
196
+ --_mark-size: var(--_glyph-size);
197
+ }
198
+
199
+ /* ── Size ─────────────────────────────────────────────────────────────── */
200
+
201
+ [data-ui='status-indicator'][data-part='root'][data-size='sm'] {
202
+ --_dot-size: var(--slotted-space-3, 0.375rem);
203
+ --_font-size: var(--slotted-control-font-size-sm, 12px);
204
+ --_glyph-size: var(--slotted-control-icon-size-sm, 14px);
205
+ --_gap: var(--slotted-space-3, 0.375rem);
206
+ --_halo-width: var(--slotted-space-1, 0.125rem);
207
+ }
208
+
209
+ [data-ui='status-indicator'][data-part='root'][data-size='lg'] {
210
+ --_font-size: var(--slotted-control-font-size-lg, 14px);
211
+ --_glyph-size: var(--slotted-control-icon-size-lg, 18px);
212
+ }
213
+
214
+ /* ── The mark ─────────────────────────────────────────────────────────
215
+ *
216
+ * One box, painted two ways. `1lh` is what puts it on the first line: the
217
+ * unit resolves against the indicator's own line box, so the mark stays
218
+ * centred on the words at every type size and every density without a
219
+ * number saying how. It is the rule `skeleton` uses to sit an inline bar on
220
+ * a line of type, applied to a box rather than to a baseline.
221
+ */
222
+ [data-ui='status-indicator'] [data-part='mark'] {
223
+ block-size: var(--_mark-size);
224
+ box-sizing: border-box;
225
+ flex: none;
226
+ inline-size: var(--_mark-size);
227
+ margin-block-start: var(--_pulse-inset);
228
+ }
229
+
230
+ [data-ui='status-indicator'][data-mark='dot'] [data-part='mark'] {
231
+ background-color: var(--_solid, ButtonFace);
232
+ border: var(--_rim-width) solid var(--_rim, CanvasText);
233
+ border-radius: var(--slotted-radius-full, 9999px);
234
+ box-shadow: 0 0 0 var(--_halo-width) var(--_halo);
235
+ }
236
+
237
+ /**
238
+ * The ring nobody filled in. A dashed edge is the second half of the answer
239
+ * to `neutral` and `unknown` sharing a tone, and it is the only mark in the
240
+ * family that says something by what it is missing.
241
+ */
242
+ [data-ui='status-indicator'][data-mark='dot'][data-status='unknown'] [data-part='mark'] {
243
+ border: 0;
244
+ position: relative;
245
+ }
246
+
247
+ /**
248
+ * The ring is drawn on a pseudo-element rather than on the mark's border, so
249
+ * it can be as large as a lit dot looks without moving the words: its outer
250
+ * edge sits where a lit dot's halo ends — 14 pixels at `md`, 10 at `sm` —
251
+ * while the box in the line stays the dot's 8 or 6. Drawn on the 8-pixel
252
+ * border it was a one-pixel dashed circle half the visible size of its
253
+ * neighbours, and read as a speck rather than as a status. `outline` is
254
+ * reserved for the focus ring (`focus-ring.test.mjs`).
255
+ */
256
+ [data-ui='status-indicator'][data-mark='dot'][data-status='unknown'] [data-part='mark']::before {
257
+ border: var(--slotted-border-width-thick, 2px) dashed var(--_rim);
258
+ border-radius: var(--slotted-radius-full, 9999px);
259
+ box-sizing: border-box;
260
+ content: '';
261
+ inset: calc(-1 * var(--_halo-width));
262
+ position: absolute;
263
+ }
264
+
265
+ /**
266
+ * The glyph, in the tone's own text role and on no ground of its own.
267
+ *
268
+ * Fluent and Carbon both knock the symbol out of a filled disc. Read at
269
+ * `sm`, that is 1.75 device pixels of stroke inside a fourteen-pixel circle,
270
+ * and it depends on a contrast the theme does not promise between a tone's
271
+ * fill and its on-solid ink at that size. The tone `text` roles are the ones
272
+ * the theme's contrast test holds to the 4.5:1 reading ratio — more than the
273
+ * 3:1 a meaningful graphic needs — so the glyph is drawn in one of those and
274
+ * the disc is left to the mark that is a disc.
275
+ *
276
+ * The mark is the icon box's container: it hands the box its own size
277
+ * through `--slotted-icon-size` and its colour through `color`, and the
278
+ * binding puts the status's glyph inside. `flex` rather than a line box, so
279
+ * no strut of the root's line-height pushes the glyph off the box.
280
+ */
281
+ [data-ui='status-indicator'][data-mark='icon'] [data-part='mark'] {
282
+ --slotted-icon-size: var(--_mark-size);
283
+
284
+ color: var(--_text, CanvasText);
285
+ display: flex;
286
+ }
287
+
288
+ /* ── The words ────────────────────────────────────────────────────────
289
+ *
290
+ * `min-inline-size: 0` is what lets the label shrink below its content
291
+ * inside the flex root, and `overflow-wrap` is what breaks the one string a
292
+ * status is likeliest to hold that no language can break — a deployment id,
293
+ * a container digest, a hostname without a hyphen. Nothing is ever removed:
294
+ * the whole string stays in the DOM, in the accessibility tree and findable
295
+ * by a page search.
296
+ *
297
+ * `break-word` rather than `anywhere`, and the difference is the one this
298
+ * family was caught by. Both break the token; only `anywhere` also reports a
299
+ * min-content size of one character, so a grid column sized to its content
300
+ * collapses to that character. Read on the reference page, “Build 4821
301
+ * failed” came out as fourteen lines of one letter each in a table whose
302
+ * first column was auto-sized. `break-word` keeps the longest word as the
303
+ * minimum and breaks only what still does not fit.
304
+ */
305
+ [data-ui='status-indicator'] [data-part='label'] {
306
+ margin-inline-start: var(--_gap);
307
+ min-inline-size: 0;
308
+ overflow-wrap: break-word;
309
+ }
310
+
311
+ /**
312
+ * The gap taken back when the label holds nothing a reader can see. A mark
313
+ * with no visible words still carries its own word, clipped by
314
+ * `visually-hidden`, and that word is out of flow — so the label box is
315
+ * empty, and a gap in front of an empty box puts a bare dot off the centre
316
+ * of the cell it was meant to sit in.
317
+ */
318
+ [data-ui='status-indicator'] [data-part='label']:has(> [data-part='announcement']) {
319
+ margin-inline-start: 0;
320
+ }
321
+
322
+ /* ── Focus ────────────────────────────────────────────────────────────
323
+ *
324
+ * The family is not focusable on its own. The ring is painted anyway,
325
+ * because `render` and Angular's host element both let a consumer make the
326
+ * root an `<a>` or a `<button>`, and a ring that appears only where a family
327
+ * expects focus is a ring that goes missing.
328
+ */
329
+ [data-ui='status-indicator'][data-part='root']:focus-visible {
330
+ outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
331
+ outline-offset: var(--slotted-focus-ring-offset, 2px);
332
+ }
333
+
334
+ /* ── The linked root ──────────────────────────────────────────────────
335
+ *
336
+ * A root the consumer made a link answers the pointer as a link in running
337
+ * text does, on the words alone: the mark keeps its tone through hover and
338
+ * press. The underline is drawn at rest in `transparent`, so hover fades it
339
+ * in rather than switching it on, and press darkens the words to the muted
340
+ * text role, a step hover does not take. The offset is `link`'s own 0.15em
341
+ * (dossier § 4).
342
+ */
343
+ [data-ui='status-indicator'][data-part='root']:any-link {
344
+ text-decoration-color: transparent;
345
+ text-decoration-line: underline;
346
+ text-decoration-thickness: from-font;
347
+ text-underline-offset: 0.15em;
348
+ transition:
349
+ color var(--slotted-duration-short) var(--slotted-easing-standard),
350
+ text-decoration-color var(--slotted-duration-short) var(--slotted-easing-standard);
351
+ }
352
+
353
+ [data-ui='status-indicator'][data-part='root']:any-link:not([data-disabled]):hover {
354
+ text-decoration-color: currentColor;
355
+ }
356
+
357
+ [data-ui='status-indicator'][data-part='root']:any-link:not([data-disabled]):active {
358
+ color: var(--slotted-text-muted, GrayText);
359
+ text-decoration-color: currentColor;
360
+ }
361
+
362
+ /* ── The pulse ────────────────────────────────────────────────────────
363
+ *
364
+ * A wave leaving the mark and fading, for a change happening now. It is an
365
+ * enhancement rather than the state: the tone, the glyph and the word have
366
+ * already said what the status is, so the wave can be withdrawn wherever it
367
+ * would be wrong without anything going unsaid.
368
+ *
369
+ * The wave is a filled disc behind the mark, not a hairline around it. A
370
+ * one-pixel ring scaled to 2.2 was a grey circle at most frames, too thin to
371
+ * read as motion beside an 8-pixel dot. It leaves on
372
+ * `--slotted-easing-enter`, fast out of the mark and settling as it fades,
373
+ * and it is gone by 70% of the beat, so each beat ends in a rest rather than
374
+ * running into the next one; ReUI's `ping` holds the same last quarter
375
+ * (dossier § 5).
376
+ *
377
+ * Mantine, Shoelace and Ant Design write a literal duration beside
378
+ * `infinite`, and none of the three answers a reader who asked the
379
+ * operating system for stillness. This one takes the loop scale's slowest
380
+ * step, the only step the theme does not shorten under reduced motion —
381
+ * shortening an endless animation is not an answer to it.
382
+ */
383
+ @keyframes slotted-status-indicator-pulse {
384
+ from {
385
+ opacity: 0.45;
386
+ scale: 1;
387
+ }
388
+
389
+ 70%,
390
+ to {
391
+ opacity: 0;
392
+ scale: 3;
393
+ }
394
+ }
395
+
396
+ /**
397
+ * The wave is the dot's rim colour, defined for all six statuses —
398
+ * `unknown` has no fill, and its wave is the neutral edge. It starts at
399
+ * 0.45 of that colour and ends at three times the dot, 24 pixels at `md`,
400
+ * inside the line above and below without reaching the next status in a
401
+ * list (dossier § 5). The root isolates a
402
+ * stacking context so the wave can sit under the mark (`z-index: -1`)
403
+ * without falling behind the surface the indicator is drawn on.
404
+ */
405
+ [data-ui='status-indicator'][data-pulse] {
406
+ isolation: isolate;
407
+ }
408
+
409
+ [data-ui='status-indicator'][data-pulse]::after {
410
+ animation: slotted-status-indicator-pulse var(--slotted-duration-loop-slow)
411
+ var(--slotted-easing-enter) infinite;
412
+ background-color: var(--_rim);
413
+ block-size: var(--_mark-size);
414
+ border-radius: var(--slotted-radius-full, 9999px);
415
+ content: '';
416
+ inline-size: var(--_mark-size);
417
+ inset-block-start: var(--_pulse-inset);
418
+ inset-inline-start: 0;
419
+ pointer-events: none;
420
+ position: absolute;
421
+ z-index: -1;
422
+ }
423
+
424
+ /**
425
+ * An icon mark does not pulse. A wave behind a glyph fills the glyph's
426
+ * counters and blurs its silhouette — the one thing the icon mark exists to
427
+ * keep — and a disc growing out of a shape that is not a disc reads as a
428
+ * second, larger mark rather than as motion. `pulse` on an icon mark is
429
+ * carried by the tone, the glyph and the word, as it is under reduced
430
+ * motion.
431
+ */
432
+ [data-ui='status-indicator'][data-mark='icon'][data-pulse]::after {
433
+ content: none;
434
+ }
435
+
436
+ /**
437
+ * Stopped, not slowed, and removed rather than frozen. A wave halted halfway
438
+ * through an expansion is a rendering fault rather than a state — the
439
+ * finding `button` recorded about its own loading band — so the pseudo goes
440
+ * and the status is left to the parts that were always carrying it.
441
+ */
442
+ @media (prefers-reduced-motion: reduce) {
443
+ [data-ui='status-indicator'][data-pulse]::after {
444
+ animation: none;
445
+ content: none;
446
+ }
447
+ }
448
+
449
+ /**
450
+ * ── Forced colours ─────────────────────────────────────────────────────
451
+ *
452
+ * The operating system replaces every author colour here, so the tone is
453
+ * gone whatever the family writes: the six statuses become one mark. What is
454
+ * left to decide is whether that mark is visible, and a dot painted from a
455
+ * forced `Canvas` background is not — the defect `link` found on its own
456
+ * external mark and answered the same way.
457
+ *
458
+ * Only the dot is filled here. The glyph is drawn in `currentColor`, which
459
+ * the system already resolves to `CanvasText`, and a filled box behind it
460
+ * would bury it. It keeps its silhouette, which is the whole reason the icon
461
+ * mark exists: a triangle and a circled cross are still different from each
462
+ * other when neither of them is red. The pulse is withdrawn, because its wave
463
+ * is painted from a tone the system replaces with a face colour, and a wave
464
+ * in `ButtonFace` on `Canvas` is an animation of nothing. The halo needs no
465
+ * rule: the system sets `box-shadow` to `none`.
466
+ */
467
+ @media (forced-colors: active) {
468
+ [data-ui='status-indicator'][data-mark='dot'] [data-part='mark'] {
469
+ background-color: CanvasText;
470
+ border-color: CanvasText;
471
+ }
472
+
473
+ [data-ui='status-indicator'][data-mark='dot'][data-status='unknown'] [data-part='mark'] {
474
+ background-color: Canvas;
475
+ }
476
+
477
+ [data-ui='status-indicator'][data-pulse]::after {
478
+ content: none;
479
+ }
480
+ }
481
+ }
@@ -0,0 +1,38 @@
1
+ [
2
+ "--slotted-border-strong",
3
+ "--slotted-border-width-thick",
4
+ "--slotted-border-width-thin",
5
+ "--slotted-control-font-family",
6
+ "--slotted-control-font-size-lg",
7
+ "--slotted-control-font-size-md",
8
+ "--slotted-control-font-size-sm",
9
+ "--slotted-control-icon-size-lg",
10
+ "--slotted-control-icon-size-md",
11
+ "--slotted-control-icon-size-sm",
12
+ "--slotted-duration-loop-slow",
13
+ "--slotted-duration-short",
14
+ "--slotted-easing-enter",
15
+ "--slotted-easing-standard",
16
+ "--slotted-focus-ring-color",
17
+ "--slotted-focus-ring-offset",
18
+ "--slotted-focus-ring-width",
19
+ "--slotted-line-height-md",
20
+ "--slotted-radius-full",
21
+ "--slotted-space-1",
22
+ "--slotted-space-3",
23
+ "--slotted-space-4",
24
+ "--slotted-status-indicator-font-family",
25
+ "--slotted-status-indicator-mark-border-color",
26
+ "--slotted-status-indicator-mark-border-width",
27
+ "--slotted-text-muted",
28
+ "--slotted-tone-accent-solid",
29
+ "--slotted-tone-accent-text",
30
+ "--slotted-tone-danger-solid",
31
+ "--slotted-tone-danger-text",
32
+ "--slotted-tone-secondary-text",
33
+ "--slotted-tone-success-solid",
34
+ "--slotted-tone-success-text",
35
+ "--slotted-tone-warning-border",
36
+ "--slotted-tone-warning-solid",
37
+ "--slotted-tone-warning-text"
38
+ ]