@ekanos/ui 0.1.0 → 0.1.2

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,414 @@
1
+ /*
2
+ * @ekanos/ui — Fusion design tokens (values only)
3
+ * ------------------------------------------------------------------
4
+ * Plain CSS custom properties. No Tailwind required to consume this file.
5
+ *
6
+ * ── Scope: the full Fusion SEMANTIC surface, on purpose ──
7
+ * This ships every semantic token a Fusion-styled component can reference —
8
+ * NOT just the subset `@ekanos/ui`'s own ~20 primitives happen to touch. That
9
+ * is deliberate, and it is the one place this file's scope differs from the
10
+ * compiled `dist/styles.css`:
11
+ *
12
+ * • `dist/styles.css` is a CLOSED artifact — it can only ever style the
13
+ * components this package ships, so `scripts/build-css.mjs` compiles it
14
+ * against the components' class strings and Tailwind prunes every token no
15
+ * component uses. It stays minimal automatically.
16
+ *
17
+ * • `tokens.css` + `theme.css` are the OPEN preset — their consumer is a
18
+ * PARTNER authoring their OWN Fusion-styled widgets against `@ekanos/ui`
19
+ * (this is exactly what `ekanos dev` scaffolds). A partner writing an
20
+ * ordinary `bg-chart-1`, `text-info`, `bg-sidebar` or `shadow-widget` has
21
+ * no `packages/ui` and no `apps/web/styles` to fall back on — this preset
22
+ * is their ONLY channel to Fusion's tokens. Scoping it to what THIS
23
+ * package's components use would leave those utilities resolving to
24
+ * nothing in a partner project, with no error. So the preset carries the
25
+ * whole semantic set: surfaces, brand, status, chart, sidebar, assistant,
26
+ * info, the full elevation scale, radius and fonts.
27
+ *
28
+ * The drift gate in `scripts/build-css.mjs` enforces a FLOOR, not a ceiling:
29
+ * its COVERAGE check fails the build if a component uses a Fusion token this
30
+ * file does NOT define; it never objects to this file defining MORE. So a
31
+ * superset is safe, and the compiled `styles.css` above still prunes back to
32
+ * exactly what the components use.
33
+ *
34
+ * Values are a verbatim copy of Fusion's canonical `apps/web/styles/
35
+ * shadcn-ui.css`. This file (a published npm subpath) plus `packages/ui` are
36
+ * the two supported ways to consume those tokens; `apps/harness` imports THIS
37
+ * one via `@ekanos/ui/theme.css` rather than forking the values again.
38
+ *
39
+ * Still NOT shipped, because they are not part of the semantic utility
40
+ * surface: `--assistant*` beyond the two aliases below stays out, and no icon
41
+ * font data ships (Fusion uses Font Awesome Pro — see base.css / README).
42
+ *
43
+ * ── Theme switching ──
44
+ * Three states, in cascade order:
45
+ * 1. `:root` → light (the default)
46
+ * 2. `@media (prefers-color-scheme: dark)` → dark, unless the page has
47
+ * explicitly opted into light
48
+ * 3. `.dark` / `[data-theme='dark']` → dark, explicitly, beating the
49
+ * media query in both directions
50
+ *
51
+ * The dark declarations are written out twice on purpose: CSS has no way to
52
+ * share one declaration block between a media query and a plain selector.
53
+ * `scripts/build-css.mjs` asserts the two dark blocks declare the same
54
+ * tokens with the same values.
55
+ */
56
+
57
+ :root {
58
+ /* Typography. Fusion loads Figtree + Bricolage Grotesque through
59
+ `next/font`; a partner who has not loaded them falls through to the
60
+ system UI stack. See the README for the exact <link> tags. */
61
+ --font-sans:
62
+ 'Figtree', ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI',
63
+ Roboto, 'Helvetica Neue', system-ui, sans-serif;
64
+ --font-heading: 'Bricolage Grotesque', var(--font-sans);
65
+
66
+ /* Surfaces */
67
+ --background: oklch(0.97 0.005 30);
68
+ --foreground: oklch(0.15 0.012 30);
69
+ --card: oklch(0.995 0.0015 30);
70
+ --card-foreground: oklch(0.15 0.012 30);
71
+ --popover: oklch(0.995 0.0015 30);
72
+ --popover-foreground: oklch(0.15 0.012 30);
73
+
74
+ /* Brand accent — coral-orange, OKLCH hue ~30 */
75
+ --primary: oklch(0.62 0.19 30);
76
+ --primary-foreground: oklch(0.99 0 0);
77
+ --primary-strong: oklch(0.51 0.17 30);
78
+
79
+ --secondary: oklch(0.94 0.008 30);
80
+ --secondary-foreground: oklch(0.24 0.015 30);
81
+
82
+ --muted: oklch(0.945 0.005 30);
83
+ --muted-foreground: oklch(0.48 0.015 30);
84
+
85
+ --accent: oklch(0.95 0.012 30);
86
+ --accent-foreground: oklch(0.24 0.015 30);
87
+
88
+ /* Status */
89
+ --destructive: oklch(0.55 0.2 25);
90
+ --destructive-foreground: oklch(0.99 0 0);
91
+ --destructive-strong: oklch(0.55 0.2 25);
92
+
93
+ --warning: oklch(0.52 0.13 70);
94
+ --warning-foreground: oklch(0.99 0 0);
95
+
96
+ --success: oklch(0.55 0.13 150);
97
+ --success-foreground: oklch(0.99 0 0);
98
+ --success-strong: oklch(0.51 0.13 150);
99
+
100
+ --info: oklch(0.55 0.15 245);
101
+ --info-foreground: oklch(0.99 0 0);
102
+
103
+ /* Lines and focus */
104
+ --border: oklch(0.91 0.006 30);
105
+ --input: oklch(0.9 0.005 30);
106
+ --ring: oklch(0.62 0.19 30);
107
+
108
+ --radius: 0.5rem;
109
+
110
+ /* Data visualization */
111
+ --chart-1: oklch(0.62 0.19 30);
112
+ --chart-2: oklch(0.61 0.12 205);
113
+ --chart-3: oklch(0.72 0.17 148);
114
+ --chart-4: oklch(0.6 0.18 260);
115
+ --chart-5: oklch(0.7 0.12 80);
116
+
117
+ /* Sidebar */
118
+ --sidebar-background: oklch(0.97 0.005 30);
119
+ --sidebar-foreground: oklch(0.15 0.012 30);
120
+ --sidebar-primary: oklch(0.62 0.19 30);
121
+ --sidebar-primary-foreground: oklch(0.99 0 0);
122
+ --sidebar-accent: oklch(0.955 0.006 30);
123
+ --sidebar-accent-foreground: oklch(0.35 0.015 30);
124
+ --sidebar-border: oklch(0.92 0.006 30);
125
+ --sidebar-ring: oklch(0.62 0.19 30);
126
+
127
+ /* Assistant — aliases onto the sidebar accent */
128
+ --assistant: var(--sidebar-primary);
129
+ --assistant-foreground: var(--sidebar-primary-foreground);
130
+
131
+ /* ── Elevation shadow system ── Stacked diffusion (light mode)
132
+ * Ambient ring + layers using 1→3→6→12→24→48 doubling.
133
+ * Spread = -(blur / 2) keeps each layer tight. */
134
+ --shadow-color: rgba(0, 0, 0, 0.04);
135
+
136
+ --el-flat: 0px 0px 0px 1px var(--shadow-color);
137
+
138
+ --el-sunken: 0px 0px 0px 1px var(--shadow-color);
139
+
140
+ --el-inset:
141
+ 0px 0px 0px 1px var(--shadow-color), 0px 1px 1px -0.5px var(--shadow-color);
142
+
143
+ --el-card:
144
+ 0px 0px 0px 1px var(--shadow-color), 0px 1px 1px -0.5px var(--shadow-color),
145
+ 0px 3px 3px -1.5px var(--shadow-color);
146
+
147
+ --shadow-widget-color: rgba(0, 0, 0, 0.07);
148
+ --el-widget:
149
+ 0px 0px 0px 1px var(--shadow-widget-color),
150
+ 0px 1px 1px -0.5px var(--shadow-widget-color),
151
+ 0px 3px 3px -1.5px var(--shadow-widget-color),
152
+ 0px 6px 6px -3px var(--shadow-widget-color);
153
+
154
+ /* Left-facing variant of the widget shadow — same color, redirected along
155
+ * -X so a full-height rail/dock casts its shadow onto the content beside it
156
+ * and reads as floating above the app. No hard edge ring: a soft, widening
157
+ * falloff only, so the seam stays gentle rather than a crisp line. */
158
+ --el-rail:
159
+ -1px 0px 2px -0.5px var(--shadow-widget-color),
160
+ -4px 0px 6px -2px var(--shadow-widget-color),
161
+ -10px 0px 14px -6px var(--shadow-widget-color);
162
+
163
+ --el-popover:
164
+ 0px 0px 0px 1px var(--shadow-color), 0px 1px 1px -0.5px var(--shadow-color),
165
+ 0px 3px 3px -1.5px var(--shadow-color), 0px 6px 6px -3px var(--shadow-color);
166
+
167
+ --el-sticky:
168
+ 0px 0px 0px 1px var(--shadow-color), 0px 1px 1px -0.5px var(--shadow-color),
169
+ 0px 3px 3px -1.5px var(--shadow-color),
170
+ 0px 6px 6px -3px var(--shadow-color), 0px 12px 12px -6px var(--shadow-color);
171
+
172
+ --el-modal:
173
+ 0px 0px 0px 1px var(--shadow-color), 0px 1px 1px -0.5px var(--shadow-color),
174
+ 0px 3px 3px -1.5px var(--shadow-color),
175
+ 0px 6px 6px -3px var(--shadow-color),
176
+ 0px 12px 12px -6px var(--shadow-color),
177
+ 0px 24px 24px -12px var(--shadow-color);
178
+
179
+ --el-overlay:
180
+ 0px 0px 0px 1px var(--shadow-color), 0px 1px 1px -0.5px var(--shadow-color),
181
+ 0px 3px 3px -1.5px var(--shadow-color),
182
+ 0px 6px 6px -3px var(--shadow-color),
183
+ 0px 12px 12px -6px var(--shadow-color),
184
+ 0px 24px 24px -12px var(--shadow-color),
185
+ 0px 48px 48px -24px var(--shadow-color);
186
+ }
187
+
188
+ /* ── State 2: system dark, unless the page opted into light ── */
189
+ @media (prefers-color-scheme: dark) {
190
+ :root:not(.light):not([data-theme='light']) {
191
+ --background: oklch(0.155 0.002 30);
192
+ --foreground: oklch(0.94 0.004 30);
193
+ --card: oklch(0.21 0.003 30);
194
+ --card-foreground: oklch(0.94 0.004 30);
195
+ --popover: oklch(0.21 0.003 30);
196
+ --popover-foreground: oklch(0.94 0.004 30);
197
+
198
+ --primary: oklch(0.72 0.17 30);
199
+ --primary-foreground: oklch(0.14 0.01 30);
200
+ --primary-strong: oklch(0.72 0.17 30);
201
+
202
+ --secondary: oklch(0.22 0.003 30);
203
+ --secondary-foreground: oklch(0.94 0.004 30);
204
+
205
+ --muted: oklch(0.22 0.003 30);
206
+ --muted-foreground: oklch(0.62 0.006 30);
207
+
208
+ --accent: oklch(0.25 0.004 30);
209
+ --accent-foreground: oklch(0.94 0.004 30);
210
+
211
+ --destructive: oklch(0.5 0.2 25);
212
+ --destructive-foreground: oklch(0.94 0.005 30);
213
+ --destructive-strong: oklch(0.66 0.2 18);
214
+
215
+ --warning: oklch(0.78 0.14 75);
216
+ --warning-foreground: oklch(0.2 0.03 70);
217
+
218
+ --success: oklch(0.72 0.15 150);
219
+ --success-foreground: oklch(0.18 0.03 150);
220
+ --success-strong: oklch(0.72 0.15 150);
221
+
222
+ --info: oklch(0.7 0.13 245);
223
+ --info-foreground: oklch(0.18 0.03 245);
224
+
225
+ --border: oklch(0.25 0.003 30);
226
+ --input: oklch(0.3 0.003 30);
227
+ --ring: oklch(0.72 0.17 30);
228
+
229
+ --chart-1: oklch(0.72 0.17 30);
230
+ --chart-2: oklch(0.7 0.1 205);
231
+ --chart-3: oklch(0.75 0.17 148);
232
+ --chart-4: oklch(0.65 0.18 260);
233
+ --chart-5: oklch(0.75 0.12 80);
234
+
235
+ --sidebar-background: oklch(0.155 0.002 30);
236
+ --sidebar-foreground: oklch(0.94 0.004 30);
237
+ --sidebar-primary: oklch(0.72 0.17 30);
238
+ --sidebar-primary-foreground: oklch(0.14 0.01 30);
239
+ --sidebar-accent: oklch(0.2 0.003 30);
240
+ --sidebar-accent-foreground: oklch(0.94 0.004 30);
241
+ --sidebar-border: oklch(0.27 0.004 30);
242
+ --sidebar-ring: oklch(0.72 0.17 30);
243
+
244
+ --assistant: var(--sidebar-primary);
245
+ --assistant-foreground: var(--sidebar-primary-foreground);
246
+
247
+ --el-flat: none;
248
+
249
+ --el-sunken: inset 0 0 0 1px rgba(255, 255, 255, 0.02);
250
+
251
+ --el-inset:
252
+ inset 0 1px 0 0 rgba(255, 255, 255, 0.02),
253
+ inset 0 0 0 1px rgba(255, 255, 255, 0.02),
254
+ 0 1px 1px -0.5px rgba(0, 0, 0, 0.18);
255
+
256
+ --el-card:
257
+ inset 0 1px 0 0 rgba(255, 255, 255, 0.05),
258
+ inset 0 0 0 1px rgba(255, 255, 255, 0.02), 0 0 0 1px rgba(0, 0, 0, 0.12),
259
+ 0 1px 1px -0.5px rgba(0, 0, 0, 0.18), 0 3px 3px -1.5px rgba(0, 0, 0, 0.18);
260
+
261
+ --el-widget:
262
+ inset 0 1px 0 0 rgba(255, 255, 255, 0.07),
263
+ inset 0 0 0 1px rgba(255, 255, 255, 0.03), 0 0 0 1px rgba(0, 0, 0, 0.14),
264
+ 0 1px 1px -0.5px rgba(0, 0, 0, 0.22),
265
+ 0 3px 3px -1.5px rgba(0, 0, 0, 0.22), 0 6px 6px -3px rgba(0, 0, 0, 0.22);
266
+
267
+ --el-rail:
268
+ inset 1px 0 0 0 rgba(255, 255, 255, 0.05),
269
+ -1px 0 2px -0.5px rgba(0, 0, 0, 0.22),
270
+ -4px 0 6px -2px rgba(0, 0, 0, 0.22), -10px 0 14px -6px rgba(0, 0, 0, 0.22);
271
+
272
+ --el-popover:
273
+ inset 0 1px 0 0 rgba(255, 255, 255, 0.05),
274
+ inset 0 0 0 1px rgba(255, 255, 255, 0.04), 0 0 0 1px rgba(0, 0, 0, 0.14),
275
+ 0 1px 1px -0.5px rgba(0, 0, 0, 0.18),
276
+ 0 3px 3px -1.5px rgba(0, 0, 0, 0.18), 0 6px 6px -3px rgba(0, 0, 0, 0.18);
277
+
278
+ --el-sticky:
279
+ inset 0 1px 0 0 rgba(255, 255, 255, 0.08),
280
+ inset 0 0 0 1px rgba(255, 255, 255, 0.04), 0 0 0 1px rgba(0, 0, 0, 0.16),
281
+ 0 1px 1px -0.5px rgba(0, 0, 0, 0.18),
282
+ 0 3px 3px -1.5px rgba(0, 0, 0, 0.18), 0 6px 6px -3px rgba(0, 0, 0, 0.18),
283
+ 0 12px 12px -6px rgba(0, 0, 0, 0.18);
284
+
285
+ --el-modal:
286
+ inset 0 1px 0 0 rgba(255, 255, 255, 0.08),
287
+ inset 0 0 0 1px rgba(255, 255, 255, 0.06), 0 0 0 1px rgba(0, 0, 0, 0.18),
288
+ 0 1px 1px -0.5px rgba(0, 0, 0, 0.18),
289
+ 0 3px 3px -1.5px rgba(0, 0, 0, 0.18), 0 6px 6px -3px rgba(0, 0, 0, 0.18),
290
+ 0 12px 12px -6px rgba(0, 0, 0, 0.18),
291
+ 0 24px 24px -12px rgba(0, 0, 0, 0.18);
292
+
293
+ --el-overlay:
294
+ inset 0 1px 0 0 rgba(255, 255, 255, 0.11),
295
+ inset 0 0 0 1px rgba(255, 255, 255, 0.06), 0 0 0 1px rgba(0, 0, 0, 0.2),
296
+ 0 1px 1px -0.5px rgba(0, 0, 0, 0.18),
297
+ 0 3px 3px -1.5px rgba(0, 0, 0, 0.18), 0 6px 6px -3px rgba(0, 0, 0, 0.18),
298
+ 0 12px 12px -6px rgba(0, 0, 0, 0.18),
299
+ 0 24px 24px -12px rgba(0, 0, 0, 0.18),
300
+ 0 48px 48px -24px rgba(0, 0, 0, 0.18);
301
+ }
302
+ }
303
+
304
+ /* ── State 3: explicit dark. Same declarations as the media block above —
305
+ kept in lockstep by scripts/build-css.mjs. ── */
306
+ .dark,
307
+ [data-theme='dark'] {
308
+ --background: oklch(0.155 0.002 30);
309
+ --foreground: oklch(0.94 0.004 30);
310
+ --card: oklch(0.21 0.003 30);
311
+ --card-foreground: oklch(0.94 0.004 30);
312
+ --popover: oklch(0.21 0.003 30);
313
+ --popover-foreground: oklch(0.94 0.004 30);
314
+
315
+ --primary: oklch(0.72 0.17 30);
316
+ --primary-foreground: oklch(0.14 0.01 30);
317
+ --primary-strong: oklch(0.72 0.17 30);
318
+
319
+ --secondary: oklch(0.22 0.003 30);
320
+ --secondary-foreground: oklch(0.94 0.004 30);
321
+
322
+ --muted: oklch(0.22 0.003 30);
323
+ --muted-foreground: oklch(0.62 0.006 30);
324
+
325
+ --accent: oklch(0.25 0.004 30);
326
+ --accent-foreground: oklch(0.94 0.004 30);
327
+
328
+ --destructive: oklch(0.5 0.2 25);
329
+ --destructive-foreground: oklch(0.94 0.005 30);
330
+ --destructive-strong: oklch(0.66 0.2 18);
331
+
332
+ --warning: oklch(0.78 0.14 75);
333
+ --warning-foreground: oklch(0.2 0.03 70);
334
+
335
+ --success: oklch(0.72 0.15 150);
336
+ --success-foreground: oklch(0.18 0.03 150);
337
+ --success-strong: oklch(0.72 0.15 150);
338
+
339
+ --info: oklch(0.7 0.13 245);
340
+ --info-foreground: oklch(0.18 0.03 245);
341
+
342
+ --border: oklch(0.25 0.003 30);
343
+ --input: oklch(0.3 0.003 30);
344
+ --ring: oklch(0.72 0.17 30);
345
+
346
+ --chart-1: oklch(0.72 0.17 30);
347
+ --chart-2: oklch(0.7 0.1 205);
348
+ --chart-3: oklch(0.75 0.17 148);
349
+ --chart-4: oklch(0.65 0.18 260);
350
+ --chart-5: oklch(0.75 0.12 80);
351
+
352
+ --sidebar-background: oklch(0.155 0.002 30);
353
+ --sidebar-foreground: oklch(0.94 0.004 30);
354
+ --sidebar-primary: oklch(0.72 0.17 30);
355
+ --sidebar-primary-foreground: oklch(0.14 0.01 30);
356
+ --sidebar-accent: oklch(0.2 0.003 30);
357
+ --sidebar-accent-foreground: oklch(0.94 0.004 30);
358
+ --sidebar-border: oklch(0.27 0.004 30);
359
+ --sidebar-ring: oklch(0.72 0.17 30);
360
+
361
+ --assistant: var(--sidebar-primary);
362
+ --assistant-foreground: var(--sidebar-primary-foreground);
363
+
364
+ --el-flat: none;
365
+
366
+ --el-sunken: inset 0 0 0 1px rgba(255, 255, 255, 0.02);
367
+
368
+ --el-inset:
369
+ inset 0 1px 0 0 rgba(255, 255, 255, 0.02),
370
+ inset 0 0 0 1px rgba(255, 255, 255, 0.02),
371
+ 0 1px 1px -0.5px rgba(0, 0, 0, 0.18);
372
+
373
+ --el-card:
374
+ inset 0 1px 0 0 rgba(255, 255, 255, 0.05),
375
+ inset 0 0 0 1px rgba(255, 255, 255, 0.02), 0 0 0 1px rgba(0, 0, 0, 0.12),
376
+ 0 1px 1px -0.5px rgba(0, 0, 0, 0.18), 0 3px 3px -1.5px rgba(0, 0, 0, 0.18);
377
+
378
+ --el-widget:
379
+ inset 0 1px 0 0 rgba(255, 255, 255, 0.07),
380
+ inset 0 0 0 1px rgba(255, 255, 255, 0.03), 0 0 0 1px rgba(0, 0, 0, 0.14),
381
+ 0 1px 1px -0.5px rgba(0, 0, 0, 0.22), 0 3px 3px -1.5px rgba(0, 0, 0, 0.22),
382
+ 0 6px 6px -3px rgba(0, 0, 0, 0.22);
383
+
384
+ --el-rail:
385
+ inset 1px 0 0 0 rgba(255, 255, 255, 0.05),
386
+ -1px 0 2px -0.5px rgba(0, 0, 0, 0.22), -4px 0 6px -2px rgba(0, 0, 0, 0.22),
387
+ -10px 0 14px -6px rgba(0, 0, 0, 0.22);
388
+
389
+ --el-popover:
390
+ inset 0 1px 0 0 rgba(255, 255, 255, 0.05),
391
+ inset 0 0 0 1px rgba(255, 255, 255, 0.04), 0 0 0 1px rgba(0, 0, 0, 0.14),
392
+ 0 1px 1px -0.5px rgba(0, 0, 0, 0.18), 0 3px 3px -1.5px rgba(0, 0, 0, 0.18),
393
+ 0 6px 6px -3px rgba(0, 0, 0, 0.18);
394
+
395
+ --el-sticky:
396
+ inset 0 1px 0 0 rgba(255, 255, 255, 0.08),
397
+ inset 0 0 0 1px rgba(255, 255, 255, 0.04), 0 0 0 1px rgba(0, 0, 0, 0.16),
398
+ 0 1px 1px -0.5px rgba(0, 0, 0, 0.18), 0 3px 3px -1.5px rgba(0, 0, 0, 0.18),
399
+ 0 6px 6px -3px rgba(0, 0, 0, 0.18), 0 12px 12px -6px rgba(0, 0, 0, 0.18);
400
+
401
+ --el-modal:
402
+ inset 0 1px 0 0 rgba(255, 255, 255, 0.08),
403
+ inset 0 0 0 1px rgba(255, 255, 255, 0.06), 0 0 0 1px rgba(0, 0, 0, 0.18),
404
+ 0 1px 1px -0.5px rgba(0, 0, 0, 0.18), 0 3px 3px -1.5px rgba(0, 0, 0, 0.18),
405
+ 0 6px 6px -3px rgba(0, 0, 0, 0.18), 0 12px 12px -6px rgba(0, 0, 0, 0.18),
406
+ 0 24px 24px -12px rgba(0, 0, 0, 0.18);
407
+
408
+ --el-overlay:
409
+ inset 0 1px 0 0 rgba(255, 255, 255, 0.11),
410
+ inset 0 0 0 1px rgba(255, 255, 255, 0.06), 0 0 0 1px rgba(0, 0, 0, 0.2),
411
+ 0 1px 1px -0.5px rgba(0, 0, 0, 0.18), 0 3px 3px -1.5px rgba(0, 0, 0, 0.18),
412
+ 0 6px 6px -3px rgba(0, 0, 0, 0.18), 0 12px 12px -6px rgba(0, 0, 0, 0.18),
413
+ 0 24px 24px -12px rgba(0, 0, 0, 0.18), 0 48px 48px -24px rgba(0, 0, 0, 0.18);
414
+ }
package/dist/trans.d.ts CHANGED
@@ -25,14 +25,52 @@ type TransProps = ComponentProps<typeof Trans$1>;
25
25
  *
