@almadar/ui 6.5.0 → 6.7.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 (48) hide show
  1. package/dist/{EntityBindingContext-0Evn_LcT.d.cts → EntityBindingContext-Bn3ePJQC.d.cts} +1 -1
  2. package/dist/{EntityBindingContext-0Evn_LcT.d.ts → EntityBindingContext-Bn3ePJQC.d.ts} +1 -1
  3. package/dist/{GameAudioProvider-B48iXwz3.d.ts → GameAudioProvider-Ctceau1s.d.ts} +6 -18
  4. package/dist/{GameAudioProvider-CQAdPreB.d.cts → GameAudioProvider-Dk_Y3LN3.d.cts} +6 -18
  5. package/dist/avl/index.cjs +1268 -220
  6. package/dist/avl/index.js +1269 -221
  7. package/dist/{avl-schema-parser-dvJKXn-o.d.ts → avl-schema-parser-DncJLEn9.d.ts} +18 -0
  8. package/dist/{avl-schema-parser-Bt4NzAam.d.cts → avl-schema-parser-FQ1474v8.d.cts} +18 -0
  9. package/dist/{cn-DJSBBm5W.d.cts → cn-CCjFspAo.d.cts} +25 -1
  10. package/dist/{cn-BbhJq_nr.d.ts → cn-sgpqpN0U.d.ts} +25 -1
  11. package/dist/components/index.cjs +1092 -249
  12. package/dist/components/index.d.cts +193 -23
  13. package/dist/components/index.d.ts +193 -23
  14. package/dist/components/index.js +1089 -250
  15. package/dist/context/index.cjs +10 -13
  16. package/dist/context/index.js +10 -13
  17. package/dist/hooks/index.cjs +63 -13
  18. package/dist/hooks/index.d.cts +2 -2
  19. package/dist/hooks/index.d.ts +2 -2
  20. package/dist/hooks/index.js +64 -15
  21. package/dist/lib/drawable/three/index.cjs +126 -13
  22. package/dist/lib/drawable/three/index.d.cts +2 -2
  23. package/dist/lib/drawable/three/index.d.ts +2 -2
  24. package/dist/lib/drawable/three/index.js +127 -14
  25. package/dist/lib/index.cjs +180 -0
  26. package/dist/lib/index.d.cts +1 -1
  27. package/dist/lib/index.d.ts +1 -1
  28. package/dist/lib/index.js +178 -1
  29. package/dist/locales/index.cjs +6 -3
  30. package/dist/locales/index.js +6 -3
  31. package/dist/providers/index.cjs +974 -202
  32. package/dist/providers/index.d.cts +2 -2
  33. package/dist/providers/index.d.ts +2 -2
  34. package/dist/providers/index.js +974 -202
  35. package/dist/runtime/index.cjs +1594 -196
  36. package/dist/runtime/index.d.cts +79 -6
  37. package/dist/runtime/index.d.ts +79 -6
  38. package/dist/runtime/index.js +1592 -198
  39. package/dist/slot-host-Czc2JAxo.d.cts +47 -0
  40. package/dist/slot-host-Czc2JAxo.d.ts +47 -0
  41. package/dist/{useEventBus-CQWyAWpK.d.ts → useKeyboardRouter-D84cNWmA.d.ts} +31 -1
  42. package/dist/{useEventBus-Ckr4wqW3.d.cts → useKeyboardRouter-DQEE_9mq.d.cts} +31 -1
  43. package/locales/ar.json +2 -1
  44. package/locales/en.json +2 -1
  45. package/locales/sl.json +2 -1
  46. package/package.json +4 -4
  47. package/themes/comic.css +437 -0
  48. package/themes/index.css +1 -0
