@sovereignfs/ui 0.23.0 → 0.33.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 (109) hide show
  1. package/dist/Button.module.css +75 -8
  2. package/dist/Calendar.module.css +126 -0
  3. package/dist/Card.module.css +4 -2
  4. package/dist/Checkbox.module.css +14 -2
  5. package/dist/CodeTextarea.module.css +53 -0
  6. package/dist/ConfirmDialog.module.css +109 -0
  7. package/dist/DatePicker.module.css +57 -0
  8. package/dist/Dialog.module.css +73 -46
  9. package/dist/DragHandleRow.module.css +9 -3
  10. package/dist/Drawer.module.css +61 -0
  11. package/dist/Icon.module.css +5 -0
  12. package/dist/Menu.module.css +82 -0
  13. package/dist/NavTabs.module.css +4 -2
  14. package/dist/OverlayHeader.module.css +98 -0
  15. package/dist/Popover.module.css +31 -1
  16. package/dist/SegmentedControl.module.css +4 -2
  17. package/dist/Sheet.module.css +55 -0
  18. package/dist/SplitPane.module.css +85 -0
  19. package/dist/StatusBadge.module.css +59 -0
  20. package/dist/SystemBanner.module.css +4 -2
  21. package/dist/Tabs.module.css +4 -2
  22. package/dist/TagInput.module.css +141 -0
  23. package/dist/Toast.module.css +4 -2
  24. package/dist/Tooltip.module.css +6 -1
  25. package/dist/index.d.ts +493 -17
  26. package/dist/index.js +1390 -209
  27. package/dist/tokens/primitives.css +19 -0
  28. package/dist/tokens/semantic.css +13 -0
  29. package/package.json +1 -1
  30. package/src/__tests__/motion.test.tsx +105 -0
  31. package/src/components/Button/Button.module.css +75 -8
  32. package/src/components/Button/Button.stories.tsx +6 -0
  33. package/src/components/Button/Button.tsx +17 -1
  34. package/src/components/Button/__tests__/Button.test.tsx +19 -0
  35. package/src/components/Calendar/Calendar.module.css +126 -0
  36. package/src/components/Calendar/Calendar.tsx +213 -0
  37. package/src/components/Calendar/__tests__/Calendar.test.tsx +98 -0
  38. package/src/components/Calendar/dateUtils.ts +97 -0
  39. package/src/components/Card/Card.module.css +4 -2
  40. package/src/components/Checkbox/Checkbox.module.css +14 -2
  41. package/src/components/Checkbox/Checkbox.tsx +22 -12
  42. package/src/components/CodeTextarea/CodeTextarea.module.css +53 -0
  43. package/src/components/CodeTextarea/CodeTextarea.stories.tsx +77 -0
  44. package/src/components/CodeTextarea/CodeTextarea.tsx +36 -0
  45. package/src/components/ConfirmDialog/ConfirmDialog.module.css +109 -0
  46. package/src/components/ConfirmDialog/ConfirmDialog.tsx +124 -0
  47. package/src/components/ConfirmDialog/__tests__/ConfirmDialog.test.tsx +146 -0
  48. package/src/components/DatePicker/DatePicker.module.css +57 -0
  49. package/src/components/DatePicker/DatePicker.tsx +103 -0
  50. package/src/components/DatePicker/__tests__/DatePicker.test.tsx +89 -0
  51. package/src/components/Dialog/Dialog.module.css +73 -46
  52. package/src/components/Dialog/Dialog.stories.tsx +51 -1
  53. package/src/components/Dialog/Dialog.tsx +86 -22
  54. package/src/components/Dialog/__tests__/Dialog.test.tsx +67 -3
  55. package/src/components/DragHandleRow/DragHandleRow.module.css +9 -3
  56. package/src/components/Drawer/Drawer.module.css +61 -0
  57. package/src/components/Drawer/Drawer.tsx +117 -13
  58. package/src/components/Drawer/__tests__/Drawer.test.tsx +123 -2
  59. package/src/components/Icon/Icon.module.css +5 -0
  60. package/src/components/Icon/Icon.stories.tsx +4 -3
  61. package/src/components/Icon/Icon.tsx +1 -1
  62. package/src/components/Icon/icons/calendar.tsx +23 -0
  63. package/src/components/Icon/icons/ellipsis-vertical.tsx +22 -0
  64. package/src/components/Icon/icons/index.ts +8 -4
  65. package/src/components/Icon/icons/sliders-horizontal.tsx +9 -9
  66. package/src/components/Menu/Menu.module.css +82 -0
  67. package/src/components/Menu/Menu.tsx +137 -0
  68. package/src/components/Menu/__tests__/Menu.test.tsx +197 -0
  69. package/src/components/NavTabs/NavTabs.module.css +4 -2
  70. package/src/components/OverlayHeader/OverlayHeader.module.css +98 -0
  71. package/src/components/OverlayHeader/OverlayHeader.tsx +62 -0
  72. package/src/components/OverlayHeader/__tests__/OverlayHeader.test.tsx +51 -0
  73. package/src/components/Popover/Popover.module.css +31 -1
  74. package/src/components/Popover/Popover.stories.tsx +126 -1
  75. package/src/components/Popover/Popover.tsx +140 -7
  76. package/src/components/SegmentedControl/SegmentedControl.module.css +4 -2
  77. package/src/components/Sheet/Sheet.module.css +55 -0
  78. package/src/components/Sheet/Sheet.tsx +151 -0
  79. package/src/components/Sheet/__tests__/Sheet.test.tsx +99 -0
  80. package/src/components/SplitPane/SplitPane.module.css +85 -0
  81. package/src/components/SplitPane/SplitPane.stories.tsx +160 -0
  82. package/src/components/SplitPane/SplitPane.tsx +149 -0
  83. package/src/components/SplitPane/__tests__/SplitPane.test.tsx +51 -0
  84. package/src/components/StatusBadge/StatusBadge.module.css +59 -0
  85. package/src/components/StatusBadge/StatusBadge.stories.tsx +74 -0
  86. package/src/components/StatusBadge/StatusBadge.tsx +65 -0
  87. package/src/components/SystemBanner/SystemBanner.module.css +4 -2
  88. package/src/components/Tabs/Tabs.module.css +4 -2
  89. package/src/components/TagInput/TagInput.module.css +141 -0
  90. package/src/components/TagInput/TagInput.stories.tsx +109 -0
  91. package/src/components/TagInput/TagInput.tsx +174 -0
  92. package/src/components/TagInput/__tests__/TagInput.test.tsx +69 -0
  93. package/src/components/Toast/Toast.module.css +4 -2
  94. package/src/components/Tooltip/Tooltip.module.css +6 -1
  95. package/src/hooks/__tests__/useDoubleTap.test.tsx +117 -0
  96. package/src/hooks/__tests__/useIsMobile.test.tsx +80 -0
  97. package/src/hooks/__tests__/useLongPress.test.tsx +158 -0
  98. package/src/hooks/index.ts +4 -0
  99. package/src/hooks/useDoubleTap.ts +91 -0
  100. package/src/hooks/useIsMobile.ts +33 -0
  101. package/src/hooks/useLongPress.ts +185 -0
  102. package/src/index.ts +30 -1
  103. package/src/motion.ts +78 -0
  104. package/src/stories/DesignSystemOverview.stories.tsx +292 -4
  105. package/src/stories/InteractionHooks.stories.tsx +388 -0
  106. package/src/stories/TokenGallery.stories.tsx +85 -1
  107. package/src/tokens/primitives.css +19 -0
  108. package/src/tokens/semantic.css +13 -0
  109. package/src/components/Icon/icons/terminal.tsx +0 -19
