@miozu/jera 0.4.4 → 0.5.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.
@@ -0,0 +1,657 @@
1
+ # @miozu/jera - Architecture Documentation
2
+
3
+ **Last Updated:** February 2026
4
+ **Version:** 0.5.0+
5
+
6
+ ---
7
+
8
+ ## Overview
9
+
10
+ jera is a **zero-dependency, AI-first component library** for Svelte 5. It follows a layered architecture that separates design tokens, CSS, and framework-specific code.
11
+
12
+ ### Design Philosophy
13
+
14
+ 1. **CSS is the library** - Pure modern CSS is 100% portable
15
+ 2. **Framework wrappers are thin** - Svelte components are ~50-100 lines
16
+ 3. **Zero dependencies** - No external runtime dependencies
17
+ 4. **AI-first documentation** - llms.txt standard for AI assistants
18
+ 5. **Browser-native** - Modern CSS features over JS polyfills
19
+
20
+ ---
21
+
22
+ ## Architecture Layers
23
+
24
+ ```
25
+ ┌─────────────────────────────────────────────────────────────────┐
26
+ │ Layer 5: AI Documentation │
27
+ │ llms.txt, llms-full.txt, CLAUDE.md │
28
+ └─────────────────────────────────────────────────────────────────┘
29
+
30
+ ┌─────────────────────────────────────────────────────────────────┐
31
+ │ Layer 4: Framework Wrappers │
32
+ │ Svelte 5 Components │
33
+ │ (Native runes: $state, $derived, $props) │
34
+ └─────────────────────────────────────────────────────────────────┘
35
+
36
+ ┌─────────────────────────────────────────────────────────────────┐
37
+ │ Layer 3: Pure CSS │
38
+ │ Component styles using CSS custom properties │
39
+ │ (nesting, :has(), container queries, color-mix()) │
40
+ └─────────────────────────────────────────────────────────────────┘
41
+
42
+ ┌─────────────────────────────────────────────────────────────────┐
43
+ │ Layer 2: CSS Custom Properties │
44
+ │ Generated from tokens.json │
45
+ │ (--color-*, --space-*, --font-*) │
46
+ └─────────────────────────────────────────────────────────────────┘
47
+
48
+ ┌─────────────────────────────────────────────────────────────────┐
49
+ │ Layer 1: W3C Design Tokens (DTCG) │
50
+ │ tokens.json │
51
+ │ (Single source of truth for all values) │
52
+ └─────────────────────────────────────────────────────────────────┘
53
+ ```
54
+
55
+ ---
56
+
57
+ ## Layer 1: W3C Design Tokens (DTCG)
58
+
59
+ Design tokens are defined following the [W3C Design Tokens Community Group](https://design-tokens.github.io/community-group/format/) specification (stable October 2025).
60
+
61
+ ### Token Structure
62
+
63
+ ```json
64
+ {
65
+ "$type": "color",
66
+ "color": {
67
+ "base": {
68
+ "00": { "$value": "#0f1419", "$description": "Primary background (dark)" },
69
+ "01": { "$value": "#1a1f26", "$description": "Surface/card" },
70
+ "0D": { "$value": "#61afef", "$description": "Primary/blue" }
71
+ },
72
+ "semantic": {
73
+ "bg": { "$value": "{color.base.00}" },
74
+ "surface": { "$value": "{color.base.01}" },
75
+ "primary": { "$value": "{color.base.0D}" }
76
+ }
77
+ },
78
+ "space": {
79
+ "1": { "$value": "4px", "$type": "dimension" },
80
+ "2": { "$value": "8px", "$type": "dimension" },
81
+ "4": { "$value": "16px", "$type": "dimension" }
82
+ }
83
+ }
84
+ ```
85
+
86
+ ### Base16 Color System (CRITICAL)
87
+
88
+ jera uses the **Miozu Base16 color system** as its foundation. All 16 colors follow the standard Base16 naming convention.
89
+
90
+ **Token Naming (hex digits, NOT decimal):**
91
+ ```
92
+ base00, base01, base02, base03, base04, base05, base06, base07
93
+ base08, base09, base0A, base0B, base0C, base0D, base0E, base0F
94
+ ```
95
+
96
+ **NEVER use:** `base0`, `base1`, `base10`, `base15` (these are incorrect)
97
+
98
+ **Color Responsibilities:**
99
+
100
+ | Token | Dark Theme | Light Theme | Usage |
101
+ |-------|------------|-------------|-------|
102
+ | base00 | #0f1419 | #ffffff | Primary background |
103
+ | base01 | #1a1f26 | #f8f9fa | Surface/card |
104
+ | base02 | #242a33 | #f1f3f5 | Selection/hover |
105
+ | base03 | #4a5568 | #adb5bd | Muted/disabled |
106
+ | base04 | #a0aec0 | #6c757d | Secondary text |
107
+ | base05 | #e2e8f0 | #212529 | Primary text |
108
+ | base06 | #f7fafc | #1a1d20 | High emphasis |
109
+ | base07 | #ffffff | #0d0f10 | Maximum contrast |
110
+ | base08 | #e06c75 | #dc3545 | Error/red |
111
+ | base09 | #d19a66 | #fd7e14 | Warning/orange |
112
+ | base0A | #e5c07b | #ffc107 | Highlight/yellow |
113
+ | base0B | #98c379 | #28a745 | Success/green |
114
+ | base0C | #56b6c2 | #17a2b8 | Info/cyan |
115
+ | base0D | #61afef | #007bff | Primary/blue |
116
+ | base0E | #c678dd | #6f42c1 | Accent/purple |
117
+ | base0F | #be5046 | #e83e8c | Secondary accent |
118
+
119
+ ### Token Build Process
120
+
121
+ ```bash
122
+ # tokens.json → CSS custom properties
123
+ node tokens/build.js
124
+ ```
125
+
126
+ The build script generates `tokens.css` with all CSS custom properties:
127
+
128
+ ```css
129
+ :root {
130
+ --color-base00: #0f1419;
131
+ --color-base01: #1a1f26;
132
+ /* ... */
133
+ --space-1: 4px;
134
+ --space-2: 8px;
135
+ }
136
+
137
+ [data-theme='miozu-light'] {
138
+ --color-base00: #ffffff;
139
+ --color-base01: #f8f9fa;
140
+ /* ... grayscale inverts, accents adjust */
141
+ }
142
+ ```
143
+
144
+ ---
145
+
146
+ ## Layer 2: CSS Custom Properties
147
+
148
+ Generated CSS custom properties serve as the interface between tokens and component styles.
149
+
150
+ ### Naming Conventions
151
+
152
+ | Prefix | Purpose | Example |
153
+ |--------|---------|---------|
154
+ | `--color-` | Colors | `--color-base0D`, `--color-primary` |
155
+ | `--space-` | Spacing | `--space-1`, `--space-4` |
156
+ | `--font-` | Typography | `--font-size-sm`, `--font-weight-bold` |
157
+ | `--radius-` | Border radius | `--radius-sm`, `--radius-full` |
158
+ | `--shadow-` | Box shadows | `--shadow-sm`, `--shadow-lg` |
159
+ | `--z-` | Z-index layers | `--z-modal`, `--z-toast` |
160
+
161
+ ### Semantic Color Aliases
162
+
163
+ Components use semantic aliases that map to Base16:
164
+
165
+ ```css
166
+ /* In tokens.css */
167
+ :root {
168
+ --color-bg: var(--color-base00);
169
+ --color-surface: var(--color-base01);
170
+ --color-surface-alt: var(--color-base02);
171
+ --color-text: var(--color-base05);
172
+ --color-text-strong: var(--color-base07);
173
+ --color-text-muted: var(--color-base04);
174
+ --color-primary: var(--color-base0D);
175
+ --color-success: var(--color-base0B);
176
+ --color-warning: var(--color-base0A);
177
+ --color-error: var(--color-base08);
178
+ --color-info: var(--color-base0C);
179
+ }
180
+ ```
181
+
182
+ ---
183
+
184
+ ## Layer 3: Pure CSS Components
185
+
186
+ Component styles use modern CSS features and are framework-agnostic.
187
+
188
+ ### Modern CSS Features Used
189
+
190
+ | Feature | Usage | Browser Support |
191
+ |---------|-------|-----------------|
192
+ | CSS Nesting | Scoped styles | 2024+ |
193
+ | `:has()` | Parent selection | 2024+ |
194
+ | Container Queries | Responsive components | 2023+ |
195
+ | `color-mix()` | Dynamic color variants | 2023+ |
196
+ | CSS Layers | Specificity management | 2023+ |
197
+ | Custom Properties | Design tokens | 2017+ |
198
+
199
+ ### CSS Component Pattern
200
+
201
+ ```css
202
+ /* button.css - Pure CSS, no framework */
203
+
204
+ .jera-button {
205
+ /* Base styles using tokens */
206
+ display: inline-flex;
207
+ align-items: center;
208
+ justify-content: center;
209
+ gap: var(--space-2);
210
+ padding: var(--space-2) var(--space-4);
211
+ font-size: var(--font-size-sm);
212
+ font-weight: var(--font-weight-medium);
213
+ border-radius: var(--radius-md);
214
+ transition: all 150ms ease;
215
+ cursor: pointer;
216
+ border: none;
217
+
218
+ /* Variants using CSS nesting */
219
+ &[data-variant="primary"] {
220
+ background: var(--color-primary);
221
+ color: var(--color-base07);
222
+
223
+ &:hover {
224
+ background: color-mix(in srgb, var(--color-primary) 85%, black);
225
+ }
226
+ }
227
+
228
+ &[data-variant="secondary"] {
229
+ background: var(--color-surface);
230
+ color: var(--color-text);
231
+ border: 1px solid var(--color-base03);
232
+ }
233
+
234
+ /* Size variants */
235
+ &[data-size="sm"] {
236
+ padding: var(--space-1) var(--space-2);
237
+ font-size: var(--font-size-xs);
238
+ }
239
+
240
+ &[data-size="lg"] {
241
+ padding: var(--space-3) var(--space-6);
242
+ font-size: var(--font-size-base);
243
+ }
244
+
245
+ /* State variants */
246
+ &:disabled {
247
+ opacity: 0.5;
248
+ cursor: not-allowed;
249
+ }
250
+
251
+ &[data-loading] {
252
+ position: relative;
253
+ color: transparent;
254
+ }
255
+ }
256
+ ```
257
+
258
+ ### Container Queries for Responsive Components
259
+
260
+ ```css
261
+ .jera-sidebar {
262
+ container-type: inline-size;
263
+ container-name: sidebar;
264
+ }
265
+
266
+ @container sidebar (max-width: 200px) {
267
+ .jera-sidebar-label {
268
+ display: none;
269
+ }
270
+
271
+ .jera-sidebar-icon {
272
+ margin: 0 auto;
273
+ }
274
+ }
275
+ ```
276
+
277
+ ---
278
+
279
+ ## Layer 4: Svelte 5 Framework Wrapper
280
+
281
+ Svelte components are thin wrappers around the CSS. They handle:
282
+ - Props and reactivity via native runes
283
+ - Event handling
284
+ - Accessibility attributes
285
+ - State management
286
+
287
+ ### Svelte 5 Runes (Required)
288
+
289
+ | Rune | Usage |
290
+ |------|-------|
291
+ | `$props()` | Declare component props (single call only) |
292
+ | `$state()` | Reactive local state |
293
+ | `$derived()` | Computed values |
294
+ | `$bindable()` | Two-way binding props |
295
+ | `$effect()` | Side effects (use sparingly) |
296
+
297
+ ### Component Template
298
+
299
+ ```svelte
300
+ <!-- Button.svelte -->
301
+ <script>
302
+ let {
303
+ variant = 'primary',
304
+ size = 'md',
305
+ disabled = false,
306
+ loading = false,
307
+ href = null,
308
+ class: className = '',
309
+ children,
310
+ onclick,
311
+ ...rest
312
+ } = $props();
313
+
314
+ // Derived class string
315
+ const classes = $derived(
316
+ ['jera-button', className].filter(Boolean).join(' ')
317
+ );
318
+ </script>
319
+
320
+ {#if href && !disabled}
321
+ <a
322
+ {href}
323
+ class={classes}
324
+ data-variant={variant}
325
+ data-size={size}
326
+ data-loading={loading || undefined}
327
+ {...rest}
328
+ >
329
+ {@render children?.()}
330
+ </a>
331
+ {:else}
332
+ <button
333
+ type="button"
334
+ class={classes}
335
+ data-variant={variant}
336
+ data-size={size}
337
+ data-loading={loading || undefined}
338
+ {disabled}
339
+ {onclick}
340
+ {...rest}
341
+ >
342
+ {@render children?.()}
343
+ </button>
344
+ {/if}
345
+ ```
346
+
347
+ ### Reactive Singleton Pattern (ThemeState)
348
+
349
+ For global state like theme management, jera uses a **reactive singleton class pattern**:
350
+
351
+ ```javascript
352
+ // reactive.svelte.js
353
+
354
+ export class ThemeState {
355
+ // Reactive state using runes
356
+ current = $state('system');
357
+
358
+ // Derived values
359
+ resolved = $derived.by(() => this.#resolveTheme());
360
+ dataTheme = $derived.by(() =>
361
+ this.resolved === 'dark' ? 'miozu-dark' : 'miozu-light'
362
+ );
363
+ isDark = $derived.by(() => this.resolved === 'dark');
364
+ isLight = $derived.by(() => this.resolved === 'light');
365
+
366
+ // Private state
367
+ #initialized = false;
368
+ #mediaQuery = null;
369
+
370
+ constructor(initial = 'system') {
371
+ this.current = initial;
372
+ }
373
+
374
+ #resolveTheme() {
375
+ if (this.current === 'system') {
376
+ if (typeof window === 'undefined') return 'dark';
377
+ return this.#mediaQuery?.matches ? 'dark' : 'light';
378
+ }
379
+ return this.current;
380
+ }
381
+
382
+ init() { /* ... */ }
383
+ sync() { /* ... */ }
384
+ toggle() { /* ... */ }
385
+ set(theme) { /* ... */ }
386
+ }
387
+
388
+ // Singleton instance
389
+ let themeInstance = null;
390
+
391
+ export function getTheme(initial = 'system') {
392
+ if (!themeInstance) {
393
+ themeInstance = new ThemeState(initial);
394
+ }
395
+ return themeInstance;
396
+ }
397
+ ```
398
+
399
+ **Usage in SvelteKit:**
400
+
401
+ ```svelte
402
+ <!-- +layout.svelte (root) -->
403
+ <script>
404
+ import { getTheme } from '@miozu/jera';
405
+ import { onMount } from 'svelte';
406
+
407
+ // Get singleton once
408
+ const themeState = getTheme();
409
+
410
+ onMount(() => {
411
+ themeState.sync(); // Hydrate from DOM
412
+ themeState.init(); // Setup listeners
413
+ });
414
+ </script>
415
+
416
+ <!-- Pass to children as props -->
417
+ <Sidebar {themeState} />
418
+ ```
419
+
420
+ ```svelte
421
+ <!-- Child component -->
422
+ <script>
423
+ // Receive as prop, DON'T call getTheme()
424
+ let { themeState } = $props();
425
+
426
+ function handleToggle() {
427
+ themeState.toggle();
428
+ }
429
+
430
+ // Reactive access
431
+ let isDark = $derived(themeState.isDark);
432
+ </script>
433
+ ```
434
+
435
+ ---
436
+
437
+ ## Layer 5: AI Documentation
438
+
439
+ ### llms.txt Standard
440
+
441
+ Following the [llms.txt specification](https://llmstxt.org/), jera provides:
442
+
443
+ | File | Purpose |
444
+ |------|---------|
445
+ | `llms.txt` | Quick reference index |
446
+ | `llms-full.txt` | Complete documentation |
447
+ | `CLAUDE.md` | Detailed AI context |
448
+
449
+ ### llms.txt Structure
450
+
451
+ ```markdown
452
+ # @miozu/jera
453
+
454
+ > Description of the library
455
+
456
+ ## Quick Start
457
+ - Installation
458
+ - Basic usage
459
+
460
+ ## Components
461
+ - [Button](/src/components/Button.svelte): Description
462
+ - [Input](/src/components/Input.svelte): Description
463
+
464
+ ## Patterns
465
+ - Theme management
466
+ - Class variants
467
+ ```
468
+
469
+ ---
470
+
471
+ ## Directory Structure
472
+
473
+ ```
474
+ jera/
475
+ ├── tokens/
476
+ │ ├── tokens.json # W3C DTCG source of truth
477
+ │ ├── tokens.dark.json # Dark theme overrides
478
+ │ ├── tokens.light.json # Light theme overrides
479
+ │ └── build.js # Token → CSS generator
480
+ ├── src/
481
+ │ ├── tokens/ # Generated CSS
482
+ │ │ ├── index.css # All tokens bundled
483
+ │ │ ├── colors.css # Base16 palette
484
+ │ │ ├── spacing.css # 4px-based scale
485
+ │ │ ├── typography.css # Font system
486
+ │ │ └── effects.css # Shadows, radius, transitions
487
+ │ ├── css/ # Pure CSS components (future)
488
+ │ │ ├── button.css
489
+ │ │ ├── input.css
490
+ │ │ └── modal.css
491
+ │ ├── components/
492
+ │ │ ├── primitives/ # Button, Badge, Avatar, etc.
493
+ │ │ ├── forms/ # Input, Select, Checkbox, etc.
494
+ │ │ ├── feedback/ # Toast, Spinner, Alert, etc.
495
+ │ │ ├── overlays/ # Modal, Popover, Dropdown
496
+ │ │ ├── navigation/ # Tabs, Sidebar, Accordion
497
+ │ │ └── docs/ # CodeBlock, PropsTable
498
+ │ ├── utils/
499
+ │ │ ├── cn.svelte.js # Class utilities (cn, cv)
500
+ │ │ ├── reactive.svelte.js # ThemeState, createReactive
501
+ │ │ └── sidebar.svelte.js # Sidebar state management
502
+ │ ├── actions/
503
+ │ │ └── index.js # Svelte actions
504
+ │ └── index.js # Main exports
505
+ ├── llms.txt # AI quick reference
506
+ ├── llms-full.txt # AI complete docs
507
+ ├── CLAUDE.md # AI detailed context
508
+ ├── ARCHITECTURE.md # This file
509
+ └── package.json
510
+ ```
511
+
512
+ ---
513
+
514
+ ## Theming
515
+
516
+ ### Theme Attribute
517
+
518
+ Themes are applied via `data-theme` attribute on `<html>`:
519
+
520
+ ```html
521
+ <html data-theme="miozu-dark">
522
+ ```
523
+
524
+ Supported values:
525
+ - `miozu-dark` - Dark theme (default)
526
+ - `miozu-light` - Light theme
527
+
528
+ ### Theme-Agnostic Components
529
+
530
+ Components **NEVER** check theme directly. They use semantic tokens:
531
+
532
+ ```svelte
533
+ <!-- CORRECT: Works in both themes -->
534
+ <div class="bg-base00 text-base05 border-base03">
535
+ <h1 class="text-base07">Title</h1>
536
+ <button class="bg-base0D text-base07">Action</button>
537
+ </div>
538
+
539
+ <!-- WRONG: Don't use theme-specific logic in components -->
540
+ {#if theme.isDark}
541
+ <div class="dark-styles">...</div>
542
+ {/if}
543
+ ```
544
+
545
+ ### CSS Theme Switching
546
+
547
+ Theme switching is handled entirely via CSS custom property values:
548
+
549
+ ```css
550
+ /* Dark theme (default) */
551
+ :root {
552
+ --color-base00: #0f1419;
553
+ --color-base05: #e2e8f0;
554
+ }
555
+
556
+ /* Light theme - only values change, not usage */
557
+ [data-theme='miozu-light'] {
558
+ --color-base00: #ffffff;
559
+ --color-base05: #212529;
560
+ }
561
+ ```
562
+
563
+ ---
564
+
565
+ ## Integration with Tailwind (Consumer Apps)
566
+
567
+ While jera itself uses pure CSS, consumer apps can use Tailwind alongside jera:
568
+
569
+ ### Tailwind 4 Configuration
570
+
571
+ ```css
572
+ /* app.css in consumer app */
573
+ @import '@miozu/jera/tokens';
574
+
575
+ @theme {
576
+ /* Extend Tailwind with jera tokens */
577
+ --color-base00: var(--color-base00);
578
+ --color-base01: var(--color-base01);
579
+ /* ... */
580
+ }
581
+ ```
582
+
583
+ ### Using jera Components with Tailwind
584
+
585
+ ```svelte
586
+ <script>
587
+ import { Button } from '@miozu/jera';
588
+ </script>
589
+
590
+ <!-- jera component with Tailwind utility classes -->
591
+ <Button variant="primary" class="mt-4 shadow-lg">
592
+ Click me
593
+ </Button>
594
+ ```
595
+
596
+ ---
597
+
598
+ ## Performance Considerations
599
+
600
+ 1. **CSS-first** - Styles load before JS, preventing FOUC
601
+ 2. **Tree-shakeable** - Import only what you use
602
+ 3. **No runtime CSS-in-JS** - Zero overhead
603
+ 4. **Lazy loading** - Components can be dynamically imported
604
+ 5. **SSR-safe** - All components work with SvelteKit SSR
605
+
606
+ ---
607
+
608
+ ## Browser Support
609
+
610
+ jera targets modern browsers (2024+):
611
+
612
+ | Browser | Minimum Version |
613
+ |---------|-----------------|
614
+ | Chrome | 120+ |
615
+ | Firefox | 118+ |
616
+ | Safari | 17+ |
617
+ | Edge | 120+ |
618
+
619
+ For older browser support, consider using CSS polyfills or fallbacks.
620
+
621
+ ---
622
+
623
+ ## Migration Guide
624
+
625
+ ### From Tailwind-Only Components
626
+
627
+ 1. Extract design tokens to `tokens.json`
628
+ 2. Generate CSS custom properties
629
+ 3. Replace Tailwind classes with semantic tokens
630
+ 4. Keep Tailwind for utility classes in consumer apps
631
+
632
+ ### From CSS-in-JS
633
+
634
+ 1. Extract styles to pure CSS files
635
+ 2. Use CSS custom properties for theming
636
+ 3. Replace dynamic style props with data attributes
637
+
638
+ ---
639
+
640
+ ## Rules for AI Assistants
641
+
642
+ 1. **Use Svelte 5 runes** - No legacy `$:`, `export let`, stores
643
+ 2. **Single $props() call** - Destructure all props in one call
644
+ 3. **Use cv() for variants** - Don't hardcode conditional classes
645
+ 4. **Semantic colors** - Use `--color-*` tokens, not raw hex values
646
+ 5. **Pure JavaScript** - No TypeScript
647
+ 6. **Zero dependencies** - Don't add external packages
648
+ 7. **Theme-agnostic** - Components work in both themes automatically
649
+ 8. **Accessibility first** - Include ARIA attributes, keyboard support
650
+
651
+ ---
652
+
653
+ ## Related Documentation
654
+
655
+ - [CLAUDE.md](./CLAUDE.md) - AI context file with API reference
656
+ - [llms.txt](./llms.txt) - Quick AI reference
657
+ - [README.md](./README.md) - Getting started guide
package/CLAUDE.md CHANGED
@@ -1,8 +1,31 @@
1
1
  # @miozu/jera - AI Context File
2
2
 
3
3
  **Package:** @miozu/jera
4
- **Purpose:** Minimal, reactive component library for Svelte 5
4
+ **Purpose:** Zero-dependency, AI-first component library for Svelte 5
5
5
  **Author:** Nicholas Glazer <glazer.nicholas@gmail.com>
6
+ **Last Updated:** February 2026
7
+
8
+ ---
9
+
10
+ ## Architecture Overview
11
+
12
+ jera follows a **5-layer architecture** designed for portability and AI-assisted development:
13
+
14
+ 1. **W3C Design Tokens (DTCG)** - `tokens.json` as single source of truth
15
+ 2. **CSS Custom Properties** - Generated from tokens, used by all styles
16
+ 3. **Pure Modern CSS** - Framework-agnostic component styles
17
+ 4. **Svelte 5 Wrappers** - Thin components using native runes
18
+ 5. **AI Documentation** - llms.txt standard for AI assistants
19
+
20
+ **Full architecture documentation:** [ARCHITECTURE.md](./ARCHITECTURE.md)
21
+
22
+ ### Key Design Principles
23
+
24
+ - **CSS is the library** - Pure CSS is 100% portable across frameworks
25
+ - **Zero dependencies** - No runtime external dependencies
26
+ - **Browser-native** - Modern CSS features over JS polyfills
27
+ - **Theme-agnostic** - Components work in both themes automatically
28
+ - **AI-first** - Documentation optimized for AI assistants
6
29
 
7
30
  ---
8
31