rizzo-css 0.0.10 → 0.0.12

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 (145) hide show
  1. package/README.md +20 -6
  2. package/bin/rizzo-css.js +13 -0
  3. package/dist/rizzo.min.css +34 -19
  4. package/package.json +1 -1
  5. package/scaffold/astro/CopyToClipboard.astro +37 -17
  6. package/scaffold/astro/FormGroup.astro +14 -6
  7. package/scaffold/astro/icons/Brush.astro +0 -1
  8. package/scaffold/astro/icons/Cake.astro +0 -1
  9. package/scaffold/astro/icons/Check.astro +1 -2
  10. package/scaffold/astro/icons/Cherry.astro +0 -1
  11. package/scaffold/astro/icons/ChevronDown.astro +1 -2
  12. package/scaffold/astro/icons/Circle.astro +1 -2
  13. package/scaffold/astro/icons/Close.astro +1 -2
  14. package/scaffold/astro/icons/Cmd.astro +26 -0
  15. package/scaffold/astro/icons/Copy.astro +1 -2
  16. package/scaffold/astro/icons/Eye.astro +1 -2
  17. package/scaffold/astro/icons/Filter.astro +1 -2
  18. package/scaffold/astro/icons/Flame.astro +0 -1
  19. package/scaffold/astro/icons/Flower.astro +0 -1
  20. package/scaffold/astro/icons/Gear.astro +1 -2
  21. package/scaffold/astro/icons/Heart.astro +0 -1
  22. package/scaffold/astro/icons/IceCream.astro +1 -2
  23. package/scaffold/astro/icons/Leaf.astro +0 -1
  24. package/scaffold/astro/icons/Lemon.astro +0 -1
  25. package/scaffold/astro/icons/Moon.astro +1 -2
  26. package/scaffold/astro/icons/Owl.astro +1 -2
  27. package/scaffold/astro/icons/Palette.astro +1 -2
  28. package/scaffold/astro/icons/Rainbow.astro +1 -2
  29. package/scaffold/astro/icons/Search.astro +1 -2
  30. package/scaffold/astro/icons/Shield.astro +0 -1
  31. package/scaffold/astro/icons/Snowflake.astro +1 -2
  32. package/scaffold/astro/icons/Sort.astro +1 -2
  33. package/scaffold/astro/icons/Sun.astro +0 -1
  34. package/scaffold/astro/icons/Sunset.astro +0 -1
  35. package/scaffold/astro/icons/Zap.astro +0 -1
  36. package/scaffold/astro/icons/devicons/Astro.astro +0 -1
  37. package/scaffold/astro/icons/devicons/Bash.astro +0 -1
  38. package/scaffold/astro/icons/devicons/Css3.astro +0 -1
  39. package/scaffold/astro/icons/devicons/Git.astro +0 -1
  40. package/scaffold/astro/icons/devicons/Html5.astro +0 -1
  41. package/scaffold/astro/icons/devicons/Javascript.astro +0 -1
  42. package/scaffold/astro/icons/devicons/Nodejs.astro +0 -1
  43. package/scaffold/astro/icons/devicons/Plaintext.astro +0 -1
  44. package/scaffold/astro/icons/devicons/React.astro +0 -1
  45. package/scaffold/astro/icons/devicons/Svelte.astro +0 -1
  46. package/scaffold/astro/icons/devicons/Vue.astro +0 -1
  47. package/scaffold/astro-app/README.md +32 -0
  48. package/scaffold/astro-app/astro.config.mjs +2 -1
  49. package/scaffold/astro-app/package.json +1 -1
  50. package/scaffold/astro-app/src/layouts/Layout.astro +4 -1
  51. package/scaffold/astro-app/src/pages/index.astro +4 -2
  52. package/scaffold/svelte/Accordion.svelte +1 -2
  53. package/scaffold/svelte/Alert.svelte +8 -3
  54. package/scaffold/svelte/Avatar.svelte +1 -1
  55. package/scaffold/svelte/Badge.svelte +11 -11
  56. package/scaffold/svelte/Breadcrumb.svelte +8 -6
  57. package/scaffold/svelte/Button.svelte +7 -3
  58. package/scaffold/svelte/Card.svelte +7 -4
  59. package/scaffold/svelte/CopyToClipboard.svelte +29 -29
  60. package/scaffold/svelte/Divider.svelte +8 -8
  61. package/scaffold/svelte/Dropdown.svelte +15 -18
  62. package/scaffold/svelte/FormGroup.svelte +14 -4
  63. package/scaffold/svelte/Input.svelte +7 -5
  64. package/scaffold/svelte/Modal.svelte +10 -6
  65. package/scaffold/svelte/Pagination.svelte +1 -1
  66. package/scaffold/svelte/ProgressBar.svelte +13 -11
  67. package/scaffold/svelte/Select.svelte +10 -6
  68. package/scaffold/svelte/Spinner.svelte +2 -2
  69. package/scaffold/svelte/Table.svelte +6 -7
  70. package/scaffold/svelte/Tabs.svelte +15 -7
  71. package/scaffold/svelte/Textarea.svelte +7 -5
  72. package/scaffold/svelte/Toast.svelte +6 -3
  73. package/scaffold/svelte/Tooltip.svelte +4 -4
  74. package/scaffold/svelte/icons/Check.svelte +17 -10
  75. package/scaffold/svelte/icons/ChevronDown.svelte +16 -9
  76. package/scaffold/svelte/icons/Circle.svelte +16 -9
  77. package/scaffold/svelte/icons/Close.svelte +18 -11
  78. package/scaffold/svelte/icons/Cmd.svelte +27 -0
  79. package/scaffold/svelte/icons/Copy.svelte +16 -9
  80. package/scaffold/svelte/icons/Eye.svelte +16 -9
  81. package/scaffold/svelte/icons/Filter.svelte +16 -9
  82. package/scaffold/svelte/icons/Gear.svelte +16 -9
  83. package/scaffold/svelte/icons/IceCream.svelte +16 -9
  84. package/scaffold/svelte/icons/Moon.svelte +16 -9
  85. package/scaffold/svelte/icons/Owl.svelte +16 -9
  86. package/scaffold/svelte/icons/Palette.svelte +16 -9
  87. package/scaffold/svelte/icons/Rainbow.svelte +16 -9
  88. package/scaffold/svelte/icons/Search.svelte +16 -9
  89. package/scaffold/svelte/icons/Snowflake.svelte +16 -9
  90. package/scaffold/svelte/icons/Sort.svelte +16 -9
  91. package/scaffold/svelte/icons/devicons/Astro.svelte +28 -14
  92. package/scaffold/svelte/icons/devicons/Bash.svelte +17 -16
  93. package/scaffold/svelte/icons/devicons/Css3.svelte +9 -8
  94. package/scaffold/svelte/icons/devicons/Git.svelte +9 -8
  95. package/scaffold/svelte/icons/devicons/Html5.svelte +9 -8
  96. package/scaffold/svelte/icons/devicons/Javascript.svelte +9 -8
  97. package/scaffold/svelte/icons/devicons/Nodejs.svelte +18 -14
  98. package/scaffold/svelte/icons/devicons/Plaintext.svelte +14 -16
  99. package/scaffold/svelte/icons/devicons/React.svelte +10 -9
  100. package/scaffold/svelte/icons/devicons/SvelteIcon.svelte +19 -0
  101. package/scaffold/svelte/icons/devicons/Vue.svelte +10 -9
  102. package/scaffold/svelte-app/README.md +4 -0
  103. package/scaffold/vanilla/README.md +47 -0
  104. package/scaffold/vanilla/icons/Brush.svg +0 -1
  105. package/scaffold/vanilla/icons/Cake.svg +0 -1
  106. package/scaffold/vanilla/icons/Check.svg +0 -1
  107. package/scaffold/vanilla/icons/Cherry.svg +0 -1
  108. package/scaffold/vanilla/icons/ChevronDown.svg +0 -1
  109. package/scaffold/vanilla/icons/Circle.svg +0 -1
  110. package/scaffold/vanilla/icons/Close.svg +0 -1
  111. package/scaffold/vanilla/icons/Cmd.svg +10 -0
  112. package/scaffold/vanilla/icons/Copy.svg +0 -1
  113. package/scaffold/vanilla/icons/Eye.svg +0 -1
  114. package/scaffold/vanilla/icons/Filter.svg +0 -1
  115. package/scaffold/vanilla/icons/Flame.svg +0 -1
  116. package/scaffold/vanilla/icons/Flower.svg +0 -1
  117. package/scaffold/vanilla/icons/Gear.svg +0 -1
  118. package/scaffold/vanilla/icons/Heart.svg +0 -1
  119. package/scaffold/vanilla/icons/IceCream.svg +0 -1
  120. package/scaffold/vanilla/icons/Leaf.svg +0 -1
  121. package/scaffold/vanilla/icons/Lemon.svg +0 -1
  122. package/scaffold/vanilla/icons/Moon.svg +0 -1
  123. package/scaffold/vanilla/icons/Owl.svg +0 -1
  124. package/scaffold/vanilla/icons/Palette.svg +0 -1
  125. package/scaffold/vanilla/icons/Rainbow.svg +0 -1
  126. package/scaffold/vanilla/icons/Search.svg +0 -1
  127. package/scaffold/vanilla/icons/Shield.svg +0 -1
  128. package/scaffold/vanilla/icons/Snowflake.svg +0 -1
  129. package/scaffold/vanilla/icons/Sort.svg +0 -1
  130. package/scaffold/vanilla/icons/Sun.svg +0 -1
  131. package/scaffold/vanilla/icons/Sunset.svg +0 -1
  132. package/scaffold/vanilla/icons/Zap.svg +0 -1
  133. package/scaffold/vanilla/icons/devicons/Astro.svg +0 -1
  134. package/scaffold/vanilla/icons/devicons/Bash.svg +0 -1
  135. package/scaffold/vanilla/icons/devicons/Css3.svg +0 -1
  136. package/scaffold/vanilla/icons/devicons/Git.svg +0 -1
  137. package/scaffold/vanilla/icons/devicons/Html5.svg +0 -1
  138. package/scaffold/vanilla/icons/devicons/Javascript.svg +0 -1
  139. package/scaffold/vanilla/icons/devicons/Nodejs.svg +0 -1
  140. package/scaffold/vanilla/icons/devicons/Plaintext.svg +0 -1
  141. package/scaffold/vanilla/icons/devicons/React.svg +0 -1
  142. package/scaffold/vanilla/icons/devicons/Svelte.svg +0 -1
  143. package/scaffold/vanilla/icons/devicons/Vue.svg +0 -1
  144. package/scaffold/vanilla/index.html +1 -277
  145. package/scaffold/vanilla/js/main.js +747 -0
