@sixthshift/design-system 0.5.0 → 0.7.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 (433) hide show
  1. package/README.md +132 -87
  2. package/dist/components/Badge/Badge.d.ts +12 -4
  3. package/dist/components/Badge/Badge.d.ts.map +1 -1
  4. package/dist/components/Badge/Badge.js +8 -6
  5. package/dist/components/Badge/Badge.js.map +1 -1
  6. package/dist/components/Button/Button.d.ts +26 -3
  7. package/dist/components/Button/Button.d.ts.map +1 -1
  8. package/dist/components/Button/Button.js +14 -5
  9. package/dist/components/Button/Button.js.map +1 -1
  10. package/dist/components/Calendar/Calendar.d.ts.map +1 -1
  11. package/dist/components/Calendar/Calendar.js +1 -0
  12. package/dist/components/Calendar/Calendar.js.map +1 -1
  13. package/dist/components/Calendar/CalendarView.d.ts +0 -11
  14. package/dist/components/Calendar/CalendarView.d.ts.map +1 -1
  15. package/dist/components/Calendar/CalendarView.js +1 -0
  16. package/dist/components/Calendar/CalendarView.js.map +1 -1
  17. package/dist/components/Calendar/calendar.hooks.d.ts.map +1 -1
  18. package/dist/components/Calendar/calendar.hooks.js +1 -0
  19. package/dist/components/Calendar/calendar.hooks.js.map +1 -1
  20. package/dist/components/Card/Card.d.ts +2 -2
  21. package/dist/components/Card/Card.d.ts.map +1 -1
  22. package/dist/components/Card/Card.js +3 -2
  23. package/dist/components/Card/Card.js.map +1 -1
  24. package/dist/components/Checkbox/Checkbox.d.ts.map +1 -1
  25. package/dist/components/Checkbox/Checkbox.js +1 -0
  26. package/dist/components/Checkbox/Checkbox.js.map +1 -1
  27. package/dist/components/CheckboxGroup/CheckboxGroup.d.ts.map +1 -1
  28. package/dist/components/CheckboxGroup/CheckboxGroup.js +1 -0
  29. package/dist/components/CheckboxGroup/CheckboxGroup.js.map +1 -1
  30. package/dist/components/Code/Editor/Editor.d.ts.map +1 -1
  31. package/dist/components/Code/Editor/Editor.js +1 -0
  32. package/dist/components/Code/Editor/Editor.js.map +1 -1
  33. package/dist/components/Code/Workspace/StatusBar.d.ts.map +1 -1
  34. package/dist/components/Code/Workspace/StatusBar.js +1 -0
  35. package/dist/components/Code/Workspace/StatusBar.js.map +1 -1
  36. package/dist/components/Code/Workspace/Toolbar.d.ts.map +1 -1
  37. package/dist/components/Code/Workspace/Toolbar.js +1 -0
  38. package/dist/components/Code/Workspace/Toolbar.js.map +1 -1
  39. package/dist/components/Code/Workspace/Workspace.d.ts.map +1 -1
  40. package/dist/components/Code/Workspace/Workspace.js +1 -0
  41. package/dist/components/Code/Workspace/Workspace.js.map +1 -1
  42. package/dist/components/DatePicker/DatePicker.d.ts.map +1 -1
  43. package/dist/components/DatePicker/DatePicker.js +7 -1
  44. package/dist/components/DatePicker/DatePicker.js.map +1 -1
  45. package/dist/components/DatePicker/DateSegments.d.ts.map +1 -1
  46. package/dist/components/DatePicker/DateSegments.js +1 -0
  47. package/dist/components/DatePicker/DateSegments.js.map +1 -1
  48. package/dist/components/DatePicker/datepicker.hooks.d.ts.map +1 -1
  49. package/dist/components/DatePicker/datepicker.hooks.js +1 -0
  50. package/dist/components/DatePicker/datepicker.hooks.js.map +1 -1
  51. package/dist/components/DateRangePicker/DateRangePicker.d.ts.map +1 -1
  52. package/dist/components/DateRangePicker/DateRangePicker.js +1 -0
  53. package/dist/components/DateRangePicker/DateRangePicker.js.map +1 -1
  54. package/dist/components/DateTimePicker/DateTimePicker.d.ts.map +1 -1
  55. package/dist/components/DateTimePicker/DateTimePicker.js +7 -1
  56. package/dist/components/DateTimePicker/DateTimePicker.js.map +1 -1
  57. package/dist/components/DateTimePicker/DateTimeSegments.d.ts.map +1 -1
  58. package/dist/components/DateTimePicker/DateTimeSegments.js +1 -0
  59. package/dist/components/DateTimePicker/DateTimeSegments.js.map +1 -1
  60. package/dist/components/DateTimeRangePicker/DateTimeRangePicker.d.ts.map +1 -1
  61. package/dist/components/DateTimeRangePicker/DateTimeRangePicker.js +7 -1
  62. package/dist/components/DateTimeRangePicker/DateTimeRangePicker.js.map +1 -1
  63. package/dist/components/FormField/FormField.d.ts +31 -10
  64. package/dist/components/FormField/FormField.d.ts.map +1 -1
  65. package/dist/components/FormField/FormField.js +46 -17
  66. package/dist/components/FormField/FormField.js.map +1 -1
  67. package/dist/components/FormField/index.d.ts +2 -2
  68. package/dist/components/FormField/index.d.ts.map +1 -1
  69. package/dist/components/FormField/index.js +1 -1
  70. package/dist/components/FormField/index.js.map +1 -1
  71. package/dist/components/HoverCard/HoverCard.d.ts.map +1 -1
  72. package/dist/components/HoverCard/HoverCard.js +4 -0
  73. package/dist/components/HoverCard/HoverCard.js.map +1 -1
  74. package/dist/components/HoverCard/components/HoverCardContent.d.ts +16 -2
  75. package/dist/components/HoverCard/components/HoverCardContent.d.ts.map +1 -1
  76. package/dist/components/HoverCard/components/HoverCardContent.js +4 -3
  77. package/dist/components/HoverCard/components/HoverCardContent.js.map +1 -1
  78. package/dist/components/HoverCard/components/HoverCardContext.d.ts +2 -0
  79. package/dist/components/HoverCard/components/HoverCardContext.d.ts.map +1 -1
  80. package/dist/components/HoverCard/components/HoverCardContext.js +1 -0
  81. package/dist/components/HoverCard/components/HoverCardContext.js.map +1 -1
  82. package/dist/components/HoverCard/components/HoverCardTrigger.d.ts.map +1 -1
  83. package/dist/components/HoverCard/components/HoverCardTrigger.js +1 -0
  84. package/dist/components/HoverCard/components/HoverCardTrigger.js.map +1 -1
  85. package/dist/components/Message/Message.d.ts +5 -1
  86. package/dist/components/Message/Message.d.ts.map +1 -1
  87. package/dist/components/Message/Message.js +3 -3
  88. package/dist/components/Message/Message.js.map +1 -1
  89. package/dist/components/Modal/Modal.d.ts +26 -9
  90. package/dist/components/Modal/Modal.d.ts.map +1 -1
  91. package/dist/components/Modal/Modal.js +46 -21
  92. package/dist/components/Modal/Modal.js.map +1 -1
  93. package/dist/components/Modal/components/ModalContext.d.ts.map +1 -1
  94. package/dist/components/Modal/components/ModalContext.js +1 -0
  95. package/dist/components/Modal/components/ModalContext.js.map +1 -1
  96. package/dist/components/Modal/components/ModalHeader.d.ts.map +1 -1
  97. package/dist/components/Modal/components/ModalHeader.js +1 -0
  98. package/dist/components/Modal/components/ModalHeader.js.map +1 -1
  99. package/dist/components/Popover/Popover.d.ts +3 -1
  100. package/dist/components/Popover/Popover.d.ts.map +1 -1
  101. package/dist/components/Popover/Popover.js +8 -0
  102. package/dist/components/Popover/Popover.js.map +1 -1
  103. package/dist/components/Popover/components/PopoverBody.d.ts +16 -2
  104. package/dist/components/Popover/components/PopoverBody.d.ts.map +1 -1
  105. package/dist/components/Popover/components/PopoverBody.js +4 -3
  106. package/dist/components/Popover/components/PopoverBody.js.map +1 -1
  107. package/dist/components/Popover/components/PopoverClose.d.ts.map +1 -1
  108. package/dist/components/Popover/components/PopoverClose.js +1 -0
  109. package/dist/components/Popover/components/PopoverClose.js.map +1 -1
  110. package/dist/components/Popover/components/PopoverContext.d.ts +2 -0
  111. package/dist/components/Popover/components/PopoverContext.d.ts.map +1 -1
  112. package/dist/components/Popover/components/PopoverContext.js +1 -0
  113. package/dist/components/Popover/components/PopoverContext.js.map +1 -1
  114. package/dist/components/Popover/components/PopoverTrigger.d.ts.map +1 -1
  115. package/dist/components/Popover/components/PopoverTrigger.js +1 -0
  116. package/dist/components/Popover/components/PopoverTrigger.js.map +1 -1
  117. package/dist/components/RadioButton/RadioButton.d.ts.map +1 -1
  118. package/dist/components/RadioButton/RadioButton.js +1 -0
  119. package/dist/components/RadioButton/RadioButton.js.map +1 -1
  120. package/dist/components/RadioButtonGroup/RadioButtonGroup.d.ts.map +1 -1
  121. package/dist/components/RadioButtonGroup/RadioButtonGroup.js +1 -0
  122. package/dist/components/RadioButtonGroup/RadioButtonGroup.js.map +1 -1
  123. package/dist/components/SearchInput/SearchInput.d.ts +10 -7
  124. package/dist/components/SearchInput/SearchInput.d.ts.map +1 -1
  125. package/dist/components/SearchInput/SearchInput.js +11 -6
  126. package/dist/components/SearchInput/SearchInput.js.map +1 -1
  127. package/dist/components/Select/Select.d.ts +6 -0
  128. package/dist/components/Select/Select.d.ts.map +1 -1
  129. package/dist/components/Select/Select.js +11 -4
  130. package/dist/components/Select/Select.js.map +1 -1
  131. package/dist/components/Select/SelectDropdown.d.ts +3 -1
  132. package/dist/components/Select/SelectDropdown.d.ts.map +1 -1
  133. package/dist/components/Select/SelectDropdown.js +3 -2
  134. package/dist/components/Select/SelectDropdown.js.map +1 -1
  135. package/dist/components/Select/SelectTrigger.d.ts.map +1 -1
  136. package/dist/components/Select/SelectTrigger.js +5 -1
  137. package/dist/components/Select/SelectTrigger.js.map +1 -1
  138. package/dist/components/Select/useSelectKeyboard.d.ts.map +1 -1
  139. package/dist/components/Select/useSelectKeyboard.js +1 -0
  140. package/dist/components/Select/useSelectKeyboard.js.map +1 -1
  141. package/dist/components/Sheet/Sheet.d.ts +12 -0
  142. package/dist/components/Sheet/Sheet.d.ts.map +1 -1
  143. package/dist/components/Sheet/Sheet.js +26 -10
  144. package/dist/components/Sheet/Sheet.js.map +1 -1
  145. package/dist/components/Sheet/components/SheetContext.d.ts.map +1 -1
  146. package/dist/components/Sheet/components/SheetContext.js +1 -0
  147. package/dist/components/Sheet/components/SheetContext.js.map +1 -1
  148. package/dist/components/Sheet/components/SheetHeader.d.ts.map +1 -1
  149. package/dist/components/Sheet/components/SheetHeader.js +1 -0
  150. package/dist/components/Sheet/components/SheetHeader.js.map +1 -1
  151. package/dist/components/Spinner/Spinner.d.ts +2 -2
  152. package/dist/components/Spinner/Spinner.js +2 -2
  153. package/dist/components/Switch/Switch.d.ts.map +1 -1
  154. package/dist/components/Switch/Switch.js +1 -0
  155. package/dist/components/Switch/Switch.js.map +1 -1
  156. package/dist/components/Tabs/Tabs.d.ts.map +1 -1
  157. package/dist/components/Tabs/Tabs.js +1 -0
  158. package/dist/components/Tabs/Tabs.js.map +1 -1
  159. package/dist/components/Tabs/components/TabsContext.d.ts.map +1 -1
  160. package/dist/components/Tabs/components/TabsContext.js +1 -0
  161. package/dist/components/Tabs/components/TabsContext.js.map +1 -1
  162. package/dist/components/Tabs/components/TabsList.d.ts.map +1 -1
  163. package/dist/components/Tabs/components/TabsList.js +1 -0
  164. package/dist/components/Tabs/components/TabsList.js.map +1 -1
  165. package/dist/components/Tabs/components/TabsPanels.d.ts.map +1 -1
  166. package/dist/components/Tabs/components/TabsPanels.js +1 -0
  167. package/dist/components/Tabs/components/TabsPanels.js.map +1 -1
  168. package/dist/components/Tabs/components/TabsTrigger.d.ts.map +1 -1
  169. package/dist/components/Tabs/components/TabsTrigger.js +2 -1
  170. package/dist/components/Tabs/components/TabsTrigger.js.map +1 -1
  171. package/dist/components/TagChip/TagChip.d.ts.map +1 -1
  172. package/dist/components/TagChip/TagChip.js +1 -0
  173. package/dist/components/TagChip/TagChip.js.map +1 -1
  174. package/dist/components/TagInput/TagInput.d.ts +8 -2
  175. package/dist/components/TagInput/TagInput.d.ts.map +1 -1
  176. package/dist/components/TagInput/TagInput.js +8 -5
  177. package/dist/components/TagInput/TagInput.js.map +1 -1
  178. package/dist/components/Textarea/Textarea.d.ts +21 -5
  179. package/dist/components/Textarea/Textarea.d.ts.map +1 -1
  180. package/dist/components/Textarea/Textarea.js +31 -5
  181. package/dist/components/Textarea/Textarea.js.map +1 -1
  182. package/dist/components/TimePicker/PeriodSelector.d.ts.map +1 -1
  183. package/dist/components/TimePicker/PeriodSelector.js +1 -0
  184. package/dist/components/TimePicker/PeriodSelector.js.map +1 -1
  185. package/dist/components/TimePicker/TimeColumn.d.ts.map +1 -1
  186. package/dist/components/TimePicker/TimeColumn.js +1 -0
  187. package/dist/components/TimePicker/TimeColumn.js.map +1 -1
  188. package/dist/components/TimePicker/TimePicker.d.ts.map +1 -1
  189. package/dist/components/TimePicker/TimePicker.js +7 -1
  190. package/dist/components/TimePicker/TimePicker.js.map +1 -1
  191. package/dist/components/TimePicker/TimeSegments.d.ts.map +1 -1
  192. package/dist/components/TimePicker/TimeSegments.js +1 -0
  193. package/dist/components/TimePicker/TimeSegments.js.map +1 -1
  194. package/dist/components/Toast/Toast.d.ts +16 -2
  195. package/dist/components/Toast/Toast.d.ts.map +1 -1
  196. package/dist/components/Toast/Toast.js +17 -7
  197. package/dist/components/Toast/Toast.js.map +1 -1
  198. package/dist/components/Toggle/Toggle.d.ts +5 -1
  199. package/dist/components/Toggle/Toggle.d.ts.map +1 -1
  200. package/dist/components/Toggle/Toggle.js +4 -3
  201. package/dist/components/Toggle/Toggle.js.map +1 -1
  202. package/dist/components/ToggleGroup/ToggleGroupMultiple.d.ts +3 -0
  203. package/dist/components/ToggleGroup/ToggleGroupMultiple.d.ts.map +1 -1
  204. package/dist/components/ToggleGroup/ToggleGroupMultiple.js +4 -3
  205. package/dist/components/ToggleGroup/ToggleGroupMultiple.js.map +1 -1
  206. package/dist/components/ToggleGroup/ToggleGroupSingle.d.ts +3 -0
  207. package/dist/components/ToggleGroup/ToggleGroupSingle.d.ts.map +1 -1
  208. package/dist/components/ToggleGroup/ToggleGroupSingle.js +4 -3
  209. package/dist/components/ToggleGroup/ToggleGroupSingle.js.map +1 -1
  210. package/dist/components/ToggleGroup/toggleGroup.types.d.ts +13 -0
  211. package/dist/components/ToggleGroup/toggleGroup.types.d.ts.map +1 -1
  212. package/dist/components/Tooltip/Tooltip.d.ts +3 -1
  213. package/dist/components/Tooltip/Tooltip.d.ts.map +1 -1
  214. package/dist/components/Tooltip/Tooltip.js +4 -0
  215. package/dist/components/Tooltip/Tooltip.js.map +1 -1
  216. package/dist/components/Tooltip/components/TooltipBody.d.ts +16 -2
  217. package/dist/components/Tooltip/components/TooltipBody.d.ts.map +1 -1
  218. package/dist/components/Tooltip/components/TooltipBody.js +4 -3
  219. package/dist/components/Tooltip/components/TooltipBody.js.map +1 -1
  220. package/dist/components/Tooltip/components/TooltipContext.d.ts +2 -0
  221. package/dist/components/Tooltip/components/TooltipContext.d.ts.map +1 -1
  222. package/dist/components/Tooltip/components/TooltipContext.js +1 -0
  223. package/dist/components/Tooltip/components/TooltipContext.js.map +1 -1
  224. package/dist/components/Tooltip/components/TooltipTrigger.d.ts.map +1 -1
  225. package/dist/components/Tooltip/components/TooltipTrigger.js +1 -0
  226. package/dist/components/Tooltip/components/TooltipTrigger.js.map +1 -1
  227. package/dist/hooks/useCollapsible.d.ts.map +1 -1
  228. package/dist/hooks/useCollapsible.js +1 -0
  229. package/dist/hooks/useCollapsible.js.map +1 -1
  230. package/dist/hooks/useControllableState.d.ts.map +1 -1
  231. package/dist/hooks/useControllableState.js +1 -0
  232. package/dist/hooks/useControllableState.js.map +1 -1
  233. package/dist/hooks/useDebouncedCallback.d.ts.map +1 -1
  234. package/dist/hooks/useDebouncedCallback.js +1 -0
  235. package/dist/hooks/useDebouncedCallback.js.map +1 -1
  236. package/dist/hooks/useDebouncedState.d.ts.map +1 -1
  237. package/dist/hooks/useDebouncedState.js +1 -0
  238. package/dist/hooks/useDebouncedState.js.map +1 -1
  239. package/dist/hooks/useDeviceFormFactor.d.ts.map +1 -1
  240. package/dist/hooks/useDeviceFormFactor.js +1 -0
  241. package/dist/hooks/useDeviceFormFactor.js.map +1 -1
  242. package/dist/hooks/useLocalStorage.d.ts.map +1 -1
  243. package/dist/hooks/useLocalStorage.js +1 -0
  244. package/dist/hooks/useLocalStorage.js.map +1 -1
  245. package/dist/hooks/useMergedFloatingRef.d.ts.map +1 -1
  246. package/dist/hooks/useMergedFloatingRef.js +1 -0
  247. package/dist/hooks/useMergedFloatingRef.js.map +1 -1
  248. package/dist/hooks/usePresence.d.ts.map +1 -1
  249. package/dist/hooks/usePresence.js +1 -0
  250. package/dist/hooks/usePresence.js.map +1 -1
  251. package/dist/hooks/useStack.d.ts.map +1 -1
  252. package/dist/hooks/useStack.js +34 -10
  253. package/dist/hooks/useStack.js.map +1 -1
  254. package/dist/hooks/useTheme.d.ts.map +1 -1
  255. package/dist/hooks/useTheme.js +1 -0
  256. package/dist/hooks/useTheme.js.map +1 -1
  257. package/dist/internal/PickerField.d.ts.map +1 -1
  258. package/dist/internal/PickerField.js +1 -0
  259. package/dist/internal/PickerField.js.map +1 -1
  260. package/dist/internal/SegmentedField.d.ts.map +1 -1
  261. package/dist/internal/SegmentedField.js +1 -0
  262. package/dist/internal/SegmentedField.js.map +1 -1
  263. package/dist/internal/brandScope.d.ts +25 -0
  264. package/dist/internal/brandScope.d.ts.map +1 -0
  265. package/dist/internal/brandScope.js +37 -0
  266. package/dist/internal/brandScope.js.map +1 -0
  267. package/dist/internal/escapeLayers.d.ts +12 -0
  268. package/dist/internal/escapeLayers.d.ts.map +1 -0
  269. package/dist/internal/escapeLayers.js +48 -0
  270. package/dist/internal/escapeLayers.js.map +1 -0
  271. package/dist/lib/ErrorBoundary.d.ts.map +1 -1
  272. package/dist/lib/ErrorBoundary.js +1 -0
  273. package/dist/lib/ErrorBoundary.js.map +1 -1
  274. package/dist/lib/contexts/ComponentsContext/ComponentsContext.d.ts.map +1 -1
  275. package/dist/lib/contexts/ComponentsContext/ComponentsContext.js +1 -0
  276. package/dist/lib/contexts/ComponentsContext/ComponentsContext.js.map +1 -1
  277. package/dist/lib/contexts/OverlayContext/OverlayContext.d.ts +9 -4
  278. package/dist/lib/contexts/OverlayContext/OverlayContext.d.ts.map +1 -1
  279. package/dist/lib/contexts/OverlayContext/OverlayContext.js +25 -12
  280. package/dist/lib/contexts/OverlayContext/OverlayContext.js.map +1 -1
  281. package/dist/lib/contexts/OverlayContext/ToastStack.d.ts +20 -0
  282. package/dist/lib/contexts/OverlayContext/ToastStack.d.ts.map +1 -0
  283. package/dist/lib/contexts/OverlayContext/ToastStack.js +45 -0
  284. package/dist/lib/contexts/OverlayContext/ToastStack.js.map +1 -0
  285. package/dist/lib/contexts/OverlayContext/hooks/useModal.d.ts.map +1 -1
  286. package/dist/lib/contexts/OverlayContext/hooks/useModal.js +1 -0
  287. package/dist/lib/contexts/OverlayContext/hooks/useModal.js.map +1 -1
  288. package/dist/lib/contexts/OverlayContext/hooks/useToast.d.ts +27 -10
  289. package/dist/lib/contexts/OverlayContext/hooks/useToast.d.ts.map +1 -1
  290. package/dist/lib/contexts/OverlayContext/hooks/useToast.js +31 -49
  291. package/dist/lib/contexts/OverlayContext/hooks/useToast.js.map +1 -1
  292. package/dist/lib/contexts/OverlayContext/index.d.ts +2 -0
  293. package/dist/lib/contexts/OverlayContext/index.d.ts.map +1 -1
  294. package/dist/lib/contexts/OverlayContext/index.js +2 -0
  295. package/dist/lib/contexts/OverlayContext/index.js.map +1 -1
  296. package/dist/lib/contexts/OverlayContext/toastStore.d.ts +68 -0
  297. package/dist/lib/contexts/OverlayContext/toastStore.d.ts.map +1 -0
  298. package/dist/lib/contexts/OverlayContext/toastStore.js +69 -0
  299. package/dist/lib/contexts/OverlayContext/toastStore.js.map +1 -0
  300. package/dist/lib/withErrorBoundary.d.ts.map +1 -1
  301. package/dist/lib/withErrorBoundary.js +1 -0
  302. package/dist/lib/withErrorBoundary.js.map +1 -1
  303. package/dist/lib/withSuspenseAndErrorBoundary.d.ts.map +1 -1
  304. package/dist/lib/withSuspenseAndErrorBoundary.js +1 -0
  305. package/dist/lib/withSuspenseAndErrorBoundary.js.map +1 -1
  306. package/dist/{theme/schema.d.ts → theming/vocabulary.d.ts} +4 -4
  307. package/dist/theming/vocabulary.d.ts.map +1 -0
  308. package/dist/{theme/schema.js → theming/vocabulary.js} +4 -4
  309. package/dist/theming/vocabulary.js.map +1 -0
  310. package/package.json +18 -6
  311. package/src/components/Badge/Badge.tsx +12 -6
  312. package/src/components/Button/Button.tsx +25 -5
  313. package/src/{theme/recipes/button.css → components/Button/button.recipe.css} +2 -2
  314. package/src/components/Calendar/Calendar.tsx +2 -0
  315. package/src/components/Calendar/CalendarView.tsx +2 -0
  316. package/src/components/Calendar/calendar.hooks.ts +2 -0
  317. package/src/components/Card/Card.tsx +4 -2
  318. package/src/components/Checkbox/Checkbox.tsx +2 -0
  319. package/src/components/CheckboxGroup/CheckboxGroup.tsx +2 -0
  320. package/src/components/Code/Editor/Editor.tsx +2 -0
  321. package/src/components/Code/Workspace/StatusBar.tsx +2 -0
  322. package/src/components/Code/Workspace/Toolbar.tsx +2 -0
  323. package/src/components/Code/Workspace/Workspace.tsx +2 -0
  324. package/src/components/DatePicker/DatePicker.tsx +10 -0
  325. package/src/components/DatePicker/DateSegments.tsx +2 -0
  326. package/src/components/DatePicker/datepicker.hooks.ts +2 -0
  327. package/src/components/DateRangePicker/DateRangePicker.tsx +2 -0
  328. package/src/components/DateTimePicker/DateTimePicker.tsx +10 -1
  329. package/src/components/DateTimePicker/DateTimeSegments.tsx +2 -0
  330. package/src/components/DateTimeRangePicker/DateTimeRangePicker.tsx +10 -1
  331. package/src/components/FormField/FormField.tsx +94 -42
  332. package/src/components/FormField/index.ts +2 -2
  333. package/src/components/HoverCard/HoverCard.tsx +5 -0
  334. package/src/components/HoverCard/components/HoverCardContent.tsx +13 -3
  335. package/src/components/HoverCard/components/HoverCardContext.tsx +4 -0
  336. package/src/components/HoverCard/components/HoverCardTrigger.tsx +2 -0
  337. package/src/components/Message/Message.tsx +27 -23
  338. package/src/components/Modal/Modal.tsx +88 -51
  339. package/src/components/Modal/components/ModalContext.tsx +2 -0
  340. package/src/components/Modal/components/ModalHeader.tsx +2 -0
  341. package/src/components/Popover/Popover.tsx +10 -0
  342. package/src/components/Popover/components/PopoverBody.tsx +13 -3
  343. package/src/components/Popover/components/PopoverClose.tsx +2 -0
  344. package/src/components/Popover/components/PopoverContext.tsx +4 -0
  345. package/src/components/Popover/components/PopoverTrigger.tsx +2 -0
  346. package/src/components/RadioButton/RadioButton.tsx +2 -0
  347. package/src/components/RadioButtonGroup/RadioButtonGroup.tsx +2 -0
  348. package/src/components/SearchInput/SearchInput.tsx +15 -8
  349. package/src/components/Select/Select.tsx +27 -2
  350. package/src/components/Select/SelectDropdown.tsx +6 -0
  351. package/src/components/Select/SelectTrigger.tsx +5 -0
  352. package/src/components/Select/useSelectKeyboard.ts +2 -0
  353. package/src/components/Sheet/Sheet.tsx +59 -34
  354. package/src/components/Sheet/components/SheetContext.tsx +2 -0
  355. package/src/components/Sheet/components/SheetHeader.tsx +2 -0
  356. package/src/components/Spinner/Spinner.tsx +2 -2
  357. package/src/components/Switch/Switch.tsx +2 -0
  358. package/src/components/Tabs/Tabs.tsx +2 -0
  359. package/src/components/Tabs/components/TabsContext.tsx +2 -0
  360. package/src/components/Tabs/components/TabsList.tsx +2 -0
  361. package/src/components/Tabs/components/TabsPanels.tsx +2 -0
  362. package/src/components/Tabs/components/TabsTrigger.tsx +3 -1
  363. package/src/{theme/recipes/tabs.css → components/Tabs/tabs.recipe.css} +1 -1
  364. package/src/components/TagChip/TagChip.tsx +2 -0
  365. package/src/components/TagInput/TagInput.tsx +15 -6
  366. package/src/components/Textarea/Textarea.tsx +42 -6
  367. package/src/{theme/recipes/textarea.css → components/Textarea/textarea.recipe.css} +1 -1
  368. package/src/components/TimePicker/PeriodSelector.tsx +2 -0
  369. package/src/components/TimePicker/TimeColumn.tsx +2 -0
  370. package/src/components/TimePicker/TimePicker.tsx +10 -0
  371. package/src/components/TimePicker/TimeSegments.tsx +2 -0
  372. package/src/components/Toast/Toast.tsx +26 -6
  373. package/src/components/Toggle/Toggle.tsx +20 -3
  374. package/src/components/ToggleGroup/ToggleGroupMultiple.tsx +8 -1
  375. package/src/components/ToggleGroup/ToggleGroupSingle.tsx +8 -0
  376. package/src/components/ToggleGroup/toggleGroup.types.ts +13 -0
  377. package/src/components/Tooltip/Tooltip.tsx +5 -0
  378. package/src/components/Tooltip/components/TooltipBody.tsx +13 -3
  379. package/src/components/Tooltip/components/TooltipContext.tsx +4 -0
  380. package/src/components/Tooltip/components/TooltipTrigger.tsx +2 -0
  381. package/src/hooks/useCollapsible.ts +2 -0
  382. package/src/hooks/useControllableState.ts +2 -0
  383. package/src/hooks/useDebouncedCallback.ts +2 -0
  384. package/src/hooks/useDebouncedState.ts +2 -0
  385. package/src/hooks/useDeviceFormFactor.ts +2 -0
  386. package/src/hooks/useLocalStorage.ts +2 -0
  387. package/src/hooks/useMergedFloatingRef.ts +2 -0
  388. package/src/hooks/usePresence.ts +2 -0
  389. package/src/hooks/useStack.ts +35 -12
  390. package/src/hooks/useTheme.ts +2 -0
  391. package/src/internal/PickerField.tsx +2 -0
  392. package/src/internal/SegmentedField.tsx +2 -0
  393. package/src/internal/brandScope.ts +39 -0
  394. package/src/internal/escapeLayers.ts +53 -0
  395. package/src/lib/ErrorBoundary.tsx +2 -0
  396. package/src/lib/contexts/ComponentsContext/ComponentsContext.tsx +2 -0
  397. package/src/lib/contexts/OverlayContext/OverlayContext.tsx +42 -26
  398. package/src/lib/contexts/OverlayContext/ToastStack.tsx +71 -0
  399. package/src/lib/contexts/OverlayContext/hooks/useModal.tsx +2 -0
  400. package/src/lib/contexts/OverlayContext/hooks/useToast.ts +30 -73
  401. package/src/lib/contexts/OverlayContext/index.ts +2 -0
  402. package/src/lib/contexts/OverlayContext/toastStore.ts +138 -0
  403. package/src/lib/withErrorBoundary.tsx +2 -0
  404. package/src/lib/withSuspenseAndErrorBoundary.tsx +2 -0
  405. package/src/theme/linen/index.css +11 -0
  406. package/src/theme/linen/palette.css +87 -0
  407. package/src/theme/linen/theme.css +371 -0
  408. package/src/theming/tailwind.css +356 -0
  409. package/src/{theme/schema.ts → theming/vocabulary.ts} +3 -3
  410. package/dist/theme/index.d.ts +0 -9
  411. package/dist/theme/index.d.ts.map +0 -1
  412. package/dist/theme/index.js +0 -8
  413. package/dist/theme/index.js.map +0 -1
  414. package/dist/theme/schema.d.ts.map +0 -1
  415. package/dist/theme/schema.js.map +0 -1
  416. package/src/theme/index.ts +0 -39
  417. package/src/theme/recipes/index.css +0 -28
  418. package/src/theme/tokens.css +0 -824
  419. /package/src/{theme/recipes/badge.css → components/Badge/badge.recipe.css} +0 -0
  420. /package/src/{theme/recipes/card.css → components/Card/card.recipe.css} +0 -0
  421. /package/src/{theme/recipes/checkbox.css → components/Checkbox/checkbox.recipe.css} +0 -0
  422. /package/src/{theme/recipes/field.css → components/Field/field.recipe.css} +0 -0
  423. /package/src/{theme/recipes/input.css → components/Input/input.recipe.css} +0 -0
  424. /package/src/{theme/recipes/message.css → components/Message/message.recipe.css} +0 -0
  425. /package/src/{theme/recipes/modal.css → components/Modal/modal.recipe.css} +0 -0
  426. /package/src/{theme/recipes/radio-button.css → components/RadioButton/radio-button.recipe.css} +0 -0
  427. /package/src/{theme/recipes/search-input.css → components/SearchInput/search-input.recipe.css} +0 -0
  428. /package/src/{theme/recipes/select.css → components/Select/select.recipe.css} +0 -0
  429. /package/src/{theme/recipes/sheet.css → components/Sheet/sheet.recipe.css} +0 -0
  430. /package/src/{theme/recipes/spinner.css → components/Spinner/spinner.recipe.css} +0 -0
  431. /package/src/{theme/recipes/switch.css → components/Switch/switch.recipe.css} +0 -0
  432. /package/src/{theme/recipes/tag-chip.css → components/TagChip/tag-chip.recipe.css} +0 -0
  433. /package/src/{theme/recipes/toggle.css → components/Toggle/toggle.recipe.css} +0 -0
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { Label } from "@sixthshift/design-system/label";
2
4
  import { Message } from "@sixthshift/design-system/message";
