kumo-svelte 0.8.0 → 0.9.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.
Files changed (90) hide show
  1. package/ai/component-registry.json +23 -4
  2. package/ai/component-registry.md +1 -1
  3. package/dist/components/badge/Badge.svelte +4 -4
  4. package/dist/components/badge/Badge.svelte.d.ts +4 -4
  5. package/dist/components/banner/Banner.svelte +99 -28
  6. package/dist/components/banner/Banner.svelte.d.ts +25 -10
  7. package/dist/components/banner/BannerAction.svelte +64 -0
  8. package/dist/components/banner/BannerAction.svelte.d.ts +13 -0
  9. package/dist/components/banner/banner.test.d.ts +1 -0
  10. package/dist/components/banner/banner.test.js +40 -0
  11. package/dist/components/banner/context.d.ts +8 -0
  12. package/dist/components/banner/context.js +11 -0
  13. package/dist/components/banner/index.d.ts +8 -2
  14. package/dist/components/banner/index.js +7 -1
  15. package/dist/components/breadcrumbs/BreadcrumbsLink.svelte +2 -2
  16. package/dist/components/button/Button.svelte +37 -11
  17. package/dist/components/button/Button.svelte.d.ts +1 -1
  18. package/dist/components/button/LinkButton.svelte +33 -2
  19. package/dist/components/button/LinkButton.svelte.d.ts +4 -0
  20. package/dist/components/button/button.test.js +60 -28
  21. package/dist/components/chart/ChartLegend.svelte +20 -4
  22. package/dist/components/chart/ChartLegend.svelte.d.ts +4 -3
  23. package/dist/components/chart/TimeseriesChart.svelte +130 -29
  24. package/dist/components/clipboard-text/ClipboardText.svelte +21 -12
  25. package/dist/components/collapsible/CollapsibleDefaultPanel.svelte +7 -2
  26. package/dist/components/collapsible/CollapsibleDefaultTrigger.svelte +1 -1
  27. package/dist/components/input-group/InputGroup.svelte +0 -1
  28. package/dist/components/input-group/InputGroupButton.svelte +1 -0
  29. package/dist/components/radio/RadioItem.svelte +1 -1
  30. package/dist/components/sensitive-input/SensitiveInput.svelte +1 -1
  31. package/dist/components/sidebar/Sidebar.svelte +8 -6
  32. package/dist/components/sidebar/Sidebar.svelte.d.ts +1 -0
  33. package/dist/components/sidebar/SidebarClose.svelte +25 -0
  34. package/dist/components/sidebar/SidebarClose.svelte.d.ts +9 -0
  35. package/dist/components/sidebar/SidebarLoading.svelte +30 -0
  36. package/dist/components/sidebar/SidebarLoading.svelte.d.ts +8 -0
  37. package/dist/components/sidebar/index.d.ts +30 -26
  38. package/dist/components/sidebar/index.js +30 -26
  39. package/dist/components/sidebar/sidebar.test.js +57 -29
  40. package/dist/components/table-of-contents/index.d.ts +8 -6
  41. package/dist/components/table-of-contents/index.js +8 -7
  42. package/dist/components/table-of-contents/use-table-of-contents-active-id.svelte.d.ts +16 -0
  43. package/dist/components/table-of-contents/use-table-of-contents-active-id.svelte.js +79 -0
  44. package/dist/components/tabs/Tabs.svelte +71 -5
  45. package/dist/components/tabs/Tabs.svelte.d.ts +5 -0
  46. package/dist/components/tabs/Tabs.test.d.ts +1 -0
  47. package/dist/components/tabs/Tabs.test.js +18 -0
  48. package/dist/components/tabs/index.d.ts +1 -1
  49. package/dist/components/toasty/KumoToastContent.svelte +4 -4
  50. package/dist/components/tooltip/Tooltip.svelte +1 -1
  51. package/dist/index.d.ts +4 -3
  52. package/dist/index.js +2 -2
  53. package/dist/styles.css +1 -1
  54. package/dist/test-fixtures/BannerLinkActionTest.svelte +10 -0
  55. package/dist/test-fixtures/BannerLinkActionTest.svelte.d.ts +18 -0
  56. package/package.json +1 -1
  57. package/src/lib/components/badge/Badge.svelte +4 -4
  58. package/src/lib/components/banner/Banner.svelte +99 -28
  59. package/src/lib/components/banner/BannerAction.svelte +64 -0
  60. package/src/lib/components/banner/banner.test.ts +48 -0
  61. package/src/lib/components/banner/context.ts +22 -0
  62. package/src/lib/components/banner/index.ts +14 -4
  63. package/src/lib/components/breadcrumbs/BreadcrumbsLink.svelte +2 -2
  64. package/src/lib/components/button/Button.svelte +37 -11
  65. package/src/lib/components/button/LinkButton.svelte +33 -2
  66. package/src/lib/components/button/button.test.ts +68 -28
  67. package/src/lib/components/chart/ChartLegend.svelte +20 -4
  68. package/src/lib/components/chart/TimeseriesChart.svelte +130 -29
  69. package/src/lib/components/clipboard-text/ClipboardText.svelte +21 -12
  70. package/src/lib/components/collapsible/CollapsibleDefaultPanel.svelte +7 -2
  71. package/src/lib/components/collapsible/CollapsibleDefaultTrigger.svelte +1 -1
  72. package/src/lib/components/input-group/InputGroup.svelte +0 -1
  73. package/src/lib/components/input-group/InputGroupButton.svelte +1 -0
  74. package/src/lib/components/radio/RadioItem.svelte +1 -1
  75. package/src/lib/components/sensitive-input/SensitiveInput.svelte +1 -1
  76. package/src/lib/components/sidebar/Sidebar.svelte +8 -6
  77. package/src/lib/components/sidebar/SidebarClose.svelte +25 -0
  78. package/src/lib/components/sidebar/SidebarLoading.svelte +30 -0
  79. package/src/lib/components/sidebar/index.ts +36 -28
  80. package/src/lib/components/sidebar/sidebar.test.ts +77 -36
  81. package/src/lib/components/table-of-contents/index.ts +18 -7
  82. package/src/lib/components/table-of-contents/use-table-of-contents-active-id.svelte.ts +109 -0
  83. package/src/lib/components/tabs/Tabs.svelte +71 -5
  84. package/src/lib/components/tabs/Tabs.test.ts +20 -0
  85. package/src/lib/components/tabs/index.ts +1 -1
  86. package/src/lib/components/toasty/KumoToastContent.svelte +4 -4
  87. package/src/lib/components/tooltip/Tooltip.svelte +1 -1
  88. package/src/lib/index.ts +10 -3
  89. package/src/lib/styles.css +71 -56
  90. package/src/lib/test-fixtures/BannerLinkActionTest.svelte +10 -0
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.8.0",
2
+ "version": "0.9.1",
3
3
  "components": {
4
4
  "Autocomplete": {
5
5
  "name": "Autocomplete",
@@ -413,13 +413,25 @@
413
413
  }
414
414
  },
