@sovereignfs/ui 0.54.0 → 0.83.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 (277) hide show
  1. package/dist/Accordion.module.css +1 -1
  2. package/dist/Alert.module.css +1 -1
  3. package/dist/AppsLauncher.module.css +96 -0
  4. package/dist/Avatar.module.css +1 -1
  5. package/dist/Badge.module.css +30 -7
  6. package/dist/BalanceChip.module.css +1 -1
  7. package/dist/Button.module.css +18 -2
  8. package/dist/Card.module.css +1 -1
  9. package/dist/CardTile.module.css +181 -0
  10. package/dist/CodeTextarea.module.css +1 -1
  11. package/dist/Collapsible.module.css +1 -1
  12. package/dist/ColorPicker.module.css +132 -0
  13. package/dist/Combobox.module.css +2 -2
  14. package/dist/Command.module.css +1 -1
  15. package/dist/ConfirmDialog.module.css +2 -2
  16. package/dist/ConsentPrompt.module.css +21 -0
  17. package/dist/DatePicker.module.css +1 -1
  18. package/dist/Dialog.module.css +130 -21
  19. package/dist/Drawer.module.css +36 -7
  20. package/dist/Header.module.css +62 -0
  21. package/dist/HeaderFooterLayout.module.css +21 -0
  22. package/dist/IconPicker.module.css +1 -1
  23. package/dist/Input.module.css +1 -1
  24. package/dist/Kbd.module.css +1 -1
  25. package/dist/Markdown.module.css +24 -0
  26. package/dist/Menu.module.css +1 -0
  27. package/dist/Message.module.css +1 -1
  28. package/dist/MessageScroller.module.css +12 -1
  29. package/dist/MobileAppsDrawer.module.css +10 -9
  30. package/dist/MobileFooter.module.css +1 -1
  31. package/dist/MobileHeader.module.css +14 -1
  32. package/dist/NavList.module.css +130 -0
  33. package/dist/NavTabs.module.css +1 -1
  34. package/dist/NotificationsPanel.module.css +258 -0
  35. package/dist/OverlayHeader.module.css +1 -1
  36. package/dist/PageContainer.module.css +62 -0
  37. package/dist/PageHeader.module.css +54 -0
  38. package/dist/PageLayout.module.css +51 -0
  39. package/dist/Popover.module.css +1 -1
  40. package/dist/QuantityStepper.module.css +1 -1
  41. package/dist/Resizable.module.css +3 -3
  42. package/dist/RootLayout.module.css +12 -0
  43. package/dist/SegmentedControl.module.css +1 -1
  44. package/dist/Select.module.css +1 -1
  45. package/dist/Sheet.module.css +4 -1
  46. package/dist/SplitPane.module.css +4 -4
  47. package/dist/StatusBadge.module.css +1 -1
  48. package/dist/StepStrip.module.css +53 -0
  49. package/dist/SuggestionInput.module.css +2 -2
  50. package/dist/SwipableMobileCarousel.module.css +12 -3
  51. package/dist/SwipableMobileCarouselDots.module.css +15 -0
  52. package/dist/SwipeStack.module.css +101 -0
  53. package/dist/SystemBanner.module.css +10 -0
  54. package/dist/Table.module.css +2 -2
  55. package/dist/Tabs.module.css +1 -1
  56. package/dist/TagInput.module.css +2 -2
  57. package/dist/Textarea.module.css +9 -1
  58. package/dist/ThreeColumnLayout.module.css +35 -0
  59. package/dist/Toast.module.css +62 -3
  60. package/dist/Tooltip.module.css +1 -1
  61. package/dist/UserMenu.module.css +90 -0
  62. package/dist/index.d.ts +1047 -45
  63. package/dist/index.js +3908 -1456
  64. package/dist/tokens/primitives.css +11 -0
  65. package/dist/tokens/semantic.css +32 -0
  66. package/dist/tokens/theme-presets.css +33 -0
  67. package/package.json +25 -9
  68. package/src/__tests__/package-json.test.ts +8 -0
  69. package/src/components/Accordion/Accordion.module.css +1 -1
  70. package/src/components/Alert/Alert.module.css +1 -1
  71. package/src/components/AppsLauncher/AppsLauncher.module.css +96 -0
  72. package/src/components/AppsLauncher/AppsLauncher.stories.tsx +58 -0
  73. package/src/components/AppsLauncher/AppsLauncher.tsx +117 -0
  74. package/src/components/AppsLauncher/__tests__/AppsLauncher.test.tsx +52 -0
  75. package/src/components/Avatar/Avatar.module.css +1 -1
  76. package/src/components/Badge/Badge.module.css +30 -7
  77. package/src/components/Badge/Badge.stories.tsx +16 -4
  78. package/src/components/Badge/Badge.tsx +7 -4
  79. package/src/components/Badge/__tests__/Badge.test.tsx +4 -2
  80. package/src/components/BalanceChip/BalanceChip.module.css +1 -1
  81. package/src/components/Button/Button.module.css +18 -2
  82. package/src/components/Card/Card.module.css +1 -1
  83. package/src/components/CardTile/CardTile.module.css +181 -0
  84. package/src/components/CardTile/CardTile.stories.tsx +86 -0
  85. package/src/components/CardTile/CardTile.tsx +128 -0
  86. package/src/components/CardTile/__tests__/CardTile.test.tsx +56 -0
  87. package/src/components/CodeTextarea/CodeTextarea.module.css +1 -1
  88. package/src/components/Collapsible/Collapsible.module.css +1 -1
  89. package/src/components/ColorPicker/ColorPicker.module.css +132 -0
  90. package/src/components/ColorPicker/ColorPicker.stories.tsx +80 -0
  91. package/src/components/ColorPicker/ColorPicker.tsx +157 -0
  92. package/src/components/ColorPicker/__tests__/ColorPicker.test.tsx +117 -0
  93. package/src/components/Combobox/Combobox.module.css +2 -2
  94. package/src/components/Command/Command.module.css +1 -1
  95. package/src/components/ConfirmDialog/ConfirmDialog.module.css +2 -2
  96. package/src/components/ConfirmDialog/ConfirmDialog.tsx +28 -2
  97. package/src/components/ConfirmDialog/__tests__/ConfirmDialog.test.tsx +71 -3
  98. package/src/components/ConsentPrompt/ConsentPrompt.module.css +21 -0
  99. package/src/components/ConsentPrompt/ConsentPrompt.stories.tsx +31 -0
  100. package/src/components/ConsentPrompt/ConsentPrompt.tsx +66 -0
  101. package/src/components/ContextMenu/ContextMenu.tsx +1 -1
  102. package/src/components/DatePicker/DatePicker.module.css +1 -1
  103. package/src/components/Dialog/Dialog.module.css +130 -21
  104. package/src/components/Dialog/Dialog.stories.tsx +179 -2
  105. package/src/components/Dialog/Dialog.tsx +129 -35
  106. package/src/components/Dialog/__tests__/Dialog.test.tsx +190 -9
  107. package/src/components/DragHandleRow/DragHandleRow.tsx +2 -21
  108. package/src/components/Drawer/Drawer.module.css +36 -7
  109. package/src/components/Drawer/Drawer.tsx +9 -10
  110. package/src/components/Drawer/__tests__/Drawer.test.tsx +33 -6
  111. package/src/components/GripIcon/GripIcon.tsx +27 -0
  112. package/src/components/Header/Header.module.css +62 -0
  113. package/src/components/Header/Header.stories.tsx +452 -0
  114. package/src/components/Header/Header.tsx +102 -0
  115. package/src/components/Header/__tests__/Header.test.tsx +104 -0
  116. package/src/components/HeaderFooterLayout/HeaderFooterLayout.module.css +21 -0
  117. package/src/components/HeaderFooterLayout/HeaderFooterLayout.stories.tsx +111 -0
  118. package/src/components/HeaderFooterLayout/HeaderFooterLayout.tsx +54 -0
  119. package/src/components/HeaderFooterLayout/__tests__/HeaderFooterLayout.test.tsx +58 -0
  120. package/src/components/Icon/icons/arrow-left-right.tsx +23 -0
  121. package/src/components/Icon/icons/bold.tsx +20 -0
  122. package/src/components/Icon/icons/calendar.tsx +4 -4
  123. package/src/components/Icon/icons/carrot.tsx +3 -3
  124. package/src/components/Icon/icons/circle-chevron-left.tsx +21 -0
  125. package/src/components/Icon/icons/code.tsx +21 -0
  126. package/src/components/Icon/icons/copy.tsx +21 -0
  127. package/src/components/Icon/icons/credit-card.tsx +21 -0
  128. package/src/components/Icon/icons/database.tsx +22 -0
  129. package/src/components/Icon/icons/download.tsx +22 -0
  130. package/src/components/Icon/icons/file-text.tsx +24 -0
  131. package/src/components/Icon/icons/folder-closed.tsx +21 -0
  132. package/src/components/Icon/icons/folder-open.tsx +20 -0
  133. package/src/components/Icon/icons/folder.tsx +20 -0
  134. package/src/components/Icon/icons/folders.tsx +21 -0
  135. package/src/components/Icon/icons/hat-glasses.tsx +24 -0
  136. package/src/components/Icon/icons/history.tsx +22 -0
  137. package/src/components/Icon/icons/inbox.tsx +21 -0
  138. package/src/components/Icon/icons/index.ts +161 -1
  139. package/src/components/Icon/icons/italic.tsx +22 -0
  140. package/src/components/Icon/icons/layers.tsx +22 -0
  141. package/src/components/Icon/icons/layout-dashboard.tsx +23 -0
  142. package/src/components/Icon/icons/layout-grid.tsx +23 -0
  143. package/src/components/Icon/icons/link.tsx +21 -0
  144. package/src/components/Icon/icons/list-ordered.tsx +25 -0
  145. package/src/components/Icon/icons/list.tsx +25 -0
  146. package/src/components/Icon/icons/luggage.tsx +24 -0
  147. package/src/components/Icon/icons/map-pin.tsx +21 -0
  148. package/src/components/Icon/icons/minus.tsx +20 -0
  149. package/src/components/Icon/icons/paint-bucket.tsx +23 -0
  150. package/src/components/Icon/icons/panel-left.tsx +21 -0
  151. package/src/components/Icon/icons/pin.tsx +21 -0
  152. package/src/components/Icon/icons/rectangle-ellipsis.tsx +23 -0
  153. package/src/components/Icon/icons/refresh-cw.tsx +23 -0
  154. package/src/components/Icon/icons/rotate-cw.tsx +21 -0
  155. package/src/components/Icon/icons/route.tsx +22 -0
  156. package/src/components/Icon/icons/send.tsx +21 -0
  157. package/src/components/Icon/icons/share-2.tsx +24 -0
  158. package/src/components/Icon/icons/sheet.tsx +24 -0
  159. package/src/components/Icon/icons/square-kanban.tsx +23 -0
  160. package/src/components/Icon/icons/table.tsx +23 -0
  161. package/src/components/Icon/icons/user-round-plus.tsx +23 -0
  162. package/src/components/Icon/icons/users.tsx +23 -0
  163. package/src/components/IconPicker/IconPicker.module.css +1 -1
  164. package/src/components/Input/Input.module.css +1 -1
  165. package/src/components/Input/Input.tsx +8 -2
  166. package/src/components/Kbd/Kbd.module.css +1 -1
  167. package/src/components/Markdown/Markdown.module.css +24 -0
  168. package/src/components/Markdown/Markdown.stories.tsx +10 -0
  169. package/src/components/Markdown/Markdown.tsx +166 -29
  170. package/src/components/Markdown/__tests__/Markdown.test.tsx +144 -0
  171. package/src/components/Menu/Menu.module.css +1 -0
  172. package/src/components/Menu/Menu.tsx +72 -22
  173. package/src/components/Menu/__tests__/Menu.test.tsx +30 -0
  174. package/src/components/Menubar/Menubar.tsx +1 -1
  175. package/src/components/Message/Message.module.css +1 -1
  176. package/src/components/MessageScroller/MessageScroller.module.css +12 -1
  177. package/src/components/MessageScroller/MessageScroller.tsx +12 -0
  178. package/src/components/MobileAppsDrawer/MobileAppsDrawer.module.css +10 -9
  179. package/src/components/MobileFooter/MobileFooter.module.css +1 -1
  180. package/src/components/MobileFooter/MobileFooter.tsx +15 -2
  181. package/src/components/MobileFooter/__tests__/MobileFooter.test.tsx +58 -0
  182. package/src/components/MobileHeader/MobileHeader.module.css +14 -1
  183. package/src/components/MobileHeader/MobileHeader.tsx +15 -2
  184. package/src/components/MobileHeader/__tests__/MobileHeader.test.tsx +56 -1
  185. package/src/components/NavList/NavList.module.css +130 -0
  186. package/src/components/NavList/NavList.tsx +136 -0
  187. package/src/components/NavList/__tests__/NavList.test.tsx +109 -0
  188. package/src/components/NavTabs/NavTabs.module.css +1 -1
  189. package/src/components/NotificationsPanel/NotificationsPanel.module.css +258 -0
  190. package/src/components/NotificationsPanel/NotificationsPanel.stories.tsx +61 -0
  191. package/src/components/NotificationsPanel/NotificationsPanel.tsx +187 -0
  192. package/src/components/NotificationsPanel/__tests__/NotificationsPanel.test.tsx +78 -0
  193. package/src/components/OverlayHeader/OverlayHeader.module.css +1 -1
  194. package/src/components/OverlayHeader/OverlayHeader.tsx +19 -4
  195. package/src/components/OverlayHeader/__tests__/OverlayHeader.test.tsx +18 -0
  196. package/src/components/PageContainer/PageContainer.module.css +62 -0
  197. package/src/components/PageContainer/PageContainer.tsx +37 -4
  198. package/src/components/PageContainer/__tests__/PageContainer.test.tsx +41 -8
  199. package/src/components/PageHeader/PageHeader.module.css +54 -0
  200. package/src/components/PageHeader/PageHeader.tsx +146 -5
  201. package/src/components/PageHeader/__tests__/PageHeader.test.tsx +103 -0
  202. package/src/components/PageLayout/PageLayout.module.css +51 -0
  203. package/src/components/PageLayout/PageLayout.stories.tsx +73 -0
  204. package/src/components/PageLayout/PageLayout.tsx +51 -0
  205. package/src/components/PageLayout/__tests__/PageLayout.test.tsx +34 -0
  206. package/src/components/Popover/Popover.module.css +1 -1
  207. package/src/components/Popover/Popover.tsx +86 -4
  208. package/src/components/Popover/__tests__/Popover.test.tsx +165 -0
  209. package/src/components/QuantityStepper/QuantityStepper.module.css +1 -1
  210. package/src/components/Resizable/Resizable.module.css +3 -3
  211. package/src/components/RootLayout/RootLayout.module.css +12 -0
  212. package/src/components/RootLayout/RootLayout.stories.tsx +125 -0
  213. package/src/components/RootLayout/RootLayout.tsx +185 -0
  214. package/src/components/RootLayout/__tests__/RootLayout.test.tsx +161 -0
  215. package/src/components/SegmentedControl/SegmentedControl.module.css +1 -1
  216. package/src/components/Select/Select.module.css +1 -1
  217. package/src/components/Sheet/Sheet.module.css +4 -1
  218. package/src/components/Sheet/Sheet.tsx +5 -5
  219. package/src/components/SplitPane/SplitPane.module.css +4 -4
  220. package/src/components/StatusBadge/StatusBadge.module.css +1 -1
  221. package/src/components/StepStrip/StepStrip.module.css +53 -0
  222. package/src/components/StepStrip/StepStrip.tsx +74 -0
  223. package/src/components/SuggestionInput/SuggestionInput.module.css +2 -2
  224. package/src/components/SwipableMobileCarousel/SwipableMobileCarousel.module.css +12 -3
  225. package/src/components/SwipableMobileCarousel/SwipableMobileCarousel.tsx +25 -9
  226. package/src/components/SwipableMobileCarousel/__tests__/SwipableMobileCarousel.test.tsx +81 -0
  227. package/src/components/SwipableMobileCarouselDots/SwipableMobileCarouselDots.module.css +15 -0
  228. package/src/components/SwipableMobileCarouselDots/SwipableMobileCarouselDots.tsx +12 -1
  229. package/src/components/SwipeStack/SwipeStack.module.css +101 -0
  230. package/src/components/SwipeStack/SwipeStack.tsx +199 -0
  231. package/src/components/SwipeStack/SwipeStackCard.tsx +22 -0
  232. package/src/components/SwipeStack/__tests__/SwipeStack.test.tsx +169 -0
  233. package/src/components/SystemBanner/SystemBanner.module.css +10 -0
  234. package/src/components/Table/Table.module.css +2 -2
  235. package/src/components/Tabs/Tabs.module.css +1 -1
  236. package/src/components/TagInput/TagInput.module.css +2 -2
  237. package/src/components/Textarea/Textarea.module.css +9 -1
  238. package/src/components/Textarea/Textarea.tsx +28 -5
  239. package/src/components/Textarea/__tests__/Textarea.test.tsx +23 -1
  240. package/src/components/ThreeColumnLayout/ThreeColumnLayout.module.css +35 -0
  241. package/src/components/ThreeColumnLayout/ThreeColumnLayout.stories.tsx +152 -0
  242. package/src/components/ThreeColumnLayout/ThreeColumnLayout.tsx +87 -0
  243. package/src/components/ThreeColumnLayout/__tests__/ThreeColumnLayout.test.tsx +78 -0
  244. package/src/components/Toast/Toast.module.css +62 -3
  245. package/src/components/Toast/Toast.stories.tsx +1 -1
  246. package/src/components/Tooltip/Tooltip.module.css +1 -1
  247. package/src/components/UserMenu/UserMenu.module.css +90 -0
  248. package/src/components/UserMenu/UserMenu.stories.tsx +49 -0
  249. package/src/components/UserMenu/UserMenu.tsx +132 -0
  250. package/src/components/UserMenu/__tests__/UserMenu.test.tsx +77 -0
  251. package/src/hooks/__tests__/usePublishShellChromeHeight.test.tsx +118 -0
  252. package/src/hooks/__tests__/useReorderSensors.test.ts +26 -0
  253. package/src/hooks/__tests__/useSnapCarousel.test.tsx +82 -0
  254. package/src/hooks/__tests__/useSwipeStack.test.tsx +155 -0
  255. package/src/hooks/index.ts +9 -0
  256. package/src/hooks/usePublishShellChromeHeight.ts +96 -0
  257. package/src/hooks/useReorderSensors.ts +88 -0
  258. package/src/hooks/useSnapCarousel.ts +211 -6
  259. package/src/hooks/useSwipeStack.ts +235 -0
  260. package/src/index.ts +61 -1
  261. package/src/overlay-shell.ts +65 -0
  262. package/src/stories/DesignSystemOverview.stories.tsx +617 -11
  263. package/src/stories/GripIcon.stories.tsx +29 -0
  264. package/src/stories/LayoutSystem.stories.tsx +639 -0
  265. package/src/stories/MenuEntries.stories.tsx +86 -0
  266. package/src/stories/NavList.stories.tsx +147 -0
  267. package/src/stories/PageContainer.stories.tsx +52 -7
  268. package/src/stories/PageHeader.stories.tsx +54 -1
  269. package/src/stories/StepStrip.stories.tsx +76 -0
  270. package/src/stories/SwipableMobileCarousel.stories.tsx +58 -0
  271. package/src/stories/SwipeStack.stories.tsx +200 -0
  272. package/src/stories/Textarea.stories.tsx +12 -0
  273. package/src/stories/TokenGallery.stories.tsx +50 -0
  274. package/src/tokens/primitives.css +11 -0
  275. package/src/tokens/semantic.css +32 -0
  276. package/src/tokens/theme-presets.css +33 -0
  277. package/src/tokens/theme-presets.ts +72 -0
