@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,80 @@
1
+ [
2
+ "--slotted-border-subtle",
3
+ "--slotted-control-border-color",
4
+ "--slotted-control-border-width",
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-gap-lg",
10
+ "--slotted-control-gap-md",
11
+ "--slotted-control-gap-sm",
12
+ "--slotted-control-height-lg",
13
+ "--slotted-control-height-md",
14
+ "--slotted-control-height-sm",
15
+ "--slotted-control-icon-size-lg",
16
+ "--slotted-control-icon-size-md",
17
+ "--slotted-control-icon-size-sm",
18
+ "--slotted-control-inner-size-lg",
19
+ "--slotted-control-inner-size-md",
20
+ "--slotted-control-inner-size-sm",
21
+ "--slotted-control-invalid-edge-width",
22
+ "--slotted-control-padding-inline-lg",
23
+ "--slotted-control-padding-inline-md",
24
+ "--slotted-control-padding-inline-sm",
25
+ "--slotted-control-radius",
26
+ "--slotted-control-shadow",
27
+ "--slotted-control-shadow-focus-geometry",
28
+ "--slotted-control-surface",
29
+ "--slotted-control-transition-duration",
30
+ "--slotted-control-transition-easing",
31
+ "--slotted-disabled-background",
32
+ "--slotted-disabled-border",
33
+ "--slotted-disabled-foreground",
34
+ "--slotted-focus-ring-color",
35
+ "--slotted-focus-ring-color-invalid",
36
+ "--slotted-focus-ring-offset",
37
+ "--slotted-focus-ring-width",
38
+ "--slotted-number-field-action-size",
39
+ "--slotted-number-field-adornment-color",
40
+ "--slotted-number-field-background",
41
+ "--slotted-number-field-border-color",
42
+ "--slotted-number-field-color",
43
+ "--slotted-number-field-disabled-background",
44
+ "--slotted-number-field-disabled-border-color",
45
+ "--slotted-number-field-disabled-color",
46
+ "--slotted-number-field-hover-border-color",
47
+ "--slotted-number-field-icon-size-min",
48
+ "--slotted-number-field-invalid-border-color",
49
+ "--slotted-number-field-invalid-edge-width",
50
+ "--slotted-number-field-invalid-focus-ring-color",
51
+ "--slotted-number-field-placeholder-color",
52
+ "--slotted-number-field-plain-hover-background",
53
+ "--slotted-number-field-radius",
54
+ "--slotted-number-field-readonly-background",
55
+ "--slotted-number-field-readonly-border-color",
56
+ "--slotted-number-field-scrub-active-color",
57
+ "--slotted-number-field-shadow",
58
+ "--slotted-number-field-stepper-active-background",
59
+ "--slotted-number-field-stepper-color",
60
+ "--slotted-number-field-stepper-disabled-background",
61
+ "--slotted-number-field-stepper-disabled-color",
62
+ "--slotted-number-field-stepper-hover-background",
63
+ "--slotted-number-field-stepper-size",
64
+ "--slotted-number-field-subtle-background",
65
+ "--slotted-number-field-subtle-border-color",
66
+ "--slotted-number-field-subtle-hover-background",
67
+ "--slotted-radius-none",
68
+ "--slotted-radius-xs",
69
+ "--slotted-space-1",
70
+ "--slotted-space-2",
71
+ "--slotted-surface-sunken",
72
+ "--slotted-target-comfortable",
73
+ "--slotted-target-min",
74
+ "--slotted-text",
75
+ "--slotted-text-muted",
76
+ "--slotted-tone-accent-border",
77
+ "--slotted-tone-danger-border",
78
+ "--slotted-tone-secondary-subtle-active",
79
+ "--slotted-tone-secondary-subtle-hover"
80
+ ]
@@ -0,0 +1,525 @@
1
+ @layer slotted.components {
2
+ /**
3
+ * ── The one-time code ──────────────────────────────────────────────────
4
+ *
5
+ * One real `input` and N painted boxes. The input is the control — value,
6
+ * name, form, `autocomplete="one-time-code"`, one tab stop, one accessible
7
+ * name — and it lies *over* the boxes, transparent: it is what receives
8
+ * the click, the long press with "Paste" on iOS and the keyboard's own
9
+ * autofill bar. The boxes are paint, hidden from assistive technology, and
10
+ * each one shows one character of the value the input holds.
11
+ *
12
+ * Every rule keys on `data-ui` and `data-part`. The field around the control
13
+ * mirrors its state onto the root rather than styling through, so a control
14
+ * used outside a field looks the same as one inside it.
15
+ */
16
+ [data-ui='otp-input'][data-part='root'] {
17
+ /**
18
+ * A box is a square on the control's height at this density, so a code
19
+ * beside a text field in the same form sits on the same row height. Its
20
+ * type is one step of the text scale rather than the control scale: a
21
+ * character alone in a box reads one size larger than a word in a field,
22
+ * and the box does not shrink with density the way the text does.
23
+ */
24
+ --_height: var(--slotted-control-height-md, 34px);
25
+ --_font-size: var(--slotted-font-size-md, 0.875rem);
26
+ --_gap: var(--slotted-control-gap-md, 8px);
27
+ --_radius: var(--slotted-otp-input-radius, var(--slotted-control-radius, 4px));
28
+ /* The width of the fake caret, from the border scale: a rule the weight of
29
+ a thin edge, which is what the browser's own caret is at this size. */
30
+ --_caret-width: var(--slotted-otp-input-caret-width, var(--slotted-border-width-thin, 1px));
31
+
32
+ align-items: center;
33
+ color: var(--slotted-otp-input-color, var(--slotted-text, CanvasText));
34
+ /**
35
+ * A code is a number, and a number does not mirror. In a right-to-left
36
+ * `<input>` the bidi algorithm keeps a run of digits left-to-right; the
37
+ * SMS shows `482 915` in that order; and a row of boxes that read
38
+ * `519 284` from the right would be the one place the code was reversed.
39
+ * The row is therefore left-to-right in both directions, and so is the
40
+ * input over it, so ArrowRight moves the cursor toward the box on its
41
+ * right. The root still sits where the page puts it — at the start edge
42
+ * of a right-to-left line — because its own position is its parent's.
43
+ */
44
+ direction: ltr;
45
+ display: inline-flex;
46
+ font-family: var(--slotted-control-font-family, system-ui, sans-serif);
47
+ font-size: var(--_font-size);
48
+ font-weight: var(--slotted-otp-input-font-weight, var(--slotted-font-weight-medium, 500));
49
+ /* Digits that do not shift as the code is typed over. */
50
+ font-variant-numeric: tabular-nums;
51
+ /* The distance between two groups holds the separator with air each side. */
52
+ gap: var(--slotted-otp-input-group-gap, var(--slotted-space-4, 0.5rem));
53
+ /**
54
+ * As wide as its boxes and no wider. Stretched to a field's full column
55
+ * the row would pack its boxes at its own left — the row is left-to-right
56
+ * whatever the page does — and a right-to-left field would show them at
57
+ * the far end of the line rather than at its start.
58
+ */
59
+ inline-size: fit-content;
60
+ line-height: 1;
61
+ /* Never wider than what holds it: the boxes give up width first. */
62
+ max-inline-size: 100%;
63
+ /* The containing block of the input laid over the boxes. */
64
+ position: relative;
65
+ }
66
+
67
+ /**
68
+ * The control. It gives up everything it would paint — text, caret,
69
+ * selection, ground, edge, ring — and keeps everything that makes it a text
70
+ * control: the value, the native selection the boxes mirror, and every
71
+ * native attribute a consumer wrote. `opacity: 1` rather than 0, because iOS
72
+ * shows no paste menu on an invisible control (input-otp, `input.tsx`).
73
+ */
74
+ [data-ui='otp-input'] > [data-part='input'] {
75
+ appearance: none;
76
+ background-color: transparent;
77
+ block-size: 100%;
78
+ border: 0;
79
+ box-shadow: none;
80
+ caret-color: transparent;
81
+ color: transparent;
82
+ font: inherit;
83
+ inline-size: 100%;
84
+ inset: 0;
85
+ margin: 0;
86
+ /* The boxes draw the ring for it, so two rings never nest. */
87
+ outline: none;
88
+ padding: 0;
89
+ position: absolute;
90
+ /* The pointer reaches the input, and the family maps it to a box. */
91
+ touch-action: manipulation;
92
+ -webkit-tap-highlight-color: transparent;
93
+ -webkit-text-fill-color: transparent;
94
+ /* Above the boxes: the paint never intercepts the control. */
95
+ z-index: 1;
96
+ }
97
+
98
+ /**
99
+ * The selected character is painted by the browser in its own selection
100
+ * ink, over the element's transparent fill, so the selection has to be
101
+ * transparent in every property Chrome reads for it. And a press on a
102
+ * selected character starts a drag of that text, with a ghost of the
103
+ * character floating under the pointer: a full code has a character
104
+ * selected at every press, so the drag is refused — here for the engines
105
+ * that read the property, and in both bindings on `dragstart`.
106
+ */
107
+ [data-ui='otp-input'] > [data-part='input']::selection {
108
+ /**
109
+ * `!important`, the only one in the library. An application that styles
110
+ * its own `::selection` — and the workbench does, unlayered — outranks a
111
+ * layered rule, and the selected character then paints in that page's
112
+ * ink at the input's left edge, beside the boxes: a second copy of the
113
+ * digit, floating. An important declaration in a layer beats every normal
114
+ * declaration outside it, which is the one thing this rule needs.
115
+ */
116
+ background-color: transparent !important;
117
+ color: transparent !important;
118
+ text-shadow: none !important;
119
+ -webkit-text-fill-color: transparent !important;
120
+ }
121
+
122
+ [data-ui='otp-input'] > [data-part='input'] {
123
+ -webkit-user-drag: none;
124
+ }
125
+
126
+ /**
127
+ * Autofill. Chrome paints its own ground over the control's, and the classic
128
+ * repair — a hundred-pixel inset shadow — is the only way to reach it. The
129
+ * signal is kept rather than erased, as `text-field` keeps it: an autofilled
130
+ * code is a fact worth seeing, so the boxes sit on the accent surface the
131
+ * theme owns rather than on the browser's yellow.
132
+ */
133
+ [data-ui='otp-input'] > [data-part='input']:autofill {
134
+ box-shadow: inset 0 0 0 100px
135
+ var(--slotted-otp-input-autofill-background, var(--slotted-tone-accent-surface));
136
+ -webkit-text-fill-color: transparent;
137
+ }
138
+
139
+ /* ── Groups and the separator ─────────────────────────────────────────
140
+ *
141
+ * `groups` partitions the boxes; `123-456` is how the SMS arrives. Each
142
+ * group is a row of boxes, and between two groups a short rule is drawn —
143
+ * never a character, which would be read by nothing and mirrored by nothing.
144
+ */
145
+ [data-ui='otp-input'] > [data-part='group'] {
146
+ align-items: center;
147
+ display: flex;
148
+ gap: var(--_gap);
149
+ min-inline-size: 0;
150
+ }
151
+
152
+ [data-ui='otp-input'] > [data-part='separator'] {
153
+ background-color: var(
154
+ --slotted-otp-input-separator-color,
155
+ var(--slotted-control-border-color, ButtonBorder)
156
+ );
157
+ block-size: var(--slotted-border-width-thick, 2px);
158
+ border-radius: var(--slotted-radius-full, 9999px);
159
+ flex: 0 0 auto;
160
+ inline-size: var(--slotted-otp-input-separator-size, var(--slotted-space-4, 0.5rem));
161
+ }
162
+
163
+ /* ── The box ──────────────────────────────────────────────────────────
164
+ *
165
+ * A square on the control height, framed like a text field: the control
166
+ * step of the border scale, the control radius, the resting lift every
167
+ * framed control carries. It shrinks before the row overflows — a code in a
168
+ * 240-pixel column loses width, not boxes.
169
+ */
170
+ [data-ui='otp-input'] [data-part='slot'] {
171
+ align-items: center;
172
+ background-color: var(--slotted-otp-input-background, var(--slotted-control-surface, Field));
173
+ block-size: var(--_height);
174
+ border: var(--slotted-control-border-width, 1px) solid
175
+ var(--slotted-otp-input-border-color, var(--slotted-control-border-color, ButtonBorder));
176
+ border-radius: var(--_radius);
177
+ box-shadow: var(--slotted-otp-input-shadow, var(--slotted-control-shadow, none));
178
+ box-sizing: border-box;
179
+ display: flex;
180
+ flex: 0 1 auto;
181
+ inline-size: var(--_height);
182
+ justify-content: center;
183
+ /**
184
+ * How narrow a box may go before the row overflows instead: six tenths
185
+ * of its height, which still holds a digit at every step and density. A
186
+ * floor of zero let a field that shrink-wrapped to its label column
187
+ * squeeze the boxes to the width of the digit inside them.
188
+ */
189
+ min-inline-size: calc(var(--_height) * 0.6);
190
+ /* The containing block of the caret. */
191
+ position: relative;
192
+ transition:
193
+ background-color var(--slotted-control-transition-duration)
194
+ var(--slotted-control-transition-easing),
195
+ border-color var(--slotted-control-transition-duration)
196
+ var(--slotted-control-transition-easing),
197
+ box-shadow var(--slotted-control-transition-duration) var(--slotted-control-transition-easing);
198
+ /* A character in a box is a character, not a line: nothing wraps. */
199
+ white-space: nowrap;
200
+ }
201
+
202
+ /**
203
+ * The placeholder, on an empty box only. `attr()` reads the character the
204
+ * binding distributed to this box; a pseudo-element leaves `:empty` true,
205
+ * so the caret and the filled state keep reading the DOM rather than the
206
+ * paint.
207
+ */
208
+ [data-ui='otp-input'] [data-part='slot']:empty::before {
209
+ color: var(--slotted-otp-input-placeholder-color, var(--slotted-text-muted));
210
+ content: attr(data-placeholder);
211
+ }
212
+
213
+ /**
214
+ * The mask. The input is `type="password"` and each filled box shows the
215
+ * bullet a password field shows — a character standing for a hidden
216
+ * character, which is the platform's own convention and not an icon. It is
217
+ * set two steps up the type scale: at the text's own size the bullet is a
218
+ * four-pixel speck in a thirty-four-pixel box, and a PIN pad draws its dots
219
+ * larger than its digits.
220
+ */
221
+ [data-ui='otp-input'][data-mask] [data-part='slot']:not(:empty) {
222
+ font-size: var(--slotted-otp-input-mask-size, var(--slotted-font-size-2xl, 1.25rem));
223
+ }
224
+
225
+ /* ── Attached ─────────────────────────────────────────────────────────
226
+ *
227
+ * The boxes of a group share their edges and keep a corner only at the
228
+ * ends, the shape shadcn draws by default and Chakra names `attached`. Each
229
+ * box pulls one border width over its neighbour, so the seam is one line
230
+ * rather than two, and the raised box — hovered, current, focused — comes
231
+ * forward so its whole edge is seen.
232
+ */
233
+ [data-ui='otp-input'][data-attached] > [data-part='group'] {
234
+ gap: 0;
235
+ }
236
+
237
+ [data-ui='otp-input'][data-attached] [data-part='slot'] {
238
+ border-radius: 0;
239
+ }
240
+
241
+ [data-ui='otp-input'][data-attached] [data-part='slot']:not(:first-child) {
242
+ margin-inline-start: calc(var(--slotted-control-border-width, 1px) * -1);
243
+ }
244
+
245
+ [data-ui='otp-input'][data-attached] [data-part='slot']:first-child {
246
+ border-start-start-radius: var(--_radius);
247
+ border-end-start-radius: var(--_radius);
248
+ }
249
+
250
+ [data-ui='otp-input'][data-attached] [data-part='slot']:last-child {
251
+ border-start-end-radius: var(--_radius);
252
+ border-end-end-radius: var(--_radius);
253
+ }
254
+
255
+ [data-ui='otp-input'][data-attached] [data-part='slot'][data-current] {
256
+ z-index: 1;
257
+ }
258
+
259
+ /* ── Size ─────────────────────────────────────────────────────────────
260
+ *
261
+ * Three steps, matching the field the control sits in. The box height is
262
+ * the control height at that step; the type is the text scale's step of the
263
+ * same name, so a `lg` code is a `lg` word's size and one step larger than
264
+ * a `lg` field's text.
265
+ */
266
+ [data-ui='otp-input'][data-part='root'][data-size='sm'] {
267
+ --_height: var(--slotted-control-height-sm, 30px);
268
+ --_font-size: var(--slotted-font-size-sm, 0.8125rem);
269
+ --_gap: var(--slotted-control-gap-sm, 6px);
270
+ }
271
+
272
+ [data-ui='otp-input'][data-part='root'][data-size='md'] {
273
+ --_height: var(--slotted-control-height-md, 34px);
274
+ --_font-size: var(--slotted-font-size-md, 0.875rem);
275
+ --_gap: var(--slotted-control-gap-md, 8px);
276
+ }
277
+
278
+ [data-ui='otp-input'][data-part='root'][data-size='lg'] {
279
+ --_height: var(--slotted-control-height-lg, 38px);
280
+ --_font-size: var(--slotted-font-size-lg, 1rem);
281
+ --_gap: var(--slotted-control-gap-lg, 8px);
282
+ }
283
+
284
+ /* ── Variant ──────────────────────────────────────────────────────────
285
+ *
286
+ * `outline` takes the ground it is placed on; `subtle` sinks into the page.
287
+ * The same two steps `text-field` draws, with the same tokens behind them,
288
+ * so a code under a text field in one form is one form.
289
+ */
290
+ [data-ui='otp-input'][data-variant='subtle'] [data-part='slot'] {
291
+ background-color: var(
292
+ --slotted-otp-input-subtle-background,
293
+ var(--slotted-surface-sunken, Canvas)
294
+ );
295
+ border-color: var(
296
+ --slotted-otp-input-subtle-border-color,
297
+ var(--slotted-border-subtle, ButtonBorder)
298
+ );
299
+ }
300
+
301
+ /* ── The caret ────────────────────────────────────────────────────────
302
+ *
303
+ * The browser's caret is transparent, so the box where the cursor is draws
304
+ * one: a rule the height of the type, in the text's ink, on the box that is
305
+ * current and empty. A current box that holds a character is selected
306
+ * instead — typing replaces it — and shows the selection, not a caret.
307
+ *
308
+ * It blinks on the loop scale, and under reduced motion it stands still
309
+ * rather than blinking faster: an endless animation stops, it does not
310
+ * hurry.
311
+ */
312
+ [data-ui='otp-input'][data-part='root']:has(> [data-part='input']:focus)
313
+ [data-part='slot'][data-current]:empty::after {
314
+ animation: slotted-otp-input-blink var(--slotted-duration-loop) step-end infinite;
315
+ background-color: currentcolor;
316
+ block-size: 1em;
317
+ content: '';
318
+ inline-size: var(--_caret-width);
319
+ position: absolute;
320
+ }
321
+
322
+ @keyframes slotted-otp-input-blink {
323
+ 0%,
324
+ 50% {
325
+ opacity: 1;
326
+ }
327
+
328
+ 50.01%,
329
+ 100% {
330
+ opacity: 0;
331
+ }
332
+ }
333
+
334
+ @media (prefers-reduced-motion: reduce) {
335
+ [data-ui='otp-input'][data-part='root']:has(> [data-part='input']:focus)
336
+ [data-part='slot'][data-current]:empty::after {
337
+ animation: none;
338
+ }
339
+ }
340
+
341
+ /* ── States ───────────────────────────────────────────────────────────
342
+ *
343
+ * Written strongest last, and each weaker one guarded against every stronger
344
+ * one it can meet, which is the order `STATE_PRECEDENCE` sets. The pointer
345
+ * is always over the input, never over a box, so hover is the root's and it
346
+ * lights the whole row: the row is one control.
347
+ */
348
+ [data-ui='otp-input'][data-part='root']:hover:not([data-disabled]):not([data-readonly]):not(
349
+ [data-invalid]
350
+ )
351
+ [data-part='slot'] {
352
+ border-color: var(--slotted-otp-input-hover-border-color, var(--slotted-tone-accent-border));
353
+ }
354
+
355
+ [data-ui='otp-input'][data-part='root'][data-variant='subtle']:hover:not([data-disabled]):not(
356
+ [data-readonly]
357
+ ):not([data-invalid])
358
+ [data-part='slot'] {
359
+ background-color: var(
360
+ --slotted-otp-input-subtle-hover-background,
361
+ var(--slotted-tone-secondary-subtle-hover)
362
+ );
363
+ }
364
+
365
+ /**
366
+ * Where the reader is. The box holding the cursor, or every box inside the
367
+ * selection, takes the accent edge; a *filled* current box is a selected
368
+ * character and tints its ground as well, so the reader can see that the
369
+ * next key replaces rather than inserts. Both are drawn only while the
370
+ * control has focus: a current box in a blurred control would be a caret in
371
+ * a field nobody is in. Invalid outranks the accent edge — a code in error
372
+ * keeps its danger edge on every box, and the ring says where the cursor
373
+ * is — while the tint survives, because it says what the next key does.
374
+ */
375
+ [data-ui='otp-input'][data-part='root']:not([data-invalid]):has(> [data-part='input']:focus)
376
+ [data-part='slot'][data-current] {
377
+ border-color: var(--slotted-otp-input-current-border-color, var(--slotted-tone-accent-border));
378
+ }
379
+
380
+ [data-ui='otp-input'][data-part='root']:has(> [data-part='input']:focus)
381
+ [data-part='slot'][data-current]:not(:empty) {
382
+ background-color: var(
383
+ --slotted-otp-input-selection-background,
384
+ var(--slotted-tone-accent-surface)
385
+ );
386
+ }
387
+
388
+ /**
389
+ * The ring belongs to the current box, and it answers the control's focus
390
+ * rather than the box's: a browser matches `:focus-visible` on a text input
391
+ * for a pointer press as well as for a Tab, which is right for a control
392
+ * whose caret is the thing that moved. A ring around the whole row would sit
393
+ * still while the cursor moved inside it.
394
+ */
395
+ [data-ui='otp-input'][data-part='root']:has(> [data-part='input']:focus-visible)
396
+ [data-part='slot'][data-current] {
397
+ /**
398
+ * The resting shadow, recoloured. The geometry is the foundation's and the
399
+ * ink is the focus ring's, so the lift under the box and the ring around
400
+ * it are one colour and the state decides which colour that is.
401
+ */
402
+ box-shadow: var(--slotted-control-shadow-focus-geometry, 0 1px 4px -1px)
403
+ var(--slotted-focus-ring-color, Highlight);
404
+ outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
405
+ outline-offset: var(--slotted-focus-ring-offset, 2px);
406
+ }
407
+
408
+ /**
409
+ * Invalid outranks hover and is written after it for that reason. The ring
410
+ * is retargeted rather than repainted: every rule that draws focus reads
411
+ * `--slotted-focus-ring-color`, so pointing that one name at the invalid
412
+ * colour on the root moves all of them at once — the rule `text-field`
413
+ * wrote and every field family inherits.
414
+ */
415
+ [data-ui='otp-input'][data-part='root'][data-invalid] {
416
+ --slotted-focus-ring-color: var(
417
+ --slotted-otp-input-invalid-focus-ring-color,
418
+ var(--slotted-focus-ring-color-invalid, Highlight)
419
+ );
420
+ }
421
+
422
+ [data-ui='otp-input'][data-part='root'][data-invalid] [data-part='slot'] {
423
+ --_invalid-edge-width: var(
424
+ --slotted-otp-input-invalid-edge-width,
425
+ var(--slotted-control-invalid-edge-width, 1px)
426
+ );
427
+ --_invalid-edge-color: var(
428
+ --slotted-otp-input-invalid-border-color,
429
+ var(--slotted-tone-danger-border)
430
+ );
431
+ --_invalid-edge: inset 0 0 0 var(--_invalid-edge-width) var(--_invalid-edge-color);
432
+
433
+ border-color: var(--slotted-otp-input-invalid-border-color, var(--slotted-tone-danger-border));
434
+ /*
435
+ * Width as well as colour (WCAG 1.4.1), the edge `text-field` draws: in
436
+ * `high-contrast` the danger border is the resting edge's black. The line
437
+ * is a shadow inside the border, so neither box nor text moves, and it
438
+ * takes the resting lift's place.
439
+ */
440
+ box-shadow: var(--_invalid-edge);
441
+ }
442
+
443
+ [data-ui='otp-input'][data-part='root'][data-invalid]:has(> [data-part='input']:focus-visible)
444
+ [data-part='slot'][data-current] {
445
+ box-shadow:
446
+ var(--_invalid-edge),
447
+ var(--slotted-control-shadow-focus-geometry, 0 1px 4px -1px)
448
+ var(--slotted-focus-ring-color, Highlight);
449
+ }
450
+
451
+ /*
452
+ * Forced colours remove every box shadow, and the edge with them. The
453
+ * width comes back as an outline inside the border, and a focused invalid
454
+ * control draws its ring double, which a valid ring is not.
455
+ */
456
+ @media (forced-colors: active) {
457
+ [data-ui='otp-input'][data-part='root'][data-invalid] [data-part='slot'] {
458
+ outline: var(--_invalid-edge-width) solid CanvasText;
459
+ outline-offset: calc(
460
+ -1 * (var(--slotted-control-border-width, 1px) + var(--_invalid-edge-width))
461
+ );
462
+ }
463
+
464
+ [data-ui='otp-input'][data-part='root'][data-invalid]:has(> [data-part='input']:focus-visible)
465
+ [data-part='slot'][data-current] {
466
+ outline: calc(var(--slotted-focus-ring-width, 2px) + var(--_invalid-edge-width)) double
467
+ var(--slotted-focus-ring-color, Highlight);
468
+ outline-offset: var(--slotted-focus-ring-offset, 2px);
469
+ }
470
+ }
471
+
472
+ /**
473
+ * Read-only stays focusable, selectable and legible, and it must not be
474
+ * mistakable for disabled: the value is still real and still submitted. The
475
+ * ground says "not yours to change" and the ink says nothing at all. No
476
+ * caret either — there is nothing to type.
477
+ */
478
+ [data-ui='otp-input'][data-part='root'][data-readonly] [data-part='slot'] {
479
+ background-color: var(
480
+ --slotted-otp-input-readonly-background,
481
+ var(--slotted-tone-secondary-subtle-hover)
482
+ );
483
+ cursor: default;
484
+ }
485
+
486
+ [data-ui='otp-input'][data-part='root'][data-readonly] [data-part='slot']::after {
487
+ content: none;
488
+ }
489
+
490
+ /**
491
+ * `:disabled` on the control is matched beside `data-disabled` on the root,
492
+ * because a control can be disabled by something that never told it so: a
493
+ * disabled `fieldset` disables every control inside it, and the control has
494
+ * no way to know.
495
+ */
496
+ [data-ui='otp-input'][data-part='root'][data-disabled],
497
+ [data-ui='otp-input'][data-part='root']:has(> [data-part='input']:disabled) {
498
+ color: var(--slotted-otp-input-disabled-color, var(--slotted-disabled-foreground));
499
+ cursor: not-allowed;
500
+ }
501
+
502
+ [data-ui='otp-input'][data-part='root'][data-disabled] [data-part='slot'],
503
+ [data-ui='otp-input'][data-part='root']:has(> [data-part='input']:disabled) [data-part='slot'] {
504
+ background-color: var(
505
+ --slotted-otp-input-disabled-background,
506
+ var(--slotted-disabled-background)
507
+ );
508
+ border-color: var(--slotted-otp-input-disabled-border-color, var(--slotted-disabled-border));
509
+ /* Nothing a person cannot reach is lifted off the page. */
510
+ box-shadow: none;
511
+ }
512
+
513
+ /**
514
+ * The one rule the theme cannot write for the family. Forced colours give
515
+ * every text its system ink, and the transparent text laid over the boxes
516
+ * would come back as a second copy of the code. The input keeps its own
517
+ * colours — all of them transparent — and the boxes read the tokens the
518
+ * theme already remaps.
519
+ */
520
+ @media (forced-colors: active) {
521
+ [data-ui='otp-input'] > [data-part='input'] {
522
+ forced-color-adjust: none;
523
+ }
524
+ }
525
+ }
@@ -0,0 +1,69 @@
1
+ [
2
+ "--slotted-border-subtle",
3
+ "--slotted-border-width-thick",
4
+ "--slotted-border-width-thin",
5
+ "--slotted-control-border-color",
6
+ "--slotted-control-border-width",
7
+ "--slotted-control-font-family",
8
+ "--slotted-control-gap-lg",
9
+ "--slotted-control-gap-md",
10
+ "--slotted-control-gap-sm",
11
+ "--slotted-control-height-lg",
12
+ "--slotted-control-height-md",
13
+ "--slotted-control-height-sm",
14
+ "--slotted-control-invalid-edge-width",
15
+ "--slotted-control-radius",
16
+ "--slotted-control-shadow",
17
+ "--slotted-control-shadow-focus-geometry",
18
+ "--slotted-control-surface",
19
+ "--slotted-control-transition-duration",
20
+ "--slotted-control-transition-easing",
21
+ "--slotted-disabled-background",
22
+ "--slotted-disabled-border",
23
+ "--slotted-disabled-foreground",
24
+ "--slotted-duration-loop",
25
+ "--slotted-focus-ring-color",
26
+ "--slotted-focus-ring-color-invalid",
27
+ "--slotted-focus-ring-offset",
28
+ "--slotted-focus-ring-width",
29
+ "--slotted-font-size-2xl",
30
+ "--slotted-font-size-lg",
31
+ "--slotted-font-size-md",
32
+ "--slotted-font-size-sm",
33
+ "--slotted-font-weight-medium",
34
+ "--slotted-otp-input-autofill-background",
35
+ "--slotted-otp-input-background",
36
+ "--slotted-otp-input-border-color",
37
+ "--slotted-otp-input-caret-width",
38
+ "--slotted-otp-input-color",
39
+ "--slotted-otp-input-current-border-color",
40
+ "--slotted-otp-input-disabled-background",
41
+ "--slotted-otp-input-disabled-border-color",
42
+ "--slotted-otp-input-disabled-color",
43
+ "--slotted-otp-input-font-weight",
44
+ "--slotted-otp-input-group-gap",
45
+ "--slotted-otp-input-hover-border-color",
46
+ "--slotted-otp-input-invalid-border-color",
47
+ "--slotted-otp-input-invalid-edge-width",
48
+ "--slotted-otp-input-invalid-focus-ring-color",
49
+ "--slotted-otp-input-mask-size",
50
+ "--slotted-otp-input-placeholder-color",
51
+ "--slotted-otp-input-radius",
52
+ "--slotted-otp-input-readonly-background",
53
+ "--slotted-otp-input-selection-background",
54
+ "--slotted-otp-input-separator-color",
55
+ "--slotted-otp-input-separator-size",
56
+ "--slotted-otp-input-shadow",
57
+ "--slotted-otp-input-subtle-background",
58
+ "--slotted-otp-input-subtle-border-color",
59
+ "--slotted-otp-input-subtle-hover-background",
60
+ "--slotted-radius-full",
61
+ "--slotted-space-4",
62
+ "--slotted-surface-sunken",
63
+ "--slotted-text",
64
+ "--slotted-text-muted",
65
+ "--slotted-tone-accent-border",
66
+ "--slotted-tone-accent-surface",
67
+ "--slotted-tone-danger-border",
68
+ "--slotted-tone-secondary-subtle-hover"
69
+ ]