create-mikstack 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 (99) hide show
  1. package/README.md +54 -0
  2. package/dist/index.js +410 -0
  3. package/package.json +43 -0
  4. package/templates/adapters/cloudflare/package.json.partial +5 -0
  5. package/templates/adapters/cloudflare/svelte.config.js +19 -0
  6. package/templates/adapters/node/Dockerfile +30 -0
  7. package/templates/adapters/node/docker-compose.prod.yml +27 -0
  8. package/templates/adapters/node/package.json.partial +5 -0
  9. package/templates/adapters/node/svelte.config.js +19 -0
  10. package/templates/adapters/vercel/package.json.partial +5 -0
  11. package/templates/adapters/vercel/svelte.config.js +19 -0
  12. package/templates/base/.env.example +23 -0
  13. package/templates/base/.gitignore.append +2 -0
  14. package/templates/base/.mcp.json +9 -0
  15. package/templates/base/.prettierignore +10 -0
  16. package/templates/base/.vscode/extensions.json +3 -0
  17. package/templates/base/AGENTS.md +123 -0
  18. package/templates/base/README.md +27 -0
  19. package/templates/base/agents.md +28 -0
  20. package/templates/base/docker-compose.yml +15 -0
  21. package/templates/base/drizzle-zero.config.ts +17 -0
  22. package/templates/base/drizzle.config.ts +17 -0
  23. package/templates/base/eslint.config.ts +65 -0
  24. package/templates/base/package.json.partial +43 -0
  25. package/templates/base/prettier.config.js +6 -0
  26. package/templates/base/src/app.d.ts +12 -0
  27. package/templates/base/src/app.html +11 -0
  28. package/templates/base/src/hooks.server.ts +15 -0
  29. package/templates/base/src/lib/auth-client.ts +6 -0
  30. package/templates/base/src/lib/server/auth.ts +52 -0
  31. package/templates/base/src/lib/server/db/index.ts +19 -0
  32. package/templates/base/src/lib/server/db/schema.ts +117 -0
  33. package/templates/base/src/lib/server/db/seed.ts +21 -0
  34. package/templates/base/src/lib/server/emails/magic-link.ts +77 -0
  35. package/templates/base/src/lib/server/emails/send.ts +55 -0
  36. package/templates/base/src/lib/server/notifications/definitions.ts +12 -0
  37. package/templates/base/src/lib/server/notifications.ts +38 -0
  38. package/templates/base/src/lib/z.svelte.ts +14 -0
  39. package/templates/base/src/lib/zero/context.ts +9 -0
  40. package/templates/base/src/lib/zero/db-provider.server.ts +11 -0
  41. package/templates/base/src/lib/zero/mutators.ts +35 -0
  42. package/templates/base/src/lib/zero/queries.ts +21 -0
  43. package/templates/base/src/lib/zero/schema.ts +1 -0
  44. package/templates/base/src/routes/+layout.server.ts +5 -0
  45. package/templates/base/src/routes/+layout.svelte +7 -0
  46. package/templates/base/src/routes/+page.server.ts +7 -0
  47. package/templates/base/src/routes/+page.svelte +319 -0
  48. package/templates/base/src/routes/api/dev/emails/+server.ts +89 -0
  49. package/templates/base/src/routes/api/dev/emails/[id]/+server.ts +24 -0
  50. package/templates/base/src/routes/api/notifications/[...path]/+server.ts +10 -0
  51. package/templates/base/src/routes/api/zero/get-queries/+server.ts +29 -0
  52. package/templates/base/src/routes/api/zero/mutate/+server.ts +31 -0
  53. package/templates/base/src/routes/sign-in/+page.svelte +97 -0
  54. package/templates/base/tsconfig.json +40 -0
  55. package/templates/github-actions-bun/.github/workflows/ci.yml +22 -0
  56. package/templates/github-actions-npm/.github/workflows/ci.yml +25 -0
  57. package/templates/github-actions-pnpm/.github/workflows/ci.yml +27 -0
  58. package/templates/i18n/lingui.config.ts +16 -0
  59. package/templates/i18n/package.json.partial +14 -0
  60. package/templates/i18n/src/lib/i18n.ts +10 -0
  61. package/templates/i18n/src/locales/en.po +6 -0
  62. package/templates/i18n/src/po.d.ts +3 -0
  63. package/templates/i18n/vite.config.ts +7 -0
  64. package/templates/supply-chain-bun/bunfig.toml +3 -0
  65. package/templates/testing/package.json.partial +11 -0
  66. package/templates/testing/src/example.test.ts +7 -0
  67. package/templates/testing/src/lib/server/db/test-utils.ts +25 -0
  68. package/templates/testing/vitest.config.ts +9 -0
  69. package/templates/ui/.vscode/extensions.json +8 -0
  70. package/templates/ui/package.json.partial +13 -0
  71. package/templates/ui/src/app.css +94 -0
  72. package/templates/ui/src/routes/+layout.svelte +12 -0
  73. package/templates/ui/stylelint.config.js +7 -0
  74. package/templates/ui/vite.config.ts +6 -0
  75. package/templates/ui-dependency/package.json.partial +5 -0
  76. package/templates/ui-vendor/src/lib/components/ui/Accordion/Accordion.svelte +71 -0
  77. package/templates/ui-vendor/src/lib/components/ui/Accordion/index.ts +1 -0
  78. package/templates/ui-vendor/src/lib/components/ui/Alert/Alert.svelte +60 -0
  79. package/templates/ui-vendor/src/lib/components/ui/Alert/index.ts +1 -0
  80. package/templates/ui-vendor/src/lib/components/ui/Badge/Badge.svelte +48 -0
  81. package/templates/ui-vendor/src/lib/components/ui/Badge/index.ts +1 -0
  82. package/templates/ui-vendor/src/lib/components/ui/Button/Button.svelte +77 -0
  83. package/templates/ui-vendor/src/lib/components/ui/Button/index.ts +1 -0
  84. package/templates/ui-vendor/src/lib/components/ui/Card/Card.svelte +49 -0
  85. package/templates/ui-vendor/src/lib/components/ui/Card/index.ts +1 -0
  86. package/templates/ui-vendor/src/lib/components/ui/Dialog/Dialog.svelte +70 -0
  87. package/templates/ui-vendor/src/lib/components/ui/Dialog/index.ts +1 -0
  88. package/templates/ui-vendor/src/lib/components/ui/FormField/FormField.svelte +53 -0
  89. package/templates/ui-vendor/src/lib/components/ui/FormField/index.ts +1 -0
  90. package/templates/ui-vendor/src/lib/components/ui/Input/Input.svelte +27 -0
  91. package/templates/ui-vendor/src/lib/components/ui/Input/index.ts +1 -0
  92. package/templates/ui-vendor/src/lib/components/ui/Separator/Separator.svelte +26 -0
  93. package/templates/ui-vendor/src/lib/components/ui/Separator/index.ts +1 -0
  94. package/templates/ui-vendor/src/lib/components/ui/Skeleton/Skeleton.svelte +40 -0
  95. package/templates/ui-vendor/src/lib/components/ui/Skeleton/index.ts +1 -0
  96. package/templates/ui-vendor/src/lib/components/ui/Switch/Switch.svelte +86 -0
  97. package/templates/ui-vendor/src/lib/components/ui/Switch/index.ts +1 -0
  98. package/templates/ui-vendor/src/lib/components/ui/Textarea/Textarea.svelte +29 -0
  99. package/templates/ui-vendor/src/lib/components/ui/Textarea/index.ts +1 -0