415
415
  "colors": [
416
- "bg-kumo-banner-info",
417
- "bg-kumo-banner-warning",
418
416
  "bg-kumo-contrast",
417
+ "bg-kumo-danger",
419
418
  "bg-kumo-danger-tint",
419
+ "bg-kumo-info",
420
+ "bg-kumo-info-tint",
421
+ "bg-kumo-warning",
422
+ "bg-kumo-warning-tint",
423
+ "fill-kumo-danger",
424
+ "fill-kumo-info",
425
+ "fill-kumo-interact",
426
+ "fill-kumo-subtle",
427
+ "fill-kumo-warning",
428
+ "ring-kumo-danger",
429
+ "ring-kumo-focus",
430
+ "ring-kumo-info",
431
+ "ring-kumo-warning",
420
432
  "text-kumo-danger",
433
+ "text-kumo-default",
421
434
  "text-kumo-info",
422
- "text-kumo-subtle",
423
435
  "text-kumo-warning"
424
436
  ]
425
437
  },
@@ -676,6 +688,7 @@
676
688
  },
677
689
  "colors": [
678
690
  "bg-kumo-base",
691
+ "bg-kumo-fill",
679
692
  "border-kumo-line",
680
693
  "text-kumo-default",
681
694
  "text-kumo-subtle"
@@ -914,6 +927,7 @@
914
927
  },
915
928
  "colors": [
916
929
  "bg-kumo-base",
930
+ "bg-kumo-fill",
917
931
  "border-kumo-line",
918
932
  "text-kumo-default",
919
933
  "text-kumo-subtle"
@@ -989,6 +1003,7 @@
989
1003
  },