3
5
  import { Muted } from "@sixthshift/design-system/muted";
@@ -10,61 +12,111 @@ export type FormFieldFeedback = {
10
12
  };
11
13
 
12
14
  export type FormFieldProps = React.HTMLAttributes<HTMLDivElement> & {
13
- label: string;
14
- description?: string;
15
+ label: React.ReactNode;
16
+ description?: React.ReactNode;
15
17
  feedback?: FormFieldFeedback;
16
18
  required?: boolean;
17
19
  };
18
20
 
21
+ /** What FormField knows about the control it wraps — see `useFormField`. */
22
+ export type FormFieldContextValue = {
23
+ /** The id the control should carry, and the label's `htmlFor` target. */
24
+ id: string;
25
+ /** Space-separated ids of the description/feedback texts, for `aria-describedby`. `undefined` when there are none. */
26
+ describedBy: string | undefined;
27
+ /** True when `feedback.intent` is `"danger"` — the control should set `aria-invalid`. */
28
+ invalid: boolean;
29
+ /** Mirrors the `required` prop, for `aria-required`/`required` on the control. */
30
+ required: boolean;
31
+ };
32
+
33
+ const FormFieldContext = React.createContext<FormFieldContextValue | null>(null);
34
+
35
+ /**
36
+ * The wiring `FormField` provides to whatever control it wraps: `id`,
37
+ * `describedBy`, `invalid`, `required`. Direct element children get these
38
+ * cloned on automatically; a control nested deeper — inside a wrapper div, a
39
+ * render prop, a custom composite — can call this instead and apply them
40
+ * itself. Returns `null` outside a `FormField`.
41
+ */
42
+ export const useFormField = () => React.useContext(FormFieldContext);
43
+
19
44
  /**
20
45
  * Wraps a form control with a label, optional description, and optional
21
46
  * validation feedback — the composition point for `Input`, `Textarea`,
22
47
  * `Select` and similar controls inside a form.
23
48
  *
24
- * Generates an `id` (or uses the one passed in) and clones it, plus
25
- * `aria-describedby` (pointing at the description and/or feedback text) and
26
- * `aria-invalid` (when `feedback.intent` is `"danger"`), onto its child —
27
- * so the wrapped control does not need those wired by hand. It expects a
28
- * single child control: the same id and aria attributes get cloned onto
29
- * every child if more than one is passed.
49
+ * Generates an `id` (or uses the one passed in) and wires it, plus
50
+ * `aria-describedby` (pointing at the description and/or feedback text),
51
+ * `aria-invalid` (when `feedback.intent` is `"danger"`) and `aria-required`
52
+ * (when `required`), onto the first element child. A child's own attributes
53
+ * win: an explicit `id` is kept (and becomes the label's `htmlFor` target),
54
+ * an existing `aria-describedby` is merged with FormField's rather than
55
+ * replaced. Controls that aren't the first element child can self-wire via
56
+ * `useFormField`.
30
57
  *
31
58
  * Distinct from `Field`, which displays a read-only label/value pair rather
32
59
  * than wrapping an interactive control.
33
60
  */