@@ -11,7 +11,11 @@ import { EmptyState } from '../components/EmptyState/EmptyState';
11
11
  import { FormField } from '../components/FormField/FormField';
12
12
  import { Icon } from '../components/Icon/Icon';
13
13
  import { Input } from '../components/Input/Input';
14
+ import { CodeTextarea } from '../components/CodeTextarea/CodeTextarea';
14
15
  import { Textarea } from '../components/Textarea/Textarea';
16
+ import { StatusBadge } from '../components/StatusBadge/StatusBadge';
17
+ import { SplitPane } from '../components/SplitPane/SplitPane';
18
+ import { TagInput } from '../components/TagInput/TagInput';
15
19
  import { NavTabs } from '../components/NavTabs/NavTabs';
16
20
  import { PageHeader } from '../components/PageHeader/PageHeader';
17
21
  import { Popover } from '../components/Popover/Popover';
@@ -25,6 +29,12 @@ import { Toggle } from '../components/Toggle/Toggle';
25
29
  import { Tooltip } from '../components/Tooltip/Tooltip';
26
30
  import { Checkbox } from '../components/Checkbox/Checkbox';
27
31
  import { DragHandleRow } from '../components/DragHandleRow/DragHandleRow';
32
+ import { OverlayHeader } from '../components/OverlayHeader/OverlayHeader';
33
+ import { Sheet } from '../components/Sheet/Sheet';
34
+ import { ConfirmDialog } from '../components/ConfirmDialog/ConfirmDialog';
35
+ import { Menu } from '../components/Menu/Menu';
36
+ import { Calendar } from '../components/Calendar/Calendar';
37
+ import { DatePicker } from '../components/DatePicker/DatePicker';
28
38
 
