@dorsk/tsumikit 0.1.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 (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +165 -0
  3. package/dist/autoresize.d.ts +11 -0
  4. package/dist/autoresize.js +24 -0
  5. package/dist/components/atoms/Badge.svelte +72 -0
  6. package/dist/components/atoms/Badge.svelte.d.ts +12 -0
  7. package/dist/components/atoms/Button.svelte +156 -0
  8. package/dist/components/atoms/Button.svelte.d.ts +13 -0
  9. package/dist/components/atoms/Card.svelte +46 -0
  10. package/dist/components/atoms/Card.svelte.d.ts +11 -0
  11. package/dist/components/atoms/Checkbox.svelte +99 -0
  12. package/dist/components/atoms/Checkbox.svelte.d.ts +10 -0
  13. package/dist/components/atoms/Chip.svelte +53 -0
  14. package/dist/components/atoms/Chip.svelte.d.ts +11 -0
  15. package/dist/components/atoms/Heading.svelte +66 -0
  16. package/dist/components/atoms/Heading.svelte.d.ts +13 -0
  17. package/dist/components/atoms/Icon.svelte +151 -0
  18. package/dist/components/atoms/Icon.svelte.d.ts +18 -0
  19. package/dist/components/atoms/Input.svelte +42 -0
  20. package/dist/components/atoms/Input.svelte.d.ts +10 -0
  21. package/dist/components/atoms/Link.svelte +31 -0
  22. package/dist/components/atoms/Link.svelte.d.ts +10 -0
  23. package/dist/components/atoms/Progress.svelte +59 -0
  24. package/dist/components/atoms/Progress.svelte.d.ts +9 -0
  25. package/dist/components/atoms/Select.svelte +95 -0
  26. package/dist/components/atoms/Select.svelte.d.ts +11 -0
  27. package/dist/components/atoms/Slider.svelte +136 -0
  28. package/dist/components/atoms/Slider.svelte.d.ts +14 -0
  29. package/dist/components/atoms/Switch.svelte +64 -0
  30. package/dist/components/atoms/Switch.svelte.d.ts +8 -0
  31. package/dist/components/atoms/Text.svelte +127 -0
  32. package/dist/components/atoms/Text.svelte.d.ts +16 -0
  33. package/dist/components/atoms/Textarea.svelte +62 -0
  34. package/dist/components/atoms/Textarea.svelte.d.ts +11 -0
  35. package/dist/components/layouts/AppShell.svelte +304 -0
  36. package/dist/components/layouts/AppShell.svelte.d.ts +21 -0
  37. package/dist/components/layouts/AutoGrid.svelte +36 -0
  38. package/dist/components/layouts/AutoGrid.svelte.d.ts +12 -0
  39. package/dist/components/layouts/Cluster.svelte +45 -0
  40. package/dist/components/layouts/Cluster.svelte.d.ts +14 -0
  41. package/dist/components/layouts/Container.svelte +40 -0
  42. package/dist/components/layouts/Container.svelte.d.ts +13 -0
  43. package/dist/components/layouts/NavItem.svelte +95 -0
  44. package/dist/components/layouts/NavItem.svelte.d.ts +14 -0
  45. package/dist/components/layouts/Stack.svelte +44 -0
  46. package/dist/components/layouts/Stack.svelte.d.ts +13 -0
  47. package/dist/components/molecules/Accordion.svelte +94 -0
  48. package/dist/components/molecules/Accordion.svelte.d.ts +16 -0
  49. package/dist/components/molecules/CodeBlock.svelte +119 -0
  50. package/dist/components/molecules/CodeBlock.svelte.d.ts +17 -0
  51. package/dist/components/molecules/CopyButton.svelte +80 -0
  52. package/dist/components/molecules/CopyButton.svelte.d.ts +13 -0
  53. package/dist/components/molecules/Dropzone.svelte +140 -0
  54. package/dist/components/molecules/Dropzone.svelte.d.ts +13 -0
  55. package/dist/components/molecules/Field.svelte +57 -0
  56. package/dist/components/molecules/Field.svelte.d.ts +12 -0
  57. package/dist/components/molecules/FileButton.svelte +68 -0
  58. package/dist/components/molecules/FileButton.svelte.d.ts +14 -0
  59. package/dist/components/molecules/FontScalePicker.svelte +21 -0
  60. package/dist/components/molecules/FontScalePicker.svelte.d.ts +6 -0
  61. package/dist/components/molecules/IconButton.svelte +36 -0
  62. package/dist/components/molecules/IconButton.svelte.d.ts +13 -0
  63. package/dist/components/molecules/Menu.svelte +120 -0
  64. package/dist/components/molecules/Menu.svelte.d.ts +17 -0
  65. package/dist/components/molecules/Modal.svelte +263 -0
  66. package/dist/components/molecules/Modal.svelte.d.ts +13 -0
  67. package/dist/components/molecules/OptionButton.svelte +76 -0
  68. package/dist/components/molecules/OptionButton.svelte.d.ts +10 -0
  69. package/dist/components/molecules/Popover.svelte +125 -0
  70. package/dist/components/molecules/Popover.svelte.d.ts +18 -0
  71. package/dist/components/molecules/RadioGroup.svelte +110 -0
  72. package/dist/components/molecules/RadioGroup.svelte.d.ts +16 -0
  73. package/dist/components/molecules/SelectButton.svelte +52 -0
  74. package/dist/components/molecules/SelectButton.svelte.d.ts +15 -0
  75. package/dist/components/molecules/Tabs.svelte +119 -0
  76. package/dist/components/molecules/Tabs.svelte.d.ts +15 -0
  77. package/dist/components/molecules/ThemePicker.svelte +22 -0
  78. package/dist/components/molecules/ThemePicker.svelte.d.ts +6 -0
  79. package/dist/components/molecules/Toaster.svelte +73 -0
  80. package/dist/components/molecules/Toaster.svelte.d.ts +18 -0
  81. package/dist/components/molecules/Toggle.svelte +68 -0
  82. package/dist/components/molecules/Toggle.svelte.d.ts +11 -0
  83. package/dist/components/molecules/Tooltip.svelte +106 -0
  84. package/dist/components/molecules/Tooltip.svelte.d.ts +10 -0
  85. package/dist/components/organisms/DataTable.svelte +145 -0
  86. package/dist/components/organisms/DataTable.svelte.d.ts +43 -0
  87. package/dist/env.d.ts +1 -0
  88. package/dist/env.js +4 -0
  89. package/dist/index.d.ts +46 -0
  90. package/dist/index.js +56 -0
  91. package/dist/stores/fontscale.svelte.d.ts +15 -0
  92. package/dist/stores/fontscale.svelte.js +49 -0
  93. package/dist/stores/theme.svelte.d.ts +96 -0
  94. package/dist/stores/theme.svelte.js +71 -0
  95. package/dist/stores/toast.svelte.d.ts +19 -0
  96. package/dist/stores/toast.svelte.js +26 -0
  97. package/dist/styles/app.css +522 -0
  98. package/dist/styles/variables.css +651 -0
  99. package/package.json +71 -0
@@ -0,0 +1,522 @@
1
+ /*
2
+ * Base reset + element defaults + reusable global classes.
3
+ * Consumes ONLY tokens from variables.css — never a raw color/size.
4
+ * Mobile-first: base styles target phones; `min-width` queries enhance up.
5
+ */
6
+ @import './variables.css';
7
+
8
+ *,
9
+ *::before,
10
+ *::after {
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ html {
15
+ -webkit-text-size-adjust: 100%;
16
+ text-size-adjust: 100%;
17
+ /* The font scaler drives --fs-scale, so only --fs-* text tokens grow — chrome
18
+ rem dimensions stay fixed. A scaled-up token can still push a min-width child
19
+ past the viewport; clip sideways so the page never gains a horizontal bar. */
20
+ overflow-x: hidden;
21
+ max-width: 100%;
22
+ scrollbar-gutter: stable;
23
+ }
24
+
25
+ body {
26
+ margin: 0;
27
+ background: var(--bg);
28
+ color: var(--text);
29
+ font-family: var(--font-sans);
30
+ font-size: var(--fs-base);
31
+ line-height: var(--lh-normal);
32
+ -webkit-font-smoothing: antialiased;
33
+ text-rendering: optimizeLegibility;
34
+ overflow-x: hidden;
35
+ scrollbar-gutter: stable;
36
+ }
37
+
38
+ h1,
39
+ h2,
40
+ h3,
41
+ h4,
42
+ h5,
43
+ h6,
44
+ p,
45
+ figure {
46
+ margin: 0;
47
+ }
48
+
49
+ a {
50
+ color: var(--link);
51
+ text-decoration: none;
52
+ }
53
+ a:hover {
54
+ text-decoration: underline;
55
+ }
56
+
57
+ button {
58
+ font: inherit;
59
+ color: inherit;
60
+ cursor: pointer;
61
+ }
62
+
63
+ input,
64
+ textarea,
65
+ select {
66
+ font: inherit;
67
+ color: inherit;
68
+ }
69
+
70
+ /* iOS: inputs <16px trigger an auto-zoom on focus — never let that happen. */
71
+ input,
72
+ textarea,
73
+ select {
74
+ font-size: max(16px, var(--fs-base));
75
+ }
76
+
77
+ code,
78
+ pre {
79
+ font-family: var(--font-mono);
80
+ font-size: 0.875em;
81
+ }
82
+
83
+ /* Visible focus ring for keyboard users only (mouse clicks don't trigger it). */
84
+ :focus-visible {
85
+ outline: 2px solid var(--accent);
86
+ outline-offset: 2px;
87
+ border-radius: var(--r-sm);
88
+ }
89
+
90
+ ::-webkit-scrollbar {
91
+ width: 8px;
92
+ height: 8px;
93
+ }
94
+ ::-webkit-scrollbar-thumb {
95
+ background: var(--border-strong);
96
+ border-radius: var(--r-pill);
97
+ }
98
+
99
+ /* ---------- layout primitives ---------- */
100
+ .container {
101
+ width: 100%;
102
+ max-width: var(--content-max);
103
+ margin-inline: auto;
104
+ padding-inline: var(--sp-4);
105
+ padding-left: max(var(--sp-4), var(--safe-left));
106
+ padding-right: max(var(--sp-4), var(--safe-right));
107
+ }
108
+ .stack {
109
+ display: flex;
110
+ flex-direction: column;
111
+ gap: var(--sp-3);
112
+ }
113
+ .row {
114
+ display: flex;
115
+ align-items: center;
116
+ gap: var(--sp-2);
117
+ }
118
+ .row-wrap {
119
+ flex-wrap: wrap;
120
+ }
121
+ .spacer {
122
+ flex: 1;
123
+ }
124
+ .muted {
125
+ color: var(--text-muted);
126
+ }
127
+ .faint {
128
+ color: var(--text-faint);
129
+ }
130
+ .mono {
131
+ font-family: var(--font-mono);
132
+ }
133
+ .truncate {
134
+ overflow: hidden;
135
+ text-overflow: ellipsis;
136
+ white-space: nowrap;
137
+ min-width: 0;
138
+ }
139
+ .nowrap {
140
+ white-space: nowrap;
141
+ }
142
+ /* Intrinsically responsive grid — no media queries: columns are at least
143
+ --col-min wide and fill the row, wrapping when they can't. Set --col-min to
144
+ tune. This adapts to the available space, so it works inside any container. */
145
+ .auto-grid {
146
+ display: grid;
147
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--col-min, 14rem)), 1fr));
148
+ gap: var(--sp-4);
149
+ }
150
+ /* Opt a wrapper into container queries so descendants can respond to THIS box's
151
+ width (not the viewport). Pair with `@container (…)` in a component's scope,
152
+ or with the .cq-* utilities below. */
153
+ .cq {
154
+ container-type: inline-size;
155
+ }
156
+ /* Container-responsive utilities — they react to the nearest ancestor query
157
+ container (any `.cq`, or an AppShell region), NOT the viewport. Put them on
158
+ children of a container so a component degrades when ITS box is tight,
159
+ wherever it lives. The breakpoints are container widths. */
160
+ @container (max-width: 30rem) {
161
+ .cq-hide {
162
+ display: none !important;
163
+ } /* e.g. wrap a button's label to drop it → icon-only */
164
+ .cq-stack {
165
+ flex-direction: column !important;
166
+ align-items: stretch !important;
167
+ }
168
+ .cq-truncate {
169
+ overflow: hidden;
170
+ text-overflow: ellipsis;
171
+ white-space: nowrap;
172
+ min-width: 0;
173
+ }
174
+ }
175
+ @container (max-width: 18rem) {
176
+ .cq-hide-xs {
177
+ display: none !important;
178
+ }
179
+ }
180
+
181
+ /* ---------- control modifiers ----------
182
+ * The Button atom owns .btn + its variants in its scoped style. What stays
183
+ * global are the icon-sizing rule (used by raw aria-hidden svgs too) and the
184
+ * string-passed modifiers (.btn-icon / .btn-control*) that ride on a
185
+ * Button-rendered element and so can't live in one component's scope. */
186
+ .btn svg,
187
+ .icon,
188
+ svg[aria-hidden='true'] {
189
+ flex: none;
190
+ width: 1em;
191
+ height: 1em;
192
+ min-width: 1em;
193
+ min-height: 1em;
194
+ }
195
+ .btn-icon {
196
+ min-height: 2.25rem;
197
+ min-width: 2.25rem;
198
+ padding: var(--sp-2);
199
+ }
200
+ .btn-icon-inline {
201
+ min-height: 0;
202
+ min-width: 0;
203
+ padding: 0 var(--sp-1);
204
+ border: none;
205
+ background: none;
206
+ color: var(--text-muted);
207
+ }
208
+ .btn-icon-inline:hover:not(:disabled) {
209
+ border: none;
210
+ background: none;
211
+ color: var(--text);
212
+ }
213
+ .btn-icon-inline.hover-danger:hover:not(:disabled) {
214
+ color: var(--danger);
215
+ }
216
+ /* Uniform-height control: icon buttons, inputs and action buttons sharing a
217
+ toolbar/composer row line up exactly. Square icon variant: .btn-control-square. */
218
+ .btn-control {
219
+ display: inline-flex;
220
+ align-items: center;
221
+ justify-content: center;
222
+ gap: var(--sp-2);
223
+ height: var(--control-height);
224
+ min-height: var(--control-height);
225
+ padding: 0 var(--sp-3);
226
+ border: 1px solid var(--border-strong);
227
+ border-radius: var(--r-md);
228
+ background: var(--surface);
229
+ color: var(--text);
230
+ font-weight: var(--fw-medium);
231
+ font-size: var(--fs-sm);
232
+ line-height: 1;
233
+ white-space: nowrap;
234
+ transition:
235
+ background 0.12s var(--ease),
236
+ border-color 0.12s var(--ease),
237
+ opacity 0.12s var(--ease);
238
+ user-select: none;
239
+ }
240
+ .btn-control:hover:not(:disabled) {
241
+ border-color: var(--accent);
242
+ }
243
+ .btn-control:disabled {
244
+ opacity: 0.45;
245
+ cursor: not-allowed;
246
+ }
247
+ .btn-control.btn-primary {
248
+ background: var(--accent);
249
+ border-color: var(--accent);
250
+ color: var(--text-on-accent);
251
+ }
252
+ .btn-control.btn-primary:hover:not(:disabled) {
253
+ border-color: var(--accent);
254
+ filter: brightness(1.08);
255
+ }
256
+ .btn-control-square {
257
+ width: var(--control-height);
258
+ padding: 0;
259
+ flex: none;
260
+ }
261
+
262
+ /* ---------- status dots ---------- */
263
+ .dot {
264
+ width: 0.55rem;
265
+ height: 0.55rem;
266
+ border-radius: var(--r-pill);
267
+ flex: none;
268
+ display: inline-block;
269
+ }
270
+ .dot-active {
271
+ background: var(--dot-active);
272
+ box-shadow: 0 0 6px var(--dot-active);
273
+ }
274
+ .dot-stale {
275
+ background: var(--dot-stale);
276
+ }
277
+ .dot-dead {
278
+ background: var(--dot-dead);
279
+ }
280
+ .dot-hibernated {
281
+ background: var(--dot-hibernated);
282
+ }
283
+
284
+ /* ---------- modal / bottom sheet ---------- */
285
+ .overlay {
286
+ position: fixed;
287
+ inset: 0;
288
+ background: rgba(0, 0, 0, 0.55);
289
+ z-index: var(--z-modal);
290
+ display: flex;
291
+ align-items: flex-end;
292
+ justify-content: center;
293
+ }
294
+ @media (min-width: 640px) {
295
+ .overlay {
296
+ align-items: center;
297
+ padding: var(--sp-6);
298
+ }
299
+ }
300
+ .sheet {
301
+ width: 100%;
302
+ max-width: 34rem;
303
+ max-height: calc(100dvh - var(--safe-top) - var(--sp-6));
304
+ display: flex;
305
+ flex-direction: column;
306
+ background: var(--bg-elevated);
307
+ border: 1px solid var(--border);
308
+ border-radius: var(--r-lg) var(--r-lg) 0 0;
309
+ box-shadow: var(--shadow-lg);
310
+ animation: sheet-up 0.18s var(--ease);
311
+ padding-bottom: var(--safe-bottom);
312
+ }
313
+ @media (min-width: 640px) {
314
+ .sheet {
315
+ border-radius: var(--r-lg);
316
+ padding-bottom: 0;
317
+ }
318
+ }
319
+ @keyframes sheet-up {
320
+ from {
321
+ transform: translateY(8%);
322
+ opacity: 0.4;
323
+ }
324
+ }
325
+ .sheet-head {
326
+ display: flex;
327
+ align-items: center;
328
+ gap: var(--sp-2);
329
+ padding: var(--sp-4);
330
+ border-bottom: 1px solid var(--border);
331
+ }
332
+ .sheet-title {
333
+ font-size: var(--fs-lg);
334
+ font-weight: var(--fw-semibold);
335
+ }
336
+ .sheet-body {
337
+ padding: var(--sp-4);
338
+ overflow-y: auto;
339
+ -webkit-overflow-scrolling: touch;
340
+ }
341
+ .sheet-foot {
342
+ display: flex;
343
+ gap: var(--sp-2);
344
+ padding: var(--sp-4);
345
+ border-top: 1px solid var(--border);
346
+ }
347
+
348
+ /* ---------- toast ---------- */
349
+ .toast-wrap {
350
+ position: fixed;
351
+ left: 50%;
352
+ transform: translateX(-50%);
353
+ bottom: calc(var(--safe-bottom) + var(--sp-4));
354
+ z-index: var(--z-toast);
355
+ display: flex;
356
+ flex-direction: column;
357
+ gap: var(--sp-2);
358
+ width: calc(100% - var(--sp-8));
359
+ max-width: 30rem;
360
+ pointer-events: none;
361
+ }
362
+ .toast-wrap .toast {
363
+ pointer-events: auto;
364
+ width: 100%;
365
+ text-align: left;
366
+ color: inherit;
367
+ cursor: pointer;
368
+ background: var(--bg-elevated-2);
369
+ border: 1px solid var(--border-strong);
370
+ border-radius: var(--r-md);
371
+ padding: var(--sp-3) var(--sp-4);
372
+ box-shadow: var(--shadow-md);
373
+ font-size: var(--fs-sm);
374
+ animation: sheet-up 0.18s var(--ease);
375
+ }
376
+ .toast-wrap .toast-err {
377
+ border-color: var(--danger);
378
+ color: var(--danger);
379
+ }
380
+ .toast-wrap .toast-ok {
381
+ border-color: var(--accent);
382
+ }
383
+
384
+ /* ---------- misc ---------- */
385
+ .empty {
386
+ text-align: center;
387
+ color: var(--text-faint);
388
+ padding: var(--sp-10) var(--sp-4);
389
+ }
390
+ .divider {
391
+ height: 1px;
392
+ background: var(--border);
393
+ border: 0;
394
+ margin: var(--sp-2) 0;
395
+ }
396
+ .spin {
397
+ width: 1.1rem;
398
+ height: 1.1rem;
399
+ border: 2px solid var(--border-strong);
400
+ border-top-color: var(--accent);
401
+ border-radius: 50%;
402
+ animation: spin 0.7s linear infinite;
403
+ display: inline-block;
404
+ }
405
+ @keyframes spin {
406
+ to {
407
+ transform: rotate(360deg);
408
+ }
409
+ }
410
+ .sr-only {
411
+ position: absolute;
412
+ width: 1px;
413
+ height: 1px;
414
+ padding: 0;
415
+ margin: -1px;
416
+ overflow: hidden;
417
+ clip: rect(0, 0, 0, 0);
418
+ white-space: nowrap;
419
+ border: 0;
420
+ }
421
+
422
+ /* ---------- syntax-highlight token mapping ----------
423
+ * CodeBlock doesn't bundle a highlighter; it renders whatever HTML you pass.
424
+ * Class-based highlighters (highlight.js `hljs-*`, Prism `token.*`) are themed
425
+ * here by mapping their token classes onto the --syn-* theme tokens, so colors
426
+ * track every theme. This must be GLOBAL: Svelte scoped styles don't reach
427
+ * {@html} content. (Shiki emits inline colors instead — see the README for how
428
+ * to theme it via its css-variables theme.) */
429
+ .hljs-keyword,
430
+ .hljs-built_in,
431
+ .hljs-type,
432
+ .hljs-literal,
433
+ .hljs-symbol,
434
+ .hljs-selector-tag,
435
+ .token.keyword,
436
+ .token.builtin,
437
+ .token.tag {
438
+ color: var(--syn-keyword);
439
+ }
440
+ .hljs-string,
441
+ .hljs-char,
442
+ .hljs-regexp,
443
+ .token.string,
444
+ .token.char,
445
+ .token.regex,
446
+ .token.attr-value {
447
+ color: var(--syn-string);
448
+ }
449
+ .hljs-number,
450
+ .hljs-attr,
451
+ .hljs-attribute,
452
+ .hljs-variable,
453
+ .hljs-template-variable,
454
+ .token.number,
455
+ .token.boolean,
456
+ .token.attr-name {
457
+ color: var(--syn-number);
458
+ }
459
+ .hljs-comment,
460
+ .hljs-quote,
461
+ .token.comment,
462
+ .token.prolog {
463
+ color: var(--syn-comment);
464
+ font-style: italic;
465
+ }
466
+ .hljs-title,
467
+ .hljs-title.function_,
468
+ .hljs-section,
469
+ .hljs-name,
470
+ .hljs-meta,
471
+ .hljs-property,
472
+ .token.function,
473
+ .token.class-name {
474
+ color: var(--syn-function);
475
+ }
476
+ .hljs-punctuation,
477
+ .token.punctuation,
478
+ .token.operator {
479
+ color: var(--syn-punct);
480
+ }
481
+ .hljs-addition {
482
+ color: var(--syn-string);
483
+ }
484
+ .hljs-deletion {
485
+ color: var(--danger);
486
+ }
487
+
488
+ @media (prefers-reduced-motion: reduce) {
489
+ *,
490
+ *::before,
491
+ *::after {
492
+ animation-duration: 0.01ms !important;
493
+ animation-iteration-count: 1 !important;
494
+ transition-duration: 0.01ms !important;
495
+ scroll-behavior: auto !important;
496
+ }
497
+ }
498
+
499
+ /* Forced colors (Windows High Contrast). The OS replaces our palette with its
500
+ own; our job is to not fight it and to keep affordances visible. Keep the
501
+ focus ring mapped to a system color, and make sure the focus outline shows
502
+ even where we'd normally rely on a token border. */
503
+ @media (forced-colors: active) {
504
+ :focus-visible {
505
+ outline: 2px solid Highlight;
506
+ outline-offset: 2px;
507
+ }
508
+ /* Status tints/borders drawn with color-mix collapse to the system palette;
509
+ force a readable border so badges/cards keep their edges. */
510
+ .dot,
511
+ .btn-control,
512
+ .btn-icon {
513
+ forced-color-adjust: auto;
514
+ }
515
+ }
516
+
517
+ /* Stronger separators when the user asks for more contrast. */
518
+ @media (prefers-contrast: more) {
519
+ :root {
520
+ --border: var(--border-strong);
521
+ }
522
+ }