@@ -0,0 +1,32 @@
1
+ # Astro + Rizzo CSS
2
+
3
+ This project was scaffolded with `npx rizzo-css init` (Astro).
4
+
5
+ ## First-time setup
6
+
7
+ **Install dependencies before running any Astro command:**
8
+
9
+ ```bash
10
+ pnpm install
11
+ # or: npm install
12
+ ```
13
+
14
+ Then start the dev server:
15
+
16
+ ```bash
17
+ pnpm dev
18
+ ```
19
+
20
+ The theme selected during `rizzo-css init` is set in `src/layouts/Layout.astro` (`data-theme` on `<html>`) and is used on first load when you have no saved preference in the browser.
21
+
22
+ ## Commands
23
+
24
+ - `pnpm dev` — Start dev server
25
+ - `pnpm build` — Build for production
26
+ - `pnpm preview` — Preview production build
27
+
28
+ ## Other scaffolds
29
+
30
+ From the same **rizzo-css** package: **Vanilla** (`scaffold/vanilla/`) — single HTML file with Settings and toast; **Svelte** (`scaffold/svelte-app/`) — SvelteKit app. Use `npx rizzo-css init` and pick a different framework to create one of them.
31
+
32
+ Docs: [rizzo-css.vercel.app](https://rizzo-css.vercel.app)
@@ -1,4 +1,5 @@
1
- // https://astro.build/config
1
+ // @ts-check
2
2
  import { defineConfig } from 'astro/config';
3
3
 
4
+ // https://astro.build/config
4
5
  export default defineConfig({});
@@ -1 +1 @@
1
- {"name":"{{PROJECT_NAME}}","type":"module","version":"0.0.1","scripts":{"dev":"astro dev","build":"astro build","preview":"astro preview"},"devDependencies":{"astro":"^5.0.0"}}
1
+ {"name":"{{PROJECT_NAME}}","type":"module","version":"0.0.1","scripts":{"dev":"astro dev","build":"astro build","preview":"astro preview"},"devDependencies":{"astro":"^5.17.1"}}
@@ -3,10 +3,13 @@ interface Props {
3
3
  title?: string;
4
4
  }
5
5
 
6
+ /* Placeholders replaced by rizzo-css CLI when scaffolding */
7
+ const DATA_THEME = '{{DATA_THEME}}';
8
+ const THEME_LIST_COMMENT = '{{THEME_LIST_COMMENT}}';
6
9
  const { title = '{{TITLE}}' } = Astro.props;
7
10
  ---
8
11
  <!doctype html>
9
- <html lang="en" data-theme="{{DATA_THEME}}">{{THEME_LIST_COMMENT}}
12
+ <html lang="en" data-theme={DATA_THEME}>{THEME_LIST_COMMENT}
10
13
  <head>
11
14
  <meta charset="UTF-8" />
12
15
  <meta name="viewport" content="width=device-width, initial-scale=1" />
@@ -1,10 +1,12 @@
1
1
  ---
2
2
  import Layout from '../layouts/Layout.astro';
3
+ /* Placeholder replaced by rizzo-css CLI when scaffolding */
4
+ const TITLE = '{{TITLE}}';
3
5
  ---
4
- <Layout title="{{TITLE}}">
6
+ <Layout title={TITLE}>
5
7
  <a href="#main-content" class="skip-link">Skip to main content</a>
6
8
  <header class="container flex flex-wrap items-center justify-between gap-4" style="padding-top: var(--spacing-4); padding-bottom: var(--spacing-4); border-bottom: 1px solid var(--border);">
7
- <a href="/" class="font-semibold" style="font-size: var(--font-size-lg); color: var(--text); text-decoration: none;">{{TITLE}}</a>
9
+ <a href="/" class="font-semibold" style="font-size: var(--font-size-lg); color: var(--text); text-decoration: none;">{TITLE}</a>
8
10
  <a href="https://rizzo-css.vercel.app/docs/getting-started" class="btn btn-outline" target="_blank" rel="noopener noreferrer">Docs</a>
9
11
  </header>
10
12
  <main id="main-content" class="flex flex-col items-center justify-center text-center min-h-screen" style="padding: var(--spacing-12) var(--spacing-4); min-height: calc(100vh - 4rem);">
@@ -31,7 +31,7 @@
31
31
 
32
32
  let expanded = $state<Set<string>>(getDefaultExpanded());
33
33
 
34
- const classes = ['accordion', className].filter(Boolean).join(' ').trim();
34
+ const classes = $derived(['accordion', className].filter(Boolean).join(' ').trim());
35
35
 
36
36
  function toggle(itemId: string) {
37
37
  expanded = new Set(
@@ -101,7 +101,6 @@
101
101
  <span class="accordion__title">{item.title}</span>
102
102
  <span class="accordion__icon" aria-hidden="true">
103
103
  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
104
- <title>Expand or collapse section</title>
105
104
  <path d="m6 9 6 6 6-6" />
106
105
  </svg>
107
106
  </span>
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { onMount } from 'svelte';
3
+ import type { Snippet } from 'svelte';
3
4
 
4
5
  interface Props {
5
6
  variant?: 'success' | 'error' | 'warning' | 'info';
@@ -7,6 +8,8 @@
7
8
  autoDismiss?: number;
8
9
  class?: string;
9
10
  id?: string;
11
+ onDismiss?: () => void;
12
+ children?: Snippet;
10
13
  }
11
14
  let {
12
15
  variant = 'info',
@@ -14,11 +17,13 @@
14
17
  autoDismiss = 0,
15
18
  class: className = '',
16
19
  id,
20
+ onDismiss,
21
+ children,
17
22
  }: Props = $props();
18
23
 
19
24
  let visible = $state(true);
20
25
  const alertId = $derived(id ?? `alert-${Math.random().toString(36).slice(2, 11)}`);
21
- const classes = ['alert', `alert--${variant}`, className].filter(Boolean).join(' ').trim();
26
+ const classes = $derived(['alert', `alert--${variant}`, className].filter(Boolean).join(' ').trim());
22
27
 
23
28
  const ariaLabels: Record<string, string> = {
24
29
  success: 'Success message',
@@ -32,6 +37,7 @@
32
37
  function dismiss() {
33
38
  visible = false;
34
39
  if (autoDismissTimeout) clearTimeout(autoDismissTimeout);
40
+ onDismiss?.();
35
41
  }
36
42
 
37
43
  onMount(() => {
@@ -54,7 +60,7 @@
54
60
  aria-label={ariaLabels[variant]}
55
61
  >
56
62
  <div class="alert__content">
57
- <slot />
63
+ {@render children?.()}
58
64
  </div>
59
65
  {#if dismissible}
60
66
  <button
@@ -71,7 +77,6 @@
71
77
  }}
72
78
  >
73
79
  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
74
- <title>Dismiss</title>
75
80
  <path d="M18 6L6 18M6 6l12 12" />
76
81
  </svg>
77
82
  </button>
@@ -26,7 +26,7 @@
26
26
  }: Props = $props();
27
27
 
28
28
  const displayInitials = $derived(name ? getInitials(name) : initialsProp);
29
- const classes = ['avatar', `avatar--${size}`, `avatar--${shape}`, className].filter(Boolean).join(' ').trim();
29
+ const classes = $derived(['avatar', `avatar--${size}`, `avatar--${shape}`, className].filter(Boolean).join(' ').trim());
30
30
  const ariaLabel = $derived(alt || name || (displayInitials ? `Avatar: ${displayInitials}` : 'Avatar'));
31
31
  </script>
32
32
 
@@ -1,4 +1,6 @@
1
1
  <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
2
4
  type Variant = 'primary' | 'success' | 'warning' | 'error' | 'info';
3
5
  type Size = 'sm' | 'md' | 'lg';
4
6
  interface Props {
@@ -6,26 +8,24 @@
6
8
  size?: Size;
7
9
  pill?: boolean;
8
10
  class?: string;
11
+ children?: Snippet;
9
12
  }
10
13
  let {
11
14
  variant = 'primary',
12
15
  size = 'md',
13
16
  pill = false,
14
17
  class: className = '',
18
+ children,
15
19
  }: Props = $props();
16
20
 
17
- const classes = [
18
- 'badge',
19
- `badge--${variant}`,
20
- `badge--${size}`,
21
- pill ? 'badge--pill' : '',
22
- className,
23
- ]
24
- .filter(Boolean)
25
- .join(' ')
26
- .trim();
21
+ const classes = $derived(
22
+ ['badge', `badge--${variant}`, `badge--${size}`, pill ? 'badge--pill' : '', className]
23
+ .filter(Boolean)
24
+ .join(' ')
25
+ .trim()
26
+ );
27
27
  </script>
28
28
 
29
29
  <span class={classes}>
30
- <slot />
30
+ {@render children?.()}
31
31
  </span>
@@ -11,11 +11,14 @@
11
11
  }
12
12
  let { items, separator = 'chevron', class: className = '' }: Props = $props();
13
13
 
14
- const separatorVariant =
15
- separator === 'slash' ? 'breadcrumb--slash' : separator === 'arrow' ? 'breadcrumb--arrow' : 'breadcrumb--chevron';
16
- const classes = ['breadcrumb', separatorVariant, className].filter(Boolean).join(' ').trim();
17
- const separatorChar = separator === 'slash' ? '/' : separator === 'arrow' ? '›' : typeof separator === 'string' ? separator : '›';
18
- const useIcon = separator === 'chevron';
14
+ const separatorVariant = $derived(
15
+ separator === 'slash' ? 'breadcrumb--slash' : separator === 'arrow' ? 'breadcrumb--arrow' : 'breadcrumb--chevron'
16
+ );
17
+ const classes = $derived(['breadcrumb', separatorVariant, className].filter(Boolean).join(' ').trim());
18
+ const separatorChar = $derived(
19
+ separator === 'slash' ? '/' : separator === 'arrow' ? '›' : typeof separator === 'string' ? separator : '›'
20
+ );
21
+ const useIcon = $derived(separator === 'chevron');
19
22
  </script>
20
23
 
21
24
  <nav class={classes} aria-label="Breadcrumb">
@@ -33,7 +36,6 @@
33
36
  <span class="breadcrumb__separator" aria-hidden="true">
34
37
  {#if useIcon}
35
38
  <svg class="breadcrumb__separator-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
36
- <title>Separator</title>
37
39
  <path d="M6 9l6 6 6-6" />
38
40
  </svg>
39
41
  {:else}
@@ -1,23 +1,27 @@
1
1
  <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
2
4
  type Variant = 'default' | 'primary' | 'success' | 'warning' | 'error' | 'info' | 'outline';
3
5
  interface Props {
4
6
  variant?: Variant;
5
7
  disabled?: boolean;
6
8
  type?: 'button' | 'submit' | 'reset';
7
9
  class?: string;
10
+ children?: Snippet;
8
11
  }
9
12
  let {
10
13
  variant = 'default',
11
14
  disabled = false,
12
15
  type = 'button',
13
16
  class: className = '',
17
+ children,
14
18
  ...rest
15
19
  }: Props = $props();
16
20
 
17
- const variantClass = variant === 'default' ? '' : `btn-${variant}`;
18
- const classes = ['btn', variantClass, className].filter(Boolean).join(' ');
21
+ const variantClass = $derived(variant === 'default' ? '' : `btn-${variant}`);
22
+ const classes = $derived(['btn', variantClass, className].filter(Boolean).join(' '));
19
23
  </script>
20
24
 
21
25
  <button {type} {disabled} class={classes} {...rest}>
22
- <slot />
26
+ {@render children?.()}
23
27
  </button>
@@ -1,14 +1,17 @@
1
1
  <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
2
4
  type Variant = 'default' | 'elevated' | 'outlined' | 'filled';
3
5
  interface Props {
4
6
  variant?: Variant;
5
7
  class?: string;
8
+ children?: Snippet;
6
9
  }
7
- let { variant = 'default', class: className = '' }: Props = $props();
8
- const variantClass = variant !== 'default' ? `card--${variant}` : '';
9
- const classes = ['card', variantClass, className].filter(Boolean).join(' ').trim();
10
+ let { variant = 'default', class: className = '', children }: Props = $props();
11
+ const variantClass = $derived(variant !== 'default' ? `card--${variant}` : '');
12
+ const classes = $derived(['card', variantClass, className].filter(Boolean).join(' ').trim());
10
13
  </script>
11
14
 
12
15
  <div class={classes}>
13
- <slot />
16
+ {@render children?.()}
14
17
  </div>
@@ -48,32 +48,32 @@
48
48
  }
49
49
  </script>
50
50
 
51
- <button
52
- type="button"
53
- class={classes}
54
- aria-label={label ?? `Copy ${value} to clipboard`}
55
- id={buttonId}
56
- onclick={copy}
57
- onkeydown={(e) => {
58
- if (e.key === 'Enter' || e.key === ' ') {
59
- e.preventDefault();
60
- copy();
61
- }
62
- }}
63
- >
64
- <span class="copy-to-clipboard__text">{value}</span>
65
- <span class="copy-to-clipboard__icon copy-to-clipboard__icon--copy" class:copy-to-clipboard__icon--hidden={copied} aria-hidden="true">
66
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
67
- <title>Copy</title>
68
- <rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
69
- <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
70
- </svg>
71
- </span>
72
- <span class="copy-to-clipboard__icon copy-to-clipboard__icon--check" class:copy-to-clipboard__icon--hidden={!copied} aria-hidden="true">
73
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
74
- <title>Copied</title>
75
- <polyline points="20 6 9 17 4 12" />
76
- </svg>
77
- </span>
78
- <span class="copy-to-clipboard__feedback" aria-live="polite">{feedbackText}</span>
79
- </button>
51
+ <span class="tooltip-host" data-tooltip={copied ? (format ? `Copied ${format}!` : 'Copied!') : (label ?? 'Copy to clipboard')}>
52
+ <button
53
+ type="button"
54
+ class={classes}
55
+ aria-label={copied ? (format ? `Copied ${format}!` : 'Copied!') : (label ?? `Copy ${value} to clipboard`)}
56
+ id={buttonId}
57
+ onclick={copy}
58
+ onkeydown={(e) => {
59
+ if (e.key === 'Enter' || e.key === ' ') {
60
+ e.preventDefault();
61
+ copy();
62
+ }
63
+ }}
64
+ >
65
+ <span class="copy-to-clipboard__text">{value}</span>
66
+ <span class="copy-to-clipboard__icon copy-to-clipboard__icon--copy" class:copy-to-clipboard__icon--hidden={copied} aria-hidden="true">
67
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
68
+ <rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
69
+ <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
70
+ </svg>
71
+ </span>
72
+ <span class="copy-to-clipboard__icon copy-to-clipboard__icon--check" class:copy-to-clipboard__icon--hidden={!copied} aria-hidden="true">
73
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
74
+ <polyline points="20 6 9 17 4 12" />
75
+ </svg>
76
+ </span>
77
+ <span class="copy-to-clipboard__feedback" aria-live="polite">{feedbackText}</span>
78
+ </button>
79
+ </span>
@@ -5,11 +5,11 @@
5
5
  class?: string;
6
6
  }
7
7
  let { orientation = 'horizontal', label, class: className = '' }: Props = $props();
8
- const orientationClass = `divider--${orientation}`;
9
- const hasLabel = typeof label === 'string' && label.trim().length > 0;
10
- const labelClass = hasLabel ? 'divider--labeled' : '';
11
- const classes = ['divider', orientationClass, labelClass, className].filter(Boolean).join(' ').trim();
12
- const labelText = label?.trim() ?? '';
8
+ const orientationClass = $derived(`divider--${orientation}`);
9
+ const hasLabel = $derived(typeof label === 'string' && label.trim().length > 0);
10
+ const labelClass = $derived(hasLabel ? 'divider--labeled' : '');
11
+ const classes = $derived(['divider', orientationClass, labelClass, className].filter(Boolean).join(' ').trim());
12
+ const labelText = $derived(label?.trim() ?? '');
13
13
  </script>
14
14
 
15
15
  <div
@@ -19,10 +19,10 @@
19
19
  aria-label={labelText || undefined}
20
20
  >
21
21
  {#if hasLabel && orientation === 'horizontal'}
22
- <span class="divider__line" aria-hidden="true" />
22
+ <span class="divider__line" aria-hidden="true"></span>
23
23
  <span class="divider__label">{labelText}</span>
24
- <span class="divider__line" aria-hidden="true" />
24
+ <span class="divider__line" aria-hidden="true"></span>
25
25
  {:else}
26
- <span class="divider__line" aria-hidden="true" />
26
+ <span class="divider__line" aria-hidden="true"></span>
27
27
  {/if}
28
28
  </div>
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { onMount } from 'svelte';
3
+ import ChevronDown from './icons/ChevronDown.svelte';
3
4
 
4
5
  export interface MenuItem {
5
6
  label: string;
@@ -119,12 +120,7 @@
119
120
  }}
120
121
  >
121
122
  <span class="dropdown__trigger-text">{trigger}</span>
122
- <span class="dropdown__icon" aria-hidden="true">
123
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" role="img" aria-labelledby="{dropdownId}-trigger-icon-title">
124
- <title id="{dropdownId}-trigger-icon-title">Expand menu</title>
125
- <path d="m6 9 6 6 6-6" />
126
- </svg>
127
- </span>
123
+ <ChevronDown class="dropdown__icon" width={16} height={16} />
128
124
  </button>
129
125
 
130
126
  <div
@@ -144,10 +140,11 @@
144
140
  {:else}
145
141
  {@const hasSubmenu = item.submenu && item.submenu.length > 0}
146
142
  {@const isSubmenuOpen = openSubmenuIndex === index}
147
- <div class="dropdown__item-wrapper dropdown__item-wrapper--has-submenu={hasSubmenu}">
143
+ <div class="dropdown__item-wrapper" class:dropdown__item-wrapper--has-submenu={hasSubmenu}>
148
144
  {#if item.href && !hasSubmenu}
149
145
  <a
150
- class="dropdown__item dropdown__item--disabled={item.disabled}"
146
+ class="dropdown__item"
147
+ class:dropdown__item--disabled={item.disabled}
151
148
  role="menuitem"
152
149
  href={item.href}
153
150
  aria-label={item.label}
@@ -162,7 +159,9 @@
162
159
  </a>
163
160
  {:else}
164
161
  <div
165
- class="dropdown__item dropdown__item--disabled={item.disabled} dropdown__item--has-submenu={hasSubmenu}"
162
+ class="dropdown__item"
163
+ class:dropdown__item--disabled={item.disabled}
164
+ class:dropdown__item--has-submenu={hasSubmenu}
166
165
  role="menuitem"
167
166
  aria-disabled={item.disabled ? 'true' : undefined}
168
167
  aria-expanded={hasSubmenu ? isSubmenuOpen : undefined}
@@ -190,18 +189,14 @@
190
189
  >
191
190
  <span>{item.label}</span>
192
191
  {#if hasSubmenu}
193
- <span class="dropdown__item-arrow" aria-hidden="true">
194
- <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" role="img" aria-labelledby="{dropdownId}-submenu-arrow-{index}-title">
195
- <title id="{dropdownId}-submenu-arrow-{index}-title">Expand submenu</title>
196
- <path d="m6 9 6 6 6-6" />
197
- </svg>
198
- </span>
192
+ <ChevronDown class="dropdown__item-arrow" width={12} height={12} />
199
193
  {/if}
200
194
  </div>
201
195
  {/if}
202
196
  {#if hasSubmenu && item.submenu}
203
197
  <div
204
- class="dropdown__submenu dropdown__submenu--open={isSubmenuOpen}"
198
+ class="dropdown__submenu"
199
+ class:dropdown__submenu--open={isSubmenuOpen}
205
200
  role="menu"
206
201
  aria-label="{item.label} submenu"
207
202
  aria-hidden={!isSubmenuOpen}
@@ -211,7 +206,8 @@
211
206
  <div class="dropdown__separator" role="separator"></div>
212
207
  {:else if subItem.href}
213
208
  <a
214
- class="dropdown__item dropdown__submenu-item dropdown__item--disabled={subItem.disabled}"
209
+ class="dropdown__item dropdown__submenu-item"
210
+ class:dropdown__item--disabled={subItem.disabled}
215
211
  role="menuitem"
216
212
  href={subItem.href}
217
213
  aria-label={subItem.label}
@@ -225,7 +221,8 @@
225
221
  </a>
226
222
  {:else}
227
223
  <div
228
- class="dropdown__item dropdown__submenu-item dropdown__item--disabled={subItem.disabled}"
224
+ class="dropdown__item dropdown__submenu-item"
225
+ class:dropdown__item--disabled={subItem.disabled}
229
226
  role="menuitem"
230
227
  aria-disabled={subItem.disabled ? 'true' : undefined}
231
228
  tabindex={open ? 0 : -1}
@@ -1,4 +1,6 @@
1
1
  <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
2
4
  interface Props {
3
5
  label?: string;
4
6
  labelFor?: string;
@@ -7,6 +9,7 @@
7
9
  error?: string;
8
10
  success?: string;
9
11
  class?: string;
12
+ children?: Snippet;
10
13
  }
11
14
  let {
12
15
  label,
@@ -16,6 +19,7 @@
16
19
  error,
17
20
  success,
18
21
  class: className = '',
22
+ children,
19
23
  }: Props = $props();
20
24
 
21
25
  const errorId = $derived(labelFor && error ? `${labelFor}-error` : undefined);
@@ -24,11 +28,17 @@
24
28
 
25
29
  <div class="form-group {className}">
26
30
  {#if label}
27
- <label for={labelFor} class="form-group__label {required ? 'required' : ''}">
28
- {label}
29
- </label>
31
+ {#if labelFor}
32
+ <label for={labelFor} class="form-group__label {required ? 'required' : ''}">
33
+ {label}
34
+ </label>
35
+ {:else}
36
+ <span class="form-group__label {required ? 'required' : ''}">
37
+ {label}
38
+ </span>
39
+ {/if}
30
40
  {/if}
31
- <slot />
41
+ {@render children?.()}
32
42
  {#if help}
33
43
  <span id={helpId} class="form-group__help">{help}</span>
34
44
  {/if}
@@ -34,11 +34,13 @@
34
34
  ariaInvalid,
35
35
  }: Props = $props();
36
36
 
37
- const sizeClass = size !== 'md' ? `form-input--${size}` : '';
38
- const errorClass = error ? 'form-input--error' : '';
39
- const successClass = success ? 'form-input--success' : '';
40
- const classes = ['form-input', sizeClass, errorClass, successClass, className].filter(Boolean).join(' ').trim();
41
- const invalid = error || ariaInvalid === true || ariaInvalid === 'true';
37
+ const sizeClass = $derived(size !== 'md' ? `form-input--${size}` : '');
38
+ const errorClass = $derived(error ? 'form-input--error' : '');
39
+ const successClass = $derived(success ? 'form-input--success' : '');
40
+ const classes = $derived(
41
+ ['form-input', sizeClass, errorClass, successClass, className].filter(Boolean).join(' ').trim()
42
+ );
43
+ const invalid = $derived(error || ariaInvalid === true || ariaInvalid === 'true');
42
44
  </script>
43
45
 
44
46
  <input
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
- import { onMount, tick } from 'svelte';
2
+ import { tick } from 'svelte';
3
+ import type { Snippet } from 'svelte';
3
4
 
4
5
  interface Props {
5
6
  id?: string;
@@ -9,6 +10,8 @@
9
10
  closeOnOverlayClick?: boolean;
10
11
  closeOnEscape?: boolean;
11
12
  class?: string;
13
+ children?: Snippet;
14
+ footer?: Snippet;
12
15
  }
13
16
 
14
17
  let {
@@ -19,11 +22,13 @@
19
22
  closeOnOverlayClick = true,
20
23
  closeOnEscape = true,
21
24
  class: className = '',
25
+ children,
26
+ footer,
22
27
  }: Props = $props();
23
28
 
24
29
  const modalId = $derived(id ?? `modal-${Math.random().toString(36).slice(2, 11)}`);
25
- const sizeClass = size !== 'md' ? `modal--${size}` : '';
26
- const classes = ['modal', sizeClass, className].filter(Boolean).join(' ').trim();
30
+ const sizeClass = $derived(size !== 'md' ? `modal--${size}` : '');
31
+ const classes = $derived(['modal', sizeClass, className].filter(Boolean).join(' ').trim());
27
32
 
28
33
  let overlayEl: HTMLDivElement;
29
34
  let modalEl: HTMLDivElement;
@@ -138,16 +143,15 @@
138
143
  <h2 id="{modalId}-title" class="modal__title">{title}</h2>
139
144
  <button type="button" class="modal__close" aria-label="Close modal" data-modal-close onclick={close}>
140
145
  <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
141
- <title>Close</title>
142
146
  <path d="M18 6L6 18" />
143
147
  <path d="M6 6l12 12" />
144
148
  </svg>
145
149
  </button>
146
150
  </div>
147
151
  <div class="modal__body">
148
- <slot />
152
+ {@render children?.()}
149
153
  </div>
150
154
  <div class="modal__footer">
151
- <slot name="footer" />
155
+ {@render footer?.()}
152
156
  </div>
153
157
  </div>
@@ -38,7 +38,7 @@
38
38
  class: className = '',
39
39
  }: Props = $props();
40
40
 
41
- const classes = ['pagination', className].filter(Boolean).join(' ').trim();
41
+ const classes = $derived(['pagination', className].filter(Boolean).join(' ').trim());
42
42
  const pageItems = $derived(getPageItems(totalPages, currentPage, maxVisible));
43
43
  const hasPrev = $derived(currentPage > 1);
44
44
  const hasNext = $derived(currentPage < totalPages);
@@ -24,16 +24,18 @@
24
24
  const clampedValue = $derived(indeterminate ? 0 : Math.max(0, Math.min(value, safeMax)));
25
25
  const percentage = $derived(indeterminate ? 0 : Math.round((clampedValue / safeMax) * 100));
26
26
 
27
- const classes = [
28
- 'progress',
29
- `progress--${variant}`,
30
- `progress--${size}`,
31
- indeterminate ? 'progress--indeterminate' : '',
32
- className,
33
- ]
34
- .filter(Boolean)
35
- .join(' ')
36
- .trim();
27
+ const classes = $derived(
28
+ [
29
+ 'progress',
30
+ `progress--${variant}`,
31
+ `progress--${size}`,
32
+ indeterminate ? 'progress--indeterminate' : '',
33
+ className,
34
+ ]
35
+ .filter(Boolean)
36
+ .join(' ')
37
+ .trim()
38
+ );
37
39
 
38
40
  const barStyle = $derived(indeterminate ? {} : { width: `${percentage}%` });
39
41
  </script>
@@ -48,7 +50,7 @@
48
50
  aria-valuenow={indeterminate ? undefined : clampedValue}
49
51
  >
50
52
  <div class="progress__track">
51
- <div class="progress__bar" style={barStyle} />
53
+ <div class="progress__bar" style={barStyle}></div>
52
54
  </div>
53
55
  {#if showLabel && !indeterminate}
54
56
  <span class="progress__label" aria-hidden="true">{percentage}%</span>