29
39
  // ---------------------------------------------------------------------------
30
40
  // Shared primitives
@@ -170,6 +180,8 @@ const COLOR_GROUPS: Array<{
170
180
  { token: '--sv-color-text-muted', name: 'text-muted' },
171
181
  { token: '--sv-color-text-subtle', name: 'text-subtle' },
172
182
  { token: '--sv-color-text-on-accent', name: 'text-on-accent' },
183
+ { token: '--sv-color-text-on-error', name: 'text-on-error' },
184
+ { token: '--sv-color-text-on-success', name: 'text-on-success' },
173
185
  ],
174
186
  },
175
187
  {
@@ -197,6 +209,7 @@ const COLOR_GROUPS: Array<{
197
209
  { token: '--sv-color-error-surface', name: 'error-surface' },
198
210
  { token: '--sv-color-error-text', name: 'error-text' },
199
211
  { token: '--sv-color-error-border', name: 'error-border' },
212
+ { token: '--sv-color-error-solid', name: 'error-solid' },
200
213
  ],
201
214
  },
202
215
  {
@@ -215,6 +228,7 @@ const COLOR_GROUPS: Array<{
215
228
  { token: '--sv-color-success-surface', name: 'success-surface' },
216
229
  { token: '--sv-color-success-text', name: 'success-text' },
217
230
  { token: '--sv-color-success-border', name: 'success-border' },
231
+ { token: '--sv-color-success-solid', name: 'success-solid' },
218
232
  ],
219
233
  },
220
234
  {
@@ -548,6 +562,150 @@ function PopoverDemo() {
548
562
  );
549
563
  }
550
564
 
