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