@@ -0,0 +1,47 @@
1
+ import { UISlot } from '@almadar/core';
2
+
3
+ /**
4
+ * Slot-host manifest types (Studio V4 §14, Part I1/I2).
5
+ *
6
+ * A "slot-host" is any shell that lays out named regions and mounts each
7
+ * one through `UISlotComponent` — e.g. a persona workspace declaring
8
+ * `sidebar`/`main`/`drawer`/... A manifest is the declarative region → slot
9
+ * binding for such a host: which compiler `UISlot` each region mounts, and
10
+ * whether the host's own stock content is replaced by slot content or the
11
+ * two are shown together (`UISlotComponent`'s `mode` prop). Substrate
12
+ * (lives in `@almadar/ui`, re-exported by `@almadar/sdk`) so any host — the
13
+ * studio's three persona shells or an embedding app — describes its
14
+ * regions the same way.
15
+ *
16
+ * @packageDocumentation
17
+ */
18
+
19
+ /**
20
+ * One host region's binding to a compiler slot. Mirrors `UISlotComponent`'s
21
+ * `mode` prop exactly — a manifest is just that prop's value, keyed by
22
+ * region, declared as data instead of scattered across JSX call sites.
23
+ */
24
+ interface SlotHostRegion {
25
+ slot: UISlot;
26
+ mode: "replace" | "append";
27
+ }
28
+ /**
29
+ * A host's full region map, keyed by the host's own region ids (e.g. a
30
+ * persona shell's `activityBar`/`sidebar`/`main`/...). `RegionId` is left
31
+ * generic so each host supplies its own literal union.
32
+ */
33
+ interface SlotHostManifest<RegionId extends string = string> {
34
+ regions: Record<RegionId, SlotHostRegion>;
35
+ }
36
+ /**
37
+ * Guards the invariant a slot-host manifest depends on: each `UISlot` may
38
+ * be mounted by at most one region. Two regions sharing a slot would mount
39
+ * `UISlotComponent` twice for the same slot — the second mount clears/reads
40
+ * the same context entry the first one owns, so one of the two regions
41
+ * silently loses its content (or, for a portal slot, double-portals it).
42
+ * Throws naming the two offending regions so the host can find the
43
+ * conflict without re-deriving it from the manifest.
44
+ */
45
+ declare function assertUniqueSlotsPerHost<RegionId extends string>(manifest: SlotHostManifest<RegionId>): void;
46
+
47
+ export { type SlotHostManifest as S, type SlotHostRegion as a, assertUniqueSlotsPerHost as b };
@@ -0,0 +1,47 @@
1
+ import { UISlot } from '@almadar/core';
2
+
3
+ /**
4
+ * Slot-host manifest types (Studio V4 §14, Part I1/I2).
5
+ *
6
+ * A "slot-host" is any shell that lays out named regions and mounts each
7
+ * one through `UISlotComponent` — e.g. a persona workspace declaring
8
+ * `sidebar`/`main`/`drawer`/... A manifest is the declarative region → slot
9
+ * binding for such a host: which compiler `UISlot` each region mounts, and
10
+ * whether the host's own stock content is replaced by slot content or the
11
+ * two are shown together (`UISlotComponent`'s `mode` prop). Substrate
12
+ * (lives in `@almadar/ui`, re-exported by `@almadar/sdk`) so any host — the
13
+ * studio's three persona shells or an embedding app — describes its
14
+ * regions the same way.
15
+ *
16
+ * @packageDocumentation
17
+ */
18
+
19
+ /**
20
+ * One host region's binding to a compiler slot. Mirrors `UISlotComponent`'s
21
+ * `mode` prop exactly — a manifest is just that prop's value, keyed by
22
+ * region, declared as data instead of scattered across JSX call sites.
23
+ */
24
+ interface SlotHostRegion {
25
+ slot: UISlot;
26
+ mode: "replace" | "append";
27
+ }
28
+ /**
29
+ * A host's full region map, keyed by the host's own region ids (e.g. a
30
+ * persona shell's `activityBar`/`sidebar`/`main`/...). `RegionId` is left
31
+ * generic so each host supplies its own literal union.
32
+ */
33
+ interface SlotHostManifest<RegionId extends string = string> {
34
+ regions: Record<RegionId, SlotHostRegion>;
35
+ }
36
+ /**
37
+ * Guards the invariant a slot-host manifest depends on: each `UISlot` may
38
+ * be mounted by at most one region. Two regions sharing a slot would mount
39
+ * `UISlotComponent` twice for the same slot — the second mount clears/reads
40
+ * the same context entry the first one owns, so one of the two regions
41
+ * silently loses its content (or, for a portal slot, double-portals it).
42
+ * Throws naming the two offending regions so the host can find the
43
+ * conflict without re-deriving it from the manifest.
44
+ */
45
+ declare function assertUniqueSlotsPerHost<RegionId extends string>(manifest: SlotHostManifest<RegionId>): void;
46
+
47
+ export { type SlotHostManifest as S, type SlotHostRegion as a, assertUniqueSlotsPerHost as b };
@@ -62,4 +62,34 @@ declare function useEventListener(event: string, handler: BusEventListener): voi
62
62
  */
63
63
  declare function useEmitEvent(): (type: string, payload?: EventPayload, source?: BusEventSource) => void;
