@dxlbnl/ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/README.md +94 -0
  2. package/dist/components/cards/Card.stories.svelte +82 -0
  3. package/dist/components/cards/Card.stories.svelte.d.ts +19 -0
  4. package/dist/components/cards/Card.svelte +28 -0
  5. package/dist/components/cards/Card.svelte.d.ts +12 -0
  6. package/dist/components/cards/NoteCard.stories.svelte +94 -0
  7. package/dist/components/cards/NoteCard.stories.svelte.d.ts +19 -0
  8. package/dist/components/cards/NoteCard.svelte +89 -0
  9. package/dist/components/cards/NoteCard.svelte.d.ts +18 -0
  10. package/dist/components/cards/ProductCard.stories.svelte +98 -0
  11. package/dist/components/cards/ProductCard.stories.svelte.d.ts +19 -0
  12. package/dist/components/cards/ProductCard.svelte +150 -0
  13. package/dist/components/cards/ProductCard.svelte.d.ts +22 -0
  14. package/dist/components/cards/ProjectCard.stories.svelte +88 -0
  15. package/dist/components/cards/ProjectCard.stories.svelte.d.ts +19 -0
  16. package/dist/components/cards/ProjectCard.svelte +109 -0
  17. package/dist/components/cards/ProjectCard.svelte.d.ts +20 -0
  18. package/dist/components/cards/index.d.ts +4 -0
  19. package/dist/components/cards/index.js +4 -0
  20. package/dist/components/data/Accordion.stories.svelte +316 -0
  21. package/dist/components/data/Accordion.stories.svelte.d.ts +19 -0
  22. package/dist/components/data/Accordion.svelte +23 -0
  23. package/dist/components/data/Accordion.svelte.d.ts +9 -0
  24. package/dist/components/data/AccordionItem.svelte +112 -0
  25. package/dist/components/data/AccordionItem.svelte.d.ts +11 -0
  26. package/dist/components/data/Table.composition.stories.svelte +67 -0
  27. package/dist/components/data/Table.composition.stories.svelte.d.ts +19 -0
  28. package/dist/components/data/Table.stories.svelte +137 -0
  29. package/dist/components/data/Table.stories.svelte.d.ts +19 -0
  30. package/dist/components/data/Table.svelte +83 -0
  31. package/dist/components/data/Table.svelte.d.ts +14 -0
  32. package/dist/components/data/Tabs.stories.svelte +386 -0
  33. package/dist/components/data/Tabs.stories.svelte.d.ts +19 -0
  34. package/dist/components/data/Tabs.svelte +142 -0
  35. package/dist/components/data/Tabs.svelte.d.ts +19 -0
  36. package/dist/components/data/index.d.ts +4 -0
  37. package/dist/components/data/index.js +4 -0
  38. package/dist/components/feedback/Modal.stories.svelte +192 -0
  39. package/dist/components/feedback/Modal.stories.svelte.d.ts +4 -0
  40. package/dist/components/feedback/Modal.svelte +185 -0
  41. package/dist/components/feedback/Modal.svelte.d.ts +19 -0
  42. package/dist/components/feedback/Toast.stories.svelte +203 -0
  43. package/dist/components/feedback/Toast.stories.svelte.d.ts +19 -0
  44. package/dist/components/feedback/Toast.svelte +109 -0
  45. package/dist/components/feedback/Toast.svelte.d.ts +15 -0
  46. package/dist/components/feedback/ToastRegion.stories.svelte +193 -0
  47. package/dist/components/feedback/ToastRegion.stories.svelte.d.ts +19 -0
  48. package/dist/components/feedback/ToastRegion.svelte +102 -0
  49. package/dist/components/feedback/ToastRegion.svelte.d.ts +9 -0
  50. package/dist/components/feedback/index.d.ts +3 -0
  51. package/dist/components/feedback/index.js +3 -0
  52. package/dist/components/forms/Checkbox.stories.svelte +103 -0
  53. package/dist/components/forms/Checkbox.stories.svelte.d.ts +19 -0
  54. package/dist/components/forms/Checkbox.svelte +150 -0
  55. package/dist/components/forms/Checkbox.svelte.d.ts +11 -0
  56. package/dist/components/forms/Field.stories.svelte +113 -0
  57. package/dist/components/forms/Field.stories.svelte.d.ts +19 -0
  58. package/dist/components/forms/Field.svelte +77 -0
  59. package/dist/components/forms/Field.svelte.d.ts +17 -0
  60. package/dist/components/forms/Input.stories.svelte +58 -0
  61. package/dist/components/forms/Input.stories.svelte.d.ts +19 -0
  62. package/dist/components/forms/Input.svelte +64 -0
  63. package/dist/components/forms/Input.svelte.d.ts +9 -0
  64. package/dist/components/forms/InputWrap.composition.stories.svelte +32 -0
  65. package/dist/components/forms/InputWrap.composition.stories.svelte.d.ts +19 -0
  66. package/dist/components/forms/InputWrap.stories.svelte +53 -0
  67. package/dist/components/forms/InputWrap.stories.svelte.d.ts +19 -0
  68. package/dist/components/forms/InputWrap.svelte +128 -0
  69. package/dist/components/forms/InputWrap.svelte.d.ts +21 -0
  70. package/dist/components/forms/Radio.stories.svelte +70 -0
  71. package/dist/components/forms/Radio.stories.svelte.d.ts +19 -0
  72. package/dist/components/forms/Radio.svelte +109 -0
  73. package/dist/components/forms/Radio.svelte.d.ts +9 -0
  74. package/dist/components/forms/RadioGroup.stories.svelte +115 -0
  75. package/dist/components/forms/RadioGroup.stories.svelte.d.ts +19 -0
  76. package/dist/components/forms/RadioGroup.svelte +116 -0
  77. package/dist/components/forms/RadioGroup.svelte.d.ts +24 -0
  78. package/dist/components/forms/Select.stories.svelte +168 -0
  79. package/dist/components/forms/Select.stories.svelte.d.ts +19 -0
  80. package/dist/components/forms/Select.svelte +262 -0
  81. package/dist/components/forms/Select.svelte.d.ts +23 -0
  82. package/dist/components/forms/Switch.stories.svelte +86 -0
  83. package/dist/components/forms/Switch.stories.svelte.d.ts +19 -0
  84. package/dist/components/forms/Switch.svelte +113 -0
  85. package/dist/components/forms/Switch.svelte.d.ts +11 -0
  86. package/dist/components/forms/Textarea.stories.svelte +40 -0
  87. package/dist/components/forms/Textarea.stories.svelte.d.ts +19 -0
  88. package/dist/components/forms/Textarea.svelte +66 -0
  89. package/dist/components/forms/Textarea.svelte.d.ts +9 -0
  90. package/dist/components/forms/field-context.d.ts +7 -0
  91. package/dist/components/forms/field-context.js +1 -0
  92. package/dist/components/forms/index.d.ts +9 -0
  93. package/dist/components/forms/index.js +9 -0
  94. package/dist/components/layout/Container.stories.svelte +67 -0
  95. package/dist/components/layout/Container.stories.svelte.d.ts +19 -0
  96. package/dist/components/layout/Container.svelte +52 -0
  97. package/dist/components/layout/Container.svelte.d.ts +14 -0
  98. package/dist/components/layout/Grid.stories.svelte +109 -0
  99. package/dist/components/layout/Grid.stories.svelte.d.ts +19 -0
  100. package/dist/components/layout/Grid.svelte +54 -0
  101. package/dist/components/layout/Grid.svelte.d.ts +19 -0
  102. package/dist/components/layout/Inline.stories.svelte +136 -0
  103. package/dist/components/layout/Inline.stories.svelte.d.ts +19 -0
  104. package/dist/components/layout/Inline.svelte +46 -0
  105. package/dist/components/layout/Inline.svelte.d.ts +19 -0
  106. package/dist/components/layout/Prose.stories.svelte +423 -0
  107. package/dist/components/layout/Prose.stories.svelte.d.ts +19 -0
  108. package/dist/components/layout/Prose.svelte +176 -0
  109. package/dist/components/layout/Prose.svelte.d.ts +12 -0
  110. package/dist/components/layout/Rule.stories.svelte +80 -0
  111. package/dist/components/layout/Rule.stories.svelte.d.ts +19 -0
  112. package/dist/components/layout/Rule.svelte +33 -0
  113. package/dist/components/layout/Rule.svelte.d.ts +9 -0
  114. package/dist/components/layout/Spread.stories.svelte +118 -0
  115. package/dist/components/layout/Spread.stories.svelte.d.ts +19 -0
  116. package/dist/components/layout/Spread.svelte +38 -0
  117. package/dist/components/layout/Spread.svelte.d.ts +16 -0
  118. package/dist/components/layout/Stack.stories.svelte +90 -0
  119. package/dist/components/layout/Stack.stories.svelte.d.ts +19 -0
  120. package/dist/components/layout/Stack.svelte +37 -0
  121. package/dist/components/layout/Stack.svelte.d.ts +16 -0
  122. package/dist/components/layout/index.d.ts +7 -0
  123. package/dist/components/layout/index.js +7 -0
  124. package/dist/components/navigation/Breadcrumb.stories.svelte +122 -0
  125. package/dist/components/navigation/Breadcrumb.stories.svelte.d.ts +19 -0
  126. package/dist/components/navigation/Breadcrumb.svelte +70 -0
  127. package/dist/components/navigation/Breadcrumb.svelte.d.ts +13 -0
  128. package/dist/components/navigation/Nav.stories.svelte +323 -0
  129. package/dist/components/navigation/Nav.stories.svelte.d.ts +19 -0
  130. package/dist/components/navigation/Nav.svelte +257 -0
  131. package/dist/components/navigation/Nav.svelte.d.ts +21 -0
  132. package/dist/components/navigation/index.d.ts +2 -0
  133. package/dist/components/navigation/index.js +2 -0
  134. package/dist/components/patterns/ActivityRow.stories.svelte +45 -0
  135. package/dist/components/patterns/ActivityRow.stories.svelte.d.ts +19 -0
  136. package/dist/components/patterns/ActivityRow.svelte +69 -0
  137. package/dist/components/patterns/ActivityRow.svelte.d.ts +16 -0
  138. package/dist/components/patterns/Alert.stories.svelte +63 -0
  139. package/dist/components/patterns/Alert.stories.svelte.d.ts +19 -0
  140. package/dist/components/patterns/Alert.svelte +91 -0
  141. package/dist/components/patterns/Alert.svelte.d.ts +16 -0
  142. package/dist/components/patterns/CtaBlock.stories.svelte +62 -0
  143. package/dist/components/patterns/CtaBlock.stories.svelte.d.ts +19 -0
  144. package/dist/components/patterns/CtaBlock.svelte +80 -0
  145. package/dist/components/patterns/CtaBlock.svelte.d.ts +16 -0
  146. package/dist/components/patterns/KvList.stories.svelte +48 -0
  147. package/dist/components/patterns/KvList.stories.svelte.d.ts +19 -0
  148. package/dist/components/patterns/KvList.svelte +65 -0
  149. package/dist/components/patterns/KvList.svelte.d.ts +15 -0
  150. package/dist/components/patterns/PageHero.stories.svelte +62 -0
  151. package/dist/components/patterns/PageHero.stories.svelte.d.ts +19 -0
  152. package/dist/components/patterns/PageHero.svelte +62 -0
  153. package/dist/components/patterns/PageHero.svelte.d.ts +14 -0
  154. package/dist/components/patterns/ProgressBar.stories.svelte +83 -0
  155. package/dist/components/patterns/ProgressBar.stories.svelte.d.ts +19 -0
  156. package/dist/components/patterns/ProgressBar.svelte +71 -0
  157. package/dist/components/patterns/ProgressBar.svelte.d.ts +13 -0
  158. package/dist/components/patterns/SectionFoot.stories.svelte +37 -0
  159. package/dist/components/patterns/SectionFoot.stories.svelte.d.ts +19 -0
  160. package/dist/components/patterns/SectionFoot.svelte +70 -0
  161. package/dist/components/patterns/SectionFoot.svelte.d.ts +15 -0
  162. package/dist/components/patterns/SectionHead.stories.svelte +67 -0
  163. package/dist/components/patterns/SectionHead.stories.svelte.d.ts +19 -0
  164. package/dist/components/patterns/SectionHead.svelte +54 -0
  165. package/dist/components/patterns/SectionHead.svelte.d.ts +14 -0
  166. package/dist/components/patterns/StatCard.stories.svelte +59 -0
  167. package/dist/components/patterns/StatCard.stories.svelte.d.ts +19 -0
  168. package/dist/components/patterns/StatCard.svelte +57 -0
  169. package/dist/components/patterns/StatCard.svelte.d.ts +15 -0
  170. package/dist/components/patterns/index.d.ts +9 -0
  171. package/dist/components/patterns/index.js +9 -0
  172. package/dist/components/primitives/Button.stories.svelte +132 -0
  173. package/dist/components/primitives/Button.stories.svelte.d.ts +19 -0
  174. package/dist/components/primitives/Button.svelte +142 -0
  175. package/dist/components/primitives/Button.svelte.d.ts +16 -0
  176. package/dist/components/primitives/Heading.stories.svelte +137 -0
  177. package/dist/components/primitives/Heading.stories.svelte.d.ts +19 -0
  178. package/dist/components/primitives/Heading.svelte +107 -0
  179. package/dist/components/primitives/Heading.svelte.d.ts +23 -0
  180. package/dist/components/primitives/Led.stories.svelte +63 -0
  181. package/dist/components/primitives/Led.stories.svelte.d.ts +19 -0
  182. package/dist/components/primitives/Led.svelte +65 -0
  183. package/dist/components/primitives/Led.svelte.d.ts +11 -0
  184. package/dist/components/primitives/TagPill.stories.svelte +90 -0
  185. package/dist/components/primitives/TagPill.stories.svelte.d.ts +19 -0
  186. package/dist/components/primitives/TagPill.svelte +44 -0
  187. package/dist/components/primitives/TagPill.svelte.d.ts +9 -0
  188. package/dist/components/primitives/Text.stories.svelte +252 -0
  189. package/dist/components/primitives/Text.stories.svelte.d.ts +19 -0
  190. package/dist/components/primitives/Text.svelte +101 -0
  191. package/dist/components/primitives/Text.svelte.d.ts +25 -0
  192. package/dist/components/primitives/index.d.ts +5 -0
  193. package/dist/components/primitives/index.js +5 -0
  194. package/dist/index.d.ts +10 -0
  195. package/dist/index.js +10 -0
  196. package/dist/stores/toast.d.ts +19 -0
  197. package/dist/stores/toast.js +22 -0
  198. package/dist/storybook-utils.d.ts +11 -0
  199. package/dist/storybook-utils.js +29 -0
  200. package/dist/tokens/ColorSwatch.svelte +73 -0
  201. package/dist/tokens/ColorSwatch.svelte.d.ts +10 -0
  202. package/dist/tokens/layout.css +144 -0
  203. package/dist/tokens/patterns.css +281 -0
  204. package/dist/tokens/tokens.css +96 -0
  205. package/dist/tokens/tokens.stories.svelte +107 -0
  206. package/dist/tokens/tokens.stories.svelte.d.ts +18 -0
  207. package/dist/tokens/typography.css +159 -0
  208. package/package.json +62 -0