@@ -0,0 +1,5 @@
1
+ {
2
+ "dependencies": {
3
+ "@mikstack/ui": "^0.1.0"
4
+ }
5
+ }
@@ -0,0 +1,71 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ interface Props {
5
+ summary: Snippet;
6
+ children: Snippet;
7
+ open?: boolean;
8
+ }
9
+
10
+ let { summary, children, open = false }: Props = $props();
11
+ </script>
12
+
13
+ <details {open}>
14
+ <summary>
15
+ {@render summary()}
16
+ <svg class="chevron" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
17
+ <path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
18
+ </svg>
19
+ </summary>
20
+ <div class="content">
21
+ {@render children()}
22
+ </div>
23
+ </details>
24
+
25
+ <style>
26
+ details {
27
+ border: 1px solid var(--border);
28
+ border-radius: var(--radius-md);
29
+ background-color: var(--surface-1);
30
+ }
31
+
32
+ summary {
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: space-between;
36
+ padding: var(--space-3) var(--space-4);
37
+ cursor: pointer;
38
+ font-weight: 500;
39
+ list-style: none;
40
+ user-select: none;
41
+
42
+ &::-webkit-details-marker {
43
+ display: none;
44
+ }
45
+
46
+ &:hover {
47
+ background-color: var(--surface-2);
48
+ }
49
+
50
+ &:focus-visible {
51
+ outline: 2px solid var(--focus);
52
+ outline-offset: -2px;
53
+ border-radius: var(--radius-md);
54
+ }
55
+ }
56
+
57
+ .chevron {
58
+ transition: transform 0.2s;
59
+ flex-shrink: 0;
60
+ }
61
+
62
+ details[open] > summary .chevron {
63
+ transform: rotate(180deg);
64
+ }
65
+
66
+ .content {
67
+ padding: 0 var(--space-4) var(--space-4);
68
+ color: var(--text-2);
69
+ font-size: var(--text-sm);
70
+ }
71
+ </style>
@@ -0,0 +1 @@
1
+ export { default } from "./Accordion.svelte";
@@ -0,0 +1,60 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ interface Props {
5
+ variant?: 'info' | 'success' | 'warning' | 'danger';
6
+ children: Snippet;
7
+ title?: Snippet;
8
+ }
9
+
10
+ let { variant = 'info', children, title }: Props = $props();
11
+ </script>
12
+
13
+ <div role="alert" data-variant={variant}>
14
+ {#if title}
15
+ <strong class="alert-title">{@render title()}</strong>
16
+ {/if}
17
+ <div class="alert-body">
18
+ {@render children()}
19
+ </div>
20
+ </div>
21
+
22
+ <style>
23
+ [role='alert'] {
24
+ padding: var(--space-3) var(--space-4);
25
+ border-radius: var(--radius-md);
26
+ border: 1px solid;
27
+ font-size: var(--text-sm);
28
+ display: flex;
29
+ flex-direction: column;
30
+ gap: var(--space-1);
31
+
32
+ &[data-variant='info'] {
33
+ background-color: oklch(from var(--accent) l c h / 10%);
34
+ border-color: oklch(from var(--accent) l c h / 30%);
35
+ color: var(--text-1);
36
+ }
37
+
38
+ &[data-variant='success'] {
39
+ background-color: oklch(55% 0.15 145 / 10%);
40
+ border-color: oklch(55% 0.15 145 / 30%);
41
+ color: var(--text-1);
42
+ }
43
+
44
+ &[data-variant='warning'] {
45
+ background-color: oklch(75% 0.15 85 / 10%);
46
+ border-color: oklch(75% 0.15 85 / 30%);
47
+ color: var(--text-1);
48
+ }
49
+
50
+ &[data-variant='danger'] {
51
+ background-color: oklch(from var(--danger) l c h / 10%);
52
+ border-color: oklch(from var(--danger) l c h / 30%);
53
+ color: var(--text-1);
54
+ }
55
+ }
56
+
57
+ .alert-title {
58
+ font-weight: 600;
59
+ }
60
+ </style>
@@ -0,0 +1 @@
1
+ export { default } from "./Alert.svelte";
@@ -0,0 +1,48 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ interface Props {
5
+ variant?: 'default' | 'secondary' | 'success' | 'danger';
6
+ children: Snippet;
7
+ }
8
+
9
+ let { variant = 'default', children }: Props = $props();
10
+ </script>
11
+
12
+ <span data-variant={variant}>
13
+ {@render children()}
14
+ </span>
15
+
16
+ <style>
17
+ span {
18
+ display: inline-flex;
19
+ align-items: center;
20
+ gap: var(--space-1);
21
+ padding: var(--space-1) var(--space-2);
22
+ border-radius: 9999px;
23
+ font-size: var(--text-xs);
24
+ font-weight: 500;
25
+ line-height: 1;
26
+ white-space: nowrap;
27
+
28
+ &[data-variant='default'] {
29
+ background-color: var(--accent);
30
+ color: white;
31
+ }
32
+
33
+ &[data-variant='secondary'] {
34
+ background-color: var(--surface-3);
35
+ color: var(--text-1);
36
+ }
37
+
38
+ &[data-variant='success'] {
39
+ background-color: oklch(55% 0.15 145);
40
+ color: white;
41
+ }
42
+
43
+ &[data-variant='danger'] {
44
+ background-color: var(--danger);
45
+ color: white;
46
+ }
47
+ }
48
+ </style>
@@ -0,0 +1 @@
1
+ export { default } from "./Badge.svelte";
@@ -0,0 +1,77 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import type { HTMLButtonAttributes } from 'svelte/elements';
4
+
5
+ interface Props extends HTMLButtonAttributes {
6
+ variant?: 'primary' | 'secondary' | 'ghost' | 'danger';
7
+ children: Snippet;
8
+ }
9
+
10
+ let { variant = 'primary', children, ...rest }: Props = $props();
11
+ </script>
12
+
13
+ <button data-variant={variant} {...rest}>
14
+ {@render children()}
15
+ </button>
16
+
17
+ <style>
18
+ button {
19
+ display: inline-flex;
20
+ align-items: center;
21
+ gap: var(--space-2);
22
+ padding: var(--space-2) var(--space-4);
23
+ border: 1px solid transparent;
24
+ border-radius: var(--radius-md);
25
+ font-size: var(--text-sm);
26
+ font-weight: 500;
27
+ cursor: pointer;
28
+ transition: background-color 0.15s, border-color 0.15s;
29
+
30
+ &:focus-visible {
31
+ outline: 2px solid var(--focus);
32
+ outline-offset: 2px;
33
+ }
34
+
35
+ &:disabled {
36
+ opacity: 0.5;
37
+ cursor: not-allowed;
38
+ }
39
+
40
+ &[data-variant='primary'] {
41
+ background-color: var(--accent);
42
+ color: white;
43
+
44
+ &:hover:not(:disabled) {
45
+ background-color: oklch(from var(--accent) calc(l - 0.05) c h);
46
+ }
47
+ }
48
+
49
+ &[data-variant='secondary'] {
50
+ background-color: var(--surface-2);
51
+ border-color: var(--border);
52
+ color: var(--text-1);
53
+
54
+ &:hover:not(:disabled) {
55
+ background-color: var(--surface-3);
56
+ }
57
+ }
58
+
59
+ &[data-variant='ghost'] {
60
+ background-color: transparent;
61
+ color: var(--text-1);
62
+
63
+ &:hover:not(:disabled) {
64
+ background-color: var(--surface-2);
65
+ }
66
+ }
67
+
68
+ &[data-variant='danger'] {
69
+ background-color: var(--danger);
70
+ color: white;
71
+
72
+ &:hover:not(:disabled) {
73
+ background-color: oklch(from var(--danger) calc(l - 0.05) c h);
74
+ }
75
+ }
76
+ }
77
+ </style>
@@ -0,0 +1 @@
1
+ export { default } from "./Button.svelte";
@@ -0,0 +1,49 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import type { HTMLAttributes } from 'svelte/elements';
4
+
5
+ interface Props extends HTMLAttributes<HTMLElement> {
6
+ children: Snippet;
7
+ header?: Snippet;
8
+ footer?: Snippet;
9
+ }
10
+
11
+ let { children, header, footer, ...rest }: Props = $props();
12
+ </script>
13
+
14
+ <article {...rest}>
15
+ {#if header}
16
+ <div class="card-header">
17
+ {@render header()}
18
+ </div>
19
+ {/if}
20
+ <div class="card-body">
21
+ {@render children()}
22
+ </div>
23
+ {#if footer}
24
+ <div class="card-footer">
25
+ {@render footer()}
26
+ </div>
27
+ {/if}
28
+ </article>
29
+
30
+ <style>
31
+ article {
32
+ border: 1px solid var(--border);
33
+ border-radius: var(--radius-lg);
34
+ background-color: var(--surface-1);
35
+ overflow: hidden;
36
+ }
37
+
38
+ .card-header {
39
+ padding: var(--space-4) var(--space-4) 0;
40
+ }
41
+
42
+ .card-body {
43
+ padding: var(--space-4);
44
+ }
45
+
46
+ .card-footer {
47
+ padding: 0 var(--space-4) var(--space-4);
48
+ }
49
+ </style>
@@ -0,0 +1 @@
1
+ export { default } from "./Card.svelte";
@@ -0,0 +1,70 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ interface Props {
5
+ open?: boolean;
6
+ onclose?: () => void;
7
+ children: Snippet;
8
+ }
9
+
10
+ let { open = $bindable(false), onclose, children }: Props = $props();
11
+
12
+ let dialogEl: HTMLDialogElement | undefined = $state();
13
+
14
+ $effect(() => {
15
+ if (!dialogEl) return;
16
+ if (open && !dialogEl.open) {
17
+ dialogEl.showModal();
18
+ } else if (!open && dialogEl.open) {
19
+ dialogEl.close();
20
+ }
21
+ });
22
+
23
+ function handleClose() {
24
+ open = false;
25
+ onclose?.();
26
+ }
27
+
28
+ function handleClick(e: MouseEvent) {
29
+ if (e.target === dialogEl) {
30
+ handleClose();
31
+ }
32
+ }
33
+ </script>
34
+
35
+ <dialog bind:this={dialogEl} onclose={handleClose} onclick={handleClick}>
36
+ <div class="dialog-content">
37
+ {@render children()}
38
+ </div>
39
+ </dialog>
40
+
41
+ <style>
42
+ dialog {
43
+ border: none;
44
+ border-radius: var(--radius-lg);
45
+ padding: 0;
46
+ max-width: min(32rem, calc(100vw - var(--space-6)));
47
+ max-height: min(85vh, calc(100vh - var(--space-6)));
48
+ background-color: var(--surface-1);
49
+ color: var(--text-1);
50
+ box-shadow:
51
+ 0 10px 15px -3px rgb(0 0 0 / 10%),
52
+ 0 4px 6px -4px rgb(0 0 0 / 10%);
53
+
54
+ &::backdrop {
55
+ background-color: rgb(0 0 0 / 50%);
56
+ }
57
+
58
+ &[open] {
59
+ display: flex;
60
+ }
61
+ }
62
+
63
+ .dialog-content {
64
+ padding: var(--space-5);
65
+ display: flex;
66
+ flex-direction: column;
67
+ gap: var(--space-4);
68
+ overflow-y: auto;
69
+ }
70
+ </style>
@@ -0,0 +1 @@
1
+ export { default } from "./Dialog.svelte";
@@ -0,0 +1,53 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ interface LabelAttrs {
5
+ for: string;
6
+ }
7
+
8
+ interface ErrorAttrs {
9
+ id: string;
10
+ role: 'alert';
11
+ 'aria-live': 'polite';
12
+ }
13
+
14
+ interface Props {
15
+ for: string;
16
+ label: Snippet<[LabelAttrs]>;
17
+ children: Snippet;
18
+ error?: Snippet<[ErrorAttrs]>;
19
+ }
20
+
21
+ let { for: htmlFor, label, children, error }: Props = $props();
22
+ </script>
23
+
24
+ <div class="field">
25
+ <div class="field-label">
26
+ {@render label({ for: htmlFor })}
27
+ </div>
28
+ {@render children()}
29
+ {#if error}
30
+ <div class="field-error">
31
+ {@render error({ id: `${htmlFor}-error`, role: 'alert', 'aria-live': 'polite' })}
32
+ </div>
33
+ {/if}
34
+ </div>
35
+
36
+ <style>
37
+ .field {
38
+ display: flex;
39
+ flex-direction: column;
40
+ gap: var(--space-1);
41
+ }
42
+
43
+ .field-label {
44
+ font-size: var(--text-sm);
45
+ font-weight: 500;
46
+ color: var(--text-1);
47
+ }
48
+
49
+ .field-error {
50
+ font-size: var(--text-sm);
51
+ color: var(--danger);
52
+ }
53
+ </style>
@@ -0,0 +1 @@
1
+ export { default } from "./FormField.svelte";
@@ -0,0 +1,27 @@
1
+ <script lang="ts">
2
+ import type { HTMLInputAttributes } from 'svelte/elements';
3
+
4
+ let { ...rest }: HTMLInputAttributes = $props();
5
+ </script>
6
+
7
+ <input {...rest} />
8
+
9
+ <style>
10
+ input {
11
+ padding: var(--space-2) var(--space-3);
12
+ border: 1px solid var(--border);
13
+ border-radius: var(--radius-md);
14
+ background-color: var(--surface-1);
15
+ font-size: var(--text-base);
16
+ width: 100%;
17
+
18
+ &:focus-visible {
19
+ outline: 2px solid var(--focus);
20
+ outline-offset: 2px;
21
+ }
22
+
23
+ &[aria-invalid] {
24
+ border-color: var(--danger);
25
+ }
26
+ }
27
+ </style>
@@ -0,0 +1 @@
1
+ export { default } from "./Input.svelte";
@@ -0,0 +1,26 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ orientation?: 'horizontal' | 'vertical';
4
+ }
5
+
6
+ let { orientation = 'horizontal' }: Props = $props();
7
+ </script>
8
+
9
+ <hr data-orientation={orientation} aria-orientation={orientation} />
10
+
11
+ <style>
12
+ hr {
13
+ border: none;
14
+ background-color: var(--border);
15
+
16
+ &[data-orientation='horizontal'] {
17
+ height: 1px;
18
+ width: 100%;
19
+ }
20
+
21
+ &[data-orientation='vertical'] {
22
+ width: 1px;
23
+ height: 100%;
24
+ }
25
+ }
26
+ </style>
@@ -0,0 +1 @@
1
+ export { default } from "./Separator.svelte";
@@ -0,0 +1,40 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ width?: string;
4
+ height?: string;
5
+ circle?: boolean;
6
+ }
7
+
8
+ let { width, height = '1rem', circle = false }: Props = $props();
9
+ </script>
10
+
11
+ <div
12
+ class="skeleton"
13
+ class:circle
14
+ style:width={circle ? height : width}
15
+ style:height
16
+ aria-hidden="true"
17
+ ></div>
18
+
19
+ <style>
20
+ .skeleton {
21
+ background-color: var(--surface-3);
22
+ border-radius: var(--radius-md);
23
+ animation: pulse 2s ease-in-out infinite;
24
+ }
25
+
26
+ .circle {
27
+ border-radius: 9999px;
28
+ }
29
+
30
+ @keyframes pulse {
31
+ 0%,
32
+ 100% {
33
+ opacity: 1;
34
+ }
35
+
36
+ 50% {
37
+ opacity: 0.5;
38
+ }
39
+ }
40
+ </style>
@@ -0,0 +1 @@
1
+ export { default } from "./Skeleton.svelte";
@@ -0,0 +1,86 @@
1
+ <script lang="ts">
2
+ import type { HTMLInputAttributes } from 'svelte/elements';
3
+
4
+ interface Props extends Omit<HTMLInputAttributes, 'type'> {
5
+ label?: string;
6
+ }
7
+
8
+ let { label, id, ...rest }: Props = $props();
9
+
10
+ const inputId = $derived(id ?? (label ? `switch-${label.toLowerCase().replace(/\s+/g, '-')}` : undefined));
11
+ </script>
12
+
13
+ <label class="switch-wrapper" for={inputId}>
14
+ <input type="checkbox" role="switch" id={inputId} {...rest} />
15
+ <span class="track">
16
+ <span class="thumb"></span>
17
+ </span>
18
+ {#if label}
19
+ <span class="switch-label">{label}</span>
20
+ {/if}
21
+ </label>
22
+
23
+ <style>
24
+ .switch-wrapper {
25
+ display: inline-flex;
26
+ align-items: center;
27
+ gap: var(--space-2);
28
+ cursor: pointer;
29
+ }
30
+
31
+ input {
32
+ position: absolute;
33
+ width: 1px;
34
+ height: 1px;
35
+ padding: 0;
36
+ margin: -1px;
37
+ overflow: hidden;
38
+ clip: rect(0, 0, 0, 0);
39
+ white-space: nowrap;
40
+ border: 0;
41
+ }
42
+
43
+ .track {
44
+ position: relative;
45
+ width: 2.75rem;
46
+ height: 1.5rem;
47
+ background-color: var(--surface-3);
48
+ border-radius: 9999px;
49
+ transition: background-color 0.15s;
50
+ }
51
+
52
+ .thumb {
53
+ position: absolute;
54
+ top: 2px;
55
+ left: 2px;
56
+ width: 1.25rem;
57
+ height: 1.25rem;
58
+ background-color: white;
59
+ border-radius: 9999px;
60
+ transition: transform 0.15s;
61
+ }
62
+
63
+ input:checked + .track {
64
+ background-color: var(--accent);
65
+ }
66
+
67
+ input:checked + .track .thumb {
68
+ transform: translateX(1.25rem);
69
+ }
70
+
71
+ input:focus-visible + .track {
72
+ outline: 2px solid var(--focus);
73
+ outline-offset: 2px;
74
+ }
75
+
76
+ input:disabled + .track {
77
+ opacity: 0.5;
78
+ cursor: not-allowed;
79
+ }
80
+
81
+ .switch-label {
82
+ font-size: var(--text-sm);
83
+ color: var(--text-1);
84
+ user-select: none;
85
+ }
86
+ </style>
@@ -0,0 +1 @@
1
+ export { default } from "./Switch.svelte";
@@ -0,0 +1,29 @@
1
+ <script lang="ts">
2
+ import type { HTMLTextareaAttributes } from 'svelte/elements';
3
+
4
+ let { ...rest }: HTMLTextareaAttributes = $props();
5
+ </script>
6
+
7
+ <textarea {...rest}></textarea>
8
+
9
+ <style>
10
+ textarea {
11
+ padding: var(--space-2) var(--space-3);
12
+ border: 1px solid var(--border);
13
+ border-radius: var(--radius-md);
14
+ background-color: var(--surface-1);
15
+ font-size: var(--text-base);
16
+ width: 100%;
17
+ min-height: 5rem;
18
+ resize: vertical;
19
+
20
+ &:focus-visible {
21
+ outline: 2px solid var(--focus);
22
+ outline-offset: 2px;
23
+ }
24
+
25
+ &[aria-invalid] {
26
+ border-color: var(--danger);
27
+ }
28
+ }
29
+ </style>
@@ -0,0 +1 @@
1
+ export { default } from "./Textarea.svelte";