tempest-react-sdk 0.24.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -4
- package/bin/lib/alias/collect.mjs +94 -0
- package/bin/lib/alias/collect.test.mjs +95 -0
- package/bin/lib/alias/discover.mjs +77 -0
- package/bin/lib/alias/discover.test.mjs +189 -0
- package/bin/lib/alias/index.mjs +88 -0
- package/bin/lib/alias/index.test.mjs +138 -0
- package/bin/lib/alias/rewrite-css.mjs +76 -0
- package/bin/lib/alias/rewrite-css.test.mjs +85 -0
- package/bin/lib/alias/rewrite.mjs +141 -0
- package/bin/lib/alias/rewrite.test.mjs +148 -0
- package/bin/lib/alias/specifier.mjs +49 -0
- package/bin/lib/alias/specifier.test.mjs +85 -0
- package/bin/lib/alias/tsconfig.mjs +116 -0
- package/bin/lib/alias/typescript.mjs +23 -0
- package/bin/lib/icons/generate.mjs +156 -0
- package/bin/lib/icons/generate.test.mjs +125 -0
- package/bin/tempest.mjs +158 -13
- package/dist/audio/use-audio.cjs +1 -1
- package/dist/audio/use-audio.cjs.map +1 -1
- package/dist/audio/use-audio.js +12 -12
- package/dist/audio/use-audio.js.map +1 -1
- package/dist/charts/AreaChart.cjs +1 -1
- package/dist/charts/AreaChart.cjs.map +1 -1
- package/dist/charts/AreaChart.js +7 -7
- package/dist/charts/AreaChart.js.map +1 -1
- package/dist/charts/BarChart.cjs +1 -1
- package/dist/charts/BarChart.cjs.map +1 -1
- package/dist/charts/BarChart.js +7 -7
- package/dist/charts/BarChart.js.map +1 -1
- package/dist/charts/LineChart.cjs +1 -1
- package/dist/charts/LineChart.cjs.map +1 -1
- package/dist/charts/LineChart.js +6 -6
- package/dist/charts/LineChart.js.map +1 -1
- package/dist/charts/PieChart.cjs +1 -1
- package/dist/charts/PieChart.cjs.map +1 -1
- package/dist/charts/PieChart.js +6 -6
- package/dist/charts/PieChart.js.map +1 -1
- package/dist/charts/RadarChart.cjs +1 -1
- package/dist/charts/RadarChart.cjs.map +1 -1
- package/dist/charts/RadarChart.js +6 -6
- package/dist/charts/RadarChart.js.map +1 -1
- package/dist/charts/palette.cjs +1 -1
- package/dist/charts/palette.cjs.map +1 -1
- package/dist/charts/palette.js +19 -2
- package/dist/charts/palette.js.map +1 -1
- package/dist/charts/use-chart-colors.cjs +2 -0
- package/dist/charts/use-chart-colors.cjs.map +1 -0
- package/dist/charts/use-chart-colors.js +29 -0
- package/dist/charts/use-chart-colors.js.map +1 -0
- package/dist/charts.cjs +1 -1
- package/dist/charts.d.ts +61 -10
- package/dist/charts.js +8 -7
- package/dist/components/AvatarGroup/AvatarGroup.cjs +2 -0
- package/dist/components/AvatarGroup/AvatarGroup.cjs.map +1 -0
- package/dist/components/AvatarGroup/AvatarGroup.js +39 -0
- package/dist/components/AvatarGroup/AvatarGroup.js.map +1 -0
- package/dist/components/AvatarGroup/AvatarGroup.module.cjs +2 -0
- package/dist/components/AvatarGroup/AvatarGroup.module.cjs.map +1 -0
- package/dist/components/AvatarGroup/AvatarGroup.module.js +16 -0
- package/dist/components/AvatarGroup/AvatarGroup.module.js.map +1 -0
- package/dist/components/ClickOutside/ClickOutside.cjs +1 -1
- package/dist/components/ClickOutside/ClickOutside.cjs.map +1 -1
- package/dist/components/ClickOutside/ClickOutside.js +3 -1
- package/dist/components/ClickOutside/ClickOutside.js.map +1 -1
- package/dist/components/DataTable/DataTable.cjs +1 -1
- package/dist/components/DataTable/DataTable.cjs.map +1 -1
- package/dist/components/DataTable/DataTable.js +21 -23
- package/dist/components/DataTable/DataTable.js.map +1 -1
- package/dist/components/Kanban/Kanban.cjs +2 -0
- package/dist/components/Kanban/Kanban.cjs.map +1 -0
- package/dist/components/Kanban/Kanban.js +102 -0
- package/dist/components/Kanban/Kanban.js.map +1 -0
- package/dist/components/Kanban/Kanban.module.cjs +2 -0
- package/dist/components/Kanban/Kanban.module.cjs.map +1 -0
- package/dist/components/Kanban/Kanban.module.js +19 -0
- package/dist/components/Kanban/Kanban.module.js.map +1 -0
- package/dist/components/Lightbox/Lightbox.cjs +2 -0
- package/dist/components/Lightbox/Lightbox.cjs.map +1 -0
- package/dist/components/Lightbox/Lightbox.js +148 -0
- package/dist/components/Lightbox/Lightbox.js.map +1 -0
- package/dist/components/Lightbox/Lightbox.module.cjs +2 -0
- package/dist/components/Lightbox/Lightbox.module.cjs.map +1 -0
- package/dist/components/Lightbox/Lightbox.module.js +21 -0
- package/dist/components/Lightbox/Lightbox.module.js.map +1 -0
- package/dist/components/NotificationCenter/NotificationCenter.cjs +2 -0
- package/dist/components/NotificationCenter/NotificationCenter.cjs.map +1 -0
- package/dist/components/NotificationCenter/NotificationCenter.js +83 -0
- package/dist/components/NotificationCenter/NotificationCenter.js.map +1 -0
- package/dist/components/NotificationCenter/NotificationCenter.module.cjs +2 -0
- package/dist/components/NotificationCenter/NotificationCenter.module.cjs.map +1 -0
- package/dist/components/NotificationCenter/NotificationCenter.module.js +21 -0
- package/dist/components/NotificationCenter/NotificationCenter.module.js.map +1 -0
- package/dist/components/NotificationCenter/use-notification-inbox.cjs +2 -0
- package/dist/components/NotificationCenter/use-notification-inbox.cjs.map +1 -0
- package/dist/components/NotificationCenter/use-notification-inbox.js +63 -0
- package/dist/components/NotificationCenter/use-notification-inbox.js.map +1 -0
- package/dist/components/RefreshIndicator/RefreshIndicator.cjs +1 -1
- package/dist/components/RefreshIndicator/RefreshIndicator.cjs.map +1 -1
- package/dist/components/RefreshIndicator/RefreshIndicator.js +17 -17
- package/dist/components/RefreshIndicator/RefreshIndicator.js.map +1 -1
- package/dist/components/SignaturePad/SignaturePad.cjs +2 -0
- package/dist/components/SignaturePad/SignaturePad.cjs.map +1 -0
- package/dist/components/SignaturePad/SignaturePad.js +115 -0
- package/dist/components/SignaturePad/SignaturePad.js.map +1 -0
- package/dist/components/SignaturePad/SignaturePad.module.cjs +2 -0
- package/dist/components/SignaturePad/SignaturePad.module.cjs.map +1 -0
- package/dist/components/SignaturePad/SignaturePad.module.js +11 -0
- package/dist/components/SignaturePad/SignaturePad.module.js.map +1 -0
- package/dist/components/Stepper/Stepper.cjs +1 -1
- package/dist/components/Stepper/Stepper.cjs.map +1 -1
- package/dist/components/Stepper/Stepper.js +43 -19
- package/dist/components/Stepper/Stepper.js.map +1 -1
- package/dist/components/Stepper/Stepper.module.cjs +1 -1
- package/dist/components/Stepper/Stepper.module.cjs.map +1 -1
- package/dist/components/Stepper/Stepper.module.js +6 -3
- package/dist/components/Stepper/Stepper.module.js.map +1 -1
- package/dist/components/Tooltip/Tooltip.cjs +1 -1
- package/dist/components/Tooltip/Tooltip.cjs.map +1 -1
- package/dist/components/Tooltip/Tooltip.js +26 -20
- package/dist/components/Tooltip/Tooltip.js.map +1 -1
- package/dist/components/TreeView/TreeView.cjs +2 -0
- package/dist/components/TreeView/TreeView.cjs.map +1 -0
- package/dist/components/TreeView/TreeView.js +124 -0
- package/dist/components/TreeView/TreeView.js.map +1 -0
- package/dist/components/TreeView/TreeView.module.cjs +2 -0
- package/dist/components/TreeView/TreeView.module.cjs.map +1 -0
- package/dist/components/TreeView/TreeView.module.js +17 -0
- package/dist/components/TreeView/TreeView.module.js.map +1 -0
- package/dist/components/VirtualTable/VirtualTable.cjs +2 -0
- package/dist/components/VirtualTable/VirtualTable.cjs.map +1 -0
- package/dist/components/VirtualTable/VirtualTable.js +116 -0
- package/dist/components/VirtualTable/VirtualTable.js.map +1 -0
- package/dist/components/VirtualTable/VirtualTable.module.cjs +2 -0
- package/dist/components/VirtualTable/VirtualTable.module.cjs.map +1 -0
- package/dist/components/VirtualTable/VirtualTable.module.js +20 -0
- package/dist/components/VirtualTable/VirtualTable.module.js.map +1 -0
- package/dist/components/Wizard/Wizard.cjs +2 -0
- package/dist/components/Wizard/Wizard.cjs.map +1 -0
- package/dist/components/Wizard/Wizard.js +111 -0
- package/dist/components/Wizard/Wizard.js.map +1 -0
- package/dist/components/Wizard/Wizard.module.cjs +2 -0
- package/dist/components/Wizard/Wizard.module.cjs.map +1 -0
- package/dist/components/Wizard/Wizard.module.js +10 -0
- package/dist/components/Wizard/Wizard.module.js.map +1 -0
- package/dist/geo/use-position-tracker.cjs +1 -1
- package/dist/geo/use-position-tracker.cjs.map +1 -1
- package/dist/geo/use-position-tracker.js +3 -1
- package/dist/geo/use-position-tracker.js.map +1 -1
- package/dist/hooks/use-async.cjs +1 -1
- package/dist/hooks/use-async.cjs.map +1 -1
- package/dist/hooks/use-async.js +3 -1
- package/dist/hooks/use-async.js.map +1 -1
- package/dist/hooks/use-click-outside.cjs +1 -1
- package/dist/hooks/use-click-outside.cjs.map +1 -1
- package/dist/hooks/use-click-outside.js +3 -1
- package/dist/hooks/use-click-outside.js.map +1 -1
- package/dist/hooks/use-deep-memo.cjs.map +1 -1
- package/dist/hooks/use-deep-memo.js.map +1 -1
- package/dist/hooks/use-event-listener.cjs +1 -1
- package/dist/hooks/use-event-listener.cjs.map +1 -1
- package/dist/hooks/use-event-listener.js +3 -1
- package/dist/hooks/use-event-listener.js.map +1 -1
- package/dist/hooks/use-interval.cjs +1 -1
- package/dist/hooks/use-interval.cjs.map +1 -1
- package/dist/hooks/use-interval.js +3 -1
- package/dist/hooks/use-interval.js.map +1 -1
- package/dist/hooks/use-is-first-render.cjs.map +1 -1
- package/dist/hooks/use-is-first-render.js.map +1 -1
- package/dist/hooks/use-long-press-handlers.cjs +1 -1
- package/dist/hooks/use-long-press-handlers.cjs.map +1 -1
- package/dist/hooks/use-long-press-handlers.js +25 -23
- package/dist/hooks/use-long-press-handlers.js.map +1 -1
- package/dist/hooks/use-long-press.cjs +1 -1
- package/dist/hooks/use-long-press.cjs.map +1 -1
- package/dist/hooks/use-long-press.js +3 -1
- package/dist/hooks/use-long-press.js.map +1 -1
- package/dist/hooks/use-online.cjs +1 -1
- package/dist/hooks/use-online.cjs.map +1 -1
- package/dist/hooks/use-online.js +6 -4
- package/dist/hooks/use-online.js.map +1 -1
- package/dist/hooks/use-previous.cjs.map +1 -1
- package/dist/hooks/use-previous.js.map +1 -1
- package/dist/hooks/use-service-worker-update.cjs +1 -1
- package/dist/hooks/use-service-worker-update.cjs.map +1 -1
- package/dist/hooks/use-service-worker-update.js +3 -1
- package/dist/hooks/use-service-worker-update.js.map +1 -1
- package/dist/hooks/use-sortable.cjs +2 -0
- package/dist/hooks/use-sortable.cjs.map +1 -0
- package/dist/hooks/use-sortable.js +169 -0
- package/dist/hooks/use-sortable.js.map +1 -0
- package/dist/hooks/use-stable-callback.cjs.map +1 -1
- package/dist/hooks/use-stable-callback.js.map +1 -1
- package/dist/hooks/use-timeout.cjs +1 -1
- package/dist/hooks/use-timeout.cjs.map +1 -1
- package/dist/hooks/use-timeout.js +3 -1
- package/dist/hooks/use-timeout.js.map +1 -1
- package/dist/icons/Icon.cjs +2 -0
- package/dist/icons/Icon.cjs.map +1 -0
- package/dist/icons/Icon.js +24 -0
- package/dist/icons/Icon.js.map +1 -0
- package/dist/icons/IconProvider.cjs +2 -0
- package/dist/icons/IconProvider.cjs.map +1 -0
- package/dist/icons/IconProvider.js +24 -0
- package/dist/icons/IconProvider.js.map +1 -0
- package/dist/icons/generated/aliases.cjs +2 -0
- package/dist/icons/generated/aliases.cjs.map +1 -0
- package/dist/icons/generated/aliases.js +255 -0
- package/dist/icons/generated/aliases.js.map +1 -0
- package/dist/icons/generated/icon-names.cjs +2 -0
- package/dist/icons/generated/icon-names.cjs.map +1 -0
- package/dist/icons/generated/icon-names.js +6 -0
- package/dist/icons/generated/icon-names.js.map +1 -0
- package/dist/icons/generated/loaders.cjs +2 -0
- package/dist/icons/generated/loaders.cjs.map +1 -0
- package/dist/icons/generated/loaders.js +32 -0
- package/dist/icons/generated/loaders.js.map +1 -0
- package/dist/icons/generated/shard-a.cjs +2 -0
- package/dist/icons/generated/shard-a.cjs.map +1 -0
- package/dist/icons/generated/shard-a.js +114 -0
- package/dist/icons/generated/shard-a.js.map +1 -0
- package/dist/icons/generated/shard-b.cjs +2 -0
- package/dist/icons/generated/shard-b.cjs.map +1 -0
- package/dist/icons/generated/shard-b.js +156 -0
- package/dist/icons/generated/shard-b.js.map +1 -0
- package/dist/icons/generated/shard-c.cjs +2 -0
- package/dist/icons/generated/shard-c.cjs.map +1 -0
- package/dist/icons/generated/shard-c.js +269 -0
- package/dist/icons/generated/shard-c.js.map +1 -0
- package/dist/icons/generated/shard-d.cjs +2 -0
- package/dist/icons/generated/shard-d.cjs.map +1 -0
- package/dist/icons/generated/shard-d.js +62 -0
- package/dist/icons/generated/shard-d.js.map +1 -0
- package/dist/icons/generated/shard-e.cjs +2 -0
- package/dist/icons/generated/shard-e.cjs.map +1 -0
- package/dist/icons/generated/shard-e.js +32 -0
- package/dist/icons/generated/shard-e.js.map +1 -0
- package/dist/icons/generated/shard-f.cjs +2 -0
- package/dist/icons/generated/shard-f.cjs.map +1 -0
- package/dist/icons/generated/shard-f.js +139 -0
- package/dist/icons/generated/shard-f.js.map +1 -0
- package/dist/icons/generated/shard-g.cjs +2 -0
- package/dist/icons/generated/shard-g.cjs.map +1 -0
- package/dist/icons/generated/shard-g.js +61 -0
- package/dist/icons/generated/shard-g.js.map +1 -0
- package/dist/icons/generated/shard-h.cjs +2 -0
- package/dist/icons/generated/shard-h.cjs.map +1 -0
- package/dist/icons/generated/shard-h.js +63 -0
- package/dist/icons/generated/shard-h.js.map +1 -0
- package/dist/icons/generated/shard-i.cjs +2 -0
- package/dist/icons/generated/shard-i.cjs.map +1 -0
- package/dist/icons/generated/shard-i.js +30 -0
- package/dist/icons/generated/shard-i.js.map +1 -0
- package/dist/icons/generated/shard-j.cjs +2 -0
- package/dist/icons/generated/shard-j.cjs.map +1 -0
- package/dist/icons/generated/shard-j.js +10 -0
- package/dist/icons/generated/shard-j.js.map +1 -0
- package/dist/icons/generated/shard-k.cjs +2 -0
- package/dist/icons/generated/shard-k.cjs.map +1 -0
- package/dist/icons/generated/shard-k.js +16 -0
- package/dist/icons/generated/shard-k.js.map +1 -0
- package/dist/icons/generated/shard-l.cjs +2 -0
- package/dist/icons/generated/shard-l.cjs.map +1 -0
- package/dist/icons/generated/shard-l.js +88 -0
- package/dist/icons/generated/shard-l.js.map +1 -0
- package/dist/icons/generated/shard-m.cjs +2 -0
- package/dist/icons/generated/shard-m.cjs.map +1 -0
- package/dist/icons/generated/shard-m.js +140 -0
- package/dist/icons/generated/shard-m.js.map +1 -0
- package/dist/icons/generated/shard-n.cjs +2 -0
- package/dist/icons/generated/shard-n.cjs.map +1 -0
- package/dist/icons/generated/shard-n.js +24 -0
- package/dist/icons/generated/shard-n.js.map +1 -0
- package/dist/icons/generated/shard-o.cjs +2 -0
- package/dist/icons/generated/shard-o.cjs.map +1 -0
- package/dist/icons/generated/shard-o.js +17 -0
- package/dist/icons/generated/shard-o.js.map +1 -0
- package/dist/icons/generated/shard-p.cjs +2 -0
- package/dist/icons/generated/shard-p.cjs.map +1 -0
- package/dist/icons/generated/shard-p.js +115 -0
- package/dist/icons/generated/shard-p.js.map +1 -0
- package/dist/icons/generated/shard-q.cjs +2 -0
- package/dist/icons/generated/shard-q.cjs.map +1 -0
- package/dist/icons/generated/shard-q.js +10 -0
- package/dist/icons/generated/shard-q.js.map +1 -0
- package/dist/icons/generated/shard-r.cjs +2 -0
- package/dist/icons/generated/shard-r.cjs.map +1 -0
- package/dist/icons/generated/shard-r.js +77 -0
- package/dist/icons/generated/shard-r.js.map +1 -0
- package/dist/icons/generated/shard-s.cjs +2 -0
- package/dist/icons/generated/shard-s.cjs.map +1 -0
- package/dist/icons/generated/shard-s.js +255 -0
- package/dist/icons/generated/shard-s.js.map +1 -0
- package/dist/icons/generated/shard-t.cjs +2 -0
- package/dist/icons/generated/shard-t.cjs.map +1 -0
- package/dist/icons/generated/shard-t.js +109 -0
- package/dist/icons/generated/shard-t.js.map +1 -0
- package/dist/icons/generated/shard-u.cjs +2 -0
- package/dist/icons/generated/shard-u.cjs.map +1 -0
- package/dist/icons/generated/shard-u.js +49 -0
- package/dist/icons/generated/shard-u.js.map +1 -0
- package/dist/icons/generated/shard-v.cjs +2 -0
- package/dist/icons/generated/shard-v.cjs.map +1 -0
- package/dist/icons/generated/shard-v.js +30 -0
- package/dist/icons/generated/shard-v.js.map +1 -0
- package/dist/icons/generated/shard-w.cjs +2 -0
- package/dist/icons/generated/shard-w.cjs.map +1 -0
- package/dist/icons/generated/shard-w.js +48 -0
- package/dist/icons/generated/shard-w.js.map +1 -0
- package/dist/icons/generated/shard-x.cjs +2 -0
- package/dist/icons/generated/shard-x.cjs.map +1 -0
- package/dist/icons/generated/shard-x.js +10 -0
- package/dist/icons/generated/shard-x.js.map +1 -0
- package/dist/icons/generated/shard-z.cjs +2 -0
- package/dist/icons/generated/shard-z.cjs.map +1 -0
- package/dist/icons/generated/shard-z.js +25 -0
- package/dist/icons/generated/shard-z.js.map +1 -0
- package/dist/icons/icon-context.cjs +2 -0
- package/dist/icons/icon-context.cjs.map +1 -0
- package/dist/icons/icon-context.js +13 -0
- package/dist/icons/icon-context.js.map +1 -0
- package/dist/icons/is-icon-name.cjs +2 -0
- package/dist/icons/is-icon-name.cjs.map +1 -0
- package/dist/icons/is-icon-name.js +10 -0
- package/dist/icons/is-icon-name.js.map +1 -0
- package/dist/icons/shard-cache.cjs +2 -0
- package/dist/icons/shard-cache.cjs.map +1 -0
- package/dist/icons/shard-cache.js +41 -0
- package/dist/icons/shard-cache.js.map +1 -0
- package/dist/icons/use-icon.cjs +2 -0
- package/dist/icons/use-icon.cjs.map +1 -0
- package/dist/icons/use-icon.js +20 -0
- package/dist/icons/use-icon.js.map +1 -0
- package/dist/icons-virtual.d.ts +22 -0
- package/dist/icons.cjs +1 -0
- package/dist/icons.d.ts +220 -0
- package/dist/icons.js +9 -0
- package/dist/offline/create-offline-sync.cjs +1 -1
- package/dist/offline/create-offline-sync.cjs.map +1 -1
- package/dist/offline/create-offline-sync.js +8 -2
- package/dist/offline/create-offline-sync.js.map +1 -1
- package/dist/offline/use-offline-sync.cjs +1 -1
- package/dist/offline/use-offline-sync.cjs.map +1 -1
- package/dist/offline/use-offline-sync.js +3 -1
- package/dist/offline/use-offline-sync.js.map +1 -1
- package/dist/push/use-push-subscription.cjs +1 -1
- package/dist/push/use-push-subscription.cjs.map +1 -1
- package/dist/push/use-push-subscription.js +3 -1
- package/dist/push/use-push-subscription.js.map +1 -1
- package/dist/sse/use-event-stream.cjs +1 -1
- package/dist/sse/use-event-stream.cjs.map +1 -1
- package/dist/sse/use-event-stream.js +3 -1
- package/dist/sse/use-event-stream.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/tempest-react-sdk.cjs +1 -1
- package/dist/tempest-react-sdk.d.ts +1187 -2
- package/dist/tempest-react-sdk.js +261 -246
- package/dist/theme/ThemeProvider.cjs +1 -1
- package/dist/theme/ThemeProvider.cjs.map +1 -1
- package/dist/theme/ThemeProvider.js +6 -2
- package/dist/theme/ThemeProvider.js.map +1 -1
- package/dist/theme/apply-theme.cjs +2 -0
- package/dist/theme/apply-theme.cjs.map +1 -0
- package/dist/theme/apply-theme.js +24 -0
- package/dist/theme/apply-theme.js.map +1 -0
- package/dist/theme/color.cjs +2 -0
- package/dist/theme/color.cjs.map +1 -0
- package/dist/theme/color.js +161 -0
- package/dist/theme/color.js.map +1 -0
- package/dist/theme/create-theme.cjs +5 -0
- package/dist/theme/create-theme.cjs.map +1 -0
- package/dist/theme/create-theme.js +141 -0
- package/dist/theme/create-theme.js.map +1 -0
- package/dist/theme/theme-presets.cjs +2 -0
- package/dist/theme/theme-presets.cjs.map +1 -0
- package/dist/theme/theme-presets.js +82 -0
- package/dist/theme/theme-presets.js.map +1 -0
- package/dist/utilities.css +432 -0
- package/dist/utils/compare-values.cjs +2 -0
- package/dist/utils/compare-values.cjs.map +1 -0
- package/dist/utils/compare-values.js +8 -0
- package/dist/utils/compare-values.js.map +1 -0
- package/dist/vision/use-camera-stream.cjs +1 -1
- package/dist/vision/use-camera-stream.cjs.map +1 -1
- package/dist/vision/use-camera-stream.js +3 -1
- package/dist/vision/use-camera-stream.js.map +1 -1
- package/dist/vite/create-vite-config.cjs +1 -1
- package/dist/vite/create-vite-config.cjs.map +1 -1
- package/dist/vite/create-vite-config.js +30 -22
- package/dist/vite/create-vite-config.js.map +1 -1
- package/dist/vite/tempest-icons.cjs +9 -0
- package/dist/vite/tempest-icons.cjs.map +1 -0
- package/dist/vite/tempest-icons.js +99 -0
- package/dist/vite/tempest-icons.js.map +1 -0
- package/dist/vite.cjs +1 -1
- package/dist/vite.d.ts +79 -0
- package/dist/vite.js +6 -5
- package/dist/ws/use-web-socket.cjs +1 -1
- package/dist/ws/use-web-socket.cjs.map +1 -1
- package/dist/ws/use-web-socket.js +3 -1
- package/dist/ws/use-web-socket.js.map +1 -1
- package/package.json +14 -4
- package/template/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
let e=require("react"),t=require("react/jsx-runtime");var n=(0,e.createContext)(null);function r(e){return e===`dark`||e===`light`?e:typeof window>`u`?`light`:window.matchMedia(`(prefers-color-scheme: dark)`).matches?`dark`:`light`}function i(e,t,n,r){let i=Array.isArray(n)?n:[n];for(let n of i)e.setAttribute(n,t);if(r&&typeof document<`u`){let e=document.querySelector(`meta[name="theme-color"]`);e&&(e.content=r[t])}}function a(e){if(!e||typeof window>`u`)return null;try{let t=window.localStorage.getItem(e);return t===`light`||t===`dark`||t===`system`?t:null}catch{return null}}function o({children:o,defaultTheme:s=`system`,storageKey:c=`tempest-theme`,target:l,attribute:u=`data-tempest-theme`,themeColor:d}){let[f,p]=(0,e.useState)(()=>a(c)??s),[m,h]=(0,e.useState)(()=>r(f)),g=(0,e.useRef)(l);g.current=l;let _=Array.isArray(u)?u.join(`,`):u,v=(0,e.useRef)(d);v.current=d,(0,e.useEffect)(()=>{let e=g.current?.()??document.documentElement;if(!e)return;let t=r(f);i(e,t,u,v.current),h(t)},[f,_]),(0,e.useEffect)(()=>{if(f!==`system`||typeof window>`u`)return;let e=window.matchMedia(`(prefers-color-scheme: dark)`),t=()=>{let t=g.current?.()??document.documentElement;if(!t)return;let n=e.matches?`dark`:`light`;i(t,n,u,v.current),h(n)};return e.addEventListener(`change`,t),()=>e.removeEventListener(`change`,t)},[f,_]);let y=(0,e.useCallback)(e=>{if(p(e),c&&typeof window<`u`)try{window.localStorage.setItem(c,e)}catch{}},[c]),b=(0,e.useCallback)(()=>{y(m===`dark`?`light`:`dark`)},[m,y]),x=(0,e.useMemo)(()=>({theme:f,resolvedTheme:m,setTheme:y,toggle:b}),[f,m,y,b]);return(0,t.jsx)(n.Provider,{value:x,children:o})}function s(){let t=(0,e.useContext)(n);if(!t)throw Error(`useTheme must be used inside a <ThemeProvider>`);return t}exports.ThemeProvider=o,exports.useTheme=s;
|
|
1
|
+
let e=require("react"),t=require("react/jsx-runtime");var n=(0,e.createContext)(null);function r(e){return e===`dark`||e===`light`?e:typeof window>`u`?`light`:window.matchMedia(`(prefers-color-scheme: dark)`).matches?`dark`:`light`}function i(e,t,n,r){let i=Array.isArray(n)?n:[n];for(let n of i)e.setAttribute(n,t);if(r&&typeof document<`u`){let e=document.querySelector(`meta[name="theme-color"]`);e&&(e.content=r[t])}}function a(e){if(!e||typeof window>`u`)return null;try{let t=window.localStorage.getItem(e);return t===`light`||t===`dark`||t===`system`?t:null}catch{return null}}function o({children:o,defaultTheme:s=`system`,storageKey:c=`tempest-theme`,target:l,attribute:u=`data-tempest-theme`,themeColor:d}){let[f,p]=(0,e.useState)(()=>a(c)??s),[m,h]=(0,e.useState)(()=>r(f)),g=(0,e.useRef)(l);(0,e.useEffect)(()=>{g.current=l},[l]);let _=Array.isArray(u)?u.join(`,`):u,v=(0,e.useRef)(d);(0,e.useEffect)(()=>{v.current=d},[d]),(0,e.useEffect)(()=>{let e=g.current?.()??document.documentElement;if(!e)return;let t=r(f);i(e,t,u,v.current),h(t)},[f,_]),(0,e.useEffect)(()=>{if(f!==`system`||typeof window>`u`)return;let e=window.matchMedia(`(prefers-color-scheme: dark)`),t=()=>{let t=g.current?.()??document.documentElement;if(!t)return;let n=e.matches?`dark`:`light`;i(t,n,u,v.current),h(n)};return e.addEventListener(`change`,t),()=>e.removeEventListener(`change`,t)},[f,_]);let y=(0,e.useCallback)(e=>{if(p(e),c&&typeof window<`u`)try{window.localStorage.setItem(c,e)}catch{}},[c]),b=(0,e.useCallback)(()=>{y(m===`dark`?`light`:`dark`)},[m,y]),x=(0,e.useMemo)(()=>({theme:f,resolvedTheme:m,setTheme:y,toggle:b}),[f,m,y,b]);return(0,t.jsx)(n.Provider,{value:x,children:o})}function s(){let t=(0,e.useContext)(n);if(!t)throw Error(`useTheme must be used inside a <ThemeProvider>`);return t}exports.ThemeProvider=o,exports.useTheme=s;
|
|
2
2
|
//# sourceMappingURL=ThemeProvider.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemeProvider.cjs","names":[],"sources":["../../src/theme/ThemeProvider.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { ResolvedTheme, ThemeMode } from \"./types\";\n\nexport interface ThemeContextValue {\n /** Raw user preference (light / dark / system). */\n theme: ThemeMode;\n /** Effective theme actually applied to the DOM (light or dark). */\n resolvedTheme: ResolvedTheme;\n /** Update the preference. Persisted to localStorage when `storageKey` is set. */\n setTheme: (next: ThemeMode) => void;\n /** Convenience: flip light ↔ dark. When in `system` mode, switches to the opposite of the current resolved theme. */\n toggle: () => void;\n}\n\nconst ThemeContext = createContext<ThemeContextValue | null>(null);\n\nexport interface ThemeProviderProps {\n children: ReactNode;\n /** Initial preference when nothing is stored. Default: `\"system\"`. */\n defaultTheme?: ThemeMode;\n /** localStorage key used to persist the preference. Pass `null` to disable persistence. Default: `\"tempest-theme\"`. */\n storageKey?: string | null;\n /**\n * Element that receives the theme attribute(s). Defaults to\n * `document.documentElement`. Override when scoping the theme to a subtree.\n */\n target?: () => HTMLElement | null;\n /**\n * Attribute name(s) written on the target with the resolved theme\n * (`\"light\"` / `\"dark\"`). Default: `\"data-tempest-theme\"`.\n *\n * Pass an array to mirror the theme onto more than one attribute — handy\n * when the SDK components read `data-tempest-theme` but the host app's own\n * CSS keys off a different attribute (e.g. `[\"data-tempest-theme\",\n * \"data-theme\"]`). Avoids a separate sync effect in the consumer.\n */\n attribute?: string | string[];\n /**\n * When set, keeps `<meta name=\"theme-color\">` in sync with the resolved\n * theme — `content` becomes `themeColor.dark` in dark mode and\n * `themeColor.light` in light mode. The meta tag must already exist in the\n * document `<head>`. No-op when omitted.\n */\n themeColor?: { light: string; dark: string };\n}\n\nfunction resolve(mode: ThemeMode): ResolvedTheme {\n if (mode === \"dark\" || mode === \"light\") return mode;\n if (typeof window === \"undefined\") return \"light\";\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches ? \"dark\" : \"light\";\n}\n\n/**\n * Write the resolved theme onto every configured attribute and, when a\n * `themeColor` map is provided, sync the `<meta name=\"theme-color\">` tag.\n */\nfunction applyResolved(\n element: HTMLElement,\n resolved: ResolvedTheme,\n attribute: string | string[],\n themeColor?: { light: string; dark: string },\n): void {\n const attrs = Array.isArray(attribute) ? attribute : [attribute];\n for (const attr of attrs) element.setAttribute(attr, resolved);\n if (themeColor && typeof document !== \"undefined\") {\n const meta = document.querySelector<HTMLMetaElement>('meta[name=\"theme-color\"]');\n if (meta) meta.content = themeColor[resolved];\n }\n}\n\nfunction readStored(storageKey: string | null): ThemeMode | null {\n if (!storageKey || typeof window === \"undefined\") return null;\n try {\n const value = window.localStorage.getItem(storageKey);\n if (value === \"light\" || value === \"dark\" || value === \"system\") return value;\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Wire dark/light theming. Writes a data attribute on a target element (the\n * `<html>` element by default) and exposes the current preference via\n * {@link useTheme}.\n *\n * Pair with `themeInitScript()` in the HTML head to prevent the flash of\n * incorrect theme on first paint.\n */\nexport function ThemeProvider({\n children,\n defaultTheme = \"system\",\n storageKey = \"tempest-theme\",\n target,\n attribute = \"data-tempest-theme\",\n themeColor,\n}: ThemeProviderProps) {\n const [theme, setThemeState] = useState<ThemeMode>(\n () => readStored(storageKey) ?? defaultTheme,\n );\n const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>(() => resolve(theme));\n\n const targetRef = useRef<typeof target>(target);\n targetRef.current = target;\n\n const attributeKey = Array.isArray(attribute) ? attribute.join(\",\") : attribute;\n const themeColorRef = useRef(themeColor);\n themeColorRef.current = themeColor;\n\n useEffect(() => {\n const element = targetRef.current?.() ?? document.documentElement;\n if (!element) return;\n const next = resolve(theme);\n applyResolved(element, next, attribute, themeColorRef.current);\n setResolvedTheme(next);\n // attributeKey is the stable string form of `attribute`.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [theme, attributeKey]);\n\n useEffect(() => {\n if (theme !== \"system\" || typeof window === \"undefined\") return;\n const list = window.matchMedia(\"(prefers-color-scheme: dark)\");\n const handler = (): void => {\n const element = targetRef.current?.() ?? document.documentElement;\n if (!element) return;\n const next: ResolvedTheme = list.matches ? \"dark\" : \"light\";\n applyResolved(element, next, attribute, themeColorRef.current);\n setResolvedTheme(next);\n };\n list.addEventListener(\"change\", handler);\n return () => list.removeEventListener(\"change\", handler);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [theme, attributeKey]);\n\n const setTheme = useCallback(\n (next: ThemeMode) => {\n setThemeState(next);\n if (storageKey && typeof window !== \"undefined\") {\n try {\n window.localStorage.setItem(storageKey, next);\n } catch {\n /* ignore quota errors */\n }\n }\n },\n [storageKey],\n );\n\n const toggle = useCallback(() => {\n setTheme(resolvedTheme === \"dark\" ? \"light\" : \"dark\");\n }, [resolvedTheme, setTheme]);\n\n const value = useMemo<ThemeContextValue>(\n () => ({ theme, resolvedTheme, setTheme, toggle }),\n [theme, resolvedTheme, setTheme, toggle],\n );\n\n return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;\n}\n\n/**\n * Read and mutate the current theme. Must be used inside a {@link ThemeProvider}.\n */\nexport function useTheme(): ThemeContextValue {\n const ctx = useContext(ThemeContext);\n if (!ctx) throw new Error(\"useTheme must be used inside a <ThemeProvider>\");\n return ctx;\n}\n"],"mappings":"sDAuBA,IAAM,GAAA,EAAA,EAAA,cAAA,CAAuD,IAAI,EAgCjE,SAAS,EAAQ,EAAgC,CAG7C,OAFI,IAAS,QAAU,IAAS,QAAgB,EAC5C,OAAO,OAAW,IAAoB,QACnC,OAAO,WAAW,8BAA8B,CAAC,CAAC,QAAU,OAAS,OAChF,CAMA,SAAS,EACL,EACA,EACA,EACA,EACI,CACJ,IAAM,EAAQ,MAAM,QAAQ,CAAS,EAAI,EAAY,CAAC,CAAS,EAC/D,IAAK,IAAM,KAAQ,EAAO,EAAQ,aAAa,EAAM,CAAQ,EAC7D,GAAI,GAAc,OAAO,SAAa,IAAa,CAC/C,IAAM,EAAO,SAAS,cAA+B,0BAA0B,EAC3E,IAAM,EAAK,QAAU,EAAW,GACxC,CACJ,CAEA,SAAS,EAAW,EAA6C,CAC7D,GAAI,CAAC,GAAc,OAAO,OAAW,IAAa,OAAO,KACzD,GAAI,CACA,IAAM,EAAQ,OAAO,aAAa,QAAQ,CAAU,EAEpD,OADI,IAAU,SAAW,IAAU,QAAU,IAAU,SAAiB,EACjE,IACX,MAAQ,CACJ,OAAO,IACX,CACJ,CAUA,SAAgB,EAAc,CAC1B,WACA,eAAe,SACf,aAAa,gBACb,SACA,YAAY,qBACZ,cACmB,CACnB,GAAM,CAAC,EAAO,IAAA,EAAA,EAAA,SAAA,KACJ,EAAW,CAAU,GAAK,CACpC,EACM,CAAC,EAAe,IAAA,EAAA,EAAA,SAAA,KAAkD,EAAQ,CAAK,CAAC,EAEhF,GAAA,EAAA,EAAA,OAAA,CAAkC,CAAM,
|
|
1
|
+
{"version":3,"file":"ThemeProvider.cjs","names":[],"sources":["../../src/theme/ThemeProvider.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { ResolvedTheme, ThemeMode } from \"./types\";\n\nexport interface ThemeContextValue {\n /** Raw user preference (light / dark / system). */\n theme: ThemeMode;\n /** Effective theme actually applied to the DOM (light or dark). */\n resolvedTheme: ResolvedTheme;\n /** Update the preference. Persisted to localStorage when `storageKey` is set. */\n setTheme: (next: ThemeMode) => void;\n /** Convenience: flip light ↔ dark. When in `system` mode, switches to the opposite of the current resolved theme. */\n toggle: () => void;\n}\n\nconst ThemeContext = createContext<ThemeContextValue | null>(null);\n\nexport interface ThemeProviderProps {\n children: ReactNode;\n /** Initial preference when nothing is stored. Default: `\"system\"`. */\n defaultTheme?: ThemeMode;\n /** localStorage key used to persist the preference. Pass `null` to disable persistence. Default: `\"tempest-theme\"`. */\n storageKey?: string | null;\n /**\n * Element that receives the theme attribute(s). Defaults to\n * `document.documentElement`. Override when scoping the theme to a subtree.\n */\n target?: () => HTMLElement | null;\n /**\n * Attribute name(s) written on the target with the resolved theme\n * (`\"light\"` / `\"dark\"`). Default: `\"data-tempest-theme\"`.\n *\n * Pass an array to mirror the theme onto more than one attribute — handy\n * when the SDK components read `data-tempest-theme` but the host app's own\n * CSS keys off a different attribute (e.g. `[\"data-tempest-theme\",\n * \"data-theme\"]`). Avoids a separate sync effect in the consumer.\n */\n attribute?: string | string[];\n /**\n * When set, keeps `<meta name=\"theme-color\">` in sync with the resolved\n * theme — `content` becomes `themeColor.dark` in dark mode and\n * `themeColor.light` in light mode. The meta tag must already exist in the\n * document `<head>`. No-op when omitted.\n */\n themeColor?: { light: string; dark: string };\n}\n\nfunction resolve(mode: ThemeMode): ResolvedTheme {\n if (mode === \"dark\" || mode === \"light\") return mode;\n if (typeof window === \"undefined\") return \"light\";\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches ? \"dark\" : \"light\";\n}\n\n/**\n * Write the resolved theme onto every configured attribute and, when a\n * `themeColor` map is provided, sync the `<meta name=\"theme-color\">` tag.\n */\nfunction applyResolved(\n element: HTMLElement,\n resolved: ResolvedTheme,\n attribute: string | string[],\n themeColor?: { light: string; dark: string },\n): void {\n const attrs = Array.isArray(attribute) ? attribute : [attribute];\n for (const attr of attrs) element.setAttribute(attr, resolved);\n if (themeColor && typeof document !== \"undefined\") {\n const meta = document.querySelector<HTMLMetaElement>('meta[name=\"theme-color\"]');\n if (meta) meta.content = themeColor[resolved];\n }\n}\n\nfunction readStored(storageKey: string | null): ThemeMode | null {\n if (!storageKey || typeof window === \"undefined\") return null;\n try {\n const value = window.localStorage.getItem(storageKey);\n if (value === \"light\" || value === \"dark\" || value === \"system\") return value;\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Wire dark/light theming. Writes a data attribute on a target element (the\n * `<html>` element by default) and exposes the current preference via\n * {@link useTheme}.\n *\n * Pair with `themeInitScript()` in the HTML head to prevent the flash of\n * incorrect theme on first paint.\n */\nexport function ThemeProvider({\n children,\n defaultTheme = \"system\",\n storageKey = \"tempest-theme\",\n target,\n attribute = \"data-tempest-theme\",\n themeColor,\n}: ThemeProviderProps) {\n const [theme, setThemeState] = useState<ThemeMode>(\n () => readStored(storageKey) ?? defaultTheme,\n );\n const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>(() => resolve(theme));\n\n const targetRef = useRef<typeof target>(target);\n useEffect(() => {\n targetRef.current = target;\n }, [target]);\n\n const attributeKey = Array.isArray(attribute) ? attribute.join(\",\") : attribute;\n const themeColorRef = useRef(themeColor);\n useEffect(() => {\n themeColorRef.current = themeColor;\n }, [themeColor]);\n\n useEffect(() => {\n const element = targetRef.current?.() ?? document.documentElement;\n if (!element) return;\n const next = resolve(theme);\n applyResolved(element, next, attribute, themeColorRef.current);\n setResolvedTheme(next);\n // attributeKey is the stable string form of `attribute`.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [theme, attributeKey]);\n\n useEffect(() => {\n if (theme !== \"system\" || typeof window === \"undefined\") return;\n const list = window.matchMedia(\"(prefers-color-scheme: dark)\");\n const handler = (): void => {\n const element = targetRef.current?.() ?? document.documentElement;\n if (!element) return;\n const next: ResolvedTheme = list.matches ? \"dark\" : \"light\";\n applyResolved(element, next, attribute, themeColorRef.current);\n setResolvedTheme(next);\n };\n list.addEventListener(\"change\", handler);\n return () => list.removeEventListener(\"change\", handler);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [theme, attributeKey]);\n\n const setTheme = useCallback(\n (next: ThemeMode) => {\n setThemeState(next);\n if (storageKey && typeof window !== \"undefined\") {\n try {\n window.localStorage.setItem(storageKey, next);\n } catch {\n /* ignore quota errors */\n }\n }\n },\n [storageKey],\n );\n\n const toggle = useCallback(() => {\n setTheme(resolvedTheme === \"dark\" ? \"light\" : \"dark\");\n }, [resolvedTheme, setTheme]);\n\n const value = useMemo<ThemeContextValue>(\n () => ({ theme, resolvedTheme, setTheme, toggle }),\n [theme, resolvedTheme, setTheme, toggle],\n );\n\n return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;\n}\n\n/**\n * Read and mutate the current theme. Must be used inside a {@link ThemeProvider}.\n */\nexport function useTheme(): ThemeContextValue {\n const ctx = useContext(ThemeContext);\n if (!ctx) throw new Error(\"useTheme must be used inside a <ThemeProvider>\");\n return ctx;\n}\n"],"mappings":"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"}
|
|
@@ -24,9 +24,13 @@ function d(e) {
|
|
|
24
24
|
}
|
|
25
25
|
function f({ children: e, defaultTheme: n = "system", storageKey: f = "tempest-theme", target: p, attribute: m = "data-tempest-theme", themeColor: h }) {
|
|
26
26
|
let [g, _] = o(() => d(f) ?? n), [v, y] = o(() => l(g)), b = a(p);
|
|
27
|
-
|
|
27
|
+
r(() => {
|
|
28
|
+
b.current = p;
|
|
29
|
+
}, [p]);
|
|
28
30
|
let x = Array.isArray(m) ? m.join(",") : m, S = a(h);
|
|
29
|
-
|
|
31
|
+
r(() => {
|
|
32
|
+
S.current = h;
|
|
33
|
+
}, [h]), r(() => {
|
|
30
34
|
let e = b.current?.() ?? document.documentElement;
|
|
31
35
|
if (!e) return;
|
|
32
36
|
let t = l(g);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemeProvider.js","names":[],"sources":["../../src/theme/ThemeProvider.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { ResolvedTheme, ThemeMode } from \"./types\";\n\nexport interface ThemeContextValue {\n /** Raw user preference (light / dark / system). */\n theme: ThemeMode;\n /** Effective theme actually applied to the DOM (light or dark). */\n resolvedTheme: ResolvedTheme;\n /** Update the preference. Persisted to localStorage when `storageKey` is set. */\n setTheme: (next: ThemeMode) => void;\n /** Convenience: flip light ↔ dark. When in `system` mode, switches to the opposite of the current resolved theme. */\n toggle: () => void;\n}\n\nconst ThemeContext = createContext<ThemeContextValue | null>(null);\n\nexport interface ThemeProviderProps {\n children: ReactNode;\n /** Initial preference when nothing is stored. Default: `\"system\"`. */\n defaultTheme?: ThemeMode;\n /** localStorage key used to persist the preference. Pass `null` to disable persistence. Default: `\"tempest-theme\"`. */\n storageKey?: string | null;\n /**\n * Element that receives the theme attribute(s). Defaults to\n * `document.documentElement`. Override when scoping the theme to a subtree.\n */\n target?: () => HTMLElement | null;\n /**\n * Attribute name(s) written on the target with the resolved theme\n * (`\"light\"` / `\"dark\"`). Default: `\"data-tempest-theme\"`.\n *\n * Pass an array to mirror the theme onto more than one attribute — handy\n * when the SDK components read `data-tempest-theme` but the host app's own\n * CSS keys off a different attribute (e.g. `[\"data-tempest-theme\",\n * \"data-theme\"]`). Avoids a separate sync effect in the consumer.\n */\n attribute?: string | string[];\n /**\n * When set, keeps `<meta name=\"theme-color\">` in sync with the resolved\n * theme — `content` becomes `themeColor.dark` in dark mode and\n * `themeColor.light` in light mode. The meta tag must already exist in the\n * document `<head>`. No-op when omitted.\n */\n themeColor?: { light: string; dark: string };\n}\n\nfunction resolve(mode: ThemeMode): ResolvedTheme {\n if (mode === \"dark\" || mode === \"light\") return mode;\n if (typeof window === \"undefined\") return \"light\";\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches ? \"dark\" : \"light\";\n}\n\n/**\n * Write the resolved theme onto every configured attribute and, when a\n * `themeColor` map is provided, sync the `<meta name=\"theme-color\">` tag.\n */\nfunction applyResolved(\n element: HTMLElement,\n resolved: ResolvedTheme,\n attribute: string | string[],\n themeColor?: { light: string; dark: string },\n): void {\n const attrs = Array.isArray(attribute) ? attribute : [attribute];\n for (const attr of attrs) element.setAttribute(attr, resolved);\n if (themeColor && typeof document !== \"undefined\") {\n const meta = document.querySelector<HTMLMetaElement>('meta[name=\"theme-color\"]');\n if (meta) meta.content = themeColor[resolved];\n }\n}\n\nfunction readStored(storageKey: string | null): ThemeMode | null {\n if (!storageKey || typeof window === \"undefined\") return null;\n try {\n const value = window.localStorage.getItem(storageKey);\n if (value === \"light\" || value === \"dark\" || value === \"system\") return value;\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Wire dark/light theming. Writes a data attribute on a target element (the\n * `<html>` element by default) and exposes the current preference via\n * {@link useTheme}.\n *\n * Pair with `themeInitScript()` in the HTML head to prevent the flash of\n * incorrect theme on first paint.\n */\nexport function ThemeProvider({\n children,\n defaultTheme = \"system\",\n storageKey = \"tempest-theme\",\n target,\n attribute = \"data-tempest-theme\",\n themeColor,\n}: ThemeProviderProps) {\n const [theme, setThemeState] = useState<ThemeMode>(\n () => readStored(storageKey) ?? defaultTheme,\n );\n const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>(() => resolve(theme));\n\n const targetRef = useRef<typeof target>(target);\n targetRef.current = target;\n\n const attributeKey = Array.isArray(attribute) ? attribute.join(\",\") : attribute;\n const themeColorRef = useRef(themeColor);\n themeColorRef.current = themeColor;\n\n useEffect(() => {\n const element = targetRef.current?.() ?? document.documentElement;\n if (!element) return;\n const next = resolve(theme);\n applyResolved(element, next, attribute, themeColorRef.current);\n setResolvedTheme(next);\n // attributeKey is the stable string form of `attribute`.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [theme, attributeKey]);\n\n useEffect(() => {\n if (theme !== \"system\" || typeof window === \"undefined\") return;\n const list = window.matchMedia(\"(prefers-color-scheme: dark)\");\n const handler = (): void => {\n const element = targetRef.current?.() ?? document.documentElement;\n if (!element) return;\n const next: ResolvedTheme = list.matches ? \"dark\" : \"light\";\n applyResolved(element, next, attribute, themeColorRef.current);\n setResolvedTheme(next);\n };\n list.addEventListener(\"change\", handler);\n return () => list.removeEventListener(\"change\", handler);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [theme, attributeKey]);\n\n const setTheme = useCallback(\n (next: ThemeMode) => {\n setThemeState(next);\n if (storageKey && typeof window !== \"undefined\") {\n try {\n window.localStorage.setItem(storageKey, next);\n } catch {\n /* ignore quota errors */\n }\n }\n },\n [storageKey],\n );\n\n const toggle = useCallback(() => {\n setTheme(resolvedTheme === \"dark\" ? \"light\" : \"dark\");\n }, [resolvedTheme, setTheme]);\n\n const value = useMemo<ThemeContextValue>(\n () => ({ theme, resolvedTheme, setTheme, toggle }),\n [theme, resolvedTheme, setTheme, toggle],\n );\n\n return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;\n}\n\n/**\n * Read and mutate the current theme. Must be used inside a {@link ThemeProvider}.\n */\nexport function useTheme(): ThemeContextValue {\n const ctx = useContext(ThemeContext);\n if (!ctx) throw new Error(\"useTheme must be used inside a <ThemeProvider>\");\n return ctx;\n}\n"],"mappings":";;;AAuBA,IAAM,IAAe,EAAwC,IAAI;AAgCjE,SAAS,EAAQ,GAAgC;CAG7C,OAFI,MAAS,UAAU,MAAS,UAAgB,IAC5C,OAAO,SAAW,MAAoB,UACnC,OAAO,WAAW,8BAA8B,CAAC,CAAC,UAAU,SAAS;AAChF;AAMA,SAAS,EACL,GACA,GACA,GACA,GACI;CACJ,IAAM,IAAQ,MAAM,QAAQ,CAAS,IAAI,IAAY,CAAC,CAAS;CAC/D,KAAK,IAAM,KAAQ,GAAO,EAAQ,aAAa,GAAM,CAAQ;CAC7D,IAAI,KAAc,OAAO,WAAa,KAAa;EAC/C,IAAM,IAAO,SAAS,cAA+B,4BAA0B;EAC/E,AAAI,MAAM,EAAK,UAAU,EAAW;CACxC;AACJ;AAEA,SAAS,EAAW,GAA6C;CAC7D,IAAI,CAAC,KAAc,OAAO,SAAW,KAAa,OAAO;CACzD,IAAI;EACA,IAAM,IAAQ,OAAO,aAAa,QAAQ,CAAU;EAEpD,OADI,MAAU,WAAW,MAAU,UAAU,MAAU,WAAiB,IACjE;CACX,QAAQ;EACJ,OAAO;CACX;AACJ;AAUA,SAAgB,EAAc,EAC1B,aACA,kBAAe,UACf,gBAAa,iBACb,WACA,eAAY,sBACZ,iBACmB;CACnB,IAAM,CAAC,GAAO,KAAiB,QACrB,EAAW,CAAU,KAAK,CACpC,GACM,CAAC,GAAe,KAAoB,QAA8B,EAAQ,CAAK,CAAC,GAEhF,IAAY,EAAsB,CAAM;CAC9C,EAAU,UAAU;
|
|
1
|
+
{"version":3,"file":"ThemeProvider.js","names":[],"sources":["../../src/theme/ThemeProvider.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type { ReactNode } from \"react\";\nimport type { ResolvedTheme, ThemeMode } from \"./types\";\n\nexport interface ThemeContextValue {\n /** Raw user preference (light / dark / system). */\n theme: ThemeMode;\n /** Effective theme actually applied to the DOM (light or dark). */\n resolvedTheme: ResolvedTheme;\n /** Update the preference. Persisted to localStorage when `storageKey` is set. */\n setTheme: (next: ThemeMode) => void;\n /** Convenience: flip light ↔ dark. When in `system` mode, switches to the opposite of the current resolved theme. */\n toggle: () => void;\n}\n\nconst ThemeContext = createContext<ThemeContextValue | null>(null);\n\nexport interface ThemeProviderProps {\n children: ReactNode;\n /** Initial preference when nothing is stored. Default: `\"system\"`. */\n defaultTheme?: ThemeMode;\n /** localStorage key used to persist the preference. Pass `null` to disable persistence. Default: `\"tempest-theme\"`. */\n storageKey?: string | null;\n /**\n * Element that receives the theme attribute(s). Defaults to\n * `document.documentElement`. Override when scoping the theme to a subtree.\n */\n target?: () => HTMLElement | null;\n /**\n * Attribute name(s) written on the target with the resolved theme\n * (`\"light\"` / `\"dark\"`). Default: `\"data-tempest-theme\"`.\n *\n * Pass an array to mirror the theme onto more than one attribute — handy\n * when the SDK components read `data-tempest-theme` but the host app's own\n * CSS keys off a different attribute (e.g. `[\"data-tempest-theme\",\n * \"data-theme\"]`). Avoids a separate sync effect in the consumer.\n */\n attribute?: string | string[];\n /**\n * When set, keeps `<meta name=\"theme-color\">` in sync with the resolved\n * theme — `content` becomes `themeColor.dark` in dark mode and\n * `themeColor.light` in light mode. The meta tag must already exist in the\n * document `<head>`. No-op when omitted.\n */\n themeColor?: { light: string; dark: string };\n}\n\nfunction resolve(mode: ThemeMode): ResolvedTheme {\n if (mode === \"dark\" || mode === \"light\") return mode;\n if (typeof window === \"undefined\") return \"light\";\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches ? \"dark\" : \"light\";\n}\n\n/**\n * Write the resolved theme onto every configured attribute and, when a\n * `themeColor` map is provided, sync the `<meta name=\"theme-color\">` tag.\n */\nfunction applyResolved(\n element: HTMLElement,\n resolved: ResolvedTheme,\n attribute: string | string[],\n themeColor?: { light: string; dark: string },\n): void {\n const attrs = Array.isArray(attribute) ? attribute : [attribute];\n for (const attr of attrs) element.setAttribute(attr, resolved);\n if (themeColor && typeof document !== \"undefined\") {\n const meta = document.querySelector<HTMLMetaElement>('meta[name=\"theme-color\"]');\n if (meta) meta.content = themeColor[resolved];\n }\n}\n\nfunction readStored(storageKey: string | null): ThemeMode | null {\n if (!storageKey || typeof window === \"undefined\") return null;\n try {\n const value = window.localStorage.getItem(storageKey);\n if (value === \"light\" || value === \"dark\" || value === \"system\") return value;\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Wire dark/light theming. Writes a data attribute on a target element (the\n * `<html>` element by default) and exposes the current preference via\n * {@link useTheme}.\n *\n * Pair with `themeInitScript()` in the HTML head to prevent the flash of\n * incorrect theme on first paint.\n */\nexport function ThemeProvider({\n children,\n defaultTheme = \"system\",\n storageKey = \"tempest-theme\",\n target,\n attribute = \"data-tempest-theme\",\n themeColor,\n}: ThemeProviderProps) {\n const [theme, setThemeState] = useState<ThemeMode>(\n () => readStored(storageKey) ?? defaultTheme,\n );\n const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>(() => resolve(theme));\n\n const targetRef = useRef<typeof target>(target);\n useEffect(() => {\n targetRef.current = target;\n }, [target]);\n\n const attributeKey = Array.isArray(attribute) ? attribute.join(\",\") : attribute;\n const themeColorRef = useRef(themeColor);\n useEffect(() => {\n themeColorRef.current = themeColor;\n }, [themeColor]);\n\n useEffect(() => {\n const element = targetRef.current?.() ?? document.documentElement;\n if (!element) return;\n const next = resolve(theme);\n applyResolved(element, next, attribute, themeColorRef.current);\n setResolvedTheme(next);\n // attributeKey is the stable string form of `attribute`.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [theme, attributeKey]);\n\n useEffect(() => {\n if (theme !== \"system\" || typeof window === \"undefined\") return;\n const list = window.matchMedia(\"(prefers-color-scheme: dark)\");\n const handler = (): void => {\n const element = targetRef.current?.() ?? document.documentElement;\n if (!element) return;\n const next: ResolvedTheme = list.matches ? \"dark\" : \"light\";\n applyResolved(element, next, attribute, themeColorRef.current);\n setResolvedTheme(next);\n };\n list.addEventListener(\"change\", handler);\n return () => list.removeEventListener(\"change\", handler);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [theme, attributeKey]);\n\n const setTheme = useCallback(\n (next: ThemeMode) => {\n setThemeState(next);\n if (storageKey && typeof window !== \"undefined\") {\n try {\n window.localStorage.setItem(storageKey, next);\n } catch {\n /* ignore quota errors */\n }\n }\n },\n [storageKey],\n );\n\n const toggle = useCallback(() => {\n setTheme(resolvedTheme === \"dark\" ? \"light\" : \"dark\");\n }, [resolvedTheme, setTheme]);\n\n const value = useMemo<ThemeContextValue>(\n () => ({ theme, resolvedTheme, setTheme, toggle }),\n [theme, resolvedTheme, setTheme, toggle],\n );\n\n return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;\n}\n\n/**\n * Read and mutate the current theme. Must be used inside a {@link ThemeProvider}.\n */\nexport function useTheme(): ThemeContextValue {\n const ctx = useContext(ThemeContext);\n if (!ctx) throw new Error(\"useTheme must be used inside a <ThemeProvider>\");\n return ctx;\n}\n"],"mappings":";;;AAuBA,IAAM,IAAe,EAAwC,IAAI;AAgCjE,SAAS,EAAQ,GAAgC;CAG7C,OAFI,MAAS,UAAU,MAAS,UAAgB,IAC5C,OAAO,SAAW,MAAoB,UACnC,OAAO,WAAW,8BAA8B,CAAC,CAAC,UAAU,SAAS;AAChF;AAMA,SAAS,EACL,GACA,GACA,GACA,GACI;CACJ,IAAM,IAAQ,MAAM,QAAQ,CAAS,IAAI,IAAY,CAAC,CAAS;CAC/D,KAAK,IAAM,KAAQ,GAAO,EAAQ,aAAa,GAAM,CAAQ;CAC7D,IAAI,KAAc,OAAO,WAAa,KAAa;EAC/C,IAAM,IAAO,SAAS,cAA+B,4BAA0B;EAC/E,AAAI,MAAM,EAAK,UAAU,EAAW;CACxC;AACJ;AAEA,SAAS,EAAW,GAA6C;CAC7D,IAAI,CAAC,KAAc,OAAO,SAAW,KAAa,OAAO;CACzD,IAAI;EACA,IAAM,IAAQ,OAAO,aAAa,QAAQ,CAAU;EAEpD,OADI,MAAU,WAAW,MAAU,UAAU,MAAU,WAAiB,IACjE;CACX,QAAQ;EACJ,OAAO;CACX;AACJ;AAUA,SAAgB,EAAc,EAC1B,aACA,kBAAe,UACf,gBAAa,iBACb,WACA,eAAY,sBACZ,iBACmB;CACnB,IAAM,CAAC,GAAO,KAAiB,QACrB,EAAW,CAAU,KAAK,CACpC,GACM,CAAC,GAAe,KAAoB,QAA8B,EAAQ,CAAK,CAAC,GAEhF,IAAY,EAAsB,CAAM;CAC9C,QAAgB;EACZ,EAAU,UAAU;CACxB,GAAG,CAAC,CAAM,CAAC;CAEX,IAAM,IAAe,MAAM,QAAQ,CAAS,IAAI,EAAU,KAAK,GAAG,IAAI,GAChE,IAAgB,EAAO,CAAU;CAevC,AAdA,QAAgB;EACZ,EAAc,UAAU;CAC5B,GAAG,CAAC,CAAU,CAAC,GAEf,QAAgB;EACZ,IAAM,IAAU,EAAU,UAAU,KAAK,SAAS;EAClD,IAAI,CAAC,GAAS;EACd,IAAM,IAAO,EAAQ,CAAK;EAE1B,AADA,EAAc,GAAS,GAAM,GAAW,EAAc,OAAO,GAC7D,EAAiB,CAAI;CAGzB,GAAG,CAAC,GAAO,CAAY,CAAC,GAExB,QAAgB;EACZ,IAAI,MAAU,YAAY,OAAO,SAAW,KAAa;EACzD,IAAM,IAAO,OAAO,WAAW,8BAA8B,GACvD,UAAsB;GACxB,IAAM,IAAU,EAAU,UAAU,KAAK,SAAS;GAClD,IAAI,CAAC,GAAS;GACd,IAAM,IAAsB,EAAK,UAAU,SAAS;GAEpD,AADA,EAAc,GAAS,GAAM,GAAW,EAAc,OAAO,GAC7D,EAAiB,CAAI;EACzB;EAEA,OADA,EAAK,iBAAiB,UAAU,CAAO,SAC1B,EAAK,oBAAoB,UAAU,CAAO;CAE3D,GAAG,CAAC,GAAO,CAAY,CAAC;CAExB,IAAM,IAAW,GACZ,MAAoB;EAEjB,IADA,EAAc,CAAI,GACd,KAAc,OAAO,SAAW,KAChC,IAAI;GACA,OAAO,aAAa,QAAQ,GAAY,CAAI;EAChD,QAAQ,CAER;CAER,GACA,CAAC,CAAU,CACf,GAEM,IAAS,QAAkB;EAC7B,EAAS,MAAkB,SAAS,UAAU,MAAM;CACxD,GAAG,CAAC,GAAe,CAAQ,CAAC,GAEtB,IAAQ,SACH;EAAE;EAAO;EAAe;EAAU;CAAO,IAChD;EAAC;EAAO;EAAe;EAAU;CAAM,CAC3C;CAEA,OAAO,kBAAC,EAAa,UAAd;EAA8B;EAAQ;CAAgC,CAAA;AACjF;AAKA,SAAgB,IAA8B;CAC1C,IAAM,IAAM,EAAW,CAAY;CACnC,IAAI,CAAC,GAAK,MAAU,MAAM,gDAAgD;CAC1E,OAAO;AACX"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=`tempest-theme`;function t(e){return e?typeof Document<`u`&&e instanceof Document?e.head:e:typeof document>`u`?null:document.head}function n(n,r={}){let{id:i=e,target:a}=r,o=t(a);if(!o||typeof document>`u`)return()=>{};let s=typeof n==`string`?n:n.css,c=Array.from(o.childNodes).find(e=>e instanceof HTMLStyleElement&&e.id===i);return c||(c=document.createElement(`style`),c.id=i,o.appendChild(c)),c.textContent=s,()=>{c?.remove()}}function r(e,t){if(typeof window>`u`||typeof document>`u`||t===null)return``;let n=t??document.documentElement;if(!n)return``;let r=e.startsWith(`--`)?e:`--${e}`;return window.getComputedStyle(n).getPropertyValue(r).trim()}exports.THEME_STYLE_ID=e,exports.applyTheme=n,exports.readThemeToken=r;
|
|
2
|
+
//# sourceMappingURL=apply-theme.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply-theme.cjs","names":[],"sources":["../../src/theme/apply-theme.ts"],"sourcesContent":["/**\n * Runtime installer for a {@link GeneratedTheme}.\n *\n * A generated theme is just CSS text, so it needs a `<style>` element to take\n * effect. This owns that element by id, which keeps re-applying idempotent — a\n * theme picker can call `applyTheme` on every click without stacking dead\n * stylesheets in `<head>`.\n */\nimport type { GeneratedTheme } from \"./create-theme\";\n\n/** Options for {@link applyTheme}. */\nexport interface ApplyThemeOptions {\n /**\n * `id` of the managed `<style>` element. Reusing the same id replaces the\n * previous theme; a distinct id lets two themes coexist (e.g. a scoped\n * preview under its own selector). Default `\"tempest-theme\"`.\n */\n id?: string;\n /**\n * Node the `<style>` is appended to. Default `document.head`. Pass a shadow\n * root to theme a single web component.\n */\n target?: Document | ShadowRoot | HTMLElement;\n}\n\n/** The default `<style>` element id owned by {@link applyTheme}. */\nexport const THEME_STYLE_ID = \"tempest-theme\";\n\nfunction resolveContainer(target: ApplyThemeOptions[\"target\"]): ParentNode | null {\n if (!target) {\n return typeof document === \"undefined\" ? null : document.head;\n }\n if (typeof Document !== \"undefined\" && target instanceof Document) {\n return target.head;\n }\n return target;\n}\n\n/**\n * Install a generated theme (or raw CSS) into the document.\n *\n * Safe to call outside a browser: with no `document` it is a no-op returning a\n * no-op disposer, so app bootstrap code does not need a `typeof window` guard.\n *\n * @param theme - A {@link GeneratedTheme} from `createTheme`, or CSS text.\n * @param options - Style element id and mount target.\n * @returns A function that removes the injected `<style>` element.\n *\n * @example\n * ```ts\n * import { applyTheme, createTheme, themePresets } from \"tempest-react-sdk\";\n *\n * const dispose = applyTheme(createTheme(themePresets.violet));\n * // later: dispose();\n * ```\n */\nexport function applyTheme(\n theme: GeneratedTheme | string,\n options: ApplyThemeOptions = {},\n): () => void {\n const { id = THEME_STYLE_ID, target } = options;\n const container = resolveContainer(target);\n if (!container || typeof document === \"undefined\") {\n return () => {};\n }\n\n const css = typeof theme === \"string\" ? theme : theme.css;\n\n let style = Array.from(container.childNodes).find(\n (node): node is HTMLStyleElement => node instanceof HTMLStyleElement && node.id === id,\n );\n\n if (!style) {\n style = document.createElement(\"style\");\n style.id = id;\n container.appendChild(style);\n }\n\n style.textContent = css;\n\n return () => {\n style?.remove();\n };\n}\n\n/**\n * Read a token's computed value from an element (default: `<html>`).\n *\n * Useful to bridge CSS tokens into JS that cannot take a `var()` — canvas\n * drawing, chart libraries that set SVG attributes, `<meta name=\"theme-color\">`.\n *\n * @param name - Token name, with or without the leading `--`.\n * @param element - Element to resolve against. Omit for `document.documentElement`;\n * an explicit `null` (a ref that has not attached yet) reads nothing rather than\n * silently falling back to the root, whose value may be a different theme.\n * @returns The trimmed value, or `\"\"` when unset or outside a browser.\n */\nexport function readThemeToken(name: string, element?: Element | null): string {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return \"\";\n if (element === null) return \"\";\n const target = element ?? document.documentElement;\n if (!target) return \"\";\n const property = name.startsWith(\"--\") ? name : `--${name}`;\n return window.getComputedStyle(target).getPropertyValue(property).trim();\n}\n"],"mappings":"AA0BA,IAAa,EAAiB,gBAE9B,SAAS,EAAiB,EAAwD,CAO9E,OANK,EAGD,OAAO,SAAa,KAAe,aAAkB,SAC9C,EAAO,KAEX,EALI,OAAO,SAAa,IAAc,KAAO,SAAS,IAMjE,CAoBA,SAAgB,EACZ,EACA,EAA6B,CAAC,EACpB,CACV,GAAM,CAAE,KAAK,EAAgB,UAAW,EAClC,EAAY,EAAiB,CAAM,EACzC,GAAI,CAAC,GAAa,OAAO,SAAa,IAClC,UAAa,CAAC,EAGlB,IAAM,EAAM,OAAO,GAAU,SAAW,EAAQ,EAAM,IAElD,EAAQ,MAAM,KAAK,EAAU,UAAU,CAAC,CAAC,KACxC,GAAmC,aAAgB,kBAAoB,EAAK,KAAO,CACxF,EAUA,OARK,IACD,EAAQ,SAAS,cAAc,OAAO,EACtC,EAAM,GAAK,EACX,EAAU,YAAY,CAAK,GAG/B,EAAM,YAAc,MAEP,CACT,GAAO,OAAO,CAClB,CACJ,CAcA,SAAgB,EAAe,EAAc,EAAkC,CAE3E,GADI,OAAO,OAAW,KAAe,OAAO,SAAa,KACrD,IAAY,KAAM,MAAO,GAC7B,IAAM,EAAS,GAAW,SAAS,gBACnC,GAAI,CAAC,EAAQ,MAAO,GACpB,IAAM,EAAW,EAAK,WAAW,IAAI,EAAI,EAAO,KAAK,IACrD,OAAO,OAAO,iBAAiB,CAAM,CAAC,CAAC,iBAAiB,CAAQ,CAAC,CAAC,KAAK,CAC3E"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region src/theme/apply-theme.ts
|
|
2
|
+
var e = "tempest-theme";
|
|
3
|
+
function t(e) {
|
|
4
|
+
return e ? typeof Document < "u" && e instanceof Document ? e.head : e : typeof document > "u" ? null : document.head;
|
|
5
|
+
}
|
|
6
|
+
function n(n, r = {}) {
|
|
7
|
+
let { id: i = e, target: a } = r, o = t(a);
|
|
8
|
+
if (!o || typeof document > "u") return () => {};
|
|
9
|
+
let s = typeof n == "string" ? n : n.css, c = Array.from(o.childNodes).find((e) => e instanceof HTMLStyleElement && e.id === i);
|
|
10
|
+
return c || (c = document.createElement("style"), c.id = i, o.appendChild(c)), c.textContent = s, () => {
|
|
11
|
+
c?.remove();
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function r(e, t) {
|
|
15
|
+
if (typeof window > "u" || typeof document > "u" || t === null) return "";
|
|
16
|
+
let n = t ?? document.documentElement;
|
|
17
|
+
if (!n) return "";
|
|
18
|
+
let r = e.startsWith("--") ? e : `--${e}`;
|
|
19
|
+
return window.getComputedStyle(n).getPropertyValue(r).trim();
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { e as THEME_STYLE_ID, n as applyTheme, r as readThemeToken };
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=apply-theme.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply-theme.js","names":[],"sources":["../../src/theme/apply-theme.ts"],"sourcesContent":["/**\n * Runtime installer for a {@link GeneratedTheme}.\n *\n * A generated theme is just CSS text, so it needs a `<style>` element to take\n * effect. This owns that element by id, which keeps re-applying idempotent — a\n * theme picker can call `applyTheme` on every click without stacking dead\n * stylesheets in `<head>`.\n */\nimport type { GeneratedTheme } from \"./create-theme\";\n\n/** Options for {@link applyTheme}. */\nexport interface ApplyThemeOptions {\n /**\n * `id` of the managed `<style>` element. Reusing the same id replaces the\n * previous theme; a distinct id lets two themes coexist (e.g. a scoped\n * preview under its own selector). Default `\"tempest-theme\"`.\n */\n id?: string;\n /**\n * Node the `<style>` is appended to. Default `document.head`. Pass a shadow\n * root to theme a single web component.\n */\n target?: Document | ShadowRoot | HTMLElement;\n}\n\n/** The default `<style>` element id owned by {@link applyTheme}. */\nexport const THEME_STYLE_ID = \"tempest-theme\";\n\nfunction resolveContainer(target: ApplyThemeOptions[\"target\"]): ParentNode | null {\n if (!target) {\n return typeof document === \"undefined\" ? null : document.head;\n }\n if (typeof Document !== \"undefined\" && target instanceof Document) {\n return target.head;\n }\n return target;\n}\n\n/**\n * Install a generated theme (or raw CSS) into the document.\n *\n * Safe to call outside a browser: with no `document` it is a no-op returning a\n * no-op disposer, so app bootstrap code does not need a `typeof window` guard.\n *\n * @param theme - A {@link GeneratedTheme} from `createTheme`, or CSS text.\n * @param options - Style element id and mount target.\n * @returns A function that removes the injected `<style>` element.\n *\n * @example\n * ```ts\n * import { applyTheme, createTheme, themePresets } from \"tempest-react-sdk\";\n *\n * const dispose = applyTheme(createTheme(themePresets.violet));\n * // later: dispose();\n * ```\n */\nexport function applyTheme(\n theme: GeneratedTheme | string,\n options: ApplyThemeOptions = {},\n): () => void {\n const { id = THEME_STYLE_ID, target } = options;\n const container = resolveContainer(target);\n if (!container || typeof document === \"undefined\") {\n return () => {};\n }\n\n const css = typeof theme === \"string\" ? theme : theme.css;\n\n let style = Array.from(container.childNodes).find(\n (node): node is HTMLStyleElement => node instanceof HTMLStyleElement && node.id === id,\n );\n\n if (!style) {\n style = document.createElement(\"style\");\n style.id = id;\n container.appendChild(style);\n }\n\n style.textContent = css;\n\n return () => {\n style?.remove();\n };\n}\n\n/**\n * Read a token's computed value from an element (default: `<html>`).\n *\n * Useful to bridge CSS tokens into JS that cannot take a `var()` — canvas\n * drawing, chart libraries that set SVG attributes, `<meta name=\"theme-color\">`.\n *\n * @param name - Token name, with or without the leading `--`.\n * @param element - Element to resolve against. Omit for `document.documentElement`;\n * an explicit `null` (a ref that has not attached yet) reads nothing rather than\n * silently falling back to the root, whose value may be a different theme.\n * @returns The trimmed value, or `\"\"` when unset or outside a browser.\n */\nexport function readThemeToken(name: string, element?: Element | null): string {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return \"\";\n if (element === null) return \"\";\n const target = element ?? document.documentElement;\n if (!target) return \"\";\n const property = name.startsWith(\"--\") ? name : `--${name}`;\n return window.getComputedStyle(target).getPropertyValue(property).trim();\n}\n"],"mappings":";AA0BA,IAAa,IAAiB;AAE9B,SAAS,EAAiB,GAAwD;CAO9E,OANK,IAGD,OAAO,WAAa,OAAe,aAAkB,WAC9C,EAAO,OAEX,IALI,OAAO,WAAa,MAAc,OAAO,SAAS;AAMjE;AAoBA,SAAgB,EACZ,GACA,IAA6B,CAAC,GACpB;CACV,IAAM,EAAE,QAAK,GAAgB,cAAW,GAClC,IAAY,EAAiB,CAAM;CACzC,IAAI,CAAC,KAAa,OAAO,WAAa,KAClC,aAAa,CAAC;CAGlB,IAAM,IAAM,OAAO,KAAU,WAAW,IAAQ,EAAM,KAElD,IAAQ,MAAM,KAAK,EAAU,UAAU,CAAC,CAAC,MACxC,MAAmC,aAAgB,oBAAoB,EAAK,OAAO,CACxF;CAUA,OARK,MACD,IAAQ,SAAS,cAAc,OAAO,GACtC,EAAM,KAAK,GACX,EAAU,YAAY,CAAK,IAG/B,EAAM,cAAc,SAEP;EACT,GAAO,OAAO;CAClB;AACJ;AAcA,SAAgB,EAAe,GAAc,GAAkC;CAE3E,IADI,OAAO,SAAW,OAAe,OAAO,WAAa,OACrD,MAAY,MAAM,OAAO;CAC7B,IAAM,IAAS,KAAW,SAAS;CACnC,IAAI,CAAC,GAAQ,OAAO;CACpB,IAAM,IAAW,EAAK,WAAW,IAAI,IAAI,IAAO,KAAK;CACrD,OAAO,OAAO,iBAAiB,CAAM,CAAC,CAAC,iBAAiB,CAAQ,CAAC,CAAC,KAAK;AAC3E"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=[50,100,200,300,400,500,600,700,800,900],t={50:.966,100:.923,200:.844,300:.743,400:.642,500:.563,600:.48,700:.391,800:.325,900:.251},n={50:.982,100:.963,200:.927,300:.872,400:.71,500:.544,600:.442,700:.369,800:.278,900:.21},r={50:.159,100:.205,200:.254,300:.314,400:.381,500:.5,600:.571,700:.762,800:.86,900:.964},i={50:.238,100:.288,200:.362,300:.452,400:.544,500:.632,600:.712,700:.788,800:.862,900:.928},a={50:.18,100:.34,200:.6,300:.82,400:.95,500:1,600:.98,700:.88,800:.74,900:.58};function o(e,t,n){return Math.min(n,Math.max(t,e))}function s(e){let t=e.trim().replace(/^#/,``),n=t.length===3||t.length===4?t.slice(0,3).split(``).map(e=>e+e).join(``):t.slice(0,6);if(!/^[0-9a-fA-F]{6}$/.test(n))throw Error(`Invalid hex color: "${e}"`);return`#${n.toLowerCase()}`}function c(e){let t=s(e).slice(1);return{r:parseInt(t.slice(0,2),16)/255,g:parseInt(t.slice(2,4),16)/255,b:parseInt(t.slice(4,6),16)/255}}function l(e,t,n){let r=e=>Math.round(o(e,0,1)*255).toString(16).padStart(2,`0`);return`#${r(e)}${r(t)}${r(n)}`}function u(e){return e<=.04045?e/12.92:((e+.055)/1.055)**2.4}function d(e){return e<=.0031308?e*12.92:1.055*e**(1/2.4)-.055}function f(e){let{r:t,g:n,b:r}=c(e),i=u(t),a=u(n),o=u(r),s=Math.cbrt(.4122214708*i+.5363325363*a+.0514459929*o),l=Math.cbrt(.2119034982*i+.6806995451*a+.1073969566*o),d=Math.cbrt(.0883024619*i+.2817188376*a+.6299787005*o),f=.2104542553*s+.793617785*l-.0040720468*d,p=1.9779984951*s-2.428592205*l+.4505937099*d,m=.0259040371*s+.7827717662*l-.808675766*d,h=Math.sqrt(p*p+m*m),g=h<1e-6?0:Math.atan2(m,p)*180/Math.PI;return{l:f,c:h,h:g<0?g+360:g}}function p({l:e,c:t,h:n}){let r=n*Math.PI/180,i=Math.cos(r)*t,a=Math.sin(r)*t,o=(e+.3963377774*i+.2158037573*a)**3,s=(e-.1055613458*i-.0638541728*a)**3,c=(e-.0894841775*i-1.291485548*a)**3;return{r:4.0767416621*o-3.3077115913*s+.2309699292*c,g:-1.2684380046*o+2.6097574011*s-.3413193965*c,b:-.0041960863*o-.7034186147*s+1.707614701*c}}function m({r:e,g:t,b:n}){return e>=-1e-4&&e<=1.0001&&t>=-1e-4&&t<=1.0001&&n>=-1e-4&&n<=1.0001}function h(e){let t=Math.max(0,e.c),n=p({...e,c:t});for(let r=0;r<24&&!m(n);r+=1)t*=.9,n=p({...e,c:t});return l(d(n.r),d(n.g),d(n.b))}function g(o,s=`light`,c={}){let l=f(o),u=c.neutral?s===`dark`?r:n:s===`dark`?i:t,d=c.anchor===!1?u:y(u,l.l,s),p={};for(let t of e)p[t]=h({l:d[t],c:l.c*a[t],h:l.h});return p}var _=.985,v=.12;function y(t,n,r){let i={},a=t[500],o=r===`dark`?t[900]:t[50],s=r===`dark`?t[50]:t[900],c=Math.min(_,Math.max(o,n+.05)),l=Math.max(v,Math.min(s,n-.05));for(let r of e){let e=t[r];e===a?i[r]=n:e>a?i[r]=n+(e-a)/(o-a)*(c-n):i[r]=n-(a-e)/(a-s)*(n-l)}return i}function b(e){let{r:t,g:n,b:r}=c(e);return .2126*u(t)+.7152*u(n)+.0722*u(r)}function x(e,t){let n=b(e),r=b(t),i=Math.max(n,r),a=Math.min(n,r);return(i+.05)/(a+.05)}function S(e,t=`#ffffff`,n=`#101828`){return x(e,t)>=x(e,n)?t:n}function C(e,t){let{r:n,g:r,b:i}=c(e),a=e=>Math.round(o(e,0,1)*255);return`rgb(${a(n)} ${a(r)} ${a(i)} / ${o(t,0,1)})`}exports.contrastRatio=x,exports.createColorScale=g,exports.hexToOklch=f,exports.hexToRgb=c,exports.hexToRgbaString=C,exports.oklchToHex=h,exports.readableForeground=S,exports.relativeLuminance=b,exports.rgbToHex=l;
|
|
2
|
+
//# sourceMappingURL=color.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color.cjs","names":[],"sources":["../../src/theme/color.ts"],"sourcesContent":["/**\n * Color math behind {@link createTheme} — OKLab/OKLCH conversions, tint scale\n * generation and WCAG contrast picking.\n *\n * Everything here is pure and dependency-free: the SDK generates brand ramps at\n * runtime in the browser, so a color library would be a disproportionate cost\n * for ~150 lines of well-specified math. OKLCH is used instead of HSL because\n * HSL lightness is not perceptual — an HSL ramp of a yellow and of a blue at the\n * same `L` read as wildly different brightness, which is exactly what breaks a\n * generated palette.\n */\n\n/** A color in the OKLCH space: perceptual lightness, chroma and hue. */\nexport interface Oklch {\n /** Perceptual lightness, `0` (black) to `1` (white). */\n l: number;\n /** Chroma (colorfulness). `0` is gray; sRGB rarely exceeds `~0.37`. */\n c: number;\n /** Hue angle in degrees, `0`–`360`. */\n h: number;\n}\n\n/** The ten steps of a Tempest tint scale, lightest to darkest. */\nexport type ScaleStep = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;\n\n/** A generated tint scale, keyed by step. */\nexport type ColorScale = Record<ScaleStep, string>;\n\nconst SCALE_STEPS: ScaleStep[] = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900];\n\n/**\n * Target lightness per step for a **brand** ramp in light mode.\n *\n * These are the measured OKLCH lightnesses of the hand-written `--tempest-primary-*`\n * scale in `colors.css`, so a generated brand ramp lands in the same visual range\n * as the built-in one instead of merely near it.\n */\nconst LIGHT_LIGHTNESS: Record<ScaleStep, number> = {\n 50: 0.966,\n 100: 0.923,\n 200: 0.844,\n 300: 0.743,\n 400: 0.642,\n 500: 0.563,\n 600: 0.48,\n 700: 0.391,\n 800: 0.325,\n 900: 0.251,\n};\n\n/**\n * Target lightness per step for a **neutral** ramp in light mode — measured from\n * the hand-written `--tempest-gray-*` scale.\n *\n * Deliberately **wider** than the brand curve at both ends (`0.982` → `0.210` vs\n * `0.966` → `0.251`): a neutral carries surfaces that must read as near-white and\n * text that must read as near-black, and the pair `text-muted` on `surface-3` has\n * to clear AA. Reusing the brand curve for neutrals is what dropped that pair to\n * 3.5:1 and made the browser axe sweep fail on every generated theme.\n */\nconst NEUTRAL_LIGHT_LIGHTNESS: Record<ScaleStep, number> = {\n 50: 0.982,\n 100: 0.963,\n 200: 0.927,\n 300: 0.872,\n 400: 0.71,\n 500: 0.544,\n 600: 0.442,\n 700: 0.369,\n 800: 0.278,\n 900: 0.21,\n};\n\n/**\n * Neutral ramp for dark mode, measured from the `[data-tempest-theme=\"dark\"]`\n * surface/text tokens: `50` is the page background and `900` is text-grade.\n */\nconst NEUTRAL_DARK_LIGHTNESS: Record<ScaleStep, number> = {\n 50: 0.159,\n 100: 0.205,\n 200: 0.254,\n 300: 0.314,\n 400: 0.381,\n 500: 0.5,\n 600: 0.571,\n 700: 0.762,\n 800: 0.86,\n 900: 0.964,\n};\n\n/**\n * Target lightness for a dark-theme ramp — the ramp is **inverted**: `50` is the\n * darkest tint (a surface) and `900` the lightest (text-grade), matching the\n * `[data-tempest-theme=\"dark\"]` block in `colors.css`.\n */\nconst DARK_LIGHTNESS: Record<ScaleStep, number> = {\n 50: 0.238,\n 100: 0.288,\n 200: 0.362,\n 300: 0.452,\n 400: 0.544,\n 500: 0.632,\n 600: 0.712,\n 700: 0.788,\n 800: 0.862,\n 900: 0.928,\n};\n\n/**\n * Chroma multiplier per step, relative to the input color's chroma.\n *\n * Peaks around `500`–`600` and falls off at both ends: near-white and near-black\n * tints hold very little chroma before they look muddy or leave the sRGB gamut.\n */\nconst CHROMA_CURVE: Record<ScaleStep, number> = {\n 50: 0.18,\n 100: 0.34,\n 200: 0.6,\n 300: 0.82,\n 400: 0.95,\n 500: 1,\n 600: 0.98,\n 700: 0.88,\n 800: 0.74,\n 900: 0.58,\n};\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value));\n}\n\n/** Expand `#abc` to `#aabbcc` and normalize to a lowercase 6-digit hex. */\nfunction normalizeHex(hex: string): string {\n const raw = hex.trim().replace(/^#/, \"\");\n const expanded =\n raw.length === 3 || raw.length === 4\n ? raw\n .slice(0, 3)\n .split(\"\")\n .map((char) => char + char)\n .join(\"\")\n : raw.slice(0, 6);\n if (!/^[0-9a-fA-F]{6}$/.test(expanded)) {\n throw new Error(`Invalid hex color: \"${hex}\"`);\n }\n return `#${expanded.toLowerCase()}`;\n}\n\n/** Parse a hex color into sRGB channels in the `0`–`1` range. */\nexport function hexToRgb(hex: string): { r: number; g: number; b: number } {\n const normalized = normalizeHex(hex).slice(1);\n return {\n r: parseInt(normalized.slice(0, 2), 16) / 255,\n g: parseInt(normalized.slice(2, 4), 16) / 255,\n b: parseInt(normalized.slice(4, 6), 16) / 255,\n };\n}\n\n/** Serialize sRGB channels (`0`–`1`, clamped) back to a `#rrggbb` string. */\nexport function rgbToHex(r: number, g: number, b: number): string {\n const channel = (value: number): string =>\n Math.round(clamp(value, 0, 1) * 255)\n .toString(16)\n .padStart(2, \"0\");\n return `#${channel(r)}${channel(g)}${channel(b)}`;\n}\n\nfunction srgbToLinear(channel: number): number {\n return channel <= 0.04045 ? channel / 12.92 : Math.pow((channel + 0.055) / 1.055, 2.4);\n}\n\nfunction linearToSrgb(channel: number): number {\n return channel <= 0.0031308 ? channel * 12.92 : 1.055 * Math.pow(channel, 1 / 2.4) - 0.055;\n}\n\n/** Convert a hex color to OKLCH. */\nexport function hexToOklch(hex: string): Oklch {\n const { r, g, b } = hexToRgb(hex);\n const lr = srgbToLinear(r);\n const lg = srgbToLinear(g);\n const lb = srgbToLinear(b);\n\n const long = Math.cbrt(0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb);\n const medium = Math.cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb);\n const short = Math.cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb);\n\n const l = 0.2104542553 * long + 0.793617785 * medium - 0.0040720468 * short;\n const a = 1.9779984951 * long - 2.428592205 * medium + 0.4505937099 * short;\n const bAxis = 0.0259040371 * long + 0.7827717662 * medium - 0.808675766 * short;\n\n const c = Math.sqrt(a * a + bAxis * bAxis);\n const hue = c < 1e-6 ? 0 : (Math.atan2(bAxis, a) * 180) / Math.PI;\n\n return { l, c, h: hue < 0 ? hue + 360 : hue };\n}\n\n/** Convert OKLCH to linear-light sRGB, without gamut clamping. */\nfunction oklchToLinearRgb({ l, c, h }: Oklch): { r: number; g: number; b: number } {\n const hRad = (h * Math.PI) / 180;\n const a = Math.cos(hRad) * c;\n const bAxis = Math.sin(hRad) * c;\n\n const long = (l + 0.3963377774 * a + 0.2158037573 * bAxis) ** 3;\n const medium = (l - 0.1055613458 * a - 0.0638541728 * bAxis) ** 3;\n const short = (l - 0.0894841775 * a - 1.291485548 * bAxis) ** 3;\n\n return {\n r: 4.0767416621 * long - 3.3077115913 * medium + 0.2309699292 * short,\n g: -1.2684380046 * long + 2.6097574011 * medium - 0.3413193965 * short,\n b: -0.0041960863 * long - 0.7034186147 * medium + 1.707614701 * short,\n };\n}\n\nfunction inGamut({ r, g, b }: { r: number; g: number; b: number }): boolean {\n const epsilon = 1e-4;\n return (\n r >= -epsilon &&\n r <= 1 + epsilon &&\n g >= -epsilon &&\n g <= 1 + epsilon &&\n b >= -epsilon &&\n b <= 1 + epsilon\n );\n}\n\n/**\n * Convert OKLCH to a hex color, reducing chroma until the result fits sRGB.\n *\n * Lightness and hue are preserved: desaturating is far less noticeable than\n * shifting either of them, and a naive channel clamp would do both.\n */\nexport function oklchToHex(color: Oklch): string {\n let chroma = Math.max(0, color.c);\n let rgb = oklchToLinearRgb({ ...color, c: chroma });\n\n for (let i = 0; i < 24 && !inGamut(rgb); i += 1) {\n chroma *= 0.9;\n rgb = oklchToLinearRgb({ ...color, c: chroma });\n }\n\n return rgbToHex(linearToSrgb(rgb.r), linearToSrgb(rgb.g), linearToSrgb(rgb.b));\n}\n\n/** Options for {@link createColorScale}. */\nexport interface ColorScaleOptions {\n /**\n * Pin step `500` to the input color's exact lightness. Default `true`.\n *\n * Right for a **brand** color: the hex a designer hands over is the one the\n * buttons must be. Wrong for a **neutral**: nobody checks that `gray-500` is\n * exactly the input, while everybody notices that a surface stopped being\n * near-white — anchoring rescales both halves around the input and compresses\n * exactly the range a neutral needs wide. With `false`, the ramp keeps the\n * tuned lightness curve and the input only contributes hue and chroma (a warm\n * or cool neutral, as asked).\n */\n anchor?: boolean;\n /**\n * Use the neutral lightness curve instead of the brand one. Default `false`.\n *\n * The neutral curve is wider at both ends, which is what keeps\n * `text-muted`-on-`surface-3` above AA. Implies the ramp is meant for\n * surfaces, borders and text rather than for an action color.\n */\n neutral?: boolean;\n}\n\n/**\n * Build a ten-step tint scale from a single color.\n *\n * The input color's hue is kept throughout and its chroma sets the intensity of\n * the whole ramp, so a muted brand color yields a muted scale instead of being\n * \"corrected\" into something the brand never approved.\n *\n * @param hex - Any hex color (`#abc` or `#aabbcc`), the intended `500` step.\n * @param mode - `\"light\"` for a light→dark ramp, `\"dark\"` for the inverted ramp\n * used under `[data-tempest-theme=\"dark\"]`.\n * @param options - See {@link ColorScaleOptions}.\n */\nexport function createColorScale(\n hex: string,\n mode: \"light\" | \"dark\" = \"light\",\n options: ColorScaleOptions = {},\n): ColorScale {\n const base = hexToOklch(hex);\n const targets = options.neutral\n ? mode === \"dark\"\n ? NEUTRAL_DARK_LIGHTNESS\n : NEUTRAL_LIGHT_LIGHTNESS\n : mode === \"dark\"\n ? DARK_LIGHTNESS\n : LIGHT_LIGHTNESS;\n const lightness =\n options.anchor === false ? targets : anchorLightnessAt500(targets, base.l, mode);\n const scale = {} as ColorScale;\n\n for (const step of SCALE_STEPS) {\n scale[step] = oklchToHex({\n l: lightness[step],\n c: base.c * CHROMA_CURVE[step],\n h: base.h,\n });\n }\n\n return scale;\n}\n\n/** Widest lightness band a generated ramp may span, so both ends stay usable. */\nconst LIGHTNESS_CEILING = 0.985;\nconst LIGHTNESS_FLOOR = 0.12;\n\n/**\n * Re-anchor a lightness ramp so step `500` lands on the brand color exactly.\n *\n * Without this, `500` is forced onto the ramp's own target lightness: a brand\n * `#7c3aed` came back as `#9161fe` — same hue, same chroma, *re-lightened*. It\n * looks fine in isolation and is wrong anyway, because the one color a designer\n * hands over is the one the buttons must actually be.\n *\n * Each half of the ramp is scaled independently around the anchor, so the shape\n * of the original curve survives and the ramp stays monotonic even for a very\n * light brand (yellow) or a very dark one (navy) — those simply get a shorter\n * run on the crowded side.\n *\n * @param targets - The default per-step lightness for this scheme.\n * @param anchor - Lightness of the brand color, used verbatim at step `500`.\n * @param mode - `\"dark\"` inverts which side of the ramp is the light one.\n */\nfunction anchorLightnessAt500(\n targets: Record<ScaleStep, number>,\n anchor: number,\n mode: \"light\" | \"dark\",\n): Record<ScaleStep, number> {\n const anchored = {} as Record<ScaleStep, number>;\n const base = targets[500];\n const lightEnd = mode === \"dark\" ? targets[900] : targets[50];\n const darkEnd = mode === \"dark\" ? targets[50] : targets[900];\n\n const top = Math.min(LIGHTNESS_CEILING, Math.max(lightEnd, anchor + 0.05));\n const bottom = Math.max(LIGHTNESS_FLOOR, Math.min(darkEnd, anchor - 0.05));\n\n for (const step of SCALE_STEPS) {\n const target = targets[step];\n if (target === base) {\n anchored[step] = anchor;\n } else if (target > base) {\n const ratio = (target - base) / (lightEnd - base);\n anchored[step] = anchor + ratio * (top - anchor);\n } else {\n const ratio = (base - target) / (base - darkEnd);\n anchored[step] = anchor - ratio * (anchor - bottom);\n }\n }\n\n return anchored;\n}\n\n/** WCAG 2.x relative luminance of a hex color. */\nexport function relativeLuminance(hex: string): number {\n const { r, g, b } = hexToRgb(hex);\n return 0.2126 * srgbToLinear(r) + 0.7152 * srgbToLinear(g) + 0.0722 * srgbToLinear(b);\n}\n\n/** WCAG 2.x contrast ratio between two hex colors, from `1` to `21`. */\nexport function contrastRatio(a: string, b: string): number {\n const la = relativeLuminance(a);\n const lb = relativeLuminance(b);\n const lighter = Math.max(la, lb);\n const darker = Math.min(la, lb);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\n/**\n * Pick the readable foreground for a background, by contrast ratio.\n *\n * Used for `--tempest-primary-foreground`: a generated brand color can land\n * anywhere on the lightness axis, and hardcoding white would silently produce\n * unreadable buttons for light brands (yellow, lime, cyan).\n */\nexport function readableForeground(\n background: string,\n light = \"#ffffff\",\n dark = \"#101828\",\n): string {\n return contrastRatio(background, light) >= contrastRatio(background, dark) ? light : dark;\n}\n\n/** Format a hex color as `rgb(r g b / alpha)`, for focus rings and overlays. */\nexport function hexToRgbaString(hex: string, alpha: number): string {\n const { r, g, b } = hexToRgb(hex);\n const channel = (value: number): number => Math.round(clamp(value, 0, 1) * 255);\n return `rgb(${channel(r)} ${channel(g)} ${channel(b)} / ${clamp(alpha, 0, 1)})`;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
//#region src/theme/color.ts
|
|
2
|
+
var e = [
|
|
3
|
+
50,
|
|
4
|
+
100,
|
|
5
|
+
200,
|
|
6
|
+
300,
|
|
7
|
+
400,
|
|
8
|
+
500,
|
|
9
|
+
600,
|
|
10
|
+
700,
|
|
11
|
+
800,
|
|
12
|
+
900
|
|
13
|
+
], t = {
|
|
14
|
+
50: .966,
|
|
15
|
+
100: .923,
|
|
16
|
+
200: .844,
|
|
17
|
+
300: .743,
|
|
18
|
+
400: .642,
|
|
19
|
+
500: .563,
|
|
20
|
+
600: .48,
|
|
21
|
+
700: .391,
|
|
22
|
+
800: .325,
|
|
23
|
+
900: .251
|
|
24
|
+
}, n = {
|
|
25
|
+
50: .982,
|
|
26
|
+
100: .963,
|
|
27
|
+
200: .927,
|
|
28
|
+
300: .872,
|
|
29
|
+
400: .71,
|
|
30
|
+
500: .544,
|
|
31
|
+
600: .442,
|
|
32
|
+
700: .369,
|
|
33
|
+
800: .278,
|
|
34
|
+
900: .21
|
|
35
|
+
}, r = {
|
|
36
|
+
50: .159,
|
|
37
|
+
100: .205,
|
|
38
|
+
200: .254,
|
|
39
|
+
300: .314,
|
|
40
|
+
400: .381,
|
|
41
|
+
500: .5,
|
|
42
|
+
600: .571,
|
|
43
|
+
700: .762,
|
|
44
|
+
800: .86,
|
|
45
|
+
900: .964
|
|
46
|
+
}, i = {
|
|
47
|
+
50: .238,
|
|
48
|
+
100: .288,
|
|
49
|
+
200: .362,
|
|
50
|
+
300: .452,
|
|
51
|
+
400: .544,
|
|
52
|
+
500: .632,
|
|
53
|
+
600: .712,
|
|
54
|
+
700: .788,
|
|
55
|
+
800: .862,
|
|
56
|
+
900: .928
|
|
57
|
+
}, a = {
|
|
58
|
+
50: .18,
|
|
59
|
+
100: .34,
|
|
60
|
+
200: .6,
|
|
61
|
+
300: .82,
|
|
62
|
+
400: .95,
|
|
63
|
+
500: 1,
|
|
64
|
+
600: .98,
|
|
65
|
+
700: .88,
|
|
66
|
+
800: .74,
|
|
67
|
+
900: .58
|
|
68
|
+
};
|
|
69
|
+
function o(e, t, n) {
|
|
70
|
+
return Math.min(n, Math.max(t, e));
|
|
71
|
+
}
|
|
72
|
+
function s(e) {
|
|
73
|
+
let t = e.trim().replace(/^#/, ""), n = t.length === 3 || t.length === 4 ? t.slice(0, 3).split("").map((e) => e + e).join("") : t.slice(0, 6);
|
|
74
|
+
if (!/^[0-9a-fA-F]{6}$/.test(n)) throw Error(`Invalid hex color: "${e}"`);
|
|
75
|
+
return `#${n.toLowerCase()}`;
|
|
76
|
+
}
|
|
77
|
+
function c(e) {
|
|
78
|
+
let t = s(e).slice(1);
|
|
79
|
+
return {
|
|
80
|
+
r: parseInt(t.slice(0, 2), 16) / 255,
|
|
81
|
+
g: parseInt(t.slice(2, 4), 16) / 255,
|
|
82
|
+
b: parseInt(t.slice(4, 6), 16) / 255
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function l(e, t, n) {
|
|
86
|
+
let r = (e) => Math.round(o(e, 0, 1) * 255).toString(16).padStart(2, "0");
|
|
87
|
+
return `#${r(e)}${r(t)}${r(n)}`;
|
|
88
|
+
}
|
|
89
|
+
function u(e) {
|
|
90
|
+
return e <= .04045 ? e / 12.92 : ((e + .055) / 1.055) ** 2.4;
|
|
91
|
+
}
|
|
92
|
+
function d(e) {
|
|
93
|
+
return e <= .0031308 ? e * 12.92 : 1.055 * e ** (1 / 2.4) - .055;
|
|
94
|
+
}
|
|
95
|
+
function f(e) {
|
|
96
|
+
let { r: t, g: n, b: r } = c(e), i = u(t), a = u(n), o = u(r), s = Math.cbrt(.4122214708 * i + .5363325363 * a + .0514459929 * o), l = Math.cbrt(.2119034982 * i + .6806995451 * a + .1073969566 * o), d = Math.cbrt(.0883024619 * i + .2817188376 * a + .6299787005 * o), f = .2104542553 * s + .793617785 * l - .0040720468 * d, p = 1.9779984951 * s - 2.428592205 * l + .4505937099 * d, m = .0259040371 * s + .7827717662 * l - .808675766 * d, h = Math.sqrt(p * p + m * m), g = h < 1e-6 ? 0 : Math.atan2(m, p) * 180 / Math.PI;
|
|
97
|
+
return {
|
|
98
|
+
l: f,
|
|
99
|
+
c: h,
|
|
100
|
+
h: g < 0 ? g + 360 : g
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function p({ l: e, c: t, h: n }) {
|
|
104
|
+
let r = n * Math.PI / 180, i = Math.cos(r) * t, a = Math.sin(r) * t, o = (e + .3963377774 * i + .2158037573 * a) ** 3, s = (e - .1055613458 * i - .0638541728 * a) ** 3, c = (e - .0894841775 * i - 1.291485548 * a) ** 3;
|
|
105
|
+
return {
|
|
106
|
+
r: 4.0767416621 * o - 3.3077115913 * s + .2309699292 * c,
|
|
107
|
+
g: -1.2684380046 * o + 2.6097574011 * s - .3413193965 * c,
|
|
108
|
+
b: -.0041960863 * o - .7034186147 * s + 1.707614701 * c
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function m({ r: e, g: t, b: n }) {
|
|
112
|
+
return e >= -1e-4 && e <= 1.0001 && t >= -1e-4 && t <= 1.0001 && n >= -1e-4 && n <= 1.0001;
|
|
113
|
+
}
|
|
114
|
+
function h(e) {
|
|
115
|
+
let t = Math.max(0, e.c), n = p({
|
|
116
|
+
...e,
|
|
117
|
+
c: t
|
|
118
|
+
});
|
|
119
|
+
for (let r = 0; r < 24 && !m(n); r += 1) t *= .9, n = p({
|
|
120
|
+
...e,
|
|
121
|
+
c: t
|
|
122
|
+
});
|
|
123
|
+
return l(d(n.r), d(n.g), d(n.b));
|
|
124
|
+
}
|
|
125
|
+
function g(o, s = "light", c = {}) {
|
|
126
|
+
let l = f(o), u = c.neutral ? s === "dark" ? r : n : s === "dark" ? i : t, d = c.anchor === !1 ? u : y(u, l.l, s), p = {};
|
|
127
|
+
for (let t of e) p[t] = h({
|
|
128
|
+
l: d[t],
|
|
129
|
+
c: l.c * a[t],
|
|
130
|
+
h: l.h
|
|
131
|
+
});
|
|
132
|
+
return p;
|
|
133
|
+
}
|
|
134
|
+
var _ = .985, v = .12;
|
|
135
|
+
function y(t, n, r) {
|
|
136
|
+
let i = {}, a = t[500], o = r === "dark" ? t[900] : t[50], s = r === "dark" ? t[50] : t[900], c = Math.min(_, Math.max(o, n + .05)), l = Math.max(v, Math.min(s, n - .05));
|
|
137
|
+
for (let r of e) {
|
|
138
|
+
let e = t[r];
|
|
139
|
+
e === a ? i[r] = n : e > a ? i[r] = n + (e - a) / (o - a) * (c - n) : i[r] = n - (a - e) / (a - s) * (n - l);
|
|
140
|
+
}
|
|
141
|
+
return i;
|
|
142
|
+
}
|
|
143
|
+
function b(e) {
|
|
144
|
+
let { r: t, g: n, b: r } = c(e);
|
|
145
|
+
return .2126 * u(t) + .7152 * u(n) + .0722 * u(r);
|
|
146
|
+
}
|
|
147
|
+
function x(e, t) {
|
|
148
|
+
let n = b(e), r = b(t), i = Math.max(n, r), a = Math.min(n, r);
|
|
149
|
+
return (i + .05) / (a + .05);
|
|
150
|
+
}
|
|
151
|
+
function S(e, t = "#ffffff", n = "#101828") {
|
|
152
|
+
return x(e, t) >= x(e, n) ? t : n;
|
|
153
|
+
}
|
|
154
|
+
function C(e, t) {
|
|
155
|
+
let { r: n, g: r, b: i } = c(e), a = (e) => Math.round(o(e, 0, 1) * 255);
|
|
156
|
+
return `rgb(${a(n)} ${a(r)} ${a(i)} / ${o(t, 0, 1)})`;
|
|
157
|
+
}
|
|
158
|
+
//#endregion
|
|
159
|
+
export { x as contrastRatio, g as createColorScale, f as hexToOklch, c as hexToRgb, C as hexToRgbaString, h as oklchToHex, S as readableForeground, b as relativeLuminance, l as rgbToHex };
|
|
160
|
+
|
|
161
|
+
//# sourceMappingURL=color.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color.js","names":[],"sources":["../../src/theme/color.ts"],"sourcesContent":["/**\n * Color math behind {@link createTheme} — OKLab/OKLCH conversions, tint scale\n * generation and WCAG contrast picking.\n *\n * Everything here is pure and dependency-free: the SDK generates brand ramps at\n * runtime in the browser, so a color library would be a disproportionate cost\n * for ~150 lines of well-specified math. OKLCH is used instead of HSL because\n * HSL lightness is not perceptual — an HSL ramp of a yellow and of a blue at the\n * same `L` read as wildly different brightness, which is exactly what breaks a\n * generated palette.\n */\n\n/** A color in the OKLCH space: perceptual lightness, chroma and hue. */\nexport interface Oklch {\n /** Perceptual lightness, `0` (black) to `1` (white). */\n l: number;\n /** Chroma (colorfulness). `0` is gray; sRGB rarely exceeds `~0.37`. */\n c: number;\n /** Hue angle in degrees, `0`–`360`. */\n h: number;\n}\n\n/** The ten steps of a Tempest tint scale, lightest to darkest. */\nexport type ScaleStep = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;\n\n/** A generated tint scale, keyed by step. */\nexport type ColorScale = Record<ScaleStep, string>;\n\nconst SCALE_STEPS: ScaleStep[] = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900];\n\n/**\n * Target lightness per step for a **brand** ramp in light mode.\n *\n * These are the measured OKLCH lightnesses of the hand-written `--tempest-primary-*`\n * scale in `colors.css`, so a generated brand ramp lands in the same visual range\n * as the built-in one instead of merely near it.\n */\nconst LIGHT_LIGHTNESS: Record<ScaleStep, number> = {\n 50: 0.966,\n 100: 0.923,\n 200: 0.844,\n 300: 0.743,\n 400: 0.642,\n 500: 0.563,\n 600: 0.48,\n 700: 0.391,\n 800: 0.325,\n 900: 0.251,\n};\n\n/**\n * Target lightness per step for a **neutral** ramp in light mode — measured from\n * the hand-written `--tempest-gray-*` scale.\n *\n * Deliberately **wider** than the brand curve at both ends (`0.982` → `0.210` vs\n * `0.966` → `0.251`): a neutral carries surfaces that must read as near-white and\n * text that must read as near-black, and the pair `text-muted` on `surface-3` has\n * to clear AA. Reusing the brand curve for neutrals is what dropped that pair to\n * 3.5:1 and made the browser axe sweep fail on every generated theme.\n */\nconst NEUTRAL_LIGHT_LIGHTNESS: Record<ScaleStep, number> = {\n 50: 0.982,\n 100: 0.963,\n 200: 0.927,\n 300: 0.872,\n 400: 0.71,\n 500: 0.544,\n 600: 0.442,\n 700: 0.369,\n 800: 0.278,\n 900: 0.21,\n};\n\n/**\n * Neutral ramp for dark mode, measured from the `[data-tempest-theme=\"dark\"]`\n * surface/text tokens: `50` is the page background and `900` is text-grade.\n */\nconst NEUTRAL_DARK_LIGHTNESS: Record<ScaleStep, number> = {\n 50: 0.159,\n 100: 0.205,\n 200: 0.254,\n 300: 0.314,\n 400: 0.381,\n 500: 0.5,\n 600: 0.571,\n 700: 0.762,\n 800: 0.86,\n 900: 0.964,\n};\n\n/**\n * Target lightness for a dark-theme ramp — the ramp is **inverted**: `50` is the\n * darkest tint (a surface) and `900` the lightest (text-grade), matching the\n * `[data-tempest-theme=\"dark\"]` block in `colors.css`.\n */\nconst DARK_LIGHTNESS: Record<ScaleStep, number> = {\n 50: 0.238,\n 100: 0.288,\n 200: 0.362,\n 300: 0.452,\n 400: 0.544,\n 500: 0.632,\n 600: 0.712,\n 700: 0.788,\n 800: 0.862,\n 900: 0.928,\n};\n\n/**\n * Chroma multiplier per step, relative to the input color's chroma.\n *\n * Peaks around `500`–`600` and falls off at both ends: near-white and near-black\n * tints hold very little chroma before they look muddy or leave the sRGB gamut.\n */\nconst CHROMA_CURVE: Record<ScaleStep, number> = {\n 50: 0.18,\n 100: 0.34,\n 200: 0.6,\n 300: 0.82,\n 400: 0.95,\n 500: 1,\n 600: 0.98,\n 700: 0.88,\n 800: 0.74,\n 900: 0.58,\n};\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value));\n}\n\n/** Expand `#abc` to `#aabbcc` and normalize to a lowercase 6-digit hex. */\nfunction normalizeHex(hex: string): string {\n const raw = hex.trim().replace(/^#/, \"\");\n const expanded =\n raw.length === 3 || raw.length === 4\n ? raw\n .slice(0, 3)\n .split(\"\")\n .map((char) => char + char)\n .join(\"\")\n : raw.slice(0, 6);\n if (!/^[0-9a-fA-F]{6}$/.test(expanded)) {\n throw new Error(`Invalid hex color: \"${hex}\"`);\n }\n return `#${expanded.toLowerCase()}`;\n}\n\n/** Parse a hex color into sRGB channels in the `0`–`1` range. */\nexport function hexToRgb(hex: string): { r: number; g: number; b: number } {\n const normalized = normalizeHex(hex).slice(1);\n return {\n r: parseInt(normalized.slice(0, 2), 16) / 255,\n g: parseInt(normalized.slice(2, 4), 16) / 255,\n b: parseInt(normalized.slice(4, 6), 16) / 255,\n };\n}\n\n/** Serialize sRGB channels (`0`–`1`, clamped) back to a `#rrggbb` string. */\nexport function rgbToHex(r: number, g: number, b: number): string {\n const channel = (value: number): string =>\n Math.round(clamp(value, 0, 1) * 255)\n .toString(16)\n .padStart(2, \"0\");\n return `#${channel(r)}${channel(g)}${channel(b)}`;\n}\n\nfunction srgbToLinear(channel: number): number {\n return channel <= 0.04045 ? channel / 12.92 : Math.pow((channel + 0.055) / 1.055, 2.4);\n}\n\nfunction linearToSrgb(channel: number): number {\n return channel <= 0.0031308 ? channel * 12.92 : 1.055 * Math.pow(channel, 1 / 2.4) - 0.055;\n}\n\n/** Convert a hex color to OKLCH. */\nexport function hexToOklch(hex: string): Oklch {\n const { r, g, b } = hexToRgb(hex);\n const lr = srgbToLinear(r);\n const lg = srgbToLinear(g);\n const lb = srgbToLinear(b);\n\n const long = Math.cbrt(0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb);\n const medium = Math.cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb);\n const short = Math.cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb);\n\n const l = 0.2104542553 * long + 0.793617785 * medium - 0.0040720468 * short;\n const a = 1.9779984951 * long - 2.428592205 * medium + 0.4505937099 * short;\n const bAxis = 0.0259040371 * long + 0.7827717662 * medium - 0.808675766 * short;\n\n const c = Math.sqrt(a * a + bAxis * bAxis);\n const hue = c < 1e-6 ? 0 : (Math.atan2(bAxis, a) * 180) / Math.PI;\n\n return { l, c, h: hue < 0 ? hue + 360 : hue };\n}\n\n/** Convert OKLCH to linear-light sRGB, without gamut clamping. */\nfunction oklchToLinearRgb({ l, c, h }: Oklch): { r: number; g: number; b: number } {\n const hRad = (h * Math.PI) / 180;\n const a = Math.cos(hRad) * c;\n const bAxis = Math.sin(hRad) * c;\n\n const long = (l + 0.3963377774 * a + 0.2158037573 * bAxis) ** 3;\n const medium = (l - 0.1055613458 * a - 0.0638541728 * bAxis) ** 3;\n const short = (l - 0.0894841775 * a - 1.291485548 * bAxis) ** 3;\n\n return {\n r: 4.0767416621 * long - 3.3077115913 * medium + 0.2309699292 * short,\n g: -1.2684380046 * long + 2.6097574011 * medium - 0.3413193965 * short,\n b: -0.0041960863 * long - 0.7034186147 * medium + 1.707614701 * short,\n };\n}\n\nfunction inGamut({ r, g, b }: { r: number; g: number; b: number }): boolean {\n const epsilon = 1e-4;\n return (\n r >= -epsilon &&\n r <= 1 + epsilon &&\n g >= -epsilon &&\n g <= 1 + epsilon &&\n b >= -epsilon &&\n b <= 1 + epsilon\n );\n}\n\n/**\n * Convert OKLCH to a hex color, reducing chroma until the result fits sRGB.\n *\n * Lightness and hue are preserved: desaturating is far less noticeable than\n * shifting either of them, and a naive channel clamp would do both.\n */\nexport function oklchToHex(color: Oklch): string {\n let chroma = Math.max(0, color.c);\n let rgb = oklchToLinearRgb({ ...color, c: chroma });\n\n for (let i = 0; i < 24 && !inGamut(rgb); i += 1) {\n chroma *= 0.9;\n rgb = oklchToLinearRgb({ ...color, c: chroma });\n }\n\n return rgbToHex(linearToSrgb(rgb.r), linearToSrgb(rgb.g), linearToSrgb(rgb.b));\n}\n\n/** Options for {@link createColorScale}. */\nexport interface ColorScaleOptions {\n /**\n * Pin step `500` to the input color's exact lightness. Default `true`.\n *\n * Right for a **brand** color: the hex a designer hands over is the one the\n * buttons must be. Wrong for a **neutral**: nobody checks that `gray-500` is\n * exactly the input, while everybody notices that a surface stopped being\n * near-white — anchoring rescales both halves around the input and compresses\n * exactly the range a neutral needs wide. With `false`, the ramp keeps the\n * tuned lightness curve and the input only contributes hue and chroma (a warm\n * or cool neutral, as asked).\n */\n anchor?: boolean;\n /**\n * Use the neutral lightness curve instead of the brand one. Default `false`.\n *\n * The neutral curve is wider at both ends, which is what keeps\n * `text-muted`-on-`surface-3` above AA. Implies the ramp is meant for\n * surfaces, borders and text rather than for an action color.\n */\n neutral?: boolean;\n}\n\n/**\n * Build a ten-step tint scale from a single color.\n *\n * The input color's hue is kept throughout and its chroma sets the intensity of\n * the whole ramp, so a muted brand color yields a muted scale instead of being\n * \"corrected\" into something the brand never approved.\n *\n * @param hex - Any hex color (`#abc` or `#aabbcc`), the intended `500` step.\n * @param mode - `\"light\"` for a light→dark ramp, `\"dark\"` for the inverted ramp\n * used under `[data-tempest-theme=\"dark\"]`.\n * @param options - See {@link ColorScaleOptions}.\n */\nexport function createColorScale(\n hex: string,\n mode: \"light\" | \"dark\" = \"light\",\n options: ColorScaleOptions = {},\n): ColorScale {\n const base = hexToOklch(hex);\n const targets = options.neutral\n ? mode === \"dark\"\n ? NEUTRAL_DARK_LIGHTNESS\n : NEUTRAL_LIGHT_LIGHTNESS\n : mode === \"dark\"\n ? DARK_LIGHTNESS\n : LIGHT_LIGHTNESS;\n const lightness =\n options.anchor === false ? targets : anchorLightnessAt500(targets, base.l, mode);\n const scale = {} as ColorScale;\n\n for (const step of SCALE_STEPS) {\n scale[step] = oklchToHex({\n l: lightness[step],\n c: base.c * CHROMA_CURVE[step],\n h: base.h,\n });\n }\n\n return scale;\n}\n\n/** Widest lightness band a generated ramp may span, so both ends stay usable. */\nconst LIGHTNESS_CEILING = 0.985;\nconst LIGHTNESS_FLOOR = 0.12;\n\n/**\n * Re-anchor a lightness ramp so step `500` lands on the brand color exactly.\n *\n * Without this, `500` is forced onto the ramp's own target lightness: a brand\n * `#7c3aed` came back as `#9161fe` — same hue, same chroma, *re-lightened*. It\n * looks fine in isolation and is wrong anyway, because the one color a designer\n * hands over is the one the buttons must actually be.\n *\n * Each half of the ramp is scaled independently around the anchor, so the shape\n * of the original curve survives and the ramp stays monotonic even for a very\n * light brand (yellow) or a very dark one (navy) — those simply get a shorter\n * run on the crowded side.\n *\n * @param targets - The default per-step lightness for this scheme.\n * @param anchor - Lightness of the brand color, used verbatim at step `500`.\n * @param mode - `\"dark\"` inverts which side of the ramp is the light one.\n */\nfunction anchorLightnessAt500(\n targets: Record<ScaleStep, number>,\n anchor: number,\n mode: \"light\" | \"dark\",\n): Record<ScaleStep, number> {\n const anchored = {} as Record<ScaleStep, number>;\n const base = targets[500];\n const lightEnd = mode === \"dark\" ? targets[900] : targets[50];\n const darkEnd = mode === \"dark\" ? targets[50] : targets[900];\n\n const top = Math.min(LIGHTNESS_CEILING, Math.max(lightEnd, anchor + 0.05));\n const bottom = Math.max(LIGHTNESS_FLOOR, Math.min(darkEnd, anchor - 0.05));\n\n for (const step of SCALE_STEPS) {\n const target = targets[step];\n if (target === base) {\n anchored[step] = anchor;\n } else if (target > base) {\n const ratio = (target - base) / (lightEnd - base);\n anchored[step] = anchor + ratio * (top - anchor);\n } else {\n const ratio = (base - target) / (base - darkEnd);\n anchored[step] = anchor - ratio * (anchor - bottom);\n }\n }\n\n return anchored;\n}\n\n/** WCAG 2.x relative luminance of a hex color. */\nexport function relativeLuminance(hex: string): number {\n const { r, g, b } = hexToRgb(hex);\n return 0.2126 * srgbToLinear(r) + 0.7152 * srgbToLinear(g) + 0.0722 * srgbToLinear(b);\n}\n\n/** WCAG 2.x contrast ratio between two hex colors, from `1` to `21`. */\nexport function contrastRatio(a: string, b: string): number {\n const la = relativeLuminance(a);\n const lb = relativeLuminance(b);\n const lighter = Math.max(la, lb);\n const darker = Math.min(la, lb);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\n/**\n * Pick the readable foreground for a background, by contrast ratio.\n *\n * Used for `--tempest-primary-foreground`: a generated brand color can land\n * anywhere on the lightness axis, and hardcoding white would silently produce\n * unreadable buttons for light brands (yellow, lime, cyan).\n */\nexport function readableForeground(\n background: string,\n light = \"#ffffff\",\n dark = \"#101828\",\n): string {\n return contrastRatio(background, light) >= contrastRatio(background, dark) ? light : dark;\n}\n\n/** Format a hex color as `rgb(r g b / alpha)`, for focus rings and overlays. */\nexport function hexToRgbaString(hex: string, alpha: number): string {\n const { r, g, b } = hexToRgb(hex);\n const channel = (value: number): number => Math.round(clamp(value, 0, 1) * 255);\n return `rgb(${channel(r)} ${channel(g)} ${channel(b)} / ${clamp(alpha, 0, 1)})`;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
const e=require("./color.cjs");var t={none:{xs:`0`,sm:`0`,md:`0`,lg:`0`,xl:`0`,"2xl":`0`},sm:{xs:`1px`,sm:`2px`,md:`4px`,lg:`6px`,xl:`8px`,"2xl":`12px`},md:{xs:`2px`,sm:`4px`,md:`8px`,lg:`12px`,xl:`16px`,"2xl":`24px`},lg:{xs:`4px`,sm:`6px`,md:`12px`,lg:`16px`,xl:`22px`,"2xl":`32px`},xl:{xs:`6px`,sm:`10px`,md:`16px`,lg:`24px`,xl:`32px`,"2xl":`44px`},full:{xs:`4px`,sm:`8px`,md:`9999px`,lg:`9999px`,xl:`9999px`,"2xl":`9999px`}},n=[50,100,200,300,400,500,600,700,800,900];function r(e,t,r){for(let i of n)e[`--tempest-${t}-${i}`]=r[i]}var i=4.5;function a(t,n,r){for(let a of r)if(e.contrastRatio(t[n],t[a])>=i)return a;return r[r.length-1]}function o(t,n,r,i){t[`--tempest-primary`]=`var(--tempest-primary-500)`,r===`light`?(t[`--tempest-primary-hover`]=`var(--tempest-primary-600)`,t[`--tempest-primary-active`]=`var(--tempest-primary-700)`,t[`--tempest-primary-soft`]=`var(--tempest-primary-50)`,t[`--tempest-primary-soft-hover`]=`var(--tempest-primary-100)`,t[`--tempest-primary-on-soft`]=`var(--tempest-primary-${a(n,50,[600,700,800,900])})`):(t[`--tempest-primary-hover`]=`var(--tempest-primary-400)`,t[`--tempest-primary-active`]=`var(--tempest-primary-300)`,t[`--tempest-primary-soft`]=`var(--tempest-primary-100)`,t[`--tempest-primary-soft-hover`]=`var(--tempest-primary-200)`,t[`--tempest-primary-on-soft`]=`var(--tempest-primary-${a(n,100,[700,800,900])})`);let o=e.readableForeground(n[500]);t[`--tempest-primary-foreground`]=o,t[`--tempest-text-on-primary`]=o,t[`--tempest-focus-ring-color`]=e.hexToRgbaString(n[500],i)}function s(e,t,n){n===`light`?(e[`--tempest-bg`]=`#ffffff`,e[`--tempest-surface`]=`var(--tempest-gray-50)`,e[`--tempest-surface-2`]=`var(--tempest-gray-100)`,e[`--tempest-surface-3`]=`var(--tempest-gray-200)`,e[`--tempest-border`]=`var(--tempest-gray-200)`,e[`--tempest-border-strong`]=`var(--tempest-gray-300)`,e[`--tempest-text`]=`var(--tempest-gray-900)`,e[`--tempest-text-muted`]=`var(--tempest-gray-600)`,e[`--tempest-text-subtle`]=`var(--tempest-gray-500)`):(e[`--tempest-bg`]=t[50],e[`--tempest-surface`]=`var(--tempest-gray-100)`,e[`--tempest-surface-2`]=`var(--tempest-gray-200)`,e[`--tempest-surface-3`]=`var(--tempest-gray-300)`,e[`--tempest-border`]=`var(--tempest-gray-300)`,e[`--tempest-border-strong`]=`var(--tempest-gray-400)`,e[`--tempest-text`]=`var(--tempest-gray-900)`,e[`--tempest-text-muted`]=`var(--tempest-gray-700)`,e[`--tempest-text-subtle`]=`var(--tempest-gray-600)`)}function c(e,t,n,r){r===`light`?(e[`--tempest-${t}`]=n[700],e[`--tempest-${t}-fg`]=n[800],e[`--tempest-${t}-bg`]=n[50],e[`--tempest-${t}-border`]=n[200],e[`--tempest-${t}-solid`]=n[600]):(e[`--tempest-${t}`]=n[700],e[`--tempest-${t}-fg`]=n[700],e[`--tempest-${t}-bg`]=n[50],e[`--tempest-${t}-border`]=n[200],e[`--tempest-${t}-solid`]=n[500])}function l(e,t){let n=Object.entries(t);return n.length===0?``:`${e} {\n${n.map(([e,t])=>` ${e}: ${t};`).join(`
|
|
2
|
+
`)}\n}`}function u(n={}){let{primary:i,gray:a,chart:u,radius:d,focusRingAlpha:f=.35,selector:p=`:root`,darkSelector:m=`[data-tempest-theme="dark"]`}=n,h={},g={};if(i){let t=e.createColorScale(i,`light`),n=e.createColorScale(i,`dark`);r(h,`primary`,t),r(g,`primary`,n),o(h,t,`light`,f),o(g,n,`dark`,f)}if(a){let t=e.createColorScale(a,`light`,{anchor:!1,neutral:!0}),n=e.createColorScale(a,`dark`,{anchor:!1,neutral:!0});r(h,`gray`,t),r(g,`gray`,n),s(h,t,`light`),s(g,n,`dark`)}for(let t of[`success`,`warning`,`danger`,`info`]){let r=n[t];r&&(c(h,t,e.createColorScale(r,`light`),`light`),c(g,t,e.createColorScale(r,`dark`),`dark`))}if(u?.length&&(u.forEach((e,t)=>{h[`--tempest-chart-${t+1}`]=e}),h[`--tempest-chart-count`]=String(u.length)),d){let e=typeof d==`string`?t[d]:d;for(let[t,n]of Object.entries(e))h[`--tempest-radius-${t}`]=n}return{light:h,dark:g,css:[l(p,h),l(m,g)].filter(Boolean).join(`
|
|
3
|
+
|
|
4
|
+
`)}}function d(t){if(!t.primary)return null;let n=e.createColorScale(t.primary,`light`);return e.contrastRatio(n[500],e.readableForeground(n[500]))}exports.createTheme=u,exports.themeContrast=d;
|
|
5
|
+
//# sourceMappingURL=create-theme.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-theme.cjs","names":[],"sources":["../../src/theme/create-theme.ts"],"sourcesContent":["/**\n * Theme factory: turns a handful of brand colors into the full set of\n * `--tempest-*` token overrides, for both light and dark.\n *\n * Rebranding used to mean hand-writing ~167 custom properties (and getting the\n * dark ramp inversion right by hand). `createTheme({ primary: \"#7c3aed\" })`\n * emits the same thing, derived in OKLCH so the ramp stays perceptually even.\n */\nimport {\n contrastRatio,\n createColorScale,\n hexToRgbaString,\n readableForeground,\n type ColorScale,\n type ScaleStep,\n} from \"./color\";\n\n/** Radius presets, applied to the whole `--tempest-radius-*` family at once. */\nexport type ThemeRadius = \"none\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"full\";\n\n/** Status token families that {@link createTheme} can regenerate. */\nexport type ThemeStatus = \"success\" | \"warning\" | \"danger\" | \"info\";\n\n/** Input for {@link createTheme}. Every field is optional — omitted families keep the built-in tokens. */\nexport interface CreateThemeOptions {\n /** Brand color, used as the `500` step of the primary scale. */\n primary?: string;\n /** Neutral color, used as the `500` step of the gray scale (surfaces, borders, text). */\n gray?: string;\n /** Success color (`--tempest-success*`). */\n success?: string;\n /** Warning color (`--tempest-warning*`). */\n warning?: string;\n /** Danger color (`--tempest-danger*`). */\n danger?: string;\n /** Info color (`--tempest-info*`). */\n info?: string;\n /**\n * Categorical series colors for `tempest-react-sdk/charts`, in cycle order.\n * Written to `--tempest-chart-1` … `--tempest-chart-N`, so charts follow the\n * theme instead of a hardcoded palette.\n */\n chart?: string[];\n /** Corner radius scale. A preset name, or explicit per-step values. */\n radius?: ThemeRadius | Partial<Record<\"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | \"2xl\", string>>;\n /** Alpha of `--tempest-focus-ring-color`, derived from the brand color. Default `0.35`. */\n focusRingAlpha?: number;\n /** Selector the light tokens are written under. Default `\":root\"`. */\n selector?: string;\n /** Selector the dark tokens are written under. Default `'[data-tempest-theme=\"dark\"]'`. */\n darkSelector?: string;\n}\n\n/** A generated theme: token maps per color scheme, plus the CSS text that carries them. */\nexport interface GeneratedTheme {\n /** Light-scheme custom properties, without the leading `--`-less names (keys include `--`). */\n light: Record<string, string>;\n /** Dark-scheme custom properties. */\n dark: Record<string, string>;\n /** Both blocks rendered as CSS, ready for {@link applyTheme} or a stylesheet. */\n css: string;\n}\n\nconst RADIUS_PRESETS: Record<ThemeRadius, Record<string, string>> = {\n none: { xs: \"0\", sm: \"0\", md: \"0\", lg: \"0\", xl: \"0\", \"2xl\": \"0\" },\n sm: { xs: \"1px\", sm: \"2px\", md: \"4px\", lg: \"6px\", xl: \"8px\", \"2xl\": \"12px\" },\n md: { xs: \"2px\", sm: \"4px\", md: \"8px\", lg: \"12px\", xl: \"16px\", \"2xl\": \"24px\" },\n lg: { xs: \"4px\", sm: \"6px\", md: \"12px\", lg: \"16px\", xl: \"22px\", \"2xl\": \"32px\" },\n xl: { xs: \"6px\", sm: \"10px\", md: \"16px\", lg: \"24px\", xl: \"32px\", \"2xl\": \"44px\" },\n full: { xs: \"4px\", sm: \"8px\", md: \"9999px\", lg: \"9999px\", xl: \"9999px\", \"2xl\": \"9999px\" },\n};\n\nconst SCALE_STEPS: ScaleStep[] = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900];\n\nfunction writeScale(tokens: Record<string, string>, name: string, scale: ColorScale): void {\n for (const step of SCALE_STEPS) {\n tokens[`--tempest-${name}-${step}`] = scale[step];\n }\n}\n\n/** Minimum contrast for body text, WCAG 2.x AA. */\nconst AA_TEXT_CONTRAST = 4.5;\n\n/**\n * Choose the ramp step for text sitting on the soft tint.\n *\n * A fixed step does not survive an arbitrary brand color: the built-in blue only\n * reaches 4.37:1 at `500` over its own `50` tint, and a generated emerald lands\n * at 4.41:1 at `600` — both fail AA for body text by a hair. So the step is\n * picked by measuring, walking away from the tint until it clears AA, and\n * falling back to the most extreme candidate when even that cannot (a very light\n * or very desaturated brand).\n *\n * @param scale - The generated ramp.\n * @param softStep - Step used as `--tempest-primary-soft`.\n * @param candidates - Steps to try, in order of preference.\n * @returns The first candidate clearing {@link AA_TEXT_CONTRAST}, else the last.\n */\nfunction pickOnSoftStep(\n scale: ColorScale,\n softStep: ScaleStep,\n candidates: ScaleStep[],\n): ScaleStep {\n for (const step of candidates) {\n if (contrastRatio(scale[softStep], scale[step]) >= AA_TEXT_CONTRAST) return step;\n }\n return candidates[candidates.length - 1];\n}\n\n/**\n * Emit the primary aliases for one scheme.\n *\n * The dark scheme walks the ramp the other way (hover is *lighter*, the soft\n * tint is a dark shade, and readable text on that tint is a light shade) — the\n * same inversion the built-in `colors.css` dark block does by hand.\n */\nfunction writePrimaryAliases(\n tokens: Record<string, string>,\n scale: ColorScale,\n scheme: \"light\" | \"dark\",\n focusRingAlpha: number,\n): void {\n tokens[\"--tempest-primary\"] = \"var(--tempest-primary-500)\";\n if (scheme === \"light\") {\n tokens[\"--tempest-primary-hover\"] = \"var(--tempest-primary-600)\";\n tokens[\"--tempest-primary-active\"] = \"var(--tempest-primary-700)\";\n tokens[\"--tempest-primary-soft\"] = \"var(--tempest-primary-50)\";\n tokens[\"--tempest-primary-soft-hover\"] = \"var(--tempest-primary-100)\";\n tokens[\"--tempest-primary-on-soft\"] =\n `var(--tempest-primary-${pickOnSoftStep(scale, 50, [600, 700, 800, 900])})`;\n } else {\n tokens[\"--tempest-primary-hover\"] = \"var(--tempest-primary-400)\";\n tokens[\"--tempest-primary-active\"] = \"var(--tempest-primary-300)\";\n tokens[\"--tempest-primary-soft\"] = \"var(--tempest-primary-100)\";\n tokens[\"--tempest-primary-soft-hover\"] = \"var(--tempest-primary-200)\";\n tokens[\"--tempest-primary-on-soft\"] =\n `var(--tempest-primary-${pickOnSoftStep(scale, 100, [700, 800, 900])})`;\n }\n\n const foreground = readableForeground(scale[500]);\n tokens[\"--tempest-primary-foreground\"] = foreground;\n tokens[\"--tempest-text-on-primary\"] = foreground;\n tokens[\"--tempest-focus-ring-color\"] = hexToRgbaString(scale[500], focusRingAlpha);\n}\n\n/**\n * Emit the neutral surface/border/text aliases from a generated gray scale.\n *\n * `--tempest-bg` is pushed past the ramp on purpose: pure white in light mode and\n * a shade darker than `gray-50` in dark mode, so a raised surface still reads as\n * raised against the page.\n */\nfunction writeNeutralAliases(\n tokens: Record<string, string>,\n scale: ColorScale,\n scheme: \"light\" | \"dark\",\n): void {\n if (scheme === \"light\") {\n tokens[\"--tempest-bg\"] = \"#ffffff\";\n tokens[\"--tempest-surface\"] = \"var(--tempest-gray-50)\";\n tokens[\"--tempest-surface-2\"] = \"var(--tempest-gray-100)\";\n tokens[\"--tempest-surface-3\"] = \"var(--tempest-gray-200)\";\n tokens[\"--tempest-border\"] = \"var(--tempest-gray-200)\";\n tokens[\"--tempest-border-strong\"] = \"var(--tempest-gray-300)\";\n tokens[\"--tempest-text\"] = \"var(--tempest-gray-900)\";\n tokens[\"--tempest-text-muted\"] = \"var(--tempest-gray-600)\";\n tokens[\"--tempest-text-subtle\"] = \"var(--tempest-gray-500)\";\n } else {\n tokens[\"--tempest-bg\"] = scale[50];\n tokens[\"--tempest-surface\"] = \"var(--tempest-gray-100)\";\n tokens[\"--tempest-surface-2\"] = \"var(--tempest-gray-200)\";\n tokens[\"--tempest-surface-3\"] = \"var(--tempest-gray-300)\";\n tokens[\"--tempest-border\"] = \"var(--tempest-gray-300)\";\n tokens[\"--tempest-border-strong\"] = \"var(--tempest-gray-400)\";\n tokens[\"--tempest-text\"] = \"var(--tempest-gray-900)\";\n tokens[\"--tempest-text-muted\"] = \"var(--tempest-gray-700)\";\n tokens[\"--tempest-text-subtle\"] = \"var(--tempest-gray-600)\";\n }\n}\n\n/**\n * Emit one status family (`--tempest-danger`, `-fg`, `-bg`, `-border`, `-solid`).\n *\n * `-fg` is the text shade over `-bg`, so it has to cross the ramp in opposite\n * directions per scheme; `-solid` stays the saturated fill used by badges.\n */\nfunction writeStatus(\n tokens: Record<string, string>,\n name: ThemeStatus,\n scale: ColorScale,\n scheme: \"light\" | \"dark\",\n): void {\n if (scheme === \"light\") {\n tokens[`--tempest-${name}`] = scale[700];\n tokens[`--tempest-${name}-fg`] = scale[800];\n tokens[`--tempest-${name}-bg`] = scale[50];\n tokens[`--tempest-${name}-border`] = scale[200];\n tokens[`--tempest-${name}-solid`] = scale[600];\n } else {\n tokens[`--tempest-${name}`] = scale[700];\n tokens[`--tempest-${name}-fg`] = scale[700];\n tokens[`--tempest-${name}-bg`] = scale[50];\n tokens[`--tempest-${name}-border`] = scale[200];\n tokens[`--tempest-${name}-solid`] = scale[500];\n }\n}\n\nfunction renderBlock(selector: string, tokens: Record<string, string>): string {\n const entries = Object.entries(tokens);\n if (entries.length === 0) return \"\";\n const body = entries.map(([name, value]) => ` ${name}: ${value};`).join(\"\\n\");\n return `${selector} {\\n${body}\\n}`;\n}\n\n/**\n * Generate `--tempest-*` overrides from a small brand description.\n *\n * Only the families you pass are generated; everything else falls through to the\n * SDK's own tokens, so a theme stays a patch and not a fork of `colors.css`.\n *\n * @example\n * ```ts\n * import { applyTheme, createTheme } from \"tempest-react-sdk\";\n *\n * const theme = createTheme({\n * primary: \"#7c3aed\",\n * radius: \"lg\",\n * chart: [\"#7c3aed\", \"#0ea5e9\", \"#22c55e\", \"#f59e0b\"],\n * });\n *\n * applyTheme(theme);\n * ```\n *\n * @param options - Brand colors plus optional radius / chart / focus-ring tuning.\n * @returns The light and dark token maps and the CSS text that carries them.\n */\nexport function createTheme(options: CreateThemeOptions = {}): GeneratedTheme {\n const {\n primary,\n gray,\n chart,\n radius,\n focusRingAlpha = 0.35,\n selector = \":root\",\n darkSelector = '[data-tempest-theme=\"dark\"]',\n } = options;\n\n const light: Record<string, string> = {};\n const dark: Record<string, string> = {};\n\n if (primary) {\n const lightScale = createColorScale(primary, \"light\");\n const darkScale = createColorScale(primary, \"dark\");\n writeScale(light, \"primary\", lightScale);\n writeScale(dark, \"primary\", darkScale);\n writePrimaryAliases(light, lightScale, \"light\", focusRingAlpha);\n writePrimaryAliases(dark, darkScale, \"dark\", focusRingAlpha);\n }\n\n if (gray) {\n // `anchor: false`: a neutral ramp has to keep its tuned lightness curve.\n // Anchoring it at the input compressed both halves and dropped\n // text-muted-on-surface-3 to ~4.2:1 — below AA — which the browser axe\n // sweep caught on the generated themes.\n const lightScale = createColorScale(gray, \"light\", { anchor: false, neutral: true });\n const darkScale = createColorScale(gray, \"dark\", { anchor: false, neutral: true });\n writeScale(light, \"gray\", lightScale);\n writeScale(dark, \"gray\", darkScale);\n writeNeutralAliases(light, lightScale, \"light\");\n writeNeutralAliases(dark, darkScale, \"dark\");\n }\n\n for (const status of [\"success\", \"warning\", \"danger\", \"info\"] as const) {\n const value = options[status];\n if (!value) continue;\n writeStatus(light, status, createColorScale(value, \"light\"), \"light\");\n writeStatus(dark, status, createColorScale(value, \"dark\"), \"dark\");\n }\n\n if (chart?.length) {\n chart.forEach((color, index) => {\n light[`--tempest-chart-${index + 1}`] = color;\n });\n // Declares how many series colors this theme owns. Without it the reader\n // keeps walking into the SDK's built-in `--tempest-chart-7`/`-8`, so a\n // 6-color brand palette silently mixes with two leftover defaults — which\n // is exactly what a 7-series chart would show.\n light[\"--tempest-chart-count\"] = String(chart.length);\n }\n\n if (radius) {\n const steps = typeof radius === \"string\" ? RADIUS_PRESETS[radius] : radius;\n for (const [step, value] of Object.entries(steps)) {\n light[`--tempest-radius-${step}`] = value;\n }\n }\n\n const css = [renderBlock(selector, light), renderBlock(darkSelector, dark)]\n .filter(Boolean)\n .join(\"\\n\\n\");\n\n return { light, dark, css };\n}\n\n/**\n * Contrast ratio of `--tempest-primary-foreground` over the brand color.\n *\n * Exposed so an app (or a test) can assert its own brand clears WCAG AA (4.5) for\n * body text or AA-large (3.0) for button labels, instead of trusting the pick.\n *\n * @param options - The same input given to {@link createTheme}.\n * @returns The ratio, or `null` when no `primary` was provided.\n */\nexport function themeContrast(options: CreateThemeOptions): number | null {\n if (!options.primary) return null;\n const scale = createColorScale(options.primary, \"light\");\n return contrastRatio(scale[500], readableForeground(scale[500]));\n}\n"],"mappings":"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"}
|