990
1004
  "colors": [
991
1005
  "bg-kumo-base",
1006
+ "bg-kumo-fill",
992
1007
  "border-kumo-line",
993
1008
  "text-kumo-default",
994
1009
  "text-kumo-subtle"
@@ -1405,6 +1420,7 @@
1405
1420
  },
1406
1421
  "colors": [
1407
1422
  "bg-kumo-base",
1423
+ "bg-kumo-fill",
1408
1424
  "border-kumo-line",
1409
1425
  "text-kumo-default",
1410
1426
  "text-kumo-subtle"
@@ -4382,6 +4398,7 @@
4382
4398
  },
4383
4399
  "colors": [
4384
4400
  "bg-kumo-base",
4401
+ "bg-kumo-fill",
4385
4402
  "border-kumo-line",
4386
4403
  "text-kumo-default",
4387
4404
  "text-kumo-subtle"
@@ -5723,6 +5740,7 @@
5723
5740
  "colors": [
5724
5741
  "bg-kumo-base",
5725
5742
  "bg-kumo-brand",
5743
+ "bg-kumo-elevated",
5726
5744
  "bg-kumo-recessed",
5727
5745
  "bg-kumo-tint",
5728
5746
  "border-kumo-hairline",
@@ -5972,6 +5990,7 @@
5972
5990
  },
5973
5991
  "colors": [
5974
5992
  "bg-kumo-base",
5993
+ "bg-kumo-fill",
5975
5994
  "border-kumo-line",
5976
5995
  "text-kumo-default",
5977
5996
  "text-kumo-subtle"
@@ -1,6 +1,6 @@
1
1
  # Kumo Svelte Component Registry
2
2
 
3
- Version: 0.8.0
3
+ Version: 0.9.1
4
4
 
5
5
  ## Autocomplete
6
6
 
@@ -3,11 +3,11 @@
3
3
  export const KUMO_BADGE_VARIANTS = {
4
4
  primary: 'bg-kumo-badge-inverted text-kumo-badge-inverted',
5
5
  secondary: 'bg-kumo-fill text-kumo-badge-neutral-subtle',
6
- error: 'bg-kumo-danger-tint/60 text-kumo-danger',
7
- warning: 'bg-kumo-warning-tint/70 text-kumo-warning',
8
- success: 'bg-kumo-success-tint/70 text-kumo-success',
6
+ error: 'bg-kumo-danger-tint text-kumo-danger',
7
+ warning: 'bg-kumo-warning-tint text-kumo-warning',
8
+ success: 'bg-kumo-success-tint text-kumo-success',
9
9
  destructive: 'bg-kumo-badge-red text-white',
10
- info: 'bg-kumo-info-tint/70 text-kumo-info',
10
+ info: 'bg-kumo-info-tint text-kumo-info',
11
11
  beta: 'border border-dashed border-kumo-brand bg-transparent text-kumo-link',
12
12
  outline: 'border border-kumo-fill bg-transparent text-kumo-default',
13
13
  red: 'bg-kumo-badge-red text-white',
@@ -2,11 +2,11 @@ export declare const KUMO_BADGE_BASE_STYLES = "inline-flex w-fit flex-none shrin
2
2
  export declare const KUMO_BADGE_VARIANTS: {
3
3
  readonly primary: "bg-kumo-badge-inverted text-kumo-badge-inverted";
4
4
  readonly secondary: "bg-kumo-fill text-kumo-badge-neutral-subtle";
5
- readonly error: "bg-kumo-danger-tint/60 text-kumo-danger";
6
- readonly warning: "bg-kumo-warning-tint/70 text-kumo-warning";
7
- readonly success: "bg-kumo-success-tint/70 text-kumo-success";
5
+ readonly error: "bg-kumo-danger-tint text-kumo-danger";
6
+ readonly warning: "bg-kumo-warning-tint text-kumo-warning";
7
+ readonly success: "bg-kumo-success-tint text-kumo-success";
8
8
  readonly destructive: "bg-kumo-badge-red text-white";
9
- readonly info: "bg-kumo-info-tint/70 text-kumo-info";
9
+ readonly info: "bg-kumo-info-tint text-kumo-info";
10
10
  readonly beta: "border border-dashed border-kumo-brand bg-transparent text-kumo-link";
11
11
  readonly outline: "border border-kumo-fill bg-transparent text-kumo-default";
12
12
  readonly red: "bg-kumo-badge-red text-white";
@@ -1,56 +1,65 @@
1
1
  <script module lang="ts">
2
2
  import { cn } from '../../utils/cn';
3
3
 
4
- export const KUMO_BANNER_BASE_STYLES =
5
- 'flex w-full items-start gap-3 rounded-lg px-4 py-3 text-base';
4
+ export const KUMO_BANNER_BASE_STYLES = 'flex w-full';
6
5
 
7
6
  export const KUMO_BANNER_VARIANTS = {
8
7
  variant: {
9
8
  default: {
10
- classes: 'bg-kumo-banner-info text-kumo-info',
11
- iconClasses: 'text-kumo-info',
9
+ classes: 'bg-kumo-info-tint text-kumo-info',
10
+ iconClasses: 'fill-kumo-info',
12
11
  description: 'Informational banner for general messages'
13
12
  },
14
13
  alert: {
15
- classes: 'bg-kumo-banner-warning text-kumo-warning',
16
- iconClasses: 'text-kumo-warning',
14
+ classes: 'bg-kumo-warning-tint text-kumo-warning',
15
+ iconClasses: 'fill-kumo-warning',
17
16
  description: 'Warning banner for cautionary messages'
18
17
  },
19
18
  error: {
20
- classes: 'bg-kumo-danger-tint/60 text-kumo-danger',
21
- iconClasses: 'text-kumo-danger',
19
+ classes: 'bg-kumo-danger-tint text-kumo-danger',
20
+ iconClasses: 'fill-kumo-danger',
22
21
  description: 'Error banner for critical issues'
23
22
  },
24
23
  secondary: {
25
- classes: 'bg-kumo-contrast/5 text-kumo-subtle',
26
- iconClasses: 'text-kumo-subtle',
24
+ classes: 'bg-kumo-contrast/5 text-kumo-default/70',
25
+ iconClasses: 'fill-kumo-interact',
27
26
  description: 'Neutral banner for secondary messages'
28
27
  }
28
+ },
29
+ size: {
30
+ base: { classes: 'items-start gap-3 rounded-lg px-4 py-3 text-base', description: 'Default banner size' },
31
+ sm: { classes: 'items-center gap-2 rounded-md px-3 py-2 text-sm', description: 'Compact banner for dialogs and tight spaces' }
29
32
  }
30
33
  } as const;
31
34
 
32
35
  export const KUMO_BANNER_DEFAULT_VARIANTS = {
33
- variant: 'default'
36
+ variant: 'default',
37
+ size: 'base'
34
38
  } as const;
35
39
 
36
40
  type Variant = keyof typeof KUMO_BANNER_VARIANTS.variant;
41
+ type Size = keyof typeof KUMO_BANNER_VARIANTS.size;
37
42
  export type BannerVariant = Variant;
43
+ export type KumoBannerSize = Size;
38
44
 
39
45
  export function bannerVariants({
40
- variant = KUMO_BANNER_DEFAULT_VARIANTS.variant
46
+ variant = KUMO_BANNER_DEFAULT_VARIANTS.variant,
47
+ size = KUMO_BANNER_DEFAULT_VARIANTS.size
41
48
  }: {
42
49
  variant?: Variant;
50
+ size?: Size;
43
51
  } = {}) {
44
52
  const variantConfig =
45
53
  KUMO_BANNER_VARIANTS.variant[variant] ??
46
54
  KUMO_BANNER_VARIANTS.variant[KUMO_BANNER_DEFAULT_VARIANTS.variant];
47
55
 
48
- return cn(KUMO_BANNER_BASE_STYLES, variantConfig.classes);
56
+ return cn(KUMO_BANNER_BASE_STYLES, variantConfig.classes, KUMO_BANNER_VARIANTS.size[size]?.classes ?? KUMO_BANNER_VARIANTS.size.base.classes);
49
57
  }
50
58
  </script>
51
59
 
52
60
  <script lang="ts">
53
61
  import type { Component, Snippet } from 'svelte';
62
+ import { setBannerActionContext } from './context';
54
63
 
55
64
  interface Props {
56
65
  children?: Snippet;
@@ -58,6 +67,7 @@
58
67
  class?: string;
59
68
  icon?: Component;
60
69
  variant?: Variant;
70
+ size?: Size;
61
71
  title?: string;
62
72
  description?: string | Snippet;
63
73
  text?: string;
@@ -70,6 +80,7 @@
70
80
  class: className,
71
81
  icon: IconComponent,
72
82
  variant = 'default',
83
+ size = 'base',
73
84
  title,
74
85
  description,
75
86
  text,
@@ -80,7 +91,36 @@
80
91
  KUMO_BANNER_VARIANTS.variant[variant] ??
81
92
  KUMO_BANNER_VARIANTS.variant[KUMO_BANNER_DEFAULT_VARIANTS.variant]
82
93
  );
83
- const classes = $derived(cn(bannerVariants({ variant }), className));
94
+ const classes = $derived(cn(bannerVariants({ variant, size }), className));
95
+ let actionElement = $state<HTMLSpanElement>();
96
+ let actionIsInlineLink = $state(false);
97
+ let observedAction = $state<Snippet>();
98
+
99
+ $effect(() => {
100
+ if (action !== observedAction) {
101
+ observedAction = action;
102
+ actionIsInlineLink = false;
103
+ }
104
+ });
105
+
106
+ $effect(() => {
107
+ if (size !== 'sm') {
108
+ actionIsInlineLink = false;
109
+ return;
110
+ }
111
+ if (!actionElement) return;
112
+
113
+ actionIsInlineLink = Boolean(actionElement.querySelector('[data-kumo-component="Link"]'));
114
+ });
115
+
116
+ setBannerActionContext({
117
+ get variant() {
118
+ return variant;
119
+ },
120
+ get size() {
121
+ return size === 'sm' ? 'xs' : 'sm';
122
+ }
123
+ });
84
124
  </script>
85
125
 
86
126
  <div class={classes} {...rest}>
@@ -90,20 +130,45 @@
90
130
  <IconComponent weight="fill" />
91
131
  </span>
92
132
  {/if}
93
- <div class={cn('flex min-w-0 flex-1 items-center justify-between gap-3', !title && 'pt-px')}>
94
- <div class="flex flex-col gap-0.5">
95
- {#if title}<p class="font-medium leading-snug">{title}</p>{/if}
96
- {#if description}
97
- <div class="text-sm leading-snug">
98
- {#if typeof description === 'function'}
99
- {@render description()}
100
- {:else}
101
- <p>{description}</p>
102
- {/if}
103
- </div>
104
- {/if}
105
- </div>
106
- {#if action}<div class="flex shrink-0 items-center gap-2">{@render action()}</div>{/if}
133
+ <div class={cn('flex min-w-0 flex-1 items-center justify-between', size === 'sm' ? 'gap-2' : 'gap-3', !title && 'pt-px')}>
134
+ {#if size === 'sm'}
135
+ <div class="flex min-w-0 flex-wrap items-baseline gap-x-1.5">
136
+ {#if title}
137
+ <span class="font-medium leading-snug">
138
+ {title}
139
+ {#if action && actionIsInlineLink && !description}
140
+ <span class="banner-compact-action ml-1.5 contents">{@render action()}</span>
141
+ {/if}
142
+ </span>
143
+ {/if}
144
+ {#if description}
145
+ <span class="text-sm leading-snug">
146
+ {#if typeof description === 'function'}{@render description()}{:else}{description}{/if}
147
+ {#if action && actionIsInlineLink}
148
+ <span class="banner-compact-action ml-1.5 contents">{@render action()}</span>
149
+ {/if}
150
+ </span>
151
+ {/if}
152
+ {#if action && !actionIsInlineLink}
153
+ <span
154
+ bind:this={actionElement}
155
+ class="banner-compact-action ml-auto flex shrink-0 items-center gap-2"
156
+ >
157
+ {@render action()}
158
+ </span>
159
+ {/if}
160
+ </div>
161
+ {:else}
162
+ <div class="flex flex-col gap-0.5">
163
+ {#if title}<p class="font-medium leading-snug">{title}</p>{/if}
164
+ {#if description}
165
+ <div class="text-sm leading-snug">
166
+ {#if typeof description === 'function'}{@render description()}{:else}<p>{description}</p>{/if}
167
+ </div>
168
+ {/if}
169
+ </div>
170
+ {/if}
171
+ {#if action && size !== 'sm'}<div class="flex shrink-0 items-center gap-2">{@render action()}</div>{/if}
107
172
  </div>
108
173
  {:else}
109
174
  {#if IconComponent}
@@ -118,3 +183,9 @@
118
183
  {/if}
119
184
  {/if}
120
185
  </div>
186
+
187
+ <style>
188
+ :global(.banner-compact-action > [data-kumo-component='Link']) {
189
+ margin-left: 0.375rem;
190
+ }
191
+ </style>
@@ -1,35 +1,49 @@
1
- export declare const KUMO_BANNER_BASE_STYLES = "flex w-full items-start gap-3 rounded-lg px-4 py-3 text-base";
1
+ export declare const KUMO_BANNER_BASE_STYLES = "flex w-full";
2
2
  export declare const KUMO_BANNER_VARIANTS: {
3
3
  readonly variant: {
4
4
  readonly default: {
5
- readonly classes: "bg-kumo-banner-info text-kumo-info";
6
- readonly iconClasses: "text-kumo-info";
5
+ readonly classes: "bg-kumo-info-tint text-kumo-info";
6
+ readonly iconClasses: "fill-kumo-info";
7
7
  readonly description: "Informational banner for general messages";
8
8
  };
9
9
  readonly alert: {
10
- readonly classes: "bg-kumo-banner-warning text-kumo-warning";
11
- readonly iconClasses: "text-kumo-warning";
10
+ readonly classes: "bg-kumo-warning-tint text-kumo-warning";
11
+ readonly iconClasses: "fill-kumo-warning";
12
12
  readonly description: "Warning banner for cautionary messages";
13
13
  };
14
14
  readonly error: {
15
- readonly classes: "bg-kumo-danger-tint/60 text-kumo-danger";
16
- readonly iconClasses: "text-kumo-danger";
15
+ readonly classes: "bg-kumo-danger-tint text-kumo-danger";
16
+ readonly iconClasses: "fill-kumo-danger";
17
17
  readonly description: "Error banner for critical issues";
18
18
  };
19
19
  readonly secondary: {
20
- readonly classes: "bg-kumo-contrast/5 text-kumo-subtle";
21
- readonly iconClasses: "text-kumo-subtle";
20
+ readonly classes: "bg-kumo-contrast/5 text-kumo-default/70";
21
+ readonly iconClasses: "fill-kumo-interact";
22
22
  readonly description: "Neutral banner for secondary messages";
23
23
  };
24
24
  };
25
+ readonly size: {
26
+ readonly base: {
27
+ readonly classes: "items-start gap-3 rounded-lg px-4 py-3 text-base";
28
+ readonly description: "Default banner size";
29
+ };
30
+ readonly sm: {
31
+ readonly classes: "items-center gap-2 rounded-md px-3 py-2 text-sm";
32
+ readonly description: "Compact banner for dialogs and tight spaces";
33
+ };
34
+ };
25
35
  };
26
36
  export declare const KUMO_BANNER_DEFAULT_VARIANTS: {
27
37
  readonly variant: "default";
38
+ readonly size: "base";
28
39
  };
29
40
  type Variant = keyof typeof KUMO_BANNER_VARIANTS.variant;
41
+ type Size = keyof typeof KUMO_BANNER_VARIANTS.size;
30
42
  export type BannerVariant = Variant;
31
- export declare function bannerVariants({ variant }?: {
43
+ export type KumoBannerSize = Size;
44
+ export declare function bannerVariants({ variant, size }?: {
32
45
  variant?: Variant;
46
+ size?: Size;
33
47
  }): string;
34
48
  import type { Component, Snippet } from 'svelte';
35
49
  interface Props {
@@ -38,6 +52,7 @@ interface Props {
38
52
  class?: string;
39
53
  icon?: Component;
40
54
  variant?: Variant;
55
+ size?: Size;
41
56
  title?: string;
42
57
  description?: string | Snippet;
43
58
  text?: string;
@@ -0,0 +1,64 @@
1
+ <script lang="ts">
2
+ import type { Component, Snippet } from 'svelte';
3
+ import { Button } from '../button';
4
+ import { cn } from '../../utils/cn';
5
+ import { getBannerActionContext } from './context';
6
+
7
+ type ActionVariant = 'primary' | 'secondary' | 'ghost';
8
+
9
+ interface Props {
10
+ children?: Snippet;
11
+ class?: string;
12
+ icon?: Component;
13
+ variant?: ActionVariant;
14
+ style?: string;
15
+ [key: string]: unknown;
16
+ }
17
+
18
+ let {
19
+ children,
20
+ class: className,
21
+ icon,
22
+ variant = 'primary',
23
+ style,
24
+ ...rest
25
+ }: Props = $props();
26
+
27
+ const banner = getBannerActionContext();
28
+ const buttonVariant = $derived(variant === 'secondary' ? 'outline' : variant);
29
+ const accents = {
30
+ default: { token: 'var(--color-kumo-info)', secondary: 'text-inherit ring-kumo-info/50 fill-kumo-info hover:!text-inherit hover:!ring-kumo-info/50 hover:bg-kumo-info/10', ghost: 'text-inherit fill-kumo-info hover:bg-kumo-info/10' },
31
+ alert: { token: 'var(--color-kumo-warning)', secondary: 'text-inherit ring-kumo-warning/50 fill-kumo-warning hover:!text-inherit hover:!ring-kumo-warning/50 hover:bg-kumo-warning/10', ghost: 'text-inherit fill-kumo-warning hover:bg-kumo-warning/10' },
32
+ error: { token: 'var(--color-kumo-danger)', secondary: 'text-inherit ring-kumo-danger/50 fill-kumo-danger hover:!text-inherit hover:!ring-kumo-danger/50 hover:bg-kumo-danger/10', ghost: 'text-inherit fill-kumo-danger hover:bg-kumo-danger/10' },
33
+ secondary: { token: 'var(--color-neutral-700, oklch(37.1% 0 0))', secondary: 'text-inherit ring-kumo-focus/20 fill-kumo-subtle hover:!text-inherit hover:!ring-kumo-focus/20 hover:bg-kumo-contrast/10', ghost: 'text-inherit fill-kumo-subtle hover:bg-kumo-contrast/10' }
34
+ } as const;
35
+ const accent = accents[banner.variant];
36
+ const actionClass = $derived(cn(variant !== 'primary' && [
37
+ 'text-inherit',
38
+ variant === 'secondary' ? accent.secondary : accent.ghost,
39
+ variant === 'secondary' && 'hover:!text-inherit'
40
+ ], className));
41
+ const actionStyle = $derived(
42
+ variant === 'primary'
43
+ ? [
44
+ `--kumo-button-emphasis-ring: color-mix(in oklch, ${accent.token}, black 10%)`,
45
+ `--kumo-button-emphasis-bg: color-mix(in oklch, ${accent.token}, white 30%)`,
46
+ `--kumo-button-emphasis-gradient-start: color-mix(in oklch, ${accent.token}, white 15%)`,
47
+ `--kumo-button-emphasis-gradient-end: ${accent.token}`,
48
+ style
49
+ ].filter(Boolean).join('; ')
50
+ : style
51
+ );
52
+ </script>
53
+
54
+ <Button
55
+ variant={buttonVariant}
56
+ size={banner.size === 'xs' ? 'xs' : 'sm'}
57
+ {icon}
58
+ class={actionClass}
59
+ style={actionStyle}
60
+ data-kumo-component="Banner.Action"
61
+ {...rest}
62
+ >
63
+ {@render children?.()}
64
+ </Button>
@@ -0,0 +1,13 @@
1
+ import type { Component, Snippet } from 'svelte';
2
+ type ActionVariant = 'primary' | 'secondary' | 'ghost';
3
+ interface Props {
4
+ children?: Snippet;
5
+ class?: string;
6
+ icon?: Component;
7
+ variant?: ActionVariant;
8
+ style?: string;
9
+ [key: string]: unknown;
10
+ }
11
+ declare const BannerAction: Component<Props, {}, "">;
12
+ type BannerAction = ReturnType<typeof BannerAction>;
13
+ export default BannerAction;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,40 @@
1
+ import { render, screen } from "@testing-library/svelte";
2
+ import { describe, expect, it } from "vitest";
3
+ import Banner, { bannerVariants } from "./Banner.svelte";
4
+ import { Banner as BannerCompound } from "./index";
5
+ import BannerLinkActionTest from "../../test-fixtures/BannerLinkActionTest.svelte";
6
+ describe("Banner", () => {
7
+ it("matches the 2.9 compact size contract", () => {
8
+ const classes = bannerVariants({ size: "sm" });
9
+ expect(classes).toContain("items-center");
10
+ expect(classes).toContain("px-3");
11
+ expect(classes).toContain("py-2");
12
+ expect(classes).toContain("text-sm");
13
+ });
14
+ it("renders compact title and description inline", () => {
15
+ render(Banner, {
16
+ size: "sm",
17
+ title: "Heads up",
18
+ description: "More details",
19
+ });
20
+ const title = screen.getByText("Heads up");
21
+ const description = screen.getByText("More details");
22
+ expect(title.tagName).toBe("SPAN");
23
+ expect(description.tagName).toBe("SPAN");
24
+ expect(title.parentElement).toBe(description.parentElement);
25
+ });
26
+ it("uses the updated neutral secondary color", () => {
27
+ expect(bannerVariants({ variant: "secondary" })).toContain("text-kumo-default/70");
28
+ });
29
+ it("exposes the action through the compound Banner API", () => {
30
+ expect(BannerCompound).toBe(Banner);
31
+ expect(BannerCompound.Action).toBeDefined();
32
+ });
33
+ it("renders a Link action inline in a compact banner", () => {
34
+ render(BannerLinkActionTest);
35
+ const description = screen.getByText("A DNS record already exists.");
36
+ const action = screen.getByTestId("action");
37
+ expect(action.parentElement?.parentElement).toBe(description);
38
+ expect(action.parentElement?.className).toContain("banner-compact-action");
39
+ });
40
+ });
@@ -0,0 +1,8 @@
1
+ import type { BannerVariant } from "./Banner.svelte";
2
+ export declare const BANNER_ACTION_CONTEXT: unique symbol;
3
+ export interface BannerActionContextValue {
4
+ variant: BannerVariant;
5
+ size: "xs" | "sm";
6
+ }
7
+ export declare function setBannerActionContext(context: BannerActionContextValue): void;
8
+ export declare function getBannerActionContext(): BannerActionContextValue;
@@ -0,0 +1,11 @@
1
+ import { getContext, setContext } from "svelte";
2
+ export const BANNER_ACTION_CONTEXT = Symbol("kumo-banner-action");
3
+ export function setBannerActionContext(context) {
4
+ setContext(BANNER_ACTION_CONTEXT, context);
5
+ }
6
+ export function getBannerActionContext() {
7
+ return (getContext(BANNER_ACTION_CONTEXT) ?? {
8
+ variant: "default",
9
+ size: "sm",
10
+ });
11
+ }
@@ -1,2 +1,8 @@
1
- export { default as Banner, KUMO_BANNER_BASE_STYLES, KUMO_BANNER_DEFAULT_VARIANTS, KUMO_BANNER_VARIANTS, bannerVariants } from './Banner.svelte';
2
- export type { BannerVariant } from './Banner.svelte';
1
+ import BannerRoot from "./Banner.svelte";
2
+ import BannerAction from "./BannerAction.svelte";
3
+ export { KUMO_BANNER_BASE_STYLES, KUMO_BANNER_DEFAULT_VARIANTS, KUMO_BANNER_VARIANTS, bannerVariants, } from "./Banner.svelte";
4
+ export type { BannerVariant, KumoBannerSize } from "./Banner.svelte";
5
+ export declare const Banner: typeof BannerRoot & {
6
+ Action: typeof BannerAction;
7
+ };
8
+ export { BannerRoot, BannerAction };
@@ -1 +1,7 @@
1
- export { default as Banner, KUMO_BANNER_BASE_STYLES, KUMO_BANNER_DEFAULT_VARIANTS, KUMO_BANNER_VARIANTS, bannerVariants } from './Banner.svelte';
1
+ import BannerRoot from "./Banner.svelte";
2
+ import BannerAction from "./BannerAction.svelte";
3
+ export { KUMO_BANNER_BASE_STYLES, KUMO_BANNER_DEFAULT_VARIANTS, KUMO_BANNER_VARIANTS, bannerVariants, } from "./Banner.svelte";
4
+ export const Banner = Object.assign(BannerRoot, {
5
+ Action: BannerAction,
6
+ });
7
+ export { BannerRoot, BannerAction };
@@ -11,11 +11,11 @@
11
11
  let { children, href, icon: Icon, ...rest }: Props = $props();
12
12
  </script>
13
13
 
14
- <a href={href} data-kumo-component="Breadcrumbs" data-kumo-part="link" class="flex min-w-0 max-w-full items-center gap-1 text-kumo-subtle no-underline" {...rest}>
14
+ <a href={href} data-kumo-component="Breadcrumbs" data-kumo-part="link" class="flex shrink-0 items-center gap-1 whitespace-nowrap text-kumo-subtle no-underline" {...rest}>
15
15
  {#if Icon}
16
16
  <span class="flex shrink-0 items-center">
17
17
  <Icon size={16} />
18
18
  </span>
19
19
  {/if}
20
- <span class="truncate">{@render children?.()}</span>
20
+ <span>{@render children?.()}</span>
21
21
  </a>