@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,641 @@
1
+ @layer slotted.components {
2
+ /**
3
+ * A metric with its name, its change and its trend, in a frame. The frame
4
+ * is the one `card` draws — a raised surface, a hairline edge out of flow —
5
+ * restated here in the family's own tokens rather than composed, because a
6
+ * card brings media, a body, a link and a selection, and a figure has none
7
+ * of them; what the two share is a surface, and a surface is three tokens.
8
+ *
9
+ * The root is the query container and the body is the grid, one element
10
+ * in: a container cannot change its own tracks
11
+ * (`docs/build-briefing.md`, _Decisions already taken_).
12
+ */
13
+ [data-ui='stat-card'] {
14
+ --_edge-width: var(--slotted-stat-card-border-width, var(--slotted-border-width-thin, 1px));
15
+ --_radius: var(--slotted-stat-card-radius, var(--slotted-radius-lg, 8px));
16
+ --_inset-block: var(--slotted-stat-card-padding-block, var(--slotted-space-6, 1rem));
17
+ --_inset-inline: var(--slotted-stat-card-padding-inline, var(--slotted-space-6, 1rem));
18
+ /* Between the name and the figure, and between the figure and the prose
19
+ under it: one step below the inset, so the regions group with each
20
+ other before they group with the edge. */
21
+ --_region-gap: var(--slotted-stat-card-region-gap, var(--slotted-space-3, 0.375rem));
22
+ /* Between the figure and the trend beside it. */
23
+ --_trend-gap: var(--slotted-stat-card-trend-gap, var(--slotted-space-6, 1rem));
24
+ /* The trend column's width when it sits beside the figure. A proportion
25
+ of the largest space step rather than a step: the scale ends at four
26
+ rem, and a line needs six to have room for twelve points. */
27
+ --_trend-size: var(
28
+ --slotted-stat-card-trend-inline-size,
29
+ calc(var(--slotted-space-12, 4rem) * 1.5)
30
+ );
31
+
32
+ --_surface: var(--slotted-stat-card-background, var(--slotted-surface-raised, Canvas));
33
+ --_edge: var(--slotted-stat-card-border-color, var(--slotted-border-subtle, ButtonBorder));
34
+ --_shadow: var(--slotted-stat-card-shadow, none);
35
+ --_ink: var(--slotted-stat-card-color, var(--slotted-text, CanvasText));
36
+ --_ink-muted: var(--slotted-stat-card-color-muted, var(--slotted-text-muted, GrayText));
37
+
38
+ /* The figure's type, by size step. */
39
+ --_value-size: var(--slotted-stat-card-value-font-size, var(--slotted-font-size-3xl, 1.5rem));
40
+ --_value-line: var(--slotted-stat-card-value-line-height, var(--slotted-line-height-3xl, 1.3));
41
+ --_label-size: var(--slotted-stat-card-label-font-size, var(--slotted-font-size-sm, 0.8125rem));
42
+ --_label-line: var(--slotted-stat-card-label-line-height, var(--slotted-line-height-sm, 1.45));
43
+ --_delta-size: var(--slotted-stat-card-delta-font-size, var(--slotted-font-size-sm, 0.8125rem));
44
+ --_prose-size: var(--slotted-stat-card-prose-font-size, var(--slotted-font-size-xs, 0.75rem));
45
+ --_prose-line: var(--slotted-stat-card-prose-line-height, var(--slotted-line-height-xs, 1.4));
46
+ /* The indicator beside the delta, one step under the delta's own type so
47
+ the mark sits inside the x-height band rather than over it. */
48
+ --_indicator-size: var(
49
+ --slotted-stat-card-indicator-size,
50
+ var(--slotted-control-icon-size-sm, 14px)
51
+ );
52
+ /* One step under the value, for the box under 220 pixels. */
53
+ --_value-size-narrow: var(--slotted-font-size-2xl, 1.25rem);
54
+ --_value-line-narrow: var(--slotted-line-height-2xl, 1.4);
55
+
56
+ /* The tone of the change, as the delta and the trend read it. Neutral
57
+ until a delta states one. */
58
+ --_tone-text: var(--_ink-muted);
59
+
60
+ background-color: var(--_surface);
61
+ border: none;
62
+ border-radius: var(--_radius);
63
+ box-shadow: var(--_shadow);
64
+ box-sizing: border-box;
65
+ color: var(--_ink);
66
+ container-name: stat-card;
67
+ container-type: inline-size;
68
+ display: flex;
69
+ flex-direction: column;
70
+ font-family: var(--slotted-font-family-sans, ui-sans-serif, system-ui, sans-serif);
71
+ /**
72
+ * A floor, and under it the card overflows its column rather than
73
+ * shrinking: squeezed below the width of a word, a card wrapped its
74
+ * label and its value one character to a line, which is a column of
75
+ * letters and not a metric. Ten rem holds a six-digit value at 24
76
+ * pixels with the trend under it; a grid that gives a card less has
77
+ * one column too many, and the overflow is what says so. A proportion
78
+ * of the largest space step, as the trend's width is.
79
+ */
80
+ min-inline-size: var(
81
+ --slotted-stat-card-min-inline-size,
82
+ calc(var(--slotted-space-12, 4rem) * 2.5)
83
+ );
84
+ padding-block: var(--_inset-block);
85
+ padding-inline: var(--_inset-inline);
86
+ position: relative;
87
+ }
88
+
89
+ /* The edge, out of flow, so a frame that changes width moves no text. */
90
+ [data-ui='stat-card']::before {
91
+ border: var(--_edge-width) solid var(--_edge);
92
+ border-radius: inherit;
93
+ content: '';
94
+ inset: 0;
95
+ pointer-events: none;
96
+ position: absolute;
97
+ }
98
+
99
+ [data-ui='stat-card'][data-size='sm'] {
100
+ --_inset-block: var(--slotted-stat-card-padding-block-sm, var(--slotted-space-5, 0.75rem));
101
+ --_inset-inline: var(--slotted-stat-card-padding-inline-sm, var(--slotted-space-5, 0.75rem));
102
+ --_region-gap: var(--slotted-stat-card-region-gap-sm, var(--slotted-space-2, 0.25rem));
103
+ --_trend-gap: var(--slotted-stat-card-trend-gap-sm, var(--slotted-space-5, 0.75rem));
104
+ --_trend-size: var(--slotted-stat-card-trend-inline-size-sm, var(--slotted-space-12, 4rem));
105
+ --_value-size: var(
106
+ --slotted-stat-card-value-font-size-sm,
107
+ var(--slotted-font-size-2xl, 1.25rem)
108
+ );
109
+ --_value-line: var(
110
+ --slotted-stat-card-value-line-height-sm,
111
+ var(--slotted-line-height-2xl, 1.4)
112
+ );
113
+ --_label-size: var(
114
+ --slotted-stat-card-label-font-size-sm,
115
+ var(--slotted-font-size-xs, 0.75rem)
116
+ );
117
+ --_label-line: var(
118
+ --slotted-stat-card-label-line-height-sm,
119
+ var(--slotted-line-height-xs, 1.4)
120
+ );
121
+ --_delta-size: var(
122
+ --slotted-stat-card-delta-font-size-sm,
123
+ var(--slotted-font-size-xs, 0.75rem)
124
+ );
125
+ --_value-size-narrow: var(--slotted-font-size-xl, 1.125rem);
126
+ --_value-line-narrow: var(--slotted-line-height-xl, 1.45);
127
+ }
128
+
129
+ [data-ui='stat-card'][data-size='lg'] {
130
+ --_inset-block: var(--slotted-stat-card-padding-block-lg, var(--slotted-space-8, 1.5rem));
131
+ --_inset-inline: var(--slotted-stat-card-padding-inline-lg, var(--slotted-space-8, 1.5rem));
132
+ --_region-gap: var(--slotted-stat-card-region-gap-lg, var(--slotted-space-4, 0.5rem));
133
+ --_trend-gap: var(--slotted-stat-card-trend-gap-lg, var(--slotted-space-8, 1.5rem));
134
+ --_trend-size: var(
135
+ --slotted-stat-card-trend-inline-size-lg,
136
+ calc(var(--slotted-space-12, 4rem) * 2)
137
+ );
138
+ --_value-size: var(--slotted-stat-card-value-font-size-lg, var(--slotted-font-size-4xl, 2rem));
139
+ --_value-line: var(
140
+ --slotted-stat-card-value-line-height-lg,
141
+ var(--slotted-line-height-4xl, 1.2)
142
+ );
143
+ --_label-size: var(
144
+ --slotted-stat-card-label-font-size-lg,
145
+ var(--slotted-font-size-md, 0.875rem)
146
+ );
147
+ --_label-line: var(
148
+ --slotted-stat-card-label-line-height-lg,
149
+ var(--slotted-line-height-md, 1.5)
150
+ );
151
+ --_delta-size: var(
152
+ --slotted-stat-card-delta-font-size-lg,
153
+ var(--slotted-font-size-md, 0.875rem)
154
+ );
155
+ --_prose-size: var(
156
+ --slotted-stat-card-prose-font-size-lg,
157
+ var(--slotted-font-size-sm, 0.8125rem)
158
+ );
159
+ --_prose-line: var(
160
+ --slotted-stat-card-prose-line-height-lg,
161
+ var(--slotted-line-height-sm, 1.45)
162
+ );
163
+ --_indicator-size: var(
164
+ --slotted-stat-card-indicator-size-lg,
165
+ var(--slotted-control-icon-size-md, 16px)
166
+ );
167
+ --_value-size-narrow: var(--slotted-font-size-3xl, 1.5rem);
168
+ --_value-line-narrow: var(--slotted-line-height-3xl, 1.3);
169
+ }
170
+
171
+ /* ── Fill: the three frames a card has, and none ─────────────────────── */
172
+
173
+ [data-ui='stat-card'][data-fill='elevated'] {
174
+ --_shadow: var(--slotted-stat-card-shadow-elevated, var(--slotted-shadow-md));
175
+ }
176
+
177
+ [data-ui='stat-card'][data-fill='subtle'] {
178
+ --_surface: var(--slotted-stat-card-background-subtle, var(--slotted-surface-sunken, Canvas));
179
+ }
180
+
181
+ /**
182
+ * `plain` is the figure with no frame of its own: in a table cell, in a row
183
+ * of metrics separated by dividers, inside a card that is already a card.
184
+ * It keeps no inset either, because the host's inset is the inset.
185
+ */
186
+ [data-ui='stat-card'][data-fill='plain'] {
187
+ --_surface: transparent;
188
+ --_edge: transparent;
189
+ --_shadow: none;
190
+ --_inset-block: 0;
191
+ --_inset-inline: 0;
192
+ --_radius: var(--slotted-radius-none, 0px);
193
+ }
194
+
195
+ /* ── The body ─────────────────────────────────────────────────────────── */
196
+
197
+ /**
198
+ * A grid with named rows, and the second row is the one that grows. Cards
199
+ * in a row are stretched to one height by the grid that holds them, and
200
+ * what a reader compares across the row is the value and the trend: the
201
+ * figure sits at the top of its row under the header, and the trend sits
202
+ * beside it (`inline`) or on the last row (`stacked`), which is the bottom
203
+ * of every card whatever the card beside it holds. A card with a shorter
204
+ * label or no comparison keeps its trend level with its neighbours'.
205
+ *
206
+ * Which layout applies is decided by the consumer's `layout` and by the
207
+ * container's width, never by the content. A wrapping row was tried — it
208
+ * moved the trend under a value too wide to sit beside it — and it was
209
+ * given up for this: a row of cards has to lay out the same way at the same
210
+ * width whatever the numbers in it, or the trends of three cards sit at
211
+ * three heights.
212
+ *
213
+ * What gives way to a wide value is the trend's width, not the number: the
214
+ * figure's column is floored at its min-content, which is the value whole
215
+ * (`break-word` leaves min-content alone), and the trend's column shrinks
216
+ * from its size step down to nothing. A value that leaves the trend no
217
+ * room is a value the consumer formats compact or stacks the card for; a
218
+ * number is broken only when it is wider than the card itself. A floor on
219
+ * the trend was tried and pushed the trend through the card's edge.
220
+ */
221
+ [data-ui='stat-card'] [data-part='body'] {
222
+ column-gap: var(--_trend-gap);
223
+ display: grid;
224
+ flex: 1 1 auto;
225
+ grid-template-areas:
226
+ 'header header'
227
+ 'figure trend'
228
+ 'message message';
229
+ grid-template-columns: minmax(min-content, 1fr) minmax(0, var(--_trend-size));
230
+ grid-template-rows: auto minmax(0, 1fr) auto;
231
+ min-inline-size: 0;
232
+ row-gap: var(--_region-gap);
233
+ }
234
+
235
+ [data-ui='stat-card'][data-layout='stacked'] [data-part='body'] {
236
+ grid-template-areas:
237
+ 'header'
238
+ 'figure'
239
+ 'trend'
240
+ 'message';
241
+ grid-template-columns: minmax(0, 1fr);
242
+ grid-template-rows: auto minmax(0, 1fr) auto auto;
243
+ }
244
+
245
+ /**
246
+ * The header, the comparison and the message hold text a consumer writes,
247
+ * and a line set on one row (`nowrap` with an ellipsis) has the whole line
248
+ * as its min-content. The figure's column is floored at its min-content, so
249
+ * that line would widen the column past the card. Size containment on the
250
+ * inline axis gives these regions no intrinsic width: the figure alone
251
+ * decides the column, and the text is cut or wrapped by the card's width.
252
+ */
253
+ [data-ui='stat-card'] [data-part='header'] {
254
+ contain: inline-size;
255
+ grid-area: header;
256
+ }
257
+
258
+ [data-ui='stat-card'] [data-part='figure'] {
259
+ align-self: start;
260
+ grid-area: figure;
261
+ min-inline-size: 0;
262
+ }
263
+
264
+ /**
265
+ * The trend is a query container of its own, so what is inside it can
266
+ * answer the width the column gave it: under forty pixels a sparkline is
267
+ * a smear, and the region hides what it holds rather than draw one. The
268
+ * width is the space a wide value left, which a query on the card cannot
269
+ * see.
270
+ */
271
+ [data-ui='stat-card'] [data-part='trend'] {
272
+ /* The marker's ring is the card's ground, not the page's. */
273
+ --slotted-sparkline-ring-color: var(--_surface);
274
+
275
+ align-self: center;
276
+ container-name: stat-card-trend;
277
+ container-type: inline-size;
278
+ grid-area: trend;
279
+ inline-size: 100%;
280
+ min-inline-size: 0;
281
+ }
282
+
283
+ @container stat-card-trend (max-width: 39px) {
284
+ [data-ui='stat-card'] [data-part='trend'] > * {
285
+ visibility: hidden;
286
+ }
287
+ }
288
+
289
+ [data-ui='stat-card'][data-layout='stacked'] [data-part='trend'] {
290
+ align-self: end;
291
+ margin-block-start: var(--slotted-space-2, 0.25rem);
292
+ }
293
+
294
+ [data-ui='stat-card'] [data-part='message'] {
295
+ contain: inline-size;
296
+ grid-area: message;
297
+ }
298
+
299
+ /**
300
+ * Under 220 pixels the trend has no room beside a figure — a value of six
301
+ * digits at 24 pixels is already 90 — so it goes under it, and the value
302
+ * drops one step of type. Literal, as every container breakpoint is
303
+ * (`docs/token-foundation.md`, rule 4).
304
+ */
305
+ @container stat-card (max-width: 219px) {
306
+ [data-ui='stat-card'] [data-part='body'] {
307
+ grid-template-areas:
308
+ 'header'
309
+ 'figure'
310
+ 'trend'
311
+ 'message';
312
+ grid-template-columns: minmax(0, 1fr);
313
+ grid-template-rows: auto minmax(0, 1fr) auto auto;
314
+ }
315
+
316
+ [data-ui='stat-card'] [data-part='trend'] {
317
+ align-self: end;
318
+ margin-block-start: var(--slotted-space-2, 0.25rem);
319
+ }
320
+
321
+ [data-ui='stat-card'] [data-part='value'] {
322
+ font-size: var(--_value-size-narrow);
323
+ line-height: var(--_value-line-narrow);
324
+ }
325
+
326
+ /* The value steps down here, and so does the line its placeholder keeps. */
327
+ [data-ui='stat-card'][data-loading] [data-part='value'] {
328
+ min-block-size: calc(var(--_value-size-narrow) * var(--_value-line-narrow));
329
+ }
330
+ }
331
+
332
+ /* ── Header: the name, a status beside it, an action at the end ──────── */
333
+
334
+ /**
335
+ * On the baseline rather than centred: a label that wraps to two lines
336
+ * keeps its status and its action on its first line, where the eye reads
337
+ * them as part of the name, instead of floating them at the middle of the
338
+ * block.
339
+ */
340
+ [data-ui='stat-card'] [data-part='header'] {
341
+ align-items: baseline;
342
+ column-gap: var(--slotted-space-3, 0.375rem);
343
+ display: flex;
344
+ flex-wrap: wrap;
345
+ min-inline-size: 0;
346
+ row-gap: var(--slotted-space-2, 0.25rem);
347
+ }
348
+
349
+ /**
350
+ * A basis of nought, so the label wraps beside its status rather than
351
+ * taking the line alone and sending the status under it — and a floor of
352
+ * ten rem, so a narrow card does not squeeze the name into a column of
353
+ * single words to keep a badge and a link on its first line. Under the
354
+ * floor the status and the action take the next line instead. A
355
+ * proportion of the type rather than a scale step: it is a width of
356
+ * words, and it scales with them.
357
+ */
358
+ [data-ui='stat-card'] [data-part='label'] {
359
+ color: var(--_ink-muted);
360
+ flex: 1 1 0%;
361
+ font-size: var(--_label-size);
362
+ font-weight: var(--slotted-font-weight-medium, 500);
363
+ line-height: var(--_label-line);
364
+ margin: 0;
365
+ min-inline-size: min(100%, 10rem);
366
+ overflow-wrap: break-word;
367
+ }
368
+
369
+ [data-ui='stat-card'] [data-part='status'] {
370
+ display: inline-flex;
371
+ flex: none;
372
+ }
373
+
374
+ /* The action takes the end of the header's first line and stays there
375
+ when the label wraps: it is the one thing on the card the reader presses,
376
+ and a corner is where a control is looked for. */
377
+ [data-ui='stat-card'] [data-part='action'] {
378
+ flex: none;
379
+ /* The label's line, so a link here is as tall in the fallback face as in
380
+ the web face and the header does not grow when the font arrives. */
381
+ font-size: var(--_label-size);
382
+ line-height: var(--_label-line);
383
+ margin-inline-start: auto;
384
+ }
385
+
386
+ /* ── Figure: the value, its unit, the delta ───────────────────────────── */
387
+
388
+ /**
389
+ * The figure holds the value with its unit and delta on one line, and the
390
+ * comparison under them. The value line is a wrapping row on the baseline,
391
+ * so a delta that does not fit beside a wide number drops under it rather
392
+ * than pushing the number.
393
+ */
394
+ [data-ui='stat-card'] [data-part='figure'] {
395
+ align-items: baseline;
396
+ column-gap: var(--slotted-space-3, 0.375rem);
397
+ display: flex;
398
+ flex-wrap: wrap;
399
+ row-gap: 0;
400
+ }
401
+
402
+ [data-ui='stat-card'] [data-part='comparison'] {
403
+ contain: inline-size;
404
+ flex: 1 1 100%;
405
+ }
406
+
407
+ /**
408
+ * Proportional figures, deliberately: `tabular-nums` gives every digit the
409
+ * width of a nought and a value like 121 reads loose at this size. A column
410
+ * of values in a table wants the tabular ones, and a table cell says so on
411
+ * its own cell.
412
+ *
413
+ * The tracking is a proportion of the type and not a scale step: it exists
414
+ * to pull a large numeral's counters back together, and it is the same
415
+ * fraction at every size.
416
+ */
417
+ [data-ui='stat-card'] [data-part='value'] {
418
+ /* The figure and its unit are two flex items on one baseline — the text
419
+ node is an item of its own — so the gap spaces the unit from the digit
420
+ on whichever side it stands. A margin on the unit, reset by
421
+ `:first-child` for a leading unit, hugged a trailing one: after a bare
422
+ figure the unit is the first element child. */
423
+ align-items: baseline;
424
+ color: var(--_ink);
425
+ display: inline-flex;
426
+ flex-wrap: wrap;
427
+ gap: 0.15em;
428
+ font-size: var(--_value-size);
429
+ font-variant-numeric: proportional-nums;
430
+ font-weight: var(--slotted-font-weight-semibold, 600);
431
+ letter-spacing: -0.02em;
432
+ unicode-bidi: plaintext;
433
+ line-height: var(--_value-line);
434
+ margin: 0;
435
+ min-inline-size: 0;
436
+ /* A number is never truncated: a value wider than its column breaks
437
+ rather than clips, and a consumer who wants it whole formats it
438
+ compact or stacks the card. */
439
+ overflow-wrap: break-word;
440
+ }
441
+
442
+ /* The unit is in the value's own line, a step down in size and weight, so
443
+ `$` and `%` read as annotation of the number rather than as digits. */
444
+ [data-ui='stat-card'] [data-part='unit'] {
445
+ color: var(--_ink-muted);
446
+ font-size: var(--_delta-size);
447
+ font-weight: var(--slotted-font-weight-medium, 500);
448
+ letter-spacing: 0;
449
+ }
450
+
451
+ /**
452
+ * The delta: a mark and a number in the tone the change earned. Text, not
453
+ * a chip — a chip on every card is twelve pills on a page of twelve
454
+ * metrics, and the tone's text role already clears the reading ratio on
455
+ * the surface. It sits on the value's baseline.
456
+ */
457
+ [data-ui='stat-card'] [data-part='delta'] {
458
+ align-items: center;
459
+ color: var(--_tone-text);
460
+ column-gap: var(--slotted-space-1, 0.125rem);
461
+ display: inline-flex;
462
+ font-size: var(--_delta-size);
463
+ font-variant-numeric: tabular-nums;
464
+ font-weight: var(--slotted-font-weight-semibold, 600);
465
+ line-height: 1;
466
+ /* A signed number is a run of its own: in a right-to-left document the
467
+ sign of `+12.4%` is a neutral character and drifts to the far end of
468
+ the run, so the run is laid out by its own content. */
469
+ unicode-bidi: plaintext;
470
+ white-space: nowrap;
471
+ }
472
+
473
+ [data-ui='stat-card'] [data-part='delta'][data-tone='neutral'] {
474
+ --_tone-text: var(--_ink-muted);
475
+ }
476
+
477
+ [data-ui='stat-card'] [data-part='delta'][data-tone='accent'] {
478
+ --_tone-text: var(--slotted-tone-accent-text);
479
+ }
480
+
481
+ [data-ui='stat-card'] [data-part='delta'][data-tone='secondary'] {
482
+ --_tone-text: var(--slotted-tone-secondary-text);
483
+ }
484
+
485
+ [data-ui='stat-card'] [data-part='delta'][data-tone='success'] {
486
+ --_tone-text: var(--slotted-tone-success-text);
487
+ }
488
+
489
+ [data-ui='stat-card'] [data-part='delta'][data-tone='warning'] {
490
+ --_tone-text: var(--slotted-tone-warning-text);
491
+ }
492
+
493
+ [data-ui='stat-card'] [data-part='delta'][data-tone='danger'] {
494
+ --_tone-text: var(--slotted-tone-danger-text);
495
+ }
496
+
497
+ /**
498
+ * The mark: one glyph, `trending-up`, turned over for a fall. Lucide's
499
+ * `trending-down` is the same artwork reflected across the horizontal
500
+ * axis, so the family draws one glyph and mirrors it, the way every
501
+ * chevron in the scale is mirrored by the family that draws it. A flat
502
+ * change draws `minus`. The binding renders the glyph inside the icon box;
503
+ * the part hands the box its size, and the box inherits the delta's ink.
504
+ */
505
+ [data-ui='stat-card'] [data-part='indicator'] {
506
+ --slotted-icon-size: var(--_indicator-size);
507
+
508
+ display: inline-flex;
509
+ flex: none;
510
+ }
511
+
512
+ /* A reflection across the horizontal axis, not motion: nothing travels when
513
+ a rise becomes a fall. Time runs from the right in a right-to-left
514
+ document, so the rise points the other way there, as the sparkline beside
515
+ it does — the `directional` icon box the binding draws does that. */
516
+ [data-ui='stat-card']
517
+ [data-part='delta'][data-trend='down']
518
+ [data-part='indicator']
519
+ > [data-ui='icon'] {
520
+ rotate: x 180deg;
521
+ }
522
+
523
+ /* ── Comparison: the prose under the figure ───────────────────────────── */
524
+
525
+ [data-ui='stat-card'] [data-part='comparison'] {
526
+ color: var(--_ink-muted);
527
+ font-size: var(--_prose-size);
528
+ line-height: var(--_prose-line);
529
+ margin-block-start: var(--_region-gap);
530
+ margin: 0;
531
+ min-inline-size: 0;
532
+ overflow-wrap: anywhere;
533
+ }
534
+
535
+ [data-ui='stat-card'] [data-part='period'] {
536
+ color: var(--_ink-muted);
537
+ }
538
+
539
+ /* ── Trend: where a sparkline goes ────────────────────────────────────── */
540
+
541
+ /**
542
+ * The region hands its tone to the sparkline inside it, which reads
543
+ * `--slotted-sparkline-color` when it has no tone or series of its own. The
544
+ * tone is the delta's: a rise that is good draws the line in `success`.
545
+ * Neutral hands nothing, so the sparkline keeps the data scale's first slot
546
+ * — a card with no delta, or a metric with no valence, draws its trend in
547
+ * the theme's blue and not in grey.
548
+ */
549
+ [data-ui='stat-card'] [data-part='trend'] {
550
+ align-self: center;
551
+ display: flex;
552
+ }
553
+
554
+ [data-ui='stat-card'][data-tone='success'] [data-part='trend'] {
555
+ --slotted-sparkline-color: var(--slotted-tone-success-text);
556
+ }
557
+
558
+ [data-ui='stat-card'][data-tone='danger'] [data-part='trend'] {
559
+ --slotted-sparkline-color: var(--slotted-tone-danger-text);
560
+ }
561
+
562
+ [data-ui='stat-card'][data-tone='warning'] [data-part='trend'] {
563
+ --slotted-sparkline-color: var(--slotted-tone-warning-text);
564
+ }
565
+
566
+ [data-ui='stat-card'][data-tone='accent'] [data-part='trend'] {
567
+ --slotted-sparkline-color: var(--slotted-tone-accent-text);
568
+ }
569
+
570
+ [data-ui='stat-card'][data-tone='secondary'] [data-part='trend'] {
571
+ --slotted-sparkline-color: var(--slotted-tone-secondary-text);
572
+ }
573
+
574
+ [data-ui='stat-card'][data-fill='plain'] [data-part='trend'] {
575
+ --slotted-sparkline-ring-color: var(--slotted-surface-raised, Canvas);
576
+ }
577
+
578
+ /* ── Message: what stands in for a value that is missing or failed ────── */
579
+
580
+ [data-ui='stat-card'] [data-part='message'] {
581
+ color: var(--_ink-muted);
582
+ font-size: var(--_prose-size);
583
+ line-height: var(--_prose-line);
584
+ margin: 0;
585
+ min-inline-size: 0;
586
+ overflow-wrap: anywhere;
587
+ }
588
+
589
+ [data-ui='stat-card'][data-status='error'] [data-part='message'] {
590
+ color: var(--slotted-tone-danger-text);
591
+ }
592
+
593
+ /* A value that is not there is a dash in the muted step, at the value's
594
+ own size, so the row keeps its height and its rhythm. */
595
+ [data-ui='stat-card'][data-status='empty'] [data-part='value'],
596
+ [data-ui='stat-card'][data-status='error'] [data-part='value'] {
597
+ color: var(--_ink-muted);
598
+ }
599
+
600
+ /* ── Loading ──────────────────────────────────────────────────────────── */
601
+
602
+ /**
603
+ * The binding renders a `skeleton` in place of each text part and a block
604
+ * in the trend, each the height of what it stands for; the sheet only has
605
+ * to keep the trend's box at the sparkline's height so the card does not
606
+ * change size when the line arrives.
607
+ */
608
+ /**
609
+ * The value's box is an inline flex line, so its height is its content's.
610
+ * A text placeholder is one cap tall — that is what makes a bar sit on the
611
+ * band a capital occupies — and a card whose figure is a bar was one cap
612
+ * tall where the same card loaded is one line, fourteen pixels shorter at
613
+ * the small step. Every box under a row of figures moved when the answer
614
+ * arrived. The floor is one line of the value's own type, so the two states
615
+ * occupy the same box.
616
+ */
617
+ [data-ui='stat-card'][data-loading] [data-part='value'] {
618
+ min-block-size: calc(var(--_value-size) * var(--_value-line));
619
+ }
620
+
621
+ [data-ui='stat-card'][data-loading] [data-part='trend'] {
622
+ --slotted-skeleton-block-size: var(
623
+ --slotted-sparkline-block-size,
624
+ var(--slotted-space-9, 2rem)
625
+ );
626
+ }
627
+
628
+ [data-ui='stat-card'][data-loading][data-size='sm'] [data-part='trend'] {
629
+ --slotted-skeleton-block-size: var(
630
+ --slotted-sparkline-block-size-sm,
631
+ var(--slotted-space-7, 1.25rem)
632
+ );
633
+ }
634
+
635
+ [data-ui='stat-card'][data-loading][data-size='lg'] [data-part='trend'] {
636
+ --slotted-skeleton-block-size: var(
637
+ --slotted-sparkline-block-size-lg,
638
+ var(--slotted-space-11, 3rem)
639
+ );
640
+ }
641
+ }