64
64
 
65
- export { useEventBus as a, useEventListener as b, useEmitEvent as u };
65
+ interface KeyCaptureEntry {
66
+ mode: string;
67
+ keys: ReadonlySet<string>;
68
+ }
69
+ /** keyed by editorId, or the literal 'shell' for chrome-level bindings */
70
+ type KeyCaptureTable = Readonly<Record<string, KeyCaptureEntry>>;
71
+ interface EditorKeyEvent {
72
+ editorId: string;
73
+ key: string;
74
+ code: string;
75
+ ctrl: boolean;
76
+ alt: boolean;
77
+ shift: boolean;
78
+ meta: boolean;
79
+ repeat: boolean;
80
+ }
81
+ interface UseKeyboardRouterOptions {
82
+ /** declared data; caller owns it; re-read on every keydown (kept in a ref) */
83
+ captureTable: KeyCaptureTable;
84
+ /** default 'EDITOR_FOCUS' -> subscribes UI:EDITOR_FOCUS {editorId} */
85
+ editorFocusEvent?: string;
86
+ /** default 'EDITOR_BLUR' */
87
+ editorBlurEvent?: string;
88
+ /** default 'KEY' -> emits UI:KEY */
89
+ keyEvent?: string;
90
+ /** default true */
91
+ enabled?: boolean;
92
+ }
93
+ declare function useKeyboardRouter(options: UseKeyboardRouterOptions): void;
94
+
95
+ export { type EditorKeyEvent as E, type KeyCaptureTable as K, type UseKeyboardRouterOptions as U, type KeyCaptureEntry as a, useEventBus as b, useEventListener as c, useKeyboardRouter as d, useEmitEvent as u };
@@ -62,4 +62,34 @@ declare function useEventListener(event: string, handler: BusEventListener): voi
62
62
  */
63
63
  declare function useEmitEvent(): (type: string, payload?: EventPayload, source?: BusEventSource) => void;
64
64
 
65
- export { useEventBus as a, useEventListener as b, useEmitEvent as u };
65
+ interface KeyCaptureEntry {
66
+ mode: string;
67
+ keys: ReadonlySet<string>;
68
+ }
69
+ /** keyed by editorId, or the literal 'shell' for chrome-level bindings */
70
+ type KeyCaptureTable = Readonly<Record<string, KeyCaptureEntry>>;
71
+ interface EditorKeyEvent {
72
+ editorId: string;
73
+ key: string;
74
+ code: string;
75
+ ctrl: boolean;
76
+ alt: boolean;
77
+ shift: boolean;
78
+ meta: boolean;
79
+ repeat: boolean;
80
+ }
81
+ interface UseKeyboardRouterOptions {
82
+ /** declared data; caller owns it; re-read on every keydown (kept in a ref) */
83
+ captureTable: KeyCaptureTable;
84
+ /** default 'EDITOR_FOCUS' -> subscribes UI:EDITOR_FOCUS {editorId} */
85
+ editorFocusEvent?: string;
86
+ /** default 'EDITOR_BLUR' */
87
+ editorBlurEvent?: string;
88
+ /** default 'KEY' -> emits UI:KEY */
89
+ keyEvent?: string;
90
+ /** default true */
91
+ enabled?: boolean;
92
+ }
93
+ declare function useKeyboardRouter(options: UseKeyboardRouterOptions): void;
94
+
95
+ export { type EditorKeyEvent as E, type KeyCaptureTable as K, type UseKeyboardRouterOptions as U, type KeyCaptureEntry as a, useEventBus as b, useEventListener as c, useKeyboardRouter as d, useEmitEvent as u };
package/locales/ar.json CHANGED
@@ -553,5 +553,6 @@
553
553
  "richTextEditor.image": "إدراج صورة",
554
554
  "richTextEditor.imagePrompt": "رابط الصورة",
555
555
  "richTextEditor.placeholder": "ابدأ الكتابة…",
556
- "form.imageUrlFallback": "…أو الصق رابط الصورة"
556
+ "form.imageUrlFallback": "…أو الصق رابط الصورة",
557
+ "mermaid.unrenderable": "تعذر عرض هذا المخطط. يظهر مصدره أدناه."
557
558
  }
package/locales/en.json CHANGED
@@ -553,5 +553,6 @@
553
553
  "richTextEditor.image": "Insert image",
554
554
  "richTextEditor.imagePrompt": "Image URL",
555
555
  "richTextEditor.placeholder": "Start writing…",