34
- const FormField = React.forwardRef<HTMLDivElement, FormFieldProps>(({ className, label, description, feedback, required, children, id, ...props }, ref) => {
35
- const generatedId = React.useId();
36
- const fieldId = id ?? generatedId;
37
- const descriptionId = description ? `${fieldId}-description` : undefined;
38
- const feedbackId = feedback ? `${fieldId}-feedback` : undefined;
39
-
40
- return (
41
- <div ref={ref} className={cn("flex flex-col gap-1.5", className)} {...props}>
42
- <Label htmlFor={fieldId}>
43
- {label}
44
- {required && <span className="ml-0.5 text-fg-danger">*</span>}
45
- </Label>
46
-
47
- {description && <Muted id={descriptionId}>{description}</Muted>}
48
-
49
- {React.Children.map(children, (child) => {
50
- if (React.isValidElement(child)) {
51
- return React.cloneElement(child, {
52
- id: fieldId,
53
- "aria-describedby": cn(descriptionId, feedbackId) || undefined,
54
- "aria-invalid": feedback?.intent === "danger" ? true : undefined,
55
- } as React.HTMLAttributes<HTMLElement>);
56
- }
57
- return child;
58
- })}
59
-
60
- {feedback && (
61
- <Message id={feedbackId} intent={feedback.intent} size="sm">
62
- {feedback.message}
63
- </Message>
64
- )}
65
- </div>
66
- );
67
- });
61
+ const FormField = React.forwardRef<HTMLDivElement, FormFieldProps>(
62
+ ({ className, label, description, feedback, required = false, children, id, ...props }, ref) => {
63
+ const generatedId = React.useId();
64
+ const fieldId = id ?? generatedId;
65
+
66
+ // The label must point at the id the control actually carries: when the
67
+ // (first element) child brings its own id, that id wins over ours.
68
+ const childArray = React.Children.toArray(children);
69
+ const firstElementIndex = childArray.findIndex((child) => React.isValidElement(child));
70
+ const firstElement = firstElementIndex === -1 ? null : (childArray[firstElementIndex] as React.ReactElement<React.HTMLAttributes<HTMLElement>>);
71
+ const controlId = firstElement?.props.id ?? fieldId;
72
+
73
+ const descriptionId = description ? `${fieldId}-description` : undefined;
74
+ const feedbackId = feedback ? `${fieldId}-feedback` : undefined;
75
+ const invalid = feedback?.intent === "danger";
76
+
77
+ const contextValue = React.useMemo<FormFieldContextValue>(
78
+ () => ({
79
+ id: controlId,
80
+ describedBy: cn(descriptionId, feedbackId) || undefined,
81
+ invalid,
82
+ required,
83
+ }),
84
+ [controlId, descriptionId, feedbackId, invalid, required]
85
+ );
86
+
87
+ const wiredChildren = childArray.map((child, index) => {
88
+ if (index !== firstElementIndex || !React.isValidElement(child)) return child;
89
+ const childProps = child.props as React.HTMLAttributes<HTMLElement>;
90
+ return React.cloneElement(child, {
91
+ id: controlId,
92
+ // The child's own describedby (a tooltip, an external hint) is kept and
93
+ // FormField's ids are appended, rather than overwritten.
94
+ "aria-describedby": cn(childProps["aria-describedby"], descriptionId, feedbackId) || undefined,
95
+ "aria-invalid": childProps["aria-invalid"] ?? (invalid ? true : undefined),
96
+ "aria-required": childProps["aria-required"] ?? (required ? true : undefined),
97
+ } as React.HTMLAttributes<HTMLElement>);
98
+ });
99
+
100
+ return (
101
+ <div ref={ref} className={cn("flex flex-col gap-1.5", className)} {...props}>
102
+ <Label htmlFor={controlId}>
103
+ {label}
104
+ {required && <span className="ml-0.5 text-fg-danger">*</span>}
105
+ </Label>
106
+
107
+ {description && <Muted id={descriptionId}>{description}</Muted>}
108
+
109
+ <FormFieldContext.Provider value={contextValue}>{wiredChildren}</FormFieldContext.Provider>
110
+
111
+ {feedback && (
112
+ <Message id={feedbackId} intent={feedback.intent} size="sm">
113
+ {feedback.message}
114
+ </Message>
115
+ )}
116
+ </div>
117
+ );
118
+ }
119
+ );
68
120
  FormField.displayName = "FormField";
