@miozu/jera 0.8.2 → 0.8.4

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miozu/jera",
3
- "version": "0.8.2",
3
+ "version": "0.8.4",
4
4
  "description": "Zero-dependency, AI-first component library for Svelte 5",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -36,7 +36,7 @@
36
36
  }
37
37
  },
38
38
  "devDependencies": {
39
- "svelte": "^5.41.0"
39
+ "svelte": "^5.49.2"
40
40
  },
41
41
  "keywords": [
42
42
  "svelte",
@@ -2,28 +2,24 @@
2
2
  @component SettingCard
3
3
 
4
4
  A card container for settings sections with optional danger variant.
5
- Provides consistent layout for settings items with labels, descriptions, and actions.
5
+ Use with SettingItem for structured setting rows.
6
6
 
7
7
  @example Basic settings card
8
8
  <SettingCard title="Account Settings">
9
- <div class="setting-item">
10
- <div class="setting-content">
11
- <h4 class="setting-label">Display Name</h4>
12
- <p class="setting-description">Your public display name</p>
13
- </div>
14
- <Input value={name} />
15
- </div>
9
+ <SettingItem label="Display Name" description="Your public display name">
10
+ {#snippet action()}
11
+ <Input value={name} />
12
+ {/snippet}
13
+ </SettingItem>
16
14
  </SettingCard>
17
15
 
18
16
  @example Danger zone
19
17
  <SettingCard title="Danger Zone" variant="danger">
20
- <div class="setting-item">
21
- <div class="setting-content">
22
- <h4 class="setting-label">Delete Account</h4>
23
- <p class="setting-description">This cannot be undone</p>
24
- </div>
25
- <Button variant="danger">Delete</Button>
26
- </div>
18
+ <SettingItem label="Delete Account" description="This cannot be undone">
19
+ {#snippet action()}
20
+ <Button variant="danger">Delete</Button>
21
+ {/snippet}
22
+ </SettingItem>
27
23
  </SettingCard>
28
24
  -->
29
25
  <script>
@@ -83,71 +79,4 @@
83
79
  display: flex;
84
80
  flex-direction: column;
85
81
  }
86
-
87
- /* Utility classes for setting items - exposed globally */
88
- :global(.setting-item) {
89
- display: flex;
90
- align-items: center;
91
- justify-content: space-between;
92
- gap: var(--space-4);
93
- padding: var(--space-4) 0;
94
- border-bottom: 1px solid var(--color-base02);
95
- }
96
-
97
- :global(.setting-item:last-child) {
98
- border-bottom: none;
99
- padding-bottom: 0;
100
- }
101
-
102
- :global(.setting-item:first-child) {
103
- padding-top: 0;
104
- }
105
-
106
- :global(.setting-content) {
107
- flex: 1;
108
- min-width: 0;
109
- }
110
-
111
- :global(.setting-label) {
112
- margin: 0 0 var(--space-1);
113
- font-size: var(--text-sm);
114
- font-weight: 500;
115
- color: var(--color-base06);
116
- }
117
-
118
- :global(.setting-description) {
119
- margin: 0;
120
- font-size: var(--text-xs);
121
- color: var(--color-base04);
122
- line-height: 1.5;
123
- }
124
-
125
- :global(.setting-item-icon) {
126
- display: flex;
127
- align-items: center;
128
- gap: var(--space-3);
129
- color: color-mix(in srgb, var(--color-base04) 80%, transparent);
130
- }
131
-
132
- :global(.setting-item-with-icon) {
133
- display: flex;
134
- align-items: flex-start;
135
- gap: var(--space-3);
136
- padding: var(--space-4) 0;
137
- border-bottom: 1px solid var(--color-base02);
138
- }
139
-
140
- :global(.setting-item-with-icon:last-child) {
141
- border-bottom: none;
142
- padding-bottom: 0;
143
- }
144
-
145
- /* Responsive */
146
- @media (max-width: 640px) {
147
- :global(.setting-item) {
148
- flex-direction: column;
149
- align-items: flex-start;
150
- gap: var(--space-3);
151
- }
152
- }
153
82
  </style>
@@ -0,0 +1,116 @@
1
+ <!--
2
+ @component SettingItem
3
+
4
+ A structured setting row for use inside SettingCard.
5
+ Replaces magic class names with a composable component API.
6
+
7
+ @example Basic setting
8
+ <SettingItem label="Display Name" description="Your public display name">
9
+ {#snippet action()}
10
+ <Input value={name} />
11
+ {/snippet}
12
+ </SettingItem>
13
+
14
+ @example With icon
15
+ <SettingItem label="Active Sessions" description="Manage your devices">
16
+ {#snippet leading()}
17
+ <Monitor size={16} />
18
+ {/snippet}
19
+ {#snippet action()}
20
+ <Button size="sm">Manage</Button>
21
+ {/snippet}
22
+ </SettingItem>
23
+ -->
24
+ <script>
25
+ let {
26
+ label = '',
27
+ description = '',
28
+ leading,
29
+ action,
30
+ class: className = ''
31
+ } = $props();
32
+ </script>
33
+
34
+ <div class="setting-item {className}" class:has-leading={leading}>
35
+ {#if leading}
36
+ <div class="setting-leading">
37
+ {@render leading()}
38
+ </div>
39
+ {/if}
40
+ <div class="setting-content">
41
+ {#if label}
42
+ <h4 class="setting-label">{label}</h4>
43
+ {/if}
44
+ {#if description}
45
+ <p class="setting-description">{description}</p>
46
+ {/if}
47
+ </div>
48
+ {#if action}
49
+ <div class="setting-action">
50
+ {@render action()}
51
+ </div>
52
+ {/if}
53
+ </div>
54
+
55
+ <style>
56
+ .setting-item {
57
+ display: flex;
58
+ align-items: center;
59
+ justify-content: space-between;
60
+ gap: var(--space-4);
61
+ padding: var(--space-4) 0;
62
+ border-bottom: 1px solid var(--color-base02);
63
+ }
64
+
65
+ .setting-item:last-child {
66
+ border-bottom: none;
67
+ padding-bottom: 0;
68
+ }
69
+
70
+ .setting-item:first-child {
71
+ padding-top: 0;
72
+ }
73
+
74
+ .has-leading {
75
+ justify-content: flex-start;
76
+ }
77
+
78
+ .setting-leading {
79
+ display: flex;
80
+ align-items: center;
81
+ color: color-mix(in srgb, var(--color-base04) 80%, transparent);
82
+ flex-shrink: 0;
83
+ }
84
+
85
+ .setting-content {
86
+ flex: 1;
87
+ min-width: 0;
88
+ }
89
+
90
+ .setting-label {
91
+ margin: 0 0 var(--space-1);
92
+ font-size: var(--text-sm);
93
+ font-weight: 500;
94
+ color: var(--color-base06);
95
+ }
96
+
97
+ .setting-description {
98
+ margin: 0;
99
+ font-size: var(--text-xs);
100
+ color: var(--color-base04);
101
+ line-height: 1.5;
102
+ }
103
+
104
+ .setting-action {
105
+ flex-shrink: 0;
106
+ }
107
+
108
+ /* Responsive */
109
+ @media (max-width: 640px) {
110
+ .setting-item:not(.has-leading) {
111
+ flex-direction: column;
112
+ align-items: flex-start;
113
+ gap: var(--space-3);
114
+ }
115
+ }
116
+ </style>
@@ -1,21 +1,18 @@
1
1
  <!--
2
2
  @component Accordion
3
3
 
4
- Collapsible content sections.
4
+ Group container for collapsible sections. Controls shared expand/collapse state.
5
5
 
6
- @example Single item
6
+ @example Single-open (default)
7
7
  <Accordion>
8
- <AccordionItem title="Section 1">
9
- Content for section 1
10
- </AccordionItem>
11
- <AccordionItem title="Section 2">
12
- Content for section 2
13
- </AccordionItem>
8
+ <AccordionItem title="Section 1">Content</AccordionItem>
9
+ <AccordionItem title="Section 2">Content</AccordionItem>
14
10
  </Accordion>
15
11
 
16
- @example Controlled
17
- <Accordion bind:expanded={openSections} multiple>
18
- ...
12
+ @example Multiple open
13
+ <Accordion bind:expanded={openIds} multiple>
14
+ <AccordionItem id="a" title="First">Content</AccordionItem>
15
+ <AccordionItem id="b" title="Second" badge="3">Content</AccordionItem>
19
16
  </Accordion>
20
17
  -->
21
18
  <script>
@@ -30,36 +27,30 @@
30
27
 
31
28
  function toggle(id) {
32
29
  if (multiple) {
33
- if (expanded.includes(id)) {
34
- expanded = expanded.filter(i => i !== id);
35
- } else {
36
- expanded = [...expanded, id];
37
- }
30
+ expanded = expanded.includes(id)
31
+ ? expanded.filter(i => i !== id)
32
+ : [...expanded, id];
38
33
  } else {
39
34
  expanded = expanded.includes(id) ? [] : [id];
40
35
  }
41
36
  }
42
37
 
43
- function isExpanded(id) {
44
- return expanded.includes(id);
45
- }
46
-
47
38
  setContext('accordion', {
48
39
  toggle,
49
40
  isExpanded: (id) => expanded.includes(id)
50
41
  });
51
42
  </script>
52
43
 
53
- <div class="accordion {className}">
44
+ <div class="jera-accordion {className}" role="region">
54
45
  {@render children?.()}
55
46
  </div>
56
47
 
57
48
  <style>
58
- .accordion {
49
+ .jera-accordion {
59
50
  display: flex;
60
51
  flex-direction: column;
61
- border: 1px solid var(--color-base03);
62
52
  border-radius: 0.5rem;
63
53
  overflow: hidden;
54
+ background: color-mix(in srgb, var(--color-base01) 60%, transparent);
64
55
  }
65
56
  </style>
@@ -1,63 +1,90 @@
1
1
  <!--
2
2
  @component AccordionItem
3
3
 
4
- Individual accordion section. Must be used inside Accordion.
4
+ Collapsible content section. Works in two modes:
5
5
 
6
- @example
7
- <AccordionItem id="section-1" title="Click to expand">
8
- Hidden content here
6
+ **Group** — inside an <Accordion>, state is managed by the parent context.
7
+ **Solo** standalone, manages its own state via bind:expanded.
8
+
9
+ @example Group (inside Accordion)
10
+ <Accordion>
11
+ <AccordionItem title="Section 1">Content</AccordionItem>
12
+ <AccordionItem title="Section 2" badge="5">Content</AccordionItem>
13
+ </Accordion>
14
+
15
+ @example Solo (standalone)
16
+ <AccordionItem title="Settings" bind:expanded={open}>
17
+ Content here
18
+ </AccordionItem>
19
+
20
+ @example With leading/trailing snippets
21
+ <AccordionItem title="Infrastructure">
22
+ {#snippet leading()}<Activity size={14} />{/snippet}
23
+ {#snippet trailing()}<Badge variant="success">OK</Badge>{/snippet}
24
+ Content here
9
25
  </AccordionItem>
10
26
  -->
11
27
  <script>
12
28
  import { getContext } from 'svelte';
13
29
  import { slide } from 'svelte/transition';
30
+ import { cubicOut } from 'svelte/easing';
14
31
 
15
32
  let {
16
33
  id,
17
34
  title = '',
35
+ expanded: expandedProp = $bindable(false),
18
36
  disabled = false,
37
+ badge = null,
38
+ leading,
39
+ trailing,
19
40
  children,
20
41
  class: className = ''
21
42
  } = $props();
22
43
 
23
44
  const accordion = getContext('accordion');
45
+ const itemId = id || `acc-${Math.random().toString(36).slice(2, 9)}`;
46
+ const inGroup = !!accordion;
24
47
 
25
- // Generate id if not provided
26
- const itemId = id || `accordion-${Math.random().toString(36).slice(2, 9)}`;
27
-
28
- const isOpen = $derived(accordion?.isExpanded(itemId) ?? false);
48
+ const isOpen = $derived(
49
+ inGroup ? (accordion.isExpanded(itemId) ?? false) : expandedProp
50
+ );
29
51
 
30
52
  function handleClick() {
31
- if (!disabled && accordion) {
53
+ if (disabled) return;
54
+ if (inGroup) {
32
55
  accordion.toggle(itemId);
33
- }
34
- }
35
-
36
- function handleKeydown(e) {
37
- if (e.key === 'Enter' || e.key === ' ') {
38
- e.preventDefault();
39
- handleClick();
56
+ } else {
57
+ expandedProp = !expandedProp;
40
58
  }
41
59
  }
42
60
  </script>
43
61
 
44
- <div class="accordion-item {className}" class:accordion-item-disabled={disabled}>
62
+ <div
63
+ class="accordion-item {className}"
64
+ class:accordion-item-solo={!inGroup}
65
+ class:accordion-item-disabled={disabled}
66
+ >
45
67
  <button
46
68
  type="button"
47
69
  class="accordion-trigger"
48
70
  class:accordion-trigger-open={isOpen}
49
71
  aria-expanded={isOpen}
50
72
  aria-controls="content-{itemId}"
51
- aria-disabled={disabled}
73
+ aria-disabled={disabled || undefined}
74
+ {disabled}
52
75
  onclick={handleClick}
53
- onkeydown={handleKeydown}
54
76
  >
77
+ {#if leading}{@render leading()}{/if}
55
78
  <span class="accordion-title">{title}</span>
79
+ {#if badge != null}
80
+ <span class="accordion-badge">{badge}</span>
81
+ {/if}
82
+ {#if trailing}{@render trailing()}{/if}
56
83
  <svg
57
- class="accordion-icon"
58
- class:accordion-icon-open={isOpen}
59
- width="16"
60
- height="16"
84
+ class="accordion-chevron"
85
+ class:accordion-chevron-open={isOpen}
86
+ width="14"
87
+ height="14"
61
88
  viewBox="0 0 24 24"
62
89
  fill="none"
63
90
  stroke="currentColor"
@@ -73,7 +100,9 @@
73
100
  <div
74
101
  id="content-{itemId}"
75
102
  class="accordion-content"
76
- transition:slide={{ duration: 200 }}
103
+ role="region"
104
+ aria-labelledby="trigger-{itemId}"
105
+ transition:slide={{ duration: 200, easing: cubicOut }}
77
106
  >
78
107
  <div class="accordion-body">
79
108
  {@render children?.()}
@@ -83,62 +112,110 @@
83
112
  </div>
84
113
 
85
114
  <style>
115
+ /* Item container */
86
116
  .accordion-item {
87
- border-bottom: 1px solid var(--color-base03);
117
+ border-bottom: 1px solid var(--color-base02);
88
118
  }
89
119
 
90
120
  .accordion-item:last-child {
91
121
  border-bottom: none;
92
122
  }
93
123
 
124
+ .accordion-item-solo {
125
+ border-radius: 0.5rem;
126
+ overflow: hidden;
127
+ background: color-mix(in srgb, var(--color-base01) 60%, transparent);
128
+ border-bottom: none;
129
+ }
130
+
94
131
  .accordion-item-disabled {
95
132
  opacity: 0.5;
133
+ pointer-events: none;
96
134
  }
97
135
 
136
+ /* Trigger / header */
98
137
  .accordion-trigger {
99
138
  display: flex;
100
139
  align-items: center;
101
- justify-content: space-between;
140
+ gap: 0.5rem;
102
141
  width: 100%;
103
- padding: 1rem;
104
- background: transparent;
142
+ height: 2.75rem;
143
+ padding: 0 1rem;
144
+ background: color-mix(in srgb, var(--color-base00) 50%, transparent);
105
145
  border: none;
106
- font-size: 0.9375rem;
107
- font-weight: 500;
108
- color: var(--color-base07);
146
+ font-family: inherit;
147
+ font-size: 0.75rem;
148
+ font-weight: 600;
149
+ color: var(--color-base06);
109
150
  cursor: pointer;
110
151
  text-align: left;
111
152
  transition: background 0.15s ease;
112
153
  }
113
154
 
114
- .accordion-trigger:hover:not([aria-disabled="true"]) {
115
- background: var(--color-base01);
155
+ .accordion-trigger:hover:not(:disabled) {
156
+ background: color-mix(in srgb, var(--color-base00) 80%, transparent);
116
157
  }
117
158
 
118
159
  .accordion-trigger-open {
119
- background: var(--color-base01);
160
+ background: color-mix(in srgb, var(--color-base00) 90%, transparent);
120
161
  }
121
162
 
163
+ .accordion-trigger:focus-visible {
164
+ outline: 2px solid var(--color-base0D);
165
+ outline-offset: -2px;
166
+ }
167
+
168
+ /* Title */
122
169
  .accordion-title {
123
170
  flex: 1;
171
+ overflow: hidden;
172
+ text-overflow: ellipsis;
173
+ white-space: nowrap;
174
+ }
175
+
176
+ /* Badge */
177
+ .accordion-badge {
178
+ flex-shrink: 0;
179
+ padding: 0.0625rem 0.375rem;
180
+ font-size: 0.6875rem;
181
+ font-weight: 600;
182
+ color: var(--color-base04);
183
+ background: color-mix(in srgb, var(--color-base04) 10%, transparent);
184
+ border-radius: 9999px;
124
185
  }
125
186
 
126
- .accordion-icon {
187
+ /* Chevron */
188
+ .accordion-chevron {
127
189
  flex-shrink: 0;
128
190
  color: var(--color-base05);
129
191
  transition: transform 0.2s ease;
130
192
  }
131
193
 
132
- .accordion-icon-open {
194
+ .accordion-trigger:hover:not(:disabled) .accordion-chevron {
195
+ color: var(--color-base06);
196
+ transform: scale(1.125);
197
+ }
198
+
199
+ .accordion-chevron-open {
133
200
  transform: rotate(180deg);
134
201
  }
135
202
 
203
+ .accordion-trigger:hover:not(:disabled) .accordion-chevron-open {
204
+ transform: rotate(180deg) scale(1.125);
205
+ }
206
+
207
+ /* Content */
136
208
  .accordion-content {
137
- overflow: hidden;
209
+ background: var(--color-base01);
210
+ border-bottom: 1px solid var(--color-base02);
211
+ }
212
+
213
+ .accordion-item:last-child .accordion-content {
214
+ border-bottom: none;
138
215
  }
139
216
 
140
217
  .accordion-body {
141
- padding: 0 1rem 1rem 1rem;
218
+ padding: 1rem;
142
219
  font-size: 0.875rem;
143
220
  color: var(--color-base05);
144
221
  line-height: 1.6;
@@ -45,10 +45,34 @@
45
45
  active = tab.id;
46
46
  onchange?.(tab);
47
47
  }
48
+
49
+ function handleKeydown(e, index) {
50
+ let nextIndex = index;
51
+
52
+ if (e.key === 'ArrowRight') {
53
+ nextIndex = (index + 1) % tabs.length;
54
+ } else if (e.key === 'ArrowLeft') {
55
+ nextIndex = (index - 1 + tabs.length) % tabs.length;
56
+ } else if (e.key === 'Home') {
57
+ nextIndex = 0;
58
+ } else if (e.key === 'End') {
59
+ nextIndex = tabs.length - 1;
60
+ } else {
61
+ return;
62
+ }
63
+
64
+ e.preventDefault();
65
+ const nextTab = tabs[nextIndex];
66
+ if (!nextTab.disabled) {
67
+ handleTabClick(nextTab);
68
+ const buttons = e.currentTarget.parentElement.querySelectorAll('[role="tab"]');
69
+ buttons[nextIndex]?.focus();
70
+ }
71
+ }
48
72
  </script>
49
73
 
50
74
  <nav class="tab-nav tab-nav-{variant} tab-nav-{size} {className}" role="tablist">
51
- {#each tabs as tab}
75
+ {#each tabs as tab, index}
52
76
  <button
53
77
  type="button"
54
78
  class="tab-item"
@@ -57,7 +81,9 @@
57
81
  role="tab"
58
82
  aria-selected={active === tab.id}
59
83
  aria-disabled={tab.disabled}
84
+ tabindex={active === tab.id ? 0 : -1}
60
85
  onclick={() => handleTabClick(tab)}
86
+ onkeydown={(e) => handleKeydown(e, index)}
61
87
  >
62
88
  {#if tab.icon}
63
89
  <span class="tab-icon">
@@ -1,7 +1,7 @@
1
1
  <!--
2
2
  @component Tabs
3
3
 
4
- Tabbed navigation component.
4
+ Tabbed navigation component with keyboard navigation.
5
5
 
6
6
  @example Basic usage
7
7
  <Tabs
@@ -13,7 +13,7 @@
13
13
  bind:active={activeTab}
14
14
  />
15
15
 
16
- @example With icons
16
+ @example With icons (component reference)
17
17
  <Tabs
18
18
  tabs={[
19
19
  { id: 'home', label: 'Home', icon: HomeIcon },
@@ -64,7 +64,6 @@
64
64
  const nextTab = tabs[nextIndex];
65
65
  if (!nextTab.disabled) {
66
66
  selectTab(nextTab);
67
- // Focus the next tab button
68
67
  const buttons = e.currentTarget.parentElement.querySelectorAll('[role="tab"]');
69
68
  buttons[nextIndex]?.focus();
70
69
  }
@@ -90,8 +89,9 @@
90
89
  onkeydown={(e) => handleKeydown(e, tab, index)}
91
90
  >
92
91
  {#if tab.icon}
92
+ {@const Icon = tab.icon}
93
93
  <span class="tab-icon">
94
- <svelte:component this={tab.icon} size={16} />
94
+ <Icon size={16} />
95
95
  </span>
96
96
  {/if}
97
97
  {#if tab.label}
@@ -107,10 +107,10 @@
107
107
  <style>
108
108
  .tabs {
109
109
  display: inline-flex;
110
- gap: 0.25rem;
110
+ gap: var(--space-1);
111
111
  background: var(--color-base01);
112
- border-radius: 0.5rem;
113
- padding: 0.25rem;
112
+ border-radius: var(--radius-lg);
113
+ padding: var(--space-1);
114
114
  }
115
115
 
116
116
  .tabs-full-width {
@@ -126,12 +126,12 @@
126
126
  .tab {
127
127
  display: inline-flex;
128
128
  align-items: center;
129
- gap: 0.5rem;
130
- padding: 0.5rem 1rem;
129
+ gap: var(--space-2);
130
+ padding: var(--space-2) var(--space-4);
131
131
  background: transparent;
132
132
  border: none;
133
- border-radius: 0.375rem;
134
- font-size: 0.875rem;
133
+ border-radius: var(--radius-md);
134
+ font-size: var(--text-sm);
135
135
  font-weight: 500;
136
136
  color: var(--color-base05);
137
137
  cursor: pointer;
@@ -141,13 +141,13 @@
141
141
 
142
142
  /* Size variants */
143
143
  .tabs-sm .tab {
144
- padding: 0.375rem 0.75rem;
145
- font-size: 0.75rem;
144
+ padding: var(--space-1) var(--space-3);
145
+ font-size: var(--text-xs);
146
146
  }
147
147
 
148
148
  .tabs-lg .tab {
149
- padding: 0.625rem 1.25rem;
150
- font-size: 1rem;
149
+ padding: var(--space-3) var(--space-5);
150
+ font-size: var(--text-base);
151
151
  }
152
152
 
153
153
  .tab:hover:not(.tab-disabled) {
@@ -180,12 +180,12 @@
180
180
  display: inline-flex;
181
181
  align-items: center;
182
182
  justify-content: center;
183
- min-width: 1.25rem;
184
- height: 1.25rem;
185
- padding: 0 0.375rem;
183
+ min-width: var(--space-5);
184
+ height: var(--space-5);
185
+ padding: 0 var(--space-1);
186
186
  background: var(--color-base03);
187
187
  border-radius: 9999px;
188
- font-size: 0.75rem;
188
+ font-size: var(--text-xs);
189
189
  font-weight: 600;
190
190
  }
191
191
 
@@ -224,7 +224,7 @@
224
224
  .tabs-pills {
225
225
  background: transparent;
226
226
  padding: 0;
227
- gap: 0.5rem;
227
+ gap: var(--space-2);
228
228
  }
229
229
 
230
230
  .tabs-pills .tab {
@@ -122,9 +122,9 @@
122
122
  {/if}
123
123
 
124
124
  <style>
125
- /* ============================================
125
+ /* --------------------------------------------
126
126
  SEGMENTED CONTROL
127
- ============================================ */
127
+ -------------------------------------------- */
128
128
  .theme-select-segmented {
129
129
  display: inline-flex;
130
130
  align-items: center;
@@ -177,9 +177,9 @@
177
177
  white-space: nowrap;
178
178
  }
179
179
 
180
- /* ============================================
180
+ /* --------------------------------------------
181
181
  SIZES - SEGMENTED
182
- ============================================ */
182
+ -------------------------------------------- */
183
183
  .theme-select-sm .theme-select-option {
184
184
  padding: 2px var(--space-2, 0.5rem);
185
185
  font-size: var(--text-xs, 0.75rem);
@@ -195,9 +195,9 @@
195
195
  font-size: var(--text-base, 1rem);
196
196
  }
197
197
 
198
- /* ============================================
198
+ /* --------------------------------------------
199
199
  DROPDOWN
200
- ============================================ */
200
+ -------------------------------------------- */
201
201
  .theme-select-dropdown {
202
202
  position: relative;
203
203
  display: inline-flex;
@@ -235,9 +235,9 @@
235
235
  color: var(--color-base04, #665c54);
236
236
  }
237
237
 
238
- /* ============================================
238
+ /* --------------------------------------------
239
239
  SIZES - DROPDOWN
240
- ============================================ */
240
+ -------------------------------------------- */
241
241
  .theme-select-dropdown.theme-select-sm .theme-select-native {
242
242
  padding: var(--space-1, 0.25rem) var(--space-6, 1.5rem) var(--space-1, 0.25rem) var(--space-2, 0.5rem);
243
243
  font-size: var(--text-xs, 0.75rem);
@@ -248,9 +248,9 @@
248
248
  font-size: var(--text-base, 1rem);
249
249
  }
250
250
 
251
- /* ============================================
251
+ /* --------------------------------------------
252
252
  REDUCED MOTION
253
- ============================================ */
253
+ -------------------------------------------- */
254
254
  @media (prefers-reduced-motion: reduce) {
255
255
  .theme-select-option,
256
256
  .theme-select-native {
@@ -121,9 +121,9 @@
121
121
  </button>
122
122
 
123
123
  <style>
124
- /* ============================================
124
+ /* --------------------------------------------
125
125
  BASE STYLES
126
- ============================================ */
126
+ -------------------------------------------- */
127
127
  .theme-toggle {
128
128
  position: relative;
129
129
  display: inline-flex;
@@ -141,9 +141,9 @@
141
141
  outline-offset: 2px;
142
142
  }
143
143
 
144
- /* ============================================
144
+ /* --------------------------------------------
145
145
  VARIANTS
146
- ============================================ */
146
+ -------------------------------------------- */
147
147
  .theme-toggle-ghost {
148
148
  background-color: transparent;
149
149
  color: var(--color-base05, #a89984);
@@ -176,9 +176,9 @@
176
176
  color: var(--color-base06, #d5c4a1);
177
177
  }
178
178
 
179
- /* ============================================
179
+ /* --------------------------------------------
180
180
  SIZES
181
- ============================================ */
181
+ -------------------------------------------- */
182
182
  .theme-toggle-sm {
183
183
  padding: var(--space-1, 0.25rem);
184
184
  min-width: 1.75rem;
@@ -197,9 +197,9 @@
197
197
  min-height: 2.75rem;
198
198
  }
199
199
 
200
- /* ============================================
200
+ /* --------------------------------------------
201
201
  ICON ANIMATION
202
- ============================================ */
202
+ -------------------------------------------- */
203
203
  .theme-toggle-icon-wrapper {
204
204
  position: relative;
205
205
  display: flex;
@@ -235,9 +235,9 @@
235
235
  color: var(--color-base0D, #83a598);
236
236
  }
237
237
 
238
- /* ============================================
238
+ /* --------------------------------------------
239
239
  REDUCED MOTION
240
- ============================================ */
240
+ -------------------------------------------- */
241
241
  @media (prefers-reduced-motion: reduce) {
242
242
  .theme-toggle-icon {
243
243
  transition: opacity var(--duration-fast, 150ms) var(--ease-out, ease-out);
@@ -249,9 +249,9 @@
249
249
  }
250
250
  }
251
251
 
252
- /* ============================================
252
+ /* --------------------------------------------
253
253
  LABEL
254
- ============================================ */
254
+ -------------------------------------------- */
255
255
  .theme-toggle-label {
256
256
  font-size: var(--text-sm, 0.875rem);
257
257
  color: var(--color-base05, #a89984);
package/src/index.js CHANGED
@@ -21,9 +21,9 @@
21
21
  * import '@miozu/jera/tokens';
22
22
  */
23
23
 
24
- // ============================================
24
+ // --------------------------------------------
25
25
  // COMPONENTS - Primitives
26
- // ============================================
26
+ // --------------------------------------------
27
27
 
28
28
  export { default as Button } from './components/primitives/Button.svelte';
29
29
  export { default as Badge } from './components/primitives/Badge.svelte';
@@ -43,9 +43,9 @@ export { default as MemberCard } from './components/primitives/MemberCard.svelte
43
43
  export { default as ThemeToggle } from './components/primitives/ThemeToggle.svelte';
44
44
  export { default as ThemeSelect } from './components/primitives/ThemeSelect.svelte';
45
45
 
46
- // ============================================
46
+ // --------------------------------------------
47
47
  // COMPONENTS - Forms
48
- // ============================================
48
+ // --------------------------------------------
49
49
 
50
50
  export { default as Input } from './components/forms/Input.svelte';
51
51
  export { default as IconInput } from './components/forms/IconInput.svelte';
@@ -62,9 +62,9 @@ export { default as SearchInput } from './components/forms/SearchInput.svelte';
62
62
  export { default as PinInput } from './components/forms/PinInput.svelte';
63
63
  export { default as Dropzone } from './components/forms/Dropzone.svelte';
64
64
 
65
- // ============================================
65
+ // --------------------------------------------
66
66
  // COMPONENTS - Feedback
67
- // ============================================
67
+ // --------------------------------------------
68
68
 
69
69
  export {
70
70
  default as Toast,
@@ -79,9 +79,9 @@ export { default as Spinner } from './components/feedback/Spinner.svelte';
79
79
  export { default as EmptyState } from './components/feedback/EmptyState.svelte';
80
80
  export { default as Alert } from './components/feedback/Alert.svelte';
81
81
 
82
- // ============================================
82
+ // --------------------------------------------
83
83
  // COMPONENTS - Overlays
84
- // ============================================
84
+ // --------------------------------------------
85
85
 
86
86
  export { default as Modal } from './components/overlays/Modal.svelte';
87
87
  export { default as Popover } from './components/overlays/Popover.svelte';
@@ -90,9 +90,9 @@ export { default as DropdownItem } from './components/overlays/DropdownItem.svel
90
90
  export { default as DropdownDivider } from './components/overlays/DropdownDivider.svelte';
91
91
  export { default as ConfirmDialog } from './components/overlays/ConfirmDialog.svelte';
92
92
 
93
- // ============================================
93
+ // --------------------------------------------
94
94
  // COMPONENTS - Navigation
95
- // ============================================
95
+ // --------------------------------------------
96
96
 
97
97
  export { default as Tabs } from './components/navigation/Tabs.svelte';
98
98
  export { default as TabNav } from './components/navigation/TabNav.svelte';
@@ -135,31 +135,32 @@ export { default as LeftBarToggle } from './components/navigation/LeftBarToggle.
135
135
  export { default as LeftBarPopover } from './components/navigation/LeftBarPopover.svelte';
136
136
  export { default as DropdownContainer } from './components/navigation/DropdownContainer.svelte';
137
137
 
138
- // ============================================
138
+ // --------------------------------------------
139
139
  // COMPONENTS - Layout
140
- // ============================================
140
+ // --------------------------------------------
141
141
 
142
142
  export { default as PageHeader } from './components/layout/PageHeader.svelte';
143
143
  export { default as SettingCard } from './components/layout/SettingCard.svelte';
144
+ export { default as SettingItem } from './components/layout/SettingItem.svelte';
144
145
 
145
- // ============================================
146
+ // --------------------------------------------
146
147
  // COMPONENTS - Documentation
147
- // ============================================
148
+ // --------------------------------------------
148
149
 
149
150
  export { default as CodeBlock } from './components/docs/CodeBlock.svelte';
150
151
  export { default as PropsTable } from './components/docs/PropsTable.svelte';
151
152
  export { default as SplitPane } from './components/docs/SplitPane.svelte';
152
153
  export { default as DocSection } from './components/docs/DocSection.svelte';
153
154
 
154
- // ============================================
155
+ // --------------------------------------------
155
156
  // UTILITIES - Class Composition
156
- // ============================================
157
+ // --------------------------------------------
157
158
 
158
159
  export { cn, rcn, cv, mergeClasses, when, match } from './utils/cn.svelte.js';
159
160
 
160
- // ============================================
161
+ // --------------------------------------------
161
162
  // UTILITIES - Reactive State
162
- // ============================================
163
+ // --------------------------------------------
163
164
 
164
165
  export {
165
166
  createReactive,
@@ -193,9 +194,9 @@ export {
193
194
  DEFAULT_LANGUAGES
194
195
  } from './utils/highlighter.js';
195
196
 
196
- // ============================================
197
+ // --------------------------------------------
197
198
  // ACTIONS
198
- // ============================================
199
+ // --------------------------------------------
199
200
 
200
201
  export {
201
202
  clickOutside,