@@ -0,0 +1,21 @@
1
+ interface BreadcrumbItem {
2
+ label: string;
3
+ href: string;
4
+ }
5
+ interface NavLink {
6
+ href: string;
7
+ label: string;
8
+ active?: boolean;
9
+ }
10
+ interface Props {
11
+ /** Navigation links to render in the top bar. @default [] */
12
+ links?: NavLink[];
13
+ /** Brand name shown next to the status LED. @default 'DEXTERLABS' */
14
+ siteName?: string;
15
+ /** Optional breadcrumb trail. @default [] */
16
+ breadcrumbs?: BreadcrumbItem[];
17
+ [key: string]: unknown;
18
+ }
19
+ declare const Nav: import("svelte").Component<Props, {}, "">;
20
+ type Nav = ReturnType<typeof Nav>;
21
+ export default Nav;
@@ -0,0 +1,2 @@
1
+ export { default as Nav } from './Nav.svelte';
2
+ export { default as Breadcrumb } from './Breadcrumb.svelte';
@@ -0,0 +1,2 @@
1
+ export { default as Nav } from './Nav.svelte';
2
+ export { default as Breadcrumb } from './Breadcrumb.svelte';
@@ -0,0 +1,45 @@
1
+ <script module lang="ts">
2
+ import { defineMeta } from "@storybook/addon-svelte-csf";
3
+ import { expect, within } from "storybook/test";
4
+ import ActivityRow from "./ActivityRow.svelte";
5
+
6
+ const { Story } = defineMeta({
7
+ title: "Patterns/ActivityRow",
8
+ component: ActivityRow,
9
+ tags: ["autodocs"],
10
+ });
11
+ </script>
12
+
13
+ <Story name="Ok Status" args={{ timestamp: "14:22", description: "PDX-2 thermal check passed", status: "ok" }}
14
+ play={async ({ canvasElement }) => {
15
+ const canvas = within(canvasElement);
16
+ const root = canvasElement.firstElementChild;
17
+ await expect(root).toBeVisible();
18
+ await expect(canvas.getByText("14:22")).toBeVisible();
19
+ await expect(canvas.getByText("PDX-2 thermal check passed")).toBeVisible();
20
+ const led = canvasElement.querySelector(".led");
21
+ await expect(led).toBeVisible();
22
+ }} />
23
+
24
+ <Story name="Amber Status" args={{ timestamp: "13:55", description: "New order: DISTRANS-AR1 · #DXL-0044", status: "amber" }}
25
+ play={async ({ canvasElement }) => {
26
+ const canvas = within(canvasElement);
27
+ await expect(canvas.getByText("13:55")).toBeVisible();
28
+ await expect(canvas.getByText(/New order/i)).toBeVisible();
29
+ }} />
30
+
31
+ <Story name="With Actor" args={{ timestamp: "09:30", actor: "PDX-2", description: "All rails nominal", status: "ok" }}
32
+ play={async ({ canvasElement }) => {
33
+ const canvas = within(canvasElement);
34
+ await expect(canvas.getByText("PDX-2")).toBeVisible();
35
+ await expect(canvas.getByText("All rails nominal")).toBeVisible();
36
+ }} />
37
+
38
+ <Story name="Off Status" args={{ timestamp: "08:00", description: "System boot", status: "off" }}
39
+ play={async ({ canvasElement }) => {
40
+ const canvas = within(canvasElement);
41
+ const root = canvasElement.firstElementChild;
42
+ await expect(root).toBeVisible();
43
+ await expect(canvas.getByText("System boot")).toBeVisible();
44
+ }} />
45
+
@@ -0,0 +1,19 @@
1
+ import ActivityRow from "./ActivityRow.svelte";
2
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
+ $$bindings?: Bindings;
5
+ } & Exports;
6
+ (internal: unknown, props: {
7
+ $$events?: Events;
8
+ $$slots?: Slots;
9
+ }): Exports & {
10
+ $set?: any;
11
+ $on?: any;
12
+ };
13
+ z_$$bindings?: Bindings;
14
+ }
15
+ declare const ActivityRow: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
16
+ [evt: string]: CustomEvent<any>;
17
+ }, {}, {}, string>;
18
+ type ActivityRow = InstanceType<typeof ActivityRow>;
19
+ export default ActivityRow;
@@ -0,0 +1,69 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements'
3
+ import Led from '../primitives/Led.svelte'
4
+ import Inline from '../layout/Inline.svelte'
5
+
6
+ type LedStatus = 'ok' | 'amber' | 'danger' | 'off'
7
+
8
+ interface Props extends HTMLAttributes<HTMLDivElement> {
9
+ /** Timestamp string displayed on the left. */
10
+ timestamp: string
11
+ /** Optional actor name shown before the description. */
12
+ actor?: string
13
+ /** Event description text. */
14
+ description: string
15
+ /** LED status colour. @default 'off' */
16
+ status?: LedStatus
17
+ [key: string]: unknown
18
+ }
19
+
20
+ let {
21
+ timestamp,
22
+ actor,
23
+ description,
24
+ status = 'off',
25
+ ...rest
26
+ }: Props = $props()
27
+ </script>
28
+
29
+ <div class="activity-row" {...rest}>
30
+ <Inline gap="sm">
31
+ <span class="activity-time">{timestamp}</span>
32
+ <Led color={status} />
33
+ <span class="activity-msg">
34
+ {#if actor}
35
+ <span class="activity-actor">{actor}</span>
36
+ <span aria-hidden="true"> · </span>
37
+ {/if}
38
+ {description}
39
+ </span>
40
+ </Inline>
41
+ </div>
42
+
43
+ <style>
44
+ .activity-row {
45
+ padding: 7px 0;
46
+ border-bottom: 1px dashed var(--rule);
47
+ font-family: var(--mono);
48
+ font-size: var(--t-micro);
49
+ letter-spacing: 0.04em;
50
+ }
51
+
52
+ .activity-row:last-child {
53
+ border-bottom: none;
54
+ }
55
+
56
+ .activity-time {
57
+ color: var(--ink-faint);
58
+ flex-shrink: 0;
59
+ }
60
+
61
+ .activity-msg {
62
+ color: var(--ink-dim);
63
+ flex: 1;
64
+ }
65
+
66
+ .activity-actor {
67
+ color: var(--ink);
68
+ }
69
+ </style>
@@ -0,0 +1,16 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ type LedStatus = 'ok' | 'amber' | 'danger' | 'off';
3
+ interface Props extends HTMLAttributes<HTMLDivElement> {
4
+ /** Timestamp string displayed on the left. */
5
+ timestamp: string;
6
+ /** Optional actor name shown before the description. */
7
+ actor?: string;
8
+ /** Event description text. */
9
+ description: string;
10
+ /** LED status colour. @default 'off' */
11
+ status?: LedStatus;
12
+ [key: string]: unknown;
13
+ }
14
+ declare const ActivityRow: import("svelte").Component<Props, {}, "">;
15
+ type ActivityRow = ReturnType<typeof ActivityRow>;
16
+ export default ActivityRow;
@@ -0,0 +1,63 @@
1
+ <script module lang="ts">
2
+ import { defineMeta } from "@storybook/addon-svelte-csf";
3
+ import { expect, within } from "storybook/test";
4
+ import Alert from "./Alert.svelte";
5
+ import { resolveTokenFgColor } from "../../storybook-utils.js";
6
+
7
+ const { Story } = defineMeta({
8
+ title: "Patterns/Alert",
9
+ component: Alert,
10
+ tags: ["autodocs"],
11
+ });
12
+ </script>
13
+
14
+ <Story name="Ok" args={{ variant: "ok", title: "Build successful", message: "All rails nominal." }}
15
+ play={async ({ canvasElement }) => {
16
+ const canvas = within(canvasElement);
17
+ const root = canvas.getByRole("status");
18
+ await expect(root).toBeVisible();
19
+ await expect(canvas.getByText("Build successful")).toBeVisible();
20
+ await expect(canvas.getByText("All rails nominal.")).toBeVisible();
21
+ const tag = canvas.getByText("ok");
22
+ await expect(tag).toHaveAttribute("aria-hidden", "true");
23
+ const okColor = resolveTokenFgColor("--ok");
24
+ await expect(getComputedStyle(root).borderLeftColor).toBe(okColor);
25
+ }} />
26
+
27
+ <Story name="Amber" args={{ variant: "amber", title: "High load", message: "+12V rail at 88%." }}
28
+ play={async ({ canvasElement }) => {
29
+ const canvas = within(canvasElement);
30
+ const root = canvas.getByRole("status");
31
+ await expect(root).toBeVisible();
32
+ await expect(canvas.getByText("High load")).toBeVisible();
33
+ const amberColor = resolveTokenFgColor("--amber");
34
+ await expect(getComputedStyle(root).borderLeftColor).toBe(amberColor);
35
+ }} />
36
+
37
+ <Story name="Danger" args={{ variant: "danger", title: "Thermal fault", message: "Over-temperature protection triggered." }}
38
+ play={async ({ canvasElement }) => {
39
+ const canvas = within(canvasElement);
40
+ const root = canvas.getByRole("status");
41
+ await expect(root).toBeVisible();
42
+ await expect(canvas.getByText("Thermal fault")).toBeVisible();
43
+ const dangerColor = resolveTokenFgColor("--danger");
44
+ await expect(getComputedStyle(root).borderLeftColor).toBe(dangerColor);
45
+ }} />
46
+
47
+ <Story name="Info" args={{ variant: "info", title: "Firmware update available", message: "v2.1.0 → v2.2.0." }}
48
+ play={async ({ canvasElement }) => {
49
+ const canvas = within(canvasElement);
50
+ const root = canvas.getByRole("status");
51
+ await expect(root).toBeVisible();
52
+ const cyanColor = resolveTokenFgColor("--cyan");
53
+ await expect(getComputedStyle(root).borderLeftColor).toBe(cyanColor);
54
+ }} />
55
+
56
+ <Story name="No Message" args={{ variant: "ok", title: "Status OK" }}
57
+ play={async ({ canvasElement }) => {
58
+ const canvas = within(canvasElement);
59
+ const root = canvas.getByRole("status");
60
+ await expect(root).toBeVisible();
61
+ await expect(canvas.getByText("Status OK")).toBeVisible();
62
+ await expect(canvasElement.querySelector(".alert-msg")).toBeNull();
63
+ }} />
@@ -0,0 +1,19 @@
1
+ import Alert from "./Alert.svelte";
2
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
+ $$bindings?: Bindings;
5
+ } & Exports;
6
+ (internal: unknown, props: {
7
+ $$events?: Events;
8
+ $$slots?: Slots;
9
+ }): Exports & {
10
+ $set?: any;
11
+ $on?: any;
12
+ };
13
+ z_$$bindings?: Bindings;
14
+ }
15
+ declare const Alert: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
16
+ [evt: string]: CustomEvent<any>;
17
+ }, {}, {}, string>;
18
+ type Alert = InstanceType<typeof Alert>;
19
+ export default Alert;
@@ -0,0 +1,91 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements'
3
+ import type { Snippet } from 'svelte'
4
+ import Stack from '../layout/Stack.svelte'
5
+
6
+ type AlertVariant = 'ok' | 'amber' | 'danger' | 'info'
7
+
8
+ interface Props extends HTMLAttributes<HTMLDivElement> {
9
+ /** Colour variant — drives the tag glyph and border accent. @default 'info' */
10
+ variant?: AlertVariant
11
+ /** Bold title text shown in the alert header. */
12
+ title: string
13
+ /** Body message text. */
14
+ message?: string
15
+ children?: Snippet
16
+ [key: string]: unknown
17
+ }
18
+
19
+ let {
20
+ variant = 'info',
21
+ title,
22
+ message,
23
+ children,
24
+ ...rest
25
+ }: Props = $props()
26
+
27
+ const TAG_GLYPHS: Record<AlertVariant, string> = {
28
+ ok: 'ok',
29
+ amber: '!!',
30
+ danger: 'err',
31
+ info: 'inf',
32
+ }
33
+ </script>
34
+
35
+ <div class="alert alert--{variant}" role="status" {...rest}>
36
+ <span class="alert-tag" aria-hidden="true">{TAG_GLYPHS[variant]}</span>
37
+ <Stack gap="sm">
38
+ <span class="alert-title">{title}</span>
39
+ {#if message}
40
+ <span class="alert-msg">{message}</span>
41
+ {/if}
42
+ {@render children?.()}
43
+ </Stack>
44
+ </div>
45
+
46
+ <style>
47
+ .alert {
48
+ display: flex;
49
+ align-items: flex-start;
50
+ gap: 12px;
51
+ padding: 12px 16px;
52
+ border-left: 2px solid;
53
+ font-size: var(--t-body);
54
+ line-height: 1.5;
55
+ }
56
+
57
+ .alert--ok { border-color: var(--ok); background: color-mix(in srgb, var(--ok) 8%, transparent); }
58
+ .alert--amber { border-color: var(--amber); background: color-mix(in srgb, var(--amber) 8%, transparent); }
59
+ .alert--danger { border-color: var(--danger); background: color-mix(in srgb, var(--danger) 8%, transparent); }
60
+ .alert--info { border-color: var(--cyan); background: color-mix(in srgb, var(--cyan) 8%, transparent); }
61
+
62
+ .alert-tag {
63
+ font-family: var(--mono);
64
+ font-size: var(--t-micro);
65
+ letter-spacing: 0.08em;
66
+ text-transform: uppercase;
67
+ flex-shrink: 0;
68
+ margin-top: 2px;
69
+ }
70
+ .alert--ok .alert-tag { color: var(--ok); }
71
+ .alert--amber .alert-tag { color: var(--amber); }
72
+ .alert--danger .alert-tag { color: var(--danger); }
73
+ .alert--info .alert-tag { color: var(--cyan); }
74
+
75
+ .alert-title {
76
+ font-family: var(--mono);
77
+ font-size: var(--t-micro);
78
+ letter-spacing: 0.08em;
79
+ text-transform: uppercase;
80
+ }
81
+ .alert--ok .alert-title { color: var(--ok); }
82
+ .alert--amber .alert-title { color: var(--amber); }
83
+ .alert--danger .alert-title { color: var(--danger); }
84
+ .alert--info .alert-title { color: var(--cyan); }
85
+
86
+ .alert-msg {
87
+ font-size: var(--t-mono);
88
+ color: var(--ink-dim);
89
+ line-height: 1.5;
90
+ }
91
+ </style>
@@ -0,0 +1,16 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ import type { Snippet } from 'svelte';
3
+ type AlertVariant = 'ok' | 'amber' | 'danger' | 'info';
4
+ interface Props extends HTMLAttributes<HTMLDivElement> {
5
+ /** Colour variant — drives the tag glyph and border accent. @default 'info' */
6
+ variant?: AlertVariant;
7
+ /** Bold title text shown in the alert header. */
8
+ title: string;
9
+ /** Body message text. */
10
+ message?: string;
11
+ children?: Snippet;
12
+ [key: string]: unknown;
13
+ }
14
+ declare const Alert: import("svelte").Component<Props, {}, "">;
15
+ type Alert = ReturnType<typeof Alert>;
16
+ export default Alert;
@@ -0,0 +1,62 @@
1
+ <script module lang="ts">
2
+ import { defineMeta } from "@storybook/addon-svelte-csf";
3
+ import { expect, within } from "storybook/test";
4
+ import CtaBlock from "./CtaBlock.svelte";
5
+ import Button from "../primitives/Button.svelte";
6
+ import { resolveTokenFgColor } from "../../storybook-utils.js";
7
+
8
+ const { Story } = defineMeta({
9
+ title: "Patterns/CtaBlock",
10
+ component: CtaBlock,
11
+ tags: ["autodocs"],
12
+ });
13
+ </script>
14
+
15
+ <Story name="Default" args={{ heading: "Conduit PDX-2", eyebrow: "// CATALOGUE · HARDWARE", subtext: "Power your entire Eurorack from a laptop charger." }}
16
+ play={async ({ canvasElement }) => {
17
+ const canvas = within(canvasElement);
18
+ const root = canvasElement.firstElementChild as HTMLElement;
19
+ await expect(root).toBeVisible();
20
+ await expect(canvas.getByText("Conduit PDX-2")).toBeVisible();
21
+ await expect(canvas.getByText(/CATALOGUE · HARDWARE/i)).toBeVisible();
22
+ await expect(canvas.getByText("Power your entire Eurorack from a laptop charger.")).toBeVisible();
23
+ const amberColor = resolveTokenFgColor("--amber");
24
+ await expect(getComputedStyle(root).borderColor).toBe(amberColor);
25
+ }}>
26
+ VIEW →
27
+ </Story>
28
+
29
+ <Story name="No Eyebrow" args={{ heading: "View All Projects" }}
30
+ play={async ({ canvasElement }) => {
31
+ const canvas = within(canvasElement);
32
+ await expect(canvas.getByText("View All Projects")).toBeVisible();
33
+ await expect(canvasElement.querySelector(".cta-eyebrow")).toBeNull();
34
+ }} />
35
+
36
+ <Story name="As Link" args={{ as: "a", href: "/catalogue", heading: "Browse Catalogue", subtext: "All Dexterlabs products." }}
37
+ play={async ({ canvasElement }) => {
38
+ const anchor = canvasElement.querySelector("a");
39
+ await expect(anchor).toHaveAttribute("href", "/catalogue");
40
+ await expect(anchor!.tagName).toBe("A");
41
+ }}>
42
+ BROWSE →
43
+ </Story>
44
+
45
+ <Story name="Border Color" args={{ heading: "Check the Border" }}
46
+ play={async ({ canvasElement }) => {
47
+ const root = canvasElement.firstElementChild!;
48
+ const amberColor = resolveTokenFgColor("--amber");
49
+ await expect(getComputedStyle(root).borderColor).toBe(amberColor);
50
+ }} />
51
+
52
+ <Story name="Full Props" args={{ eyebrow: "// CATALOGUE · HARDWARE", heading: "Conduit PDX-2", subtext: "Power your entire Eurorack from a laptop charger." }}
53
+ play={async ({ canvasElement }) => {
54
+ const canvas = within(canvasElement);
55
+ await expect(canvas.getByText(/CATALOGUE · HARDWARE/i)).toBeVisible();
56
+ await expect(canvas.getByText("Conduit PDX-2")).toBeVisible();
57
+ await expect(canvas.getByText("Power your entire Eurorack from a laptop charger.")).toBeVisible();
58
+ await expect(canvas.getByRole("button", { name: /ORDER NOW/i })).toBeVisible();
59
+ await expect(canvasElement.querySelector(".cta-eyebrow")).not.toBeNull();
60
+ }}>
61
+ <Button variant="primary">ORDER NOW</Button>
62
+ </Story>
@@ -0,0 +1,19 @@
1
+ import CtaBlock from "./CtaBlock.svelte";
2
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
+ $$bindings?: Bindings;
5
+ } & Exports;
6
+ (internal: unknown, props: {
7
+ $$events?: Events;
8
+ $$slots?: Slots;
9
+ }): Exports & {
10
+ $set?: any;
11
+ $on?: any;
12
+ };
13
+ z_$$bindings?: Bindings;
14
+ }
15
+ declare const CtaBlock: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
16
+ [evt: string]: CustomEvent<any>;
17
+ }, {}, {}, string>;
18
+ type CtaBlock = InstanceType<typeof CtaBlock>;
19
+ export default CtaBlock;
@@ -0,0 +1,80 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte'
3
+ import Stack from '../layout/Stack.svelte'
4
+ import Spread from '../layout/Spread.svelte'
5
+ import Text from '../primitives/Text.svelte'
6
+
7
+ interface Props {
8
+ /** HTML element to render as. @default 'div' */
9
+ as?: string
10
+ /** Small mono label shown above the heading. */
11
+ eyebrow?: string
12
+ /** Primary heading text. */
13
+ heading: string
14
+ /** Secondary descriptive text below the heading. */
15
+ subtext?: string
16
+ children?: Snippet
17
+ [key: string]: unknown
18
+ }
19
+
20
+ let {
21
+ as = 'div',
22
+ eyebrow,
23
+ heading,
24
+ subtext,
25
+ children,
26
+ ...rest
27
+ }: Props = $props()
28
+ </script>
29
+
30
+ <svelte:element this={as} class="cta-block" {...rest}>
31
+ <Spread gap="md">
32
+ <Stack gap="xs">
33
+ {#if eyebrow}
34
+ <Text variant="eyebrow" class="cta-eyebrow">{eyebrow}</Text>
35
+ {/if}
36
+ <span class="cta-heading">{heading}</span>
37
+ {#if subtext}
38
+ <span class="cta-desc">{subtext}</span>
39
+ {/if}
40
+ </Stack>
41
+ {#if children}
42
+ <div class="cta-action">
43
+ {@render children()}
44
+ </div>
45
+ {/if}
46
+ </Spread>
47
+ </svelte:element>
48
+
49
+ <style>
50
+ .cta-block {
51
+ border: 1px solid var(--amber);
52
+ padding: 24px 32px;
53
+ color: inherit;
54
+ transition: background var(--transition);
55
+ cursor: pointer;
56
+ text-decoration: none;
57
+ }
58
+
59
+ .cta-block:hover {
60
+ background: color-mix(in srgb, var(--amber) 6%, transparent);
61
+ }
62
+
63
+ .cta-heading {
64
+ font-weight: 500;
65
+ font-size: var(--t-lede);
66
+ letter-spacing: -0.01em;
67
+ color: var(--ink);
68
+ }
69
+
70
+ .cta-desc {
71
+ font-family: var(--mono);
72
+ font-size: var(--t-mono);
73
+ color: var(--ink-dim);
74
+ line-height: 1.4;
75
+ }
76
+
77
+ .cta-action {
78
+ flex-shrink: 0;
79
+ }
80
+ </style>
@@ -0,0 +1,16 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ /** HTML element to render as. @default 'div' */
4
+ as?: string;
5
+ /** Small mono label shown above the heading. */
6
+ eyebrow?: string;
7
+ /** Primary heading text. */
8
+ heading: string;
9
+ /** Secondary descriptive text below the heading. */
10
+ subtext?: string;
11
+ children?: Snippet;
12
+ [key: string]: unknown;
13
+ }
14
+ declare const CtaBlock: import("svelte").Component<Props, {}, "">;
15
+ type CtaBlock = ReturnType<typeof CtaBlock>;
16
+ export default CtaBlock;
@@ -0,0 +1,48 @@
1
+ <script module lang="ts">
2
+ import { defineMeta } from "@storybook/addon-svelte-csf";
3
+ import { expect, within } from "storybook/test";
4
+ import KvList from "./KvList.svelte";
5
+ import { resolveTokenFgColor } from "../../storybook-utils.js";
6
+
7
+ const { Story } = defineMeta({
8
+ title: "Patterns/KvList",
9
+ component: KvList,
10
+ tags: ["autodocs"],
11
+ });
12
+ </script>
13
+
14
+ <Story name="Default" args={{ items: [{ key: "+12V Output", value: "3.0A" }, { key: "-12V Output", value: "1.5A" }, { key: "Efficiency", value: ">92%", color: "amber" }, { key: "Width", value: "4 HP" }] }}
15
+ play={async ({ canvasElement }) => {
16
+ const canvas = within(canvasElement);
17
+ const root = canvasElement.firstElementChild;
18
+ await expect(root).toBeVisible();
19
+ await expect(canvas.getByText(/\+12V Output/i)).toBeVisible();
20
+ await expect(canvas.getByText("3.0A")).toBeVisible();
21
+ const amberColor = resolveTokenFgColor("--amber");
22
+ await expect(getComputedStyle(canvas.getByText(">92%")).color).toBe(amberColor);
23
+ const rows = canvasElement.querySelectorAll(".kv-row");
24
+ await expect(rows.length).toBe(4);
25
+ const lastRow = rows[rows.length - 1];
26
+ await expect(getComputedStyle(lastRow).borderBottomStyle).toBe("none");
27
+ }} />
28
+
29
+ <Story name="Single Item" args={{ items: [{ key: "Status", value: "Nominal", color: "ok" }] }}
30
+ play={async ({ canvasElement }) => {
31
+ const canvas = within(canvasElement);
32
+ const rows = canvasElement.querySelectorAll(".kv-row");
33
+ await expect(rows.length).toBe(1);
34
+ const okColor = resolveTokenFgColor("--ok");
35
+ await expect(getComputedStyle(canvas.getByText("Nominal")).color).toBe(okColor);
36
+ await expect(getComputedStyle(rows[0]).borderBottomStyle).toBe("none");
37
+ }} />
38
+
39
+ <Story name="All Colors" args={{ items: [{ key: "Ok", value: "Pass", color: "ok" }, { key: "Amber", value: "Warn", color: "amber" }, { key: "Danger", value: "Fail", color: "danger" }, { key: "Cyan", value: "Info", color: "cyan" }, { key: "Default", value: "Normal" }] }}
40
+ play={async ({ canvasElement }) => {
41
+ const canvas = within(canvasElement);
42
+ const rows = canvasElement.querySelectorAll(".kv-row");
43
+ await expect(rows.length).toBe(5);
44
+ const okColor = resolveTokenFgColor("--ok");
45
+ await expect(getComputedStyle(canvas.getByText("Pass")).color).toBe(okColor);
46
+ const inkColor = resolveTokenFgColor("--ink");
47
+ await expect(getComputedStyle(canvas.getByText("Normal")).color).toBe(inkColor);
48
+ }} />
@@ -0,0 +1,19 @@
1
+ import KvList from "./KvList.svelte";
2
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
+ $$bindings?: Bindings;
5
+ } & Exports;
6
+ (internal: unknown, props: {
7
+ $$events?: Events;
8
+ $$slots?: Slots;
9
+ }): Exports & {
10
+ $set?: any;
11
+ $on?: any;
12
+ };
13
+ z_$$bindings?: Bindings;
14
+ }
15
+ declare const KvList: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
16
+ [evt: string]: CustomEvent<any>;
17
+ }, {}, {}, string>;
18
+ type KvList = InstanceType<typeof KvList>;
19
+ export default KvList;