@aiaiai-pt/design-system 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 (52) hide show
  1. package/components/Alert.svelte +100 -0
  2. package/components/Badge.svelte +108 -0
  3. package/components/BottomNav.svelte +37 -0
  4. package/components/BottomNavItem.svelte +121 -0
  5. package/components/Button.svelte +269 -0
  6. package/components/Card.svelte +108 -0
  7. package/components/Checkbox.svelte +138 -0
  8. package/components/CodeBlock.svelte +187 -0
  9. package/components/CodeEditor.svelte +221 -0
  10. package/components/CollapsibleSection.svelte +160 -0
  11. package/components/Combobox.svelte +396 -0
  12. package/components/EmptyState.svelte +148 -0
  13. package/components/FileUpload.svelte +280 -0
  14. package/components/FileUploadItem.svelte +222 -0
  15. package/components/Input.svelte +222 -0
  16. package/components/KeyValue.svelte +79 -0
  17. package/components/Label.svelte +49 -0
  18. package/components/List.svelte +70 -0
  19. package/components/ListItem.svelte +125 -0
  20. package/components/Menu.svelte +161 -0
  21. package/components/MenuItem.svelte +120 -0
  22. package/components/MenuSeparator.svelte +34 -0
  23. package/components/Modal.svelte +260 -0
  24. package/components/OptionGrid.svelte +195 -0
  25. package/components/Panel.svelte +256 -0
  26. package/components/Popover.svelte +194 -0
  27. package/components/Progress.svelte +78 -0
  28. package/components/Select.svelte +182 -0
  29. package/components/Separator.svelte +47 -0
  30. package/components/Sidebar.svelte +106 -0
  31. package/components/SidebarItem.svelte +154 -0
  32. package/components/SidebarSection.svelte +43 -0
  33. package/components/Skeleton.svelte +79 -0
  34. package/components/Status.svelte +104 -0
  35. package/components/Stepper.svelte +142 -0
  36. package/components/Tab.svelte +94 -0
  37. package/components/TabList.svelte +36 -0
  38. package/components/TabPanel.svelte +45 -0
  39. package/components/Tabs.svelte +46 -0
  40. package/components/Tag.svelte +96 -0
  41. package/components/Textarea.svelte +143 -0
  42. package/components/Toast.svelte +114 -0
  43. package/components/Toggle.svelte +132 -0
  44. package/components/index.js +70 -0
  45. package/package.json +45 -0
  46. package/tokens/base.css +175 -0
  47. package/tokens/components.css +530 -0
  48. package/tokens/semantic.css +211 -0
  49. package/tokens/themes/aiaiai.css +53 -0
  50. package/tokens/themes/bespoke-example.css +148 -0
  51. package/tokens/themes/branded-example.css +55 -0
  52. package/tokens/utilities.css +1865 -0
