@heyramzi/ui 1.54.2 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMPONENTS.md +3 -2
- package/README.md +34 -9
- package/package.json +205 -13
- package/scripts/sync-shadcn.mjs +2 -0
- package/src/ai/generation-progress/GenerationProgress.svelte +12 -12
- package/src/ambient.d.ts +8 -0
- package/src/brand/brand.test.ts +67 -0
- package/src/brand/index.ts +137 -0
- package/src/canvas/README.md +236 -0
- package/src/canvas/card/card-frame.svelte +61 -0
- package/src/canvas/card/card.css +39 -0
- package/src/canvas/card/resize-handles.svelte +119 -0
- package/src/canvas/card/resize.ts +33 -0
- package/src/canvas/chrome/chrome-zoom.svelte +120 -0
- package/src/canvas/chrome/chrome.css +231 -0
- package/src/canvas/chrome/lock-badge.svelte +18 -0
- package/src/canvas/chrome/side-handles.svelte +30 -0
- package/src/canvas/chrome/size-chip.svelte +45 -0
- package/src/canvas/chrome/snap-guides.svelte +69 -0
- package/src/canvas/components/icons/ToolIcon.svelte +30 -0
- package/src/canvas/components/shared/svg/PreviewSvg.svelte +67 -0
- package/src/canvas/components/shared/svg/ShapeOutline.svelte +76 -0
- package/src/canvas/components/shared/svg/StrokeNode.svelte +69 -0
- package/src/canvas/components/shared/svg/TextRule.svelte +37 -0
- package/src/canvas/edges/bend-handle.svelte +76 -0
- package/src/canvas/edges/edge-gradient.svelte +30 -0
- package/src/canvas/edges/edge-path.test.ts +58 -0
- package/src/canvas/edges/edge-path.ts +293 -0
- package/src/canvas/edges/gradient-edge.svelte +43 -0
- package/src/canvas/editor/context-menu.test.ts +65 -0
- package/src/canvas/editor/context-menu.ts +48 -0
- package/src/canvas/editor/dispatch.svelte.test.ts +243 -0
- package/src/canvas/editor/dispatch.svelte.ts +205 -0
- package/src/canvas/editor/editor-context.ts +62 -0
- package/src/canvas/editor/editor-mode.ts +67 -0
- package/src/canvas/editor/frame.ts +98 -0
- package/src/canvas/editor/hand-context.ts +15 -0
- package/src/canvas/editor/history-core.test.ts +239 -0
- package/src/canvas/editor/history-core.ts +247 -0
- package/src/canvas/editor/keymap.test.ts +161 -0
- package/src/canvas/editor/keymap.ts +151 -0
- package/src/canvas/editor/layout-actions.test.ts +156 -0
- package/src/canvas/editor/layout-actions.ts +219 -0
- package/src/canvas/editor/lock.test.ts +68 -0
- package/src/canvas/editor/lock.ts +87 -0
- package/src/canvas/editor/rebase.test.ts +208 -0
- package/src/canvas/editor/rebase.ts +272 -0
- package/src/canvas/editor/style.ts +184 -0
- package/src/canvas/editor/text-field-context.ts +41 -0
- package/src/canvas/editor/z-order.test.ts +75 -0
- package/src/canvas/editor/z-order.ts +87 -0
- package/src/canvas/headless/alignment.test.ts +59 -0
- package/src/canvas/headless/alignment.ts +23 -0
- package/src/canvas/headless/break-apart-find.ts +418 -0
- package/src/canvas/headless/break-apart-pieces.ts +104 -0
- package/src/canvas/headless/break-apart-plan.ts +439 -0
- package/src/canvas/headless/break-apart-style.ts +106 -0
- package/src/canvas/headless/break-apart.ts +244 -0
- package/src/canvas/headless/canvas-cleanup.test.ts +181 -0
- package/src/canvas/headless/canvas-cleanup.ts +102 -0
- package/src/canvas/headless/edge-anchors.test.ts +70 -0
- package/src/canvas/headless/edge-anchors.ts +143 -0
- package/src/canvas/headless/edge-memo.test.ts +58 -0
- package/src/canvas/headless/edge-memo.ts +32 -0
- package/src/canvas/headless/edge-serialization.test.ts +55 -0
- package/src/canvas/headless/edge-serialization.ts +32 -0
- package/src/canvas/headless/figure-geometry.ts +293 -0
- package/src/canvas/headless/fit-viewport.test.ts +85 -0
- package/src/canvas/headless/fit-viewport.ts +137 -0
- package/src/canvas/headless/grid.test.ts +34 -0
- package/src/canvas/headless/grid.ts +94 -0
- package/src/canvas/headless/icon-node.test.ts +23 -0
- package/src/canvas/headless/icon-node.ts +66 -0
- package/src/canvas/headless/index.ts +9 -0
- package/src/canvas/headless/linkify.test.ts +23 -0
- package/src/canvas/headless/linkify.ts +25 -0
- package/src/canvas/headless/pen-marks.ts +133 -0
- package/src/canvas/headless/sketch.test.ts +99 -0
- package/src/canvas/headless/sketch.ts +302 -0
- package/src/canvas/headless/snap-guides.test.ts +61 -0
- package/src/canvas/headless/snap-guides.ts +146 -0
- package/src/canvas/headless/svg-figure.test.ts +49 -0
- package/src/canvas/headless/svg-figure.ts +176 -0
- package/src/canvas/headless/table-layout.test.ts +158 -0
- package/src/canvas/headless/table-layout.ts +309 -0
- package/src/canvas/index.ts +35 -0
- package/src/canvas/lod/lod-mode.svelte.ts +30 -0
- package/src/canvas/lod/lod-switch.svelte +140 -0
- package/src/canvas/lod/render-all.svelte.ts +52 -0
- package/src/canvas/ops/arrange.ts +108 -0
- package/src/canvas/ops/ops.test.ts +98 -0
- package/src/canvas/ops/ops.ts +347 -0
- package/src/canvas/pen/PenField.svelte +128 -0
- package/src/canvas/pen/PenField.test.ts +87 -0
- package/src/canvas/pen/PenOverlay.svelte +172 -0
- package/src/canvas/pen/ShapeNode.svelte +64 -0
- package/src/canvas/pen/TextNode.svelte +97 -0
- package/src/canvas/pen/colors.ts +28 -0
- package/src/canvas/pen/eraser.ts +46 -0
- package/src/canvas/pen/geometry.ts +148 -0
- package/src/canvas/pen/host-field.test.svelte +13 -0
- package/src/canvas/pen/index.ts +40 -0
- package/src/canvas/pen/ops.ts +16 -0
- package/src/canvas/pen/pen-session.ts +177 -0
- package/src/canvas/pen/pen.css +149 -0
- package/src/canvas/pen/plugin.ts +139 -0
- package/src/canvas/pen/shapes.ts +104 -0
- package/src/canvas/pen/stroke-path.ts +54 -0
- package/src/canvas/pen/types.ts +96 -0
- package/src/canvas/pen/ui.ts +26 -0
- package/src/canvas/plugin.test.ts +45 -0
- package/src/canvas/plugin.ts +159 -0
- package/src/canvas/screen/GlyphIcon.svelte +25 -0
- package/src/canvas/screen/PartView.svelte +176 -0
- package/src/canvas/screen/ScreenBody.svelte +142 -0
- package/src/canvas/screen/ScreenCard.svelte +32 -0
- package/src/canvas/screen/glyphs/button.svg +1 -0
- package/src/canvas/screen/glyphs/close.svg +1 -0
- package/src/canvas/screen/glyphs/down.svg +1 -0
- package/src/canvas/screen/glyphs/header.svg +1 -0
- package/src/canvas/screen/glyphs/hills.svg +1 -0
- package/src/canvas/screen/glyphs/image.svg +1 -0
- package/src/canvas/screen/glyphs/input.svg +1 -0
- package/src/canvas/screen/glyphs/list.svg +1 -0
- package/src/canvas/screen/glyphs/modal.svg +1 -0
- package/src/canvas/screen/glyphs/plus.svg +1 -0
- package/src/canvas/screen/glyphs/up.svg +1 -0
- package/src/canvas/screen/glyphs.ts +40 -0
- package/src/canvas/screen/labels.ts +41 -0
- package/src/canvas/screen/parts.ts +164 -0
- package/src/canvas/screen/plugin.ts +67 -0
- package/src/canvas/screen/screen.css +593 -0
- package/src/canvas/screen/types.ts +47 -0
- package/src/canvas/screen/ui.ts +13 -0
- package/src/canvas/test-utils.ts +18 -0
- package/src/canvas/types.ts +53 -0
- package/src/canvas/ui/CanvasToolbar.svelte +169 -0
- package/src/canvas/ui/FrameLayer.svelte +116 -0
- package/src/canvas/ui/PenLayer.svelte +39 -0
- package/src/canvas/ui/StylePanel.svelte +268 -0
- package/src/canvas/ui/canvas-grid.svelte +156 -0
- package/src/canvas/ui/canvas-ui.css +350 -0
- package/src/canvas/ui/forward-wheel.ts +29 -0
- package/src/canvas/ui/icons.ts +124 -0
- package/src/canvas/ui/place-panel.ts +80 -0
- package/src/canvas/ui/shortcut-tip.test.ts +16 -0
- package/src/canvas/ui/shortcut-tip.ts +17 -0
- package/src/canvas/ui/tools.ts +146 -0
- package/src/charts/README.md +104 -0
- package/src/charts/ambient.d.ts +4 -0
- package/src/charts/animated-value.css +7 -0
- package/src/charts/animated-value.svelte +110 -0
- package/src/charts/area-chart.css +29 -0
- package/src/charts/area-chart.svelte +204 -0
- package/src/charts/bar-chart.css +119 -0
- package/src/charts/bar-chart.svelte +275 -0
- package/src/charts/charts.css +201 -0
- package/src/charts/crosshair.svelte +35 -0
- package/src/charts/dashboard/chart.ts +5 -0
- package/src/charts/dashboard/dashboard-empty.svelte +55 -0
- package/src/charts/dashboard/dashboard-section.svelte +58 -0
- package/src/charts/dashboard/data-panel.svelte +81 -0
- package/src/charts/dashboard/data-sort-head.svelte +102 -0
- package/src/charts/dashboard/data-table.svelte +278 -0
- package/src/charts/dashboard/data-table.ts +26 -0
- package/src/charts/dashboard/index.ts +25 -0
- package/src/charts/dashboard/panel-grid.svelte +43 -0
- package/src/charts/dashboard/period-filter.svelte +53 -0
- package/src/charts/dashboard/rating-chip.svelte +35 -0
- package/src/charts/dashboard/stat-band.svelte +47 -0
- package/src/charts/dashboard/stat-tile.svelte +176 -0
- package/src/charts/dashboard/windows.ts +8 -0
- package/src/charts/donut-chart.css +56 -0
- package/src/charts/donut-chart.svelte +158 -0
- package/src/charts/edge-fade.svelte +43 -0
- package/src/charts/edge-fade.ts +16 -0
- package/src/charts/figure.css +100 -0
- package/src/charts/figure.svelte +122 -0
- package/src/charts/gauge-chart.css +94 -0
- package/src/charts/gauge-chart.svelte +302 -0
- package/src/charts/hover-band.test.ts +30 -0
- package/src/charts/hover-band.ts +21 -0
- package/src/charts/hover-focus.svelte +85 -0
- package/src/charts/index.ts +31 -0
- package/src/charts/legend.css +136 -0
- package/src/charts/legend.svelte +112 -0
- package/src/charts/line-chart.css +26 -0
- package/src/charts/line-chart.svelte +256 -0
- package/src/charts/motion.ts +19 -0
- package/src/charts/number-chart.css +179 -0
- package/src/charts/number-chart.svelte +187 -0
- package/src/charts/primitives/chart-container.svelte +57 -0
- package/src/charts/primitives/chart-style.svelte +36 -0
- package/src/charts/primitives/chart-tooltip.svelte +161 -0
- package/src/charts/primitives/chart-utils.ts +54 -0
- package/src/charts/primitives/index.ts +11 -0
- package/src/charts/reference-line.css +18 -0
- package/src/charts/reference-line.svelte +31 -0
- package/src/charts/reveal.svelte +61 -0
- package/src/charts/ring-chart.css +104 -0
- package/src/charts/ring-chart.svelte +166 -0
- package/src/charts/sparkline.css +45 -0
- package/src/charts/sparkline.svelte +141 -0
- package/src/charts/stat-card.css +105 -0
- package/src/charts/stat-card.svelte +267 -0
- package/src/charts/time-series-chart.css +28 -0
- package/src/charts/time-series-chart.svelte +179 -0
- package/src/charts/trend-pill.css +38 -0
- package/src/charts/trend-pill.svelte +48 -0
- package/src/components/copy/copy-button.svelte +1 -1
- package/src/components/docs/doc-page.svelte +1 -1
- package/src/components/docs/on-this-page.svelte +2 -2
- package/src/components/list-page/empty-state.svelte +1 -1
- package/src/components/list-page/filter-popover.svelte +1 -1
- package/src/components/list-page/section-label.svelte +1 -1
- package/src/components/segmented/segment.svelte +4 -4
- package/src/components/theme/theme-provider.svelte +6 -4
- package/src/components/toolbar/index.ts +5 -0
- package/src/components/toolbar/toolbar.svelte +168 -0
- package/src/components/toolbar/toolbar.test.ts +66 -0
- package/src/editor/README.md +58 -0
- package/src/editor/ambient.d.ts +1 -0
- package/src/editor/block-id.ts +77 -0
- package/src/editor/block-ops.test.ts +237 -0
- package/src/editor/block-ops.ts +157 -0
- package/src/editor/content.css +418 -0
- package/src/editor/editor-toolbar.svelte +303 -0
- package/src/editor/editor.svelte +469 -0
- package/src/editor/extensions.ts +145 -0
- package/src/editor/image-toolbar.svelte +123 -0
- package/src/editor/index.ts +17 -0
- package/src/editor/inline-editor.svelte +261 -0
- package/src/editor/markdown-escaping.test.ts +158 -0
- package/src/editor/markdown-marks.test.ts +89 -0
- package/src/editor/markdown-test-helpers.ts +13 -0
- package/src/editor/markdown-youtube.test.ts +93 -0
- package/src/editor/markdown.test.ts +250 -0
- package/src/editor/markdown.ts +511 -0
- package/src/editor/renderer.svelte +92 -0
- package/src/editor/serializer.test.ts +133 -0
- package/src/editor/serializer.ts +370 -0
- package/src/editor/slash-items.ts +115 -0
- package/src/editor/slash-menu.svelte +159 -0
- package/src/editor/youtube-url.ts +35 -0
- package/src/effects/interactive-hover-button/InteractiveHoverButton.svelte +13 -13
- package/src/gallery.test.ts +20 -1
- package/src/lib/result.ts +19 -0
- package/src/onboarding/README.md +120 -0
- package/src/onboarding/define-tours.test.ts +50 -0
- package/src/onboarding/define-tours.ts +31 -0
- package/src/onboarding/demo.svelte +86 -0
- package/src/onboarding/index.ts +12 -0
- package/src/onboarding/onboarding-provider.svelte +120 -0
- package/src/onboarding/onboarding.svelte.ts +24 -0
- package/src/onboarding/pick.test.ts +79 -0
- package/src/onboarding/pick.ts +36 -0
- package/src/onboarding/stores.test.ts +103 -0
- package/src/onboarding/stores.ts +58 -0
- package/src/onboarding/tour-card.svelte +70 -0
- package/src/onboarding/tour-runner.svelte +197 -0
- package/src/onboarding/types.ts +34 -0
- package/src/onboarding/wait.ts +29 -0
- package/src/shadcn/command/command-input.svelte +1 -1
- package/src/slides/Controls.svelte +138 -0
- package/src/slides/Deck.svelte +271 -0
- package/src/slides/Fragment.svelte +71 -0
- package/src/slides/Overview.svelte +113 -0
- package/src/slides/Presenter.svelte +292 -0
- package/src/slides/README.md +89 -0
- package/src/slides/Slide.svelte +179 -0
- package/src/slides/SlideList.svelte +19 -0
- package/src/slides/ambient.d.ts +1 -0
- package/src/slides/bridges.ts +69 -0
- package/src/slides/deck-controller.svelte.ts +292 -0
- package/src/slides/demo-slides.svelte +358 -0
- package/src/slides/demo.svelte +176 -0
- package/src/slides/export/capture.ts +113 -0
- package/src/slides/export/ffmpeg.ts +120 -0
- package/src/slides/export/index.ts +9 -0
- package/src/slides/export/launch.ts +10 -0
- package/src/slides/export/pdf.ts +152 -0
- package/src/slides/export/types.ts +48 -0
- package/src/slides/index.ts +19 -0
- package/src/slides/labels.ts +48 -0
- package/src/slides/layout.ts +51 -0
- package/src/slides/model.ts +47 -0
- package/src/slides/navigation.ts +100 -0
- package/src/slides/theme.css +205 -0
- package/src/slides/types.ts +86 -0
- package/src/tasks/README.md +312 -0
- package/src/tasks/ambient.d.ts +3 -0
- package/src/tasks/board-order.ts +137 -0
- package/src/tasks/dates.ts +31 -0
- package/src/tasks/define-statuses.ts +32 -0
- package/src/tasks/demo.svelte +389 -0
- package/src/tasks/detail/detail-demo.svelte +186 -0
- package/src/tasks/detail/detail-types.ts +54 -0
- package/src/tasks/detail/task-chip.svelte +28 -0
- package/src/tasks/detail/task-detail.svelte +315 -0
- package/src/tasks/detail/task-property.svelte +17 -0
- package/src/tasks/detail/task-thread.svelte +258 -0
- package/src/tasks/gantt/gantt-bar.svelte +243 -0
- package/src/tasks/gantt/gantt-grid.svelte +94 -0
- package/src/tasks/gantt/gantt-group-row.svelte +55 -0
- package/src/tasks/gantt/gantt-header.svelte +71 -0
- package/src/tasks/gantt/gantt-task-row.svelte +186 -0
- package/src/tasks/gantt/gantt.css +22 -0
- package/src/tasks/gantt/gantt.test.ts +77 -0
- package/src/tasks/gantt/logic.test.ts +163 -0
- package/src/tasks/gantt/logic.ts +248 -0
- package/src/tasks/inbox/inbox-card.svelte +283 -0
- package/src/tasks/inbox/inbox-draft.svelte +84 -0
- package/src/tasks/inbox/inbox-logic.ts +55 -0
- package/src/tasks/inbox/inbox-statuses.ts +48 -0
- package/src/tasks/inbox/inbox-trace.svelte +45 -0
- package/src/tasks/inbox/inbox-types.ts +108 -0
- package/src/tasks/inbox/inbox.svelte +203 -0
- package/src/tasks/inbox/index.ts +23 -0
- package/src/tasks/index.ts +85 -0
- package/src/tasks/kanban-board.svelte +561 -0
- package/src/tasks/kanban-column.svelte +117 -0
- package/src/tasks/kanban-drag.ts +38 -0
- package/src/tasks/kanban-types.ts +65 -0
- package/src/tasks/presets/sales.ts +16 -0
- package/src/tasks/record/demo-data.ts +286 -0
- package/src/tasks/record/field-section.svelte +53 -0
- package/src/tasks/record/fields.ts +142 -0
- package/src/tasks/record/format.ts +87 -0
- package/src/tasks/record/gutter.ts +2 -0
- package/src/tasks/record/index.ts +54 -0
- package/src/tasks/record/info-list.svelte +316 -0
- package/src/tasks/record/kinds.ts +42 -0
- package/src/tasks/record/logic.ts +324 -0
- package/src/tasks/record/next-up.svelte +70 -0
- package/src/tasks/record/project-list.svelte +133 -0
- package/src/tasks/record/record-demo.svelte +324 -0
- package/src/tasks/record/record-header.svelte +70 -0
- package/src/tasks/record/record-shell.svelte +138 -0
- package/src/tasks/record/record-stats.svelte +48 -0
- package/src/tasks/record/record-timeline.svelte +231 -0
- package/src/tasks/record/record.test.ts +350 -0
- package/src/tasks/record/steps-rail.svelte +474 -0
- package/src/tasks/record/types.ts +280 -0
- package/src/tasks/record/where-we-are.svelte +121 -0
- package/src/tasks/reorder.ts +19 -0
- package/src/tasks/side-panel-types.ts +16 -0
- package/src/tasks/side-panel.svelte +193 -0
- package/src/tasks/status-badge.svelte +63 -0
- package/src/tasks/status-dot.svelte +228 -0
- package/src/tasks/status-icon.svelte +30 -0
- package/src/tasks/status-types.ts +40 -0
- package/src/tasks/task-gantt.svelte +230 -0
- package/src/tasks/task-list.svelte +470 -0
- package/src/tasks/task-types.ts +33 -0
- package/src/tasks/time/duration.ts +69 -0
- package/src/tasks/time/time-chip.svelte +62 -0
- package/src/tasks/time/time-dot.svelte +58 -0
- package/src/tasks/time/time-ring.svelte +74 -0
- package/src/tasks/time/time-tracker.svelte +565 -0
- package/src/tasks/time/time-types.ts +74 -0
- package/src/tasks/use-kanban-dnd.svelte.ts +173 -0
- package/src/tokens/base.css +5 -5
- package/src/tokens/palette.css +115 -120
package/COMPONENTS.md
CHANGED
|
@@ -67,6 +67,7 @@ Generic pieces built on the primitives, imported from `@heyramzi/ui/components/<
|
|
|
67
67
|
- `icon` — Icon, a Phosphor component at a size
|
|
68
68
|
- `language-flag` — LanguageFlag
|
|
69
69
|
- `list-page` — ListPageShell, MasterDetail, GroupedList, RailRow and the rest of the master-detail page kit, NoticeBar, EmptyState, FilterPopover, setSectionHost
|
|
70
|
+
- `toolbar` — Toolbar: one row at any width; search, tabs and the view switch fold into menus in a narrow pane
|
|
70
71
|
- `locale-switch` — LocaleSwitch, a language switch on SegmentedFilter
|
|
71
72
|
- `mark` — Mark, a logo or photo glyph with an initial fallback
|
|
72
73
|
- `open-in` — OpenIn, a link out to a tool that takes the tool's name, label and mark as a prop
|
|
@@ -134,6 +135,6 @@ Generic pieces built on the primitives, imported from `@heyramzi/ui/components/<
|
|
|
134
135
|
|
|
135
136
|
---
|
|
136
137
|
|
|
137
|
-
Skipped on purpose: brand assets (logos, marks, brand renderer), kanban, list, gantt and status (they live in `@heyramzi/tasks`), and charts
|
|
138
|
-
(they live in `@heyramzi/charts`). The bot avatars
|
|
138
|
+
Skipped on purpose: brand assets (logos, marks, brand renderer), kanban, list, gantt and status (they live in `@heyramzi/ui/tasks`), and charts
|
|
139
|
+
(they live in `@heyramzi/ui/charts`). The bot avatars
|
|
139
140
|
are in `src/agents`, imported from `@heyramzi/ui/agents`.
|
package/README.md
CHANGED
|
@@ -33,21 +33,46 @@ Import the style layer once, after Tailwind:
|
|
|
33
33
|
@import "@heyramzi/ui/animations";
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
`tokens.css` carries
|
|
36
|
+
`tokens.css` carries a neutral palette, the semantic tokens and the craft layer
|
|
37
37
|
(radius, elevation, focus, motion). `animations` carries the keyframes the
|
|
38
38
|
components reference. If you use the Attachment or Message component, also
|
|
39
39
|
import `@heyramzi/ui/shadcn/utilities.css`.
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
`--color-brand-indigo` or `--radius-lg`. Dark mode keys off a `.dark`
|
|
43
|
-
class on `html`.
|
|
41
|
+
## A brand
|
|
44
42
|
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
The default look is neutral, and its colours are named by role: `brand`, `ink`, `surface`,
|
|
44
|
+
`line`, `raised`, `paper`, `highlight` and `signal` (`bg-brand`, `text-ink`, `border-line`).
|
|
45
|
+
A client's look is one typed file with no build step:
|
|
47
46
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
```ts
|
|
48
|
+
// brand.ts
|
|
49
|
+
import { defineBrand } from "@heyramzi/ui/brand";
|
|
50
|
+
|
|
51
|
+
export const brand = defineBrand({
|
|
52
|
+
colors: { brand: "oklch(0.5 0.2 272)", ink: "oklch(0.12 0.04 258)" },
|
|
53
|
+
fonts: { sans: '"Satoshi", system-ui, sans-serif' },
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Apply it with `brandStyle(brand)` on `<html style>` (right on first paint), `applyBrand(brand)`
|
|
58
|
+
in the browser, or `brandCss(brand)` in a stylesheet. A brand names only the roles it changes; light
|
|
59
|
+
and dark both follow from the tokens, and the tint scales (`brand-100` to `brand-800`) derive
|
|
60
|
+
from the three hues.
|
|
61
|
+
|
|
62
|
+
## Subpaths
|
|
63
|
+
|
|
64
|
+
One package, six larger parts, each imported from its own subpath:
|
|
65
|
+
|
|
66
|
+
| Subpath | What it is |
|
|
67
|
+
| --- | --- |
|
|
68
|
+
| `@heyramzi/ui/tasks` | Board, list, gantt, status badges, task detail, record, time tracker, inbox |
|
|
69
|
+
| `@heyramzi/ui/charts` | LayerChart charts, sparklines, stat cards, and `/charts/dashboard` |
|
|
70
|
+
| `@heyramzi/ui/slides` | Decks on a fixed canvas, plus `/slides/model` and `/slides/export` |
|
|
71
|
+
| `@heyramzi/ui/editor` | The Tiptap editor, its renderer, `/editor/markdown`, `/editor/block-ops` |
|
|
72
|
+
| `@heyramzi/ui/onboarding` | Page tours |
|
|
73
|
+
| `@heyramzi/ui/canvas` | The xyflow canvas with a pen and headless ops. Add `@xyflow/svelte` yourself |
|
|
74
|
+
|
|
75
|
+
Each has a README next to its source under `src/<subpath>/`.
|
|
51
76
|
|
|
52
77
|
React and other framework ports are out of scope. The source is Svelte 5
|
|
53
78
|
runes only.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heyramzi/ui",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Svelte 5 UI
|
|
3
|
+
"version": "2.0.1",
|
|
4
|
+
"description": "Svelte 5 UI for apps: primitives, effects and animations, plus tasks, charts, slides, an editor, onboarding and a canvas as subpaths.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -29,6 +29,11 @@
|
|
|
29
29
|
"default": "./src/index.ts"
|
|
30
30
|
},
|
|
31
31
|
"./tokens.css": "./src/tokens/index.css",
|
|
32
|
+
"./brand": {
|
|
33
|
+
"svelte": "./src/brand/index.ts",
|
|
34
|
+
"types": "./src/brand/index.ts",
|
|
35
|
+
"default": "./src/brand/index.ts"
|
|
36
|
+
},
|
|
32
37
|
"./lib/*.js": {
|
|
33
38
|
"svelte": "./src/lib/*.ts",
|
|
34
39
|
"types": "./src/lib/*.ts",
|
|
@@ -166,28 +171,210 @@
|
|
|
166
171
|
"types": "./src/agents/index.ts",
|
|
167
172
|
"default": "./src/agents/index.ts"
|
|
168
173
|
},
|
|
169
|
-
"./agents/demo.svelte": "./src/agents/demo.svelte"
|
|
174
|
+
"./agents/demo.svelte": "./src/agents/demo.svelte",
|
|
175
|
+
"./tasks": {
|
|
176
|
+
"svelte": "./src/tasks/index.ts",
|
|
177
|
+
"types": "./src/tasks/index.ts",
|
|
178
|
+
"default": "./src/tasks/index.ts"
|
|
179
|
+
},
|
|
180
|
+
"./tasks/kanban": {
|
|
181
|
+
"svelte": "./src/tasks/kanban-board.svelte",
|
|
182
|
+
"types": "./src/tasks/kanban-board.svelte",
|
|
183
|
+
"default": "./src/tasks/kanban-board.svelte"
|
|
184
|
+
},
|
|
185
|
+
"./tasks/list": {
|
|
186
|
+
"svelte": "./src/tasks/task-list.svelte",
|
|
187
|
+
"types": "./src/tasks/task-list.svelte",
|
|
188
|
+
"default": "./src/tasks/task-list.svelte"
|
|
189
|
+
},
|
|
190
|
+
"./tasks/gantt": {
|
|
191
|
+
"svelte": "./src/tasks/task-gantt.svelte",
|
|
192
|
+
"types": "./src/tasks/task-gantt.svelte",
|
|
193
|
+
"default": "./src/tasks/task-gantt.svelte"
|
|
194
|
+
},
|
|
195
|
+
"./tasks/status": {
|
|
196
|
+
"svelte": "./src/tasks/status-badge.svelte",
|
|
197
|
+
"types": "./src/tasks/status-badge.svelte",
|
|
198
|
+
"default": "./src/tasks/status-badge.svelte"
|
|
199
|
+
},
|
|
200
|
+
"./tasks/status-dot": {
|
|
201
|
+
"svelte": "./src/tasks/status-dot.svelte",
|
|
202
|
+
"types": "./src/tasks/status-dot.svelte",
|
|
203
|
+
"default": "./src/tasks/status-dot.svelte"
|
|
204
|
+
},
|
|
205
|
+
"./tasks/statuses": {
|
|
206
|
+
"svelte": "./src/tasks/define-statuses.ts",
|
|
207
|
+
"types": "./src/tasks/define-statuses.ts",
|
|
208
|
+
"default": "./src/tasks/define-statuses.ts"
|
|
209
|
+
},
|
|
210
|
+
"./tasks/time": {
|
|
211
|
+
"svelte": "./src/tasks/time/time-tracker.svelte",
|
|
212
|
+
"types": "./src/tasks/time/time-tracker.svelte",
|
|
213
|
+
"default": "./src/tasks/time/time-tracker.svelte"
|
|
214
|
+
},
|
|
215
|
+
"./tasks/inbox": {
|
|
216
|
+
"svelte": "./src/tasks/inbox/index.ts",
|
|
217
|
+
"types": "./src/tasks/inbox/index.ts",
|
|
218
|
+
"default": "./src/tasks/inbox/index.ts"
|
|
219
|
+
},
|
|
220
|
+
"./tasks/side-panel": {
|
|
221
|
+
"svelte": "./src/tasks/side-panel.svelte",
|
|
222
|
+
"types": "./src/tasks/side-panel.svelte",
|
|
223
|
+
"default": "./src/tasks/side-panel.svelte"
|
|
224
|
+
},
|
|
225
|
+
"./tasks/presets/sales": {
|
|
226
|
+
"svelte": "./src/tasks/presets/sales.ts",
|
|
227
|
+
"types": "./src/tasks/presets/sales.ts",
|
|
228
|
+
"default": "./src/tasks/presets/sales.ts"
|
|
229
|
+
},
|
|
230
|
+
"./tasks/detail": {
|
|
231
|
+
"svelte": "./src/tasks/detail/task-detail.svelte",
|
|
232
|
+
"types": "./src/tasks/detail/task-detail.svelte",
|
|
233
|
+
"default": "./src/tasks/detail/task-detail.svelte"
|
|
234
|
+
},
|
|
235
|
+
"./tasks/detail-demo.svelte": "./src/tasks/detail/detail-demo.svelte",
|
|
236
|
+
"./tasks/record": {
|
|
237
|
+
"svelte": "./src/tasks/record/index.ts",
|
|
238
|
+
"types": "./src/tasks/record/index.ts",
|
|
239
|
+
"default": "./src/tasks/record/index.ts"
|
|
240
|
+
},
|
|
241
|
+
"./tasks/record-demo.svelte": "./src/tasks/record/record-demo.svelte",
|
|
242
|
+
"./tasks/demo": {
|
|
243
|
+
"svelte": "./src/tasks/demo.svelte",
|
|
244
|
+
"default": "./src/tasks/demo.svelte"
|
|
245
|
+
},
|
|
246
|
+
"./tasks/demo.svelte": "./src/tasks/demo.svelte",
|
|
247
|
+
"./charts": {
|
|
248
|
+
"svelte": "./src/charts/index.ts",
|
|
249
|
+
"types": "./src/charts/index.ts",
|
|
250
|
+
"default": "./src/charts/index.ts"
|
|
251
|
+
},
|
|
252
|
+
"./charts/dashboard": {
|
|
253
|
+
"svelte": "./src/charts/dashboard/index.ts",
|
|
254
|
+
"types": "./src/charts/dashboard/index.ts",
|
|
255
|
+
"default": "./src/charts/dashboard/index.ts"
|
|
256
|
+
},
|
|
257
|
+
"./slides": {
|
|
258
|
+
"types": "./src/slides/index.ts",
|
|
259
|
+
"svelte": "./src/slides/index.ts",
|
|
260
|
+
"default": "./src/slides/index.ts"
|
|
261
|
+
},
|
|
262
|
+
"./slides/model": {
|
|
263
|
+
"types": "./src/slides/model.ts",
|
|
264
|
+
"default": "./src/slides/model.ts"
|
|
265
|
+
},
|
|
266
|
+
"./slides/export": {
|
|
267
|
+
"types": "./src/slides/export/index.ts",
|
|
268
|
+
"default": "./src/slides/export/index.ts"
|
|
269
|
+
},
|
|
270
|
+
"./slides/demo": {
|
|
271
|
+
"svelte": "./src/slides/demo.svelte",
|
|
272
|
+
"default": "./src/slides/demo.svelte"
|
|
273
|
+
},
|
|
274
|
+
"./slides/demo.svelte": "./src/slides/demo.svelte",
|
|
275
|
+
"./slides/demo-slides": {
|
|
276
|
+
"svelte": "./src/slides/demo-slides.svelte",
|
|
277
|
+
"default": "./src/slides/demo-slides.svelte"
|
|
278
|
+
},
|
|
279
|
+
"./slides/demo-slides.svelte": "./src/slides/demo-slides.svelte",
|
|
280
|
+
"./editor": {
|
|
281
|
+
"svelte": "./src/editor/index.ts",
|
|
282
|
+
"types": "./src/editor/index.ts",
|
|
283
|
+
"default": "./src/editor/index.ts"
|
|
284
|
+
},
|
|
285
|
+
"./editor/serializer": {
|
|
286
|
+
"types": "./src/editor/serializer.ts",
|
|
287
|
+
"default": "./src/editor/serializer.ts"
|
|
288
|
+
},
|
|
289
|
+
"./editor/markdown": {
|
|
290
|
+
"types": "./src/editor/markdown.ts",
|
|
291
|
+
"default": "./src/editor/markdown.ts"
|
|
292
|
+
},
|
|
293
|
+
"./editor/block-ops": {
|
|
294
|
+
"types": "./src/editor/block-ops.ts",
|
|
295
|
+
"default": "./src/editor/block-ops.ts"
|
|
296
|
+
},
|
|
297
|
+
"./editor/inline-editor": {
|
|
298
|
+
"svelte": "./src/editor/inline-editor.svelte",
|
|
299
|
+
"default": "./src/editor/inline-editor.svelte"
|
|
300
|
+
},
|
|
301
|
+
"./editor/content.css": {
|
|
302
|
+
"style": "./src/editor/content.css",
|
|
303
|
+
"default": "./src/editor/content.css"
|
|
304
|
+
},
|
|
305
|
+
"./onboarding": {
|
|
306
|
+
"svelte": "./src/onboarding/index.ts",
|
|
307
|
+
"types": "./src/onboarding/index.ts",
|
|
308
|
+
"default": "./src/onboarding/index.ts"
|
|
309
|
+
},
|
|
310
|
+
"./onboarding/stores": {
|
|
311
|
+
"svelte": "./src/onboarding/stores.ts",
|
|
312
|
+
"types": "./src/onboarding/stores.ts",
|
|
313
|
+
"default": "./src/onboarding/stores.ts"
|
|
314
|
+
},
|
|
315
|
+
"./onboarding/demo": {
|
|
316
|
+
"svelte": "./src/onboarding/demo.svelte",
|
|
317
|
+
"default": "./src/onboarding/demo.svelte"
|
|
318
|
+
},
|
|
319
|
+
"./onboarding/demo.svelte": "./src/onboarding/demo.svelte",
|
|
320
|
+
"./canvas": {
|
|
321
|
+
"types": "./src/canvas/index.ts",
|
|
322
|
+
"svelte": "./src/canvas/index.ts",
|
|
323
|
+
"default": "./src/canvas/index.ts"
|
|
324
|
+
},
|
|
325
|
+
"./canvas/*.svelte": {
|
|
326
|
+
"types": "./src/canvas/*.svelte",
|
|
327
|
+
"svelte": "./src/canvas/*.svelte",
|
|
328
|
+
"default": "./src/canvas/*.svelte"
|
|
329
|
+
},
|
|
330
|
+
"./canvas/*.css": "./src/canvas/*.css",
|
|
331
|
+
"./canvas/*.js": {
|
|
332
|
+
"types": "./src/canvas/*.ts",
|
|
333
|
+
"svelte": "./src/canvas/*.ts",
|
|
334
|
+
"default": "./src/canvas/*.ts"
|
|
335
|
+
}
|
|
170
336
|
},
|
|
171
337
|
"dependencies": {
|
|
338
|
+
"@dagrejs/dagre": "^3.1.1",
|
|
172
339
|
"@shadcn-svelte/primitives": "^0.0.1",
|
|
340
|
+
"@tiptap/core": "^3.32.0",
|
|
341
|
+
"@tiptap/extension-drag-handle": "^3.32.0",
|
|
342
|
+
"@tiptap/extension-image": "^3.32.0",
|
|
343
|
+
"@tiptap/extension-placeholder": "^3.32.0",
|
|
344
|
+
"@tiptap/pm": "^3.32.0",
|
|
345
|
+
"@tiptap/starter-kit": "^3.32.0",
|
|
346
|
+
"@zag-js/svelte": "^1.46.0",
|
|
347
|
+
"@zag-js/tour": "^1.46.0",
|
|
173
348
|
"clsx": "^2.1.1",
|
|
174
|
-
"
|
|
175
|
-
"
|
|
349
|
+
"d3-scale": "^4.0.2",
|
|
350
|
+
"d3-shape": "^3.2.0",
|
|
351
|
+
"layerchart": "^2.8.1",
|
|
352
|
+
"markdown-it": "^15.0.3",
|
|
353
|
+
"marked": "^18.1.1",
|
|
354
|
+
"perfect-freehand": "^1.2.3",
|
|
355
|
+
"phosphor-svelte": "^3.1.1",
|
|
356
|
+
"roughjs": "^4.6.6",
|
|
176
357
|
"shiki": "^4.5.0",
|
|
177
358
|
"tailwind-merge": "^3.7.0",
|
|
178
|
-
"tailwind-variants": "^3.3.1"
|
|
359
|
+
"tailwind-variants": "^3.3.1",
|
|
360
|
+
"zod": "^4.6.7"
|
|
179
361
|
},
|
|
180
362
|
"devDependencies": {
|
|
181
363
|
"@internationalized/date": "^3.12.4",
|
|
182
364
|
"@sveltejs/vite-plugin-svelte": "^7.3.1",
|
|
183
|
-
"@types/
|
|
184
|
-
"
|
|
185
|
-
"
|
|
365
|
+
"@types/d3-scale": "^4.0.9",
|
|
366
|
+
"@types/d3-shape": "^3.2.0",
|
|
367
|
+
"@types/node": "^26.6.5",
|
|
368
|
+
"@xyflow/svelte": "^1.7.0",
|
|
369
|
+
"bits-ui": "^2.20.0",
|
|
370
|
+
"jsdom": "^30.1.3",
|
|
186
371
|
"mode-watcher": "^1.1.0",
|
|
187
372
|
"svelte": "^5.57.2",
|
|
373
|
+
"svelte-check": "^4.7.7",
|
|
188
374
|
"svelte-sonner": "^1.2.1",
|
|
189
|
-
"
|
|
190
|
-
"
|
|
375
|
+
"typescript": "~7.0.2",
|
|
376
|
+
"vite-plus": "^1.3.0",
|
|
377
|
+
"vitest": "^5.0.5"
|
|
191
378
|
},
|
|
192
379
|
"peerDependencies": {
|
|
193
380
|
"@internationalized/date": "^3.12.4",
|
|
@@ -195,7 +382,8 @@
|
|
|
195
382
|
"bits-ui": "^2.19.5",
|
|
196
383
|
"mode-watcher": "^1.1.0",
|
|
197
384
|
"svelte": "^5.57.2",
|
|
198
|
-
"svelte-sonner": "^1.2.1"
|
|
385
|
+
"svelte-sonner": "^1.2.1",
|
|
386
|
+
"@xyflow/svelte": "^1.7.0"
|
|
199
387
|
},
|
|
200
388
|
"peerDependenciesMeta": {
|
|
201
389
|
"mode-watcher": {
|
|
@@ -209,6 +397,9 @@
|
|
|
209
397
|
},
|
|
210
398
|
"@tabler/icons-svelte-runes": {
|
|
211
399
|
"optional": true
|
|
400
|
+
},
|
|
401
|
+
"@xyflow/svelte": {
|
|
402
|
+
"optional": true
|
|
212
403
|
}
|
|
213
404
|
},
|
|
214
405
|
"scripts": {
|
|
@@ -216,6 +407,7 @@
|
|
|
216
407
|
"sync:transitions": "node scripts/sync-transitions.mjs",
|
|
217
408
|
"sync:shadcn": "node scripts/sync-shadcn.mjs",
|
|
218
409
|
"sync": "pnpm sync:transitions && pnpm sync:shadcn",
|
|
219
|
-
"sync:check": "node scripts/sync-transitions.mjs --check && node scripts/sync-shadcn.mjs --check"
|
|
410
|
+
"sync:check": "node scripts/sync-transitions.mjs --check && node scripts/sync-shadcn.mjs --check",
|
|
411
|
+
"check": "svelte-check --tsconfig ./tsconfig.json"
|
|
220
412
|
}
|
|
221
413
|
}
|
package/scripts/sync-shadcn.mjs
CHANGED
|
@@ -69,6 +69,8 @@ const DEMO_ALIASES = [
|
|
|
69
69
|
["$lib/utils.js", "@heyramzi/utils"],
|
|
70
70
|
// The peer here is the runes build, published under its own name.
|
|
71
71
|
["@tabler/icons-svelte/", "@tabler/icons-svelte-runes/"],
|
|
72
|
+
// Upstream's docs live under /docs/components; ours are at /components, and the prerender follows the link.
|
|
73
|
+
['href="/docs/components"', 'href="/components"'],
|
|
72
74
|
];
|
|
73
75
|
|
|
74
76
|
/* Registry primitives this package does not ship: no product imports them. Skipped, so a sync does not bring them back. */
|
|
@@ -111,10 +111,10 @@
|
|
|
111
111
|
/* ── Surface ─────────────────────────────────────────── */
|
|
112
112
|
.gen {
|
|
113
113
|
--shimmer-dur: 2200ms;
|
|
114
|
-
--shimmer-base: var(--color-brand
|
|
114
|
+
--shimmer-base: var(--color-brand);
|
|
115
115
|
/* The band is darker than the base on a pale tint and lighter on ink.
|
|
116
116
|
Otherwise the sweep looks like the label dropping out. */
|
|
117
|
-
--shimmer-highlight: color-mix(in oklab, black 45%, var(--color-brand
|
|
117
|
+
--shimmer-highlight: color-mix(in oklab, black 45%, var(--color-brand));
|
|
118
118
|
--shimmer-band: 400%;
|
|
119
119
|
--shimmer-ease: linear;
|
|
120
120
|
--digit-dur: var(--duration-very-slow);
|
|
@@ -126,11 +126,11 @@
|
|
|
126
126
|
|
|
127
127
|
padding: 0.625rem 0.875rem;
|
|
128
128
|
border-radius: var(--radius-lg);
|
|
129
|
-
background: color-mix(in oklab, var(--color-brand
|
|
129
|
+
background: color-mix(in oklab, var(--color-brand) 10%, transparent);
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
:global(.dark) .gen {
|
|
133
|
-
--shimmer-highlight: color-mix(in oklab, white 70%, var(--color-brand
|
|
133
|
+
--shimmer-highlight: color-mix(in oklab, white 70%, var(--color-brand));
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
.gen-failed {
|
|
@@ -150,7 +150,7 @@
|
|
|
150
150
|
width: 0.375rem;
|
|
151
151
|
height: 0.375rem;
|
|
152
152
|
border-radius: 9999px;
|
|
153
|
-
background: var(--color-brand
|
|
153
|
+
background: var(--color-brand);
|
|
154
154
|
animation: gen-pulse 1.8s var(--ease-in-out, ease-in-out) infinite;
|
|
155
155
|
}
|
|
156
156
|
|
|
@@ -166,14 +166,14 @@
|
|
|
166
166
|
line-height: 1.4;
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
/*
|
|
169
|
+
/* signal-600, not --color-destructive: that token is tuned for fills, and at
|
|
170
170
|
L=0.6 small text on the pale tint fails. Dark mode uses the lighter step. */
|
|
171
171
|
.gen-failed .gen-label {
|
|
172
|
-
color: var(--color-
|
|
172
|
+
color: var(--color-signal-600);
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
:global(.dark) .gen-failed .gen-label {
|
|
176
|
-
color: var(--color-
|
|
176
|
+
color: var(--color-signal-400);
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
.gen-meta {
|
|
@@ -183,7 +183,7 @@
|
|
|
183
183
|
font-size: 0.75rem;
|
|
184
184
|
font-weight: 500;
|
|
185
185
|
font-variant-numeric: tabular-nums;
|
|
186
|
-
color: var(--color-brand
|
|
186
|
+
color: var(--color-brand);
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
.gen-count {
|
|
@@ -203,7 +203,7 @@
|
|
|
203
203
|
margin-top: 0.5rem;
|
|
204
204
|
height: 0.25rem;
|
|
205
205
|
border-radius: 9999px;
|
|
206
|
-
background: color-mix(in oklab, var(--color-brand
|
|
206
|
+
background: color-mix(in oklab, var(--color-brand) 22%, transparent);
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
.gen-fill {
|
|
@@ -211,7 +211,7 @@
|
|
|
211
211
|
height: 100%;
|
|
212
212
|
transform-origin: left;
|
|
213
213
|
border-radius: 9999px;
|
|
214
|
-
background: var(--color-brand
|
|
214
|
+
background: var(--color-brand);
|
|
215
215
|
transition: transform var(--duration-slow, 400ms) var(--ease-smooth-out, ease);
|
|
216
216
|
}
|
|
217
217
|
|
|
@@ -232,7 +232,7 @@
|
|
|
232
232
|
text-overflow: ellipsis;
|
|
233
233
|
/* Mix toward the foreground, not transparent: a 70% indigo wash on its
|
|
234
234
|
own track measured 2.9:1. This stays quieter than the label and AA. */
|
|
235
|
-
color: color-mix(in oklab, var(--color-brand
|
|
235
|
+
color: color-mix(in oklab, var(--color-brand) 70%, var(--color-foreground));
|
|
236
236
|
animation: gen-step-in var(--duration-quick, 150ms) var(--ease-in-out, ease-in-out) both;
|
|
237
237
|
}
|
|
238
238
|
|
package/src/ambient.d.ts
CHANGED
|
@@ -2,3 +2,11 @@
|
|
|
2
2
|
* A stylesheet imported for its side effect, bundled by the consumer's Vite.
|
|
3
3
|
*/
|
|
4
4
|
declare module "*.css";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* An SVG imported as a URL, resolved by the consumer's Vite.
|
|
8
|
+
*/
|
|
9
|
+
declare module "*.svg" {
|
|
10
|
+
const src: string;
|
|
11
|
+
export default src;
|
|
12
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { readFileSync, readdirSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { describe, expect, it } from "vite-plus/test";
|
|
4
|
+
import { BRAND_COLOR_VARS, applyBrand, brandCss, brandStyle, brandVars, defineBrand } from "./index.js";
|
|
5
|
+
|
|
6
|
+
const TOKENS = join(import.meta.dirname, "..", "tokens");
|
|
7
|
+
const palette = readFileSync(join(TOKENS, "palette.css"), "utf8");
|
|
8
|
+
|
|
9
|
+
describe("brandVars", () => {
|
|
10
|
+
it("sets only the roles a brand names", () => {
|
|
11
|
+
const brand = defineBrand({ colors: { brand: "oklch(0.5 0.2 272)", signal: "#f0503d" } });
|
|
12
|
+
expect(brandVars(brand)).toEqual({ "--brand": "oklch(0.5 0.2 272)", "--brand-signal": "#f0503d" });
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("maps fonts and extra properties", () => {
|
|
16
|
+
const brand = defineBrand({
|
|
17
|
+
colors: { brand: "red" },
|
|
18
|
+
fonts: { sans: '"Satoshi", system-ui', mono: "ui-monospace" },
|
|
19
|
+
vars: { "--logo-height": "24px" },
|
|
20
|
+
});
|
|
21
|
+
expect(brandVars(brand)).toEqual({
|
|
22
|
+
"--brand": "red",
|
|
23
|
+
"--font-sans": '"Satoshi", system-ui',
|
|
24
|
+
"--font-mono": "ui-monospace",
|
|
25
|
+
"--logo-height": "24px",
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("refuses a value that would break out of its declaration", () => {
|
|
30
|
+
expect(() => brandVars({ colors: { brand: "red; } body { display: none" } })).toThrow(/--brand/);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe("the three ways to apply one", () => {
|
|
35
|
+
const brand = defineBrand({ colors: { brand: "blue", ink: "black" } });
|
|
36
|
+
|
|
37
|
+
it("writes a style attribute and a rule", () => {
|
|
38
|
+
expect(brandStyle(brand)).toBe("--brand:blue;--brand-ink:black");
|
|
39
|
+
expect(brandCss(brand, ".client")).toBe(".client {\n --brand: blue;\n --brand-ink: black;\n}\n");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("applies to an element and puts back what it found", () => {
|
|
43
|
+
const root = document.createElement("div");
|
|
44
|
+
root.style.setProperty("--brand", "green");
|
|
45
|
+
const reset = applyBrand(brand, root);
|
|
46
|
+
expect(root.style.getPropertyValue("--brand")).toBe("blue");
|
|
47
|
+
expect(root.style.getPropertyValue("--brand-ink")).toBe("black");
|
|
48
|
+
reset();
|
|
49
|
+
expect(root.style.getPropertyValue("--brand")).toBe("green");
|
|
50
|
+
expect(root.style.getPropertyValue("--brand-ink")).toBe("");
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe("the tokens", () => {
|
|
55
|
+
it("define a neutral default for every role a brand can set", () => {
|
|
56
|
+
for (const name of Object.values(BRAND_COLOR_VARS)) {
|
|
57
|
+
expect(palette, name).toMatch(new RegExp(`^\\s+${name}: `, "m"));
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("carry no client palette", () => {
|
|
62
|
+
for (const file of readdirSync(TOKENS).filter((f) => f.endsWith(".css"))) {
|
|
63
|
+
const css = readFileSync(join(TOKENS, file), "utf8");
|
|
64
|
+
expect(css, file).not.toMatch(/brand-(indigo|sand|terra|night|veil|dusk|cream|linen|mist)|--(indigo|sand|terra)-\d/);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A client's brand is a typed object in its own `brand.ts`, with no build step:
|
|
3
|
+
*
|
|
4
|
+
* export const brand = defineBrand({ colors: { brand: "oklch(0.5 0.2 272)" } });
|
|
5
|
+
*
|
|
6
|
+
* `@heyramzi/ui/tokens.css` ships a neutral look. A brand replaces only the roles it names:
|
|
7
|
+
* - on the server, put `brandStyle(brand)` on the `<html style>` so the first paint is right;
|
|
8
|
+
* - or call `applyBrand(brand)` once in the browser;
|
|
9
|
+
* - or write `brandCss(brand)` into a stylesheet.
|
|
10
|
+
* Light and dark come from the tokens, which read these roles, so a brand never types both.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** Any CSS colour: oklch(), hex, a named colour or a var(). */
|
|
14
|
+
export type CssColor = string;
|
|
15
|
+
|
|
16
|
+
export type BrandColors = {
|
|
17
|
+
/** The signature colour: focus ring, links, active states, the mark. */
|
|
18
|
+
brand: CssColor;
|
|
19
|
+
/** The same colour lifted for a dark page. Falls back to the neutral default. */
|
|
20
|
+
brandOnDark?: CssColor;
|
|
21
|
+
/** Warmth: badges, highlights, soft accents. */
|
|
22
|
+
highlight?: CssColor;
|
|
23
|
+
/** Attention: warnings, errors, energy. */
|
|
24
|
+
signal?: CssColor;
|
|
25
|
+
/** The dark page background. */
|
|
26
|
+
ink?: CssColor;
|
|
27
|
+
/** Cards and panels on a dark page. */
|
|
28
|
+
surface?: CssColor;
|
|
29
|
+
/** Borders and dividers on a dark page. */
|
|
30
|
+
line?: CssColor;
|
|
31
|
+
/** Hover surfaces and inputs on a dark page. */
|
|
32
|
+
raised?: CssColor;
|
|
33
|
+
/** Text on a dark page, and a light page's background tone. */
|
|
34
|
+
paper?: CssColor;
|
|
35
|
+
/** Cards on a light page. */
|
|
36
|
+
surfaceLight?: CssColor;
|
|
37
|
+
/** Borders on a light page. */
|
|
38
|
+
lineLight?: CssColor;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export type BrandFonts = {
|
|
42
|
+
/** The body family, as CSS: `"Satoshi", system-ui, sans-serif`. */
|
|
43
|
+
sans?: string;
|
|
44
|
+
/** Headings. Falls back to the body family. */
|
|
45
|
+
heading?: string;
|
|
46
|
+
/** Code and figures. */
|
|
47
|
+
mono?: string;
|
|
48
|
+
/** Quotes. Falls back to the mono family. */
|
|
49
|
+
quote?: string;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type Brand = {
|
|
53
|
+
colors: BrandColors;
|
|
54
|
+
fonts?: BrandFonts;
|
|
55
|
+
/** Extra custom properties the brand owns, such as a logo size. Each key starts with `--`. */
|
|
56
|
+
vars?: Record<`--${string}`, string>;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const COLOR_VARS = {
|
|
60
|
+
brand: "--brand",
|
|
61
|
+
brandOnDark: "--brand-on-dark",
|
|
62
|
+
highlight: "--brand-highlight",
|
|
63
|
+
signal: "--brand-signal",
|
|
64
|
+
ink: "--brand-ink",
|
|
65
|
+
surface: "--brand-surface",
|
|
66
|
+
line: "--brand-line",
|
|
67
|
+
raised: "--brand-raised",
|
|
68
|
+
paper: "--brand-paper",
|
|
69
|
+
surfaceLight: "--brand-surface-light",
|
|
70
|
+
lineLight: "--brand-line-light",
|
|
71
|
+
} as const satisfies Record<keyof BrandColors, string>;
|
|
72
|
+
|
|
73
|
+
const FONT_VARS = {
|
|
74
|
+
sans: "--font-sans",
|
|
75
|
+
heading: "--font-heading",
|
|
76
|
+
mono: "--font-mono",
|
|
77
|
+
quote: "--font-quote",
|
|
78
|
+
} as const satisfies Record<keyof BrandFonts, string>;
|
|
79
|
+
|
|
80
|
+
/** The custom property each colour role sets. The tokens read these and nothing else. */
|
|
81
|
+
export const BRAND_COLOR_VARS: Readonly<Record<keyof BrandColors, string>> = COLOR_VARS;
|
|
82
|
+
|
|
83
|
+
/** Keeps a typed brand typed at the call site. It returns the object unchanged. */
|
|
84
|
+
export function defineBrand<const B extends Brand>(brand: B): B {
|
|
85
|
+
return brand;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function checked(name: string, value: string): string {
|
|
89
|
+
if (/[;{}<>]/.test(value)) throw new Error(`brand: ${name} holds a character CSS would read as more than a value`);
|
|
90
|
+
return value;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** The custom properties a brand sets, by name. Roles the brand leaves out keep the neutral default. */
|
|
94
|
+
export function brandVars(brand: Brand): Record<string, string> {
|
|
95
|
+
const out: Record<string, string> = {};
|
|
96
|
+
for (const [key, name] of Object.entries(COLOR_VARS)) {
|
|
97
|
+
const value = brand.colors[key as keyof BrandColors];
|
|
98
|
+
if (value !== undefined) out[name] = checked(name, value);
|
|
99
|
+
}
|
|
100
|
+
for (const [key, name] of Object.entries(FONT_VARS)) {
|
|
101
|
+
const value = brand.fonts?.[key as keyof BrandFonts];
|
|
102
|
+
if (value !== undefined) out[name] = checked(name, value);
|
|
103
|
+
}
|
|
104
|
+
for (const [name, value] of Object.entries(brand.vars ?? {})) out[name] = checked(name, value);
|
|
105
|
+
return out;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** The brand as a `style` attribute value: put it on `<html>` in `app.html` or a layout. */
|
|
109
|
+
export function brandStyle(brand: Brand): string {
|
|
110
|
+
return Object.entries(brandVars(brand))
|
|
111
|
+
.map(([name, value]) => `${name}:${value}`)
|
|
112
|
+
.join(";");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** The brand as a rule, for a stylesheet or a `<style>` in `<svelte:head>`. */
|
|
116
|
+
export function brandCss(brand: Brand, selector = ":root"): string {
|
|
117
|
+
const body = Object.entries(brandVars(brand))
|
|
118
|
+
.map(([name, value]) => ` ${name}: ${value};`)
|
|
119
|
+
.join("\n");
|
|
120
|
+
return `${selector} {\n${body}\n}\n`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Sets the brand on the page in the browser. Returns a function that puts the neutral look back. */
|
|
124
|
+
export function applyBrand(brand: Brand, root: HTMLElement = document.documentElement): () => void {
|
|
125
|
+
const vars = brandVars(brand);
|
|
126
|
+
const before = new Map<string, string>();
|
|
127
|
+
for (const [name, value] of Object.entries(vars)) {
|
|
128
|
+
before.set(name, root.style.getPropertyValue(name));
|
|
129
|
+
root.style.setProperty(name, value);
|
|
130
|
+
}
|
|
131
|
+
return () => {
|
|
132
|
+
for (const [name, value] of before) {
|
|
133
|
+
if (value) root.style.setProperty(name, value);
|
|
134
|
+
else root.style.removeProperty(name);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
}
|