69
121
 
70
122
  export { FormField };
@@ -1,2 +1,2 @@
1
- export type { FormFieldFeedback, FormFieldProps } from "./FormField";
2
- export { FormField } from "./FormField";
1
+ export type { FormFieldContextValue, FormFieldFeedback, FormFieldProps } from "./FormField";
2
+ export { FormField, useFormField } from "./FormField";
@@ -1,6 +1,9 @@
1
+ "use client";
2
+
1
3
  import { autoUpdate, flip, offset, type Placement, safePolygon, shift, useDismiss, useFloating, useFocus, useHover, useInteractions } from "@floating-ui/react";
2
4
  import { useControllableState } from "@sixthshift/design-system/hooks";
3
5
  import type * as React from "react";
6
+ import { closestBrand } from "../../internal/brandScope";
4
7
  import { HoverCardContent } from "./components/HoverCardContent";
5
8
  import { HoverCardContext } from "./components/HoverCardContext";
6
9
  import { HoverCardTrigger } from "./components/HoverCardTrigger";
@@ -65,6 +68,8 @@ const HoverCardRoot = ({
65
68
  setFloating: refs.setFloating,
66
69
  },
67
70
  floatingStyles,
71
+ // Read at render: `domReference` is state, so this updates once the trigger mounts.
72
+ inheritedBrand: closestBrand(context.elements.domReference),
68
73
  getReferenceProps,
69
74
  getFloatingProps,
70
75
  };