@@ -0,0 +1,46 @@
1
+ <!--
2
+ @component Tabs
3
+
4
+ Container for tabbed interface. Provides context to TabList, Tab, and TabPanel.
5
+ Consumes --tabs-* tokens from components.css.
6
+
7
+ @example
8
+ <Tabs bind:value={activeTab}>
9
+ <TabList>
10
+ <Tab value="overview">OVERVIEW</Tab>
11
+ <Tab value="settings">SETTINGS</Tab>
12
+ </TabList>
13
+ <TabPanel value="overview">Overview content</TabPanel>
14
+ <TabPanel value="settings">Settings content</TabPanel>
15
+ </Tabs>
16
+ -->
17
+ <script>
18
+ import { setContext } from 'svelte';
19
+
20
+ let {
21
+ /** @type {string} */
22
+ value = $bindable(''),
23
+ /** @type {string} */
24
+ class: className = '',
25
+ /** @type {import('svelte').Snippet | undefined} */
26
+ children = undefined,
27
+ ...rest
28
+ } = $props();
29
+
30
+ setContext('tabs', {
31
+ get value() { return value; },
32
+ /** @param {string} v */
33
+ setValue(v) { value = v; }
34
+ });
35
+ </script>
36
+
37
+ <div class="tabs {className}" {...rest}>
38
+ {#if children}{@render children()}{/if}
39
+ </div>
40
+
41
+ <style>
42
+ .tabs {
43
+ display: flex;
44
+ flex-direction: column;
45
+ }
46
+ </style>
@@ -0,0 +1,96 @@
1
+ <!--
2
+ @component Tag
3
+
4
+ Categorization label. Bordered, optionally removable.
5
+ Consumes --tag-* tokens from components.css.
6
+
7
+ @example
8
+ <Tag>SVELTE</Tag>
9
+
10
+ @example Removable
11
+ <Tag removable onremove={() => console.log('removed')}>CSS</Tag>
12
+ -->
13
+ <script>
14
+ let {
15
+ /** @type {boolean} */
16
+ removable = false,
17
+ /** @type {(() => void) | undefined} */
18
+ onremove = undefined,
19
+ /** @type {string} */
20
+ class: className = '',
21
+ /** @type {import('svelte').Snippet | undefined} */
22
+ children = undefined,
23
+ ...rest
24
+ } = $props();
25
+ </script>
26
+
27
+ <span
28
+ class="tag {className}"
29
+ class:tag-removable={removable}
30
+ {...rest}
31
+ >
32
+ {#if children}
33
+ {@render children()}
34
+ {/if}
35
+ {#if removable}
36
+ <button
37
+ class="tag-remove"
38
+ aria-label="Remove tag"
39
+ onclick={(e) => {
40
+ e.stopPropagation();
41
+ onremove?.();
42
+ }}
43
+ >
44
+ <svg viewBox="0 0 10 10" fill="none" class="tag-remove-icon" aria-hidden="true">
45
+ <path d="M2 2l6 6M8 2l-6 6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
46
+ </svg>
47
+ </button>
48
+ {/if}
49
+ </span>
50
+
51
+ <style>
52
+ .tag {
53
+ display: inline-flex;
54
+ align-items: center;
55
+ gap: var(--space-xs);
56
+ font-family: var(--tag-font);
57
+ font-size: var(--tag-size);
58
+ border-radius: var(--tag-radius);
59
+ padding: var(--tag-padding-y) var(--tag-padding-x);
60
+ border: var(--tag-border);
61
+ background: var(--tag-bg);
62
+ color: var(--tag-text);
63
+ white-space: nowrap;
64
+ }
65
+
66
+ .tag-removable {
67
+ padding-right: var(--space-xs);
68
+ }
69
+
70
+ .tag-remove {
71
+ all: unset;
72
+ cursor: pointer;
73
+ display: flex;
74
+ align-items: center;
75
+ justify-content: center;
76
+ width: 14px;
77
+ height: 14px;
78
+ border-radius: var(--radius-sm);
79
+ color: var(--color-text-muted);
80
+ transition: color var(--duration-instant) var(--easing-default);
81
+ }
82
+
83
+ .tag-remove:hover {
84
+ color: var(--color-text);
85
+ }
86
+
87
+ .tag-remove:focus-visible {
88
+ outline: var(--focus-ring-width) solid var(--focus-ring-color);
89
+ outline-offset: var(--focus-ring-offset);
90
+ }
91
+
92
+ .tag-remove-icon {
93
+ width: 10px;
94
+ height: 10px;
95
+ }
96
+ </style>
@@ -0,0 +1,143 @@
1
+ <!--
2
+ @component Textarea
3
+
4
+ Multi-line text input with label, help text, and error state.
5
+ Values displayed in Berkeley Mono (data font).
6
+ Consumes --input-* and --textarea-* tokens from components.css.
7
+
8
+ @example
9
+ <Textarea label="DESCRIPTION" placeholder="Enter a description..." rows={4} />
10
+
11
+ @example With error
12
+ <Textarea label="SQL QUERY" value="SELCT *" error="Syntax error near SELCT" />
13
+ -->
14
+ <script module>
15
+ let _textareaUid = 0;
16
+ </script>
17
+
18
+ <script>
19
+ let {
20
+ /** @type {string | undefined} */
21
+ label = undefined,
22
+ /** @type {string | undefined} */
23
+ placeholder = undefined,
24
+ /** @type {string} */
25
+ value = $bindable(''),
26
+ /** @type {string | undefined} */
27
+ help = undefined,
28
+ /** @type {string | undefined} */
29
+ error = undefined,
30
+ /** @type {number} */
31
+ rows = 3,
32
+ /** @type {boolean} */
33
+ disabled = false,
34
+ /** @type {boolean} */
35
+ readonly = false,
36
+ /** @type {string | undefined} */
37
+ id = undefined,
38
+ /** @type {string} */
39
+ class: className = '',
40
+ ...rest
41
+ } = $props();
42
+
43
+ const fallbackId = `textarea-${_textareaUid++}`;
44
+ const textareaId = $derived(id ?? fallbackId);
45
+ const hintId = $derived(`${textareaId}-hint`);
46
+ const hasHint = $derived(!!error || !!help);
47
+ </script>
48
+
49
+ <div class="textarea-group {className}">
50
+ {#if label}
51
+ <label class="textarea-label" for={textareaId}>{label}</label>
52
+ {/if}
53
+
54
+ <textarea
55
+ id={textareaId}
56
+ class="textarea"
57
+ class:textarea-error={!!error}
58
+ class:textarea-readonly={readonly}
59
+ aria-invalid={error ? true : undefined}
60
+ aria-describedby={hasHint ? hintId : undefined}
61
+ {placeholder}
62
+ {disabled}
63
+ {readonly}
64
+ {rows}
65
+ bind:value
66
+ {...rest}
67
+ ></textarea>
68
+
69
+ {#if error}
70
+ <span id={hintId} class="textarea-error-text" role="alert">{error}</span>
71
+ {:else if help}
72
+ <span id={hintId} class="textarea-help">{help}</span>
73
+ {/if}
74
+ </div>
75
+
76
+ <style>
77
+ .textarea-group {
78
+ display: flex;
79
+ flex-direction: column;
80
+ gap: var(--input-label-gap);
81
+ width: 100%;
82
+ }
83
+
84
+ .textarea-label {
85
+ font-family: var(--input-label-font);
86
+ font-size: var(--input-label-size);
87
+ letter-spacing: var(--input-label-tracking);
88
+ color: var(--input-label-color);
89
+ }
90
+
91
+ .textarea {
92
+ font-family: var(--input-font);
93
+ font-size: var(--input-font-size);
94
+ border: var(--input-border);
95
+ border-radius: var(--input-radius);
96
+ background: var(--input-bg);
97
+ color: var(--input-text);
98
+ transition: border var(--input-transition);
99
+ width: 100%;
100
+ padding: var(--textarea-padding);
101
+ min-height: var(--textarea-min-height);
102
+ resize: vertical;
103
+ }
104
+
105
+ .textarea::placeholder {
106
+ color: var(--input-placeholder);
107
+ }
108
+
109
+ .textarea:focus {
110
+ outline: none;
111
+ border: var(--input-border-focus);
112
+ }
113
+
114
+ .textarea:disabled {
115
+ opacity: 0.5;
116
+ cursor: not-allowed;
117
+ }
118
+
119
+ .textarea-readonly {
120
+ background: var(--color-surface-secondary);
121
+ cursor: default;
122
+ }
123
+
124
+ .textarea-error {
125
+ border-color: var(--input-error-border-color);
126
+ }
127
+
128
+ .textarea-error:focus {
129
+ border-color: var(--input-error-border-color);
130
+ }
131
+
132
+ .textarea-help {
133
+ font-family: var(--input-help-font);
134
+ font-size: var(--input-help-size);
135
+ color: var(--input-help-color);
136
+ }
137
+
138
+ .textarea-error-text {
139
+ font-family: var(--input-help-font);
140
+ font-size: var(--input-help-size);
141
+ color: var(--input-error-text);
142
+ }
143
+ </style>
@@ -0,0 +1,114 @@
1
+ <!--
2
+ @component Toast
3
+
4
+ Non-blocking notification with accent stripe.
5
+ Consumes --toast-* tokens from components.css.
6
+
7
+ This is the visual component only — toast positioning/stacking/auto-dismiss
8
+ is the consumer's responsibility.
9
+
10
+ @example
11
+ <Toast variant="success">
12
+ <strong>Project created.</strong> Your new project is ready.
13
+ </Toast>
14
+
15
+ @example With action
16
+ <Toast variant="info" actionLabel="UNDO" onaction={() => undo()}>
17
+ <strong>Message archived.</strong>
18
+ </Toast>
19
+ -->
20
+ <script>
21
+ /**
22
+ * @typedef {'info' | 'success' | 'warning' | 'error'} Variant
23
+ */
24
+
25
+ let {
26
+ /** @type {Variant} */
27
+ variant = 'info',
28
+ /** @type {string | undefined} */
29
+ actionLabel = undefined,
30
+ /** @type {(() => void) | undefined} */
31
+ onaction = undefined,
32
+ /** @type {string} */
33
+ class: className = '',
34
+ /** @type {import('svelte').Snippet | undefined} */
35
+ children = undefined,
36
+ ...rest
37
+ } = $props();
38
+
39
+ /** @type {Record<string, string>} */
40
+ const accentColors = {
41
+ info: 'var(--color-info)',
42
+ success: 'var(--color-success)',
43
+ warning: 'var(--color-warning)',
44
+ error: 'var(--color-destructive)',
45
+ };
46
+ </script>
47
+
48
+ <div class="toast {className}" role={variant === 'error' ? 'alert' : 'status'} {...rest}>
49
+ <div class="toast-accent" style:background={accentColors[variant]}></div>
50
+ <div class="toast-content">
51
+ <span class="toast-message">
52
+ {#if children}{@render children()}{/if}
53
+ </span>
54
+ {#if actionLabel && onaction}
55
+ <button class="toast-action" onclick={onaction}>
56
+ <span class="toast-action-label">{actionLabel}</span>
57
+ </button>
58
+ {/if}
59
+ </div>
60
+ </div>
61
+
62
+ <style>
63
+ .toast {
64
+ display: flex;
65
+ overflow: hidden;
66
+ border-radius: var(--toast-radius);
67
+ border: var(--toast-border);
68
+ box-shadow: var(--toast-shadow);
69
+ background: var(--toast-bg);
70
+ max-width: var(--toast-max-width);
71
+ }
72
+
73
+ .toast-accent {
74
+ width: var(--accent-stripe-width);
75
+ flex-shrink: 0;
76
+ }
77
+
78
+ .toast-content {
79
+ padding: var(--toast-padding);
80
+ font-family: var(--toast-font);
81
+ font-size: var(--toast-font-size);
82
+ display: flex;
83
+ align-items: center;
84
+ gap: var(--space-md);
85
+ flex: 1;
86
+ }
87
+
88
+ .toast-message {
89
+ flex: 1;
90
+ }
91
+
92
+ .toast-action {
93
+ all: unset;
94
+ cursor: pointer;
95
+ flex-shrink: 0;
96
+ transition: opacity var(--duration-instant) var(--easing-default);
97
+ }
98
+
99
+ .toast-action:hover {
100
+ opacity: 0.8;
101
+ }
102
+
103
+ .toast-action:focus-visible {
104
+ outline: var(--focus-ring-width) solid var(--focus-ring-color);
105
+ outline-offset: var(--focus-ring-offset);
106
+ }
107
+
108
+ .toast-action-label {
109
+ font-family: var(--type-label-font);
110
+ font-size: var(--type-label-size);
111
+ letter-spacing: var(--type-label-tracking);
112
+ color: var(--color-accent);
113
+ }
114
+ </style>
@@ -0,0 +1,132 @@
1
+ <!--
2
+ @component Toggle
3
+
4
+ On/off switch with label.
5
+ Consumes --toggle-* tokens from components.css.
6
+
7
+ @example
8
+ <Toggle label="Dark mode" bind:checked />
9
+
10
+ @example Disabled
11
+ <Toggle label="Notifications" checked disabled />
12
+ -->
13
+ <script module>
14
+ let _toggleUid = 0;
15
+ </script>
16
+
17
+ <script>
18
+ let {
19
+ /** @type {boolean} */
20
+ checked = $bindable(false),
21
+ /** @type {boolean} */
22
+ disabled = false,
23
+ /** @type {string | undefined} */
24
+ label = undefined,
25
+ /** @type {string | undefined} */
26
+ id = undefined,
27
+ /** @type {((checked: boolean) => void) | undefined} */
28
+ onchange = undefined,
29
+ /** @type {string} */
30
+ class: className = '',
31
+ ...rest
32
+ } = $props();
33
+
34
+ const fallbackId = `toggle-${_toggleUid++}`;
35
+ const toggleId = $derived(id ?? fallbackId);
36
+
37
+ function handleClick() {
38
+ if (!disabled) {
39
+ checked = !checked;
40
+ onchange?.(checked);
41
+ }
42
+ }
43
+ </script>
44
+
45
+ <div class="toggle-group {className}">
46
+ <button
47
+ id={toggleId}
48
+ class="toggle"
49
+ class:toggle-on={checked}
50
+ class:toggle-disabled={disabled}
51
+ {disabled}
52
+ role="switch"
53
+ aria-checked={checked}
54
+ aria-labelledby={label ? `${toggleId}-label` : undefined}
55
+ {...rest}
56
+ onclick={handleClick}
57
+ >
58
+ <span class="toggle-knob"></span>
59
+ </button>
60
+ {#if label}
61
+ <span
62
+ id="{toggleId}-label"
63
+ class="toggle-label"
64
+ class:toggle-label-disabled={disabled}
65
+ onclick={handleClick}
66
+ role="none"
67
+ >{label}</span>
68
+ {/if}
69
+ </div>
70
+
71
+ <style>
72
+ .toggle-group {
73
+ display: flex;
74
+ align-items: center;
75
+ gap: var(--space-sm);
76
+ }
77
+
78
+ .toggle {
79
+ width: var(--toggle-width);
80
+ height: var(--toggle-height);
81
+ border-radius: var(--toggle-radius);
82
+ background: var(--toggle-bg-off);
83
+ position: relative;
84
+ cursor: pointer;
85
+ transition: background var(--duration-fast) var(--easing-default);
86
+ border: none;
87
+ padding: 0;
88
+ flex-shrink: 0;
89
+ }
90
+
91
+ .toggle:focus-visible {
92
+ outline: var(--focus-ring-width) solid var(--focus-ring-color);
93
+ outline-offset: var(--focus-ring-offset);
94
+ }
95
+
96
+ .toggle-on {
97
+ background: var(--toggle-bg-on);
98
+ }
99
+
100
+ .toggle-disabled {
101
+ opacity: 0.5;
102
+ cursor: not-allowed;
103
+ }
104
+
105
+ .toggle-knob {
106
+ position: absolute;
107
+ top: var(--space-2xs);
108
+ left: var(--space-2xs);
109
+ width: var(--toggle-knob-size);
110
+ height: var(--toggle-knob-size);
111
+ border-radius: var(--radius-circle);
112
+ background: var(--toggle-knob-color);
113
+ transition: transform var(--duration-fast) var(--easing-default);
114
+ }
115
+
116
+ .toggle-on .toggle-knob {
117
+ transform: translateX(
118
+ calc(var(--toggle-width) - var(--toggle-knob-size) - calc(2 * var(--space-2xs)))
119
+ );
120
+ }
121
+
122
+ .toggle-label {
123
+ font-family: var(--type-body-sm-font);
124
+ font-size: var(--type-body-sm-size);
125
+ color: var(--color-text);
126
+ cursor: pointer;
127
+ }
128
+
129
+ .toggle-label-disabled {
130
+ color: var(--color-text-muted);
131
+ }
132
+ </style>
@@ -0,0 +1,70 @@
1
+ /*
2
+ * aiaiai Design System — Component Library
3
+ *
4
+ * Svelte 5 components consuming tokens from components.css.
5
+ * Import tokens (base.css → semantic.css → components.css) before using.
6
+ *
7
+ * Usage:
8
+ * import { Button, Card, Badge } from '$lib/ui';
9
+ */
10
+
11
+ // Atoms
12
+ export { default as Badge } from "./Badge.svelte";
13
+ export { default as Tag } from "./Tag.svelte";
14
+ export { default as Status } from "./Status.svelte";
15
+ export { default as Skeleton } from "./Skeleton.svelte";
16
+ export { default as KeyValue } from "./KeyValue.svelte";
17
+
18
+ // Form controls
19
+ export { default as Button } from "./Button.svelte";
20
+ export { default as Input } from "./Input.svelte";
21
+ export { default as Textarea } from "./Textarea.svelte";
22
+ export { default as Select } from "./Select.svelte";
23
+ export { default as Toggle } from "./Toggle.svelte";
24
+ export { default as Checkbox } from "./Checkbox.svelte";
25
+ export { default as Label } from "./Label.svelte";
26
+ export { default as FileUpload } from "./FileUpload.svelte";
27
+ export { default as FileUploadItem } from "./FileUploadItem.svelte";
28
+
29
+ // Layout
30
+ export { default as Separator } from "./Separator.svelte";
31
+ export { default as Progress } from "./Progress.svelte";
32
+ export { default as List } from "./List.svelte";
33
+ export { default as ListItem } from "./ListItem.svelte";
34
+
35
+ // Containers
36
+ export { default as Card } from "./Card.svelte";
37
+ export { default as Panel } from "./Panel.svelte";
38
+ export { default as Modal } from "./Modal.svelte";
39
+ export { default as Popover } from "./Popover.svelte";
40
+ export { default as Menu } from "./Menu.svelte";
41
+ export { default as MenuItem } from "./MenuItem.svelte";
42
+ export { default as MenuSeparator } from "./MenuSeparator.svelte";
43
+
44
+ // Form controls — composite
45
+ export { default as Combobox } from "./Combobox.svelte";
46
+
47
+ // Tabs
48
+ export { default as Tabs } from "./Tabs.svelte";
49
+ export { default as TabList } from "./TabList.svelte";
50
+ export { default as Tab } from "./Tab.svelte";
51
+ export { default as TabPanel } from "./TabPanel.svelte";
52
+
53
+ // Feedback
54
+ export { default as Alert } from "./Alert.svelte";
55
+ export { default as Toast } from "./Toast.svelte";
56
+ export { default as EmptyState } from "./EmptyState.svelte";
57
+
58
+ // Navigation
59
+ export { default as Sidebar } from "./Sidebar.svelte";
60
+ export { default as SidebarItem } from "./SidebarItem.svelte";
61
+ export { default as SidebarSection } from "./SidebarSection.svelte";
62
+ export { default as BottomNav } from "./BottomNav.svelte";
63
+ export { default as BottomNavItem } from "./BottomNavItem.svelte";
64
+
65
+ // Complex
66
+ export { default as Stepper } from "./Stepper.svelte";
67
+ export { default as CodeBlock } from "./CodeBlock.svelte";
68
+ export { default as CodeEditor } from "./CodeEditor.svelte";
69
+ export { default as CollapsibleSection } from "./CollapsibleSection.svelte";
70
+ export { default as OptionGrid } from "./OptionGrid.svelte";
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@aiaiai-pt/design-system",
3
+ "version": "0.1.0",
4
+ "description": "Design system tokens and Svelte components for aiaiai products",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/aiaiai-pt/aiaiai-design-system.git"
10
+ },
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "svelte": "./components/index.js",
15
+ "exports": {
16
+ ".": {
17
+ "svelte": "./components/index.js",
18
+ "default": "./components/index.js"
19
+ },
20
+ "./components": {
21
+ "svelte": "./components/index.js",
22
+ "default": "./components/index.js"
23
+ },
24
+ "./components/*": {
25
+ "svelte": "./components/*",
26
+ "default": "./components/*"
27
+ },
28
+ "./tokens/*": "./tokens/*"
29
+ },
30
+ "files": [
31
+ "components",
32
+ "tokens"
33
+ ],
34
+ "peerDependencies": {
35
+ "svelte": "^5.0.0",
36
+ "@codemirror/view": "^6.0.0",
37
+ "@codemirror/state": "^6.0.0",
38
+ "@codemirror/language": "^6.0.0",
39
+ "@codemirror/commands": "^6.0.0",
40
+ "@codemirror/lang-sql": "^6.0.0",
41
+ "@codemirror/lang-python": "^6.0.0",
42
+ "@codemirror/lang-json": "^6.0.0",
43
+ "@lezer/highlight": "^1.0.0"
44
+ }
45
+ }