@miozu/jera 0.0.2 → 0.3.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.
- package/CLAUDE.md +443 -0
- package/README.md +211 -1
- package/llms.txt +64 -0
- package/package.json +44 -14
- package/src/actions/index.js +375 -0
- package/src/components/feedback/EmptyState.svelte +179 -0
- package/src/components/feedback/ProgressBar.svelte +116 -0
- package/src/components/feedback/Skeleton.svelte +107 -0
- package/src/components/feedback/Spinner.svelte +77 -0
- package/src/components/feedback/Toast.svelte +297 -0
- package/src/components/forms/Checkbox.svelte +147 -0
- package/src/components/forms/Dropzone.svelte +248 -0
- package/src/components/forms/FileUpload.svelte +266 -0
- package/src/components/forms/IconInput.svelte +184 -0
- package/src/components/forms/Input.svelte +121 -0
- package/src/components/forms/NumberInput.svelte +225 -0
- package/src/components/forms/PinInput.svelte +169 -0
- package/src/components/forms/Radio.svelte +143 -0
- package/src/components/forms/RadioGroup.svelte +62 -0
- package/src/components/forms/RangeSlider.svelte +212 -0
- package/src/components/forms/SearchInput.svelte +175 -0
- package/src/components/forms/Select.svelte +326 -0
- package/src/components/forms/Switch.svelte +159 -0
- package/src/components/forms/Textarea.svelte +122 -0
- package/src/components/navigation/Accordion.svelte +65 -0
- package/src/components/navigation/AccordionItem.svelte +146 -0
- package/src/components/navigation/Tabs.svelte +239 -0
- package/src/components/overlays/ConfirmDialog.svelte +272 -0
- package/src/components/overlays/Dropdown.svelte +153 -0
- package/src/components/overlays/DropdownDivider.svelte +23 -0
- package/src/components/overlays/DropdownItem.svelte +97 -0
- package/src/components/overlays/Modal.svelte +232 -0
- package/src/components/overlays/Popover.svelte +206 -0
- package/src/components/primitives/Avatar.svelte +132 -0
- package/src/components/primitives/Badge.svelte +118 -0
- package/src/components/primitives/Button.svelte +262 -0
- package/src/components/primitives/Card.svelte +104 -0
- package/src/components/primitives/Divider.svelte +105 -0
- package/src/components/primitives/LazyImage.svelte +104 -0
- package/src/components/primitives/Link.svelte +122 -0
- package/src/components/primitives/StatusBadge.svelte +122 -0
- package/src/index.js +128 -0
- package/src/tokens/colors.css +189 -0
- package/src/tokens/effects.css +128 -0
- package/src/tokens/index.css +81 -0
- package/src/tokens/spacing.css +49 -0
- package/src/tokens/typography.css +79 -0
- package/src/utils/cn.svelte.js +175 -0
- package/src/utils/index.js +17 -0
- package/src/utils/reactive.svelte.js +239 -0
- package/jera.js +0 -135
- package/www/components/jera/Input/Input.svelte +0 -63
- package/www/components/jera/Input/index.js +0 -1
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
# @miozu/jera - AI Context File
|
|
2
|
+
|
|
3
|
+
**Package:** @miozu/jera
|
|
4
|
+
**Purpose:** Minimal, reactive component library for Svelte 5
|
|
5
|
+
**Author:** Nicholas Glazer <glazer.nicholas@gmail.com>
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Project Structure
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
jera/
|
|
13
|
+
├── src/
|
|
14
|
+
│ ├── index.js # Main exports
|
|
15
|
+
│ ├── tokens/ # Design tokens (CSS custom properties)
|
|
16
|
+
│ │ ├── index.css # Bundle all tokens
|
|
17
|
+
│ │ ├── colors.css # Miozu Base16 palette
|
|
18
|
+
│ │ ├── spacing.css # 4px-based scale
|
|
19
|
+
│ │ ├── typography.css # Font system
|
|
20
|
+
│ │ └── effects.css # Shadows, radius, transitions
|
|
21
|
+
│ ├── utils/
|
|
22
|
+
│ │ ├── cn.svelte.js # cn(), cv() class utilities
|
|
23
|
+
│ │ └── reactive.svelte.js # ThemeState, reactive helpers
|
|
24
|
+
│ ├── actions/
|
|
25
|
+
│ │ └── index.js # Svelte actions
|
|
26
|
+
│ └── components/
|
|
27
|
+
│ ├── primitives/ # Button, Badge, Divider, Avatar
|
|
28
|
+
│ ├── forms/ # Input, Select, Checkbox, Switch
|
|
29
|
+
│ ├── feedback/ # Toast, Skeleton, ProgressBar, Spinner
|
|
30
|
+
│ ├── overlays/ # Modal, Popover
|
|
31
|
+
│ └── navigation/ # Tabs, Accordion, AccordionItem
|
|
32
|
+
├── llms.txt # AI documentation index
|
|
33
|
+
├── CLAUDE.md # This file
|
|
34
|
+
└── package.json
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Coding Standards
|
|
40
|
+
|
|
41
|
+
### Svelte 5 Patterns (REQUIRED)
|
|
42
|
+
- Use `$props()` for component props (single call only)
|
|
43
|
+
- Use `$state()` for reactive local state
|
|
44
|
+
- Use `$derived()` for computed values
|
|
45
|
+
- Use `$effect()` sparingly, only for side effects
|
|
46
|
+
- Use `$bindable()` for two-way binding props
|
|
47
|
+
|
|
48
|
+
### Component Template
|
|
49
|
+
```svelte
|
|
50
|
+
<script>
|
|
51
|
+
let {
|
|
52
|
+
variant = 'default',
|
|
53
|
+
size = 'md',
|
|
54
|
+
disabled = false,
|
|
55
|
+
class: className = '',
|
|
56
|
+
...rest
|
|
57
|
+
} = $props();
|
|
58
|
+
|
|
59
|
+
const computedClass = $derived(/* class logic */);
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
<element class={computedClass} {disabled} {...rest}>
|
|
63
|
+
{@render children?.()}
|
|
64
|
+
</element>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Class Variants (cv) Pattern
|
|
68
|
+
```javascript
|
|
69
|
+
export const componentStyles = cv({
|
|
70
|
+
base: 'base-classes here',
|
|
71
|
+
variants: {
|
|
72
|
+
variantName: {
|
|
73
|
+
option1: 'classes-for-option1',
|
|
74
|
+
option2: 'classes-for-option2'
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
compounds: [
|
|
78
|
+
{ condition: { variant: 'x', size: 'y' }, class: 'compound-classes' }
|
|
79
|
+
],
|
|
80
|
+
defaults: { variantName: 'option1' }
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Naming Conventions
|
|
85
|
+
- Components: PascalCase (`Button.svelte`)
|
|
86
|
+
- Utilities: camelCase (`createThemeContext`)
|
|
87
|
+
- CSS tokens: kebab-case (`--color-primary`)
|
|
88
|
+
- Actions: camelCase (`clickOutside`)
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Miozu Color System
|
|
93
|
+
|
|
94
|
+
### Base Colors (Grayscale)
|
|
95
|
+
| Token | Hex | Usage |
|
|
96
|
+
|-------|-----|-------|
|
|
97
|
+
| `--base0` | #232733 | Darkest background |
|
|
98
|
+
| `--base1` | #2C3040 | Default dark bg |
|
|
99
|
+
| `--base2` | #3E4359 | Selection, hover |
|
|
100
|
+
| `--base3` | #565E78 | Comments, subtle |
|
|
101
|
+
| `--base4` | #737E99 | Muted text |
|
|
102
|
+
| `--base5` | #D0D2DB | Default text |
|
|
103
|
+
| `--base6` | #F3F4F7 | Light text |
|
|
104
|
+
| `--base7` | #FAFDFB | Lightest/white |
|
|
105
|
+
|
|
106
|
+
### Accent Colors
|
|
107
|
+
| Token | Hex | Usage |
|
|
108
|
+
|-------|-----|-------|
|
|
109
|
+
| `--magenta` | #C974E6 | Primary brand |
|
|
110
|
+
| `--blue` | #83D2FC | Info, links |
|
|
111
|
+
| `--green` | #6DD672 | Success |
|
|
112
|
+
| `--yellow` | #E8D176 | Warning |
|
|
113
|
+
| `--red` | #EB3137 | Error |
|
|
114
|
+
| `--cyan` | #40FFE2 | Accent |
|
|
115
|
+
| `--orange` | #FF9837 | Attention |
|
|
116
|
+
| `--peach` | #FF9982 | Warm accent |
|
|
117
|
+
|
|
118
|
+
### Semantic Mappings
|
|
119
|
+
```css
|
|
120
|
+
--color-bg: var(--base0);
|
|
121
|
+
--color-surface: var(--base1);
|
|
122
|
+
--color-text: var(--base5);
|
|
123
|
+
--color-text-strong: var(--base7);
|
|
124
|
+
--color-primary: var(--magenta);
|
|
125
|
+
--color-success: var(--green);
|
|
126
|
+
--color-error: var(--red);
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Component API Reference
|
|
132
|
+
|
|
133
|
+
### Button
|
|
134
|
+
```svelte
|
|
135
|
+
<Button
|
|
136
|
+
variant="primary|secondary|ghost|outline|danger|success"
|
|
137
|
+
size="xs|sm|md|lg|xl"
|
|
138
|
+
disabled={boolean}
|
|
139
|
+
loading={boolean}
|
|
140
|
+
fullWidth={boolean}
|
|
141
|
+
href={string} // Renders as <a> if provided
|
|
142
|
+
onclick={function}
|
|
143
|
+
>
|
|
144
|
+
Content
|
|
145
|
+
</Button>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Input
|
|
149
|
+
```svelte
|
|
150
|
+
<Input
|
|
151
|
+
bind:value={string}
|
|
152
|
+
type="text|email|password|number|..."
|
|
153
|
+
placeholder={string}
|
|
154
|
+
disabled={boolean}
|
|
155
|
+
required={boolean}
|
|
156
|
+
oninput={function}
|
|
157
|
+
onchange={function}
|
|
158
|
+
/>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Select
|
|
162
|
+
```svelte
|
|
163
|
+
<Select
|
|
164
|
+
options={[{ value, label }]}
|
|
165
|
+
bind:value={any}
|
|
166
|
+
placeholder={string}
|
|
167
|
+
labelKey="label"
|
|
168
|
+
valueKey="value"
|
|
169
|
+
disabled={boolean}
|
|
170
|
+
onchange={function}
|
|
171
|
+
/>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Badge
|
|
175
|
+
```svelte
|
|
176
|
+
<Badge
|
|
177
|
+
variant="default|primary|secondary|success|warning|error"
|
|
178
|
+
size="sm|md|lg"
|
|
179
|
+
>
|
|
180
|
+
Text
|
|
181
|
+
</Badge>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Toast
|
|
185
|
+
```svelte
|
|
186
|
+
<!-- In root layout -->
|
|
187
|
+
<script>
|
|
188
|
+
import { Toast, createToastContext } from '@miozu/jera';
|
|
189
|
+
const toast = createToastContext();
|
|
190
|
+
</script>
|
|
191
|
+
<Toast />
|
|
192
|
+
|
|
193
|
+
<!-- Usage anywhere -->
|
|
194
|
+
<script>
|
|
195
|
+
import { getToastContext } from '@miozu/jera';
|
|
196
|
+
const toast = getToastContext();
|
|
197
|
+
toast.success('Message');
|
|
198
|
+
toast.error('Error message');
|
|
199
|
+
</script>
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Modal
|
|
203
|
+
```svelte
|
|
204
|
+
<script>
|
|
205
|
+
import { Modal, Button } from '@miozu/jera';
|
|
206
|
+
let showModal = $state(false);
|
|
207
|
+
</script>
|
|
208
|
+
|
|
209
|
+
<Button onclick={() => showModal = true}>Open Modal</Button>
|
|
210
|
+
|
|
211
|
+
<Modal bind:open={showModal} title="Confirm Action" variant="danger">
|
|
212
|
+
<p>Are you sure you want to proceed?</p>
|
|
213
|
+
{#snippet footer()}
|
|
214
|
+
<Button variant="ghost" onclick={() => showModal = false}>Cancel</Button>
|
|
215
|
+
<Button variant="danger" onclick={handleConfirm}>Confirm</Button>
|
|
216
|
+
{/snippet}
|
|
217
|
+
</Modal>
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Props: `open`, `title`, `size` (sm/md/lg/xl), `variant` (default/danger/warning/success/info), `closeOnBackdrop`, `closeOnEscape`, `showClose`, `children`, `footer`, `icon`, `onclose`
|
|
221
|
+
|
|
222
|
+
### Popover
|
|
223
|
+
```svelte
|
|
224
|
+
<script>
|
|
225
|
+
import { Popover, Button } from '@miozu/jera';
|
|
226
|
+
</script>
|
|
227
|
+
|
|
228
|
+
<Popover content="Helpful tooltip text" position="top">
|
|
229
|
+
<Button>Hover me</Button>
|
|
230
|
+
</Popover>
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Props: `content`, `position` (top/bottom/left/right), `delay` ({show, hide}), `offset`
|
|
234
|
+
|
|
235
|
+
### Divider
|
|
236
|
+
```svelte
|
|
237
|
+
<Divider />
|
|
238
|
+
<Divider orientation="vertical" />
|
|
239
|
+
<Divider>or continue with</Divider>
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Props: `orientation` (horizontal/vertical), `thickness`, `spacing`, `children`
|
|
243
|
+
|
|
244
|
+
### Avatar
|
|
245
|
+
```svelte
|
|
246
|
+
<Avatar src="/user.jpg" alt="John Doe" />
|
|
247
|
+
<Avatar name="John Doe" />
|
|
248
|
+
<Avatar src="/user.jpg" status="online" size="lg" />
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Props: `src`, `alt`, `name`, `size` (xs/sm/md/lg/xl/2xl), `status` (online/offline/busy/away)
|
|
252
|
+
|
|
253
|
+
### Skeleton
|
|
254
|
+
```svelte
|
|
255
|
+
<Skeleton width="80%" />
|
|
256
|
+
<Skeleton variant="circle" size="48px" />
|
|
257
|
+
<Skeleton variant="rect" width="100%" height="200px" />
|
|
258
|
+
<Skeleton lines={3} />
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Props: `variant` (text/heading/circle/rect), `width`, `height`, `size`, `lines`, `animate`
|
|
262
|
+
|
|
263
|
+
### ProgressBar
|
|
264
|
+
```svelte
|
|
265
|
+
<ProgressBar value={65} />
|
|
266
|
+
<ProgressBar value={80} showLabel variant="success" />
|
|
267
|
+
<ProgressBar indeterminate />
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Props: `value`, `max`, `size` (sm/md/lg), `variant` (primary/success/warning/error/info), `showLabel`, `label`, `indeterminate`
|
|
271
|
+
|
|
272
|
+
### Spinner
|
|
273
|
+
```svelte
|
|
274
|
+
<Spinner />
|
|
275
|
+
<Spinner size="lg" color="var(--color-base11)" />
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Props: `size` (xs/sm/md/lg/xl), `color`, `label`
|
|
279
|
+
|
|
280
|
+
### Tabs
|
|
281
|
+
```svelte
|
|
282
|
+
<script>
|
|
283
|
+
import { Tabs } from '@miozu/jera';
|
|
284
|
+
let activeTab = $state('tab1');
|
|
285
|
+
</script>
|
|
286
|
+
|
|
287
|
+
<Tabs
|
|
288
|
+
tabs={[
|
|
289
|
+
{ id: 'tab1', label: 'Overview' },
|
|
290
|
+
{ id: 'tab2', label: 'Settings', badge: 3 },
|
|
291
|
+
{ id: 'tab3', label: 'Analytics', disabled: true }
|
|
292
|
+
]}
|
|
293
|
+
bind:active={activeTab}
|
|
294
|
+
variant="underline"
|
|
295
|
+
/>
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Props: `tabs` (array), `active`, `variant` (default/underline/pills), `size` (sm/md/lg), `fullWidth`, `onchange`
|
|
299
|
+
|
|
300
|
+
### Accordion
|
|
301
|
+
```svelte
|
|
302
|
+
<script>
|
|
303
|
+
import { Accordion, AccordionItem } from '@miozu/jera';
|
|
304
|
+
</script>
|
|
305
|
+
|
|
306
|
+
<Accordion multiple>
|
|
307
|
+
<AccordionItem id="section1" title="Section 1">
|
|
308
|
+
Content for section 1
|
|
309
|
+
</AccordionItem>
|
|
310
|
+
<AccordionItem id="section2" title="Section 2">
|
|
311
|
+
Content for section 2
|
|
312
|
+
</AccordionItem>
|
|
313
|
+
</Accordion>
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Accordion props: `expanded` (array of ids), `multiple`
|
|
317
|
+
AccordionItem props: `id`, `title`, `disabled`
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## Actions Reference
|
|
322
|
+
|
|
323
|
+
### clickOutside
|
|
324
|
+
```svelte
|
|
325
|
+
<div use:clickOutside={() => isOpen = false}>
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### focusTrap
|
|
329
|
+
```svelte
|
|
330
|
+
<dialog use:focusTrap={{ enabled: isOpen }}>
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### portal
|
|
334
|
+
```svelte
|
|
335
|
+
<div use:portal={'body'}>
|
|
336
|
+
Renders at body level
|
|
337
|
+
</div>
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### escapeKey
|
|
341
|
+
```svelte
|
|
342
|
+
<div use:escapeKey={() => close()}>
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## Common Tasks
|
|
348
|
+
|
|
349
|
+
### Add New Component
|
|
350
|
+
1. Create file in appropriate folder (`primitives/`, `forms/`, `feedback/`)
|
|
351
|
+
2. Use single `$props()` call
|
|
352
|
+
3. Export styles with `cv()` if variants needed
|
|
353
|
+
4. Add to `src/index.js` exports
|
|
354
|
+
5. Document in this file
|
|
355
|
+
|
|
356
|
+
### Add New Token
|
|
357
|
+
1. Add to appropriate token file in `src/tokens/`
|
|
358
|
+
2. Use semantic naming
|
|
359
|
+
3. Add light theme variant if applicable
|
|
360
|
+
|
|
361
|
+
### Theme Switching
|
|
362
|
+
```javascript
|
|
363
|
+
// System preference detection
|
|
364
|
+
const theme = matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
365
|
+
document.documentElement.setAttribute('data-theme', theme);
|
|
366
|
+
|
|
367
|
+
// Or use ThemeState class
|
|
368
|
+
import { createThemeContext } from '@miozu/jera';
|
|
369
|
+
const theme = createThemeContext();
|
|
370
|
+
theme.init(); // Reads from localStorage/system
|
|
371
|
+
theme.toggle();
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Supported Theme Selectors
|
|
375
|
+
jera supports multiple theme attribute values for flexibility:
|
|
376
|
+
|
|
377
|
+
| Theme | Selectors |
|
|
378
|
+
|-------|-----------|
|
|
379
|
+
| Dark | `[data-theme="dark"]`, `[data-theme="miozu-dark"]`, `.dark` |
|
|
380
|
+
| Light | `[data-theme="light"]`, `[data-theme="miozu-light"]`, `.light` |
|
|
381
|
+
| High Contrast | `[data-theme="high-contrast"]` |
|
|
382
|
+
|
|
383
|
+
This allows jera to integrate with any theming system. For example:
|
|
384
|
+
- Selify apps use `miozu-dark` / `miozu-light`
|
|
385
|
+
- Generic apps can use `dark` / `light`
|
|
386
|
+
- Class-based theming via `.dark` / `.light`
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## Integration with dash.selify.ai
|
|
391
|
+
|
|
392
|
+
jera components work out-of-the-box with dash.selify.ai. The semantic tokens are already configured in both systems.
|
|
393
|
+
|
|
394
|
+
### Required: Import jera tokens
|
|
395
|
+
```css
|
|
396
|
+
/* In your app.css or layout */
|
|
397
|
+
@import '@miozu/jera/tokens/colors.css';
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### Semantic Token Mapping
|
|
401
|
+
| jera Token | dash.selify.ai Equivalent |
|
|
402
|
+
|------------|---------------------------|
|
|
403
|
+
| `--color-bg` | `--color-base00` |
|
|
404
|
+
| `--color-surface` | `--color-base01` |
|
|
405
|
+
| `--color-surface-alt` | `--color-base02` |
|
|
406
|
+
| `--color-text` | `--color-base05` |
|
|
407
|
+
| `--color-text-strong` | `--color-base07` |
|
|
408
|
+
| `--color-text-muted` | `--color-base04` |
|
|
409
|
+
| `--color-primary` | `--color-base0D` |
|
|
410
|
+
| `--color-success` | `--color-base0B` |
|
|
411
|
+
| `--color-warning` | `--color-base0A` |
|
|
412
|
+
| `--color-error` | `--color-base08` |
|
|
413
|
+
| `--color-info` | `--color-base0C` |
|
|
414
|
+
|
|
415
|
+
### Using jera Components in dash.selify.ai
|
|
416
|
+
```svelte
|
|
417
|
+
<script>
|
|
418
|
+
import { Button, Modal, Input } from '@miozu/jera';
|
|
419
|
+
|
|
420
|
+
let showModal = $state(false);
|
|
421
|
+
</script>
|
|
422
|
+
|
|
423
|
+
<!-- Works with existing dash.selify.ai theme system -->
|
|
424
|
+
<Button variant="primary" onclick={() => showModal = true}>
|
|
425
|
+
Open Modal
|
|
426
|
+
</Button>
|
|
427
|
+
|
|
428
|
+
<Modal bind:open={showModal} title="Example">
|
|
429
|
+
<Input placeholder="Type here..." />
|
|
430
|
+
</Modal>
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
## Rules for AI Assistants
|
|
436
|
+
|
|
437
|
+
1. **Always use Svelte 5 runes** - No legacy `$:`, `export let`, stores
|
|
438
|
+
2. **Single $props() call** - Destructure all props in one call
|
|
439
|
+
3. **Use cv() for variants** - Don't hardcode conditional classes
|
|
440
|
+
4. **Semantic colors** - Use `--color-*` tokens, not raw `--base*`
|
|
441
|
+
5. **Accessibility first** - Include ARIA attributes, keyboard support
|
|
442
|
+
6. **No TypeScript** - Pure JavaScript with JSDoc for documentation
|
|
443
|
+
7. **Mobile-first** - Design for mobile, enhance for desktop
|
package/README.md
CHANGED
|
@@ -1 +1,211 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @miozu/jera
|
|
2
|
+
|
|
3
|
+
A minimal, reactive component library for Svelte 5.
|
|
4
|
+
|
|
5
|
+
**Jera** (ᛃ) — the rune of harvest and cycles. Build on foundations, yield results.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Miozu Design Tokens** — Base16 color system, spacing, typography, effects
|
|
10
|
+
- **Reactive Utilities** — `cn()`, `cv()` for class composition with Svelte 5 runes
|
|
11
|
+
- **Accessible Components** — Button, Input, Select, Badge, Checkbox, Switch, Toast
|
|
12
|
+
- **Svelte Actions** — clickOutside, focusTrap, portal, escapeKey, and more
|
|
13
|
+
- **AI-First Documentation** — Optimized for LLM-assisted development
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pnpm add @miozu/jera
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```svelte
|
|
24
|
+
<script>
|
|
25
|
+
import { Button, Input, Badge } from '@miozu/jera';
|
|
26
|
+
import '@miozu/jera/tokens';
|
|
27
|
+
|
|
28
|
+
let email = $state('');
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<Input bind:value={email} type="email" placeholder="Enter email" />
|
|
32
|
+
<Button onclick={() => console.log(email)}>Submit</Button>
|
|
33
|
+
<Badge variant="success">Active</Badge>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Design Tokens
|
|
37
|
+
|
|
38
|
+
Import tokens for consistent styling:
|
|
39
|
+
|
|
40
|
+
```css
|
|
41
|
+
/* All tokens */
|
|
42
|
+
@import '@miozu/jera/tokens';
|
|
43
|
+
|
|
44
|
+
/* Individual token sets */
|
|
45
|
+
@import '@miozu/jera/tokens/colors';
|
|
46
|
+
@import '@miozu/jera/tokens/spacing';
|
|
47
|
+
@import '@miozu/jera/tokens/typography';
|
|
48
|
+
@import '@miozu/jera/tokens/effects';
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Miozu Color Palette
|
|
52
|
+
|
|
53
|
+
| Base | Hex | Accent | Hex |
|
|
54
|
+
|------|-----|--------|-----|
|
|
55
|
+
| base0 | `#232733` | magenta | `#C974E6` |
|
|
56
|
+
| base1 | `#2C3040` | blue | `#83D2FC` |
|
|
57
|
+
| base2 | `#3E4359` | green | `#6DD672` |
|
|
58
|
+
| base3 | `#565E78` | yellow | `#E8D176` |
|
|
59
|
+
| base4 | `#737E99` | red | `#EB3137` |
|
|
60
|
+
| base5 | `#D0D2DB` | cyan | `#40FFE2` |
|
|
61
|
+
| base6 | `#F3F4F7` | orange | `#FF9837` |
|
|
62
|
+
| base7 | `#FAFDFB` | peach | `#FF9982` |
|
|
63
|
+
|
|
64
|
+
## Components
|
|
65
|
+
|
|
66
|
+
### Button
|
|
67
|
+
|
|
68
|
+
```svelte
|
|
69
|
+
<Button variant="primary|secondary|ghost|outline|danger|success" size="xs|sm|md|lg|xl">
|
|
70
|
+
Click me
|
|
71
|
+
</Button>
|
|
72
|
+
<Button href="/about">Link Button</Button>
|
|
73
|
+
<Button loading>Loading...</Button>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Input
|
|
77
|
+
|
|
78
|
+
```svelte
|
|
79
|
+
<Input bind:value={text} placeholder="Enter text" />
|
|
80
|
+
<Input type="password" disableBrowserFeatures />
|
|
81
|
+
<Input error={!isValid} />
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Select
|
|
85
|
+
|
|
86
|
+
```svelte
|
|
87
|
+
<Select
|
|
88
|
+
options={[{ value: '1', label: 'Option 1' }]}
|
|
89
|
+
bind:value={selected}
|
|
90
|
+
placeholder="Choose..."
|
|
91
|
+
/>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Badge
|
|
95
|
+
|
|
96
|
+
```svelte
|
|
97
|
+
<Badge variant="default|primary|secondary|success|warning|error|info">
|
|
98
|
+
Status
|
|
99
|
+
</Badge>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Checkbox & Switch
|
|
103
|
+
|
|
104
|
+
```svelte
|
|
105
|
+
<Checkbox bind:checked={agreed}>I agree</Checkbox>
|
|
106
|
+
<Switch bind:checked={enabled}>Enable notifications</Switch>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Toast
|
|
110
|
+
|
|
111
|
+
```svelte
|
|
112
|
+
<!-- Root layout -->
|
|
113
|
+
<script>
|
|
114
|
+
import { Toast, createToastContext } from '@miozu/jera';
|
|
115
|
+
createToastContext();
|
|
116
|
+
</script>
|
|
117
|
+
<Toast />
|
|
118
|
+
|
|
119
|
+
<!-- Any component -->
|
|
120
|
+
<script>
|
|
121
|
+
import { getToastContext } from '@miozu/jera';
|
|
122
|
+
const toast = getToastContext();
|
|
123
|
+
toast.success('Saved!');
|
|
124
|
+
</script>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Utilities
|
|
128
|
+
|
|
129
|
+
### cn() — Class concatenation
|
|
130
|
+
|
|
131
|
+
```javascript
|
|
132
|
+
import { cn } from '@miozu/jera';
|
|
133
|
+
|
|
134
|
+
cn('base', condition && 'conditional', ['array', 'classes']);
|
|
135
|
+
// => "base conditional array classes"
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### cv() — Class variants
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
import { cv } from '@miozu/jera';
|
|
142
|
+
|
|
143
|
+
const button = cv({
|
|
144
|
+
base: 'inline-flex items-center',
|
|
145
|
+
variants: {
|
|
146
|
+
variant: { primary: 'bg-primary', secondary: 'bg-surface' },
|
|
147
|
+
size: { sm: 'h-8', md: 'h-10' }
|
|
148
|
+
},
|
|
149
|
+
defaults: { variant: 'primary', size: 'md' }
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
button({ variant: 'secondary' }); // => "inline-flex items-center bg-surface h-10"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Actions
|
|
156
|
+
|
|
157
|
+
```svelte
|
|
158
|
+
<script>
|
|
159
|
+
import { clickOutside, focusTrap, escapeKey, portal } from '@miozu/jera/actions';
|
|
160
|
+
</script>
|
|
161
|
+
|
|
162
|
+
<div use:clickOutside={() => close()}>
|
|
163
|
+
<dialog use:focusTrap={{ enabled: isOpen }}>
|
|
164
|
+
<div use:escapeKey={() => close()}>
|
|
165
|
+
Content
|
|
166
|
+
</div>
|
|
167
|
+
</dialog>
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
<div use:portal={'body'}>Renders at body level</div>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Theming
|
|
174
|
+
|
|
175
|
+
Dark theme is default. Switch themes with `data-theme` attribute:
|
|
176
|
+
|
|
177
|
+
```html
|
|
178
|
+
<html data-theme="dark"> <!-- Dark (default) -->
|
|
179
|
+
<html data-theme="light"> <!-- Light -->
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Or use ThemeState:
|
|
183
|
+
|
|
184
|
+
```javascript
|
|
185
|
+
import { createThemeContext } from '@miozu/jera';
|
|
186
|
+
|
|
187
|
+
const theme = createThemeContext();
|
|
188
|
+
theme.init(); // Reads from localStorage/system preference
|
|
189
|
+
theme.toggle(); // Switch between light/dark
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## AI-First Design
|
|
193
|
+
|
|
194
|
+
This library includes AI-optimized documentation:
|
|
195
|
+
|
|
196
|
+
- `llms.txt` — Structured index for LLM consumption
|
|
197
|
+
- `CLAUDE.md` — Detailed context for AI assistants
|
|
198
|
+
- JSDoc comments throughout the codebase
|
|
199
|
+
|
|
200
|
+
## Browser Support
|
|
201
|
+
|
|
202
|
+
- Chrome 88+
|
|
203
|
+
- Firefox 78+
|
|
204
|
+
- Safari 14+
|
|
205
|
+
- Edge 88+
|
|
206
|
+
|
|
207
|
+
Requires Svelte 5.0+
|
|
208
|
+
|
|
209
|
+
## License
|
|
210
|
+
|
|
211
|
+
MIT © [Nicholas Glazer](https://miozu.com)
|
package/llms.txt
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# @miozu/jera
|
|
2
|
+
|
|
3
|
+
> A minimal, reactive component library for Svelte 5. Provides design tokens (Miozu Base16 color system), utility functions (cn, cv), Svelte actions, and accessible components. Designed for AI-assisted development with clear, predictable APIs.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
- [Installation](#installation): `pnpm add @miozu/jera`
|
|
7
|
+
- [Basic Usage](#usage): Import components and tokens
|
|
8
|
+
|
|
9
|
+
## Design Tokens
|
|
10
|
+
- [Colors](/src/tokens/colors.css): Miozu Base16 palette with dark/light themes
|
|
11
|
+
- [Spacing](/src/tokens/spacing.css): 4px-based spacing scale
|
|
12
|
+
- [Typography](/src/tokens/typography.css): Font scales and weights
|
|
13
|
+
- [Effects](/src/tokens/effects.css): Shadows, radius, transitions, z-index
|
|
14
|
+
|
|
15
|
+
## Utilities
|
|
16
|
+
- [cn()](/src/utils/cn.svelte.js): Class name concatenation with falsy filtering
|
|
17
|
+
- [cv()](/src/utils/cn.svelte.js): Class variants for type-safe variant composition
|
|
18
|
+
- [ThemeState](/src/utils/reactive.svelte.js): SSR-safe theme management
|
|
19
|
+
|
|
20
|
+
## Components
|
|
21
|
+
- [Button](/src/components/primitives/Button.svelte): Polymorphic button with 6 variants, 5 sizes
|
|
22
|
+
- [Input](/src/components/forms/Input.svelte): Text input with validation support
|
|
23
|
+
- [Select](/src/components/forms/Select.svelte): Accessible dropdown with keyboard navigation
|
|
24
|
+
- [Badge](/src/components/primitives/Badge.svelte): Status tags with semantic variants
|
|
25
|
+
- [Checkbox](/src/components/forms/Checkbox.svelte): Checkbox input
|
|
26
|
+
- [Switch](/src/components/forms/Switch.svelte): Toggle switch
|
|
27
|
+
- [Toast](/src/components/feedback/Toast.svelte): Notification system
|
|
28
|
+
|
|
29
|
+
## Actions
|
|
30
|
+
- [clickOutside](/src/actions/index.js): Detect clicks outside element
|
|
31
|
+
- [focusTrap](/src/actions/index.js): Trap focus within element
|
|
32
|
+
- [portal](/src/actions/index.js): Render element in different DOM location
|
|
33
|
+
- [escapeKey](/src/actions/index.js): Handle escape key press
|
|
34
|
+
|
|
35
|
+
## API Patterns
|
|
36
|
+
|
|
37
|
+
### Component Props Pattern
|
|
38
|
+
All components accept these common props:
|
|
39
|
+
- `class`: Additional CSS classes (always merged)
|
|
40
|
+
- `disabled`: Boolean to disable interaction
|
|
41
|
+
- Event handlers: `onclick`, `onchange`, etc.
|
|
42
|
+
|
|
43
|
+
### Class Variants Pattern
|
|
44
|
+
```javascript
|
|
45
|
+
import { cv } from '@miozu/jera';
|
|
46
|
+
|
|
47
|
+
const button = cv({
|
|
48
|
+
base: 'inline-flex items-center',
|
|
49
|
+
variants: {
|
|
50
|
+
variant: { primary: 'bg-primary', secondary: 'bg-surface' },
|
|
51
|
+
size: { sm: 'h-8 px-3', md: 'h-10 px-4' }
|
|
52
|
+
},
|
|
53
|
+
defaults: { variant: 'primary', size: 'md' }
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Usage: button({ variant: 'secondary', size: 'sm' })
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Theme Usage Pattern
|
|
60
|
+
```javascript
|
|
61
|
+
import { ThemeState, createThemeContext } from '@miozu/jera';
|
|
62
|
+
const theme = createThemeContext(); // In root layout
|
|
63
|
+
theme.toggle(); // Switch between light/dark
|
|
64
|
+
```
|