556
- "form.imageUrlFallback": "…or paste an image address"
556
+ "form.imageUrlFallback": "…or paste an image address",
557
+ "mermaid.unrenderable": "This diagram could not be displayed. Its source is shown below."
557
558
  }
package/locales/sl.json CHANGED
@@ -553,5 +553,6 @@
553
553
  "richTextEditor.image": "Vstavi sliko",
554
554
  "richTextEditor.imagePrompt": "URL slike",
555
555
  "richTextEditor.placeholder": "Začnite pisati …",
556
- "form.imageUrlFallback": "…ali prilepite naslov slike"
556
+ "form.imageUrlFallback": "…ali prilepite naslov slike",
557
+ "mermaid.unrenderable": "Tega diagrama ni bilo mogoče prikazati. Njegova izvorna koda je prikazana spodaj."
557
558
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "6.5.0",
3
+ "version": "6.7.0",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -118,11 +118,11 @@
118
118
  "access": "public"
119
119
  },
120
120
  "dependencies": {
121
- "@almadar/core": "^10.81.0",
121
+ "@almadar/core": "^10.84.0",
122
122
  "@almadar/evaluator": "^2.44.0",
123
123
  "@almadar/logger": "^1.12.0",
124
- "@almadar/runtime": "^6.69.0",
125
- "@almadar/std": "^16.201.0",
124
+ "@almadar/runtime": "^6.71.0",
125
+ "@almadar/std": "^16.204.0",
126
126
  "@almadar/syntax": "^1.16.0",
127
127
  "@dnd-kit/core": "^6.3.1",
128
128
  "@dnd-kit/sortable": "^10.0.0",
@@ -0,0 +1,437 @@
1
+ /**
2
+ * Comic Theme — "Comic Studio"
3
+ *
4
+ * Fun, light, cartoonish comic-book-art skin for the game-studio editor:
5
+ * bold black-ink outlines, hard offset drop shadows (no blur, like
6
+ * screen-printed panel borders), chunky rounded corners, a bright sunny
7
+ * paper palette with a subtle halftone-dot texture, and snappy springy
8
+ * transitions. Dark mode ("night ink") inverts the ink onto a deep indigo
9
+ * page while keeping the same accent hues, brightened.
10
+ *
11
+ * Halftone texture: applied to `body` (descendant of the themed root, which
12
+ * carries [data-theme] on <html>) rather than baked into --color-background,
13
+ * because --color-background must stay a flat parseable color for
14
+ * scripts/theme-contrast-audit.mjs.
15
+ */
16
+
17
+ /* ==========================================================================
18
+ * LIGHT MODE — sunny paper + ink
19
+ * ========================================================================== */
20
+ [data-theme="comic-light"] {
21
+ /* Shadows - Hard offset ink silhouette, no blur */
22
+ --shadow-main: 4px 4px 0 0 #1c1c28;
23
+ --shadow-sm: 2px 2px 0 0 #1c1c28;
24
+ --shadow-lg: 6px 6px 0 0 #1c1c28;
25
+ --shadow-inner: inset 2px 2px 0 0 rgba(28, 28, 40, 0.15);
26
+ --shadow-none: none;
27
+ --shadow-hover: 6px 6px 0 0 #1c1c28;
28
+ --shadow-active: 1px 1px 0 0 #1c1c28;
29
+
30
+ /* Border radius - Chunky, friendly, rounded-panel corners */
31
+ --radius-none: 0px;
32
+ --radius-sm: 8px;
33
+ --radius-md: 14px;
34
+ --radius-lg: 20px;
35
+ --radius-xl: 28px;
36
+ --radius-full: 9999px;
37
+
38
+ /* Border width - Bold inked outline */
39
+ --border-width: 3px;
40
+ --border-width-thin: 2px;
41
+ --border-width-thick: 4px;
42
+
43
+ /* Colors - Sunny cream paper, comic red-orange primary, mint + yellow pop */
44
+ --color-primary: #c2301b;
45
+ --color-primary-hover: #a92814;
46
+ --color-primary-foreground: #fff8e7;
47
+
48
+ --color-secondary: #a9e8d3;
49
+ --color-secondary-hover: #8fdcc1;
50
+ --color-secondary-foreground: #0f3d2c;
51
+
52
+ --color-accent: #8f6300;
53
+ --color-accent-foreground: #fff8e7;
54
+
55
+ --color-muted: #f5e9c9;
56
+ --color-muted-foreground: #6b5a34;
57
+
58
+ --color-background: #fff8e7;
59
+ --color-foreground: #1c1c28;
60
+ --color-card: #ffffff;
61
+ --color-card-foreground: #1c1c28;
62
+ --color-surface: #fff1d6;
63
+ --color-border: #1c1c28;
64
+ --color-input: #2b2b3d;
65
+ --color-ring: #2f6fed;
66
+
67
+ --color-table-header: #fff1d6;
68
+ --color-table-border: #1c1c28;
69
+ --color-table-row-hover: #fff8e7;
70
+ --color-surface-hover: #ffe9bc;
71
+ --color-border-hover: #c2301b;
72
+ --color-placeholder: #8a7a54;
73
+
74
+ /* Semantic colors */
75
+ --color-error: #e0293b;
76
+ --color-error-foreground: #ffffff;
77
+ --color-success: #12874f;
78
+ --color-success-foreground: #ffffff;
79
+ --color-warning: #f5a623;
80
+ --color-warning-foreground: #2a1a00;
81
+ --color-info: #2f6fed;
82
+ --color-info-foreground: #ffffff;
83
+
84
+ /* Typography - Rounded friendly display + body, generous line-height */
85
+ --font-family:
86
+ "Fredoka", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
87
+ --font-weight-normal: 400;
88
+ --font-weight-medium: 500;
89
+ --font-weight-bold: 700;
90
+ --letter-spacing: 0em;
91
+ --line-height: 1.55;
92
+
93
+ /* Icon styling - Thick cartoon strokes */
94
+ --icon-stroke-width: 2.5;
95
+ --icon-color: #c2301b;
96
+
97
+ /* Transitions - Snappy + springy, bouncy overshoot */
98
+ --transition-fast: 120ms;
99
+ --transition-normal: 200ms;
100
+ --transition-slow: 320ms;
101
+ --transition-timing: cubic-bezier(0.34, 1.56, 0.64, 1);
102
+
103
+ /* Hover/Active transforms - Peel-off-the-page lift / press-into-page */
104
+ --hover-scale: 1.03;
105
+ --hover-translate-y: -2px;
106
+ --hover-translate-x: -1px;
107
+ --active-scale: 0.97;
108
+ --active-translate-y: 1px;
109
+
110
+ /* Focus ring - Electric blue pop */
111
+ --focus-ring-width: 3px;
112
+ --focus-ring-offset: 2px;
113
+ --focus-ring-color: #2f6fed;
114
+
115
+ /* Layer 1 — Density axis (spacing scale) */
116
+ --space-0: 0px;
117
+ --space-1: 4px;
118
+ --space-2: 8px;
119
+ --space-3: 12px;
120
+ --space-4: 16px;
121
+ --space-5: 20px;
122
+ --space-6: 24px;
123
+ --space-7: 28px;
124
+ --space-8: 32px;
125
+ --space-9: 36px;
126
+ --space-10: 40px;
127
+ --space-11: 44px;
128
+ --space-12: 48px;
129
+ /* Density axis — per-element heights */
130
+ --button-height-sm: 30px;
131
+ --button-height-md: 40px;
132
+ --button-height-lg: 48px;
133
+ --input-height-sm: 30px;
134
+ --input-height-md: 40px;
135
+ --input-height-lg: 48px;
136
+ --row-height-compact: 32px;
137
+ --row-height-normal: 40px;
138
+ --row-height-spacious: 48px;
139
+ /* Density axis — per-element padding */
140
+ --card-padding-sm: 12px;
141
+ --card-padding-md: 18px;
142
+ --card-padding-lg: 24px;
143
+ --dialog-padding: 24px;
144
+ --section-gap: 32px;
145
+ /* Type axis — family triplet */
146
+ --font-family-display: "Fredoka", "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
147
+ --font-family-body: var(--font-family);
148
+ --font-family-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
149
+ /* Type axis — size scale */
150
+ --text-xs: 12px;
151
+ --leading-xs: 16px;
152
+ --text-sm: 14px;
153
+ --leading-sm: 20px;
154
+ --text-base: 16px;
155
+ --leading-base: 24px;
156
+ --text-lg: 18px;
157
+ --leading-lg: 28px;
158
+ --text-xl: 20px;
159
+ --leading-xl: 28px;
160
+ --text-2xl: 24px;
161
+ --leading-2xl: 32px;
162
+ --text-3xl: 30px;
163
+ --leading-3xl: 36px;
164
+ --text-4xl: 36px;
165
+ --leading-4xl: 40px;
166
+ --text-display-1: 48px;
167
+ --leading-display-1: 52px;
168
+ --text-display-2: 60px;
169
+ --leading-display-2: 64px;
170
+ /* Type axis — intent mapping */
171
+ --intent-heading-major-size: var(--text-2xl);
172
+ --intent-heading-major-weight: var(--font-weight-bold);
173
+ --intent-heading-major-leading: var(--leading-2xl);
174
+ --intent-heading-minor-size: var(--text-lg);
175
+ --intent-heading-minor-weight: var(--font-weight-bold);
176
+ --intent-heading-minor-leading: var(--leading-lg);
177
+ --intent-body-emphasis-size: var(--text-base);
178
+ --intent-body-emphasis-weight: var(--font-weight-medium);
179
+ --intent-body-emphasis-leading: var(--leading-base);
180
+ --intent-body-default-size: var(--text-sm);
181
+ --intent-body-default-weight: var(--font-weight-normal);
182
+ --intent-body-default-leading: var(--leading-sm);
183
+ --intent-body-quiet-size: var(--text-sm);
184
+ --intent-body-quiet-weight: var(--font-weight-normal);
185
+ --intent-body-quiet-leading: var(--leading-sm);
186
+ --intent-caption-size: var(--text-xs);
187
+ --intent-caption-weight: var(--font-weight-normal);
188
+ --intent-caption-leading: var(--leading-xs);
189
+ --intent-numeric-size: var(--text-sm);
190
+ --intent-numeric-weight: var(--font-weight-medium);
191
+ --intent-numeric-leading: var(--leading-sm);
192
+ /* Motion axis — duration palette */
193
+ --duration-instant: 0ms;
194
+ --duration-fast: var(--transition-fast);
195
+ --duration-normal: var(--transition-normal);
196
+ --duration-slow: var(--transition-slow);
197
+ --duration-dramatic: 600ms;
198
+ /* Motion axis — easing palette */
199
+ --easing-linear: linear;
200
+ --easing-standard: var(--transition-timing);
201
+ --easing-emphasized: cubic-bezier(0.2, 0, 0, 1);
202
+ --easing-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
203
+ /* Iconography axis */
204
+ --icon-family: lucide;
205
+ --icon-default-size: 16px;
206
+ /* Elevation axis — per-layer mapping */
207
+ --elevation-card: var(--shadow-sm);
208
+ --elevation-popover: var(--shadow-main);
209
+ --elevation-dialog: var(--shadow-lg);
210
+ --elevation-toast: var(--shadow-main);
211
+ /* Geometry axis — radius rhythm with intent */
212
+ --radius-container: var(--radius-md);
213
+ --radius-interactive: var(--radius-md);
214
+ --radius-pill: var(--radius-full);
215
+ /* Geometry axis — border-width rhythm */
216
+ --border-hairline: 1px;
217
+ --border-standard: var(--border-width);
218
+ --border-heavy: var(--border-width-thick);
219
+ }
220
+
221
+ /* Halftone-dot paper texture — subtle, doesn't fight card/panel contrast. */
222
+ [data-theme="comic-light"] body {
223
+ background-color: var(--color-background);
224
+ background-image: radial-gradient(circle at 1px 1px, rgba(28, 28, 40, 0.07) 1.2px, transparent 0);
225
+ background-size: 14px 14px;
226
+ }
227
+
228
+ /* ==========================================================================
229
+ * DARK MODE — "Night Ink": deep indigo paper, cream ink outlines
230
+ * ========================================================================== */
231
+ [data-theme="comic-dark"] {
232
+ /* Shadows - Hard offset cream-ink silhouette, no blur */
233
+ --shadow-main: 4px 4px 0 0 #f0e9d8;
234
+ --shadow-sm: 2px 2px 0 0 #f0e9d8;
235
+ --shadow-lg: 6px 6px 0 0 #f0e9d8;
236
+ --shadow-inner: inset 2px 2px 0 0 rgba(240, 233, 216, 0.15);
237
+ --shadow-none: none;
238
+ --shadow-hover: 6px 6px 0 0 #f0e9d8;
239
+ --shadow-active: 1px 1px 0 0 #f0e9d8;
240
+
241
+ /* Border radius - Chunky, friendly, rounded-panel corners */
242
+ --radius-none: 0px;
243
+ --radius-sm: 8px;
244
+ --radius-md: 14px;
245
+ --radius-lg: 20px;
246
+ --radius-xl: 28px;
247
+ --radius-full: 9999px;
248
+
249
+ /* Border width - Bold inked outline */
250
+ --border-width: 3px;
251
+ --border-width-thin: 2px;
252
+ --border-width-thick: 4px;
253
+
254
+ /* Colors - Deep indigo night paper, brightened comic accents */
255
+ --color-primary: #ff7a5c;
256
+ --color-primary-hover: #ff9174;
257
+ --color-primary-foreground: #180a05;
258
+
259
+ --color-secondary: #6fe3c4;
260
+ --color-secondary-hover: #8ceccf;
261
+ --color-secondary-foreground: #062a1e;
262
+
263
+ --color-accent: #ffdd57;
264
+ --color-accent-foreground: #2a1e00;
265
+
266
+ --color-muted: #262a4d;
267
+ --color-muted-foreground: #b7bbe3;
268
+
269
+ --color-background: #14162c;
270
+ --color-foreground: #f5efe0;
271
+ --color-card: #1d2044;
272
+ --color-card-foreground: #f5efe0;
273
+ --color-surface: #1a1d3c;
274
+ --color-border: #f0e9d8;
275
+ --color-input: #c9c2a9;
276
+ --color-ring: #6fa8ff;
277
+
278
+ --color-table-header: #1a1d3c;
279
+ --color-table-border: #f0e9d8;
280
+ --color-table-row-hover: #20244a;
281
+ --color-surface-hover: #262a52;
282
+ --color-border-hover: #ff7a5c;
283
+ --color-placeholder: #8e92c0;
284
+
285
+ /* Semantic colors - Brightened for dark ground */
286
+ --color-error: #ff6b6b;
287
+ --color-error-foreground: #180505;
288
+ --color-success: #4ede93;
289
+ --color-success-foreground: #04210f;
290
+ --color-warning: #ffc24c;
291
+ --color-warning-foreground: #2a1a00;
292
+ --color-info: #6fa8ff;
293
+ --color-info-foreground: #071b3d;
294
+
295
+ /* Typography - Rounded friendly display + body, generous line-height */
296
+ --font-family:
297
+ "Fredoka", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
298
+ --font-weight-normal: 400;
299
+ --font-weight-medium: 500;
300
+ --font-weight-bold: 700;
301
+ --letter-spacing: 0em;
302
+ --line-height: 1.55;
303
+
304
+ /* Icon styling - Thick cartoon strokes */
305
+ --icon-stroke-width: 2.5;
306
+ --icon-color: #ff7a5c;
307
+
308
+ /* Transitions - Snappy + springy, bouncy overshoot */
309
+ --transition-fast: 120ms;
310
+ --transition-normal: 200ms;
311
+ --transition-slow: 320ms;
312
+ --transition-timing: cubic-bezier(0.34, 1.56, 0.64, 1);
313
+
314
+ /* Hover/Active transforms - Peel-off-the-page lift / press-into-page */
315
+ --hover-scale: 1.03;
316
+ --hover-translate-y: -2px;
317
+ --hover-translate-x: -1px;
318
+ --active-scale: 0.97;
319
+ --active-translate-y: 1px;
320
+
321
+ /* Focus ring - Electric blue pop */
322
+ --focus-ring-width: 3px;
323
+ --focus-ring-offset: 2px;
324
+ --focus-ring-color: #6fa8ff;
325
+
326
+ /* Layer 1 — Density axis (spacing scale) */
327
+ --space-0: 0px;
328
+ --space-1: 4px;
329
+ --space-2: 8px;
330
+ --space-3: 12px;
331
+ --space-4: 16px;
332
+ --space-5: 20px;
333
+ --space-6: 24px;
334
+ --space-7: 28px;
335
+ --space-8: 32px;
336
+ --space-9: 36px;
337
+ --space-10: 40px;
338
+ --space-11: 44px;
339
+ --space-12: 48px;
340
+ /* Density axis — per-element heights */
341
+ --button-height-sm: 30px;
342
+ --button-height-md: 40px;
343
+ --button-height-lg: 48px;
344
+ --input-height-sm: 30px;
345
+ --input-height-md: 40px;
346
+ --input-height-lg: 48px;
347
+ --row-height-compact: 32px;
348
+ --row-height-normal: 40px;
349
+ --row-height-spacious: 48px;
350
+ /* Density axis — per-element padding */
351
+ --card-padding-sm: 12px;
352
+ --card-padding-md: 18px;
353
+ --card-padding-lg: 24px;
354
+ --dialog-padding: 24px;
355
+ --section-gap: 32px;
356
+ /* Type axis — family triplet */
357
+ --font-family-display: "Fredoka", "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
358
+ --font-family-body: var(--font-family);
359
+ --font-family-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
360
+ /* Type axis — size scale */
361
+ --text-xs: 12px;
362
+ --leading-xs: 16px;
363
+ --text-sm: 14px;
364
+ --leading-sm: 20px;
365
+ --text-base: 16px;
366
+ --leading-base: 24px;
367
+ --text-lg: 18px;
368
+ --leading-lg: 28px;
369
+ --text-xl: 20px;
370
+ --leading-xl: 28px;
371
+ --text-2xl: 24px;
372
+ --leading-2xl: 32px;
373
+ --text-3xl: 30px;
374
+ --leading-3xl: 36px;
375
+ --text-4xl: 36px;
376
+ --leading-4xl: 40px;
377
+ --text-display-1: 48px;
378
+ --leading-display-1: 52px;
379
+ --text-display-2: 60px;
380
+ --leading-display-2: 64px;
381
+ /* Type axis — intent mapping */
382
+ --intent-heading-major-size: var(--text-2xl);
383
+ --intent-heading-major-weight: var(--font-weight-bold);
384
+ --intent-heading-major-leading: var(--leading-2xl);
385
+ --intent-heading-minor-size: var(--text-lg);
386
+ --intent-heading-minor-weight: var(--font-weight-bold);
387
+ --intent-heading-minor-leading: var(--leading-lg);
388
+ --intent-body-emphasis-size: var(--text-base);
389
+ --intent-body-emphasis-weight: var(--font-weight-medium);
390
+ --intent-body-emphasis-leading: var(--leading-base);
391
+ --intent-body-default-size: var(--text-sm);
392
+ --intent-body-default-weight: var(--font-weight-normal);
393
+ --intent-body-default-leading: var(--leading-sm);
394
+ --intent-body-quiet-size: var(--text-sm);
395
+ --intent-body-quiet-weight: var(--font-weight-normal);
396
+ --intent-body-quiet-leading: var(--leading-sm);
397
+ --intent-caption-size: var(--text-xs);
398
+ --intent-caption-weight: var(--font-weight-normal);
399
+ --intent-caption-leading: var(--leading-xs);
400
+ --intent-numeric-size: var(--text-sm);
401
+ --intent-numeric-weight: var(--font-weight-medium);
402
+ --intent-numeric-leading: var(--leading-sm);
403
+ /* Motion axis — duration palette */
404
+ --duration-instant: 0ms;
405
+ --duration-fast: var(--transition-fast);
406
+ --duration-normal: var(--transition-normal);
407
+ --duration-slow: var(--transition-slow);
408
+ --duration-dramatic: 600ms;
409
+ /* Motion axis — easing palette */
410
+ --easing-linear: linear;
411
+ --easing-standard: var(--transition-timing);
412
+ --easing-emphasized: cubic-bezier(0.2, 0, 0, 1);
413
+ --easing-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
414
+ /* Iconography axis */
415
+ --icon-family: lucide;
416
+ --icon-default-size: 16px;
417
+ /* Elevation axis — per-layer mapping */
418
+ --elevation-card: var(--shadow-sm);
419
+ --elevation-popover: var(--shadow-main);
420
+ --elevation-dialog: var(--shadow-lg);
421
+ --elevation-toast: var(--shadow-main);
422
+ /* Geometry axis — radius rhythm with intent */
423
+ --radius-container: var(--radius-md);
424
+ --radius-interactive: var(--radius-md);
425
+ --radius-pill: var(--radius-full);
426
+ /* Geometry axis — border-width rhythm */
427
+ --border-hairline: 1px;
428
+ --border-standard: var(--border-width);
429
+ --border-heavy: var(--border-width-thick);
430
+ }
431
+
432
+ /* Halftone-dot paper texture — subtle, doesn't fight card/panel contrast. */
433
+ [data-theme="comic-dark"] body {
434
+ background-color: var(--color-background);
435
+ background-image: radial-gradient(circle at 1px 1px, rgba(240, 233, 216, 0.05) 1.2px, transparent 0);
436
+ background-size: 14px 14px;
437
+ }
package/themes/index.css CHANGED
@@ -52,6 +52,7 @@
52
52
  @import "./clay.css";
53
53
  @import "./retro.css";
54
54
  @import "./corporate.css";
55
+ @import "./comic.css";
55
56
 
56
57
  /**
57
58
  * Minimal element reset for @almadar/ui components.