@@ -0,0 +1,200 @@
1
+ import { useState } from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react-vite';
3
+ import { SwipeStack } from '../components/SwipeStack/SwipeStack';
4
+ import { SwipeStackCard } from '../components/SwipeStack/SwipeStackCard';
5
+
6
+ const AGENDA_TASKS = [
7
+ { id: 'water-plants', title: 'Water the plants', detail: 'Living room + balcony' },
8
+ { id: 'standup', title: 'Team standup', detail: '9:30am · 15 min' },
9
+ { id: 'groceries', title: 'Buy groceries', detail: 'Milk, eggs, bread' },
10
+ { id: 'review-pr', title: "Review teammate's PR", detail: 'sovereign/sovereign #482' },
11
+ { id: 'call-dentist', title: 'Call the dentist', detail: 'Reschedule cleaning' },
12
+ ];
13
+
14
+ function taskCardStyle(): React.CSSProperties {
15
+ return {
16
+ height: '100%',
17
+ boxSizing: 'border-box',
18
+ display: 'flex',
19
+ flexDirection: 'column',
20
+ justifyContent: 'center',
21
+ padding: 'var(--sv-space-6)',
22
+ fontFamily: 'var(--sv-font-family)',
23
+ };
24
+ }
25
+
26
+ function TaskCardDemo({ title, detail }: { title: string; detail: string }) {
27
+ return (
28
+ <div style={taskCardStyle()}>
29
+ <div
30
+ style={{
31
+ fontSize: 'var(--sv-font-size-lg)',
32
+ fontWeight: 'var(--sv-font-weight-semibold)',
33
+ color: 'var(--sv-color-text-primary)',
34
+ marginBottom: 'var(--sv-space-2)',
35
+ }}
36
+ >
37
+ {title}
38
+ </div>
39
+ <div style={{ fontSize: 'var(--sv-font-size-sm)', color: 'var(--sv-color-text-muted)' }}>
40
+ {detail}
41
+ </div>
42
+ </div>
43
+ );
44
+ }
45
+
46
+ function StackFrame({ children }: { children: React.ReactNode }) {
47
+ return (
48
+ <div
49
+ style={{
50
+ height: 420,
51
+ maxWidth: 375,
52
+ margin: '0 auto',
53
+ padding: 'var(--sv-space-4)',
54
+ border: '1px solid var(--sv-color-border)',
55
+ borderRadius: 'var(--sv-radius-md)',
56
+ boxSizing: 'border-box',
57
+ }}
58
+ >
59
+ {children}
60
+ </div>
61
+ );
62
+ }
63
+
64
+ function AgendaDemo() {
65
+ const [log, setLog] = useState<string[]>([]);
66
+ return (
67
+ <StackFrame>
68
+ <SwipeStack
69
+ aria-label="Today's agenda"
70
+ directions={{
71
+ up: { label: 'Done', icon: 'check' },
72
+ down: { label: 'Defer', icon: 'history' },
73
+ right: { label: 'Skip', icon: 'chevron-right' },
74
+ }}
75
+ onSwipe={(direction, cardId) =>
76
+ setLog((l) => [`${cardId} → ${direction}`, ...l].slice(0, 5))
77
+ }
78
+ >
79
+ {AGENDA_TASKS.map((task) => (
80
+ <SwipeStackCard key={task.id} cardId={task.id}>
81
+ <TaskCardDemo title={task.title} detail={task.detail} />
82
+ </SwipeStackCard>
83
+ ))}
84
+ </SwipeStack>
85
+ <div
86
+ style={{
87
+ marginTop: 'var(--sv-space-3)',
88
+ fontFamily: 'var(--sv-font-family)',
89
+ fontSize: 'var(--sv-font-size-xs)',
90
+ color: 'var(--sv-color-text-muted)',
91
+ }}
92
+ >
93
+ {log.map((entry, i) => (
94
+ <div key={i}>{entry}</div>
95
+ ))}
96
+ </div>
97
+ </StackFrame>
98
+ );
99
+ }
100
+
101
+ function TwoDirectionsDemo() {
102
+ return (
103
+ <StackFrame>
104
+ <SwipeStack
105
+ aria-label="Photo picker"
106
+ directions={{
107
+ left: { label: 'Skip' },
108
+ right: { label: 'Keep', icon: 'check' },
109
+ }}
110
+ onSwipe={() => {}}
111
+ >
112
+ {AGENDA_TASKS.map((task) => (
113
+ <SwipeStackCard key={task.id} cardId={task.id}>
114
+ <TaskCardDemo title={task.title} detail={task.detail} />
115
+ </SwipeStackCard>
116
+ ))}
117
+ </SwipeStack>
118
+ </StackFrame>
119
+ );
120
+ }
121
+
122
+ function EmptyDemo() {
123
+ return (
124
+ <StackFrame>
125
+ <SwipeStack
126
+ aria-label="Today's agenda"
127
+ directions={{ up: { label: 'Done', icon: 'check' } }}
128
+ onSwipe={() => {}}
129
+ >
130
+ {[]}
131
+ </SwipeStack>
132
+ </StackFrame>
133
+ );
134
+ }
135
+
136
+ const meta = {
137
+ title: 'Components/SwipeStack',
138
+ component: SwipeStack,
139
+ parameters: {
140
+ layout: 'padded',
141
+ docs: {
142
+ description: {
143
+ component:
144
+ 'A compound component for triaging one card at a time by dragging it left/right/up/down, plus an always-visible non-gesture fallback (a button per configured direction) wired to the identical commit path and exit animation as a real drag. Every direction is independently optional and has no baked-in meaning — the caller supplies label/icon per direction and reacts to onSwipe; once triggered, a direction always removes the card for good, there is no reversible "go back".',
145
+ },
146
+ },
147
+ },
148
+ // Every story below supplies its own render(), stateless or with its own
149
+ // useState — these args only satisfy the required-prop typing.
150
+ args: {
151
+ 'aria-label': "Today's agenda",
152
+ directions: {},
153
+ onSwipe: () => {},
154
+ children: null,
155
+ },
156
+ } satisfies Meta<typeof SwipeStack>;
157
+
158
+ export default meta;
159
+ type Story = StoryObj<typeof meta>;
160
+
161
+ export const Default: Story = {
162
+ render: () => <AgendaDemo />,
163
+ parameters: {
164
+ docs: {
165
+ description: {
166
+ story:
167
+ 'up/down/right configured, left intentionally omitted — SwipeStack does not require all 4 directions. The log below the stack shows onSwipe firing with (direction, cardId) for both a drag and a fallback-button click.',
168
+ },
169
+ },
170
+ },
171
+ };
172
+
173
+ export const TwoDirections: Story = {
174
+ render: () => <TwoDirectionsDemo />,
175
+ parameters: {
176
+ docs: {
177
+ description: {
178
+ story:
179
+ 'Only left/right configured — up/down are a wall, not just an unreachable outcome, so the card never visually drifts vertically, and touch-action stays pan-y so a page can still scroll vertically through the stack.',
180
+ },
181
+ },
182
+ },
183
+ };
184
+
185
+ export const Empty: Story = {
186
+ render: () => <EmptyDemo />,
187
+ parameters: {
188
+ docs: {
189
+ description: {
190
+ story:
191
+ 'No cards left — SwipeStack renders its empty region and disables the fallback button rather than showing any built-in empty-state copy; that vocabulary belongs to the caller, same as SwipableMobileCarousel has no opinion on where slide data lives.',
192
+ },
193
+ },
194
+ },
195
+ };
196
+
197
+ export const MobileViewport: Story = {
198
+ render: () => <AgendaDemo />,
199
+ parameters: { viewport: { defaultViewport: 'mobile1' } },
200
+ };
@@ -49,3 +49,15 @@ export const CustomRows: Story = {
49
49
  export const Disabled: Story = {
50
50
  render: (_args) => <Labeled label="Read-only field" defaultValue="Cannot be changed" disabled />,
51
51
  };
52
+
53
+ export const AutoGrow: Story = {
54
+ render: (_args) => (
55
+ <Labeled
56
+ label="Title (grows with content)"
57
+ autoGrow
58
+ rows={1}
59
+ defaultValue="A short title"
60
+ placeholder="Add a title…"
61
+ />
62
+ ),
63
+ };
@@ -1,5 +1,7 @@
1
1
  import { useEffect, useRef, useState } from 'react';
2
2
  import type { Meta, StoryObj } from '@storybook/react-vite';
3
+ import { Button } from '../components/Button/Button';
4
+ import { Card } from '../components/Card/Card';
3
5
 
4
6
  // ---------------------------------------------------------------------------
5
7
  // Token Gallery — live rendering of every CSS custom property tier.
@@ -16,8 +18,11 @@ const SEMANTIC_COLORS = [
16
18
  '--sv-color-text-muted',
17
19
  '--sv-color-text-subtle',
18
20
  '--sv-color-text-on-accent',
21
+ '--sv-color-text-on-light-fill',
22
+ '--sv-color-text-on-dark-fill',
19
23
  '--sv-color-border',
20
24
  '--sv-color-border-strong',
25
+ '--sv-color-shadow-strong',
21
26
  '--sv-color-accent',
22
27
  '--sv-color-accent-hover',
23
28
  '--sv-color-accent-subtle',
@@ -99,6 +104,7 @@ const ICON_SIZE_TOKENS = [
99
104
  ];
100
105
 
101
106
  const FONT_SIZE_TOKENS = [
107
+ '--sv-font-size-2xs',
102
108
  '--sv-font-size-label',
103
109
  '--sv-font-size-xs',
104
110
  '--sv-font-size-caption',
@@ -107,6 +113,7 @@ const FONT_SIZE_TOKENS = [
107
113
  '--sv-font-size-lg',
108
114
  '--sv-font-size-xl',
109
115
  '--sv-font-size-2xl',
116
+ '--sv-font-size-3xl',
110
117
  ];
111
118
 
112
119
  // Task 10.2 — in-app text-size control. --sv-text-size-scale is theme-stable
@@ -138,6 +145,10 @@ const SHADOW_TOKENS = [
138
145
  '--sv-shadow-control',
139
146
  ];
140
147
 
148
+ // RFC 0094 — theme-stable like the other scale tokens; a theme preset can
149
+ // override it (e.g. neobrutalism's thicker outline).
150
+ const BORDER_WIDTH_TOKENS = ['--sv-border-width-hairline'];
151
+
141
152
  // Realistic duration+easing pairings, not every combination — matches how
142
153
  // Dialog/Drawer actually use these tokens (see motion.ts).
143
154
  const MOTION_PAIRS: { durationToken: string; easeToken: string; use: string }[] = [
@@ -530,6 +541,45 @@ function TokenGalleryComponent() {
530
541
  ))}
531
542
  </Section>
532
543
 
544
+ <Section title="Border width">
545
+ {BORDER_WIDTH_TOKENS.map((t) => (
546
+ <ScaleRow
547
+ key={t}
548
+ token={t}
549
+ renderPreview={(_v) => (
550
+ <div
551
+ style={{
552
+ width: 48,
553
+ height: 28,
554
+ background: 'var(--sv-color-surface-raised)',
555
+ border: `var(${t}) solid var(--sv-color-border-strong)`,
556
+ flexShrink: 0,
557
+ }}
558
+ />
559
+ )}
560
+ />
561
+ ))}
562
+ </Section>
563
+
564
+ <Section title="Theme presets (RFC 0094)">
565
+ <p style={{ color: 'var(--sv-color-text-muted)', fontSize: 13, marginBottom: 12 }}>
566
+ A closed, built-in set of full-identity presets — border width, shadow shape, corner
567
+ radius, and the <code>Button</code> press interaction (<code>--sv-button-shadow</code>,{' '}
568
+ <code>--sv-button-press-x/-y</code>) all move together. Use the{' '}
569
+ <strong>Theme preset</strong> toolbar above (next to Radius) to switch — these are live
570
+ components, not a static swatch, so they respond immediately.
571
+ </p>
572
+ <div style={{ display: 'flex', gap: 16, alignItems: 'flex-start', flexWrap: 'wrap' }}>
573
+ <Button variant="primary">Primary button</Button>
574
+ <Button variant="secondary">Secondary button</Button>
575
+ <Card padding="md" style={{ width: 220 }}>
576
+ <p style={{ margin: 0, color: 'var(--sv-color-text-primary)', fontSize: 14 }}>
577
+ Card content
578
+ </p>
579
+ </Card>
580
+ </div>
581
+ </Section>
582
+
533
583
  <Section title="Icon sizes">
534
584
  {ICON_SIZE_TOKENS.map((t) => (
535
585
  <ScaleRow
@@ -64,6 +64,7 @@
64
64
  sans-serif;
65
65
  --sv-font-family-mono:
66
66
  'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;
67
+ --sv-font-size-2xs: 0.625rem; /* 10px — smallest badge size (Badge size="xs") */
67
68
  --sv-font-size-label: 0.6875rem; /* 11px — all-caps section labels */
68
69
  --sv-font-size-xs: 0.75rem; /* 12px — mono identifiers */
69
70
  --sv-font-size-caption: 0.8125rem; /* 13px — secondary / supporting copy */
@@ -72,6 +73,10 @@
72
73
  --sv-font-size-lg: 1.125rem; /* 18px */
73
74
  --sv-font-size-xl: 1.25rem; /* 20px */
74
75
  --sv-font-size-2xl: 1.5rem; /* 24px */
76
+ --sv-font-size-3xl: 2rem; /* 32px — document title style (RichTextEditor "Title" paragraph style). A
77
+ modest step above 2xl reads too close to Heading 1 at a glance (both
78
+ bold, only 4px apart) — this keeps roughly the same ~1.3x jump Google
79
+ Docs itself uses between its own Title and Heading 1 sizes. */
75
80
  --sv-font-weight-regular: 400;
76
81
  --sv-font-weight-medium: 500;
77
82
  --sv-font-weight-semibold: 600;
@@ -116,6 +121,12 @@
116
121
  ); /* 20px @ scale 1 — bottom sheet (Drawer top corners) */
117
122
  --sv-radius-full: 9999px; /* pill / avatar — unaffected by --sv-radius-scale */
118
123
 
124
+ /* Border width (RFC 0094). Theme-stable like the other scale tokens above —
125
+ a theme preset can override it (e.g. neobrutalism's thicker outline), but
126
+ it has no separate semantic tier. Every component references this by
127
+ name instead of a literal `1px`, the way --sv-radius-* etc. do. */
128
+ --sv-border-width-hairline: 1px;
129
+
119
130
  /* Touch targets — minimum hit-area dimension for interactive controls.
120
131
  44px follows the WCAG 2.5.5 / Apple HIG / Material guideline for reliable
121
132
  touch without a pointing device. Applied to icon-only controls on mobile
@@ -19,6 +19,17 @@
19
19
  --sv-color-text-muted: var(--sv-grey-500);
20
20
  --sv-color-text-subtle: var(--sv-grey-400);
21
21
  --sv-color-text-on-accent: var(--sv-white);
22
+ /* Guaranteed-contrast text pair for content painted directly on a
23
+ plugin-supplied literal background colour (data, not a design-system
24
+ token — e.g. a kanban board's own canvas colour). A consumer picks
25
+ whichever member actually contrasts with its own literal background;
26
+ neither can be theme-reactive the way --sv-color-text-primary is,
27
+ since the background itself doesn't react to the app's theme either.
28
+ Intentionally NOT overridden under [data-theme='dark'] below — same
29
+ "reads the same regardless of overall page theme" reasoning as
30
+ --sv-color-text-on-error/-success. */
31
+ --sv-color-text-on-light-fill: var(--sv-grey-950);
32
+ --sv-color-text-on-dark-fill: var(--sv-grey-50);
22
33
 
23
34
  --sv-color-border: var(--sv-grey-200);
24
35
  --sv-color-border-strong: var(--sv-grey-300);
@@ -68,6 +79,14 @@
68
79
  elevation shadow. Composed rgba layers, like --sv-shadow-card. */
69
80
  --sv-color-scrim: rgba(0, 0, 0, 0.5);
70
81
 
82
+ /* High-contrast shadow colour (RFC 0094). Distinct from --sv-color-border-strong
83
+ even though they resolve to the same monochrome scale — a theme preset's
84
+ hard offset shadows (e.g. neobrutalism) need a bolder, more saturated value
85
+ than a border does to read as "authentic," so this is its own token rather
86
+ than reusing border-strong. Unused by the default theme's soft rgba()
87
+ shadows above; only a preset that opts in (theme-presets.ts) references it. */
88
+ --sv-color-shadow-strong: var(--sv-grey-950);
89
+
71
90
  --sv-shadow-card: 0 1px 2px rgba(0, 0, 0, 0.06), 0 1px 3px rgba(0, 0, 0, 0.1);
72
91
  --sv-shadow-hover: 0 2px 6px rgba(0, 0, 0, 0.08); /* e1 — card hover lift */
73
92
  --sv-shadow-popover:
@@ -75,6 +94,17 @@
75
94
  --sv-shadow-overlay: 0 10px 38px rgba(0, 0, 0, 0.18), 0 6px 12px rgba(0, 0, 0, 0.12);
76
95
  --sv-shadow-control: 0 1px 3px rgba(0, 0, 0, 0.2); /* small interactive-control shadows, e.g. Toggle thumb */
77
96
 
97
+ /* Button press-interaction tokens (RFC 0094). Consumed unconditionally by
98
+ Button.module.css's :active rule — these defaults (none/0/0) make that
99
+ rule a no-op for today's default look. A theme preset (e.g.
100
+ neobrutalism) sets non-zero values to get a tactile "press into shadow"
101
+ effect. Not overridden under [data-theme='dark'] below — like the
102
+ shadow tokens above, a theme preset's own light/dark variants set these
103
+ directly rather than this file deriving a dark-mode default for them. */
104
+ --sv-button-shadow: none;
105
+ --sv-button-press-x: 0;
106
+ --sv-button-press-y: 0;
107
+
78
108
  /*
79
109
  * Instance identity tokens (RFC 0027 / RFC 0032 rename). Distinct namespace from --sv-color-*:
80
110
  * these hold URLs (not colours) and are set once by the operator, not swapped
@@ -122,6 +152,8 @@
122
152
 
123
153
  --sv-color-scrim: rgba(0, 0, 0, 0.65);
124
154
 
155
+ --sv-color-shadow-strong: var(--sv-grey-50);
156
+
125
157
  --sv-shadow-card: 0 1px 2px rgba(0, 0, 0, 0.4), 0 1px 3px rgba(0, 0, 0, 0.5);
126
158
  --sv-shadow-hover: 0 2px 6px rgba(0, 0, 0, 0.3);
127
159
  --sv-shadow-popover: 0 4px 16px rgba(0, 0, 0, 0.5), 0 2px 6px rgba(0, 0, 0, 0.3);
@@ -0,0 +1,33 @@
1
+ /*
2
+ * Generated by scripts/generate-theme-presets-css.ts — do not edit.
3
+ * Source of truth: packages/ui/src/tokens/theme-presets.ts
4
+ *
5
+ * For Storybook / standalone (non-runtime) use only — set data-theme-preset
6
+ * on the document root element (<html>), never a nested wrapper. See the
7
+ * generator script's doc comment for why.
8
+ */
9
+ [data-theme-preset='neobrutalism'] {
10
+ --sv-radius-scale: 0;
11
+ --sv-border-width-hairline: 2px;
12
+ --sv-shadow-card: 4px 4px 0 0 var(--sv-color-shadow-strong);
13
+ --sv-shadow-hover: 6px 6px 0 0 var(--sv-color-shadow-strong);
14
+ --sv-shadow-popover: 4px 4px 0 0 var(--sv-color-shadow-strong);
15
+ --sv-shadow-overlay: 8px 8px 0 0 var(--sv-color-shadow-strong);
16
+ --sv-shadow-control: 2px 2px 0 0 var(--sv-color-shadow-strong);
17
+ --sv-button-shadow: 4px 4px 0 0 var(--sv-color-shadow-strong);
18
+ --sv-button-press-x: 4px;
19
+ --sv-button-press-y: 4px;
20
+ }
21
+
22
+ [data-theme-preset='neobrutalism'][data-theme='dark'] {
23
+ --sv-radius-scale: 0;
24
+ --sv-border-width-hairline: 2px;
25
+ --sv-shadow-card: 4px 4px 0 0 var(--sv-color-shadow-strong);
26
+ --sv-shadow-hover: 6px 6px 0 0 var(--sv-color-shadow-strong);
27
+ --sv-shadow-popover: 4px 4px 0 0 var(--sv-color-shadow-strong);
28
+ --sv-shadow-overlay: 8px 8px 0 0 var(--sv-color-shadow-strong);
29
+ --sv-shadow-control: 2px 2px 0 0 var(--sv-color-shadow-strong);
30
+ --sv-button-shadow: 4px 4px 0 0 var(--sv-color-shadow-strong);
31
+ --sv-button-press-x: 4px;
32
+ --sv-button-press-y: 4px;
33
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Sovereign Design System — theme presets (RFC 0094).
3
+ *
4
+ * The single source of truth for every built-in theme preset's token
5
+ * overrides. Deliberately plain data (no CSS-in-JS, no build dependency) so
6
+ * it can be consumed two ways with zero duplication:
7
+ * - `scripts/generate-theme-presets-css.ts` (`pnpm generate:theme-presets`)
8
+ * reads this file and emits `theme-presets.css` for Storybook/standalone
9
+ * consumers.
10
+ * - `runtime/src/instance-style.ts` (RFC 0095) imports `THEME_PRESETS`
11
+ * directly and flattens the selected preset into the existing
12
+ * `:root { ... }` instance-style injection — no CSS parsing, no second
13
+ * copy of these values to drift out of sync.
14
+ *
15
+ * Presets are closed and built-in (RFC 0094's "Alternatives considered") —
16
+ * add a new one here and re-run the generator, never accept arbitrary
17
+ * operator-supplied values into this shape.
18
+ *
19
+ * `'default'` MUST stay `{ light: {}, dark: {} }` — an empty override set is
20
+ * what guarantees today's look stays byte-identical for every instance that
21
+ * never opts into a different preset.
22
+ */
23
+
24
+ export type ThemePresetName = 'default' | 'neobrutalism';
25
+
26
+ export const THEME_PRESET_NAMES: readonly ThemePresetName[] = ['default', 'neobrutalism'];
27
+
28
+ export interface ThemePresetTokens {
29
+ /** Sparse — only tokens this preset overrides. Everything else falls through to the default. */
30
+ light: Record<string, string>;
31
+ dark: Record<string, string>;
32
+ }
33
+
34
+ export const THEME_PRESETS: Record<ThemePresetName, ThemePresetTokens> = {
35
+ default: { light: {}, dark: {} },
36
+ neobrutalism: {
37
+ light: {
38
+ // --sv-radius-scale is a calc()-chain root: --sv-radius-sm..-3xl are
39
+ // all `calc(var(--sv-radius-scale) * ...)`, declared once at :root
40
+ // (primitives.css). This ONLY cascades correctly if the selector this
41
+ // override lands under matches :root itself (<html>) — a scoped
42
+ // descendant selector would set --sv-radius-scale there, but the
43
+ // dependent --sv-radius-* tokens were never re-declared at that node,
44
+ // so they'd keep inheriting their :root-resolved value unchanged. See
45
+ // the `withRadius` decorator comment in `.storybook/preview.ts` for
46
+ // the same constraint, discovered independently while building RFC
47
+ // 0077's Token Gallery preset demo.
48
+ '--sv-radius-scale': '0',
49
+ '--sv-border-width-hairline': '2px',
50
+ '--sv-shadow-card': '4px 4px 0 0 var(--sv-color-shadow-strong)',
51
+ '--sv-shadow-hover': '6px 6px 0 0 var(--sv-color-shadow-strong)',
52
+ '--sv-shadow-popover': '4px 4px 0 0 var(--sv-color-shadow-strong)',
53
+ '--sv-shadow-overlay': '8px 8px 0 0 var(--sv-color-shadow-strong)',
54
+ '--sv-shadow-control': '2px 2px 0 0 var(--sv-color-shadow-strong)',
55
+ '--sv-button-shadow': '4px 4px 0 0 var(--sv-color-shadow-strong)',
56
+ '--sv-button-press-x': '4px',
57
+ '--sv-button-press-y': '4px',
58
+ },
59
+ dark: {
60
+ '--sv-radius-scale': '0',
61
+ '--sv-border-width-hairline': '2px',
62
+ '--sv-shadow-card': '4px 4px 0 0 var(--sv-color-shadow-strong)',
63
+ '--sv-shadow-hover': '6px 6px 0 0 var(--sv-color-shadow-strong)',
64
+ '--sv-shadow-popover': '4px 4px 0 0 var(--sv-color-shadow-strong)',
65
+ '--sv-shadow-overlay': '8px 8px 0 0 var(--sv-color-shadow-strong)',
66
+ '--sv-shadow-control': '2px 2px 0 0 var(--sv-color-shadow-strong)',
67
+ '--sv-button-shadow': '4px 4px 0 0 var(--sv-color-shadow-strong)',
68
+ '--sv-button-press-x': '4px',
69
+ '--sv-button-press-y': '4px',
70
+ },
71
+ },
72
+ };