26
26
  * Verified against react-i18next 16.5.4: when no instance is passed via the
27
27
  * `i18n` prop and none has been registered globally, the component logs a
28
- * one-time `NO_I18NEXT_INSTANCE` console warning and returns its children
29
- * unmodified. It does not throw.
30
- *
31
- * Note the sharp edge this implies: the fallback is **children**, not
32
- * `defaults`. `<Trans i18nKey="a.b" defaults="Continue" />` has no children, so
33
- * with no instance available it renders nothing at all. Supply children when a
34
- * string must survive an unconfigured host:
35
- * `<Trans i18nKey="a.b" defaults="Continue">Continue</Trans>`.
28
+ * one-time `NO_I18NEXT_INSTANCE` console warning and returns its **children**
29
+ * unmodified not `defaults`. Every SDK call site passes `i18nKey` +
30
+ * `defaults` and no children, so unpatched that renders nothing at all in any
31
+ * host without i18next (a partner's Storybook, a bare unit test). Inside Fusion
32
+ * and the harness it never shows, which is how it reached 0.1.0.
33
+ *
34
+ * We therefore detect that fall-through and render `defaults` ourselves.
35
+ *
36
+ * ### How the no-instance case is detected
37
+ *
38
+ * Not by asking `getI18n()`, because from here we cannot. It is not exported
39
+ * from `react-i18next/TransWithoutContext`, and react-i18next's `exports` map
40
+ * lists only `.`, `./TransWithoutContext`, `./initReactI18next`, `./icu.macro`
41
+ * and `./package.json`, so a deep import is refused. Reaching it through the
42
+ * main entry would drag in `context.js`, which calls `createContext()` at
43
+ * module scope — and under the `react-server` condition React 19 exports
44
+ * `createContext: undefined`, which would cost this module the
45
+ * RSC-renderability that is its whole reason for using this entry point.
46
+ *
47
+ * What is available is the library's own signal: with no instance it returns
48
+ * the `children` prop BY REFERENCE, and with an instance it returns
49
+ * `renderNodes(...)`, which never is that reference. Reusing react-i18next's
50
+ * own guard rather than reimplementing its condition means the two cannot
51
+ * drift apart across upgrades, and its module-level warn-once still owns the
52
+ * warning, so nothing warns twice.
53
+ *
54
+ * ### Why not simply pass `defaults` as children
55
+ *
56
+ * Because it has a measurable regression in the no-instance branch: children
57
+ * are exactly what react-i18next returns there, so injected `defaults` would
58
+ * override children a caller actually supplied. There is a test for that.
59
+ *
60
+ * The reason usually given for avoiding it — that children flip
61
+ * `interpolationOverride` (react-i18next rewrites the interpolation delimiters
62
+ * to `#$?…?$#` when `values` and `count` are absent but children are present)
63
+ * and also feed `getValuesFromChildren` — is real in the source but, measured,
64
+ * is NOT observable: `renderNodes` re-interpolates afterwards, and injecting
65
+ * `defaults` as children produced byte-identical output across `values`,
66
+ * `tOptions`, `components`, plural-via-`count`, and
67
+ * missing-key-falls-back-to-`defaults`. That argument should not be repeated
68
+ * as fact.
69
+ *
70
+ * The approach below sidesteps the question entirely: it forwards the caller's
71
+ * props untouched and injects nothing, so the with-instance path is identical
72
+ * by construction rather than identical across whichever cases someone
73
+ * happened to try.
36
74
  */
37
75
  declare function Trans(props: TransProps): React.JSX.Element;
38
76
 
package/dist/trans.js CHANGED
@@ -1,8 +1,14 @@
1
1
  // src/_impl/trans.tsx
2
2
  import { Trans as I18nTrans } from "react-i18next/TransWithoutContext";
3
- import { jsx } from "react/jsx-runtime";
3
+ import { Fragment, jsx } from "react/jsx-runtime";
4
+ var renderI18nTrans = I18nTrans;
4
5
  function Trans(props) {
5
- return /* @__PURE__ */ jsx(I18nTrans, { ...props });
6
+ const rendered = renderI18nTrans(props);
7
+ const fellThroughToChildren = rendered === props.children && props.children == null;
8
+ if (fellThroughToChildren && typeof props.defaults === "string") {
9
+ return /* @__PURE__ */ jsx(Fragment, { children: props.defaults });
10
+ }
11
+ return /* @__PURE__ */ jsx(Fragment, { children: rendered });
6
12
  }
7
13
  export {
8
14
  Trans
package/package.json CHANGED
@@ -1,17 +1,11 @@
1
1
  {
2
2
  "name": "@ekanos/ui",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "description": "Fusion-styled UI primitives for building Ekanos integrations outside the monorepo. A curated, published slice of the internal @kit/ui library.",
6
6
  "license": "MIT",
7
- "repository": {
8
- "type": "git",
9
- "url": "git+https://github.com/companydotcom/fusion.git",
10
- "directory": "packages/ekanos-ui"
11
- },
12
- "homepage": "https://github.com/companydotcom/fusion/tree/dev/packages/ekanos-ui#readme",
13
7
  "bugs": {
14
- "url": "https://github.com/companydotcom/fusion/issues"
8
+ "email": "npm@govastly.com"
15
9
  },
16
10
  "sideEffects": false,
17
11
  "files": [
@@ -20,6 +14,10 @@
20
14
  "LICENSE"
21
15
  ],
22
16
  "exports": {
17
+ "./styles.css": "./dist/styles.css",
18
+ "./theme.css": "./dist/theme.css",
19
+ "./tokens.css": "./dist/tokens.css",
20
+ "./base.css": "./dist/base.css",
23
21
  "./alert": {
24
22
  "types": "./dist/alert.d.ts",
25
23
  "default": "./dist/alert.js"
@@ -60,6 +58,14 @@
60
58
  "types": "./dist/input.d.ts",
61
59
  "default": "./dist/input.js"
62
60
  },
61
+ "./textarea": {
62
+ "types": "./dist/textarea.d.ts",
63
+ "default": "./dist/textarea.js"
64
+ },
65
+ "./switch": {
66
+ "types": "./dist/switch.d.ts",
67
+ "default": "./dist/switch.js"
68
+ },
63
69
  "./dropdown-menu": {
64
70
  "types": "./dist/dropdown-menu.d.ts",
65
71
  "default": "./dist/dropdown-menu.js"
@@ -102,6 +108,7 @@
102
108
  },
103
109
  "dependencies": {
104
110
  "@base-ui/react": "1.6.0",
111
+ "@types/json-schema": "^7.0.15",
105
112
  "ai": "^6.0.197",
106
113
  "class-variance-authority": "^0.7.1",
107
114
  "clsx": "^2.1.1",
@@ -124,12 +131,14 @@
124
131
  "react": "19.2.8",
125
132
  "react-dom": "19.2.8",
126
133
  "react-hook-form": "^7.68.0",
134
+ "tailwindcss": "4.3.3",
127
135
  "tsup": "8.5.1",
128
136
  "typescript": "^5.9.3",
137
+ "vitest": "4.1.10",
129
138
  "@kit/eslint-config": "0.2.0",
130
- "@kit/ui": "0.1.0",
139
+ "@kit/tsconfig": "0.1.0",
131
140
  "@kit/prettier-config": "0.1.0",
132
- "@kit/tsconfig": "0.1.0"
141
+ "@kit/ui": "0.1.0"
133
142
  },
134
143
  "prettier": "@kit/prettier-config",
135
144
  "typesVersions": {
@@ -140,11 +149,12 @@
140
149
  }
141
150
  },
142
151
  "scripts": {
143
- "build": "tsup",
152
+ "build": "tsup && node scripts/build-css.mjs",
144
153
  "clean": "git clean -xdf .turbo node_modules dist",
145
154
  "format": "prettier --check \"**/*.{ts,tsx,mjs}\"",
146
155
  "lint": "eslint .",
147
156
  "typecheck": "tsc --noEmit",
157
+ "test": "vitest run --config vitest.config.ts",
148
158
  "pack:test": "node scripts/pack-test.mjs"
149
159
  }
150
160
  }