@@ -1,12 +1,21 @@
1
+ "use client";
2
+
1
3
  import { FloatingPortal } from "@floating-ui/react";
2
4
  import { cn } from "@sixthshift/design-system/utils";
3
5
  import * as React from "react";
4
6
  import { useHoverCardContext } from "./HoverCardContext";
5
7
 
6
- export type HoverCardContentProps = React.HTMLAttributes<HTMLDivElement>;
8
+ export type HoverCardContentProps = React.HTMLAttributes<HTMLDivElement> & {
9
+ /**
10
+ * Name of a brand the theme defines, rendered as `data-brand`. Omitted, the
11
+ * body copies the brand scope of its trigger, which the portal would
12
+ * otherwise leave behind.
13
+ */
14
+ brand?: string | undefined;
15
+ };
7
16
 
8
- export const HoverCardContent = React.forwardRef<HTMLDivElement, HoverCardContentProps>(({ className, children, ...props }, forwardedRef) => {
9
- const { open, refs, floatingStyles, getFloatingProps } = useHoverCardContext();
17
+ export const HoverCardContent = React.forwardRef<HTMLDivElement, HoverCardContentProps>(({ className, brand, children, ...props }, forwardedRef) => {
18
+ const { open, refs, floatingStyles, inheritedBrand, getFloatingProps } = useHoverCardContext();
10
19
 
11
20
  if (!open) return null;
12
21
 
@@ -19,6 +28,7 @@ export const HoverCardContent = React.forwardRef<HTMLDivElement, HoverCardConten
19
28
  else if (forwardedRef) forwardedRef.current = node;
20
29
  }}
21
30
  style={floatingStyles}
31
+ data-brand={brand ?? inheritedBrand}
22
32
  className={cn("z-popover w-80 rounded-lg border border-border-normal bg-bg-normal p-4 shadow-lg", className)}
23
33
  {...getFloatingProps(props)}
24
34
  >
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { createContext, useContext } from "react";
2
4
 
3
5
  export type HoverCardContextValue = {
@@ -7,6 +9,8 @@ export type HoverCardContextValue = {
7
9
  setFloating: (node: HTMLElement | null) => void;
8
10
  };
9
11
  floatingStyles: React.CSSProperties;
12
+ /** The trigger's brand scope, re-stamped on the portalled body. */
13
+ inheritedBrand: string | undefined;
10
14
  getReferenceProps: (props?: React.HTMLProps<HTMLElement>) => Record<string, unknown>;
11
15
  getFloatingProps: (props?: React.HTMLProps<HTMLElement>) => Record<string, unknown>;
12
16
  };
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import * as React from "react";
2
4
  import { Slot } from "../../../internal/Slot";
3
5
  import type { WritableRefObject } from "../../../internal/types";
@@ -10,7 +10,7 @@ import { MessageTitle } from "./components/MessageTitle";
10
10
  * Geometry only, plus the three `--message-*` component tokens.
11
11
  *
12
12
  * Every colour reads a `--message-*` component token whose value is decided by
13
- * src/theme/recipes/message.css. That file is the mapping from `intent` to a
13
+ * src/components/Message/message.recipe.css. That file is the mapping from `intent` to a
14
14
  * semantic token — the layer that used to be the `intent` entry in `variants`
15
15
  * here, compiled into class-name literals and unreachable from outside.
16
16
  * `size` stays here: it is pure geometry (padding, gap, font-size), not
@@ -48,6 +48,8 @@ export type MessageIntent = Loose<MessageIntentName>;
48
48
  export type MessageProps = React.HTMLAttributes<HTMLDivElement> &
49
49
  VariantProps<typeof messageVariants> & {
50
50
  intent?: MessageIntent | undefined;
51
+ /** Name of a brand the theme defines, rendered as `data-brand`. Omitted, the message follows the nearest `data-brand` ancestor. */
52
+ brand?: string | undefined;
51
53
  /** Simple API: Optional title displayed above children */
52
54
  title?: React.ReactNode;
53
55
  /** Simple API: Optional icon displayed on the left */
@@ -99,27 +101,29 @@ function intentRole(intent: MessageIntent): "status" | "alert" {
99
101
  * simple API can't express. Message detects which one is in use by
100
102
  * inspecting its children, so the two aren't meant to be mixed.
101
103
  */
102
- export const Message = React.forwardRef<HTMLDivElement, MessageProps>(({ className, intent = "neutral", size, title, icon, children, ...props }, ref) => {
103
- const hasCompoundChildren = React.Children.toArray(children).some(
104
- (child) =>
105
- React.isValidElement(child) &&
106
- (child.type === MessageIcon || child.type === MessageBody || child.type === MessageTitle || child.type === MessageDescription)
107
- );
104
+ export const Message = React.forwardRef<HTMLDivElement, MessageProps>(
105
+ ({ className, intent = "neutral", brand, size, title, icon, children, ...props }, ref) => {
106
+ const hasCompoundChildren = React.Children.toArray(children).some(
107
+ (child) =>
108
+ React.isValidElement(child) &&
109
+ (child.type === MessageIcon || child.type === MessageBody || child.type === MessageTitle || child.type === MessageDescription)
110
+ );
108
111
 
109
- return (
110
- <div ref={ref} role={intentRole(intent)} data-intent={intent} className={cn(messageVariants({ size, className }))} {...props}>
111
- {hasCompoundChildren ? (
112
- children
113
- ) : (
114
- <>
115
- {icon && <MessageIcon>{icon}</MessageIcon>}
116
- <MessageBody>
117
- {title && <MessageTitle>{title}</MessageTitle>}
118
- <MessageDescription>{children}</MessageDescription>
119
- </MessageBody>
120
- </>
121
- )}
122
- </div>
123
- );
124
- });
112
+ return (
113
+ <div ref={ref} role={intentRole(intent)} data-intent={intent} data-brand={brand} className={cn(messageVariants({ size, className }))} {...props}>
114
+ {hasCompoundChildren ? (
115
+ children
116
+ ) : (
117
+ <>
118
+ {icon && <MessageIcon>{icon}</MessageIcon>}
119
+ <MessageBody>
120
+ {title && <MessageTitle>{title}</MessageTitle>}
121
+ <MessageDescription>{children}</MessageDescription>
122
+ </MessageBody>
123
+ </>
124
+ )}
125
+ </div>
126
+ );
127
+ }
128
+ );
125
129
  Message.displayName = "Message";
@@ -1,7 +1,11 @@
1
- import { FloatingFocusManager, FloatingOverlay, useDismiss, useFloating, useInteractions, useRole } from "@floating-ui/react";
1
+ "use client";
2
+
3
+ import { FloatingFocusManager, FloatingOverlay, FloatingPortal, useDismiss, useFloating, useInteractions, useRole } from "@floating-ui/react";
2
4
  import { useMergedFloatingRef, usePresence } from "@sixthshift/design-system/hooks";
3
5
  import { cn } from "@sixthshift/design-system/utils";
4
6
  import { forwardRef, type HTMLAttributes, type ReactNode, useCallback, useContext, useEffect, useId, useMemo, useState } from "react";
7
+ import { useOpenerBrand } from "../../internal/brandScope";
8
+ import { hasOpenEscapeLayer, useEscapeDialog } from "../../internal/escapeLayers";
5
9
  import { ModalBody, ModalContext, ModalFooter, ModalHeader } from "./components";
6
10
 
7
11
  // =============================================================================
@@ -21,15 +25,23 @@ export type ModalProps = Pick<HTMLAttributes<HTMLDivElement>, "className" | "sty
21
25
  /** Children - use Modal.Header, Modal.Body, Modal.Footer */
22
26
  children: ReactNode;
23
27
  /**
24
- * When true (default), clicking outside closes the modal.
28
+ * When true (default), clicking outside — and, for a directly-mounted
29
+ * modal, pressing Escape — closes the modal.
25
30
  * When false, the modal can only be closed programmatically.
26
- * Note: Escape key is handled by OverlayContext for proper stack ordering.
31
+ * Note: for modals opened via `useModal()`, Escape is handled by
32
+ * OverlayContext for proper stack ordering.
27
33
  */
28
34
  dismissable?: boolean;
29
35
  /** When true, shows a close (X) button in the modal header */
30
36
  closable?: boolean;
31
37
  /** Vertical alignment on desktop. "center" (default) or "top" (anchored near top, content grows down) */
32
38
  align?: "center" | "top";
39
+ /**
40
+ * Name of a brand the theme defines, rendered as `data-brand` on the dialog.
41
+ * Omitted, the modal takes the brand scope of the control that opened it
42
+ * (whatever held focus), since the portal leaves the trigger's scope behind.
43
+ */
44
+ brand?: string | undefined;
33
45
  };
34
46
 
35
47
  // =============================================================================
@@ -58,6 +70,7 @@ const ModalRoot = forwardRef<HTMLDivElement, ModalProps>(
58
70
  dismissable = true,
59
71
  closable,
60
72
  align = "center",
73
+ brand,
61
74
  "aria-label": ariaLabel,
62
75
  "aria-labelledby": ariaLabelledBy,
63
76
  "aria-describedby": ariaDescribedBy,
@@ -66,6 +79,9 @@ const ModalRoot = forwardRef<HTMLDivElement, ModalProps>(
66
79
  ) => {
67
80
  // Prefer explicit prop over context (context is set by useModal's overlay system)
68
81
  const contextValue = useContext(ModalContext);
82
+ // A context means useModal's stack owns this modal — and with it, Escape
83
+ // ordering. Without one the modal is mounted directly and handles its own.
84
+ const isStackManaged = contextValue != null;
69
85
  // Context provides a parameterless onClose for programmatic modals; adapt to the boolean shape.
70
86
  const handleDismiss = useCallback(() => {
71
87
  if (onOpenChange) onOpenChange(false);
@@ -73,6 +89,8 @@ const ModalRoot = forwardRef<HTMLDivElement, ModalProps>(
73
89
  }, [onOpenChange, contextValue]);
74
90
 
75
91
  const { ref: presenceRef, state, isMounted, show, hide } = usePresence();
92
+ // Mount-driven, so the modal is open for as long as it exists.
93
+ const openerBrand = useOpenerBrand(true);
76
94
 
77
95
  useEffect(() => {
78
96
  show();
@@ -84,8 +102,12 @@ const ModalRoot = forwardRef<HTMLDivElement, ModalProps>(
84
102
 
85
103
  const { refs, context } = useFloating({
86
104
  open: true,
87
- onOpenChange: (nextOpen) => {
88
- if (!nextOpen) handleClose();
105
+ onOpenChange: (nextOpen, _event, reason) => {
106
+ if (nextOpen) return;
107
+ // A transient overlay (Select dropdown, Popover, picker) open above
108
+ // this modal consumes the Escape itself — don't also close the modal.
109
+ if (reason === "escape-key" && hasOpenEscapeLayer()) return;
110
+ handleClose();
89
111
  },
90
112
  });
91
113
 
@@ -93,8 +115,10 @@ const ModalRoot = forwardRef<HTMLDivElement, ModalProps>(
93
115
 
94
116
  const dismiss = useDismiss(context, {
95
117
  enabled: dismissable,
96
- // OverlayContext handles escape key for proper stack ordering
97
- escapeKey: false,
118
+ // Stack-managed modals leave Escape to OverlayContext, which closes
119
+ // topmost-first. A directly-mounted modal has no stack above it and
120
+ // handles its own Escape here.
121
+ escapeKey: !isStackManaged,
98
122
  outsidePress: true,
99
123
  outsidePressEvent: "mousedown",
100
124
  });
@@ -119,52 +143,64 @@ const ModalRoot = forwardRef<HTMLDivElement, ModalProps>(
119
143
 
120
144
  const modalContextValue = useMemo(() => ({ onClose: handleClose, closable, titleId, registerTitle }), [closable, handleClose, titleId, registerTitle]);
121
145
 
146
+ // While mounted (exit animation included), a non-modal Sheet underneath
147
+ // must leave Escape to this dialog.
148
+ useEscapeDialog(isMounted);
149
+
122
150
  if (!isMounted) return null;
123
151
 
124
152
  const isEntering = state === "entering";
125
153
  const isExiting = state === "exiting";
126
154
 
127
155
  return (
128
- <FloatingFocusManager context={context} modal>
129
- <FloatingOverlay
130
- lockScroll
131
- className={cn("modal-overlay fixed inset-0 z-modal bg-(--modal-overlay-bg)", isEntering && "animate-fade-in", isExiting && "animate-fade-out")}
132
- >
133
- <div
134
- ref={mergedRef}
135
- role="dialog"
136
- aria-modal="true"
137
- aria-label={ariaLabel}
138
- aria-labelledby={labelledBy}
139
- aria-describedby={ariaDescribedBy}
140
- tabIndex={-1}
141
- data-state={state}
142
- className={cn(
143
- "modal fixed flex flex-col overflow-hidden outline-hidden",
144
- "border-(color:--modal-border) rounded-xl border bg-(--modal-bg)",
145
- // Size
146
- sizeClasses[size],
147
- // Cap height so ModalBody scrolls instead of overflowing the viewport
148
- "max-h-[95%]",
149
- // Mobile: slide up from bottom, full width
150
- "max-sm:inset-x-0 max-sm:bottom-0 max-sm:rounded-b-none",
151
- isEntering && "max-sm:animate-slide-up-in",
152
- isExiting && "max-sm:animate-slide-up-out",
153
- // Desktop: horizontal center + vertical alignment
154
- "sm:left-1/2 sm:-translate-x-1/2",
155
- align === "center" && "sm:top-1/2 sm:-translate-y-1/2",
156
- align === "top" && "sm:top-[15vh]",
157
- isEntering && "sm:animate-fade-in",
158
- isExiting && "sm:animate-fade-out",
159
- className
160
- )}
161
- style={style}
162
- {...getFloatingProps({})}
156
+ // Self-portalled: `position: fixed` resolves against the nearest
157
+ // transformed/filtered ancestor, so a modal rendered in place inside a
158
+ // `transform` container would be positioned (and clipped) by it. Nested
159
+ // FloatingPortals attach to the parent portal's node, so a stack-managed
160
+ // modal stays inside the `OverlayProvider`'s custom `modal` root.
161
+ <FloatingPortal>
162
+ <FloatingFocusManager context={context} modal>
163
+ <FloatingOverlay
164
+ lockScroll
165
+ className={cn("modal-overlay fixed inset-0 z-modal bg-(--modal-overlay-bg)", isEntering && "animate-fade-in", isExiting && "animate-fade-out")}
163
166
  >
164
- <ModalContext.Provider value={modalContextValue}>{children}</ModalContext.Provider>
165
- </div>
166
- </FloatingOverlay>
167
- </FloatingFocusManager>
167
+ <div
168
+ ref={mergedRef}
169
+ role="dialog"
170
+ aria-modal="true"
171
+ aria-label={ariaLabel}
172
+ aria-labelledby={labelledBy}
173
+ aria-describedby={ariaDescribedBy}
174
+ tabIndex={-1}
175
+ data-state={state}
176
+ data-brand={brand ?? openerBrand}
177
+ className={cn(
178
+ "modal fixed flex flex-col overflow-hidden outline-hidden",
179
+ "border-(color:--modal-border) rounded-xl border bg-(--modal-bg)",
180
+ // Size
181
+ sizeClasses[size],
182
+ // Cap height so ModalBody scrolls instead of overflowing the viewport
183
+ "max-h-[95%]",
184
+ // Mobile: slide up from bottom, full width
185
+ "max-sm:inset-x-0 max-sm:bottom-0 max-sm:rounded-b-none",
186
+ isEntering && "max-sm:animate-slide-up-in",
187
+ isExiting && "max-sm:animate-slide-up-out",
188
+ // Desktop: horizontal center + vertical alignment
189
+ "sm:left-1/2 sm:-translate-x-1/2",
190
+ align === "center" && "sm:top-1/2 sm:-translate-y-1/2",
191
+ align === "top" && "sm:top-[15vh]",
192
+ isEntering && "sm:animate-fade-in",
193
+ isExiting && "sm:animate-fade-out",
194
+ className
195
+ )}
196
+ style={style}
197
+ {...getFloatingProps({})}
198
+ >
199
+ <ModalContext.Provider value={modalContextValue}>{children}</ModalContext.Provider>
200
+ </div>
201
+ </FloatingOverlay>
202
+ </FloatingFocusManager>
203
+ </FloatingPortal>
168
204
  );
169
205
  }
170
206
  );
@@ -192,11 +228,12 @@ ModalRoot.displayName = "Modal";
192
228
  * Modal is mount-driven — a parent (or `useModal`'s stack) decides whether it
193
229
  * renders at all, so there's no `open` prop. `onOpenChange` still exists,
194
230
  * firing only with `false` on dismissal, so every overlay in this library
195
- * shares one open/close shape. Escape is *not* wired inside Modal itself:
196
- * `OverlayProvider` owns it globally so a stack of modals closes
197
- * topmost-first. That means Escape only closes a Modal opened through
198
- * `useModal()` — one a parent mounts directly from its own boolean state
199
- * won't respond to Escape unless the app wires that separately.
231
+ * shares one open/close shape. Escape closes the modal: a directly-mounted
232
+ * Modal handles it itself (via `useDismiss`, so `dismissable={false}` turns
233
+ * it off along with outside-press), while one opened through `useModal()`
234
+ * defers to `OverlayProvider`, which closes a stack of modals topmost-first.
235
+ * Either way, an Escape pressed while a transient overlay (Select dropdown,
236
+ * Popover, picker) is open above the modal closes only that overlay.
200
237
  *
201
238
  * Focus moves into the modal on mount and is trapped there
202
239
  * (`FloatingFocusManager modal`) until it unmounts, then returns to whatever
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { createContext, type PropsWithChildren, useContext, useMemo } from "react";
2
4
 
3
5
  /**
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { cn } from "@sixthshift/design-system/utils";
2
4
  import { X } from "lucide-react";
3
5
  import * as React from "react";
@@ -1,7 +1,11 @@
1
+ "use client";
2
+
1
3
  import { autoUpdate, flip, offset, type Placement, shift, useClick, useDismiss, useFloating, useInteractions } from "@floating-ui/react";
2
4
  import { useControllableState } from "@sixthshift/design-system/hooks";
3
5
  import type * as React from "react";
4
6
  import { useId } from "react";
7
+ import { closestBrand } from "../../internal/brandScope";
8
+ import { useEscapeLayer } from "../../internal/escapeLayers";
5
9
  import { PopoverBody } from "./components/PopoverBody";
6
10
  import { PopoverClose } from "./components/PopoverClose";
7
11
  import { PopoverContext } from "./components/PopoverContext";
@@ -42,6 +46,10 @@ const PopoverRoot = ({ open: controlledOpen, onOpenChange, defaultOpen = false,
42
46
  const dismiss = useDismiss(context);
43
47
  const { getReferenceProps, getFloatingProps } = useInteractions([click, dismiss]);
44
48
 
49
+ // While open, an Escape press dismisses this popover and must not also
50
+ // close a Modal/Sheet underneath it.
51
+ useEscapeLayer(open);
52
+
45
53
  const id = useId();
46
54
  const contentId = `popover-content-${id}`;
47
55
  // `Popover.Body` is a `role="dialog"`, and a dialog with no accessible name is
@@ -59,6 +67,8 @@ const PopoverRoot = ({ open: controlledOpen, onOpenChange, defaultOpen = false,
59
67
  setFloating: refs.setFloating,
60
68
  },
61
69
  floatingStyles,
70
+ // Read at render: `domReference` is state, so this updates once the trigger mounts.
71
+ inheritedBrand: closestBrand(context.elements.domReference),
62
72
  getReferenceProps,
63
73
  getFloatingProps,
64
74
  contentId,
@@ -1,12 +1,21 @@
1
+ "use client";
2
+
1
3
  import { FloatingPortal } from "@floating-ui/react";
2
4
  import { cn } from "@sixthshift/design-system/utils";
3
5
  import * as React from "react";
4
6
  import { usePopoverContext } from "./PopoverContext";
5
7
 
6
- export type PopoverBodyProps = React.HTMLAttributes<HTMLDivElement>;
8
+ export type PopoverBodyProps = React.HTMLAttributes<HTMLDivElement> & {
9
+ /**
10
+ * Name of a brand the theme defines, rendered as `data-brand`. Omitted, the
11
+ * body copies the brand scope of its trigger, which the portal would
12
+ * otherwise leave behind.
13
+ */
14
+ brand?: string | undefined;
15
+ };
7
16
 
8
- export const PopoverBody = React.forwardRef<HTMLDivElement, PopoverBodyProps>(({ className, children, ...props }, forwardedRef) => {
9
- const { open, refs, floatingStyles, getFloatingProps, contentId, triggerId } = usePopoverContext();
17
+ export const PopoverBody = React.forwardRef<HTMLDivElement, PopoverBodyProps>(({ className, brand, children, ...props }, forwardedRef) => {
18
+ const { open, refs, floatingStyles, inheritedBrand, getFloatingProps, contentId, triggerId } = usePopoverContext();
10
19
 
11
20
  // `aria-labelledby` outranks `aria-label` in the name computation, so the
12
21
  // trigger only names the dialog when the caller hasn't named it themselves.
@@ -26,6 +35,7 @@ export const PopoverBody = React.forwardRef<HTMLDivElement, PopoverBodyProps>(({
26
35
  role="dialog"
27
36
  aria-labelledby={hasOwnLabel ? undefined : triggerId}
28
37
  style={floatingStyles}
38
+ data-brand={brand ?? inheritedBrand}
29
39
  className={cn("z-popover rounded-lg border border-border-normal bg-bg-normal p-4 shadow-lg", className)}
30
40
  {...getFloatingProps(props)}
31
41
  >
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import * as React from "react";
2
4
  import { Slot } from "../../../internal/Slot";
3
5
  import { usePopoverContext } from "./PopoverContext";
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { createContext, useContext } from "react";
2
4
 
3
5
  export type PopoverContextValue = {
@@ -8,6 +10,8 @@ export type PopoverContextValue = {
8
10
  setFloating: (node: HTMLElement | null) => void;
9
11
  };
10
12
  floatingStyles: React.CSSProperties;
13
+ /** The trigger's brand scope, re-stamped on the portalled body. */
14
+ inheritedBrand: string | undefined;
11
15
  getReferenceProps: (props?: React.HTMLProps<HTMLElement>) => Record<string, unknown>;
12
16
  getFloatingProps: (props?: React.HTMLProps<HTMLElement>) => Record<string, unknown>;
13
17
  contentId: string;
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import * as React from "react";
2
4
  import { Slot } from "../../../internal/Slot";
3
5
  import { usePopoverContext } from "./PopoverContext";
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  import { Label } from "@sixthshift/design-system/label";
2
4
  import { cn } from "@sixthshift/design-system/utils";
3
5
  import * as React from "react";