@sovereignfs/ui 0.83.1 → 0.89.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.
- package/LICENSE +373 -661
- package/NOTICE +8 -0
- package/README.md +4 -6
- package/dist/AppsLauncher.module.css +6 -0
- package/dist/Button.module.css +5 -0
- package/dist/Card.module.css +6 -0
- package/dist/CardTile.module.css +6 -0
- package/dist/CheckableListRow.module.css +8 -0
- package/dist/Checkbox.module.css +8 -0
- package/dist/CodeTextarea.module.css +6 -0
- package/dist/ColorPicker.module.css +6 -0
- package/dist/Combobox.module.css +6 -0
- package/dist/Command.module.css +6 -0
- package/dist/ConfirmDialog.module.css +6 -0
- package/dist/DatePicker.module.css +6 -0
- package/dist/Dialog.module.css +1 -1
- package/dist/DragHandleRow.module.css +6 -0
- package/dist/FileDropzone.module.css +25 -1
- package/dist/Header.module.css +6 -0
- package/dist/IconPicker.module.css +14 -4
- package/dist/Input.module.css +6 -0
- package/dist/LegalLinks.module.css +9 -2
- package/dist/Markdown.module.css +5 -0
- package/dist/Message.module.css +9 -0
- package/dist/MobileAppsDrawer.module.css +9 -5
- package/dist/MobileFooter.module.css +6 -0
- package/dist/NavList.module.css +11 -2
- package/dist/NavTabs.module.css +7 -1
- package/dist/NotificationsPanel.module.css +10 -0
- package/dist/QrCode.module.css +17 -0
- package/dist/QuantityStepper.module.css +4 -2
- package/dist/RadioGroup.module.css +6 -0
- package/dist/SegmentedControl.module.css +6 -0
- package/dist/Select.module.css +6 -0
- package/dist/Sheet.module.css +16 -1
- package/dist/SplitPane.module.css +1 -1
- package/dist/StepStrip.module.css +5 -3
- package/dist/SuggestionInput.module.css +12 -1
- package/dist/SwipableMobileCarouselDots.module.css +6 -0
- package/dist/SystemBanner.module.css +6 -0
- package/dist/Tabs.module.css +6 -0
- package/dist/TagInput.module.css +6 -0
- package/dist/Textarea.module.css +6 -0
- package/dist/Toast.module.css +5 -0
- package/dist/Toggle.module.css +7 -0
- package/dist/Tooltip.module.css +6 -0
- package/dist/UserMenu.module.css +5 -0
- package/dist/fonts/OFL.txt +93 -0
- package/dist/fonts/hanken-grotesk-cyrillic-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/hanken-grotesk-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/hanken-grotesk-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/hanken-grotesk-vietnamese-wght-normal.woff2 +0 -0
- package/dist/index.d.ts +121 -43
- package/dist/index.js +1410 -1152
- package/dist/tokens/fonts.css +53 -0
- package/dist/tokens/primitives.css +12 -12
- package/dist/tokens.css +2 -1
- package/package.json +24 -13
- package/src/__tests__/motion.test.tsx +0 -105
- package/src/__tests__/package-json.test.ts +0 -8
- package/src/components/Accordion/Accordion.module.css +0 -3
- package/src/components/Accordion/Accordion.stories.tsx +0 -110
- package/src/components/Accordion/Accordion.tsx +0 -53
- package/src/components/Accordion/__tests__/Accordion.test.tsx +0 -61
- package/src/components/Alert/Alert.module.css +0 -62
- package/src/components/Alert/Alert.stories.tsx +0 -96
- package/src/components/Alert/Alert.tsx +0 -58
- package/src/components/Alert/__tests__/Alert.test.tsx +0 -75
- package/src/components/AppsLauncher/AppsLauncher.module.css +0 -96
- package/src/components/AppsLauncher/AppsLauncher.stories.tsx +0 -58
- package/src/components/AppsLauncher/AppsLauncher.tsx +0 -117
- package/src/components/AppsLauncher/__tests__/AppsLauncher.test.tsx +0 -52
- package/src/components/AspectRatio/AspectRatio.module.css +0 -15
- package/src/components/AspectRatio/AspectRatio.stories.tsx +0 -78
- package/src/components/AspectRatio/AspectRatio.tsx +0 -21
- package/src/components/AspectRatio/__tests__/AspectRatio.test.tsx +0 -27
- package/src/components/Avatar/Avatar.module.css +0 -38
- package/src/components/Avatar/Avatar.tsx +0 -42
- package/src/components/Badge/Badge.module.css +0 -177
- package/src/components/Badge/Badge.stories.tsx +0 -238
- package/src/components/Badge/Badge.tsx +0 -84
- package/src/components/Badge/__tests__/Badge.test.tsx +0 -90
- package/src/components/BalanceChip/BalanceChip.module.css +0 -32
- package/src/components/BalanceChip/BalanceChip.stories.tsx +0 -45
- package/src/components/BalanceChip/BalanceChip.tsx +0 -33
- package/src/components/Breadcrumb/Breadcrumb.module.css +0 -49
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +0 -47
- package/src/components/Breadcrumb/Breadcrumb.tsx +0 -57
- package/src/components/Breadcrumb/__tests__/Breadcrumb.test.tsx +0 -52
- package/src/components/Button/Button.module.css +0 -169
- package/src/components/Button/Button.stories.tsx +0 -83
- package/src/components/Button/Button.tsx +0 -52
- package/src/components/Button/__tests__/Button.test.tsx +0 -66
- package/src/components/ButtonGroup/ButtonGroup.module.css +0 -29
- package/src/components/ButtonGroup/ButtonGroup.stories.tsx +0 -57
- package/src/components/ButtonGroup/ButtonGroup.tsx +0 -27
- package/src/components/ButtonGroup/__tests__/ButtonGroup.test.tsx +0 -29
- package/src/components/Calendar/Calendar.module.css +0 -126
- package/src/components/Calendar/Calendar.tsx +0 -213
- package/src/components/Calendar/__tests__/Calendar.test.tsx +0 -98
- package/src/components/Calendar/dateUtils.ts +0 -97
- package/src/components/Card/Card.module.css +0 -42
- package/src/components/Card/Card.tsx +0 -34
- package/src/components/CardTile/CardTile.module.css +0 -181
- package/src/components/CardTile/CardTile.stories.tsx +0 -86
- package/src/components/CardTile/CardTile.tsx +0 -128
- package/src/components/CardTile/__tests__/CardTile.test.tsx +0 -56
- package/src/components/CheckableListRow/CheckableListRow.module.css +0 -95
- package/src/components/CheckableListRow/CheckableListRow.stories.tsx +0 -99
- package/src/components/CheckableListRow/CheckableListRow.tsx +0 -89
- package/src/components/Checkbox/Checkbox.module.css +0 -111
- package/src/components/Checkbox/Checkbox.tsx +0 -78
- package/src/components/CodeTextarea/CodeTextarea.module.css +0 -53
- package/src/components/CodeTextarea/CodeTextarea.stories.tsx +0 -77
- package/src/components/CodeTextarea/CodeTextarea.tsx +0 -36
- package/src/components/Collapsible/Collapsible.module.css +0 -70
- package/src/components/Collapsible/Collapsible.stories.tsx +0 -62
- package/src/components/Collapsible/Collapsible.tsx +0 -66
- package/src/components/Collapsible/__tests__/Collapsible.test.tsx +0 -71
- package/src/components/ColorPicker/ColorPicker.module.css +0 -132
- package/src/components/ColorPicker/ColorPicker.stories.tsx +0 -80
- package/src/components/ColorPicker/ColorPicker.tsx +0 -157
- package/src/components/ColorPicker/__tests__/ColorPicker.test.tsx +0 -117
- package/src/components/Combobox/Combobox.module.css +0 -122
- package/src/components/Combobox/Combobox.stories.tsx +0 -79
- package/src/components/Combobox/Combobox.tsx +0 -186
- package/src/components/Combobox/__tests__/Combobox.test.tsx +0 -158
- package/src/components/Command/Command.module.css +0 -80
- package/src/components/Command/Command.stories.tsx +0 -104
- package/src/components/Command/Command.tsx +0 -168
- package/src/components/Command/__tests__/Command.test.tsx +0 -162
- package/src/components/ConfirmDialog/ConfirmDialog.module.css +0 -113
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +0 -150
- package/src/components/ConfirmDialog/__tests__/ConfirmDialog.test.tsx +0 -214
- package/src/components/ConsentPrompt/ConsentPrompt.module.css +0 -21
- package/src/components/ConsentPrompt/ConsentPrompt.stories.tsx +0 -31
- package/src/components/ConsentPrompt/ConsentPrompt.tsx +0 -66
- package/src/components/ContextMenu/ContextMenu.stories.tsx +0 -81
- package/src/components/ContextMenu/ContextMenu.tsx +0 -100
- package/src/components/ContextMenu/__tests__/ContextMenu.test.tsx +0 -87
- package/src/components/CurrencyInput/CurrencyInput.stories.tsx +0 -50
- package/src/components/CurrencyInput/CurrencyInput.tsx +0 -60
- package/src/components/DataTable/DataTable.module.css +0 -28
- package/src/components/DataTable/DataTable.stories.tsx +0 -74
- package/src/components/DataTable/DataTable.tsx +0 -154
- package/src/components/DataTable/__tests__/DataTable.test.tsx +0 -123
- package/src/components/DatePicker/DatePicker.module.css +0 -61
- package/src/components/DatePicker/DatePicker.tsx +0 -103
- package/src/components/DatePicker/__tests__/DatePicker.test.tsx +0 -89
- package/src/components/DeviceOnlyGate/DeviceOnlyGate.tsx +0 -58
- package/src/components/DeviceOnlyGate/__tests__/DeviceOnlyGate.test.tsx +0 -45
- package/src/components/DeviceStorageKeyGate/DeviceStorageKeyGate.tsx +0 -100
- package/src/components/DeviceStorageKeyGate/__tests__/DeviceStorageKeyGate.test.tsx +0 -119
- package/src/components/Dialog/Dialog.module.css +0 -335
- package/src/components/Dialog/Dialog.stories.tsx +0 -324
- package/src/components/Dialog/Dialog.tsx +0 -254
- package/src/components/Dialog/__tests__/Dialog.test.tsx +0 -318
- package/src/components/DragHandleRow/DragHandleRow.module.css +0 -57
- package/src/components/DragHandleRow/DragHandleRow.tsx +0 -42
- package/src/components/Drawer/Drawer.module.css +0 -121
- package/src/components/Drawer/Drawer.stories.tsx +0 -132
- package/src/components/Drawer/Drawer.tsx +0 -175
- package/src/components/Drawer/__tests__/Drawer.test.tsx +0 -226
- package/src/components/EmptyState/EmptyState.module.css +0 -27
- package/src/components/EmptyState/EmptyState.tsx +0 -27
- package/src/components/FileDropzone/FileDropzone.module.css +0 -63
- package/src/components/FileDropzone/FileDropzone.tsx +0 -109
- package/src/components/FileDropzone/__tests__/FileDropzone.test.tsx +0 -67
- package/src/components/FormField/FormField.module.css +0 -33
- package/src/components/FormField/FormField.tsx +0 -78
- package/src/components/FormField/__tests__/FormField.test.tsx +0 -83
- package/src/components/GripIcon/GripIcon.tsx +0 -27
- package/src/components/Header/Header.module.css +0 -62
- package/src/components/Header/Header.stories.tsx +0 -452
- package/src/components/Header/Header.tsx +0 -102
- package/src/components/Header/__tests__/Header.test.tsx +0 -104
- package/src/components/HeaderFooterLayout/HeaderFooterLayout.module.css +0 -21
- package/src/components/HeaderFooterLayout/HeaderFooterLayout.stories.tsx +0 -111
- package/src/components/HeaderFooterLayout/HeaderFooterLayout.tsx +0 -54
- package/src/components/HeaderFooterLayout/__tests__/HeaderFooterLayout.test.tsx +0 -58
- package/src/components/HoverCard/HoverCard.module.css +0 -3
- package/src/components/HoverCard/HoverCard.stories.tsx +0 -66
- package/src/components/HoverCard/HoverCard.tsx +0 -111
- package/src/components/HoverCard/__tests__/HoverCard.test.tsx +0 -99
- package/src/components/Icon/Icon.module.css +0 -27
- package/src/components/Icon/Icon.stories.tsx +0 -95
- package/src/components/Icon/Icon.tsx +0 -43
- package/src/components/Icon/__tests__/Icon.test.tsx +0 -40
- package/src/components/Icon/icons/activity.tsx +0 -20
- package/src/components/Icon/icons/alert-triangle.tsx +0 -22
- package/src/components/Icon/icons/apple.tsx +0 -21
- package/src/components/Icon/icons/arrow-left-right.tsx +0 -23
- package/src/components/Icon/icons/banana.tsx +0 -21
- package/src/components/Icon/icons/beef.tsx +0 -22
- package/src/components/Icon/icons/beer.tsx +0 -24
- package/src/components/Icon/icons/bell.tsx +0 -21
- package/src/components/Icon/icons/bold.tsx +0 -20
- package/src/components/Icon/icons/calendar.tsx +0 -23
- package/src/components/Icon/icons/candy.tsx +0 -24
- package/src/components/Icon/icons/carrot.tsx +0 -22
- package/src/components/Icon/icons/check.tsx +0 -20
- package/src/components/Icon/icons/chevron-down.tsx +0 -20
- package/src/components/Icon/icons/chevron-left.tsx +0 -20
- package/src/components/Icon/icons/chevron-right.tsx +0 -20
- package/src/components/Icon/icons/chevron-up.tsx +0 -20
- package/src/components/Icon/icons/circle-check.tsx +0 -21
- package/src/components/Icon/icons/circle-chevron-left.tsx +0 -21
- package/src/components/Icon/icons/circle-x.tsx +0 -22
- package/src/components/Icon/icons/code.tsx +0 -21
- package/src/components/Icon/icons/coffee.tsx +0 -23
- package/src/components/Icon/icons/cookie.tsx +0 -25
- package/src/components/Icon/icons/copy.tsx +0 -21
- package/src/components/Icon/icons/credit-card.tsx +0 -21
- package/src/components/Icon/icons/croissant.tsx +0 -24
- package/src/components/Icon/icons/cup-soda.tsx +0 -23
- package/src/components/Icon/icons/database.tsx +0 -22
- package/src/components/Icon/icons/download.tsx +0 -22
- package/src/components/Icon/icons/drumstick.tsx +0 -21
- package/src/components/Icon/icons/egg.tsx +0 -20
- package/src/components/Icon/icons/ellipsis-vertical.tsx +0 -22
- package/src/components/Icon/icons/external-link.tsx +0 -22
- package/src/components/Icon/icons/eye-off.tsx +0 -23
- package/src/components/Icon/icons/eye.tsx +0 -21
- package/src/components/Icon/icons/file-text.tsx +0 -24
- package/src/components/Icon/icons/file.tsx +0 -21
- package/src/components/Icon/icons/fish.tsx +0 -25
- package/src/components/Icon/icons/folder-closed.tsx +0 -21
- package/src/components/Icon/icons/folder-open.tsx +0 -20
- package/src/components/Icon/icons/folder.tsx +0 -20
- package/src/components/Icon/icons/folders.tsx +0 -21
- package/src/components/Icon/icons/grid-2x2.tsx +0 -22
- package/src/components/Icon/icons/hat-glasses.tsx +0 -24
- package/src/components/Icon/icons/history.tsx +0 -22
- package/src/components/Icon/icons/house.tsx +0 -21
- package/src/components/Icon/icons/inbox.tsx +0 -21
- package/src/components/Icon/icons/index.ts +0 -394
- package/src/components/Icon/icons/info.tsx +0 -22
- package/src/components/Icon/icons/italic.tsx +0 -22
- package/src/components/Icon/icons/layers.tsx +0 -22
- package/src/components/Icon/icons/layout-dashboard.tsx +0 -23
- package/src/components/Icon/icons/layout-grid.tsx +0 -23
- package/src/components/Icon/icons/link.tsx +0 -21
- package/src/components/Icon/icons/list-ordered.tsx +0 -25
- package/src/components/Icon/icons/list.tsx +0 -25
- package/src/components/Icon/icons/lock.tsx +0 -21
- package/src/components/Icon/icons/log-out.tsx +0 -22
- package/src/components/Icon/icons/luggage.tsx +0 -24
- package/src/components/Icon/icons/mail.tsx +0 -21
- package/src/components/Icon/icons/map-pin.tsx +0 -21
- package/src/components/Icon/icons/menu.tsx +0 -22
- package/src/components/Icon/icons/milk.tsx +0 -22
- package/src/components/Icon/icons/minus.tsx +0 -20
- package/src/components/Icon/icons/package.tsx +0 -23
- package/src/components/Icon/icons/paint-bucket.tsx +0 -23
- package/src/components/Icon/icons/panel-left.tsx +0 -21
- package/src/components/Icon/icons/pencil.tsx +0 -21
- package/src/components/Icon/icons/pin.tsx +0 -21
- package/src/components/Icon/icons/pizza.tsx +0 -24
- package/src/components/Icon/icons/plus.tsx +0 -21
- package/src/components/Icon/icons/rectangle-ellipsis.tsx +0 -23
- package/src/components/Icon/icons/refresh-cw.tsx +0 -23
- package/src/components/Icon/icons/rotate-ccw.tsx +0 -21
- package/src/components/Icon/icons/rotate-cw.tsx +0 -21
- package/src/components/Icon/icons/route.tsx +0 -22
- package/src/components/Icon/icons/salad.tsx +0 -24
- package/src/components/Icon/icons/search.tsx +0 -21
- package/src/components/Icon/icons/send.tsx +0 -21
- package/src/components/Icon/icons/settings.tsx +0 -21
- package/src/components/Icon/icons/share-2.tsx +0 -24
- package/src/components/Icon/icons/sheet.tsx +0 -24
- package/src/components/Icon/icons/shield.tsx +0 -20
- package/src/components/Icon/icons/shopping-basket.tsx +0 -26
- package/src/components/Icon/icons/sliders-horizontal.tsx +0 -28
- package/src/components/Icon/icons/smartphone.tsx +0 -21
- package/src/components/Icon/icons/snowflake.tsx +0 -31
- package/src/components/Icon/icons/spray-can.tsx +0 -29
- package/src/components/Icon/icons/square-kanban.tsx +0 -23
- package/src/components/Icon/icons/table.tsx +0 -23
- package/src/components/Icon/icons/trash-2.tsx +0 -24
- package/src/components/Icon/icons/upload.tsx +0 -22
- package/src/components/Icon/icons/user-round-plus.tsx +0 -23
- package/src/components/Icon/icons/user.tsx +0 -21
- package/src/components/Icon/icons/users.tsx +0 -23
- package/src/components/Icon/icons/wine.tsx +0 -23
- package/src/components/Icon/icons/x.tsx +0 -21
- package/src/components/IconPicker/IconPicker.module.css +0 -63
- package/src/components/IconPicker/IconPicker.stories.tsx +0 -84
- package/src/components/IconPicker/IconPicker.tsx +0 -90
- package/src/components/Input/Input.module.css +0 -52
- package/src/components/Input/Input.stories.tsx +0 -86
- package/src/components/Input/Input.tsx +0 -20
- package/src/components/Input/__tests__/Input.test.tsx +0 -22
- package/src/components/Item/Item.module.css +0 -70
- package/src/components/Item/Item.stories.tsx +0 -62
- package/src/components/Item/Item.tsx +0 -54
- package/src/components/Item/__tests__/Item.test.tsx +0 -39
- package/src/components/Kbd/Kbd.module.css +0 -14
- package/src/components/Kbd/Kbd.stories.tsx +0 -32
- package/src/components/Kbd/Kbd.tsx +0 -12
- package/src/components/Kbd/__tests__/Kbd.test.tsx +0 -14
- package/src/components/Label/Label.module.css +0 -11
- package/src/components/Label/Label.stories.tsx +0 -40
- package/src/components/Label/Label.tsx +0 -24
- package/src/components/Label/__tests__/Label.test.tsx +0 -32
- package/src/components/LegalLinks/LegalLinks.module.css +0 -24
- package/src/components/LegalLinks/LegalLinks.stories.tsx +0 -22
- package/src/components/LegalLinks/LegalLinks.tsx +0 -40
- package/src/components/Markdown/Markdown.module.css +0 -73
- package/src/components/Markdown/Markdown.stories.tsx +0 -40
- package/src/components/Markdown/Markdown.tsx +0 -305
- package/src/components/Markdown/__tests__/Markdown.test.tsx +0 -144
- package/src/components/Marker/Marker.module.css +0 -33
- package/src/components/Marker/Marker.stories.tsx +0 -39
- package/src/components/Marker/Marker.tsx +0 -34
- package/src/components/Marker/__tests__/Marker.test.tsx +0 -23
- package/src/components/MemberMultiSelect/MemberMultiSelect.module.css +0 -27
- package/src/components/MemberMultiSelect/MemberMultiSelect.stories.tsx +0 -74
- package/src/components/MemberMultiSelect/MemberMultiSelect.tsx +0 -67
- package/src/components/Menu/Menu.module.css +0 -90
- package/src/components/Menu/Menu.tsx +0 -209
- package/src/components/Menu/__tests__/Menu.test.tsx +0 -227
- package/src/components/Menubar/Menubar.stories.tsx +0 -52
- package/src/components/Menubar/Menubar.tsx +0 -42
- package/src/components/Menubar/__tests__/Menubar.test.tsx +0 -45
- package/src/components/Message/Message.module.css +0 -99
- package/src/components/Message/Message.stories.tsx +0 -79
- package/src/components/Message/Message.tsx +0 -41
- package/src/components/Message/__tests__/Message.test.tsx +0 -46
- package/src/components/MessageScroller/MessageScroller.module.css +0 -65
- package/src/components/MessageScroller/MessageScroller.stories.tsx +0 -84
- package/src/components/MessageScroller/MessageScroller.tsx +0 -84
- package/src/components/MessageScroller/__tests__/MessageScroller.test.tsx +0 -105
- package/src/components/MobileAppsDrawer/MobileAppsDrawer.module.css +0 -82
- package/src/components/MobileAppsDrawer/MobileAppsDrawer.tsx +0 -78
- package/src/components/MobileAppsDrawer/__tests__/MobileAppsDrawer.test.tsx +0 -80
- package/src/components/MobileFooter/MobileFooter.module.css +0 -73
- package/src/components/MobileFooter/MobileFooter.stories.tsx +0 -47
- package/src/components/MobileFooter/MobileFooter.tsx +0 -122
- package/src/components/MobileFooter/__tests__/MobileFooter.test.tsx +0 -129
- package/src/components/MobileHeader/MobileHeader.module.css +0 -76
- package/src/components/MobileHeader/MobileHeader.stories.tsx +0 -57
- package/src/components/MobileHeader/MobileHeader.tsx +0 -90
- package/src/components/MobileHeader/__tests__/MobileHeader.test.tsx +0 -113
- package/src/components/NavList/NavList.module.css +0 -130
- package/src/components/NavList/NavList.tsx +0 -136
- package/src/components/NavList/__tests__/NavList.test.tsx +0 -109
- package/src/components/NavTabs/NavTabs.module.css +0 -50
- package/src/components/NavTabs/NavTabs.tsx +0 -61
- package/src/components/NavigationMenu/NavigationMenu.module.css +0 -53
- package/src/components/NavigationMenu/NavigationMenu.stories.tsx +0 -58
- package/src/components/NavigationMenu/NavigationMenu.tsx +0 -125
- package/src/components/NavigationMenu/__tests__/NavigationMenu.test.tsx +0 -93
- package/src/components/NotificationsPanel/NotificationsPanel.module.css +0 -258
- package/src/components/NotificationsPanel/NotificationsPanel.stories.tsx +0 -61
- package/src/components/NotificationsPanel/NotificationsPanel.tsx +0 -187
- package/src/components/NotificationsPanel/__tests__/NotificationsPanel.test.tsx +0 -78
- package/src/components/OfflineGate/OfflineGate.tsx +0 -44
- package/src/components/OfflineGate/__tests__/OfflineGate.test.tsx +0 -66
- package/src/components/OverlayHeader/OverlayHeader.module.css +0 -98
- package/src/components/OverlayHeader/OverlayHeader.tsx +0 -77
- package/src/components/OverlayHeader/__tests__/OverlayHeader.test.tsx +0 -69
- package/src/components/PageContainer/PageContainer.module.css +0 -82
- package/src/components/PageContainer/PageContainer.tsx +0 -64
- package/src/components/PageContainer/__tests__/PageContainer.test.tsx +0 -70
- package/src/components/PageHeader/PageHeader.module.css +0 -86
- package/src/components/PageHeader/PageHeader.tsx +0 -176
- package/src/components/PageHeader/__tests__/PageHeader.test.tsx +0 -103
- package/src/components/PageLayout/PageLayout.module.css +0 -51
- package/src/components/PageLayout/PageLayout.stories.tsx +0 -73
- package/src/components/PageLayout/PageLayout.tsx +0 -51
- package/src/components/PageLayout/__tests__/PageLayout.test.tsx +0 -34
- package/src/components/Pagination/Pagination.module.css +0 -98
- package/src/components/Pagination/Pagination.stories.tsx +0 -49
- package/src/components/Pagination/Pagination.tsx +0 -87
- package/src/components/Pagination/__tests__/Pagination.test.tsx +0 -56
- package/src/components/Popover/Popover.module.css +0 -74
- package/src/components/Popover/Popover.stories.tsx +0 -357
- package/src/components/Popover/Popover.tsx +0 -290
- package/src/components/Popover/__tests__/Popover.test.tsx +0 -263
- package/src/components/Progress/Progress.module.css +0 -20
- package/src/components/Progress/Progress.stories.tsx +0 -40
- package/src/components/Progress/Progress.tsx +0 -35
- package/src/components/Progress/__tests__/Progress.test.tsx +0 -31
- package/src/components/QuantityStepper/QuantityStepper.module.css +0 -67
- package/src/components/QuantityStepper/QuantityStepper.stories.tsx +0 -79
- package/src/components/QuantityStepper/QuantityStepper.tsx +0 -107
- package/src/components/RadioGroup/RadioGroup.module.css +0 -88
- package/src/components/RadioGroup/RadioGroup.stories.tsx +0 -69
- package/src/components/RadioGroup/RadioGroup.tsx +0 -99
- package/src/components/RadioGroup/__tests__/RadioGroup.test.tsx +0 -59
- package/src/components/Resizable/Resizable.module.css +0 -70
- package/src/components/Resizable/Resizable.stories.tsx +0 -82
- package/src/components/Resizable/Resizable.tsx +0 -313
- package/src/components/Resizable/__tests__/Resizable.test.tsx +0 -128
- package/src/components/ResponsiveSurface/ResponsiveSurface.tsx +0 -24
- package/src/components/ResponsiveSurface/__tests__/ResponsiveSurface.test.tsx +0 -73
- package/src/components/RootLayout/RootLayout.module.css +0 -12
- package/src/components/RootLayout/RootLayout.stories.tsx +0 -125
- package/src/components/RootLayout/RootLayout.tsx +0 -185
- package/src/components/RootLayout/__tests__/RootLayout.test.tsx +0 -161
- package/src/components/ScrollArea/ScrollArea.module.css +0 -20
- package/src/components/ScrollArea/ScrollArea.stories.tsx +0 -57
- package/src/components/ScrollArea/ScrollArea.tsx +0 -27
- package/src/components/ScrollArea/__tests__/ScrollArea.test.tsx +0 -39
- package/src/components/SegmentedControl/SegmentedControl.module.css +0 -55
- package/src/components/SegmentedControl/SegmentedControl.stories.tsx +0 -118
- package/src/components/SegmentedControl/SegmentedControl.tsx +0 -56
- package/src/components/SegmentedControl/__tests__/SegmentedControl.test.tsx +0 -63
- package/src/components/Select/Select.module.css +0 -82
- package/src/components/Select/Select.stories.tsx +0 -115
- package/src/components/Select/Select.tsx +0 -48
- package/src/components/Select/__tests__/Select.test.tsx +0 -59
- package/src/components/Sheet/Sheet.module.css +0 -58
- package/src/components/Sheet/Sheet.tsx +0 -109
- package/src/components/Sheet/__tests__/Sheet.test.tsx +0 -99
- package/src/components/Slider/Slider.module.css +0 -95
- package/src/components/Slider/Slider.stories.tsx +0 -64
- package/src/components/Slider/Slider.tsx +0 -77
- package/src/components/Slider/__tests__/Slider.test.tsx +0 -45
- package/src/components/Spinner/Spinner.module.css +0 -39
- package/src/components/Spinner/Spinner.tsx +0 -23
- package/src/components/SplitMethodSelector/SplitMethodSelector.stories.tsx +0 -33
- package/src/components/SplitMethodSelector/SplitMethodSelector.tsx +0 -43
- package/src/components/SplitPane/SplitPane.module.css +0 -85
- package/src/components/SplitPane/SplitPane.stories.tsx +0 -160
- package/src/components/SplitPane/SplitPane.tsx +0 -149
- package/src/components/SplitPane/__tests__/SplitPane.test.tsx +0 -51
- package/src/components/StatusBadge/StatusBadge.module.css +0 -59
- package/src/components/StatusBadge/StatusBadge.stories.tsx +0 -74
- package/src/components/StatusBadge/StatusBadge.tsx +0 -65
- package/src/components/StepStrip/StepStrip.module.css +0 -53
- package/src/components/StepStrip/StepStrip.tsx +0 -74
- package/src/components/SuggestionInput/SuggestionInput.module.css +0 -103
- package/src/components/SuggestionInput/SuggestionInput.stories.tsx +0 -144
- package/src/components/SuggestionInput/SuggestionInput.tsx +0 -207
- package/src/components/SwipableMobileCarousel/SwipableMobileCarousel.module.css +0 -64
- package/src/components/SwipableMobileCarousel/SwipableMobileCarousel.tsx +0 -243
- package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlide.tsx +0 -35
- package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideBody.tsx +0 -42
- package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideFooter.tsx +0 -18
- package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideHeader.tsx +0 -21
- package/src/components/SwipableMobileCarousel/SwipableMobileCarouselSlideParts.module.css +0 -29
- package/src/components/SwipableMobileCarousel/__tests__/SwipableMobileCarousel.test.tsx +0 -214
- package/src/components/SwipableMobileCarousel/__tests__/SwipableMobileCarouselDevGuard.test.tsx +0 -61
- package/src/components/SwipableMobileCarousel/context.ts +0 -13
- package/src/components/SwipableMobileCarouselDots/SwipableMobileCarouselDots.module.css +0 -64
- package/src/components/SwipableMobileCarouselDots/SwipableMobileCarouselDots.tsx +0 -73
- package/src/components/SwipableMobileCarouselDots/__tests__/SwipableMobileCarouselDots.test.tsx +0 -63
- package/src/components/SwipeStack/SwipeStack.module.css +0 -101
- package/src/components/SwipeStack/SwipeStack.tsx +0 -199
- package/src/components/SwipeStack/SwipeStackCard.tsx +0 -22
- package/src/components/SwipeStack/__tests__/SwipeStack.test.tsx +0 -169
- package/src/components/SystemBanner/SystemBanner.module.css +0 -79
- package/src/components/SystemBanner/SystemBanner.stories.tsx +0 -86
- package/src/components/SystemBanner/SystemBanner.tsx +0 -50
- package/src/components/SystemBanner/__tests__/SystemBanner.test.tsx +0 -50
- package/src/components/Table/Table.module.css +0 -45
- package/src/components/Table/Table.stories.tsx +0 -125
- package/src/components/Table/Table.tsx +0 -51
- package/src/components/Table/__tests__/Table.test.tsx +0 -50
- package/src/components/Tabs/Tabs.module.css +0 -56
- package/src/components/Tabs/Tabs.stories.tsx +0 -100
- package/src/components/Tabs/Tabs.tsx +0 -46
- package/src/components/Tabs/__tests__/Tabs.test.tsx +0 -45
- package/src/components/TagInput/TagInput.module.css +0 -141
- package/src/components/TagInput/TagInput.stories.tsx +0 -109
- package/src/components/TagInput/TagInput.tsx +0 -174
- package/src/components/TagInput/__tests__/TagInput.test.tsx +0 -69
- package/src/components/Textarea/Textarea.module.css +0 -47
- package/src/components/Textarea/Textarea.tsx +0 -38
- package/src/components/Textarea/__tests__/Textarea.test.tsx +0 -51
- package/src/components/ThreeColumnLayout/ThreeColumnLayout.module.css +0 -35
- package/src/components/ThreeColumnLayout/ThreeColumnLayout.stories.tsx +0 -152
- package/src/components/ThreeColumnLayout/ThreeColumnLayout.tsx +0 -87
- package/src/components/ThreeColumnLayout/__tests__/ThreeColumnLayout.test.tsx +0 -78
- package/src/components/Toast/Toast.module.css +0 -191
- package/src/components/Toast/Toast.stories.tsx +0 -191
- package/src/components/Toast/Toast.tsx +0 -139
- package/src/components/Toggle/Toggle.module.css +0 -57
- package/src/components/Toggle/Toggle.stories.tsx +0 -95
- package/src/components/Toggle/Toggle.tsx +0 -39
- package/src/components/Toggle/__tests__/Toggle.test.tsx +0 -47
- package/src/components/Tooltip/Tooltip.module.css +0 -56
- package/src/components/Tooltip/Tooltip.tsx +0 -30
- package/src/components/Typography/Typography.module.css +0 -47
- package/src/components/Typography/Typography.stories.tsx +0 -62
- package/src/components/Typography/Typography.tsx +0 -30
- package/src/components/Typography/__tests__/Typography.test.tsx +0 -34
- package/src/components/UserMenu/UserMenu.module.css +0 -90
- package/src/components/UserMenu/UserMenu.stories.tsx +0 -49
- package/src/components/UserMenu/UserMenu.tsx +0 -132
- package/src/components/UserMenu/__tests__/UserMenu.test.tsx +0 -77
- package/src/css-modules.d.ts +0 -4
- package/src/hooks/__tests__/useCarouselRouteSync.test.tsx +0 -126
- package/src/hooks/__tests__/useDoubleTap.test.tsx +0 -117
- package/src/hooks/__tests__/useIsMobile.test.tsx +0 -80
- package/src/hooks/__tests__/useIsOffline.test.tsx +0 -53
- package/src/hooks/__tests__/useLongPress.test.tsx +0 -158
- package/src/hooks/__tests__/useOfflineTileState.test.tsx +0 -69
- package/src/hooks/__tests__/usePublishShellChromeHeight.test.tsx +0 -118
- package/src/hooks/__tests__/useReorderSensors.test.ts +0 -26
- package/src/hooks/__tests__/useResponsiveLayout.test.tsx +0 -87
- package/src/hooks/__tests__/useSnapCarousel.test.tsx +0 -175
- package/src/hooks/__tests__/useSwipeReveal.test.tsx +0 -110
- package/src/hooks/__tests__/useSwipeStack.test.tsx +0 -155
- package/src/hooks/index.ts +0 -33
- package/src/hooks/useCarouselRouteSync.ts +0 -92
- package/src/hooks/useCommitOnEnterOrBlur.ts +0 -39
- package/src/hooks/useDoubleTap.ts +0 -91
- package/src/hooks/useIsMobile.ts +0 -33
- package/src/hooks/useIsOffline.ts +0 -41
- package/src/hooks/useLongPress.ts +0 -185
- package/src/hooks/useOfflineTileState.ts +0 -45
- package/src/hooks/usePublishShellChromeHeight.ts +0 -96
- package/src/hooks/useReorderSensors.ts +0 -88
- package/src/hooks/useResponsiveLayout.ts +0 -36
- package/src/hooks/useSnapCarousel.ts +0 -284
- package/src/hooks/useSwipeReveal.ts +0 -107
- package/src/hooks/useSwipeStack.ts +0 -235
- package/src/index.ts +0 -311
- package/src/motion.ts +0 -78
- package/src/overlay-shell.ts +0 -159
- package/src/scroll-lock.ts +0 -22
- package/src/stories/Avatar.stories.tsx +0 -44
- package/src/stories/Card.stories.tsx +0 -35
- package/src/stories/Checkbox.stories.tsx +0 -45
- package/src/stories/DesignSystemOverview.stories.tsx +0 -3793
- package/src/stories/DeviceOnlyGate.stories.tsx +0 -35
- package/src/stories/DeviceStorageKeyGate.stories.tsx +0 -57
- package/src/stories/DragHandleRow.stories.tsx +0 -41
- package/src/stories/EmptyState.stories.tsx +0 -33
- package/src/stories/FileDropzone.stories.tsx +0 -69
- package/src/stories/FormField.stories.tsx +0 -47
- package/src/stories/GripIcon.stories.tsx +0 -29
- package/src/stories/InteractionHooks.stories.tsx +0 -925
- package/src/stories/LayoutSystem.stories.tsx +0 -639
- package/src/stories/MenuEntries.stories.tsx +0 -86
- package/src/stories/MobilePatterns.stories.tsx +0 -1536
- package/src/stories/NavList.stories.tsx +0 -147
- package/src/stories/NavTabs.stories.tsx +0 -53
- package/src/stories/OfflineGate.stories.tsx +0 -42
- package/src/stories/PageContainer.stories.tsx +0 -82
- package/src/stories/PageHeader.stories.tsx +0 -95
- package/src/stories/ResponsiveSurface.stories.tsx +0 -47
- package/src/stories/Spinner.stories.tsx +0 -25
- package/src/stories/StepStrip.stories.tsx +0 -76
- package/src/stories/SwipableMobileCarousel.stories.tsx +0 -362
- package/src/stories/SwipeStack.stories.tsx +0 -200
- package/src/stories/Textarea.stories.tsx +0 -63
- package/src/stories/TokenGallery.stories.tsx +0 -652
- package/src/stories/Tooltip.stories.tsx +0 -59
- package/src/tokens/primitives.css +0 -168
- package/src/tokens/semantic.css +0 -162
- package/src/tokens/theme-presets.css +0 -33
- package/src/tokens/theme-presets.ts +0 -72
- package/src/tokens.css +0 -5
|
@@ -1,3793 +0,0 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
3
|
-
|
|
4
|
-
import { Avatar } from '../components/Avatar/Avatar';
|
|
5
|
-
import { Badge } from '../components/Badge/Badge';
|
|
6
|
-
import { Button } from '../components/Button/Button';
|
|
7
|
-
import { Card } from '../components/Card/Card';
|
|
8
|
-
import { CardTile, CardTileGrid, NewCardTile } from '../components/CardTile/CardTile';
|
|
9
|
-
import { Dialog } from '../components/Dialog/Dialog';
|
|
10
|
-
import { Drawer } from '../components/Drawer/Drawer';
|
|
11
|
-
import { EmptyState } from '../components/EmptyState/EmptyState';
|
|
12
|
-
import { OfflineGate } from '../components/OfflineGate/OfflineGate';
|
|
13
|
-
import { DeviceOnlyGate } from '../components/DeviceOnlyGate/DeviceOnlyGate';
|
|
14
|
-
import { DeviceStorageKeyGate } from '../components/DeviceStorageKeyGate/DeviceStorageKeyGate';
|
|
15
|
-
import { FormField } from '../components/FormField/FormField';
|
|
16
|
-
import { Icon } from '../components/Icon/Icon';
|
|
17
|
-
import type { IconName } from '../components/Icon/Icon';
|
|
18
|
-
import { IconPicker } from '../components/IconPicker/IconPicker';
|
|
19
|
-
import { ColorPicker } from '../components/ColorPicker/ColorPicker';
|
|
20
|
-
import { QuantityStepper } from '../components/QuantityStepper/QuantityStepper';
|
|
21
|
-
import { CheckableListRow } from '../components/CheckableListRow/CheckableListRow';
|
|
22
|
-
import { Input } from '../components/Input/Input';
|
|
23
|
-
import { CodeTextarea } from '../components/CodeTextarea/CodeTextarea';
|
|
24
|
-
import { Textarea } from '../components/Textarea/Textarea';
|
|
25
|
-
import { StatusBadge } from '../components/StatusBadge/StatusBadge';
|
|
26
|
-
import { SplitPane } from '../components/SplitPane/SplitPane';
|
|
27
|
-
import { ThreeColumnLayout } from '../components/ThreeColumnLayout/ThreeColumnLayout';
|
|
28
|
-
import { HeaderFooterLayout } from '../components/HeaderFooterLayout/HeaderFooterLayout';
|
|
29
|
-
import { RootLayout } from '../components/RootLayout/RootLayout';
|
|
30
|
-
import { PageLayout } from '../components/PageLayout/PageLayout';
|
|
31
|
-
import { TagInput } from '../components/TagInput/TagInput';
|
|
32
|
-
import { SuggestionInput } from '../components/SuggestionInput/SuggestionInput';
|
|
33
|
-
import { AppsLauncher } from '../components/AppsLauncher/AppsLauncher';
|
|
34
|
-
import { Header } from '../components/Header/Header';
|
|
35
|
-
import { MobileAppsDrawer } from '../components/MobileAppsDrawer/MobileAppsDrawer';
|
|
36
|
-
import { MobileFooter } from '../components/MobileFooter/MobileFooter';
|
|
37
|
-
import { MobileHeader } from '../components/MobileHeader/MobileHeader';
|
|
38
|
-
import { NavList } from '../components/NavList/NavList';
|
|
39
|
-
import { NavTabs } from '../components/NavTabs/NavTabs';
|
|
40
|
-
import { NotificationsPanel } from '../components/NotificationsPanel/NotificationsPanel';
|
|
41
|
-
import { PageHeader } from '../components/PageHeader/PageHeader';
|
|
42
|
-
import { PageContainer } from '../components/PageContainer/PageContainer';
|
|
43
|
-
import { Popover } from '../components/Popover/Popover';
|
|
44
|
-
import { UserMenu } from '../components/UserMenu/UserMenu';
|
|
45
|
-
import { ResponsiveSurface } from '../components/ResponsiveSurface/ResponsiveSurface';
|
|
46
|
-
import { SwipableMobileCarousel } from '../components/SwipableMobileCarousel/SwipableMobileCarousel';
|
|
47
|
-
import { SwipableMobileCarouselSlide } from '../components/SwipableMobileCarousel/SwipableMobileCarouselSlide';
|
|
48
|
-
import { SwipableMobileCarouselSlideHeader } from '../components/SwipableMobileCarousel/SwipableMobileCarouselSlideHeader';
|
|
49
|
-
import { SwipableMobileCarouselSlideBody } from '../components/SwipableMobileCarousel/SwipableMobileCarouselSlideBody';
|
|
50
|
-
import { SwipableMobileCarouselDots } from '../components/SwipableMobileCarouselDots/SwipableMobileCarouselDots';
|
|
51
|
-
import { SwipeStack } from '../components/SwipeStack/SwipeStack';
|
|
52
|
-
import { SwipeStackCard } from '../components/SwipeStack/SwipeStackCard';
|
|
53
|
-
import { SegmentedControl } from '../components/SegmentedControl/SegmentedControl';
|
|
54
|
-
import { Select } from '../components/Select/Select';
|
|
55
|
-
import { Spinner } from '../components/Spinner/Spinner';
|
|
56
|
-
import { SystemBanner } from '../components/SystemBanner/SystemBanner';
|
|
57
|
-
import { Tabs } from '../components/Tabs/Tabs';
|
|
58
|
-
import { ToastProvider, useToast } from '../components/Toast/Toast';
|
|
59
|
-
import { Toggle } from '../components/Toggle/Toggle';
|
|
60
|
-
import { Tooltip } from '../components/Tooltip/Tooltip';
|
|
61
|
-
import { Checkbox } from '../components/Checkbox/Checkbox';
|
|
62
|
-
import { RadioGroup } from '../components/RadioGroup/RadioGroup';
|
|
63
|
-
import { Slider } from '../components/Slider/Slider';
|
|
64
|
-
import { Progress } from '../components/Progress/Progress';
|
|
65
|
-
import {
|
|
66
|
-
Table,
|
|
67
|
-
TableHead,
|
|
68
|
-
TableBody,
|
|
69
|
-
TableRow,
|
|
70
|
-
TableHeaderCell,
|
|
71
|
-
TableCell,
|
|
72
|
-
} from '../components/Table/Table';
|
|
73
|
-
import { Alert } from '../components/Alert/Alert';
|
|
74
|
-
import { Breadcrumb } from '../components/Breadcrumb/Breadcrumb';
|
|
75
|
-
import { LegalLinks } from '../components/LegalLinks/LegalLinks';
|
|
76
|
-
import { ConsentPrompt } from '../components/ConsentPrompt/ConsentPrompt';
|
|
77
|
-
import { Markdown } from '../components/Markdown/Markdown';
|
|
78
|
-
import { Pagination } from '../components/Pagination/Pagination';
|
|
79
|
-
import { Kbd } from '../components/Kbd/Kbd';
|
|
80
|
-
import { Accordion } from '../components/Accordion/Accordion';
|
|
81
|
-
import { AspectRatio } from '../components/AspectRatio/AspectRatio';
|
|
82
|
-
import { ButtonGroup } from '../components/ButtonGroup/ButtonGroup';
|
|
83
|
-
import { Item } from '../components/Item/Item';
|
|
84
|
-
import { Label } from '../components/Label/Label';
|
|
85
|
-
import { ScrollArea } from '../components/ScrollArea/ScrollArea';
|
|
86
|
-
import { Typography } from '../components/Typography/Typography';
|
|
87
|
-
import { Marker } from '../components/Marker/Marker';
|
|
88
|
-
import { Message } from '../components/Message/Message';
|
|
89
|
-
import { MessageScroller } from '../components/MessageScroller/MessageScroller';
|
|
90
|
-
import { HoverCard } from '../components/HoverCard/HoverCard';
|
|
91
|
-
import { ContextMenu } from '../components/ContextMenu/ContextMenu';
|
|
92
|
-
import { Command } from '../components/Command/Command';
|
|
93
|
-
import {
|
|
94
|
-
ResizableHandle,
|
|
95
|
-
ResizablePanel,
|
|
96
|
-
ResizablePanelGroup,
|
|
97
|
-
} from '../components/Resizable/Resizable';
|
|
98
|
-
import { DataTable } from '../components/DataTable/DataTable';
|
|
99
|
-
import { Combobox } from '../components/Combobox/Combobox';
|
|
100
|
-
import { NavigationMenu } from '../components/NavigationMenu/NavigationMenu';
|
|
101
|
-
import { Menubar } from '../components/Menubar/Menubar';
|
|
102
|
-
import { Collapsible } from '../components/Collapsible/Collapsible';
|
|
103
|
-
import { DragHandleRow } from '../components/DragHandleRow/DragHandleRow';
|
|
104
|
-
import { GripIcon } from '../components/GripIcon/GripIcon';
|
|
105
|
-
import { StepStrip, type StepStripItem } from '../components/StepStrip/StepStrip';
|
|
106
|
-
import { FileDropzone } from '../components/FileDropzone/FileDropzone';
|
|
107
|
-
import { OverlayHeader } from '../components/OverlayHeader/OverlayHeader';
|
|
108
|
-
import { Sheet } from '../components/Sheet/Sheet';
|
|
109
|
-
import { ConfirmDialog } from '../components/ConfirmDialog/ConfirmDialog';
|
|
110
|
-
import { Menu, MenuEntries } from '../components/Menu/Menu';
|
|
111
|
-
import { Calendar } from '../components/Calendar/Calendar';
|
|
112
|
-
import { DatePicker } from '../components/DatePicker/DatePicker';
|
|
113
|
-
import { CurrencyInput } from '../components/CurrencyInput/CurrencyInput';
|
|
114
|
-
import { BalanceChip } from '../components/BalanceChip/BalanceChip';
|
|
115
|
-
import {
|
|
116
|
-
SplitMethodSelector,
|
|
117
|
-
type SplitMethod,
|
|
118
|
-
} from '../components/SplitMethodSelector/SplitMethodSelector';
|
|
119
|
-
import {
|
|
120
|
-
MemberMultiSelect,
|
|
121
|
-
type MemberMultiSelectOption,
|
|
122
|
-
} from '../components/MemberMultiSelect/MemberMultiSelect';
|
|
123
|
-
|
|
124
|
-
// ---------------------------------------------------------------------------
|
|
125
|
-
// Shared primitives
|
|
126
|
-
// ---------------------------------------------------------------------------
|
|
127
|
-
|
|
128
|
-
const ff = 'var(--sv-font-family)';
|
|
129
|
-
const ffm = 'var(--sv-font-family-mono)';
|
|
130
|
-
|
|
131
|
-
function Heading({ level = 2, children }: { level?: 1 | 2 | 3; children: React.ReactNode }) {
|
|
132
|
-
const sizes: Record<number, string> = { 1: '2rem', 2: '1.25rem', 3: '1rem' };
|
|
133
|
-
const weights: Record<number, number> = { 1: 700, 2: 600, 3: 600 };
|
|
134
|
-
const Tag = `h${level}` as 'h1' | 'h2' | 'h3';
|
|
135
|
-
return (
|
|
136
|
-
<Tag
|
|
137
|
-
style={{
|
|
138
|
-
fontFamily: ff,
|
|
139
|
-
fontSize: sizes[level],
|
|
140
|
-
fontWeight: weights[level],
|
|
141
|
-
color: 'var(--sv-color-text-primary)',
|
|
142
|
-
margin: 0,
|
|
143
|
-
lineHeight: 1.2,
|
|
144
|
-
}}
|
|
145
|
-
>
|
|
146
|
-
{children}
|
|
147
|
-
</Tag>
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function SectionHeader({ title, subtitle }: { title: string; subtitle?: string }) {
|
|
152
|
-
return (
|
|
153
|
-
<div
|
|
154
|
-
style={{
|
|
155
|
-
paddingBottom: 'var(--sv-space-3)',
|
|
156
|
-
borderBottom: '2px solid var(--sv-color-accent)',
|
|
157
|
-
marginBottom: 'var(--sv-space-5)',
|
|
158
|
-
}}
|
|
159
|
-
>
|
|
160
|
-
<Heading level={2}>{title}</Heading>
|
|
161
|
-
{subtitle && (
|
|
162
|
-
<p
|
|
163
|
-
style={{
|
|
164
|
-
fontFamily: ff,
|
|
165
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
166
|
-
color: 'var(--sv-color-text-muted)',
|
|
167
|
-
marginTop: 'var(--sv-space-1)',
|
|
168
|
-
}}
|
|
169
|
-
>
|
|
170
|
-
{subtitle}
|
|
171
|
-
</p>
|
|
172
|
-
)}
|
|
173
|
-
</div>
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function Code({ children }: { children: string }) {
|
|
178
|
-
return (
|
|
179
|
-
<pre
|
|
180
|
-
style={{
|
|
181
|
-
fontFamily: ffm,
|
|
182
|
-
fontSize: 'var(--sv-font-size-xs)',
|
|
183
|
-
background: 'var(--sv-color-surface-sunken)',
|
|
184
|
-
border: '1px solid var(--sv-color-border)',
|
|
185
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
186
|
-
padding: 'var(--sv-space-3) var(--sv-space-4)',
|
|
187
|
-
color: 'var(--sv-color-text-primary)',
|
|
188
|
-
overflowX: 'auto',
|
|
189
|
-
margin: 0,
|
|
190
|
-
lineHeight: 1.6,
|
|
191
|
-
whiteSpace: 'pre',
|
|
192
|
-
}}
|
|
193
|
-
>
|
|
194
|
-
{children}
|
|
195
|
-
</pre>
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function DemoBox({ children, style }: { children: React.ReactNode; style?: React.CSSProperties }) {
|
|
200
|
-
return (
|
|
201
|
-
<div
|
|
202
|
-
style={{
|
|
203
|
-
background: 'var(--sv-color-surface-sunken)',
|
|
204
|
-
border: '1px solid var(--sv-color-border)',
|
|
205
|
-
borderRadius: 'var(--sv-radius-lg)',
|
|
206
|
-
padding: 'var(--sv-space-4)',
|
|
207
|
-
display: 'flex',
|
|
208
|
-
alignItems: 'center',
|
|
209
|
-
justifyContent: 'center',
|
|
210
|
-
gap: 'var(--sv-space-3)',
|
|
211
|
-
flexWrap: 'wrap',
|
|
212
|
-
minHeight: 80,
|
|
213
|
-
...style,
|
|
214
|
-
}}
|
|
215
|
-
>
|
|
216
|
-
{children}
|
|
217
|
-
</div>
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function Pill({ children }: { children: React.ReactNode }) {
|
|
222
|
-
return (
|
|
223
|
-
<span
|
|
224
|
-
style={{
|
|
225
|
-
display: 'inline-block',
|
|
226
|
-
fontFamily: ffm,
|
|
227
|
-
fontSize: '0.6875rem',
|
|
228
|
-
fontWeight: 600,
|
|
229
|
-
letterSpacing: '0.03em',
|
|
230
|
-
background: 'var(--sv-color-surface-sunken)',
|
|
231
|
-
border: '1px solid var(--sv-color-border)',
|
|
232
|
-
color: 'var(--sv-color-text-muted)',
|
|
233
|
-
borderRadius: 'var(--sv-radius-full)',
|
|
234
|
-
padding: '2px 10px',
|
|
235
|
-
}}
|
|
236
|
-
>
|
|
237
|
-
{children}
|
|
238
|
-
</span>
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// ---------------------------------------------------------------------------
|
|
243
|
-
// Color groups
|
|
244
|
-
// ---------------------------------------------------------------------------
|
|
245
|
-
|
|
246
|
-
const COLOR_GROUPS: Array<{
|
|
247
|
-
label: string;
|
|
248
|
-
usage: string;
|
|
249
|
-
tokens: Array<{ token: string; name: string }>;
|
|
250
|
-
}> = [
|
|
251
|
-
{
|
|
252
|
-
label: 'Surface',
|
|
253
|
-
usage: 'Page backgrounds and card elevations.',
|
|
254
|
-
tokens: [
|
|
255
|
-
{ token: '--sv-color-surface', name: 'surface' },
|
|
256
|
-
{ token: '--sv-color-surface-sunken', name: 'surface-sunken' },
|
|
257
|
-
{ token: '--sv-color-surface-raised', name: 'surface-raised' },
|
|
258
|
-
],
|
|
259
|
-
},
|
|
260
|
-
{
|
|
261
|
-
label: 'Text',
|
|
262
|
-
usage: 'Copy hierarchy from primary body to disabled hints.',
|
|
263
|
-
tokens: [
|
|
264
|
-
{ token: '--sv-color-text-primary', name: 'text-primary' },
|
|
265
|
-
{ token: '--sv-color-text-muted', name: 'text-muted' },
|
|
266
|
-
{ token: '--sv-color-text-subtle', name: 'text-subtle' },
|
|
267
|
-
{ token: '--sv-color-text-on-accent', name: 'text-on-accent' },
|
|
268
|
-
{ token: '--sv-color-text-on-error', name: 'text-on-error' },
|
|
269
|
-
{ token: '--sv-color-text-on-success', name: 'text-on-success' },
|
|
270
|
-
{ token: '--sv-color-text-on-light-fill', name: 'text-on-light-fill' },
|
|
271
|
-
{ token: '--sv-color-text-on-dark-fill', name: 'text-on-dark-fill' },
|
|
272
|
-
],
|
|
273
|
-
},
|
|
274
|
-
{
|
|
275
|
-
label: 'Border',
|
|
276
|
-
usage: 'Dividers, input outlines, and card edges.',
|
|
277
|
-
tokens: [
|
|
278
|
-
{ token: '--sv-color-border', name: 'border' },
|
|
279
|
-
{ token: '--sv-color-border-strong', name: 'border-strong' },
|
|
280
|
-
],
|
|
281
|
-
},
|
|
282
|
-
{
|
|
283
|
-
label: 'Shadow',
|
|
284
|
-
usage:
|
|
285
|
-
'High-contrast shadow color for theme presets with hard offset shadows (RFC 0094). Unused by the default theme.',
|
|
286
|
-
tokens: [{ token: '--sv-color-shadow-strong', name: 'shadow-strong' }],
|
|
287
|
-
},
|
|
288
|
-
{
|
|
289
|
-
label: 'Accent',
|
|
290
|
-
usage:
|
|
291
|
-
'Primary interactive color. Monochrome by default; instance admins override with their brand color.',
|
|
292
|
-
tokens: [
|
|
293
|
-
{ token: '--sv-color-accent', name: 'accent' },
|
|
294
|
-
{ token: '--sv-color-accent-hover', name: 'accent-hover' },
|
|
295
|
-
{ token: '--sv-color-focus-ring', name: 'focus-ring' },
|
|
296
|
-
],
|
|
297
|
-
},
|
|
298
|
-
{
|
|
299
|
-
label: 'Error',
|
|
300
|
-
usage: 'Destructive states, form validation errors, critical banners.',
|
|
301
|
-
tokens: [
|
|
302
|
-
{ token: '--sv-color-error-surface', name: 'error-surface' },
|
|
303
|
-
{ token: '--sv-color-error-text', name: 'error-text' },
|
|
304
|
-
{ token: '--sv-color-error-border', name: 'error-border' },
|
|
305
|
-
{ token: '--sv-color-error-solid', name: 'error-solid' },
|
|
306
|
-
],
|
|
307
|
-
},
|
|
308
|
-
{
|
|
309
|
-
label: 'Warning',
|
|
310
|
-
usage: 'Caution states, expiring licenses, near-limit notices.',
|
|
311
|
-
tokens: [
|
|
312
|
-
{ token: '--sv-color-warning-surface', name: 'warning-surface' },
|
|
313
|
-
{ token: '--sv-color-warning-text', name: 'warning-text' },
|
|
314
|
-
{ token: '--sv-color-warning-border', name: 'warning-border' },
|
|
315
|
-
],
|
|
316
|
-
},
|
|
317
|
-
{
|
|
318
|
-
label: 'Success',
|
|
319
|
-
usage: 'Positive confirmations, completed actions, healthy status.',
|
|
320
|
-
tokens: [
|
|
321
|
-
{ token: '--sv-color-success-surface', name: 'success-surface' },
|
|
322
|
-
{ token: '--sv-color-success-text', name: 'success-text' },
|
|
323
|
-
{ token: '--sv-color-success-border', name: 'success-border' },
|
|
324
|
-
{ token: '--sv-color-success-solid', name: 'success-solid' },
|
|
325
|
-
],
|
|
326
|
-
},
|
|
327
|
-
{
|
|
328
|
-
label: 'Info',
|
|
329
|
-
usage: 'Informational notices and neutral callouts.',
|
|
330
|
-
tokens: [
|
|
331
|
-
{ token: '--sv-color-info-surface', name: 'info-surface' },
|
|
332
|
-
{ token: '--sv-color-info-text', name: 'info-text' },
|
|
333
|
-
{ token: '--sv-color-info-border', name: 'info-border' },
|
|
334
|
-
],
|
|
335
|
-
},
|
|
336
|
-
];
|
|
337
|
-
|
|
338
|
-
function ColorSwatch({ token, name }: { token: string; name: string }) {
|
|
339
|
-
const value =
|
|
340
|
-
typeof window !== 'undefined'
|
|
341
|
-
? getComputedStyle(document.documentElement).getPropertyValue(token).trim()
|
|
342
|
-
: '';
|
|
343
|
-
return (
|
|
344
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 6, flex: '1 1 160px' }}>
|
|
345
|
-
<div
|
|
346
|
-
style={{
|
|
347
|
-
height: 44,
|
|
348
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
349
|
-
background: `var(${token})`,
|
|
350
|
-
border: '1px solid var(--sv-color-border)',
|
|
351
|
-
}}
|
|
352
|
-
/>
|
|
353
|
-
<div>
|
|
354
|
-
<p
|
|
355
|
-
style={{
|
|
356
|
-
fontFamily: ffm,
|
|
357
|
-
fontSize: '0.6875rem',
|
|
358
|
-
color: 'var(--sv-color-text-primary)',
|
|
359
|
-
margin: 0,
|
|
360
|
-
}}
|
|
361
|
-
>
|
|
362
|
-
--sv-color-{name}
|
|
363
|
-
</p>
|
|
364
|
-
<p
|
|
365
|
-
style={{
|
|
366
|
-
fontFamily: ffm,
|
|
367
|
-
fontSize: '0.6875rem',
|
|
368
|
-
color: 'var(--sv-color-text-muted)',
|
|
369
|
-
margin: 0,
|
|
370
|
-
}}
|
|
371
|
-
>
|
|
372
|
-
{value}
|
|
373
|
-
</p>
|
|
374
|
-
</div>
|
|
375
|
-
</div>
|
|
376
|
-
);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
function ColorGroup({
|
|
380
|
-
label,
|
|
381
|
-
usage,
|
|
382
|
-
tokens,
|
|
383
|
-
}: {
|
|
384
|
-
label: string;
|
|
385
|
-
usage: string;
|
|
386
|
-
tokens: Array<{ token: string; name: string }>;
|
|
387
|
-
}) {
|
|
388
|
-
return (
|
|
389
|
-
<div style={{ marginBottom: 'var(--sv-space-8)' }}>
|
|
390
|
-
<div style={{ marginBottom: 'var(--sv-space-3)' }}>
|
|
391
|
-
<Heading level={3}>{label}</Heading>
|
|
392
|
-
<p
|
|
393
|
-
style={{
|
|
394
|
-
fontFamily: ff,
|
|
395
|
-
fontSize: 'var(--sv-font-size-caption)',
|
|
396
|
-
color: 'var(--sv-color-text-muted)',
|
|
397
|
-
margin: '4px 0 0',
|
|
398
|
-
}}
|
|
399
|
-
>
|
|
400
|
-
{usage}
|
|
401
|
-
</p>
|
|
402
|
-
</div>
|
|
403
|
-
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 'var(--sv-space-4)' }}>
|
|
404
|
-
{tokens.map((t) => (
|
|
405
|
-
<ColorSwatch key={t.token} token={t.token} name={t.name} />
|
|
406
|
-
))}
|
|
407
|
-
</div>
|
|
408
|
-
</div>
|
|
409
|
-
);
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// ---------------------------------------------------------------------------
|
|
413
|
-
// Typography
|
|
414
|
-
// ---------------------------------------------------------------------------
|
|
415
|
-
|
|
416
|
-
const TYPE_SCALE: Array<{ token: string; label: string; note: string }> = [
|
|
417
|
-
{ token: '--sv-font-size-3xl', label: '3xl — 32px', note: 'Document title style' },
|
|
418
|
-
{ token: '--sv-font-size-2xl', label: '2xl — 24px', note: 'Page titles' },
|
|
419
|
-
{ token: '--sv-font-size-xl', label: 'xl — 20px', note: 'Section headings' },
|
|
420
|
-
{ token: '--sv-font-size-lg', label: 'lg — 18px', note: 'Sub-headings' },
|
|
421
|
-
{ token: '--sv-font-size-md', label: 'md — 16px', note: 'Body (base)' },
|
|
422
|
-
{ token: '--sv-font-size-sm', label: 'sm — 14px', note: 'Body copy, labels' },
|
|
423
|
-
{ token: '--sv-font-size-caption', label: 'caption — 13px', note: 'Secondary / supporting copy' },
|
|
424
|
-
{ token: '--sv-font-size-xs', label: 'xs — 12px', note: 'Mono identifiers, badges' },
|
|
425
|
-
{ token: '--sv-font-size-label', label: 'label — 11px', note: 'All-caps section labels' },
|
|
426
|
-
{
|
|
427
|
-
token: '--sv-font-size-2xs',
|
|
428
|
-
label: '2xs — 10px',
|
|
429
|
-
note: 'Badge size="xs" — smallest badge tier',
|
|
430
|
-
},
|
|
431
|
-
];
|
|
432
|
-
|
|
433
|
-
// ---------------------------------------------------------------------------
|
|
434
|
-
// Component cards
|
|
435
|
-
// ---------------------------------------------------------------------------
|
|
436
|
-
|
|
437
|
-
function ComponentCard({
|
|
438
|
-
name,
|
|
439
|
-
importLine,
|
|
440
|
-
usage,
|
|
441
|
-
children,
|
|
442
|
-
}: {
|
|
443
|
-
name: string;
|
|
444
|
-
importLine: string;
|
|
445
|
-
usage: string;
|
|
446
|
-
children: React.ReactNode;
|
|
447
|
-
}) {
|
|
448
|
-
return (
|
|
449
|
-
<div
|
|
450
|
-
style={{
|
|
451
|
-
border: '1px solid var(--sv-color-border)',
|
|
452
|
-
borderRadius: 'var(--sv-radius-xl)',
|
|
453
|
-
overflow: 'hidden',
|
|
454
|
-
background: 'var(--sv-color-surface)',
|
|
455
|
-
boxShadow: 'var(--sv-shadow-card)',
|
|
456
|
-
display: 'flex',
|
|
457
|
-
flexDirection: 'column',
|
|
458
|
-
}}
|
|
459
|
-
>
|
|
460
|
-
{/* Header */}
|
|
461
|
-
<div
|
|
462
|
-
style={{
|
|
463
|
-
padding: 'var(--sv-space-4)',
|
|
464
|
-
borderBottom: '1px solid var(--sv-color-border)',
|
|
465
|
-
background: 'var(--sv-color-surface-sunken)',
|
|
466
|
-
}}
|
|
467
|
-
>
|
|
468
|
-
<Heading level={3}>{name}</Heading>
|
|
469
|
-
<p
|
|
470
|
-
style={{
|
|
471
|
-
fontFamily: ff,
|
|
472
|
-
fontSize: 'var(--sv-font-size-caption)',
|
|
473
|
-
color: 'var(--sv-color-text-muted)',
|
|
474
|
-
margin: '4px 0 0',
|
|
475
|
-
}}
|
|
476
|
-
>
|
|
477
|
-
{usage}
|
|
478
|
-
</p>
|
|
479
|
-
</div>
|
|
480
|
-
{/* Demo */}
|
|
481
|
-
<div
|
|
482
|
-
style={{
|
|
483
|
-
padding: 'var(--sv-space-5)',
|
|
484
|
-
display: 'flex',
|
|
485
|
-
alignItems: 'center',
|
|
486
|
-
justifyContent: 'center',
|
|
487
|
-
minHeight: 96,
|
|
488
|
-
gap: 'var(--sv-space-3)',
|
|
489
|
-
flexWrap: 'wrap',
|
|
490
|
-
flexGrow: 1,
|
|
491
|
-
}}
|
|
492
|
-
>
|
|
493
|
-
{children}
|
|
494
|
-
</div>
|
|
495
|
-
{/* Import */}
|
|
496
|
-
<div style={{ padding: 'var(--sv-space-3) var(--sv-space-4)' }}>
|
|
497
|
-
<code
|
|
498
|
-
style={{
|
|
499
|
-
fontFamily: ffm,
|
|
500
|
-
fontSize: '0.6875rem',
|
|
501
|
-
color: 'var(--sv-color-text-muted)',
|
|
502
|
-
display: 'block',
|
|
503
|
-
}}
|
|
504
|
-
>
|
|
505
|
-
{importLine}
|
|
506
|
-
</code>
|
|
507
|
-
</div>
|
|
508
|
-
</div>
|
|
509
|
-
);
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
// Interactive wrappers
|
|
513
|
-
|
|
514
|
-
function ToggleDemo() {
|
|
515
|
-
const [on, setOn] = useState(false);
|
|
516
|
-
return <Toggle checked={on} onChange={setOn} aria-label="Enable feature" />;
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
function SegmentedDemo() {
|
|
520
|
-
const [v, setV] = useState<'user' | 'admin'>('user');
|
|
521
|
-
return (
|
|
522
|
-
<SegmentedControl
|
|
523
|
-
value={v}
|
|
524
|
-
onChange={setV}
|
|
525
|
-
options={[
|
|
526
|
-
{ label: 'User', value: 'user' },
|
|
527
|
-
{ label: 'Admin', value: 'admin' },
|
|
528
|
-
]}
|
|
529
|
-
aria-label="Role"
|
|
530
|
-
/>
|
|
531
|
-
);
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
function TabsDemo() {
|
|
535
|
-
const [tab, setTab] = useState('profile');
|
|
536
|
-
return (
|
|
537
|
-
<div style={{ width: '100%', fontFamily: ff }}>
|
|
538
|
-
<Tabs
|
|
539
|
-
items={[
|
|
540
|
-
{ label: 'Profile', value: 'profile' },
|
|
541
|
-
{ label: 'Security', value: 'security' },
|
|
542
|
-
{ label: 'Data', value: 'data' },
|
|
543
|
-
]}
|
|
544
|
-
value={tab}
|
|
545
|
-
onChange={setTab}
|
|
546
|
-
aria-label="Account"
|
|
547
|
-
/>
|
|
548
|
-
</div>
|
|
549
|
-
);
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
function CommandDemo() {
|
|
553
|
-
const [open, setOpen] = useState(false);
|
|
554
|
-
return (
|
|
555
|
-
<>
|
|
556
|
-
<Button size="sm" onClick={() => setOpen(true)}>
|
|
557
|
-
Open command palette
|
|
558
|
-
</Button>
|
|
559
|
-
<Command
|
|
560
|
-
open={open}
|
|
561
|
-
onClose={() => setOpen(false)}
|
|
562
|
-
aria-label="Command palette"
|
|
563
|
-
items={[
|
|
564
|
-
{
|
|
565
|
-
id: 'new',
|
|
566
|
-
label: 'New conversation',
|
|
567
|
-
group: 'Actions',
|
|
568
|
-
icon: 'plus',
|
|
569
|
-
onSelect: () => {},
|
|
570
|
-
},
|
|
571
|
-
{
|
|
572
|
-
id: 'export',
|
|
573
|
-
label: 'Export chat',
|
|
574
|
-
group: 'Actions',
|
|
575
|
-
icon: 'upload',
|
|
576
|
-
onSelect: () => {},
|
|
577
|
-
},
|
|
578
|
-
{
|
|
579
|
-
id: 'profile',
|
|
580
|
-
label: 'Go to profile',
|
|
581
|
-
group: 'Navigation',
|
|
582
|
-
icon: 'user',
|
|
583
|
-
onSelect: () => {},
|
|
584
|
-
},
|
|
585
|
-
]}
|
|
586
|
-
/>
|
|
587
|
-
</>
|
|
588
|
-
);
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
function ComboboxDemo() {
|
|
592
|
-
const [value, setValue] = useState<string | null>(null);
|
|
593
|
-
return (
|
|
594
|
-
<div style={{ width: 240 }}>
|
|
595
|
-
<Combobox
|
|
596
|
-
options={[
|
|
597
|
-
{ value: 'tasks', label: 'sovereign-tasks' },
|
|
598
|
-
{ value: 'ledger', label: 'sovereign-ledger' },
|
|
599
|
-
{ value: 'shopper', label: 'sovereign-shopper' },
|
|
600
|
-
]}
|
|
601
|
-
value={value}
|
|
602
|
-
onChange={setValue}
|
|
603
|
-
placeholder="Select a plugin"
|
|
604
|
-
aria-label="Plugin"
|
|
605
|
-
/>
|
|
606
|
-
</div>
|
|
607
|
-
);
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
function DialogDemo() {
|
|
611
|
-
const [open, setOpen] = useState(false);
|
|
612
|
-
return (
|
|
613
|
-
<>
|
|
614
|
-
<Button size="sm" onClick={() => setOpen(true)}>
|
|
615
|
-
Open dialog
|
|
616
|
-
</Button>
|
|
617
|
-
<Dialog open={open} onClose={() => setOpen(false)} size="sm" aria-label="Example dialog">
|
|
618
|
-
<div style={{ padding: 24, fontFamily: ff }}>
|
|
619
|
-
<Heading level={3}>Confirm action</Heading>
|
|
620
|
-
<p
|
|
621
|
-
style={{
|
|
622
|
-
color: 'var(--sv-color-text-muted)',
|
|
623
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
624
|
-
margin: '12px 0 20px',
|
|
625
|
-
}}
|
|
626
|
-
>
|
|
627
|
-
This will permanently delete the item. Are you sure?
|
|
628
|
-
</p>
|
|
629
|
-
<div style={{ display: 'flex', gap: 8 }}>
|
|
630
|
-
<Button variant="destructive" size="sm" onClick={() => setOpen(false)}>
|
|
631
|
-
Delete
|
|
632
|
-
</Button>
|
|
633
|
-
<Button variant="secondary" size="sm" onClick={() => setOpen(false)}>
|
|
634
|
-
Cancel
|
|
635
|
-
</Button>
|
|
636
|
-
</div>
|
|
637
|
-
</div>
|
|
638
|
-
</Dialog>
|
|
639
|
-
</>
|
|
640
|
-
);
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
function DrawerDemo() {
|
|
644
|
-
const [open, setOpen] = useState(false);
|
|
645
|
-
return (
|
|
646
|
-
<>
|
|
647
|
-
<Button size="sm" variant="secondary" onClick={() => setOpen(true)}>
|
|
648
|
-
Open drawer
|
|
649
|
-
</Button>
|
|
650
|
-
<Drawer open={open} onClose={() => setOpen(false)} aria-label="Navigation">
|
|
651
|
-
<ul style={{ listStyle: 'none', margin: 0, padding: '8px 0' }}>
|
|
652
|
-
{(['Home', 'Settings', 'Account'] as const).map((item) => (
|
|
653
|
-
<li key={item}>
|
|
654
|
-
<button
|
|
655
|
-
onClick={() => setOpen(false)}
|
|
656
|
-
style={{
|
|
657
|
-
display: 'block',
|
|
658
|
-
width: '100%',
|
|
659
|
-
padding: '12px 20px',
|
|
660
|
-
background: 'none',
|
|
661
|
-
border: 'none',
|
|
662
|
-
cursor: 'pointer',
|
|
663
|
-
fontSize: 15,
|
|
664
|
-
textAlign: 'left',
|
|
665
|
-
color: 'var(--sv-color-text-primary)',
|
|
666
|
-
fontFamily: ff,
|
|
667
|
-
}}
|
|
668
|
-
>
|
|
669
|
-
{item}
|
|
670
|
-
</button>
|
|
671
|
-
</li>
|
|
672
|
-
))}
|
|
673
|
-
</ul>
|
|
674
|
-
</Drawer>
|
|
675
|
-
</>
|
|
676
|
-
);
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
function MobileAppsDrawerDemo() {
|
|
680
|
-
const [open, setOpen] = useState(false);
|
|
681
|
-
const items = [
|
|
682
|
-
{ key: 'home', icon: <Icon name="house" size="lg" aria-hidden />, label: 'Home' },
|
|
683
|
-
{ key: 'notes', icon: <Icon name="file" size="lg" aria-hidden />, label: 'Notes' },
|
|
684
|
-
{ key: 'gallery', icon: <Icon name="grid-2x2" size="lg" aria-hidden />, label: 'Gallery' },
|
|
685
|
-
];
|
|
686
|
-
return (
|
|
687
|
-
<>
|
|
688
|
-
<Button size="sm" variant="secondary" onClick={() => setOpen(true)}>
|
|
689
|
-
Open drawer
|
|
690
|
-
</Button>
|
|
691
|
-
<MobileAppsDrawer
|
|
692
|
-
open={open}
|
|
693
|
-
onClose={() => setOpen(false)}
|
|
694
|
-
aria-label="Sections"
|
|
695
|
-
items={items.map((item) => ({ ...item, onClick: () => setOpen(false) }))}
|
|
696
|
-
/>
|
|
697
|
-
</>
|
|
698
|
-
);
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
function PopoverDemo() {
|
|
702
|
-
const [open, setOpen] = useState(false);
|
|
703
|
-
return (
|
|
704
|
-
<Popover
|
|
705
|
-
open={open}
|
|
706
|
-
onClose={() => setOpen(false)}
|
|
707
|
-
align="left"
|
|
708
|
-
aria-label="Options menu"
|
|
709
|
-
trigger={
|
|
710
|
-
<Button size="sm" variant="ghost" onClick={() => setOpen((o) => !o)}>
|
|
711
|
-
<Icon name="settings" size="sm" aria-hidden /> Options
|
|
712
|
-
</Button>
|
|
713
|
-
}
|
|
714
|
-
>
|
|
715
|
-
<div style={{ padding: 'var(--sv-space-3)', fontFamily: ff }}>
|
|
716
|
-
{['Edit', 'Duplicate', 'Delete'].map((item) => (
|
|
717
|
-
<button
|
|
718
|
-
key={item}
|
|
719
|
-
type="button"
|
|
720
|
-
onClick={() => setOpen(false)}
|
|
721
|
-
style={{
|
|
722
|
-
display: 'block',
|
|
723
|
-
width: '100%',
|
|
724
|
-
padding: '8px 12px',
|
|
725
|
-
background: 'none',
|
|
726
|
-
border: 'none',
|
|
727
|
-
cursor: 'pointer',
|
|
728
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
729
|
-
textAlign: 'left',
|
|
730
|
-
color:
|
|
731
|
-
item === 'Delete' ? 'var(--sv-color-error-text)' : 'var(--sv-color-text-primary)',
|
|
732
|
-
fontFamily: ff,
|
|
733
|
-
borderRadius: 'var(--sv-radius-sm)',
|
|
734
|
-
}}
|
|
735
|
-
>
|
|
736
|
-
{item}
|
|
737
|
-
</button>
|
|
738
|
-
))}
|
|
739
|
-
</div>
|
|
740
|
-
</Popover>
|
|
741
|
-
);
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
function OverlayHeaderDemo() {
|
|
745
|
-
return (
|
|
746
|
-
<div
|
|
747
|
-
style={{
|
|
748
|
-
width: '100%',
|
|
749
|
-
maxWidth: 360,
|
|
750
|
-
border: '1px solid var(--sv-color-border)',
|
|
751
|
-
borderRadius: 'var(--sv-radius-lg)',
|
|
752
|
-
overflow: 'hidden',
|
|
753
|
-
}}
|
|
754
|
-
>
|
|
755
|
-
<OverlayHeader
|
|
756
|
-
title="Edit list"
|
|
757
|
-
onClose={() => {}}
|
|
758
|
-
onBack={() => {}}
|
|
759
|
-
action={
|
|
760
|
-
<Button size="sm" variant="ghost" onClick={() => {}}>
|
|
761
|
-
Save
|
|
762
|
-
</Button>
|
|
763
|
-
}
|
|
764
|
-
/>
|
|
765
|
-
</div>
|
|
766
|
-
);
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
function SheetDemo() {
|
|
770
|
-
const [open, setOpen] = useState(false);
|
|
771
|
-
return (
|
|
772
|
-
<>
|
|
773
|
-
<Button size="sm" onClick={() => setOpen(true)}>
|
|
774
|
-
Open sheet
|
|
775
|
-
</Button>
|
|
776
|
-
{/* Storybook has no --sv-shell-header-height/--sv-shell-footer-height, so
|
|
777
|
-
the panel fills this fixed-height preview box instead of the real
|
|
778
|
-
app's header-to-footer region — same component, just no shell
|
|
779
|
-
around it here. */}
|
|
780
|
-
<div
|
|
781
|
-
style={{
|
|
782
|
-
position: 'relative',
|
|
783
|
-
width: '100%',
|
|
784
|
-
maxWidth: 360,
|
|
785
|
-
height: 280,
|
|
786
|
-
marginTop: 12,
|
|
787
|
-
border: '1px solid var(--sv-color-border)',
|
|
788
|
-
borderRadius: 'var(--sv-radius-lg)',
|
|
789
|
-
overflow: 'hidden',
|
|
790
|
-
}}
|
|
791
|
-
>
|
|
792
|
-
<div style={{ position: 'absolute', inset: 0 }}>
|
|
793
|
-
<Sheet
|
|
794
|
-
open={open}
|
|
795
|
-
onClose={() => setOpen(false)}
|
|
796
|
-
title="Task detail"
|
|
797
|
-
aria-label="Task detail"
|
|
798
|
-
>
|
|
799
|
-
<div
|
|
800
|
-
style={{
|
|
801
|
-
padding: 16,
|
|
802
|
-
fontFamily: ff,
|
|
803
|
-
fontSize: 14,
|
|
804
|
-
color: 'var(--sv-color-text-muted)',
|
|
805
|
-
}}
|
|
806
|
-
>
|
|
807
|
-
Full-page overlay content — same pattern the tasks plugin used for its task-detail and
|
|
808
|
-
list-edit panels.
|
|
809
|
-
</div>
|
|
810
|
-
</Sheet>
|
|
811
|
-
</div>
|
|
812
|
-
</div>
|
|
813
|
-
</>
|
|
814
|
-
);
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
function ConfirmDialogDemo() {
|
|
818
|
-
const [open, setOpen] = useState(false);
|
|
819
|
-
return (
|
|
820
|
-
<>
|
|
821
|
-
<Button size="sm" variant="destructive" onClick={() => setOpen(true)}>
|
|
822
|
-
Remove passkey
|
|
823
|
-
</Button>
|
|
824
|
-
<ConfirmDialog
|
|
825
|
-
open={open}
|
|
826
|
-
onClose={() => setOpen(false)}
|
|
827
|
-
onConfirm={() => setOpen(false)}
|
|
828
|
-
title="Remove passkey"
|
|
829
|
-
message="You will no longer be able to sign in with this passkey."
|
|
830
|
-
confirmLabel="Remove"
|
|
831
|
-
destructive
|
|
832
|
-
/>
|
|
833
|
-
</>
|
|
834
|
-
);
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
function MenuDemo() {
|
|
838
|
-
const [open, setOpen] = useState(false);
|
|
839
|
-
const [sortBy, setSortBy] = useState<'manual' | 'title' | 'due'>('manual');
|
|
840
|
-
return (
|
|
841
|
-
<Menu
|
|
842
|
-
open={open}
|
|
843
|
-
onClose={() => setOpen(false)}
|
|
844
|
-
aria-label="List actions"
|
|
845
|
-
align="left"
|
|
846
|
-
trigger={
|
|
847
|
-
<Button size="sm" variant="ghost" onClick={() => setOpen((o) => !o)}>
|
|
848
|
-
<Icon name="settings" size="sm" aria-hidden /> Actions
|
|
849
|
-
</Button>
|
|
850
|
-
}
|
|
851
|
-
items={[
|
|
852
|
-
{ type: 'label', label: 'Sort by' },
|
|
853
|
-
{ label: 'Manual', checked: sortBy === 'manual', onSelect: () => setSortBy('manual') },
|
|
854
|
-
{ label: 'Title', checked: sortBy === 'title', onSelect: () => setSortBy('title') },
|
|
855
|
-
{ label: 'Due date', checked: sortBy === 'due', onSelect: () => setSortBy('due') },
|
|
856
|
-
{ type: 'separator' },
|
|
857
|
-
{ label: 'Rename', icon: 'pencil', onSelect: () => {} },
|
|
858
|
-
{ label: 'Duplicate', icon: 'plus', onSelect: () => {} },
|
|
859
|
-
{ label: 'Delete', icon: 'trash-2', destructive: true, onSelect: () => {} },
|
|
860
|
-
]}
|
|
861
|
-
/>
|
|
862
|
-
);
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
function MenuEntriesDemo() {
|
|
866
|
-
return (
|
|
867
|
-
<div style={{ width: '100%', maxWidth: 240 }}>
|
|
868
|
-
<MenuEntries
|
|
869
|
-
items={[
|
|
870
|
-
{ type: 'label', label: 'Sort by' },
|
|
871
|
-
{ label: 'Manual', checked: true },
|
|
872
|
-
{ label: 'Title', checked: false },
|
|
873
|
-
{ type: 'separator' },
|
|
874
|
-
{ label: 'Rename', icon: 'pencil' },
|
|
875
|
-
{ label: 'Delete', icon: 'trash-2', destructive: true },
|
|
876
|
-
]}
|
|
877
|
-
onSelect={() => {}}
|
|
878
|
-
/>
|
|
879
|
-
</div>
|
|
880
|
-
);
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
function CalendarDemo() {
|
|
884
|
-
const [value, setValue] = useState<Date | null>(new Date());
|
|
885
|
-
return (
|
|
886
|
-
<div style={{ width: '100%', maxWidth: 320 }}>
|
|
887
|
-
<Calendar value={value} onChange={setValue} aria-label="Example calendar" />
|
|
888
|
-
</div>
|
|
889
|
-
);
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
function DatePickerDemo() {
|
|
893
|
-
const [value, setValue] = useState<Date | null>(null);
|
|
894
|
-
return (
|
|
895
|
-
<div style={{ width: '100%', maxWidth: 240 }}>
|
|
896
|
-
<DatePicker
|
|
897
|
-
value={value}
|
|
898
|
-
onChange={setValue}
|
|
899
|
-
aria-label="Due date"
|
|
900
|
-
placeholder="Select date"
|
|
901
|
-
/>
|
|
902
|
-
</div>
|
|
903
|
-
);
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
function ToastDemo() {
|
|
907
|
-
const { show } = useToast();
|
|
908
|
-
return (
|
|
909
|
-
<Button
|
|
910
|
-
size="sm"
|
|
911
|
-
variant="secondary"
|
|
912
|
-
onClick={() =>
|
|
913
|
-
show({
|
|
914
|
-
title: 'Plugin installed',
|
|
915
|
-
message: 'Tasks v1.2.0 is now active.',
|
|
916
|
-
category: 'success',
|
|
917
|
-
})
|
|
918
|
-
}
|
|
919
|
-
>
|
|
920
|
-
Fire toast
|
|
921
|
-
</Button>
|
|
922
|
-
);
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
function TagInputDemo() {
|
|
926
|
-
const [tags, setTags] = useState(['draft', 'frontmatter']);
|
|
927
|
-
return <TagInput value={tags} onChange={setTags} aria-label="Tags" />;
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
function QuantityStepperDemo() {
|
|
931
|
-
const [value, setValue] = useState(6);
|
|
932
|
-
return <QuantityStepper value={value} onChange={setValue} unit="pcs" aria-label="Quantity" />;
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
function CheckableListRowDemo() {
|
|
936
|
-
const [checked, setChecked] = useState(false);
|
|
937
|
-
return (
|
|
938
|
-
<div style={{ width: '100%' }}>
|
|
939
|
-
<CheckableListRow
|
|
940
|
-
checked={checked}
|
|
941
|
-
onCheckedChange={setChecked}
|
|
942
|
-
label="Bananas"
|
|
943
|
-
icon={<Icon name="banana" size="md" aria-hidden />}
|
|
944
|
-
trailing={<span style={{ fontSize: 13, color: 'var(--sv-color-text-muted)' }}>6 pcs</span>}
|
|
945
|
-
/>
|
|
946
|
-
</div>
|
|
947
|
-
);
|
|
948
|
-
}
|
|
949
|
-
|
|
950
|
-
function CurrencyInputDemo() {
|
|
951
|
-
const [cents, setCents] = useState<number | null>(4250);
|
|
952
|
-
return (
|
|
953
|
-
<CurrencyInput
|
|
954
|
-
valueCents={cents}
|
|
955
|
-
onValueChange={setCents}
|
|
956
|
-
placeholder="0.00"
|
|
957
|
-
aria-label="Amount"
|
|
958
|
-
/>
|
|
959
|
-
);
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
function SplitMethodSelectorDemo() {
|
|
963
|
-
const [value, setValue] = useState<SplitMethod>('equal');
|
|
964
|
-
return <SplitMethodSelector value={value} onChange={setValue} />;
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
const MEMBER_MULTI_SELECT_OPTIONS: MemberMultiSelectOption[] = [
|
|
968
|
-
{ id: '1', label: 'Priya' },
|
|
969
|
-
{ id: '2', label: 'Jamie (guest)' },
|
|
970
|
-
{ id: '3', label: 'Sam' },
|
|
971
|
-
];
|
|
972
|
-
|
|
973
|
-
function MemberMultiSelectDemo() {
|
|
974
|
-
const [selected, setSelected] = useState<Set<string>>(new Set(['1', '3']));
|
|
975
|
-
return (
|
|
976
|
-
<div style={{ width: '100%' }}>
|
|
977
|
-
<MemberMultiSelect
|
|
978
|
-
options={MEMBER_MULTI_SELECT_OPTIONS}
|
|
979
|
-
selectedIds={selected}
|
|
980
|
-
onToggle={(id, checked) => {
|
|
981
|
-
setSelected((prev) => {
|
|
982
|
-
const next = new Set(prev);
|
|
983
|
-
if (checked) next.add(id);
|
|
984
|
-
else next.delete(id);
|
|
985
|
-
return next;
|
|
986
|
-
});
|
|
987
|
-
}}
|
|
988
|
-
label="Split between"
|
|
989
|
-
/>
|
|
990
|
-
</div>
|
|
991
|
-
);
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
function FileDropzoneDemo() {
|
|
995
|
-
const [file, setFile] = useState<File | null>(null);
|
|
996
|
-
return (
|
|
997
|
-
<div style={{ width: '100%' }}>
|
|
998
|
-
<FileDropzone
|
|
999
|
-
ariaLabel="Upload ZIP file"
|
|
1000
|
-
accept=".zip,application/zip"
|
|
1001
|
-
label={file ? file.name : 'Choose a ZIP file'}
|
|
1002
|
-
hint={file ? `${(file.size / 1024).toFixed(0)} KB` : 'or drag and drop here'}
|
|
1003
|
-
onFileSelect={setFile}
|
|
1004
|
-
/>
|
|
1005
|
-
</div>
|
|
1006
|
-
);
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
interface StepStripDemoStop extends StepStripItem {
|
|
1010
|
-
label: string;
|
|
1011
|
-
sublabel: string;
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
const STEP_STRIP_DEMO_STOPS: StepStripDemoStop[] = [
|
|
1015
|
-
{ id: 'lisbon', label: 'Lisbon', sublabel: 'Aug 26 – 28' },
|
|
1016
|
-
{ id: 'porto', label: 'Porto', sublabel: 'Aug 28 – 30' },
|
|
1017
|
-
];
|
|
1018
|
-
|
|
1019
|
-
function StepStripDemo() {
|
|
1020
|
-
const [activeId, setActiveId] = useState('lisbon');
|
|
1021
|
-
return (
|
|
1022
|
-
<StepStrip
|
|
1023
|
-
items={STEP_STRIP_DEMO_STOPS}
|
|
1024
|
-
activeId={activeId}
|
|
1025
|
-
aria-label="Trip stops"
|
|
1026
|
-
onAdd={() => undefined}
|
|
1027
|
-
addLabel="Add a stop"
|
|
1028
|
-
renderItem={(stop, { isActive }) => (
|
|
1029
|
-
<button
|
|
1030
|
-
type="button"
|
|
1031
|
-
onClick={() => setActiveId(stop.id)}
|
|
1032
|
-
style={{
|
|
1033
|
-
boxSizing: 'border-box',
|
|
1034
|
-
minWidth: '150px',
|
|
1035
|
-
padding: 'var(--sv-space-3) var(--sv-space-4)',
|
|
1036
|
-
borderRadius: 'var(--sv-radius-lg)',
|
|
1037
|
-
border: `1px solid ${isActive ? 'var(--sv-color-text-primary)' : 'var(--sv-color-border)'}`,
|
|
1038
|
-
background: isActive
|
|
1039
|
-
? 'var(--sv-color-text-primary)'
|
|
1040
|
-
: 'var(--sv-color-surface-raised)',
|
|
1041
|
-
color: isActive ? 'var(--sv-color-surface)' : 'var(--sv-color-text-primary)',
|
|
1042
|
-
cursor: 'pointer',
|
|
1043
|
-
textAlign: 'left',
|
|
1044
|
-
}}
|
|
1045
|
-
>
|
|
1046
|
-
<div style={{ fontWeight: 600, fontSize: 'var(--sv-font-size-sm)' }}>{stop.label}</div>
|
|
1047
|
-
<div style={{ fontSize: 'var(--sv-font-size-xs)', opacity: 0.8 }}>{stop.sublabel}</div>
|
|
1048
|
-
</button>
|
|
1049
|
-
)}
|
|
1050
|
-
/>
|
|
1051
|
-
);
|
|
1052
|
-
}
|
|
1053
|
-
|
|
1054
|
-
function ResponsiveSurfaceDemo() {
|
|
1055
|
-
return (
|
|
1056
|
-
<div
|
|
1057
|
-
style={{
|
|
1058
|
-
border: '1px dashed var(--sv-color-border)',
|
|
1059
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
1060
|
-
padding: 'var(--sv-space-4)',
|
|
1061
|
-
fontFamily: ff,
|
|
1062
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
1063
|
-
color: 'var(--sv-color-text-muted)',
|
|
1064
|
-
}}
|
|
1065
|
-
>
|
|
1066
|
-
<ResponsiveSurface
|
|
1067
|
-
web={
|
|
1068
|
-
<span style={{ color: 'var(--sv-color-text-primary)' }}>
|
|
1069
|
-
Web tree (resize below 768px)
|
|
1070
|
-
</span>
|
|
1071
|
-
}
|
|
1072
|
-
mobile={<span style={{ color: 'var(--sv-color-text-primary)' }}>Mobile tree</span>}
|
|
1073
|
-
/>
|
|
1074
|
-
</div>
|
|
1075
|
-
);
|
|
1076
|
-
}
|
|
1077
|
-
|
|
1078
|
-
const SWIPABLE_CAROUSEL_DEMO_SLIDES = [
|
|
1079
|
-
{ key: 'lists', label: 'Lists' },
|
|
1080
|
-
{ key: 'groceries', label: 'Groceries' },
|
|
1081
|
-
{ key: 'errands', label: 'Errands' },
|
|
1082
|
-
];
|
|
1083
|
-
|
|
1084
|
-
function SwipableMobileCarouselDemo() {
|
|
1085
|
-
const [activeIndex, setActiveIndex] = useState(0);
|
|
1086
|
-
return (
|
|
1087
|
-
<div
|
|
1088
|
-
style={{
|
|
1089
|
-
height: 220,
|
|
1090
|
-
border: '1px solid var(--sv-color-border)',
|
|
1091
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
1092
|
-
overflow: 'hidden',
|
|
1093
|
-
}}
|
|
1094
|
-
>
|
|
1095
|
-
<SwipableMobileCarousel
|
|
1096
|
-
activeIndex={activeIndex}
|
|
1097
|
-
onSettle={setActiveIndex}
|
|
1098
|
-
aria-label="Task lists"
|
|
1099
|
-
>
|
|
1100
|
-
{SWIPABLE_CAROUSEL_DEMO_SLIDES.map((slide) => (
|
|
1101
|
-
<SwipableMobileCarouselSlide key={slide.key} slideKey={slide.key} label={slide.label}>
|
|
1102
|
-
<SwipableMobileCarouselSlideHeader>
|
|
1103
|
-
<div
|
|
1104
|
-
style={{
|
|
1105
|
-
padding: 'var(--sv-space-3)',
|
|
1106
|
-
fontFamily: ff,
|
|
1107
|
-
fontWeight: 'var(--sv-font-weight-semibold)',
|
|
1108
|
-
color: 'var(--sv-color-text-primary)',
|
|
1109
|
-
}}
|
|
1110
|
-
>
|
|
1111
|
-
{slide.label}
|
|
1112
|
-
</div>
|
|
1113
|
-
</SwipableMobileCarouselSlideHeader>
|
|
1114
|
-
<SwipableMobileCarouselSlideBody>
|
|
1115
|
-
<div
|
|
1116
|
-
style={{
|
|
1117
|
-
display: 'flex',
|
|
1118
|
-
alignItems: 'center',
|
|
1119
|
-
justifyContent: 'center',
|
|
1120
|
-
height: '100%',
|
|
1121
|
-
fontFamily: ff,
|
|
1122
|
-
color: 'var(--sv-color-text-muted)',
|
|
1123
|
-
}}
|
|
1124
|
-
>
|
|
1125
|
-
{slide.label} content
|
|
1126
|
-
</div>
|
|
1127
|
-
</SwipableMobileCarouselSlideBody>
|
|
1128
|
-
</SwipableMobileCarouselSlide>
|
|
1129
|
-
))}
|
|
1130
|
-
</SwipableMobileCarousel>
|
|
1131
|
-
</div>
|
|
1132
|
-
);
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
function SwipableMobileCarouselDotsDemo() {
|
|
1136
|
-
const [activeIndex, setActiveIndex] = useState(0);
|
|
1137
|
-
return (
|
|
1138
|
-
<SwipableMobileCarouselDots
|
|
1139
|
-
count={3}
|
|
1140
|
-
activeIndex={activeIndex}
|
|
1141
|
-
onJump={setActiveIndex}
|
|
1142
|
-
labels={['Lists', 'Groceries', 'Errands']}
|
|
1143
|
-
aria-label="Task lists"
|
|
1144
|
-
/>
|
|
1145
|
-
);
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
const SWIPE_STACK_DEMO_CARDS = [
|
|
1149
|
-
{ id: 'water-plants', title: 'Water the plants' },
|
|
1150
|
-
{ id: 'standup', title: 'Team standup · 9:30am' },
|
|
1151
|
-
{ id: 'groceries', title: 'Buy groceries' },
|
|
1152
|
-
];
|
|
1153
|
-
|
|
1154
|
-
function SwipeStackDemo() {
|
|
1155
|
-
return (
|
|
1156
|
-
<div style={{ height: 260, maxWidth: 320 }}>
|
|
1157
|
-
<SwipeStack
|
|
1158
|
-
aria-label="Today's agenda"
|
|
1159
|
-
directions={{
|
|
1160
|
-
up: { label: 'Done', icon: 'check' },
|
|
1161
|
-
down: { label: 'Defer', icon: 'history' },
|
|
1162
|
-
}}
|
|
1163
|
-
onSwipe={() => {}}
|
|
1164
|
-
>
|
|
1165
|
-
{SWIPE_STACK_DEMO_CARDS.map((card) => (
|
|
1166
|
-
<SwipeStackCard key={card.id} cardId={card.id}>
|
|
1167
|
-
<div
|
|
1168
|
-
style={{
|
|
1169
|
-
height: '100%',
|
|
1170
|
-
boxSizing: 'border-box',
|
|
1171
|
-
display: 'flex',
|
|
1172
|
-
alignItems: 'center',
|
|
1173
|
-
justifyContent: 'center',
|
|
1174
|
-
padding: 'var(--sv-space-4)',
|
|
1175
|
-
fontFamily: ff,
|
|
1176
|
-
fontWeight: 'var(--sv-font-weight-semibold)',
|
|
1177
|
-
color: 'var(--sv-color-text-primary)',
|
|
1178
|
-
textAlign: 'center',
|
|
1179
|
-
}}
|
|
1180
|
-
>
|
|
1181
|
-
{card.title}
|
|
1182
|
-
</div>
|
|
1183
|
-
</SwipeStackCard>
|
|
1184
|
-
))}
|
|
1185
|
-
</SwipeStack>
|
|
1186
|
-
</div>
|
|
1187
|
-
);
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
function IconPickerDemo() {
|
|
1191
|
-
const [value, setValue] = useState<IconName | null>('banana');
|
|
1192
|
-
const options: IconName[] = [
|
|
1193
|
-
'banana',
|
|
1194
|
-
'apple',
|
|
1195
|
-
'carrot',
|
|
1196
|
-
'egg',
|
|
1197
|
-
'milk',
|
|
1198
|
-
'beef',
|
|
1199
|
-
'drumstick',
|
|
1200
|
-
'fish',
|
|
1201
|
-
'croissant',
|
|
1202
|
-
'cookie',
|
|
1203
|
-
];
|
|
1204
|
-
return (
|
|
1205
|
-
<IconPicker
|
|
1206
|
-
value={value}
|
|
1207
|
-
onChange={setValue}
|
|
1208
|
-
options={options}
|
|
1209
|
-
aria-label="Item icon"
|
|
1210
|
-
triggerLabel={value ?? 'Choose icon'}
|
|
1211
|
-
/>
|
|
1212
|
-
);
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
|
-
function ColorPickerDemo() {
|
|
1216
|
-
const [value, setValue] = useState<string | null>('#b5c9e8');
|
|
1217
|
-
const swatches = [
|
|
1218
|
-
{ label: 'Sky', value: '#b5c9e8' },
|
|
1219
|
-
{ label: 'Sage', value: '#c9e0c4' },
|
|
1220
|
-
{ label: 'Sand', value: '#e8d3b5' },
|
|
1221
|
-
{ label: 'Clay', value: '#e0c4c4' },
|
|
1222
|
-
{ label: 'Lilac', value: '#d8c4e0' },
|
|
1223
|
-
];
|
|
1224
|
-
return (
|
|
1225
|
-
<ColorPicker
|
|
1226
|
-
swatches={swatches}
|
|
1227
|
-
value={value}
|
|
1228
|
-
onChange={setValue}
|
|
1229
|
-
allowNone
|
|
1230
|
-
aria-label="Board color"
|
|
1231
|
-
/>
|
|
1232
|
-
);
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
|
-
function SuggestionInputDemo() {
|
|
1236
|
-
const [value, setValue] = useState('ban');
|
|
1237
|
-
const options = ['Bananas', 'Banana bread mix']
|
|
1238
|
-
.filter((label) => label.toLowerCase().includes(value.trim().toLowerCase()))
|
|
1239
|
-
.map((label, i) => ({ id: String(i), label }));
|
|
1240
|
-
return (
|
|
1241
|
-
<SuggestionInput
|
|
1242
|
-
value={value}
|
|
1243
|
-
onChange={setValue}
|
|
1244
|
-
options={value.trim() ? options : []}
|
|
1245
|
-
onSelect={(o) => setValue(o.label)}
|
|
1246
|
-
placeholder="Add an item…"
|
|
1247
|
-
aria-label="Add an item"
|
|
1248
|
-
createLabel={(v) => `Add "${v}" as a new item`}
|
|
1249
|
-
onCreate={setValue}
|
|
1250
|
-
/>
|
|
1251
|
-
);
|
|
1252
|
-
}
|
|
1253
|
-
|
|
1254
|
-
// ---------------------------------------------------------------------------
|
|
1255
|
-
// Main component
|
|
1256
|
-
// ---------------------------------------------------------------------------
|
|
1257
|
-
|
|
1258
|
-
function OverviewPage() {
|
|
1259
|
-
return (
|
|
1260
|
-
<div
|
|
1261
|
-
style={{
|
|
1262
|
-
fontFamily: ff,
|
|
1263
|
-
background: 'var(--sv-color-surface)',
|
|
1264
|
-
minHeight: '100vh',
|
|
1265
|
-
color: 'var(--sv-color-text-primary)',
|
|
1266
|
-
}}
|
|
1267
|
-
>
|
|
1268
|
-
{/* ── Hero ─────────────────────────────────────────────────────────── */}
|
|
1269
|
-
<div
|
|
1270
|
-
style={{
|
|
1271
|
-
padding: 'var(--sv-space-10) var(--sv-space-8)',
|
|
1272
|
-
borderBottom: '1px solid var(--sv-color-border)',
|
|
1273
|
-
background: 'var(--sv-color-surface-sunken)',
|
|
1274
|
-
}}
|
|
1275
|
-
>
|
|
1276
|
-
<div style={{ maxWidth: 800, margin: '0 auto' }}>
|
|
1277
|
-
<div
|
|
1278
|
-
style={{
|
|
1279
|
-
display: 'flex',
|
|
1280
|
-
alignItems: 'center',
|
|
1281
|
-
gap: 'var(--sv-space-3)',
|
|
1282
|
-
marginBottom: 'var(--sv-space-4)',
|
|
1283
|
-
}}
|
|
1284
|
-
>
|
|
1285
|
-
<Pill>@sovereignfs/ui</Pill>
|
|
1286
|
-
<Pill>v1.x</Pill>
|
|
1287
|
-
</div>
|
|
1288
|
-
<Heading level={1}>Sovereign Design System</Heading>
|
|
1289
|
-
<p
|
|
1290
|
-
style={{
|
|
1291
|
-
fontSize: 'var(--sv-font-size-lg)',
|
|
1292
|
-
color: 'var(--sv-color-text-muted)',
|
|
1293
|
-
marginTop: 'var(--sv-space-3)',
|
|
1294
|
-
maxWidth: 600,
|
|
1295
|
-
lineHeight: 1.5,
|
|
1296
|
-
}}
|
|
1297
|
-
>
|
|
1298
|
-
The component library and token system for building Sovereign plugins. Everything here
|
|
1299
|
-
is the public contract — available to every plugin developer, stable across minor
|
|
1300
|
-
versions.
|
|
1301
|
-
</p>
|
|
1302
|
-
<div
|
|
1303
|
-
style={{
|
|
1304
|
-
marginTop: 'var(--sv-space-5)',
|
|
1305
|
-
padding: 'var(--sv-space-4)',
|
|
1306
|
-
background: 'var(--sv-color-surface)',
|
|
1307
|
-
border: '1px solid var(--sv-color-border)',
|
|
1308
|
-
borderRadius: 'var(--sv-radius-lg)',
|
|
1309
|
-
display: 'flex',
|
|
1310
|
-
flexDirection: 'column',
|
|
1311
|
-
gap: 'var(--sv-space-2)',
|
|
1312
|
-
}}
|
|
1313
|
-
>
|
|
1314
|
-
<p
|
|
1315
|
-
style={{
|
|
1316
|
-
fontFamily: ffm,
|
|
1317
|
-
fontSize: 'var(--sv-font-size-xs)',
|
|
1318
|
-
color: 'var(--sv-color-text-muted)',
|
|
1319
|
-
margin: 0,
|
|
1320
|
-
}}
|
|
1321
|
-
>
|
|
1322
|
-
Three things to remember:
|
|
1323
|
-
</p>
|
|
1324
|
-
<ul
|
|
1325
|
-
style={{
|
|
1326
|
-
margin: 0,
|
|
1327
|
-
paddingLeft: 'var(--sv-space-5)',
|
|
1328
|
-
color: 'var(--sv-color-text-primary)',
|
|
1329
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
1330
|
-
lineHeight: 1.8,
|
|
1331
|
-
}}
|
|
1332
|
-
>
|
|
1333
|
-
<li>
|
|
1334
|
-
Import components from <code style={{ fontFamily: ffm }}>@sovereignfs/ui</code>
|
|
1335
|
-
</li>
|
|
1336
|
-
<li>
|
|
1337
|
-
Use <code style={{ fontFamily: ffm }}>--sv-*</code> semantic tokens in your CSS —
|
|
1338
|
-
they are injected globally by the runtime shell, no import needed
|
|
1339
|
-
</li>
|
|
1340
|
-
<li>Never hardcode hex values or reference primitive tokens directly</li>
|
|
1341
|
-
</ul>
|
|
1342
|
-
</div>
|
|
1343
|
-
</div>
|
|
1344
|
-
</div>
|
|
1345
|
-
|
|
1346
|
-
<div style={{ maxWidth: 800, margin: '0 auto', padding: 'var(--sv-space-8)' }}>
|
|
1347
|
-
{/* ── Quick Start ───────────────────────────────────────────────── */}
|
|
1348
|
-
<section style={{ marginBottom: 'var(--sv-space-12)' }}>
|
|
1349
|
-
<SectionHeader
|
|
1350
|
-
title="Quick start"
|
|
1351
|
-
subtitle="Everything you need to build a Sovereign plugin UI."
|
|
1352
|
-
/>
|
|
1353
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--sv-space-4)' }}>
|
|
1354
|
-
<div>
|
|
1355
|
-
<p
|
|
1356
|
-
style={{
|
|
1357
|
-
fontSize: 'var(--sv-font-size-caption)',
|
|
1358
|
-
color: 'var(--sv-color-text-muted)',
|
|
1359
|
-
marginBottom: 'var(--sv-space-2)',
|
|
1360
|
-
}}
|
|
1361
|
-
>
|
|
1362
|
-
Import typed React components:
|
|
1363
|
-
</p>
|
|
1364
|
-
<Code>{`import { Button, Badge, Input, Icon, Toggle, Tabs } from '@sovereignfs/ui';`}</Code>
|
|
1365
|
-
</div>
|
|
1366
|
-
<div>
|
|
1367
|
-
<p
|
|
1368
|
-
style={{
|
|
1369
|
-
fontSize: 'var(--sv-font-size-caption)',
|
|
1370
|
-
color: 'var(--sv-color-text-muted)',
|
|
1371
|
-
marginBottom: 'var(--sv-space-2)',
|
|
1372
|
-
}}
|
|
1373
|
-
>
|
|
1374
|
-
Reference semantic tokens directly in plugin CSS — no import required:
|
|
1375
|
-
</p>
|
|
1376
|
-
<Code>{`.my-card {
|
|
1377
|
-
background: var(--sv-color-surface);
|
|
1378
|
-
border: 1px solid var(--sv-color-border);
|
|
1379
|
-
border-radius: var(--sv-radius-lg);
|
|
1380
|
-
padding: var(--sv-space-4);
|
|
1381
|
-
box-shadow: var(--sv-shadow-card);
|
|
1382
|
-
font-family: var(--sv-font-family);
|
|
1383
|
-
color: var(--sv-color-text-primary);
|
|
1384
|
-
}`}</Code>
|
|
1385
|
-
</div>
|
|
1386
|
-
</div>
|
|
1387
|
-
</section>
|
|
1388
|
-
|
|
1389
|
-
{/* ── Token Architecture ────────────────────────────────────────── */}
|
|
1390
|
-
<section style={{ marginBottom: 'var(--sv-space-12)' }}>
|
|
1391
|
-
<SectionHeader
|
|
1392
|
-
title="Token architecture"
|
|
1393
|
-
subtitle="Two layers — only the semantic layer is a public API."
|
|
1394
|
-
/>
|
|
1395
|
-
<div
|
|
1396
|
-
style={{
|
|
1397
|
-
display: 'grid',
|
|
1398
|
-
gridTemplateColumns: '1fr auto 1fr auto 1fr',
|
|
1399
|
-
gap: 'var(--sv-space-3)',
|
|
1400
|
-
alignItems: 'center',
|
|
1401
|
-
marginBottom: 'var(--sv-space-6)',
|
|
1402
|
-
}}
|
|
1403
|
-
>
|
|
1404
|
-
{[
|
|
1405
|
-
{
|
|
1406
|
-
label: 'Primitives',
|
|
1407
|
-
sub: '--sv-grey-900\n--sv-red-100\n--sv-space-4',
|
|
1408
|
-
note: 'Internal only. Raw scale values.',
|
|
1409
|
-
muted: true,
|
|
1410
|
-
},
|
|
1411
|
-
].map((b) => (
|
|
1412
|
-
<div
|
|
1413
|
-
key={b.label}
|
|
1414
|
-
style={{
|
|
1415
|
-
border: `1px solid ${b.muted ? 'var(--sv-color-border)' : 'var(--sv-color-accent)'}`,
|
|
1416
|
-
borderRadius: 'var(--sv-radius-lg)',
|
|
1417
|
-
padding: 'var(--sv-space-4)',
|
|
1418
|
-
opacity: b.muted ? 0.6 : 1,
|
|
1419
|
-
}}
|
|
1420
|
-
>
|
|
1421
|
-
<p
|
|
1422
|
-
style={{
|
|
1423
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
1424
|
-
fontWeight: 600,
|
|
1425
|
-
margin: '0 0 6px',
|
|
1426
|
-
color: 'var(--sv-color-text-primary)',
|
|
1427
|
-
}}
|
|
1428
|
-
>
|
|
1429
|
-
{b.label}
|
|
1430
|
-
</p>
|
|
1431
|
-
<pre
|
|
1432
|
-
style={{
|
|
1433
|
-
fontFamily: ffm,
|
|
1434
|
-
fontSize: '0.6875rem',
|
|
1435
|
-
color: 'var(--sv-color-text-muted)',
|
|
1436
|
-
margin: 0,
|
|
1437
|
-
whiteSpace: 'pre',
|
|
1438
|
-
}}
|
|
1439
|
-
>
|
|
1440
|
-
{b.sub}
|
|
1441
|
-
</pre>
|
|
1442
|
-
<p
|
|
1443
|
-
style={{
|
|
1444
|
-
fontSize: 'var(--sv-font-size-caption)',
|
|
1445
|
-
color: 'var(--sv-color-text-muted)',
|
|
1446
|
-
margin: '8px 0 0',
|
|
1447
|
-
}}
|
|
1448
|
-
>
|
|
1449
|
-
{b.note}
|
|
1450
|
-
</p>
|
|
1451
|
-
</div>
|
|
1452
|
-
))}
|
|
1453
|
-
<div style={{ textAlign: 'center', color: 'var(--sv-color-text-muted)', fontSize: 24 }}>
|
|
1454
|
-
→
|
|
1455
|
-
</div>
|
|
1456
|
-
<div
|
|
1457
|
-
style={{
|
|
1458
|
-
border: '2px solid var(--sv-color-accent)',
|
|
1459
|
-
borderRadius: 'var(--sv-radius-lg)',
|
|
1460
|
-
padding: 'var(--sv-space-4)',
|
|
1461
|
-
}}
|
|
1462
|
-
>
|
|
1463
|
-
<p
|
|
1464
|
-
style={{
|
|
1465
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
1466
|
-
fontWeight: 600,
|
|
1467
|
-
margin: '0 0 6px',
|
|
1468
|
-
color: 'var(--sv-color-text-primary)',
|
|
1469
|
-
}}
|
|
1470
|
-
>
|
|
1471
|
-
Semantic tokens ✓
|
|
1472
|
-
</p>
|
|
1473
|
-
<pre
|
|
1474
|
-
style={{
|
|
1475
|
-
fontFamily: ffm,
|
|
1476
|
-
fontSize: '0.6875rem',
|
|
1477
|
-
color: 'var(--sv-color-text-muted)',
|
|
1478
|
-
margin: 0,
|
|
1479
|
-
whiteSpace: 'pre',
|
|
1480
|
-
}}
|
|
1481
|
-
>
|
|
1482
|
-
{'--sv-color-surface\n--sv-color-error-text\n--sv-radius-lg'}
|
|
1483
|
-
</pre>
|
|
1484
|
-
<p
|
|
1485
|
-
style={{
|
|
1486
|
-
fontSize: 'var(--sv-font-size-caption)',
|
|
1487
|
-
color: 'var(--sv-color-text-muted)',
|
|
1488
|
-
margin: '8px 0 0',
|
|
1489
|
-
}}
|
|
1490
|
-
>
|
|
1491
|
-
Plugin public API. Theme-aware.
|
|
1492
|
-
</p>
|
|
1493
|
-
</div>
|
|
1494
|
-
<div style={{ textAlign: 'center', color: 'var(--sv-color-text-muted)', fontSize: 24 }}>
|
|
1495
|
-
→
|
|
1496
|
-
</div>
|
|
1497
|
-
<div
|
|
1498
|
-
style={{
|
|
1499
|
-
border: '2px solid var(--sv-color-accent)',
|
|
1500
|
-
borderRadius: 'var(--sv-radius-lg)',
|
|
1501
|
-
padding: 'var(--sv-space-4)',
|
|
1502
|
-
}}
|
|
1503
|
-
>
|
|
1504
|
-
<p
|
|
1505
|
-
style={{
|
|
1506
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
1507
|
-
fontWeight: 600,
|
|
1508
|
-
margin: '0 0 6px',
|
|
1509
|
-
color: 'var(--sv-color-text-primary)',
|
|
1510
|
-
}}
|
|
1511
|
-
>
|
|
1512
|
-
Components ✓
|
|
1513
|
-
</p>
|
|
1514
|
-
<pre
|
|
1515
|
-
style={{
|
|
1516
|
-
fontFamily: ffm,
|
|
1517
|
-
fontSize: '0.6875rem',
|
|
1518
|
-
color: 'var(--sv-color-text-muted)',
|
|
1519
|
-
margin: 0,
|
|
1520
|
-
whiteSpace: 'pre',
|
|
1521
|
-
}}
|
|
1522
|
-
>
|
|
1523
|
-
{'<Button />\n<Badge />\n<Input />'}
|
|
1524
|
-
</pre>
|
|
1525
|
-
<p
|
|
1526
|
-
style={{
|
|
1527
|
-
fontSize: 'var(--sv-font-size-caption)',
|
|
1528
|
-
color: 'var(--sv-color-text-muted)',
|
|
1529
|
-
margin: '8px 0 0',
|
|
1530
|
-
}}
|
|
1531
|
-
>
|
|
1532
|
-
React, typed, RSC-safe.
|
|
1533
|
-
</p>
|
|
1534
|
-
</div>
|
|
1535
|
-
</div>
|
|
1536
|
-
<div
|
|
1537
|
-
style={{
|
|
1538
|
-
background: 'var(--sv-color-warning-surface)',
|
|
1539
|
-
border: '1px solid var(--sv-color-warning-border)',
|
|
1540
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
1541
|
-
padding: 'var(--sv-space-3) var(--sv-space-4)',
|
|
1542
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
1543
|
-
color: 'var(--sv-color-warning-text)',
|
|
1544
|
-
}}
|
|
1545
|
-
>
|
|
1546
|
-
<strong>Never use primitive tokens in plugin code.</strong> Primitives like{' '}
|
|
1547
|
-
<code style={{ fontFamily: ffm }}>--sv-grey-900</code> or{' '}
|
|
1548
|
-
<code style={{ fontFamily: ffm }}>--sv-red-100</code> are fixed values — they do not
|
|
1549
|
-
swap with dark mode or instance theming. Only semantic tokens do.
|
|
1550
|
-
</div>
|
|
1551
|
-
</section>
|
|
1552
|
-
|
|
1553
|
-
{/* ── Color System ─────────────────────────────────────────────── */}
|
|
1554
|
-
<section style={{ marginBottom: 'var(--sv-space-12)' }}>
|
|
1555
|
-
<SectionHeader
|
|
1556
|
-
title="Color system"
|
|
1557
|
-
subtitle="Semantic groups — use the Themes toolbar above to compare light and dark."
|
|
1558
|
-
/>
|
|
1559
|
-
{COLOR_GROUPS.map((g) => (
|
|
1560
|
-
<ColorGroup key={g.label} label={g.label} usage={g.usage} tokens={g.tokens} />
|
|
1561
|
-
))}
|
|
1562
|
-
</section>
|
|
1563
|
-
|
|
1564
|
-
{/* ── Typography ───────────────────────────────────────────────── */}
|
|
1565
|
-
<section style={{ marginBottom: 'var(--sv-space-12)' }}>
|
|
1566
|
-
<SectionHeader
|
|
1567
|
-
title="Typography"
|
|
1568
|
-
subtitle="Hanken Grotesk (body) · JetBrains Mono (code) — fallback stacks apply when web fonts are not loaded."
|
|
1569
|
-
/>
|
|
1570
|
-
|
|
1571
|
-
<div
|
|
1572
|
-
style={{
|
|
1573
|
-
display: 'grid',
|
|
1574
|
-
gridTemplateColumns: '1fr 1fr',
|
|
1575
|
-
gap: 'var(--sv-space-6)',
|
|
1576
|
-
marginBottom: 'var(--sv-space-6)',
|
|
1577
|
-
}}
|
|
1578
|
-
>
|
|
1579
|
-
<div>
|
|
1580
|
-
<p
|
|
1581
|
-
style={{
|
|
1582
|
-
fontSize: 'var(--sv-font-size-caption)',
|
|
1583
|
-
color: 'var(--sv-color-text-muted)',
|
|
1584
|
-
marginBottom: 'var(--sv-space-2)',
|
|
1585
|
-
}}
|
|
1586
|
-
>
|
|
1587
|
-
Body — var(--sv-font-family)
|
|
1588
|
-
</p>
|
|
1589
|
-
<p
|
|
1590
|
-
style={{
|
|
1591
|
-
fontFamily: ff,
|
|
1592
|
-
fontSize: 'var(--sv-font-size-lg)',
|
|
1593
|
-
color: 'var(--sv-color-text-primary)',
|
|
1594
|
-
margin: 0,
|
|
1595
|
-
}}
|
|
1596
|
-
>
|
|
1597
|
-
The quick brown fox
|
|
1598
|
-
</p>
|
|
1599
|
-
<p
|
|
1600
|
-
style={{
|
|
1601
|
-
fontFamily: ff,
|
|
1602
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
1603
|
-
color: 'var(--sv-color-text-muted)',
|
|
1604
|
-
margin: '4px 0 0',
|
|
1605
|
-
}}
|
|
1606
|
-
>
|
|
1607
|
-
ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
|
1608
|
-
</p>
|
|
1609
|
-
</div>
|
|
1610
|
-
<div>
|
|
1611
|
-
<p
|
|
1612
|
-
style={{
|
|
1613
|
-
fontSize: 'var(--sv-font-size-caption)',
|
|
1614
|
-
color: 'var(--sv-color-text-muted)',
|
|
1615
|
-
marginBottom: 'var(--sv-space-2)',
|
|
1616
|
-
}}
|
|
1617
|
-
>
|
|
1618
|
-
Mono — var(--sv-font-family-mono)
|
|
1619
|
-
</p>
|
|
1620
|
-
<p
|
|
1621
|
-
style={{
|
|
1622
|
-
fontFamily: ffm,
|
|
1623
|
-
fontSize: 'var(--sv-font-size-lg)',
|
|
1624
|
-
color: 'var(--sv-color-text-primary)',
|
|
1625
|
-
margin: 0,
|
|
1626
|
-
}}
|
|
1627
|
-
>
|
|
1628
|
-
const x = 42;
|
|
1629
|
-
</p>
|
|
1630
|
-
<p
|
|
1631
|
-
style={{
|
|
1632
|
-
fontFamily: ffm,
|
|
1633
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
1634
|
-
color: 'var(--sv-color-text-muted)',
|
|
1635
|
-
margin: '4px 0 0',
|
|
1636
|
-
}}
|
|
1637
|
-
>
|
|
1638
|
-
--sv-font-family-mono
|
|
1639
|
-
</p>
|
|
1640
|
-
</div>
|
|
1641
|
-
</div>
|
|
1642
|
-
|
|
1643
|
-
<div
|
|
1644
|
-
style={{
|
|
1645
|
-
border: '1px solid var(--sv-color-border)',
|
|
1646
|
-
borderRadius: 'var(--sv-radius-lg)',
|
|
1647
|
-
overflow: 'hidden',
|
|
1648
|
-
}}
|
|
1649
|
-
>
|
|
1650
|
-
{TYPE_SCALE.map((t, i) => (
|
|
1651
|
-
<div
|
|
1652
|
-
key={t.token}
|
|
1653
|
-
style={{
|
|
1654
|
-
display: 'flex',
|
|
1655
|
-
alignItems: 'baseline',
|
|
1656
|
-
gap: 'var(--sv-space-4)',
|
|
1657
|
-
padding: 'var(--sv-space-3) var(--sv-space-4)',
|
|
1658
|
-
borderBottom:
|
|
1659
|
-
i < TYPE_SCALE.length - 1 ? '1px solid var(--sv-color-border)' : 'none',
|
|
1660
|
-
background:
|
|
1661
|
-
i % 2 === 0 ? 'var(--sv-color-surface)' : 'var(--sv-color-surface-sunken)',
|
|
1662
|
-
}}
|
|
1663
|
-
>
|
|
1664
|
-
<span
|
|
1665
|
-
style={{
|
|
1666
|
-
fontSize: `var(${t.token})`,
|
|
1667
|
-
color: 'var(--sv-color-text-primary)',
|
|
1668
|
-
lineHeight: 1.2,
|
|
1669
|
-
minWidth: 160,
|
|
1670
|
-
}}
|
|
1671
|
-
>
|
|
1672
|
-
{t.label}
|
|
1673
|
-
</span>
|
|
1674
|
-
<code
|
|
1675
|
-
style={{
|
|
1676
|
-
fontFamily: ffm,
|
|
1677
|
-
fontSize: '0.6875rem',
|
|
1678
|
-
color: 'var(--sv-color-text-muted)',
|
|
1679
|
-
flexShrink: 0,
|
|
1680
|
-
}}
|
|
1681
|
-
>
|
|
1682
|
-
{t.token}
|
|
1683
|
-
</code>
|
|
1684
|
-
<span
|
|
1685
|
-
style={{
|
|
1686
|
-
fontSize: 'var(--sv-font-size-caption)',
|
|
1687
|
-
color: 'var(--sv-color-text-muted)',
|
|
1688
|
-
marginLeft: 'auto',
|
|
1689
|
-
}}
|
|
1690
|
-
>
|
|
1691
|
-
{t.note}
|
|
1692
|
-
</span>
|
|
1693
|
-
</div>
|
|
1694
|
-
))}
|
|
1695
|
-
</div>
|
|
1696
|
-
|
|
1697
|
-
<div style={{ marginTop: 'var(--sv-space-4)' }}>
|
|
1698
|
-
<p
|
|
1699
|
-
style={{
|
|
1700
|
-
fontSize: 'var(--sv-font-size-caption)',
|
|
1701
|
-
color: 'var(--sv-color-text-muted)',
|
|
1702
|
-
marginBottom: 'var(--sv-space-2)',
|
|
1703
|
-
}}
|
|
1704
|
-
>
|
|
1705
|
-
Font weight tokens:
|
|
1706
|
-
</p>
|
|
1707
|
-
<Code>{`font-weight: var(--sv-font-weight-regular); /* 400 */
|
|
1708
|
-
font-weight: var(--sv-font-weight-medium); /* 500 */
|
|
1709
|
-
font-weight: var(--sv-font-weight-semibold); /* 600 */
|
|
1710
|
-
font-weight: var(--sv-font-weight-bold); /* 700 */`}</Code>
|
|
1711
|
-
</div>
|
|
1712
|
-
|
|
1713
|
-
<div style={{ marginTop: 'var(--sv-space-4)' }}>
|
|
1714
|
-
<p
|
|
1715
|
-
style={{
|
|
1716
|
-
fontSize: 'var(--sv-font-size-caption)',
|
|
1717
|
-
color: 'var(--sv-color-text-muted)',
|
|
1718
|
-
}}
|
|
1719
|
-
>
|
|
1720
|
-
Every size above is rem-based, so the whole scale responds to the in-app text-size
|
|
1721
|
-
control (Account → Preferences → Appearance, task 10.2), which sets{' '}
|
|
1722
|
-
<code>[data-text-size]</code> on <code><html></code> to override{' '}
|
|
1723
|
-
<code>--sv-text-size-scale</code> — see the "Text-size presets" section of
|
|
1724
|
-
the Token Gallery.
|
|
1725
|
-
</p>
|
|
1726
|
-
</div>
|
|
1727
|
-
</section>
|
|
1728
|
-
|
|
1729
|
-
{/* ── Spacing & Radius ─────────────────────────────────────────── */}
|
|
1730
|
-
<section style={{ marginBottom: 'var(--sv-space-12)' }}>
|
|
1731
|
-
<SectionHeader title="Spacing & radius" subtitle="4px base grid." />
|
|
1732
|
-
<div
|
|
1733
|
-
style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 'var(--sv-space-8)' }}
|
|
1734
|
-
>
|
|
1735
|
-
{/* Spacing */}
|
|
1736
|
-
<div>
|
|
1737
|
-
<p
|
|
1738
|
-
style={{
|
|
1739
|
-
fontSize: 'var(--sv-font-size-caption)',
|
|
1740
|
-
color: 'var(--sv-color-text-muted)',
|
|
1741
|
-
marginBottom: 'var(--sv-space-3)',
|
|
1742
|
-
}}
|
|
1743
|
-
>
|
|
1744
|
-
Spacing (--sv-space-*)
|
|
1745
|
-
</p>
|
|
1746
|
-
{(
|
|
1747
|
-
[
|
|
1748
|
-
'--sv-space-1',
|
|
1749
|
-
'--sv-space-2',
|
|
1750
|
-
'--sv-space-3',
|
|
1751
|
-
'--sv-space-4',
|
|
1752
|
-
'--sv-space-5',
|
|
1753
|
-
'--sv-space-6',
|
|
1754
|
-
'--sv-space-8',
|
|
1755
|
-
'--sv-space-10',
|
|
1756
|
-
'--sv-space-12',
|
|
1757
|
-
'--sv-space-16',
|
|
1758
|
-
] as const
|
|
1759
|
-
).map((t) => (
|
|
1760
|
-
<div
|
|
1761
|
-
key={t}
|
|
1762
|
-
style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 8 }}
|
|
1763
|
-
>
|
|
1764
|
-
<div
|
|
1765
|
-
style={{
|
|
1766
|
-
width: `var(${t})`,
|
|
1767
|
-
minWidth: 4,
|
|
1768
|
-
height: 10,
|
|
1769
|
-
background: 'var(--sv-color-accent)',
|
|
1770
|
-
borderRadius: 2,
|
|
1771
|
-
flexShrink: 0,
|
|
1772
|
-
}}
|
|
1773
|
-
/>
|
|
1774
|
-
<code
|
|
1775
|
-
style={{
|
|
1776
|
-
fontFamily: ffm,
|
|
1777
|
-
fontSize: '0.6875rem',
|
|
1778
|
-
color: 'var(--sv-color-text-muted)',
|
|
1779
|
-
}}
|
|
1780
|
-
>
|
|
1781
|
-
{t}
|
|
1782
|
-
</code>
|
|
1783
|
-
</div>
|
|
1784
|
-
))}
|
|
1785
|
-
</div>
|
|
1786
|
-
{/* Radius */}
|
|
1787
|
-
<div>
|
|
1788
|
-
<p
|
|
1789
|
-
style={{
|
|
1790
|
-
fontSize: 'var(--sv-font-size-caption)',
|
|
1791
|
-
color: 'var(--sv-color-text-muted)',
|
|
1792
|
-
marginBottom: 'var(--sv-space-3)',
|
|
1793
|
-
}}
|
|
1794
|
-
>
|
|
1795
|
-
Border radius (--sv-radius-*)
|
|
1796
|
-
</p>
|
|
1797
|
-
{(
|
|
1798
|
-
[
|
|
1799
|
-
'--sv-radius-sm',
|
|
1800
|
-
'--sv-radius-md',
|
|
1801
|
-
'--sv-radius-lg',
|
|
1802
|
-
'--sv-radius-xl',
|
|
1803
|
-
'--sv-radius-2xl',
|
|
1804
|
-
'--sv-radius-3xl',
|
|
1805
|
-
'--sv-radius-full',
|
|
1806
|
-
] as const
|
|
1807
|
-
).map((t) => (
|
|
1808
|
-
<div
|
|
1809
|
-
key={t}
|
|
1810
|
-
style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 }}
|
|
1811
|
-
>
|
|
1812
|
-
<div
|
|
1813
|
-
style={{
|
|
1814
|
-
width: 48,
|
|
1815
|
-
height: 24,
|
|
1816
|
-
background: 'var(--sv-color-accent)',
|
|
1817
|
-
borderRadius: `var(${t})`,
|
|
1818
|
-
flexShrink: 0,
|
|
1819
|
-
}}
|
|
1820
|
-
/>
|
|
1821
|
-
<code
|
|
1822
|
-
style={{
|
|
1823
|
-
fontFamily: ffm,
|
|
1824
|
-
fontSize: '0.6875rem',
|
|
1825
|
-
color: 'var(--sv-color-text-muted)',
|
|
1826
|
-
}}
|
|
1827
|
-
>
|
|
1828
|
-
{t}
|
|
1829
|
-
</code>
|
|
1830
|
-
</div>
|
|
1831
|
-
))}
|
|
1832
|
-
</div>
|
|
1833
|
-
</div>
|
|
1834
|
-
</section>
|
|
1835
|
-
|
|
1836
|
-
{/* ── Shadows ──────────────────────────────────────────────────── */}
|
|
1837
|
-
<section style={{ marginBottom: 'var(--sv-space-12)' }}>
|
|
1838
|
-
<SectionHeader title="Elevation & shadows" subtitle="Four levels, dark-mode adjusted." />
|
|
1839
|
-
<div
|
|
1840
|
-
style={{
|
|
1841
|
-
display: 'grid',
|
|
1842
|
-
gridTemplateColumns: 'repeat(4, 1fr)',
|
|
1843
|
-
gap: 'var(--sv-space-5)',
|
|
1844
|
-
}}
|
|
1845
|
-
>
|
|
1846
|
-
{(
|
|
1847
|
-
[
|
|
1848
|
-
'--sv-shadow-card',
|
|
1849
|
-
'--sv-shadow-hover',
|
|
1850
|
-
'--sv-shadow-popover',
|
|
1851
|
-
'--sv-shadow-overlay',
|
|
1852
|
-
] as const
|
|
1853
|
-
).map((t) => (
|
|
1854
|
-
<div
|
|
1855
|
-
key={t}
|
|
1856
|
-
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12 }}
|
|
1857
|
-
>
|
|
1858
|
-
<div
|
|
1859
|
-
style={{
|
|
1860
|
-
width: 64,
|
|
1861
|
-
height: 40,
|
|
1862
|
-
background: 'var(--sv-color-surface-raised)',
|
|
1863
|
-
boxShadow: `var(${t})`,
|
|
1864
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
1865
|
-
}}
|
|
1866
|
-
/>
|
|
1867
|
-
<code
|
|
1868
|
-
style={{
|
|
1869
|
-
fontFamily: ffm,
|
|
1870
|
-
fontSize: '0.6875rem',
|
|
1871
|
-
color: 'var(--sv-color-text-muted)',
|
|
1872
|
-
textAlign: 'center',
|
|
1873
|
-
}}
|
|
1874
|
-
>
|
|
1875
|
-
{t.replace('--sv-shadow-', '')}
|
|
1876
|
-
</code>
|
|
1877
|
-
</div>
|
|
1878
|
-
))}
|
|
1879
|
-
</div>
|
|
1880
|
-
</section>
|
|
1881
|
-
|
|
1882
|
-
{/* ── Component Gallery ─────────────────────────────────────────── */}
|
|
1883
|
-
<section style={{ marginBottom: 'var(--sv-space-12)' }}>
|
|
1884
|
-
<SectionHeader
|
|
1885
|
-
title="Component gallery"
|
|
1886
|
-
subtitle="All 77 components — click each story in the sidebar for the full API, variants, and controls."
|
|
1887
|
-
/>
|
|
1888
|
-
|
|
1889
|
-
<div style={{ marginBottom: 'var(--sv-space-6)' }}>
|
|
1890
|
-
<SystemBanner variant="info">
|
|
1891
|
-
All components reference <code style={{ fontFamily: ffm }}>--sv-*</code> tokens
|
|
1892
|
-
internally — they automatically adapt to dark mode and instance theming without any
|
|
1893
|
-
extra configuration.
|
|
1894
|
-
</SystemBanner>
|
|
1895
|
-
</div>
|
|
1896
|
-
|
|
1897
|
-
<div
|
|
1898
|
-
style={{
|
|
1899
|
-
display: 'grid',
|
|
1900
|
-
gridTemplateColumns: 'repeat(auto-fill, minmax(340px, 1fr))',
|
|
1901
|
-
gap: 'var(--sv-space-5)',
|
|
1902
|
-
}}
|
|
1903
|
-
>
|
|
1904
|
-
{/* Button */}
|
|
1905
|
-
<ComponentCard
|
|
1906
|
-
name="Button"
|
|
1907
|
-
importLine="import { Button } from '@sovereignfs/ui';"
|
|
1908
|
-
usage="Primary interactive control. Four variants: primary, secondary, ghost, destructive. Three sizes: lg, md (default), sm. loading disables the button, sets aria-busy, and shows a spinner. 44px min-height under (pointer: coarse); hover behind (hover: hover)."
|
|
1909
|
-
>
|
|
1910
|
-
<Button>Primary</Button>
|
|
1911
|
-
<Button variant="secondary">Secondary</Button>
|
|
1912
|
-
<Button variant="ghost">Ghost</Button>
|
|
1913
|
-
<Button variant="destructive" size="sm">
|
|
1914
|
-
Delete
|
|
1915
|
-
</Button>
|
|
1916
|
-
<Button size="lg">Large</Button>
|
|
1917
|
-
<Button loading>Saving…</Button>
|
|
1918
|
-
</ComponentCard>
|
|
1919
|
-
|
|
1920
|
-
{/* Badge */}
|
|
1921
|
-
<ComponentCard
|
|
1922
|
-
name="Badge"
|
|
1923
|
-
importLine="import { Badge } from '@sovereignfs/ui';"
|
|
1924
|
-
usage="Compact label for roles (role), lifecycle states (status), and type/version tags (mono). xs/sm/md/lg sizes; ALL CAPS by default, or uppercase={false} for title case. RSC-safe."
|
|
1925
|
-
>
|
|
1926
|
-
<Badge variant="role">Admin</Badge>
|
|
1927
|
-
<Badge variant="status" status="active">
|
|
1928
|
-
Active
|
|
1929
|
-
</Badge>
|
|
1930
|
-
<Badge variant="status" status="invited">
|
|
1931
|
-
Invited
|
|
1932
|
-
</Badge>
|
|
1933
|
-
<Badge variant="mono">v1.2.0</Badge>
|
|
1934
|
-
<Badge variant="role" size="lg" uppercase={false}>
|
|
1935
|
-
Owner
|
|
1936
|
-
</Badge>
|
|
1937
|
-
<Badge variant="status" status="active" size="xs">
|
|
1938
|
-
Connected
|
|
1939
|
-
</Badge>
|
|
1940
|
-
</ComponentCard>
|
|
1941
|
-
|
|
1942
|
-
{/* StatusBadge */}
|
|
1943
|
-
<ComponentCard
|
|
1944
|
-
name="StatusBadge"
|
|
1945
|
-
importLine="import { StatusBadge } from '@sovereignfs/ui';"
|
|
1946
|
-
usage="Inline status indicator for editor sync, draft, conflict, and delete-pending states. RSC-safe."
|
|
1947
|
-
>
|
|
1948
|
-
<StatusBadge status="draft" />
|
|
1949
|
-
<StatusBadge status="synced" />
|
|
1950
|
-
<StatusBadge status="conflict" />
|
|
1951
|
-
<StatusBadge status="pending-delete" />
|
|
1952
|
-
</ComponentCard>
|
|
1953
|
-
|
|
1954
|
-
{/* BalanceChip */}
|
|
1955
|
-
<ComponentCard
|
|
1956
|
-
name="BalanceChip"
|
|
1957
|
-
importLine="import { BalanceChip } from '@sovereignfs/ui';"
|
|
1958
|
-
usage="Inline net-balance indicator — green when owed to them, red when they owe, neutral when settled. Not tied to expense-splitting specifically."
|
|
1959
|
-
>
|
|
1960
|
-
<BalanceChip amountCents={2500} currency="USD" />
|
|
1961
|
-
<BalanceChip amountCents={-1350} currency="USD" />
|
|
1962
|
-
<BalanceChip amountCents={0} currency="USD" />
|
|
1963
|
-
</ComponentCard>
|
|
1964
|
-
|
|
1965
|
-
{/* Input */}
|
|
1966
|
-
<ComponentCard
|
|
1967
|
-
name="Input"
|
|
1968
|
-
importLine="import { Input } from '@sovereignfs/ui';"
|
|
1969
|
-
usage="Primitive text field. No label built-in — always pair with <label> for accessibility. Forwards all native input props."
|
|
1970
|
-
>
|
|
1971
|
-
<div style={{ width: '100%', display: 'flex', flexDirection: 'column', gap: 6 }}>
|
|
1972
|
-
<Input placeholder="Email address" type="email" style={{ width: '100%' }} />
|
|
1973
|
-
<Input placeholder="Disabled" disabled style={{ width: '100%' }} />
|
|
1974
|
-
</div>
|
|
1975
|
-
</ComponentCard>
|
|
1976
|
-
|
|
1977
|
-
{/* CurrencyInput */}
|
|
1978
|
-
<ComponentCard
|
|
1979
|
-
name="CurrencyInput"
|
|
1980
|
-
importLine="import { CurrencyInput } from '@sovereignfs/ui';"
|
|
1981
|
-
usage="Decimal amount entry that reports its value as integer cents. Preserves in-progress typing instead of reformatting on every keystroke."
|
|
1982
|
-
>
|
|
1983
|
-
<CurrencyInputDemo />
|
|
1984
|
-
</ComponentCard>
|
|
1985
|
-
|
|
1986
|
-
{/* TagInput */}
|
|
1987
|
-
<ComponentCard
|
|
1988
|
-
name="TagInput"
|
|
1989
|
-
importLine="import { TagInput } from '@sovereignfs/ui';"
|
|
1990
|
-
usage="Controlled multi-value input for tags and frontmatter arrays. Enter/comma adds, Backspace removes, paste can split multiple tags."
|
|
1991
|
-
>
|
|
1992
|
-
<TagInputDemo />
|
|
1993
|
-
</ComponentCard>
|
|
1994
|
-
|
|
1995
|
-
{/* SuggestionInput */}
|
|
1996
|
-
<ComponentCard
|
|
1997
|
-
name="SuggestionInput"
|
|
1998
|
-
importLine="import { SuggestionInput } from '@sovereignfs/ui';"
|
|
1999
|
-
usage="Text field with an anchored, keyboard-navigable async suggestion list, plus an optional trailing 'create new' row. Built on Popover."
|
|
2000
|
-
>
|
|
2001
|
-
<div style={{ width: '100%' }}>
|
|
2002
|
-
<SuggestionInputDemo />
|
|
2003
|
-
</div>
|
|
2004
|
-
</ComponentCard>
|
|
2005
|
-
|
|
2006
|
-
{/* IconPicker */}
|
|
2007
|
-
<ComponentCard
|
|
2008
|
-
name="IconPicker"
|
|
2009
|
-
importLine="import { IconPicker } from '@sovereignfs/ui';"
|
|
2010
|
-
usage="Trigger button + Popover grid for a curated, bounded icon set (e.g. a plugin's category icons) — not the full icon library."
|
|
2011
|
-
>
|
|
2012
|
-
<IconPickerDemo />
|
|
2013
|
-
</ComponentCard>
|
|
2014
|
-
|
|
2015
|
-
{/* ColorPicker */}
|
|
2016
|
-
<ComponentCard
|
|
2017
|
-
name="ColorPicker"
|
|
2018
|
-
importLine="import { ColorPicker } from '@sovereignfs/ui';"
|
|
2019
|
-
usage="Curated swatch suggestions plus a native color-input trigger for picking any color — not just the curated set."
|
|
2020
|
-
>
|
|
2021
|
-
<ColorPickerDemo />
|
|
2022
|
-
</ComponentCard>
|
|
2023
|
-
|
|
2024
|
-
{/* QuantityStepper */}
|
|
2025
|
-
<ComponentCard
|
|
2026
|
-
name="QuantityStepper"
|
|
2027
|
-
importLine="import { QuantityStepper } from '@sovereignfs/ui';"
|
|
2028
|
-
usage="Numeric input with +/- buttons and an optional read-only unit suffix. Supports fractional step values (e.g. 0.5 kg)."
|
|
2029
|
-
>
|
|
2030
|
-
<QuantityStepperDemo />
|
|
2031
|
-
</ComponentCard>
|
|
2032
|
-
|
|
2033
|
-
{/* Select */}
|
|
2034
|
-
<ComponentCard
|
|
2035
|
-
name="Select"
|
|
2036
|
-
importLine="import { Select } from '@sovereignfs/ui';"
|
|
2037
|
-
usage="Styled native <select> — same visual language as Input. Preserves native picker on mobile. RSC-safe."
|
|
2038
|
-
>
|
|
2039
|
-
<div style={{ width: '100%' }}>
|
|
2040
|
-
<Select defaultValue="admin" style={{ width: '100%' }}>
|
|
2041
|
-
<option value="user">User</option>
|
|
2042
|
-
<option value="admin">Admin</option>
|
|
2043
|
-
<option value="owner">Owner</option>
|
|
2044
|
-
</Select>
|
|
2045
|
-
</div>
|
|
2046
|
-
</ComponentCard>
|
|
2047
|
-
|
|
2048
|
-
{/* Toggle */}
|
|
2049
|
-
<ComponentCard
|
|
2050
|
-
name="Toggle"
|
|
2051
|
-
importLine="import { Toggle } from '@sovereignfs/ui';"
|
|
2052
|
-
usage="38×22px binary switch. Renders as role='switch' for screen reader support. aria-label is required."
|
|
2053
|
-
>
|
|
2054
|
-
<ToggleDemo />
|
|
2055
|
-
<span
|
|
2056
|
-
style={{
|
|
2057
|
-
fontFamily: ff,
|
|
2058
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
2059
|
-
color: 'var(--sv-color-text-muted)',
|
|
2060
|
-
}}
|
|
2061
|
-
>
|
|
2062
|
-
Click to toggle
|
|
2063
|
-
</span>
|
|
2064
|
-
</ComponentCard>
|
|
2065
|
-
|
|
2066
|
-
{/* SegmentedControl */}
|
|
2067
|
-
<ComponentCard
|
|
2068
|
-
name="SegmentedControl"
|
|
2069
|
-
importLine="import { SegmentedControl } from '@sovereignfs/ui';"
|
|
2070
|
-
usage="Pill-based 2–3 option picker for inline use (role selector, theme switcher). Renders as role='radiogroup'."
|
|
2071
|
-
>
|
|
2072
|
-
<SegmentedDemo />
|
|
2073
|
-
</ComponentCard>
|
|
2074
|
-
|
|
2075
|
-
{/* SplitMethodSelector */}
|
|
2076
|
-
<ComponentCard
|
|
2077
|
-
name="SplitMethodSelector"
|
|
2078
|
-
importLine="import { SplitMethodSelector } from '@sovereignfs/ui';"
|
|
2079
|
-
usage="The four-way Equal/Amount/Percentage/Shares picker shared by any cost-splitting plugin. A thin SegmentedControl preset."
|
|
2080
|
-
>
|
|
2081
|
-
<SplitMethodSelectorDemo />
|
|
2082
|
-
</ComponentCard>
|
|
2083
|
-
|
|
2084
|
-
{/* Tabs */}
|
|
2085
|
-
<ComponentCard
|
|
2086
|
-
name="Tabs"
|
|
2087
|
-
importLine="import { Tabs } from '@sovereignfs/ui';"
|
|
2088
|
-
usage="Underline tab nav. Stateless — caller owns value + onChange. Scrolls horizontally on mobile."
|
|
2089
|
-
>
|
|
2090
|
-
<TabsDemo />
|
|
2091
|
-
</ComponentCard>
|
|
2092
|
-
|
|
2093
|
-
{/* Icon */}
|
|
2094
|
-
<ComponentCard
|
|
2095
|
-
name="Icon"
|
|
2096
|
-
importLine="import { Icon } from '@sovereignfs/ui';"
|
|
2097
|
-
usage="SVG icon primitive. 96 bundled icons. Decorative: aria-hidden. Meaningful: aria-label. Three sizes: sm, md, lg."
|
|
2098
|
-
>
|
|
2099
|
-
{(
|
|
2100
|
-
[
|
|
2101
|
-
'house',
|
|
2102
|
-
'settings',
|
|
2103
|
-
'bell',
|
|
2104
|
-
'user',
|
|
2105
|
-
'shield',
|
|
2106
|
-
'mail',
|
|
2107
|
-
'search',
|
|
2108
|
-
'plus',
|
|
2109
|
-
'trash-2',
|
|
2110
|
-
'external-link',
|
|
2111
|
-
] as const
|
|
2112
|
-
).map((n) => (
|
|
2113
|
-
<Icon key={n} name={n} size="md" aria-hidden />
|
|
2114
|
-
))}
|
|
2115
|
-
</ComponentCard>
|
|
2116
|
-
|
|
2117
|
-
{/* Dialog */}
|
|
2118
|
-
<ComponentCard
|
|
2119
|
-
name="Dialog"
|
|
2120
|
-
importLine="import { Dialog } from '@sovereignfs/ui';"
|
|
2121
|
-
usage="Modal surface (scrim + panel). Esc, scrim-click, focus trap. Sizes: sm, md, lg, full. Mobile renders fullscreen."
|
|
2122
|
-
>
|
|
2123
|
-
<DialogDemo />
|
|
2124
|
-
</ComponentCard>
|
|
2125
|
-
|
|
2126
|
-
{/* Drawer */}
|
|
2127
|
-
<ComponentCard
|
|
2128
|
-
name="Drawer"
|
|
2129
|
-
importLine="import { Drawer } from '@sovereignfs/ui';"
|
|
2130
|
-
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."
|
|
2131
|
-
>
|
|
2132
|
-
<DrawerDemo />
|
|
2133
|
-
</ComponentCard>
|
|
2134
|
-
|
|
2135
|
-
{/* OverlayHeader */}
|
|
2136
|
-
<ComponentCard
|
|
2137
|
-
name="OverlayHeader"
|
|
2138
|
-
importLine="import { OverlayHeader } from '@sovereignfs/ui';"
|
|
2139
|
-
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."
|
|
2140
|
-
>
|
|
2141
|
-
<OverlayHeaderDemo />
|
|
2142
|
-
</ComponentCard>
|
|
2143
|
-
|
|
2144
|
-
{/* Sheet */}
|
|
2145
|
-
<ComponentCard
|
|
2146
|
-
name="Sheet"
|
|
2147
|
-
importLine="import { Sheet } from '@sovereignfs/ui';"
|
|
2148
|
-
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."
|
|
2149
|
-
>
|
|
2150
|
-
<SheetDemo />
|
|
2151
|
-
</ComponentCard>
|
|
2152
|
-
|
|
2153
|
-
{/* ConfirmDialog */}
|
|
2154
|
-
<ComponentCard
|
|
2155
|
-
name="ConfirmDialog"
|
|
2156
|
-
importLine="import { ConfirmDialog } from '@sovereignfs/ui';"
|
|
2157
|
-
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."
|
|
2158
|
-
>
|
|
2159
|
-
<ConfirmDialogDemo />
|
|
2160
|
-
</ComponentCard>
|
|
2161
|
-
|
|
2162
|
-
{/* Popover */}
|
|
2163
|
-
<ComponentCard
|
|
2164
|
-
name="Popover"
|
|
2165
|
-
importLine="import { Popover } from '@sovereignfs/ui';"
|
|
2166
|
-
usage="Floating panel anchored below a trigger. Non-modal. Closes on outside click or Escape. Left or right aligned."
|
|
2167
|
-
>
|
|
2168
|
-
<PopoverDemo />
|
|
2169
|
-
</ComponentCard>
|
|
2170
|
-
|
|
2171
|
-
{/* Menu */}
|
|
2172
|
-
<ComponentCard
|
|
2173
|
-
name="Menu"
|
|
2174
|
-
importLine="import { Menu } from '@sovereignfs/ui';"
|
|
2175
|
-
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."
|
|
2176
|
-
>
|
|
2177
|
-
<MenuDemo />
|
|
2178
|
-
</ComponentCard>
|
|
2179
|
-
|
|
2180
|
-
{/* MenuEntries */}
|
|
2181
|
-
<ComponentCard
|
|
2182
|
-
name="MenuEntries"
|
|
2183
|
-
importLine="import { MenuEntries } from '@sovereignfs/ui';"
|
|
2184
|
-
usage='The <ul role="menu"> item list itself, factored out of Menu so other adaptive-surface components (ContextMenu, Menubar) can render the exact same items/destructive/checked/icon markup inside their own positioning shell instead of re-deriving it. No panel chrome of its own — the background/border/shadow around it belongs to whichever surface hosts it.'
|
|
2185
|
-
>
|
|
2186
|
-
<MenuEntriesDemo />
|
|
2187
|
-
</ComponentCard>
|
|
2188
|
-
|
|
2189
|
-
{/* Calendar */}
|
|
2190
|
-
<ComponentCard
|
|
2191
|
-
name="Calendar"
|
|
2192
|
-
importLine="import { Calendar } from '@sovereignfs/ui';"
|
|
2193
|
-
usage="Keyboard-navigable month grid. Date-only (no time/range yet). Arrow keys, Home/End, PageUp/PageDown, Enter/Space to select."
|
|
2194
|
-
>
|
|
2195
|
-
<CalendarDemo />
|
|
2196
|
-
</ComponentCard>
|
|
2197
|
-
|
|
2198
|
-
{/* DatePicker */}
|
|
2199
|
-
<ComponentCard
|
|
2200
|
-
name="DatePicker"
|
|
2201
|
-
importLine="import { DatePicker } from '@sovereignfs/ui';"
|
|
2202
|
-
usage="Form field pairing a trigger with Calendar: Popover on desktop, Drawer on mobile. Built-in trigger, unlike Menu's caller-supplied one."
|
|
2203
|
-
>
|
|
2204
|
-
<DatePickerDemo />
|
|
2205
|
-
</ComponentCard>
|
|
2206
|
-
|
|
2207
|
-
{/* SystemBanner */}
|
|
2208
|
-
<ComponentCard
|
|
2209
|
-
name="SystemBanner"
|
|
2210
|
-
importLine="import { SystemBanner } from '@sovereignfs/ui';"
|
|
2211
|
-
usage="Full-width sticky strip for platform-level notices. Three variants: info, warning, error. Dismissible optional."
|
|
2212
|
-
>
|
|
2213
|
-
<div style={{ width: '100%', display: 'flex', flexDirection: 'column', gap: 4 }}>
|
|
2214
|
-
<SystemBanner variant="info">Read-only mode during migration.</SystemBanner>
|
|
2215
|
-
<SystemBanner variant="warning">License expires in 7 days.</SystemBanner>
|
|
2216
|
-
<SystemBanner variant="error">Maintenance mode active.</SystemBanner>
|
|
2217
|
-
</div>
|
|
2218
|
-
</ComponentCard>
|
|
2219
|
-
|
|
2220
|
-
{/* Toast */}
|
|
2221
|
-
<ComponentCard
|
|
2222
|
-
name="Toast"
|
|
2223
|
-
importLine="import { ToastProvider, useToast } from '@sovereignfs/ui';"
|
|
2224
|
-
usage="Fixed top-right on desktop, bottom-center on mobile. Wrap app in ToastProvider; call useToast().show() imperatively. Six categories."
|
|
2225
|
-
>
|
|
2226
|
-
<ToastProvider>
|
|
2227
|
-
<ToastDemo />
|
|
2228
|
-
</ToastProvider>
|
|
2229
|
-
</ComponentCard>
|
|
2230
|
-
|
|
2231
|
-
{/* Card */}
|
|
2232
|
-
<ComponentCard
|
|
2233
|
-
name="Card"
|
|
2234
|
-
importLine="import { Card } from '@sovereignfs/ui';"
|
|
2235
|
-
usage="Surface container with border, shadow, and padding. Use as='article' or 'li' for semantics. Add interactive for hover/focus styles on clickable cards."
|
|
2236
|
-
>
|
|
2237
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 8, width: '100%' }}>
|
|
2238
|
-
<Card padding="sm">Small padding card</Card>
|
|
2239
|
-
<Card padding="md" interactive>
|
|
2240
|
-
Interactive card — hover me
|
|
2241
|
-
</Card>
|
|
2242
|
-
</div>
|
|
2243
|
-
</ComponentCard>
|
|
2244
|
-
|
|
2245
|
-
{/* CardTile */}
|
|
2246
|
-
<ComponentCard
|
|
2247
|
-
name="CardTile"
|
|
2248
|
-
importLine="import { CardTile, CardTileGrid, NewCardTile } from '@sovereignfs/ui';"
|
|
2249
|
-
usage="A colored/iconed banner over a footer label, for a grid of navigable top-level entities (projects, boards). Card can't express this — its padding always wraps the whole box, with no bleed slot for a full-width banner. Not link-aware; wrap each tile in the host router's own link component. Pair with NewCardTile for the grid's 'add new' affordance. The icon variant below swaps the card chrome for a plain Finder-style icon + label, for entities that read as a single glyph + name (e.g. a folder)."
|
|
2250
|
-
>
|
|
2251
|
-
<CardTileGrid minTileWidth={140}>
|
|
2252
|
-
<CardTile banner={<Icon name="folder" size="lg" aria-hidden={true} />}>
|
|
2253
|
-
Q4 Planning
|
|
2254
|
-
</CardTile>
|
|
2255
|
-
<CardTile bannerColor="#c7d8ff">Marketing site</CardTile>
|
|
2256
|
-
<NewCardTile label="New project" />
|
|
2257
|
-
</CardTileGrid>
|
|
2258
|
-
<div style={{ marginTop: 'var(--sv-space-4)' }}>
|
|
2259
|
-
<CardTileGrid dense minTileWidth={100}>
|
|
2260
|
-
<CardTile
|
|
2261
|
-
variant="icon"
|
|
2262
|
-
banner={<Icon name="folder-closed" size="lg" aria-hidden={true} />}
|
|
2263
|
-
>
|
|
2264
|
-
Handbook
|
|
2265
|
-
</CardTile>
|
|
2266
|
-
<CardTile
|
|
2267
|
-
variant="icon"
|
|
2268
|
-
banner={<Icon name="folder-closed" size="lg" aria-hidden={true} />}
|
|
2269
|
-
>
|
|
2270
|
-
Q4 Planning
|
|
2271
|
-
</CardTile>
|
|
2272
|
-
<NewCardTile variant="icon" label="New" />
|
|
2273
|
-
</CardTileGrid>
|
|
2274
|
-
</div>
|
|
2275
|
-
</ComponentCard>
|
|
2276
|
-
|
|
2277
|
-
{/* FormField */}
|
|
2278
|
-
<ComponentCard
|
|
2279
|
-
name="FormField"
|
|
2280
|
-
importLine="import { FormField } from '@sovereignfs/ui';"
|
|
2281
|
-
usage="Accessible label + input wrapper. The render-prop `children` receives field props (id, aria-describedby, aria-invalid) to spread onto the control, so hint/error text stays wired to it."
|
|
2282
|
-
>
|
|
2283
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 12, width: '100%' }}>
|
|
2284
|
-
<FormField label="Email" hint="We'll never share this." id="ov-email">
|
|
2285
|
-
{(field) => <Input {...field} type="email" placeholder="you@example.com" />}
|
|
2286
|
-
</FormField>
|
|
2287
|
-
<FormField label="Password" error="Must be 8+ characters." id="ov-pw">
|
|
2288
|
-
{(field) => <Input {...field} type="password" />}
|
|
2289
|
-
</FormField>
|
|
2290
|
-
</div>
|
|
2291
|
-
</ComponentCard>
|
|
2292
|
-
|
|
2293
|
-
{/* Textarea */}
|
|
2294
|
-
<ComponentCard
|
|
2295
|
-
name="Textarea"
|
|
2296
|
-
importLine="import { Textarea } from '@sovereignfs/ui';"
|
|
2297
|
-
usage="The primitive multi-line text field. Forwards all native textarea props. Pair with FormField or a <label> for accessibility."
|
|
2298
|
-
>
|
|
2299
|
-
<Textarea
|
|
2300
|
-
aria-label="Description"
|
|
2301
|
-
placeholder="Add a description…"
|
|
2302
|
-
style={{ width: '100%' }}
|
|
2303
|
-
/>
|
|
2304
|
-
</ComponentCard>
|
|
2305
|
-
|
|
2306
|
-
{/* CodeTextarea */}
|
|
2307
|
-
<ComponentCard
|
|
2308
|
-
name="CodeTextarea"
|
|
2309
|
-
importLine="import { CodeTextarea } from '@sovereignfs/ui';"
|
|
2310
|
-
usage="Monospace textarea for Markdown, YAML, JSON, and raw frontmatter editing. Pair with FormField for labels and validation."
|
|
2311
|
-
>
|
|
2312
|
-
<CodeTextarea
|
|
2313
|
-
aria-label="YAML source"
|
|
2314
|
-
defaultValue={'title: Release notes\ntags:\n - launch'}
|
|
2315
|
-
rows={5}
|
|
2316
|
-
style={{ width: '100%' }}
|
|
2317
|
-
/>
|
|
2318
|
-
</ComponentCard>
|
|
2319
|
-
|
|
2320
|
-
{/* SplitPane */}
|
|
2321
|
-
<ComponentCard
|
|
2322
|
-
name="SplitPane"
|
|
2323
|
-
importLine="import { SplitPane } from '@sovereignfs/ui';"
|
|
2324
|
-
usage="Responsive editor/preview or list/detail layout. Desktop panes are resizable with pointer and keyboard controls; mobile stacks to one column."
|
|
2325
|
-
>
|
|
2326
|
-
<SplitPane
|
|
2327
|
-
defaultPrimarySize={44}
|
|
2328
|
-
primary={
|
|
2329
|
-
<div
|
|
2330
|
-
style={{
|
|
2331
|
-
padding: 'var(--sv-space-3)',
|
|
2332
|
-
fontFamily: ffm,
|
|
2333
|
-
fontSize: 'var(--sv-font-size-xs)',
|
|
2334
|
-
color: 'var(--sv-color-text-primary)',
|
|
2335
|
-
}}
|
|
2336
|
-
>
|
|
2337
|
-
# Draft
|
|
2338
|
-
</div>
|
|
2339
|
-
}
|
|
2340
|
-
secondary={
|
|
2341
|
-
<div
|
|
2342
|
-
style={{
|
|
2343
|
-
padding: 'var(--sv-space-3)',
|
|
2344
|
-
fontFamily: ff,
|
|
2345
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
2346
|
-
color: 'var(--sv-color-text-primary)',
|
|
2347
|
-
}}
|
|
2348
|
-
>
|
|
2349
|
-
Draft preview
|
|
2350
|
-
</div>
|
|
2351
|
-
}
|
|
2352
|
-
/>
|
|
2353
|
-
</ComponentCard>
|
|
2354
|
-
|
|
2355
|
-
{/* ThreeColumnLayout */}
|
|
2356
|
-
<ComponentCard
|
|
2357
|
-
name="ThreeColumnLayout"
|
|
2358
|
-
importLine="import { ThreeColumnLayout } from '@sovereignfs/ui';"
|
|
2359
|
-
usage="Sidebar + main + optional detail column, for list-app shells (Tasks, Shopper). Purely positional — pass 2 children for sidebar+main, or 3 for sidebar+main+detail; the third is simply omitted when nothing is selected."
|
|
2360
|
-
>
|
|
2361
|
-
<div style={{ width: '100%', height: '220px' }}>
|
|
2362
|
-
<ThreeColumnLayout sidebarWidth={120} detailWidth={140}>
|
|
2363
|
-
<div
|
|
2364
|
-
style={{
|
|
2365
|
-
padding: 'var(--sv-space-3)',
|
|
2366
|
-
fontFamily: ffm,
|
|
2367
|
-
fontSize: 'var(--sv-font-size-xs)',
|
|
2368
|
-
color: 'var(--sv-color-text-primary)',
|
|
2369
|
-
}}
|
|
2370
|
-
>
|
|
2371
|
-
Sidebar
|
|
2372
|
-
</div>
|
|
2373
|
-
<div
|
|
2374
|
-
style={{
|
|
2375
|
-
padding: 'var(--sv-space-3)',
|
|
2376
|
-
fontFamily: ffm,
|
|
2377
|
-
fontSize: 'var(--sv-font-size-xs)',
|
|
2378
|
-
color: 'var(--sv-color-text-primary)',
|
|
2379
|
-
}}
|
|
2380
|
-
>
|
|
2381
|
-
Main
|
|
2382
|
-
</div>
|
|
2383
|
-
<div
|
|
2384
|
-
style={{
|
|
2385
|
-
padding: 'var(--sv-space-3)',
|
|
2386
|
-
fontFamily: ffm,
|
|
2387
|
-
fontSize: 'var(--sv-font-size-xs)',
|
|
2388
|
-
color: 'var(--sv-color-text-primary)',
|
|
2389
|
-
}}
|
|
2390
|
-
>
|
|
2391
|
-
Detail
|
|
2392
|
-
</div>
|
|
2393
|
-
</ThreeColumnLayout>
|
|
2394
|
-
</div>
|
|
2395
|
-
</ComponentCard>
|
|
2396
|
-
|
|
2397
|
-
{/* HeaderFooterLayout */}
|
|
2398
|
-
<ComponentCard
|
|
2399
|
-
name="HeaderFooterLayout"
|
|
2400
|
-
importLine="import { HeaderFooterLayout } from '@sovereignfs/ui';"
|
|
2401
|
-
usage="Header + main + optional footer, both fixed-height and independently optional; main always claims the remaining height. The vertical counterpart to ThreeColumnLayout."
|
|
2402
|
-
>
|
|
2403
|
-
<div style={{ width: '100%', height: '220px' }}>
|
|
2404
|
-
<HeaderFooterLayout
|
|
2405
|
-
headerHeight={36}
|
|
2406
|
-
footerHeight={36}
|
|
2407
|
-
header={
|
|
2408
|
-
<div
|
|
2409
|
-
style={{
|
|
2410
|
-
display: 'flex',
|
|
2411
|
-
alignItems: 'center',
|
|
2412
|
-
justifyContent: 'center',
|
|
2413
|
-
height: '100%',
|
|
2414
|
-
fontFamily: ffm,
|
|
2415
|
-
fontSize: 'var(--sv-font-size-xs)',
|
|
2416
|
-
color: 'var(--sv-color-text-primary)',
|
|
2417
|
-
}}
|
|
2418
|
-
>
|
|
2419
|
-
Header
|
|
2420
|
-
</div>
|
|
2421
|
-
}
|
|
2422
|
-
footer={
|
|
2423
|
-
<div
|
|
2424
|
-
style={{
|
|
2425
|
-
display: 'flex',
|
|
2426
|
-
alignItems: 'center',
|
|
2427
|
-
justifyContent: 'center',
|
|
2428
|
-
height: '100%',
|
|
2429
|
-
fontFamily: ffm,
|
|
2430
|
-
fontSize: 'var(--sv-font-size-xs)',
|
|
2431
|
-
color: 'var(--sv-color-text-primary)',
|
|
2432
|
-
}}
|
|
2433
|
-
>
|
|
2434
|
-
Footer
|
|
2435
|
-
</div>
|
|
2436
|
-
}
|
|
2437
|
-
>
|
|
2438
|
-
<div
|
|
2439
|
-
style={{
|
|
2440
|
-
padding: 'var(--sv-space-3)',
|
|
2441
|
-
fontFamily: ffm,
|
|
2442
|
-
fontSize: 'var(--sv-font-size-xs)',
|
|
2443
|
-
color: 'var(--sv-color-text-primary)',
|
|
2444
|
-
}}
|
|
2445
|
-
>
|
|
2446
|
-
Main (scrolls)
|
|
2447
|
-
</div>
|
|
2448
|
-
</HeaderFooterLayout>
|
|
2449
|
-
</div>
|
|
2450
|
-
</ComponentCard>
|
|
2451
|
-
|
|
2452
|
-
{/* RootLayout */}
|
|
2453
|
-
<ComponentCard
|
|
2454
|
-
name="RootLayout"
|
|
2455
|
-
importLine="import { RootLayout } from '@sovereignfs/ui';"
|
|
2456
|
-
usage="The root-level layout a plugin's page tree sits in. variant='sidebar' is web-only (sidebar + main); variant='shell' is mobile-only (header + main + footer) — same children, different fork per breakpoint via ResponsiveSurface. Shown here: variant='header', identical on both breakpoints."
|
|
2457
|
-
>
|
|
2458
|
-
<div style={{ width: '100%', height: '160px' }}>
|
|
2459
|
-
<RootLayout variant="header" headerHeight={32}>
|
|
2460
|
-
<div
|
|
2461
|
-
style={{
|
|
2462
|
-
display: 'flex',
|
|
2463
|
-
alignItems: 'center',
|
|
2464
|
-
justifyContent: 'center',
|
|
2465
|
-
height: '100%',
|
|
2466
|
-
fontFamily: ffm,
|
|
2467
|
-
fontSize: 'var(--sv-font-size-xs)',
|
|
2468
|
-
color: 'var(--sv-color-text-primary)',
|
|
2469
|
-
}}
|
|
2470
|
-
>
|
|
2471
|
-
Header
|
|
2472
|
-
</div>
|
|
2473
|
-
<div
|
|
2474
|
-
style={{
|
|
2475
|
-
padding: 'var(--sv-space-3)',
|
|
2476
|
-
fontFamily: ffm,
|
|
2477
|
-
fontSize: 'var(--sv-font-size-xs)',
|
|
2478
|
-
color: 'var(--sv-color-text-primary)',
|
|
2479
|
-
}}
|
|
2480
|
-
>
|
|
2481
|
-
Main
|
|
2482
|
-
</div>
|
|
2483
|
-
</RootLayout>
|
|
2484
|
-
</div>
|
|
2485
|
-
</ComponentCard>
|
|
2486
|
-
|
|
2487
|
-
{/* PageLayout */}
|
|
2488
|
-
<ComponentCard
|
|
2489
|
-
name="PageLayout"
|
|
2490
|
-
importLine="import { PageLayout } from '@sovereignfs/ui';"
|
|
2491
|
-
usage="A single page's content area, nested inside RootLayout's main slot. No padding by default (opt in via padding, same scale as PageContainer). The optional header is page-specific — a board title/toolbar, not the app-level header."
|
|
2492
|
-
>
|
|
2493
|
-
<div
|
|
2494
|
-
style={{
|
|
2495
|
-
width: '100%',
|
|
2496
|
-
height: '160px',
|
|
2497
|
-
border: '1px solid var(--sv-color-border)',
|
|
2498
|
-
}}
|
|
2499
|
-
>
|
|
2500
|
-
<PageLayout
|
|
2501
|
-
padding="sm"
|
|
2502
|
-
header={
|
|
2503
|
-
<div
|
|
2504
|
-
style={{
|
|
2505
|
-
padding: 'var(--sv-space-2) var(--sv-space-3)',
|
|
2506
|
-
borderBottom: '1px solid var(--sv-color-border)',
|
|
2507
|
-
fontFamily: ffm,
|
|
2508
|
-
fontSize: 'var(--sv-font-size-xs)',
|
|
2509
|
-
fontWeight: 600,
|
|
2510
|
-
color: 'var(--sv-color-text-primary)',
|
|
2511
|
-
}}
|
|
2512
|
-
>
|
|
2513
|
-
Page header
|
|
2514
|
-
</div>
|
|
2515
|
-
}
|
|
2516
|
-
>
|
|
2517
|
-
<div
|
|
2518
|
-
style={{
|
|
2519
|
-
fontFamily: ffm,
|
|
2520
|
-
fontSize: 'var(--sv-font-size-xs)',
|
|
2521
|
-
color: 'var(--sv-color-text-primary)',
|
|
2522
|
-
}}
|
|
2523
|
-
>
|
|
2524
|
-
Page content
|
|
2525
|
-
</div>
|
|
2526
|
-
</PageLayout>
|
|
2527
|
-
</div>
|
|
2528
|
-
</ComponentCard>
|
|
2529
|
-
|
|
2530
|
-
{/* PageHeader */}
|
|
2531
|
-
<ComponentCard
|
|
2532
|
-
name="PageHeader"
|
|
2533
|
-
importLine="import { PageHeader } from '@sovereignfs/ui';"
|
|
2534
|
-
usage="Plugin page top section. Title + optional description + right-side action slot. Replaces the hand-rolled .pageHeader pattern in every plugin. An optional `icon` renders before the title text on both breakpoints. On mobile, description drops out and onBack/onMenuClick add an optional back button and vertical-ellipsis menu trigger."
|
|
2535
|
-
>
|
|
2536
|
-
<div style={{ width: '100%' }}>
|
|
2537
|
-
<PageHeader
|
|
2538
|
-
title="Users"
|
|
2539
|
-
description="Manage who has access to this instance."
|
|
2540
|
-
action={<Button size="sm">Invite user</Button>}
|
|
2541
|
-
/>
|
|
2542
|
-
</div>
|
|
2543
|
-
</ComponentCard>
|
|
2544
|
-
|
|
2545
|
-
{/* PageContainer */}
|
|
2546
|
-
<ComponentCard
|
|
2547
|
-
name="PageContainer"
|
|
2548
|
-
importLine="import { PageContainer } from '@sovereignfs/ui';"
|
|
2549
|
-
usage="The wrapper every plugin page renders through. Supplies the page gutter (padding: none/sm/md/lg, default md — the runtime shell no longer pads plugin content) and constrains width (maxWidth: sm/md/lg/full, default full). Use instead of declaring padding or max-width in a plugin's own CSS."
|
|
2550
|
-
>
|
|
2551
|
-
<div style={{ width: '100%', background: 'var(--sv-color-surface-sunken)' }}>
|
|
2552
|
-
<PageContainer maxWidth="sm" padding="md">
|
|
2553
|
-
<div
|
|
2554
|
-
style={{
|
|
2555
|
-
background: 'var(--sv-color-surface-raised)',
|
|
2556
|
-
border: '1px dashed var(--sv-color-border)',
|
|
2557
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
2558
|
-
padding: 'var(--sv-space-4)',
|
|
2559
|
-
}}
|
|
2560
|
-
>
|
|
2561
|
-
maxWidth="sm" padding="md"
|
|
2562
|
-
</div>
|
|
2563
|
-
</PageContainer>
|
|
2564
|
-
</div>
|
|
2565
|
-
</ComponentCard>
|
|
2566
|
-
|
|
2567
|
-
{/* Header */}
|
|
2568
|
-
<ComponentCard
|
|
2569
|
-
name="Header"
|
|
2570
|
-
importLine="import { Header } from '@sovereignfs/ui';"
|
|
2571
|
-
usage="The web top-bar header for shell: minimal plugins that own their whole viewport (no platform sidebar). Brand badge + optional active plugin on the left; launcher (AppsLauncher), notifications (NotificationsPanel), and avatarMenu (UserMenu) on the right — all real, shared, independently clickable components. Presentational only — see Components/Header for the mobile counterpart (MobileHeader) shown side by side."
|
|
2572
|
-
>
|
|
2573
|
-
<Header
|
|
2574
|
-
instanceName="Sovereign"
|
|
2575
|
-
homeHref="/launcher"
|
|
2576
|
-
plugin={{
|
|
2577
|
-
// The plugin icon is 28px — the same size as the brand
|
|
2578
|
-
// badge next to it, not a smaller inline glyph.
|
|
2579
|
-
name: 'Notes',
|
|
2580
|
-
icon: (
|
|
2581
|
-
<span
|
|
2582
|
-
aria-hidden="true"
|
|
2583
|
-
style={{
|
|
2584
|
-
display: 'inline-flex',
|
|
2585
|
-
alignItems: 'center',
|
|
2586
|
-
justifyContent: 'center',
|
|
2587
|
-
width: 28,
|
|
2588
|
-
height: 28,
|
|
2589
|
-
}}
|
|
2590
|
-
>
|
|
2591
|
-
<span style={{ display: 'inline-flex', transform: 'scale(1.15)' }}>
|
|
2592
|
-
<Icon name="file" size="lg" aria-hidden />
|
|
2593
|
-
</span>
|
|
2594
|
-
</span>
|
|
2595
|
-
),
|
|
2596
|
-
href: '/notes',
|
|
2597
|
-
}}
|
|
2598
|
-
launcher={
|
|
2599
|
-
<AppsLauncher
|
|
2600
|
-
items={[
|
|
2601
|
-
{
|
|
2602
|
-
key: 'home',
|
|
2603
|
-
icon: <Icon name="house" size="md" aria-hidden />,
|
|
2604
|
-
label: 'Home',
|
|
2605
|
-
href: '/launcher',
|
|
2606
|
-
},
|
|
2607
|
-
{
|
|
2608
|
-
key: 'notes',
|
|
2609
|
-
icon: <Icon name="file" size="md" aria-hidden />,
|
|
2610
|
-
label: 'Notes',
|
|
2611
|
-
href: '/notes',
|
|
2612
|
-
},
|
|
2613
|
-
]}
|
|
2614
|
-
/>
|
|
2615
|
-
}
|
|
2616
|
-
notifications={<NotificationsPanel items={[]} unreadCount={0} />}
|
|
2617
|
-
avatarMenu={
|
|
2618
|
-
<UserMenu
|
|
2619
|
-
name="Kasun Benthara"
|
|
2620
|
-
email="kasun@openfs.io"
|
|
2621
|
-
items={[
|
|
2622
|
-
{ label: 'Account', icon: 'user', href: '/account' },
|
|
2623
|
-
{ label: 'Sign out', icon: 'log-out', destructive: true, onSelect: () => {} },
|
|
2624
|
-
]}
|
|
2625
|
-
size="sm"
|
|
2626
|
-
/>
|
|
2627
|
-
}
|
|
2628
|
-
/>
|
|
2629
|
-
</ComponentCard>
|
|
2630
|
-
|
|
2631
|
-
{/* AppsLauncher */}
|
|
2632
|
-
<ComponentCard
|
|
2633
|
-
name="AppsLauncher"
|
|
2634
|
-
importLine="import { AppsLauncher } from '@sovereignfs/ui';"
|
|
2635
|
-
usage="The desktop 'Apps' switcher — a 28px grid trigger opening a Popover tile grid. The desktop counterpart to MobileAppsDrawer: same items shape (key/icon/label/href/onClick), different chrome. Presentational only — items are supplied already resolved; loading/error are separate props."
|
|
2636
|
-
>
|
|
2637
|
-
<AppsLauncher
|
|
2638
|
-
items={[
|
|
2639
|
-
{
|
|
2640
|
-
key: 'home',
|
|
2641
|
-
icon: <Icon name="house" size="md" aria-hidden />,
|
|
2642
|
-
label: 'Home',
|
|
2643
|
-
href: '/launcher',
|
|
2644
|
-
},
|
|
2645
|
-
{
|
|
2646
|
-
key: 'console',
|
|
2647
|
-
icon: <Icon name="layout-dashboard" size="md" aria-hidden />,
|
|
2648
|
-
label: 'Console',
|
|
2649
|
-
href: '/console',
|
|
2650
|
-
},
|
|
2651
|
-
{
|
|
2652
|
-
key: 'notes',
|
|
2653
|
-
icon: <Icon name="file" size="md" aria-hidden />,
|
|
2654
|
-
label: 'Notes',
|
|
2655
|
-
href: '/notes',
|
|
2656
|
-
},
|
|
2657
|
-
]}
|
|
2658
|
-
/>
|
|
2659
|
-
</ComponentCard>
|
|
2660
|
-
|
|
2661
|
-
{/* NotificationsPanel */}
|
|
2662
|
-
<ComponentCard
|
|
2663
|
-
name="NotificationsPanel"
|
|
2664
|
-
importLine="import { NotificationsPanel } from '@sovereignfs/ui';"
|
|
2665
|
-
usage="The bell trigger + dropdown, generalized from the runtime shell's own NotificationBell — title/Mark all read/Clear all/close, then category-icon + title + time + unread-dot + dismiss rows. Pure presentation: no fetch/SSE/mark-read logic of its own; the consumer supplies already-resolved items and wires onOpen/onDismiss/onMarkAllRead/onClearAll."
|
|
2666
|
-
>
|
|
2667
|
-
<NotificationsPanel
|
|
2668
|
-
items={[
|
|
2669
|
-
{
|
|
2670
|
-
id: '1',
|
|
2671
|
-
icon: <Icon name="layers" size="sm" aria-hidden />,
|
|
2672
|
-
title: 'Added to a project',
|
|
2673
|
-
timeLabel: '2d ago',
|
|
2674
|
-
read: false,
|
|
2675
|
-
onDismiss: () => {},
|
|
2676
|
-
},
|
|
2677
|
-
]}
|
|
2678
|
-
unreadCount={1}
|
|
2679
|
-
onMarkAllRead={() => {}}
|
|
2680
|
-
onClearAll={() => {}}
|
|
2681
|
-
/>
|
|
2682
|
-
</ComponentCard>
|
|
2683
|
-
|
|
2684
|
-
{/* UserMenu */}
|
|
2685
|
-
<ComponentCard
|
|
2686
|
-
name="UserMenu"
|
|
2687
|
-
importLine="import { UserMenu } from '@sovereignfs/ui';"
|
|
2688
|
-
usage="The account avatar trigger + dropdown, generalized from the runtime shell's own AccountMenu — accent-filled avatar, a name/email header block, then a plain item list. Presentational only: no auth/session logic — items carry their own onSelect and/or href (e.g. a real 'Sign out' item just does whatever the consuming app's sign-out requires)."
|
|
2689
|
-
>
|
|
2690
|
-
<UserMenu
|
|
2691
|
-
name="Kasun Benthara"
|
|
2692
|
-
email="kasun@openfs.io"
|
|
2693
|
-
items={[
|
|
2694
|
-
{ label: 'Account', icon: 'user', href: '/account' },
|
|
2695
|
-
{
|
|
2696
|
-
label: 'Preferences',
|
|
2697
|
-
icon: 'sliders-horizontal',
|
|
2698
|
-
href: '/account/preferences',
|
|
2699
|
-
},
|
|
2700
|
-
{ label: 'Sign out', icon: 'log-out', destructive: true, onSelect: () => {} },
|
|
2701
|
-
]}
|
|
2702
|
-
/>
|
|
2703
|
-
</ComponentCard>
|
|
2704
|
-
|
|
2705
|
-
{/* MobileHeader */}
|
|
2706
|
-
<ComponentCard
|
|
2707
|
-
name="MobileHeader"
|
|
2708
|
-
importLine="import { MobileHeader } from '@sovereignfs/ui';"
|
|
2709
|
-
usage="Mobile shell header content row (RFC 0088). Bell and avatarMenu are always rendered by the consumer; logo is optional — omit it for a default 'S' badge (matching the shell's own fallback brand icon), or supply your own. Title is the only content-level override. Presentational only — see Overview/Mobile Patterns for the full immutable/overridable boundary."
|
|
2710
|
-
>
|
|
2711
|
-
<div
|
|
2712
|
-
style={{
|
|
2713
|
-
width: '100%',
|
|
2714
|
-
background: 'var(--sv-color-surface)',
|
|
2715
|
-
border: '1px solid var(--sv-color-border)',
|
|
2716
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
2717
|
-
padding: '0 var(--sv-space-3)',
|
|
2718
|
-
}}
|
|
2719
|
-
>
|
|
2720
|
-
<MobileHeader
|
|
2721
|
-
title="Tasks"
|
|
2722
|
-
bell={<Icon name="bell" size="lg" aria-label="Notifications" />}
|
|
2723
|
-
avatarMenu={<Avatar name="Jamie Doe" size="sm" />}
|
|
2724
|
-
/>
|
|
2725
|
-
</div>
|
|
2726
|
-
</ComponentCard>
|
|
2727
|
-
|
|
2728
|
-
{/* MobileFooter */}
|
|
2729
|
-
<ComponentCard
|
|
2730
|
-
name="MobileFooter"
|
|
2731
|
-
importLine="import { MobileFooter } from '@sovereignfs/ui';"
|
|
2732
|
-
usage="Mobile shell footer nav row (RFC 0088). The centered launcher (onOpenApps) is always rendered; leftIcons/rightIcons take 1 or 2 items each, symmetric so the launcher stays centered."
|
|
2733
|
-
>
|
|
2734
|
-
<div
|
|
2735
|
-
style={{
|
|
2736
|
-
width: '100%',
|
|
2737
|
-
background: 'var(--sv-color-surface)',
|
|
2738
|
-
border: '1px solid var(--sv-color-border)',
|
|
2739
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
2740
|
-
padding: '0 var(--sv-space-3)',
|
|
2741
|
-
}}
|
|
2742
|
-
>
|
|
2743
|
-
<MobileFooter
|
|
2744
|
-
onOpenApps={() => {}}
|
|
2745
|
-
leftIcons={[{ icon: <Icon name="house" size="md" aria-hidden />, label: 'Home' }]}
|
|
2746
|
-
rightIcons={[
|
|
2747
|
-
{ icon: <Icon name="search" size="md" aria-hidden />, label: 'Search' },
|
|
2748
|
-
]}
|
|
2749
|
-
/>
|
|
2750
|
-
</div>
|
|
2751
|
-
</ComponentCard>
|
|
2752
|
-
|
|
2753
|
-
{/* MobileAppsDrawer */}
|
|
2754
|
-
<ComponentCard
|
|
2755
|
-
name="MobileAppsDrawer"
|
|
2756
|
-
importLine="import { MobileAppsDrawer } from '@sovereignfs/ui';"
|
|
2757
|
-
usage="The mobile 'Apps' bottom sheet: a 3-column tile grid matching the runtime shell's own Apps drawer exactly (56px icon tiles, radius 14px). No header row — Drawer's own grab handle plus swipe-down/scrim-tap dismissal are enough. The tile list is supplied by the consumer via `items` — e.g. installed plugins for the shell, or a plugin's own sections for its own launcher button."
|
|
2758
|
-
>
|
|
2759
|
-
<MobileAppsDrawerDemo />
|
|
2760
|
-
</ComponentCard>
|
|
2761
|
-
|
|
2762
|
-
{/* EmptyState */}
|
|
2763
|
-
<ComponentCard
|
|
2764
|
-
name="EmptyState"
|
|
2765
|
-
importLine="import { EmptyState } from '@sovereignfs/ui';"
|
|
2766
|
-
usage="Zero-data placeholder. Icon slot, heading, description, and optional CTA. Use whenever a list or table has no rows."
|
|
2767
|
-
>
|
|
2768
|
-
<EmptyState
|
|
2769
|
-
icon="search"
|
|
2770
|
-
heading="No results found"
|
|
2771
|
-
description="Try adjusting your search."
|
|
2772
|
-
action={
|
|
2773
|
-
<Button variant="secondary" size="sm">
|
|
2774
|
-
Clear filters
|
|
2775
|
-
</Button>
|
|
2776
|
-
}
|
|
2777
|
-
/>
|
|
2778
|
-
</ComponentCard>
|
|
2779
|
-
|
|
2780
|
-
{/* OfflineGate */}
|
|
2781
|
-
<ComponentCard
|
|
2782
|
-
name="OfflineGate"
|
|
2783
|
-
importLine="import { OfflineGate } from '@sovereignfs/ui';"
|
|
2784
|
-
usage="Blocks an administrative/settings surface from rendering possibly-stale cached content while offline, showing an EmptyState instead. Reads the live useIsOffline() hook — toggle DevTools > Network > Offline to see it swap. Wrap the body of a surface like Console or Account where operating against stale data is actively wrong, not every page."
|
|
2785
|
-
>
|
|
2786
|
-
<OfflineGate surfaceName="Console">
|
|
2787
|
-
<p style={{ margin: 0, fontFamily: 'var(--sv-font-family)', fontSize: 14 }}>
|
|
2788
|
-
Renders normally while online.
|
|
2789
|
-
</p>
|
|
2790
|
-
</OfflineGate>
|
|
2791
|
-
</ComponentCard>
|
|
2792
|
-
|
|
2793
|
-
{/* DeviceOnlyGate */}
|
|
2794
|
-
<ComponentCard
|
|
2795
|
-
name="DeviceOnlyGate"
|
|
2796
|
-
importLine="import { DeviceOnlyGate } from '@sovereignfs/ui';"
|
|
2797
|
-
usage="Blocks a device-only-tier plugin's content on a surface without the durable, encrypted, device-auth-gated store that tier needs. `available` is the caller's own isDeviceOnlyTierAvailable() result (from @sovereignfs/sdk/device-client) — a capability signal, never a security boundary; the real protection is that the data is encrypted regardless."
|
|
2798
|
-
>
|
|
2799
|
-
<DeviceOnlyGate available={false} surfaceName="Wallet">
|
|
2800
|
-
<p style={{ margin: 0, fontFamily: 'var(--sv-font-family)', fontSize: 14 }}>
|
|
2801
|
-
Renders normally when available.
|
|
2802
|
-
</p>
|
|
2803
|
-
</DeviceOnlyGate>
|
|
2804
|
-
</ComponentCard>
|
|
2805
|
-
|
|
2806
|
-
{/* DeviceStorageKeyGate */}
|
|
2807
|
-
<ComponentCard
|
|
2808
|
-
name="DeviceStorageKeyGate"
|
|
2809
|
-
importLine="import { DeviceStorageKeyGate } from '@sovereignfs/ui';"
|
|
2810
|
-
usage="Blocks a device-only-tier plugin's content until the user has set up their Device Storage Key. `status` is the caller's own getDeviceStorageKeyStatus() result (from @sovereignfs/sdk/device-only-storage). Enrollment is centralized in Account > Security, not per-plugin — `setupAction` is where the caller supplies a link there."
|
|
2811
|
-
>
|
|
2812
|
-
<DeviceStorageKeyGate
|
|
2813
|
-
status="not-set-up"
|
|
2814
|
-
surfaceName="Notes"
|
|
2815
|
-
setupAction={
|
|
2816
|
-
<a href="/account/security" style={{ color: 'var(--sv-color-accent)' }}>
|
|
2817
|
-
Go to Account → Security
|
|
2818
|
-
</a>
|
|
2819
|
-
}
|
|
2820
|
-
>
|
|
2821
|
-
<p style={{ margin: 0, fontFamily: 'var(--sv-font-family)', fontSize: 14 }}>
|
|
2822
|
-
Renders normally once set up.
|
|
2823
|
-
</p>
|
|
2824
|
-
</DeviceStorageKeyGate>
|
|
2825
|
-
</ComponentCard>
|
|
2826
|
-
|
|
2827
|
-
{/* Spinner */}
|
|
2828
|
-
<ComponentCard
|
|
2829
|
-
name="Spinner"
|
|
2830
|
-
importLine="import { Spinner } from '@sovereignfs/ui';"
|
|
2831
|
-
usage="CSS-animated loading ring in sm/md/lg sizes matching icon-size tokens. Pauses under prefers-reduced-motion. Sets role='status' with aria-label."
|
|
2832
|
-
>
|
|
2833
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
|
|
2834
|
-
<Spinner size="sm" />
|
|
2835
|
-
<Spinner size="md" />
|
|
2836
|
-
<Spinner size="lg" />
|
|
2837
|
-
</div>
|
|
2838
|
-
</ComponentCard>
|
|
2839
|
-
|
|
2840
|
-
{/* Avatar */}
|
|
2841
|
-
<ComponentCard
|
|
2842
|
-
name="Avatar"
|
|
2843
|
-
importLine="import { Avatar } from '@sovereignfs/ui';"
|
|
2844
|
-
usage="User representation. Shows image when src loads; falls back to initials derived from name. Three sizes. Always sets alt text for accessibility."
|
|
2845
|
-
>
|
|
2846
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
|
2847
|
-
<Avatar name="Jane Smith" size="sm" />
|
|
2848
|
-
<Avatar name="Jane Smith" size="md" />
|
|
2849
|
-
<Avatar name="Jane Smith" size="lg" />
|
|
2850
|
-
<Avatar name="Admin" src="https://i.pravatar.cc/150?u=sb" size="lg" />
|
|
2851
|
-
</div>
|
|
2852
|
-
</ComponentCard>
|
|
2853
|
-
|
|
2854
|
-
{/* NavTabs */}
|
|
2855
|
-
<ComponentCard
|
|
2856
|
-
name="NavTabs"
|
|
2857
|
-
importLine="import { NavTabs } from '@sovereignfs/ui';"
|
|
2858
|
-
usage="Underline-style navigation tabs for plugin-level page routing. Distinct from the contained Tabs component. Scrolls horizontally on mobile with no visible scrollbar."
|
|
2859
|
-
>
|
|
2860
|
-
<div style={{ width: '100%' }}>
|
|
2861
|
-
<NavTabs
|
|
2862
|
-
items={[
|
|
2863
|
-
{ label: 'Profile', href: '#', active: true },
|
|
2864
|
-
{ label: 'Security', href: '#' },
|
|
2865
|
-
{ label: 'Preferences', href: '#' },
|
|
2866
|
-
{ label: 'Data', href: '#' },
|
|
2867
|
-
]}
|
|
2868
|
-
/>
|
|
2869
|
-
</div>
|
|
2870
|
-
</ComponentCard>
|
|
2871
|
-
|
|
2872
|
-
{/* NavList */}
|
|
2873
|
-
<ComponentCard
|
|
2874
|
-
name="NavList"
|
|
2875
|
-
importLine="import { NavList } from '@sovereignfs/ui';"
|
|
2876
|
-
usage='Vertical list of icon+label rows, optionally grouped under section headers. static: active row highlighted, no chevron — a persistent sidebar. drilldown: trailing chevron on every row, no active state — a native-Settings-app style index. density="compact" (static only) shrinks row height for a short, always-visible list. Framework-agnostic like NavTabs; supply active/renderLink from the consumer.'
|
|
2877
|
-
>
|
|
2878
|
-
<div style={{ width: '100%', maxWidth: 220 }}>
|
|
2879
|
-
<NavList
|
|
2880
|
-
variant="static"
|
|
2881
|
-
aria-label="Example sections"
|
|
2882
|
-
groups={[
|
|
2883
|
-
{
|
|
2884
|
-
id: 'overview',
|
|
2885
|
-
items: [
|
|
2886
|
-
{
|
|
2887
|
-
id: 'overview',
|
|
2888
|
-
label: 'Overview',
|
|
2889
|
-
href: '#',
|
|
2890
|
-
icon: 'layout-dashboard',
|
|
2891
|
-
active: true,
|
|
2892
|
-
},
|
|
2893
|
-
],
|
|
2894
|
-
},
|
|
2895
|
-
{
|
|
2896
|
-
id: 'people',
|
|
2897
|
-
label: 'People',
|
|
2898
|
-
items: [
|
|
2899
|
-
{ id: 'users', label: 'Users', href: '#', icon: 'users' },
|
|
2900
|
-
{ id: 'groups', label: 'Groups', href: '#', icon: 'layers' },
|
|
2901
|
-
],
|
|
2902
|
-
},
|
|
2903
|
-
]}
|
|
2904
|
-
/>
|
|
2905
|
-
</div>
|
|
2906
|
-
</ComponentCard>
|
|
2907
|
-
|
|
2908
|
-
{/* Tooltip */}
|
|
2909
|
-
<ComponentCard
|
|
2910
|
-
name="Tooltip"
|
|
2911
|
-
importLine="import { Tooltip } from '@sovereignfs/ui';"
|
|
2912
|
-
usage="CSS-only hover/focus hint. Four placements. Wired to its trigger via aria-describedby. No JS positioning — RSC-safe."
|
|
2913
|
-
>
|
|
2914
|
-
<div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
|
|
2915
|
-
<Tooltip content="Saved to your account" side="top">
|
|
2916
|
-
<Button variant="secondary" size="sm">
|
|
2917
|
-
Save
|
|
2918
|
-
</Button>
|
|
2919
|
-
</Tooltip>
|
|
2920
|
-
<Tooltip content="Cannot be undone" side="right">
|
|
2921
|
-
<Button variant="destructive" size="sm">
|
|
2922
|
-
Delete
|
|
2923
|
-
</Button>
|
|
2924
|
-
</Tooltip>
|
|
2925
|
-
</div>
|
|
2926
|
-
</ComponentCard>
|
|
2927
|
-
|
|
2928
|
-
{/* CheckableListRow */}
|
|
2929
|
-
<ComponentCard
|
|
2930
|
-
name="CheckableListRow"
|
|
2931
|
-
importLine="import { CheckableListRow } from '@sovereignfs/ui';"
|
|
2932
|
-
usage="Whole-row tap target that toggles a checked state, with strike-through on the label — for 'tap the row to mark it done' lists (not a form checkbox)."
|
|
2933
|
-
>
|
|
2934
|
-
<CheckableListRowDemo />
|
|
2935
|
-
</ComponentCard>
|
|
2936
|
-
|
|
2937
|
-
{/* MemberMultiSelect */}
|
|
2938
|
-
<ComponentCard
|
|
2939
|
-
name="MemberMultiSelect"
|
|
2940
|
-
importLine="import { MemberMultiSelect } from '@sovereignfs/ui';"
|
|
2941
|
-
usage="Checkbox list for picking any number of people from an already-resolved option set. Domain-agnostic about guest vs. instance-user options; pair with SuggestionInput for search + add."
|
|
2942
|
-
>
|
|
2943
|
-
<MemberMultiSelectDemo />
|
|
2944
|
-
</ComponentCard>
|
|
2945
|
-
|
|
2946
|
-
{/* Checkbox */}
|
|
2947
|
-
<ComponentCard
|
|
2948
|
-
name="Checkbox"
|
|
2949
|
-
importLine="import { Checkbox } from '@sovereignfs/ui';"
|
|
2950
|
-
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."
|
|
2951
|
-
>
|
|
2952
|
-
{(() => {
|
|
2953
|
-
const [a, setA] = useState(false);
|
|
2954
|
-
const [b, setB] = useState(true);
|
|
2955
|
-
return (
|
|
2956
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
2957
|
-
<Checkbox checked={a} onChange={setA} label="Unchecked task" strikeThrough />
|
|
2958
|
-
<Checkbox checked={b} onChange={setB} label="Completed task" strikeThrough />
|
|
2959
|
-
</div>
|
|
2960
|
-
);
|
|
2961
|
-
})()}
|
|
2962
|
-
</ComponentCard>
|
|
2963
|
-
|
|
2964
|
-
{/* RadioGroup */}
|
|
2965
|
-
<ComponentCard
|
|
2966
|
-
name="RadioGroup"
|
|
2967
|
-
importLine="import { RadioGroup } from '@sovereignfs/ui';"
|
|
2968
|
-
usage="Single-select list of options. Renders real <input type=radio> elements sharing one name, so arrow-key navigation between options is native browser behavior, not hand-rolled."
|
|
2969
|
-
>
|
|
2970
|
-
{(() => {
|
|
2971
|
-
const [size, setSize] = useState('md');
|
|
2972
|
-
return (
|
|
2973
|
-
<RadioGroup
|
|
2974
|
-
items={[
|
|
2975
|
-
{ label: 'Small', value: 'sm' },
|
|
2976
|
-
{ label: 'Medium', value: 'md' },
|
|
2977
|
-
{ label: 'Large', value: 'lg' },
|
|
2978
|
-
]}
|
|
2979
|
-
value={size}
|
|
2980
|
-
onChange={setSize}
|
|
2981
|
-
aria-label="Size"
|
|
2982
|
-
/>
|
|
2983
|
-
);
|
|
2984
|
-
})()}
|
|
2985
|
-
</ComponentCard>
|
|
2986
|
-
|
|
2987
|
-
{/* Slider */}
|
|
2988
|
-
<ComponentCard
|
|
2989
|
-
name="Slider"
|
|
2990
|
-
importLine="import { Slider } from '@sovereignfs/ui';"
|
|
2991
|
-
usage="Single-thumb range input. A native <input type=range> under custom styling — arrow keys, Home/End, and touch-drag all come from the browser."
|
|
2992
|
-
>
|
|
2993
|
-
{(() => {
|
|
2994
|
-
const [value, setValue] = useState(50);
|
|
2995
|
-
return (
|
|
2996
|
-
<div style={{ width: 240 }}>
|
|
2997
|
-
<Slider value={value} onChange={setValue} min={0} max={100} label="Volume" />
|
|
2998
|
-
</div>
|
|
2999
|
-
);
|
|
3000
|
-
})()}
|
|
3001
|
-
</ComponentCard>
|
|
3002
|
-
|
|
3003
|
-
{/* Progress */}
|
|
3004
|
-
<ComponentCard
|
|
3005
|
-
name="Progress"
|
|
3006
|
-
importLine="import { Progress } from '@sovereignfs/ui';"
|
|
3007
|
-
usage="Determinate progress bar. role=progressbar with aria-valuenow/min/max so screen readers announce the current value."
|
|
3008
|
-
>
|
|
3009
|
-
<div style={{ width: 240 }}>
|
|
3010
|
-
<Progress value={65} label="Upload progress" />
|
|
3011
|
-
</div>
|
|
3012
|
-
</ComponentCard>
|
|
3013
|
-
|
|
3014
|
-
{/* Table */}
|
|
3015
|
-
<ComponentCard
|
|
3016
|
-
name="Table"
|
|
3017
|
-
importLine="import { Table, TableHead, TableBody, TableRow, TableHeaderCell, TableCell } from '@sovereignfs/ui';"
|
|
3018
|
-
usage="Thin, styled wrappers around the native table elements. Not a data grid — no sort/filter/virtualization. Scrolls horizontally (masked-edge fade) at any viewport size."
|
|
3019
|
-
>
|
|
3020
|
-
<Table>
|
|
3021
|
-
<TableHead>
|
|
3022
|
-
<TableRow>
|
|
3023
|
-
<TableHeaderCell>Plugin</TableHeaderCell>
|
|
3024
|
-
<TableHeaderCell>Status</TableHeaderCell>
|
|
3025
|
-
</TableRow>
|
|
3026
|
-
</TableHead>
|
|
3027
|
-
<TableBody>
|
|
3028
|
-
<TableRow>
|
|
3029
|
-
<TableCell>sovereign-tasks</TableCell>
|
|
3030
|
-
<TableCell>Active</TableCell>
|
|
3031
|
-
</TableRow>
|
|
3032
|
-
<TableRow>
|
|
3033
|
-
<TableCell>sovereign-shopper</TableCell>
|
|
3034
|
-
<TableCell>Disabled</TableCell>
|
|
3035
|
-
</TableRow>
|
|
3036
|
-
</TableBody>
|
|
3037
|
-
</Table>
|
|
3038
|
-
</ComponentCard>
|
|
3039
|
-
|
|
3040
|
-
{/* Alert */}
|
|
3041
|
-
<ComponentCard
|
|
3042
|
-
name="Alert"
|
|
3043
|
-
importLine="import { Alert } from '@sovereignfs/ui';"
|
|
3044
|
-
usage="Inline, non-dismissible banner. Distinct from Toast (transient) and SystemBanner (instance-wide) — for form-level errors or explaining an empty/blocked state. Leading icon defaults per variant; pass icon to override or icon={false} to suppress."
|
|
3045
|
-
>
|
|
3046
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
3047
|
-
<Alert variant="error" heading="Something went wrong">
|
|
3048
|
-
We couldn’t save your changes. Check your connection and try again.
|
|
3049
|
-
</Alert>
|
|
3050
|
-
<Alert variant="success">Invite sent.</Alert>
|
|
3051
|
-
<Alert variant="neutral">This project has no members yet.</Alert>
|
|
3052
|
-
</div>
|
|
3053
|
-
</ComponentCard>
|
|
3054
|
-
|
|
3055
|
-
{/* Breadcrumb */}
|
|
3056
|
-
<ComponentCard
|
|
3057
|
-
name="Breadcrumb"
|
|
3058
|
-
importLine="import { Breadcrumb } from '@sovereignfs/ui';"
|
|
3059
|
-
usage="Link trail. The last item (no href) renders as plain text with aria-current=page. Pass renderLink to keep navigation client-side inside overlay-shell plugins."
|
|
3060
|
-
>
|
|
3061
|
-
<Breadcrumb
|
|
3062
|
-
items={[
|
|
3063
|
-
{ label: 'Console', href: '/console' },
|
|
3064
|
-
{ label: 'Plugins', href: '/console/plugins' },
|
|
3065
|
-
{ label: 'sovereign-tasks' },
|
|
3066
|
-
]}
|
|
3067
|
-
/>
|
|
3068
|
-
</ComponentCard>
|
|
3069
|
-
|
|
3070
|
-
{/* LegalLinks */}
|
|
3071
|
-
<ComponentCard
|
|
3072
|
-
name="LegalLinks"
|
|
3073
|
-
importLine="import { LegalLinks } from '@sovereignfs/ui';"
|
|
3074
|
-
usage="Privacy Policy / Terms of Service link row for standalone auth pages (login, register, forgot/reset password). Pass renderLink to keep navigation client-side if ever mounted inside an SPA-like shell."
|
|
3075
|
-
>
|
|
3076
|
-
<LegalLinks />
|
|
3077
|
-
</ComponentCard>
|
|
3078
|
-
|
|
3079
|
-
{/* ConsentPrompt */}
|
|
3080
|
-
<ComponentCard
|
|
3081
|
-
name="ConsentPrompt"
|
|
3082
|
-
importLine="import { ConsentPrompt } from '@sovereignfs/ui';"
|
|
3083
|
-
usage="Consent screen shown before one plugin is allowed to read another plugin's data (RFC 0002 data grants) or connect to an external service on the user's behalf. description should come from the provider's own manifest declaration, never caller-supplied copy."
|
|
3084
|
-
>
|
|
3085
|
-
<ConsentPrompt
|
|
3086
|
-
consumerName="Ledger"
|
|
3087
|
-
providerName="Finance Tracker"
|
|
3088
|
-
contract="expenses"
|
|
3089
|
-
description="Monthly expense totals by category, no transaction-level detail."
|
|
3090
|
-
onAllow={() => {}}
|
|
3091
|
-
onDeny={() => {}}
|
|
3092
|
-
/>
|
|
3093
|
-
</ComponentCard>
|
|
3094
|
-
|
|
3095
|
-
{/* Markdown */}
|
|
3096
|
-
<ComponentCard
|
|
3097
|
-
name="Markdown"
|
|
3098
|
-
importLine="import { Markdown } from '@sovereignfs/ui';"
|
|
3099
|
-
usage="Renders a constrained markdown subset (headings, paragraphs, bold/italic/code, links, ordered and unordered lists, fenced code blocks, blockquotes) as Typography-based React elements — no HTML string injection. Built for first-party long-form content and model-generated replies; still not a full CommonMark parser (no tables, images, or nested blocks)."
|
|
3100
|
-
>
|
|
3101
|
-
<Markdown
|
|
3102
|
-
content={
|
|
3103
|
-
'## Heading\n\nA paragraph with **bold** and a [link](/privacy).\n\n- One\n- Two\n\n1. First\n2. Second\n\n```ts\nconst a = 1;\n```'
|
|
3104
|
-
}
|
|
3105
|
-
/>
|
|
3106
|
-
</ComponentCard>
|
|
3107
|
-
|
|
3108
|
-
{/* Pagination */}
|
|
3109
|
-
<ComponentCard
|
|
3110
|
-
name="Pagination"
|
|
3111
|
-
importLine="import { Pagination } from '@sovereignfs/ui';"
|
|
3112
|
-
usage="Page-number / prev-next control. Shows first, last, and pages near the current one, with an ellipsis for gaps once the page count grows."
|
|
3113
|
-
>
|
|
3114
|
-
{(() => {
|
|
3115
|
-
const [page, setPage] = useState(4);
|
|
3116
|
-
return <Pagination page={page} totalPages={12} onChange={setPage} />;
|
|
3117
|
-
})()}
|
|
3118
|
-
</ComponentCard>
|
|
3119
|
-
|
|
3120
|
-
{/* Kbd */}
|
|
3121
|
-
<ComponentCard
|
|
3122
|
-
name="Kbd"
|
|
3123
|
-
importLine="import { Kbd } from '@sovereignfs/ui';"
|
|
3124
|
-
usage="Inline keyboard-key styling. Renders a real <kbd> element."
|
|
3125
|
-
>
|
|
3126
|
-
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 4 }}>
|
|
3127
|
-
<Kbd>Ctrl</Kbd>
|
|
3128
|
-
<span>+</span>
|
|
3129
|
-
<Kbd>K</Kbd>
|
|
3130
|
-
</span>
|
|
3131
|
-
</ComponentCard>
|
|
3132
|
-
|
|
3133
|
-
{/* Collapsible */}
|
|
3134
|
-
<ComponentCard
|
|
3135
|
-
name="Collapsible"
|
|
3136
|
-
importLine="import { Collapsible } from '@sovereignfs/ui';"
|
|
3137
|
-
usage="Single expand/collapse primitive. Independently useful (e.g. a 'show more' toggle), and composed internally by Accordion."
|
|
3138
|
-
>
|
|
3139
|
-
{(() => {
|
|
3140
|
-
const [open, setOpen] = useState(false);
|
|
3141
|
-
return (
|
|
3142
|
-
<Collapsible open={open} onOpenChange={setOpen} trigger="Advanced settings">
|
|
3143
|
-
These settings are rarely needed — change them only if you know what you're
|
|
3144
|
-
doing.
|
|
3145
|
-
</Collapsible>
|
|
3146
|
-
);
|
|
3147
|
-
})()}
|
|
3148
|
-
</ComponentCard>
|
|
3149
|
-
|
|
3150
|
-
{/* Accordion */}
|
|
3151
|
-
<ComponentCard
|
|
3152
|
-
name="Accordion"
|
|
3153
|
-
importLine="import { Accordion } from '@sovereignfs/ui';"
|
|
3154
|
-
usage="One or more Collapsible sections. type=single closes other sections when one opens; type=multiple allows any number open at once."
|
|
3155
|
-
>
|
|
3156
|
-
{(() => {
|
|
3157
|
-
const [openIds, setOpenIds] = useState<string[]>(['plugins']);
|
|
3158
|
-
return (
|
|
3159
|
-
<Accordion
|
|
3160
|
-
items={[
|
|
3161
|
-
{
|
|
3162
|
-
id: 'plugins',
|
|
3163
|
-
trigger: 'What plugins ship by default?',
|
|
3164
|
-
content:
|
|
3165
|
-
'Only the platform plugins (Console, Launcher, Account) plus Sovereign Tasks.',
|
|
3166
|
-
},
|
|
3167
|
-
{
|
|
3168
|
-
id: 'hosting',
|
|
3169
|
-
trigger: 'Can I self-host this?',
|
|
3170
|
-
content:
|
|
3171
|
-
'Yes — Sovereign is designed to be self-hosted via Docker Compose.',
|
|
3172
|
-
},
|
|
3173
|
-
]}
|
|
3174
|
-
type="single"
|
|
3175
|
-
openIds={openIds}
|
|
3176
|
-
onOpenIdsChange={setOpenIds}
|
|
3177
|
-
/>
|
|
3178
|
-
);
|
|
3179
|
-
})()}
|
|
3180
|
-
</ComponentCard>
|
|
3181
|
-
|
|
3182
|
-
{/* AspectRatio */}
|
|
3183
|
-
<ComponentCard
|
|
3184
|
-
name="AspectRatio"
|
|
3185
|
-
importLine="import { AspectRatio } from '@sovereignfs/ui';"
|
|
3186
|
-
usage="Constrains content (image, video, embed) to a fixed ratio."
|
|
3187
|
-
>
|
|
3188
|
-
<div style={{ width: 200 }}>
|
|
3189
|
-
<AspectRatio ratio={16 / 9}>
|
|
3190
|
-
<div
|
|
3191
|
-
style={{
|
|
3192
|
-
width: '100%',
|
|
3193
|
-
height: '100%',
|
|
3194
|
-
background: 'var(--sv-color-surface-sunken)',
|
|
3195
|
-
display: 'flex',
|
|
3196
|
-
alignItems: 'center',
|
|
3197
|
-
justifyContent: 'center',
|
|
3198
|
-
color: 'var(--sv-color-text-muted)',
|
|
3199
|
-
fontSize: 'var(--sv-font-size-xs)',
|
|
3200
|
-
}}
|
|
3201
|
-
>
|
|
3202
|
-
16:9
|
|
3203
|
-
</div>
|
|
3204
|
-
</AspectRatio>
|
|
3205
|
-
</div>
|
|
3206
|
-
</ComponentCard>
|
|
3207
|
-
|
|
3208
|
-
{/* ButtonGroup */}
|
|
3209
|
-
<ComponentCard
|
|
3210
|
-
name="ButtonGroup"
|
|
3211
|
-
importLine="import { ButtonGroup } from '@sovereignfs/ui';"
|
|
3212
|
-
usage="Visually joins adjacent Buttons into one control (shared border, connected corners)."
|
|
3213
|
-
>
|
|
3214
|
-
<ButtonGroup aria-label="View">
|
|
3215
|
-
<Button variant="secondary" size="sm">
|
|
3216
|
-
Day
|
|
3217
|
-
</Button>
|
|
3218
|
-
<Button variant="secondary" size="sm">
|
|
3219
|
-
Week
|
|
3220
|
-
</Button>
|
|
3221
|
-
<Button variant="secondary" size="sm">
|
|
3222
|
-
Month
|
|
3223
|
-
</Button>
|
|
3224
|
-
</ButtonGroup>
|
|
3225
|
-
</ComponentCard>
|
|
3226
|
-
|
|
3227
|
-
{/* Item */}
|
|
3228
|
-
<ComponentCard
|
|
3229
|
-
name="Item"
|
|
3230
|
-
importLine="import { Item } from '@sovereignfs/ui';"
|
|
3231
|
-
usage="Generic row primitive: leading slot, title + optional description, trailing slot. For settings rows and list rows that don't fit Menu's own item shape."
|
|
3232
|
-
>
|
|
3233
|
-
<Item
|
|
3234
|
-
title="Notifications"
|
|
3235
|
-
description="Manage email and push alerts"
|
|
3236
|
-
leading={<Icon name="bell" aria-hidden />}
|
|
3237
|
-
trailing={<Icon name="chevron-right" size="sm" aria-hidden />}
|
|
3238
|
-
onClick={() => {}}
|
|
3239
|
-
/>
|
|
3240
|
-
</ComponentCard>
|
|
3241
|
-
|
|
3242
|
-
{/* Label */}
|
|
3243
|
-
<ComponentCard
|
|
3244
|
-
name="Label"
|
|
3245
|
-
importLine="import { Label } from '@sovereignfs/ui';"
|
|
3246
|
-
usage="Standalone accessible form label, independent of FormField. Use FormField when a control needs hint/error text too."
|
|
3247
|
-
>
|
|
3248
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
|
|
3249
|
-
<Label htmlFor="label-demo-input">Email address</Label>
|
|
3250
|
-
<input
|
|
3251
|
-
id="label-demo-input"
|
|
3252
|
-
type="email"
|
|
3253
|
-
placeholder="you@example.com"
|
|
3254
|
-
style={{
|
|
3255
|
-
padding: 'var(--sv-space-2) var(--sv-space-3)',
|
|
3256
|
-
border: '1px solid var(--sv-color-border-strong)',
|
|
3257
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
3258
|
-
fontFamily: 'var(--sv-font-family)',
|
|
3259
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
3260
|
-
}}
|
|
3261
|
-
/>
|
|
3262
|
-
</div>
|
|
3263
|
-
</ComponentCard>
|
|
3264
|
-
|
|
3265
|
-
{/* ScrollArea */}
|
|
3266
|
-
<ComponentCard
|
|
3267
|
-
name="ScrollArea"
|
|
3268
|
-
importLine="import { ScrollArea } from '@sovereignfs/ui';"
|
|
3269
|
-
usage="Styled scrollable container — thin, token-colored scrollbar instead of the OS default. Native overflow scrolling underneath."
|
|
3270
|
-
>
|
|
3271
|
-
<ScrollArea maxHeight={120}>
|
|
3272
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 8, padding: 4 }}>
|
|
3273
|
-
{Array.from({ length: 10 }, (_, i) => (
|
|
3274
|
-
<div key={i} style={{ fontSize: 13 }}>
|
|
3275
|
-
Row {i + 1}
|
|
3276
|
-
</div>
|
|
3277
|
-
))}
|
|
3278
|
-
</div>
|
|
3279
|
-
</ScrollArea>
|
|
3280
|
-
</ComponentCard>
|
|
3281
|
-
|
|
3282
|
-
{/* Typography */}
|
|
3283
|
-
<ComponentCard
|
|
3284
|
-
name="Typography"
|
|
3285
|
-
importLine="import { Typography } from '@sovereignfs/ui';"
|
|
3286
|
-
usage="Text bound to the design system's font-size/weight scale. Pass `as` to override the rendered tag while keeping the variant's visual style."
|
|
3287
|
-
>
|
|
3288
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
3289
|
-
<Typography variant="h3">Heading 3</Typography>
|
|
3290
|
-
<Typography variant="body">Body copy for descriptions.</Typography>
|
|
3291
|
-
<Typography variant="caption">Caption — secondary text.</Typography>
|
|
3292
|
-
</div>
|
|
3293
|
-
</ComponentCard>
|
|
3294
|
-
|
|
3295
|
-
{/* Marker */}
|
|
3296
|
-
<ComponentCard
|
|
3297
|
-
name="Marker"
|
|
3298
|
-
importLine="import { Marker } from '@sovereignfs/ui';"
|
|
3299
|
-
usage="Inline citation/reference marker — attributes part of an assistant answer to a source (Sovereign Harness 'source trace', RFC 0040)."
|
|
3300
|
-
>
|
|
3301
|
-
<p style={{ fontSize: 14, margin: 0 }}>
|
|
3302
|
-
Your next task is due Thursday
|
|
3303
|
-
<Marker index={1} label="Source: Tasks" onClick={() => {}} />.
|
|
3304
|
-
</p>
|
|
3305
|
-
</ComponentCard>
|
|
3306
|
-
|
|
3307
|
-
{/* Message */}
|
|
3308
|
-
<ComponentCard
|
|
3309
|
-
name="Message"
|
|
3310
|
-
importLine="import { Message } from '@sovereignfs/ui';"
|
|
3311
|
-
usage="A single chat turn for the Sovereign Harness assistant. role is user/assistant/tool, matching harness_messages (RFC 0040). Content is caller-controlled ReactNode."
|
|
3312
|
-
>
|
|
3313
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
|
3314
|
-
<Message sender="user">What's on my plate this week?</Message>
|
|
3315
|
-
<Message sender="assistant">You have 3 tasks due this week.</Message>
|
|
3316
|
-
</div>
|
|
3317
|
-
</ComponentCard>
|
|
3318
|
-
|
|
3319
|
-
{/* MessageScroller */}
|
|
3320
|
-
<ComponentCard
|
|
3321
|
-
name="MessageScroller"
|
|
3322
|
-
importLine="import { MessageScroller } from '@sovereignfs/ui';"
|
|
3323
|
-
usage="Auto-scrolling chat container. Scrolls to the newest message while the user is near the bottom; shows a 'New messages' button instead of yanking them down if they've scrolled up."
|
|
3324
|
-
>
|
|
3325
|
-
<div style={{ height: 160, border: '1px solid var(--sv-color-border)' }}>
|
|
3326
|
-
<MessageScroller>
|
|
3327
|
-
<Message sender="user">Hi there</Message>
|
|
3328
|
-
<Message sender="assistant">How can I help?</Message>
|
|
3329
|
-
</MessageScroller>
|
|
3330
|
-
</div>
|
|
3331
|
-
</ComponentCard>
|
|
3332
|
-
|
|
3333
|
-
{/* HoverCard */}
|
|
3334
|
-
<ComponentCard
|
|
3335
|
-
name="HoverCard"
|
|
3336
|
-
importLine="import { HoverCard } from '@sovereignfs/ui';"
|
|
3337
|
-
usage="Hover-triggered popover on desktop, tap-to-toggle on touch. Built on Popover for positioning — adds hover-intent timing and the touch fallback. Opens on keyboard focus too."
|
|
3338
|
-
>
|
|
3339
|
-
<HoverCard
|
|
3340
|
-
aria-label="User preview"
|
|
3341
|
-
trigger={
|
|
3342
|
-
<button
|
|
3343
|
-
type="button"
|
|
3344
|
-
style={{ background: 'none', border: 'none', padding: 0, cursor: 'pointer' }}
|
|
3345
|
-
>
|
|
3346
|
-
<Avatar name="Jane Smith" size="sm" />
|
|
3347
|
-
</button>
|
|
3348
|
-
}
|
|
3349
|
-
>
|
|
3350
|
-
<div style={{ padding: 'var(--sv-space-3)', fontSize: 14 }}>
|
|
3351
|
-
<div style={{ fontWeight: 600 }}>Jane Smith</div>
|
|
3352
|
-
<div style={{ color: 'var(--sv-color-text-muted)' }}>jane@example.com</div>
|
|
3353
|
-
</div>
|
|
3354
|
-
</HoverCard>
|
|
3355
|
-
</ComponentCard>
|
|
3356
|
-
|
|
3357
|
-
{/* ContextMenu */}
|
|
3358
|
-
<ComponentCard
|
|
3359
|
-
name="ContextMenu"
|
|
3360
|
-
importLine="import { ContextMenu } from '@sovereignfs/ui';"
|
|
3361
|
-
usage="Right-click menu on desktop, long-press on touch. Desktop positioning reuses Popover's collision detection anchored to the click point; touch opens the same items in a Drawer."
|
|
3362
|
-
>
|
|
3363
|
-
<ContextMenu
|
|
3364
|
-
aria-label="Row actions"
|
|
3365
|
-
items={[
|
|
3366
|
-
{ label: 'Rename', onSelect: () => {} },
|
|
3367
|
-
{ label: 'Duplicate', onSelect: () => {} },
|
|
3368
|
-
{ type: 'separator' },
|
|
3369
|
-
{ label: 'Delete', destructive: true, onSelect: () => {} },
|
|
3370
|
-
]}
|
|
3371
|
-
>
|
|
3372
|
-
<div
|
|
3373
|
-
style={{
|
|
3374
|
-
padding: 'var(--sv-space-4)',
|
|
3375
|
-
border: '1px dashed var(--sv-color-border-strong)',
|
|
3376
|
-
borderRadius: 'var(--sv-radius-md)',
|
|
3377
|
-
textAlign: 'center',
|
|
3378
|
-
fontSize: 14,
|
|
3379
|
-
color: 'var(--sv-color-text-muted)',
|
|
3380
|
-
}}
|
|
3381
|
-
>
|
|
3382
|
-
Right-click here
|
|
3383
|
-
</div>
|
|
3384
|
-
</ContextMenu>
|
|
3385
|
-
</ComponentCard>
|
|
3386
|
-
|
|
3387
|
-
{/* NavigationMenu */}
|
|
3388
|
-
<ComponentCard
|
|
3389
|
-
name="NavigationMenu"
|
|
3390
|
-
importLine="import { NavigationMenu } from '@sovereignfs/ui';"
|
|
3391
|
-
usage="Top-level nav bar where some items open a flyout panel. Desktop-oriented — a hover-triggered flyout bar has no mobile equivalent."
|
|
3392
|
-
>
|
|
3393
|
-
<NavigationMenu
|
|
3394
|
-
aria-label="Main"
|
|
3395
|
-
items={[
|
|
3396
|
-
{ label: 'Home', href: '/home' },
|
|
3397
|
-
{
|
|
3398
|
-
label: 'Products',
|
|
3399
|
-
content: (
|
|
3400
|
-
<div style={{ padding: 'var(--sv-space-3)', fontSize: 14 }}>
|
|
3401
|
-
Tasks · Ledger · Shopper
|
|
3402
|
-
</div>
|
|
3403
|
-
),
|
|
3404
|
-
},
|
|
3405
|
-
]}
|
|
3406
|
-
/>
|
|
3407
|
-
</ComponentCard>
|
|
3408
|
-
|
|
3409
|
-
{/* Menubar */}
|
|
3410
|
-
<ComponentCard
|
|
3411
|
-
name="Menubar"
|
|
3412
|
-
importLine="import { Menubar } from '@sovereignfs/ui';"
|
|
3413
|
-
usage="Desktop app-style menu bar (File/Edit/View...). A thin composition of NavigationMenu with MenuEntries as each item's flyout content."
|
|
3414
|
-
>
|
|
3415
|
-
<Menubar
|
|
3416
|
-
menus={[
|
|
3417
|
-
{
|
|
3418
|
-
label: 'File',
|
|
3419
|
-
items: [
|
|
3420
|
-
{ label: 'New conversation', onSelect: () => {} },
|
|
3421
|
-
{ label: 'Export', onSelect: () => {} },
|
|
3422
|
-
],
|
|
3423
|
-
},
|
|
3424
|
-
{
|
|
3425
|
-
label: 'Edit',
|
|
3426
|
-
items: [
|
|
3427
|
-
{ label: 'Undo', onSelect: () => {} },
|
|
3428
|
-
{ label: 'Redo', onSelect: () => {} },
|
|
3429
|
-
],
|
|
3430
|
-
},
|
|
3431
|
-
]}
|
|
3432
|
-
/>
|
|
3433
|
-
</ComponentCard>
|
|
3434
|
-
|
|
3435
|
-
{/* Command */}
|
|
3436
|
-
<ComponentCard
|
|
3437
|
-
name="Command"
|
|
3438
|
-
importLine="import { Command } from '@sovereignfs/ui';"
|
|
3439
|
-
usage="⌘K-style command palette. Opened via Dialog for the modal shell — adds the search input, substring filtering, and arrow-key/Enter selection. Controlled: consumer owns the open state."
|
|
3440
|
-
>
|
|
3441
|
-
<CommandDemo />
|
|
3442
|
-
</ComponentCard>
|
|
3443
|
-
|
|
3444
|
-
{/* Resizable */}
|
|
3445
|
-
<ComponentCard
|
|
3446
|
-
name="Resizable"
|
|
3447
|
-
importLine="import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from '@sovereignfs/ui';"
|
|
3448
|
-
usage="A row or column of resizable panes. Each handle resizes only its two immediate neighbor panels. Desktop-oriented — panels render at their default sizes on touch."
|
|
3449
|
-
>
|
|
3450
|
-
<div style={{ width: '100%', height: 160 }}>
|
|
3451
|
-
<ResizablePanelGroup direction="horizontal">
|
|
3452
|
-
<ResizablePanel defaultSize={30} minSize={15} maxSize={50}>
|
|
3453
|
-
<div
|
|
3454
|
-
style={{
|
|
3455
|
-
display: 'flex',
|
|
3456
|
-
alignItems: 'center',
|
|
3457
|
-
justifyContent: 'center',
|
|
3458
|
-
height: '100%',
|
|
3459
|
-
fontSize: 14,
|
|
3460
|
-
color: 'var(--sv-color-text-muted)',
|
|
3461
|
-
}}
|
|
3462
|
-
>
|
|
3463
|
-
Sidebar
|
|
3464
|
-
</div>
|
|
3465
|
-
</ResizablePanel>
|
|
3466
|
-
<ResizableHandle aria-label="Resize sidebar" />
|
|
3467
|
-
<ResizablePanel defaultSize={70}>
|
|
3468
|
-
<div
|
|
3469
|
-
style={{
|
|
3470
|
-
display: 'flex',
|
|
3471
|
-
alignItems: 'center',
|
|
3472
|
-
justifyContent: 'center',
|
|
3473
|
-
height: '100%',
|
|
3474
|
-
fontSize: 14,
|
|
3475
|
-
color: 'var(--sv-color-text-muted)',
|
|
3476
|
-
}}
|
|
3477
|
-
>
|
|
3478
|
-
Content
|
|
3479
|
-
</div>
|
|
3480
|
-
</ResizablePanel>
|
|
3481
|
-
</ResizablePanelGroup>
|
|
3482
|
-
</div>
|
|
3483
|
-
</ComponentCard>
|
|
3484
|
-
|
|
3485
|
-
{/* DataTable */}
|
|
3486
|
-
<ComponentCard
|
|
3487
|
-
name="DataTable"
|
|
3488
|
-
importLine="import { DataTable } from '@sovereignfs/ui';"
|
|
3489
|
-
usage="Sortable table built on Table. Column-driven: pass columns + data instead of composing TableRow/TableCell by hand. Clicking a sortable header cycles ascending → descending → unsorted."
|
|
3490
|
-
>
|
|
3491
|
-
<DataTable
|
|
3492
|
-
columns={[
|
|
3493
|
-
{ key: 'name', header: 'Plugin', sortable: true },
|
|
3494
|
-
{ key: 'installs', header: 'Installs', sortable: true, align: 'end' },
|
|
3495
|
-
]}
|
|
3496
|
-
data={[
|
|
3497
|
-
{ id: 'tasks', name: 'sovereign-tasks', installs: 482 },
|
|
3498
|
-
{ id: 'ledger', name: 'sovereign-ledger', installs: 219 },
|
|
3499
|
-
{ id: 'shopper', name: 'sovereign-shopper', installs: 37 },
|
|
3500
|
-
]}
|
|
3501
|
-
getRowKey={(row) => row.id}
|
|
3502
|
-
/>
|
|
3503
|
-
</ComponentCard>
|
|
3504
|
-
|
|
3505
|
-
{/* Combobox */}
|
|
3506
|
-
<ComponentCard
|
|
3507
|
-
name="Combobox"
|
|
3508
|
-
importLine="import { Combobox } from '@sovereignfs/ui';"
|
|
3509
|
-
usage="A searchable single-select: Popover on desktop, a bottom-sheet Drawer on mobile. For a short fixed list where search adds no value, use Select's native <select> instead."
|
|
3510
|
-
>
|
|
3511
|
-
<ComboboxDemo />
|
|
3512
|
-
</ComponentCard>
|
|
3513
|
-
|
|
3514
|
-
{/* DragHandleRow */}
|
|
3515
|
-
<ComponentCard
|
|
3516
|
-
name="DragHandleRow"
|
|
3517
|
-
importLine="import { DragHandleRow } from '@sovereignfs/ui';"
|
|
3518
|
-
usage="Row wrapper with a drag handle that appears on hover. Attach dnd-kit sortable props via handleProps."
|
|
3519
|
-
>
|
|
3520
|
-
<div style={{ width: '100%' }}>
|
|
3521
|
-
{['First item', 'Second item', 'Third item'].map((label) => (
|
|
3522
|
-
<DragHandleRow key={label}>
|
|
3523
|
-
<div
|
|
3524
|
-
style={{
|
|
3525
|
-
padding: 'var(--sv-space-2) var(--sv-space-1)',
|
|
3526
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
3527
|
-
color: 'var(--sv-color-text-primary)',
|
|
3528
|
-
borderBottom: '1px solid var(--sv-color-border)',
|
|
3529
|
-
}}
|
|
3530
|
-
>
|
|
3531
|
-
{label}
|
|
3532
|
-
</div>
|
|
3533
|
-
</DragHandleRow>
|
|
3534
|
-
))}
|
|
3535
|
-
</div>
|
|
3536
|
-
</ComponentCard>
|
|
3537
|
-
|
|
3538
|
-
{/* GripIcon */}
|
|
3539
|
-
<ComponentCard
|
|
3540
|
-
name="GripIcon"
|
|
3541
|
-
importLine="import { GripIcon } from '@sovereignfs/ui';"
|
|
3542
|
-
usage="Six-dot drag-handle icon, sized via `size` (default 14). No baked-in pointer-events or color — DragHandleRow uses it internally; a floating, absolutely-positioned handle can use it directly."
|
|
3543
|
-
>
|
|
3544
|
-
<div style={{ color: 'var(--sv-color-text-muted)' }}>
|
|
3545
|
-
<GripIcon />
|
|
3546
|
-
</div>
|
|
3547
|
-
</ComponentCard>
|
|
3548
|
-
|
|
3549
|
-
{/* StepStrip */}
|
|
3550
|
-
<ComponentCard
|
|
3551
|
-
name="StepStrip"
|
|
3552
|
-
importLine="import { StepStrip } from '@sovereignfs/ui';"
|
|
3553
|
-
usage="Horizontal connected-chip strip (a route/itinerary sequence) with an optional trailing add chip. Purely presentational — renderItem owns each chip's content, click handling, and any drag-reorder wiring (attach dnd-kit's useSortable the same way DragHandleRow's handleProps expects it)."
|
|
3554
|
-
>
|
|
3555
|
-
<StepStripDemo />
|
|
3556
|
-
</ComponentCard>
|
|
3557
|
-
|
|
3558
|
-
{/* FileDropzone */}
|
|
3559
|
-
<ComponentCard
|
|
3560
|
-
name="FileDropzone"
|
|
3561
|
-
importLine="import { FileDropzone } from '@sovereignfs/ui';"
|
|
3562
|
-
usage="Styled drag-and-drop file picker — a dashed-border dropzone wrapping a visually-hidden native file input. Caller owns selected-file state and passes label/hint accordingly."
|
|
3563
|
-
>
|
|
3564
|
-
<FileDropzoneDemo />
|
|
3565
|
-
</ComponentCard>
|
|
3566
|
-
|
|
3567
|
-
{/* ResponsiveSurface */}
|
|
3568
|
-
<ComponentCard
|
|
3569
|
-
name="ResponsiveSurface"
|
|
3570
|
-
importLine="import { ResponsiveSurface } from '@sovereignfs/ui';"
|
|
3571
|
-
usage="Renders an entirely different component tree below a breakpoint, not a CSS squeeze of the same one — only the active side is ever mounted. See also the useResponsiveLayout hook for non-JSX values."
|
|
3572
|
-
>
|
|
3573
|
-
<ResponsiveSurfaceDemo />
|
|
3574
|
-
</ComponentCard>
|
|
3575
|
-
|
|
3576
|
-
{/* SwipableMobileCarousel */}
|
|
3577
|
-
<ComponentCard
|
|
3578
|
-
name="SwipableMobileCarousel"
|
|
3579
|
-
importLine="import { SwipableMobileCarousel, SwipableMobileCarouselSlide, SwipableMobileCarouselSlideHeader, SwipableMobileCarouselSlideBody } from '@sovereignfs/ui';"
|
|
3580
|
-
usage="Compound component for swiping between full-width slides. A Slide's Header/Footer always render; only Body's own loading prop gates its content, so a title can show immediately while the body's own fetch is still in flight. Owns mount-window/reorder mechanics only — never put cross-slide aggregation or a detail overlay inside a Slide."
|
|
3581
|
-
>
|
|
3582
|
-
<SwipableMobileCarouselDemo />
|
|
3583
|
-
</ComponentCard>
|
|
3584
|
-
|
|
3585
|
-
{/* SwipableMobileCarouselDots */}
|
|
3586
|
-
<ComponentCard
|
|
3587
|
-
name="SwipableMobileCarouselDots"
|
|
3588
|
-
importLine="import { SwipableMobileCarouselDots } from '@sovereignfs/ui';"
|
|
3589
|
-
usage="A real, tappable, labeled slide indicator (role=tablist/tab) — standalone and reusable outside SwipableMobileCarousel too, and its default renderIndicator."
|
|
3590
|
-
>
|
|
3591
|
-
<SwipableMobileCarouselDotsDemo />
|
|
3592
|
-
</ComponentCard>
|
|
3593
|
-
|
|
3594
|
-
{/* SwipeStack */}
|
|
3595
|
-
<ComponentCard
|
|
3596
|
-
name="SwipeStack"
|
|
3597
|
-
importLine="import { SwipeStack, SwipeStackCard } from '@sovereignfs/ui';"
|
|
3598
|
-
usage="Triage one card at a time by dragging left/right/up/down, plus an always-visible fallback button per configured direction wired to the identical commit path. Every direction is independently optional and has no baked-in meaning — the caller supplies label/icon and reacts to onSwipe; once triggered a direction removes the card for good, there is no reversible 'go back'."
|
|
3599
|
-
>
|
|
3600
|
-
<SwipeStackDemo />
|
|
3601
|
-
</ComponentCard>
|
|
3602
|
-
</div>
|
|
3603
|
-
</section>
|
|
3604
|
-
|
|
3605
|
-
{/* ── Theming ──────────────────────────────────────────────────── */}
|
|
3606
|
-
<section style={{ marginBottom: 'var(--sv-space-12)' }}>
|
|
3607
|
-
<SectionHeader
|
|
3608
|
-
title="Tenant theming"
|
|
3609
|
-
subtitle="The identity is monochrome by default. Tenants add brand color by overriding --sv-color-accent."
|
|
3610
|
-
/>
|
|
3611
|
-
<p
|
|
3612
|
-
style={{
|
|
3613
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
3614
|
-
color: 'var(--sv-color-text-muted)',
|
|
3615
|
-
marginBottom: 'var(--sv-space-4)',
|
|
3616
|
-
}}
|
|
3617
|
-
>
|
|
3618
|
-
Set via the Console → Instance identity section (or the{' '}
|
|
3619
|
-
<code style={{ fontFamily: ffm }}>/api/instance/</code> API). Plugin CSS does not need
|
|
3620
|
-
to change — all components read{' '}
|
|
3621
|
-
<code style={{ fontFamily: ffm }}>--sv-color-accent</code> from the cascade.
|
|
3622
|
-
</p>
|
|
3623
|
-
<Code>{`/* Operator CSS override — injected by InstanceProvider */
|
|
3624
|
-
:root {
|
|
3625
|
-
--sv-color-accent: #5c6bc0; /* brand blue */
|
|
3626
|
-
--sv-color-accent-hover: #3949ab;
|
|
3627
|
-
--sv-color-focus-ring: #5c6bc0;
|
|
3628
|
-
--sv-color-text-on-accent: #ffffff; /* must contrast with accent */
|
|
3629
|
-
}`}</Code>
|
|
3630
|
-
|
|
3631
|
-
<DemoBox style={{ marginTop: 'var(--sv-space-4)' }}>
|
|
3632
|
-
<Button style={{ background: '#5c6bc0', borderColor: '#5c6bc0', color: '#fff' }}>
|
|
3633
|
-
Themed button
|
|
3634
|
-
</Button>
|
|
3635
|
-
<span
|
|
3636
|
-
style={{
|
|
3637
|
-
display: 'inline-block',
|
|
3638
|
-
fontFamily: ff,
|
|
3639
|
-
fontSize: 'var(--sv-font-size-caption)',
|
|
3640
|
-
fontWeight: 600,
|
|
3641
|
-
padding: '2px 8px',
|
|
3642
|
-
borderRadius: 'var(--sv-radius-sm)',
|
|
3643
|
-
background: '#e8eaf6',
|
|
3644
|
-
color: '#3949ab',
|
|
3645
|
-
border: '1px solid #c5cae9',
|
|
3646
|
-
}}
|
|
3647
|
-
>
|
|
3648
|
-
Owner
|
|
3649
|
-
</span>
|
|
3650
|
-
<span
|
|
3651
|
-
style={{
|
|
3652
|
-
fontFamily: ff,
|
|
3653
|
-
fontSize: 'var(--sv-font-size-caption)',
|
|
3654
|
-
color: 'var(--sv-color-text-muted)',
|
|
3655
|
-
}}
|
|
3656
|
-
>
|
|
3657
|
-
Components read the accent token — brand applied everywhere.
|
|
3658
|
-
</span>
|
|
3659
|
-
</DemoBox>
|
|
3660
|
-
</section>
|
|
3661
|
-
|
|
3662
|
-
{/* ── Rules ────────────────────────────────────────────────────── */}
|
|
3663
|
-
<section style={{ marginBottom: 'var(--sv-space-8)' }}>
|
|
3664
|
-
<SectionHeader
|
|
3665
|
-
title="Design rules"
|
|
3666
|
-
subtitle="The short list of things that break dark mode or instance theming if ignored."
|
|
3667
|
-
/>
|
|
3668
|
-
<div
|
|
3669
|
-
style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 'var(--sv-space-5)' }}
|
|
3670
|
-
>
|
|
3671
|
-
<div
|
|
3672
|
-
style={{
|
|
3673
|
-
background: 'var(--sv-color-success-surface)',
|
|
3674
|
-
border: '1px solid var(--sv-color-success-border)',
|
|
3675
|
-
borderRadius: 'var(--sv-radius-lg)',
|
|
3676
|
-
padding: 'var(--sv-space-4)',
|
|
3677
|
-
}}
|
|
3678
|
-
>
|
|
3679
|
-
<p
|
|
3680
|
-
style={{
|
|
3681
|
-
fontWeight: 600,
|
|
3682
|
-
color: 'var(--sv-color-success-text)',
|
|
3683
|
-
marginBottom: 'var(--sv-space-3)',
|
|
3684
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
3685
|
-
}}
|
|
3686
|
-
>
|
|
3687
|
-
✓ Do
|
|
3688
|
-
</p>
|
|
3689
|
-
<ul
|
|
3690
|
-
style={{
|
|
3691
|
-
margin: 0,
|
|
3692
|
-
padding: '0 0 0 var(--sv-space-4)',
|
|
3693
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
3694
|
-
color: 'var(--sv-color-text-primary)',
|
|
3695
|
-
lineHeight: 1.8,
|
|
3696
|
-
}}
|
|
3697
|
-
>
|
|
3698
|
-
<li>
|
|
3699
|
-
Use <code style={{ fontFamily: ffm }}>--sv-color-*</code> semantic tokens
|
|
3700
|
-
</li>
|
|
3701
|
-
<li>
|
|
3702
|
-
Use <code style={{ fontFamily: ffm }}>--sv-space-*</code>,{' '}
|
|
3703
|
-
<code style={{ fontFamily: ffm }}>--sv-radius-*</code> scale tokens
|
|
3704
|
-
</li>
|
|
3705
|
-
<li>
|
|
3706
|
-
Use <code style={{ fontFamily: ffm }}>--sv-font-family</code> and{' '}
|
|
3707
|
-
<code style={{ fontFamily: ffm }}>--sv-font-family-mono</code>
|
|
3708
|
-
</li>
|
|
3709
|
-
<li>
|
|
3710
|
-
Import components from <code style={{ fontFamily: ffm }}>@sovereignfs/ui</code>
|
|
3711
|
-
</li>
|
|
3712
|
-
<li>
|
|
3713
|
-
Use <code style={{ fontFamily: ffm }}>--sv-shadow-*</code> for elevation
|
|
3714
|
-
</li>
|
|
3715
|
-
<li>
|
|
3716
|
-
Use <code style={{ fontFamily: ffm }}>--sv-border-width-hairline</code> instead of
|
|
3717
|
-
a literal border width (RFC 0094)
|
|
3718
|
-
</li>
|
|
3719
|
-
</ul>
|
|
3720
|
-
</div>
|
|
3721
|
-
<div
|
|
3722
|
-
style={{
|
|
3723
|
-
background: 'var(--sv-color-error-surface)',
|
|
3724
|
-
border: '1px solid var(--sv-color-error-border)',
|
|
3725
|
-
borderRadius: 'var(--sv-radius-lg)',
|
|
3726
|
-
padding: 'var(--sv-space-4)',
|
|
3727
|
-
}}
|
|
3728
|
-
>
|
|
3729
|
-
<p
|
|
3730
|
-
style={{
|
|
3731
|
-
fontWeight: 600,
|
|
3732
|
-
color: 'var(--sv-color-error-text)',
|
|
3733
|
-
marginBottom: 'var(--sv-space-3)',
|
|
3734
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
3735
|
-
}}
|
|
3736
|
-
>
|
|
3737
|
-
✗ Don't
|
|
3738
|
-
</p>
|
|
3739
|
-
<ul
|
|
3740
|
-
style={{
|
|
3741
|
-
margin: 0,
|
|
3742
|
-
padding: '0 0 0 var(--sv-space-4)',
|
|
3743
|
-
fontSize: 'var(--sv-font-size-sm)',
|
|
3744
|
-
color: 'var(--sv-color-text-primary)',
|
|
3745
|
-
lineHeight: 1.8,
|
|
3746
|
-
}}
|
|
3747
|
-
>
|
|
3748
|
-
<li>
|
|
3749
|
-
Hardcode hex values like <code style={{ fontFamily: ffm }}>#333</code>
|
|
3750
|
-
</li>
|
|
3751
|
-
<li>
|
|
3752
|
-
Use primitive tokens like <code style={{ fontFamily: ffm }}>--sv-grey-900</code>{' '}
|
|
3753
|
-
directly
|
|
3754
|
-
</li>
|
|
3755
|
-
<li>Use Tailwind classes or runtime CSS-in-JS</li>
|
|
3756
|
-
<li>
|
|
3757
|
-
Import from <code style={{ fontFamily: ffm }}>runtime/src</code> (SDK boundary)
|
|
3758
|
-
</li>
|
|
3759
|
-
<li>
|
|
3760
|
-
Override <code style={{ fontFamily: ffm }}>--sv-color-accent</code> from plugin
|
|
3761
|
-
CSS
|
|
3762
|
-
</li>
|
|
3763
|
-
</ul>
|
|
3764
|
-
</div>
|
|
3765
|
-
</div>
|
|
3766
|
-
</section>
|
|
3767
|
-
</div>
|
|
3768
|
-
</div>
|
|
3769
|
-
);
|
|
3770
|
-
}
|
|
3771
|
-
|
|
3772
|
-
// ---------------------------------------------------------------------------
|
|
3773
|
-
// Story
|
|
3774
|
-
// ---------------------------------------------------------------------------
|
|
3775
|
-
|
|
3776
|
-
const meta = {
|
|
3777
|
-
title: 'Overview',
|
|
3778
|
-
component: OverviewPage,
|
|
3779
|
-
parameters: {
|
|
3780
|
-
layout: 'fullscreen',
|
|
3781
|
-
docs: {
|
|
3782
|
-
description: {
|
|
3783
|
-
component:
|
|
3784
|
-
'Complete reference for plugin developers — components, tokens, theming, and design rules in one page.',
|
|
3785
|
-
},
|
|
3786
|
-
},
|
|
3787
|
-
},
|
|
3788
|
-
} satisfies Meta<typeof OverviewPage>;
|
|
3789
|
-
|
|
3790
|
-
export default meta;
|
|
3791
|
-
type Story = StoryObj<typeof meta>;
|
|
3792
|
-
|
|
3793
|
-
export const DesignSystem: Story = {};
|