565
+ function OverlayHeaderDemo() {
566
+ return (
567
+ <div
568
+ style={{
569
+ width: '100%',
570
+ maxWidth: 360,
571
+ border: '1px solid var(--sv-color-border)',
572
+ borderRadius: 'var(--sv-radius-lg)',
573
+ overflow: 'hidden',
574
+ }}
575
+ >
576
+ <OverlayHeader
577
+ title="Edit list"
578
+ onClose={() => {}}
579
+ onBack={() => {}}
580
+ action={
581
+ <Button size="sm" variant="ghost" onClick={() => {}}>
582
+ Save
583
+ </Button>
584
+ }
585
+ />
586
+ </div>
587
+ );
588
+ }
589
+
590
+ function SheetDemo() {
591
+ const [open, setOpen] = useState(false);
592
+ return (
593
+ <>
594
+ <Button size="sm" onClick={() => setOpen(true)}>
595
+ Open sheet
596
+ </Button>
597
+ {/* Storybook has no --sv-shell-header-height/--sv-shell-footer-height, so
598
+ the panel fills this fixed-height preview box instead of the real
599
+ app's header-to-footer region — same component, just no shell
600
+ around it here. */}
601
+ <div
602
+ style={{
603
+ position: 'relative',
604
+ width: '100%',
605
+ maxWidth: 360,
606
+ height: 280,
607
+ marginTop: 12,
608
+ border: '1px solid var(--sv-color-border)',
609
+ borderRadius: 'var(--sv-radius-lg)',
610
+ overflow: 'hidden',
611
+ }}
612
+ >
613
+ <div style={{ position: 'absolute', inset: 0 }}>
614
+ <Sheet
615
+ open={open}
616
+ onClose={() => setOpen(false)}
617
+ title="Task detail"
618
+ aria-label="Task detail"
619
+ >
620
+ <div
621
+ style={{
622
+ padding: 16,
623
+ fontFamily: ff,
624
+ fontSize: 14,
625
+ color: 'var(--sv-color-text-muted)',
626
+ }}
627
+ >
628
+ Full-page overlay content — same pattern the tasks plugin used for its task-detail and
629
+ list-edit panels.
630
+ </div>
631
+ </Sheet>
632
+ </div>
633
+ </div>
634
+ </>
635
+ );
636
+ }
637
+
638
+ function ConfirmDialogDemo() {
639
+ const [open, setOpen] = useState(false);
640
+ return (
641
+ <>
642
+ <Button size="sm" variant="destructive" onClick={() => setOpen(true)}>
643
+ Remove passkey
644
+ </Button>
645
+ <ConfirmDialog
646
+ open={open}
647
+ onClose={() => setOpen(false)}
648
+ onConfirm={() => setOpen(false)}
649
+ title="Remove passkey"
650
+ message="You will no longer be able to sign in with this passkey."
651
+ confirmLabel="Remove"
652
+ destructive
653
+ />
654
+ </>
655
+ );
656
+ }
657
+
658
+ function MenuDemo() {
659
+ const [open, setOpen] = useState(false);
660
+ const [sortBy, setSortBy] = useState<'manual' | 'title' | 'due'>('manual');
661
+ return (
662
+ <Menu
663
+ open={open}
664
+ onClose={() => setOpen(false)}
665
+ aria-label="List actions"
666
+ align="left"
667
+ trigger={
668
+ <Button size="sm" variant="ghost" onClick={() => setOpen((o) => !o)}>
669
+ <Icon name="settings" size="sm" aria-hidden /> Actions
670
+ </Button>
671
+ }
672
+ items={[
673
+ { type: 'label', label: 'Sort by' },
674
+ { label: 'Manual', checked: sortBy === 'manual', onSelect: () => setSortBy('manual') },
675
+ { label: 'Title', checked: sortBy === 'title', onSelect: () => setSortBy('title') },
676
+ { label: 'Due date', checked: sortBy === 'due', onSelect: () => setSortBy('due') },
677
+ { type: 'separator' },
678
+ { label: 'Rename', icon: 'pencil', onSelect: () => {} },
679
+ { label: 'Duplicate', icon: 'plus', onSelect: () => {} },
680
+ { label: 'Delete', icon: 'trash-2', destructive: true, onSelect: () => {} },
681
+ ]}
682
+ />
683
+ );
684
+ }
685
+
686
+ function CalendarDemo() {
687
+ const [value, setValue] = useState<Date | null>(new Date());
688
+ return (
689
+ <div style={{ width: '100%', maxWidth: 320 }}>
690
+ <Calendar value={value} onChange={setValue} aria-label="Example calendar" />
691
+ </div>
692
+ );
693
+ }
694
+
695
+ function DatePickerDemo() {
696
+ const [value, setValue] = useState<Date | null>(null);
697
+ return (
698
+ <div style={{ width: '100%', maxWidth: 240 }}>
699
+ <DatePicker
700
+ value={value}
701
+ onChange={setValue}
702
+ aria-label="Due date"
703
+ placeholder="Select date"
704
+ />
705
+ </div>
706
+ );
707
+ }
708
+
551
709
  function ToastDemo() {
552
710
  const { show } = useToast();
553
711
  return (
@@ -567,6 +725,11 @@ function ToastDemo() {
567
725
  );
568
726
  }
569
727
 
728
+ function TagInputDemo() {
729
+ const [tags, setTags] = useState(['draft', 'frontmatter']);
730
+ return <TagInput value={tags} onChange={setTags} aria-label="Tags" />;
731
+ }
732
+
570
733
  // ---------------------------------------------------------------------------
571
734
  // Main component
572
735
  // ---------------------------------------------------------------------------
@@ -1184,7 +1347,7 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
1184
1347
  <section style={{ marginBottom: 'var(--sv-space-12)' }}>
1185
1348
  <SectionHeader
1186
1349
  title="Component gallery"
1187
- subtitle="All 15 components — click each story in the sidebar for the full API, variants, and controls."
1350
+ subtitle="All 22 components — click each story in the sidebar for the full API, variants, and controls."
1188
1351
  />
1189
1352
 
1190
1353
  <div style={{ marginBottom: 'var(--sv-space-6)' }}>
@@ -1206,7 +1369,7 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
1206
1369
  <ComponentCard
1207
1370
  name="Button"
1208
1371
  importLine="import { Button } from '@sovereignfs/ui';"
1209
- usage="Primary interactive control. Four variants: primary, secondary, ghost, destructive. Two sizes: md (default), sm."
1372
+ usage="Primary interactive control. Four variants: primary, secondary, ghost, destructive. Two sizes: md (default), sm. loading disables the button, sets aria-busy, and shows a spinner. 44px min-height under (pointer: coarse); hover behind (hover: hover)."
1210
1373
  >
1211
1374
  <Button>Primary</Button>
1212
1375
  <Button variant="secondary">Secondary</Button>
@@ -1214,6 +1377,7 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
1214
1377
  <Button variant="destructive" size="sm">
1215
1378
  Delete
1216
1379
  </Button>
1380
+ <Button loading>Saving…</Button>
1217
1381
  </ComponentCard>
1218
1382
 
1219
1383
  {/* Badge */}
@@ -1232,6 +1396,18 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
1232
1396
  <Badge variant="mono">v1.2.0</Badge>
1233
1397
  </ComponentCard>
1234
1398
 
1399
+ {/* StatusBadge */}
1400
+ <ComponentCard
1401
+ name="StatusBadge"
1402
+ importLine="import { StatusBadge } from '@sovereignfs/ui';"
1403
+ usage="Inline status indicator for editor sync, draft, conflict, and delete-pending states. RSC-safe."
1404
+ >
1405
+ <StatusBadge status="draft" />
1406
+ <StatusBadge status="synced" />
1407
+ <StatusBadge status="conflict" />
1408
+ <StatusBadge status="pending-delete" />
1409
+ </ComponentCard>
1410
+
1235
1411
  {/* Input */}
1236
1412
  <ComponentCard
1237
1413
  name="Input"
@@ -1244,6 +1420,15 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
1244
1420
  </div>
1245
1421
  </ComponentCard>
1246
1422
 
1423
+ {/* TagInput */}
1424
+ <ComponentCard
1425
+ name="TagInput"
1426
+ importLine="import { TagInput } from '@sovereignfs/ui';"
1427
+ usage="Controlled multi-value input for tags and frontmatter arrays. Enter/comma adds, Backspace removes, paste can split multiple tags."
1428
+ >
1429
+ <TagInputDemo />
1430
+ </ComponentCard>
1431
+
1247
1432
  {/* Select */}
1248
1433
  <ComponentCard
1249
1434
  name="Select"
@@ -1331,11 +1516,38 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
1331
1516
  <ComponentCard
1332
1517
  name="Drawer"
1333
1518
  importLine="import { Drawer } from '@sovereignfs/ui';"
1334
- usage="Bottom-sheet panel for mobile navigation. Esc, scrim-click, focus trap. Respects safe-area-inset-bottom."
1519
+ usage="Bottom-sheet panel. Esc, scrim-click, or swipe-down on the built-in grab handle to dismiss. snapHeight: 'content' (default, capped 80dvh) or 'half' (fixed 50dvh). Respects safe-area-inset-bottom."
1335
1520
  >
1336
1521
  <DrawerDemo />
1337
1522
  </ComponentCard>
1338
1523
 
1524
+ {/* OverlayHeader */}
1525
+ <ComponentCard
1526
+ name="OverlayHeader"
1527
+ importLine="import { OverlayHeader } from '@sovereignfs/ui';"
1528
+ usage="Shared fixed secondary header for Dialog's mobile mode, Sheet, and Drawer: title + close, optional back button, trailing action, and a second row for tab strips."
1529
+ >
1530
+ <OverlayHeaderDemo />
1531
+ </ComponentCard>
1532
+
1533
+ {/* Sheet */}
1534
+ <ComponentCard
1535
+ name="Sheet"
1536
+ importLine="import { Sheet } from '@sovereignfs/ui';"
1537
+ usage="Full-page overlay filling a plugin's content area between the shell header and footer — for detail views (task detail, list edit). No desktop equivalent; a desktop layout shows the same content inline instead."
1538
+ >
1539
+ <SheetDemo />
1540
+ </ComponentCard>
1541
+
1542
+ {/* ConfirmDialog */}
1543
+ <ComponentCard
1544
+ name="ConfirmDialog"
1545
+ importLine="import { ConfirmDialog } from '@sovereignfs/ui';"
1546
+ usage="Small, content-sized confirm/cancel prompt. Same presentation on desktop and mobile — not a full-screen sheet. destructive for a solid red confirm action; pending + error for an async onConfirm."
1547
+ >
1548
+ <ConfirmDialogDemo />
1549
+ </ComponentCard>
1550
+
1339
1551
  {/* Popover */}
1340
1552
  <ComponentCard
1341
1553
  name="Popover"
@@ -1345,6 +1557,33 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
1345
1557
  <PopoverDemo />
1346
1558
  </ComponentCard>
1347
1559
 
1560
+ {/* Menu */}
1561
+ <ComponentCard
1562
+ name="Menu"
1563
+ importLine="import { Menu } from '@sovereignfs/ui';"
1564
+ usage="Adaptive action menu: Popover on desktop, Drawer on mobile. Same items list renders in both — for '⋯' row/list actions. Entries can be a plain item, a { type: 'label' } section heading, a { type: 'separator' } divider, or a checkable item (pass checked on every item in the group) for a mutually-exclusive set like sort order."
1565
+ >
1566
+ <MenuDemo />
1567
+ </ComponentCard>
1568
+
1569
+ {/* Calendar */}
1570
+ <ComponentCard
1571
+ name="Calendar"
1572
+ importLine="import { Calendar } from '@sovereignfs/ui';"
1573
+ usage="Keyboard-navigable month grid. Date-only (no time/range yet). Arrow keys, Home/End, PageUp/PageDown, Enter/Space to select."
1574
+ >
1575
+ <CalendarDemo />
1576
+ </ComponentCard>
1577
+
1578
+ {/* DatePicker */}
1579
+ <ComponentCard
1580
+ name="DatePicker"
1581
+ importLine="import { DatePicker } from '@sovereignfs/ui';"
1582
+ usage="Form field pairing a trigger with Calendar: Popover on desktop, Drawer on mobile. Built-in trigger, unlike Menu's caller-supplied one."
1583
+ >
1584
+ <DatePickerDemo />
1585
+ </ComponentCard>
1586
+
1348
1587
  {/* SystemBanner */}
1349
1588
  <ComponentCard
1350
1589
  name="SystemBanner"
@@ -1412,6 +1651,55 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
1412
1651
  />
1413
1652
  </ComponentCard>
1414
1653
 
1654
+ {/* CodeTextarea */}
1655
+ <ComponentCard
1656
+ name="CodeTextarea"
1657
+ importLine="import { CodeTextarea } from '@sovereignfs/ui';"
1658
+ usage="Monospace textarea for Markdown, YAML, JSON, and raw frontmatter editing. Pair with FormField for labels and validation."
1659
+ >
1660
+ <CodeTextarea
1661
+ aria-label="YAML source"
1662
+ defaultValue={'title: Release notes\ntags:\n - launch'}
1663
+ rows={5}
1664
+ style={{ width: '100%' }}
1665
+ />
1666
+ </ComponentCard>
1667
+
1668
+ {/* SplitPane */}
1669
+ <ComponentCard
1670
+ name="SplitPane"
1671
+ importLine="import { SplitPane } from '@sovereignfs/ui';"
1672
+ usage="Responsive editor/preview or list/detail layout. Desktop panes are resizable with pointer and keyboard controls; mobile stacks to one column."
1673
+ >
1674
+ <SplitPane
1675
+ defaultPrimarySize={44}
1676
+ primary={
1677
+ <div
1678
+ style={{
1679
+ padding: 'var(--sv-space-3)',
1680
+ fontFamily: ffm,
1681
+ fontSize: 'var(--sv-font-size-xs)',
1682
+ color: 'var(--sv-color-text-primary)',
1683
+ }}
1684
+ >
1685
+ # Draft
1686
+ </div>
1687
+ }
1688
+ secondary={
1689
+ <div
1690
+ style={{
1691
+ padding: 'var(--sv-space-3)',
1692
+ fontFamily: ff,
1693
+ fontSize: 'var(--sv-font-size-sm)',
1694
+ color: 'var(--sv-color-text-primary)',
1695
+ }}
1696
+ >
1697
+ Draft preview
1698
+ </div>
1699
+ }
1700
+ />
1701
+ </ComponentCard>
1702
+
1415
1703
  {/* PageHeader */}
1416
1704
  <ComponentCard
1417
1705
  name="PageHeader"
@@ -1514,7 +1802,7 @@ font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
1514
1802
  <ComponentCard
1515
1803
  name="Checkbox"
1516
1804
  importLine="import { Checkbox } from '@sovereignfs/ui';"
1517
- usage="Accessible checkbox with optional animated strike-through on the label when checked. Used in task lists."
1805
+ usage="Accessible checkbox with optional animated strike-through on the label when checked. Used in task lists. Tap target expands to 44px under (pointer: coarse) without growing the visible 18px box."
1518
1806
  >
1519
1807
  {(() => {
1520
1808
  const [a, setA] = useState(false);