@zuilib/text-editor 0.11.1 → 0.13.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 (50) hide show
  1. package/CHANGELOG.md +202 -18
  2. package/DRAWING_FORMAT.md +23 -21
  3. package/README.md +531 -87
  4. package/dist/chunk-32RX4GZF.js +2 -0
  5. package/dist/chunk-3KAABUZQ.js +2 -0
  6. package/dist/chunk-5J4TV6XA.js +2 -0
  7. package/dist/chunk-5VRVC56Y.js +2 -0
  8. package/dist/chunk-6JQG6QDN.js +16 -0
  9. package/dist/chunk-6LHJXZZ2.js +2 -0
  10. package/dist/chunk-7Z3CW6Q3.js +2 -0
  11. package/dist/chunk-A7MCBSAZ.js +2 -0
  12. package/dist/chunk-AGTPMKLK.js +2 -0
  13. package/dist/chunk-LDVPFZCN.js +2 -0
  14. package/dist/chunk-N5JWZ2VW.js +9 -0
  15. package/dist/chunk-N6OUUWQO.js +5 -0
  16. package/dist/chunk-QKHMJQO3.js +2 -0
  17. package/dist/chunk-TGVEPLDM.js +1 -0
  18. package/dist/chunk-XRJNLGI4.js +2 -0
  19. package/dist/chunk-YFFSTMIR.js +2 -0
  20. package/dist/chunk-ZXZMYJ7F.js +2 -0
  21. package/dist/comment-markers-BrrrAdSL.d.ts +27 -0
  22. package/dist/comments.d.ts +68 -0
  23. package/dist/comments.js +2 -0
  24. package/dist/drawing-data-EWzEQ1_i.d.ts +111 -0
  25. package/dist/drawing-preset-Ag7a7F9h.d.ts +69 -0
  26. package/dist/drawing.d.ts +7 -0
  27. package/dist/drawing.js +2 -0
  28. package/dist/image-node-kR1Zf5kz.d.ts +70 -0
  29. package/dist/images.d.ts +66 -0
  30. package/dist/images.js +2 -0
  31. package/dist/index-A4S7bRwQ.d.ts +425 -0
  32. package/dist/index.d.ts +36 -1394
  33. package/dist/index.js +1 -7449
  34. package/dist/lexical.d.ts +117 -0
  35. package/dist/lexical.js +2 -0
  36. package/dist/markdown.d.ts +29 -0
  37. package/dist/markdown.js +2 -0
  38. package/dist/mention-node-eSsjUpaE.d.ts +44 -0
  39. package/dist/mentions-plugin-gio3_XKd.d.ts +74 -0
  40. package/dist/mentions.d.ts +5 -0
  41. package/dist/mentions.js +2 -0
  42. package/dist/mermaid-BwGHjwpk.d.ts +553 -0
  43. package/dist/paste.d.ts +45 -0
  44. package/dist/paste.js +2 -0
  45. package/dist/presets-BtNPH0pa.d.ts +78 -0
  46. package/dist/styles.css +612 -96
  47. package/dist/table-grid-D70tNhp2.d.ts +459 -0
  48. package/dist/tailwind.css +7 -0
  49. package/package.json +58 -27
  50. package/dist/index.css +0 -79
package/dist/styles.css CHANGED
@@ -1,8 +1,22 @@
1
+ /**
2
+ * Prefix for the generated plain-CSS entry. The build appends the editor and
3
+ * plugin CSS so dist/styles.css stays directly inspectable and usable by
4
+ * processors that do not inline relative imports.
5
+ */
6
+ @import "@zuilib/tokens/tokens.css";
7
+ /*
8
+ * The root is an inline-size container: the toolbar, the outline and the
9
+ * document respond to the editor's own width (see the small-container
10
+ * rules at the end), so an editor in a sidebar lays out like one on a
11
+ * phone. Containment also makes the root a stacking context, so the
12
+ * sticky toolbar's z-index stays inside the editor.
13
+ */
1
14
  .zui-text-editor {
2
15
  position: relative;
3
16
  width: 100%;
4
17
  height: 100%;
5
18
  overflow-y: auto;
19
+ container-type: inline-size;
6
20
  }
7
21
 
8
22
  .zui-text-editor-body {
@@ -41,6 +55,28 @@
41
55
  min-height: 100%;
42
56
  padding: 1.5rem var(--zui-text-editor-gutter, 2rem);
43
57
  outline: none;
58
+ /* A URL or token longer than the line wraps instead of widening the pane */
59
+ overflow-wrap: break-word;
60
+ }
61
+
62
+ /* Keyboard focus: an inset ring so it is visible without changing layout */
63
+ .zui-text-editor-content:focus-visible,
64
+ .zui-text-editor-textarea:focus-visible {
65
+ outline: none;
66
+ box-shadow: inset 0 0 0 2px var(--ring, var(--primary, currentColor));
67
+ }
68
+
69
+ .zui-text-editor-content[aria-invalid='true'],
70
+ .zui-text-editor-textarea[aria-invalid='true'] {
71
+ box-shadow: inset 0 0 0 1px var(--danger, currentColor);
72
+ }
73
+
74
+ /* Invalid and focused: the focus ring stays visible, the invalid line sits inside it */
75
+ .zui-text-editor-content[aria-invalid='true']:focus-visible,
76
+ .zui-text-editor-textarea[aria-invalid='true']:focus-visible {
77
+ box-shadow:
78
+ inset 0 0 0 2px var(--ring, var(--primary, currentColor)),
79
+ inset 0 0 0 3px var(--danger, currentColor);
44
80
  }
45
81
 
46
82
  /*
@@ -48,7 +84,7 @@
48
84
  * past it by `--zui-text-editor-block-bleed` (0 unless set). Tables and
49
85
  * drawings default it to the pane's bleed (`full`); their `text` and
50
86
  * `content` settings pin it to 0 — as an inline style on the table (see
51
- * tableSettings.ts) and a class on the drawing canvas. Zero-specificity
87
+ * table-settings.ts) and a class on the drawing canvas. Zero-specificity
52
88
  * selectors, so one class rule overrides any block kind, e.g.
53
89
  * `.zui-code { --zui-text-editor-block-bleed: var(--zui-text-editor-bleed) }`.
54
90
  */
@@ -95,7 +131,7 @@
95
131
  height: 1.5rem;
96
132
  margin-bottom: 0.25rem;
97
133
  border: none;
98
- border-radius: 0.375rem;
134
+ border-radius: var(--radius-sm, 0.375rem);
99
135
  background: transparent;
100
136
  color: inherit;
101
137
  opacity: 0.55;
@@ -111,7 +147,7 @@
111
147
  display: flex;
112
148
  flex-direction: column;
113
149
  gap: 1px;
114
- border-left: 1px solid color-mix(in srgb, currentColor 12%, transparent);
150
+ border-inline-start: 1px solid color-mix(in srgb, currentColor 12%, transparent);
115
151
  }
116
152
 
117
153
  .zui-text-editor-outline-item {
@@ -124,7 +160,7 @@
124
160
  opacity: 0.6;
125
161
  font-size: 0.8125rem;
126
162
  line-height: 1.35;
127
- text-align: left;
163
+ text-align: start;
128
164
  cursor: pointer;
129
165
  overflow: hidden;
130
166
  text-overflow: ellipsis;
@@ -138,7 +174,7 @@
138
174
  .zui-text-editor-outline-item.is-active {
139
175
  opacity: 1;
140
176
  font-weight: 600;
141
- box-shadow: inset 2px 0 0 #6965db;
177
+ box-shadow: inset 2px 0 0 var(--primary, currentColor);
142
178
  }
143
179
 
144
180
  .zui-text-editor-outline-empty {
@@ -156,15 +192,15 @@
156
192
 
157
193
  .zui-text-editor-fold {
158
194
  position: absolute;
159
- /* Just left of the text column; 0.45rem with the default gutter */
160
- left: calc(var(--zui-text-editor-gutter, 2rem) - 1.55rem);
195
+ /* Just before the text column; 0.45rem with the default gutter */
196
+ inset-inline-start: calc(var(--zui-text-editor-gutter, 2rem) - 1.55rem);
161
197
  display: inline-flex;
162
198
  align-items: center;
163
199
  justify-content: center;
164
200
  width: 1.125rem;
165
201
  height: 1.125rem;
166
202
  border: none;
167
- border-radius: 0.25rem;
203
+ border-radius: var(--radius-sm, 0.25rem);
168
204
  background: transparent;
169
205
  color: inherit;
170
206
  opacity: 0;
@@ -197,7 +233,7 @@
197
233
  .zui-text-editor-placeholder {
198
234
  position: absolute;
199
235
  top: 1.5rem;
200
- left: var(--zui-text-editor-gutter, 2rem);
236
+ inset-inline-start: var(--zui-text-editor-gutter, 2rem);
201
237
  pointer-events: none;
202
238
  user-select: none;
203
239
  }
@@ -205,14 +241,14 @@
205
241
  /* Checklist styles */
206
242
  .zui-checklist {
207
243
  list-style: none;
208
- margin-left: 0;
209
- padding-left: 0;
244
+ margin-inline-start: 0;
245
+ padding-inline-start: 0;
210
246
  }
211
247
 
212
248
  .zui-checklist li[role='checkbox'] {
213
249
  position: relative;
214
- padding-left: 1.75rem;
215
- margin-left: 0;
250
+ padding-inline-start: 1.75rem;
251
+ margin-inline-start: 0;
216
252
  cursor: pointer;
217
253
  list-style: none;
218
254
  }
@@ -220,12 +256,12 @@
220
256
  .zui-checklist li[role='checkbox']::before {
221
257
  content: '';
222
258
  position: absolute;
223
- left: 0;
259
+ inset-inline-start: 0;
224
260
  top: 0.25em;
225
261
  width: 1rem;
226
262
  height: 1rem;
227
263
  border: 2px solid currentColor;
228
- border-radius: 0.2rem;
264
+ border-radius: var(--radius-sm, 0.2rem);
229
265
  opacity: 0.5;
230
266
  background: transparent;
231
267
  }
@@ -238,7 +274,7 @@
238
274
  .zui-checklist li[role='checkbox'][aria-checked='true']::after {
239
275
  content: '';
240
276
  position: absolute;
241
- left: 0.3rem;
277
+ inset-inline-start: 0.3rem;
242
278
  top: 0.35em;
243
279
  width: 0.4rem;
244
280
  height: 0.7rem;
@@ -254,8 +290,8 @@
254
290
  margin-block: 0 1rem;
255
291
  padding: 0.75rem 1rem;
256
292
  border: 1px solid currentColor;
257
- border-radius: 0.375rem;
258
- border-left-width: 3px;
293
+ border-radius: var(--radius-md, 0.375rem);
294
+ border-inline-start-width: 3px;
259
295
  font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas,
260
296
  'Liberation Mono', monospace;
261
297
  font-size: 0.8125rem;
@@ -263,6 +299,20 @@
263
299
  opacity: 0.7;
264
300
  }
265
301
 
302
+ /* Polite live region for row / column / fold announcements */
303
+ .zui-text-editor-live {
304
+ position: absolute;
305
+ width: 1px;
306
+ height: 1px;
307
+ margin: -1px;
308
+ padding: 0;
309
+ overflow: hidden;
310
+ clip: rect(0 0 0 0);
311
+ clip-path: inset(50%);
312
+ white-space: nowrap;
313
+ border: 0;
314
+ }
315
+
266
316
  /* Toolbar */
267
317
  .zui-text-editor-toolbar {
268
318
  position: sticky;
@@ -271,6 +321,7 @@
271
321
  display: flex;
272
322
  align-items: center;
273
323
  gap: 0.125rem;
324
+ min-width: 0;
274
325
  padding: 0.375rem 0.75rem;
275
326
  border-bottom: 1px solid color-mix(in srgb, currentColor 12%, transparent);
276
327
  background: inherit;
@@ -279,12 +330,13 @@
279
330
 
280
331
  .zui-text-editor-toolbar-button {
281
332
  display: inline-flex;
333
+ flex-shrink: 0;
282
334
  align-items: center;
283
335
  justify-content: center;
284
336
  width: 1.75rem;
285
337
  height: 1.75rem;
286
338
  border: none;
287
- border-radius: 0.375rem;
339
+ border-radius: var(--radius-sm, 0.375rem);
288
340
  background: transparent;
289
341
  color: inherit;
290
342
  opacity: 0.65;
@@ -308,6 +360,7 @@
308
360
  }
309
361
 
310
362
  .zui-text-editor-toolbar-divider {
363
+ flex-shrink: 0;
311
364
  width: 1px;
312
365
  height: 1.125rem;
313
366
  margin: 0 0.375rem;
@@ -326,9 +379,9 @@
326
379
  border-collapse: separate;
327
380
  border-spacing: 0;
328
381
  border: 1px solid color-mix(in srgb, currentColor 15%, transparent);
329
- border-radius: 0.625rem;
382
+ border-radius: var(--radius-lg, 0.625rem);
330
383
  overflow: hidden;
331
- /* Density presets set these custom properties inline (tableSettings.ts) */
384
+ /* Density presets set these custom properties inline (table-settings.ts) */
332
385
  font-size: var(--zui-table-font-size, 0.875rem);
333
386
  table-layout: fixed;
334
387
  }
@@ -336,13 +389,13 @@
336
389
  .zui-table-cell {
337
390
  padding: var(--zui-table-cell-padding, 0.5rem 0.75rem);
338
391
  border-bottom: 1px solid color-mix(in srgb, currentColor 10%, transparent);
339
- border-right: 1px solid color-mix(in srgb, currentColor 10%, transparent);
392
+ border-inline-end: 1px solid color-mix(in srgb, currentColor 10%, transparent);
340
393
  vertical-align: top;
341
- text-align: left;
394
+ text-align: start;
342
395
  }
343
396
 
344
397
  .zui-table-cell:last-child {
345
- border-right: none;
398
+ border-inline-end: none;
346
399
  }
347
400
 
348
401
  .zui-table tr:last-child > .zui-table-cell {
@@ -362,18 +415,18 @@
362
415
  .zui-table-selected,
363
416
  .zui-table-cell.zui-table-selected {
364
417
  outline: none;
365
- background: color-mix(in srgb, var(--primary, #6965db) 14%, transparent);
418
+ background: color-mix(in srgb, var(--primary, currentColor) 14%, transparent);
366
419
  }
367
420
 
368
421
  /*
369
422
  * Floating table controls (positioned within .zui-text-editor-main, so they
370
- * scroll with the document; TableControlsPlugin.tsx).
423
+ * scroll with the document; table-controls-plugin.tsx).
371
424
  *
372
425
  * The layout presets sit above the table the caret is in, aligned with
373
426
  * its right edge and stacked above the column rail (the plugin sets `top`
374
427
  * to the rail's top; the transform lifts the toolbar clear of it). The
375
428
  * settings themselves are an inline style on the <table> (see
376
- * tableSettings.ts).
429
+ * table-settings.ts).
377
430
  */
378
431
  .zui-table-settings {
379
432
  position: absolute;
@@ -386,7 +439,7 @@
386
439
  border-radius: var(--radius-md, 0.5rem);
387
440
  background: var(--popover, #ffffff);
388
441
  color: var(--popover-foreground, inherit);
389
- box-shadow: var(--shadow-medium, 0 1px 4px rgba(0, 0, 0, 0.08));
442
+ box-shadow: var(--shadow-2, 0 1px 4px rgba(0, 0, 0, 0.08));
390
443
  transform: translateY(calc(-100% - 0.25rem));
391
444
  }
392
445
 
@@ -402,27 +455,28 @@
402
455
  .zui-table-rail {
403
456
  position: absolute;
404
457
  z-index: 4;
405
- --zui-table-rail-accent: var(--primary, #6965db);
458
+ --zui-table-rail-accent: var(--primary, currentColor);
406
459
  --zui-table-rail-accent-foreground: var(--primary-foreground, #ffffff);
407
- --zui-table-rail-danger: var(--destructive, #dc2626);
408
- --zui-table-rail-danger-foreground: var(--destructive-foreground, #ffffff);
460
+ --zui-table-rail-danger: var(--danger, #dc2626);
461
+ --zui-table-rail-danger-foreground: var(--danger-foreground, #ffffff);
409
462
  }
410
463
 
411
464
  .zui-table-rail-marker {
412
465
  position: absolute;
413
- border-radius: 999px;
466
+ border-radius: var(--radius-full, 999px);
414
467
  background: var(--zui-table-rail-accent);
415
468
  opacity: 0.55;
416
469
  pointer-events: none;
417
470
  transition:
418
471
  top 140ms cubic-bezier(0.2, 0, 0, 1),
419
472
  left 140ms cubic-bezier(0.2, 0, 0, 1),
473
+ inset-inline-start 140ms cubic-bezier(0.2, 0, 0, 1),
420
474
  width 140ms cubic-bezier(0.2, 0, 0, 1),
421
475
  height 140ms cubic-bezier(0.2, 0, 0, 1);
422
476
  }
423
477
 
424
478
  .zui-table-rail.is-row .zui-table-rail-marker {
425
- right: 5px;
479
+ inset-inline-end: 5px;
426
480
  width: 3px;
427
481
  }
428
482
 
@@ -440,14 +494,15 @@
440
494
  height: 20px;
441
495
  padding: 0;
442
496
  border: 1px solid var(--border, color-mix(in srgb, currentColor 14%, transparent));
443
- border-radius: 999px;
497
+ border-radius: var(--radius-full, 999px);
444
498
  background: var(--popover, #ffffff);
445
499
  color: var(--popover-foreground, inherit);
446
- box-shadow: var(--shadow-medium, 0 1px 4px rgba(0, 0, 0, 0.08));
500
+ box-shadow: var(--shadow-2, 0 1px 4px rgba(0, 0, 0, 0.08));
447
501
  cursor: pointer;
448
502
  transition:
449
503
  top 140ms cubic-bezier(0.2, 0, 0, 1),
450
504
  left 140ms cubic-bezier(0.2, 0, 0, 1),
505
+ inset-inline-start 140ms cubic-bezier(0.2, 0, 0, 1),
451
506
  opacity 140ms ease,
452
507
  background-color 120ms ease,
453
508
  color 120ms ease,
@@ -456,7 +511,7 @@
456
511
  }
457
512
 
458
513
  .zui-table-rail.is-row .zui-table-rail-handle {
459
- left: 2px;
514
+ inset-inline-start: 2px;
460
515
  transform: translateY(-50%);
461
516
  }
462
517
 
@@ -498,6 +553,7 @@
498
553
  transition:
499
554
  top 140ms cubic-bezier(0.2, 0, 0, 1),
500
555
  left 140ms cubic-bezier(0.2, 0, 0, 1),
556
+ inset-inline-start 140ms cubic-bezier(0.2, 0, 0, 1),
501
557
  opacity 120ms ease;
502
558
  }
503
559
 
@@ -526,6 +582,24 @@
526
582
  opacity: 1;
527
583
  }
528
584
 
585
+ /*
586
+ * Touch: a tap on the rail pins the handle where the finger was (there is
587
+ * no hover to follow); a second tap on the handle acts. A finger sliding
588
+ * along the rail moves the handle, so the rail claims that axis.
589
+ */
590
+ .zui-table-rail.is-row {
591
+ touch-action: pan-x;
592
+ }
593
+
594
+ .zui-table-rail.is-column {
595
+ touch-action: pan-y;
596
+ }
597
+
598
+ .zui-table-rail.is-pinned .zui-table-rail-handle,
599
+ .zui-table-rail.is-pinned .zui-table-rail-preview {
600
+ opacity: 1;
601
+ }
602
+
529
603
  @media (prefers-reduced-motion: reduce) {
530
604
  .zui-table-rail-marker,
531
605
  .zui-table-rail-handle,
@@ -559,14 +633,14 @@
559
633
  --zui-drawing-chrome: var(--background, #ffffff);
560
634
  --zui-drawing-popover: var(--popover, #ffffff);
561
635
  --zui-drawing-popover-foreground: var(--popover-foreground, var(--zui-drawing-foreground));
562
- --zui-drawing-danger: var(--destructive, #dc2626);
636
+ --zui-drawing-danger: var(--danger, #dc2626);
563
637
  --zui-drawing-success: var(--success, #15803d);
564
638
  --zui-drawing-radius: var(--radius-lg, 0.75rem);
565
639
  --zui-drawing-control-radius: var(--radius-sm, 0.375rem);
566
- --zui-drawing-shadow: var(--shadow-medium, 0 4px 6px -1px rgba(0, 0, 0, 0.1));
640
+ --zui-drawing-shadow: var(--shadow-2, 0 4px 6px -1px rgba(0, 0, 0, 0.1));
567
641
  --zui-drawing-font: ui-sans-serif, system-ui, sans-serif;
568
- --zui-drawing-surface: #ffffff;
569
- --zui-drawing-grid: rgba(0, 0, 0, 0.09);
642
+ --zui-drawing-surface: var(--zui-drawing-surface-color, #ffffff);
643
+ --zui-drawing-grid: var(--zui-drawing-grid-color, rgba(0, 0, 0, 0.09));
570
644
  --zui-drawing-grid-size: 20px;
571
645
  --zui-drawing-dark-filter: invert(93%) hue-rotate(180deg);
572
646
  --zui-drawing-hover: color-mix(in srgb, currentColor 8%, transparent);
@@ -588,6 +662,10 @@
588
662
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--zui-drawing-accent) 12%, transparent);
589
663
  }
590
664
 
665
+ .zui-drawing-canvas.is-editable:focus-within .zui-drawing-surface {
666
+ touch-action: none;
667
+ }
668
+
591
669
  .zui-drawing-canvas.is-content-width {
592
670
  width: fit-content;
593
671
  max-width: 100%;
@@ -601,7 +679,12 @@
601
679
  display: block;
602
680
  width: 100%;
603
681
  max-width: 100%;
604
- touch-action: none;
682
+ /*
683
+ * A finger on an idle canvas scrolls the page; once the canvas is
684
+ * active (tapped, so focused) touches draw and drag. Mouse input is
685
+ * unaffected either way.
686
+ */
687
+ touch-action: pan-y pinch-zoom;
605
688
  font-family: var(--zui-drawing-font);
606
689
  background-color: var(--zui-drawing-surface);
607
690
  background-image: radial-gradient(circle, var(--zui-drawing-grid) 1px, transparent 1px);
@@ -678,12 +761,15 @@
678
761
 
679
762
  .zui-drawing-toolbar-group {
680
763
  display: flex;
764
+ /* Narrow canvases: the tools flow onto a second line rather than being clipped */
765
+ flex-wrap: wrap;
681
766
  align-items: center;
682
767
  gap: 0.125rem;
768
+ min-width: 0;
683
769
  }
684
770
 
685
771
  .zui-drawing-toolbar-group-end {
686
- margin-left: auto;
772
+ margin-inline-start: auto;
687
773
  }
688
774
 
689
775
  .zui-drawing-toolbar-divider {
@@ -741,11 +827,11 @@
741
827
 
742
828
  .zui-drawing-caret {
743
829
  position: absolute;
744
- right: 0.25rem;
830
+ inset-inline-end: 0.25rem;
745
831
  bottom: 0.25rem;
746
832
  width: 0;
747
833
  height: 0;
748
- border-left: 4px solid transparent;
834
+ border-inline-start: 4px solid transparent;
749
835
  border-top: 4px solid currentColor;
750
836
  opacity: 0.7;
751
837
  }
@@ -757,7 +843,7 @@
757
843
  .zui-drawing-popover {
758
844
  position: absolute;
759
845
  top: calc(100% + 0.25rem);
760
- left: 0;
846
+ inset-inline-start: 0;
761
847
  z-index: 7;
762
848
  display: flex;
763
849
  flex-direction: column;
@@ -782,7 +868,7 @@
782
868
  background: transparent;
783
869
  color: inherit;
784
870
  font-size: 0.8125rem;
785
- text-align: left;
871
+ text-align: start;
786
872
  cursor: pointer;
787
873
  transition: background-color 0.12s ease;
788
874
  }
@@ -815,8 +901,8 @@
815
901
  display: flex;
816
902
  align-items: center;
817
903
  gap: 0.25rem;
818
- padding-left: 0.5rem;
819
- border-left: 1px solid color-mix(in srgb, currentColor 15%, transparent);
904
+ padding-inline-start: 0.5rem;
905
+ border-inline-start: 1px solid color-mix(in srgb, currentColor 15%, transparent);
820
906
  }
821
907
 
822
908
  .zui-drawing-props-group {
@@ -826,12 +912,12 @@
826
912
  }
827
913
 
828
914
  .zui-drawing-props-group + .zui-drawing-props-group {
829
- padding-left: 0.5rem;
830
- border-left: 1px solid color-mix(in srgb, currentColor 15%, transparent);
915
+ padding-inline-start: 0.5rem;
916
+ border-inline-start: 1px solid color-mix(in srgb, currentColor 15%, transparent);
831
917
  }
832
918
 
833
919
  .zui-drawing-swatch-label {
834
- margin: 0 0.25rem 0 0.125rem;
920
+ margin-inline: 0.125rem 0.25rem;
835
921
  font-size: 0.6875rem;
836
922
  letter-spacing: 0.02em;
837
923
  color: var(--zui-drawing-muted-foreground);
@@ -844,7 +930,7 @@
844
930
  margin: 0 0.125rem;
845
931
  padding: 0;
846
932
  border: 2px solid rgba(0, 0, 0, 0.18);
847
- border-radius: 999px;
933
+ border-radius: var(--radius-full, 999px);
848
934
  cursor: pointer;
849
935
  transition:
850
936
  transform 0.12s ease,
@@ -877,10 +963,7 @@
877
963
  /* Inline text editing */
878
964
  .zui-drawing-overlay {
879
965
  position: absolute;
880
- top: 0;
881
- left: 0;
882
- right: 0;
883
- bottom: 0;
966
+ inset: 0;
884
967
  transform-origin: top left;
885
968
  pointer-events: none;
886
969
  }
@@ -891,7 +974,7 @@
891
974
  margin: 0;
892
975
  padding: 0;
893
976
  border: 0;
894
- border-radius: 3px;
977
+ border-radius: var(--radius-sm, 3px);
895
978
  outline: 1.5px dashed var(--zui-drawing-accent);
896
979
  outline-offset: 3px;
897
980
  background: color-mix(in srgb, var(--zui-drawing-surface) 85%, transparent);
@@ -919,7 +1002,7 @@
919
1002
  width: 1.375rem;
920
1003
  height: 1.375rem;
921
1004
  border-radius: var(--zui-drawing-control-radius);
922
- color: #1e1e1e;
1005
+ color: var(--zui-drawing-foreground);
923
1006
  opacity: 0.35;
924
1007
  cursor: ns-resize;
925
1008
  touch-action: none;
@@ -945,6 +1028,67 @@
945
1028
  filter: var(--zui-drawing-dark-filter);
946
1029
  }
947
1030
 
1031
+ /* Link bubble (link-bubble-plugin.tsx): under the link the caret is in */
1032
+ .zui-text-editor-link-bubble {
1033
+ position: absolute;
1034
+ z-index: 6;
1035
+ display: flex;
1036
+ flex-wrap: wrap;
1037
+ align-items: center;
1038
+ gap: 0.25rem;
1039
+ /* Never wider than the pane (`cqw` is .zui-text-editor-main's width) */
1040
+ max-width: calc(100cqw - 0.5rem);
1041
+ padding: 0.25rem;
1042
+ border: 1px solid var(--border, color-mix(in srgb, currentColor 12%, transparent));
1043
+ border-radius: var(--radius-md, 0.5rem);
1044
+ background: var(--popover, #ffffff);
1045
+ color: var(--popover-foreground, inherit);
1046
+ box-shadow: var(--shadow-2, 0 1px 4px rgba(0, 0, 0, 0.08));
1047
+ }
1048
+
1049
+ .zui-text-editor-link-input {
1050
+ flex: 1 1 auto;
1051
+ width: 14rem;
1052
+ min-width: 0;
1053
+ padding: 0.25rem 0.5rem;
1054
+ border: 1px solid var(--border, color-mix(in srgb, currentColor 15%, transparent));
1055
+ border-radius: var(--radius-sm, 0.375rem);
1056
+ background: var(--background, transparent);
1057
+ color: inherit;
1058
+ font: inherit;
1059
+ font-size: 0.8125rem;
1060
+ }
1061
+
1062
+ .zui-text-editor-link-input:focus-visible {
1063
+ outline: none;
1064
+ box-shadow: 0 0 0 2px var(--ring, var(--primary, currentColor));
1065
+ }
1066
+
1067
+ .zui-text-editor-link-input[aria-invalid='true'] {
1068
+ border-color: var(--danger, currentColor);
1069
+ }
1070
+
1071
+ .zui-text-editor-link-bubble .zui-text-editor-toolbar-button {
1072
+ width: auto;
1073
+ padding-inline: 0.5rem;
1074
+ font-size: 0.8125rem;
1075
+ }
1076
+
1077
+ /* Character counter (character-count.tsx); its limit announcements use .zui-text-editor-live */
1078
+ .zui-text-editor-count {
1079
+ position: sticky;
1080
+ bottom: 0;
1081
+ padding: 0.25rem var(--zui-text-editor-gutter, 2rem);
1082
+ text-align: end;
1083
+ font-size: 0.75rem;
1084
+ opacity: 0.6;
1085
+ }
1086
+
1087
+ .zui-text-editor-count.is-over {
1088
+ opacity: 1;
1089
+ color: var(--danger, currentColor);
1090
+ }
1091
+
948
1092
  .zui-text-editor-textarea {
949
1093
  width: 100%;
950
1094
  height: 100%;
@@ -963,55 +1107,57 @@
963
1107
 
964
1108
  /*
965
1109
  * Code block syntax highlighting. Each semantic token type (see
966
- * src/code/types.ts) gets one class and one color custom property, so a host
1110
+ * src/code/grammar.ts) gets one class and one color custom property, so a host
967
1111
  * theme restyles the palette by overriding `--zui-code-<type>` on `.zui-code`
968
1112
  * (or any ancestor) — light and dark defaults follow the `.dark` convention.
969
1113
  * Operators and punctuation inherit the text color on purpose: fewer colors,
970
- * calmer blocks.
1114
+ * calmer blocks. The defaults read `--zui-code-<type>-light` /
1115
+ * `--zui-code-<type>-dark` (defined by @zuilib/tokens) before their
1116
+ * built-in fallbacks.
971
1117
  */
972
1118
  .zui-code {
973
- --zui-code-comment: #6e7781;
974
- --zui-code-string: #0a3069;
975
- --zui-code-number: #0550ae;
976
- --zui-code-keyword: #cf222e;
977
- --zui-code-builtin: #6639ba;
978
- --zui-code-type: #953800;
979
- --zui-code-function: #8250df;
980
- --zui-code-property: #0550ae;
981
- --zui-code-variable: #953800;
982
- --zui-code-constant: #0550ae;
1119
+ --zui-code-comment: var(--zui-code-comment-light, #6e7781);
1120
+ --zui-code-string: var(--zui-code-string-light, #0a3069);
1121
+ --zui-code-number: var(--zui-code-number-light, #0550ae);
1122
+ --zui-code-keyword: var(--zui-code-keyword-light, #cf222e);
1123
+ --zui-code-builtin: var(--zui-code-builtin-light, #6639ba);
1124
+ --zui-code-type: var(--zui-code-type-light, #953800);
1125
+ --zui-code-function: var(--zui-code-function-light, #8250df);
1126
+ --zui-code-property: var(--zui-code-property-light, #0550ae);
1127
+ --zui-code-variable: var(--zui-code-variable-light, #953800);
1128
+ --zui-code-constant: var(--zui-code-constant-light, #0550ae);
983
1129
  --zui-code-operator: currentColor;
984
1130
  --zui-code-punctuation: currentColor;
985
- --zui-code-tag: #116329;
986
- --zui-code-attr: #0550ae;
987
- --zui-code-regex: #0a3069;
988
- --zui-code-meta: #6639ba;
989
- --zui-code-inserted: #116329;
990
- --zui-code-inserted-background: #dafbe1;
991
- --zui-code-deleted: #82071e;
992
- --zui-code-deleted-background: #ffebe9;
1131
+ --zui-code-tag: var(--zui-code-tag-light, #116329);
1132
+ --zui-code-attr: var(--zui-code-attr-light, #0550ae);
1133
+ --zui-code-regex: var(--zui-code-regex-light, #0a3069);
1134
+ --zui-code-meta: var(--zui-code-meta-light, #6639ba);
1135
+ --zui-code-inserted: var(--zui-code-inserted-light, #116329);
1136
+ --zui-code-inserted-background: var(--zui-code-inserted-background-light, #dafbe1);
1137
+ --zui-code-deleted: var(--zui-code-deleted-light, #82071e);
1138
+ --zui-code-deleted-background: var(--zui-code-deleted-background-light, #ffebe9);
993
1139
  tab-size: 2;
994
1140
  }
995
1141
 
996
1142
  .dark .zui-code {
997
- --zui-code-comment: #8b949e;
998
- --zui-code-string: #a5d6ff;
999
- --zui-code-number: #79c0ff;
1000
- --zui-code-keyword: #ff7b72;
1001
- --zui-code-builtin: #d2a8ff;
1002
- --zui-code-type: #ffa657;
1003
- --zui-code-function: #d2a8ff;
1004
- --zui-code-property: #79c0ff;
1005
- --zui-code-variable: #ffa657;
1006
- --zui-code-constant: #79c0ff;
1007
- --zui-code-tag: #7ee787;
1008
- --zui-code-attr: #79c0ff;
1009
- --zui-code-regex: #a5d6ff;
1010
- --zui-code-meta: #d2a8ff;
1011
- --zui-code-inserted: #7ee787;
1012
- --zui-code-inserted-background: rgba(63, 185, 80, 0.15);
1013
- --zui-code-deleted: #ffa198;
1014
- --zui-code-deleted-background: rgba(248, 81, 73, 0.15);
1143
+ --zui-code-comment: var(--zui-code-comment-dark, #8b949e);
1144
+ --zui-code-string: var(--zui-code-string-dark, #a5d6ff);
1145
+ --zui-code-number: var(--zui-code-number-dark, #79c0ff);
1146
+ --zui-code-keyword: var(--zui-code-keyword-dark, #ff7b72);
1147
+ --zui-code-builtin: var(--zui-code-builtin-dark, #d2a8ff);
1148
+ --zui-code-type: var(--zui-code-type-dark, #ffa657);
1149
+ --zui-code-function: var(--zui-code-function-dark, #d2a8ff);
1150
+ --zui-code-property: var(--zui-code-property-dark, #79c0ff);
1151
+ --zui-code-variable: var(--zui-code-variable-dark, #ffa657);
1152
+ --zui-code-constant: var(--zui-code-constant-dark, #79c0ff);
1153
+ --zui-code-tag: var(--zui-code-tag-dark, #7ee787);
1154
+ --zui-code-attr: var(--zui-code-attr-dark, #79c0ff);
1155
+ --zui-code-regex: var(--zui-code-regex-dark, #a5d6ff);
1156
+ --zui-code-meta: var(--zui-code-meta-dark, #d2a8ff);
1157
+ --zui-code-inserted: var(--zui-code-inserted-dark, #7ee787);
1158
+ --zui-code-inserted-background: var(--zui-code-inserted-background-dark, rgba(63, 185, 80, 0.15));
1159
+ --zui-code-deleted: var(--zui-code-deleted-dark, #ffa198);
1160
+ --zui-code-deleted-background: var(--zui-code-deleted-background-dark, rgba(248, 81, 73, 0.15));
1015
1161
  }
1016
1162
 
1017
1163
  .zui-token-comment { color: var(--zui-code-comment); font-style: italic; }
@@ -1049,3 +1195,373 @@
1049
1195
  font-variation-settings: 'CASL' 1, 'CRSV' 1, 'MONO' 0, 'wght' 440, 'slnt' -3;
1050
1196
  letter-spacing: 0.01em;
1051
1197
  }
1198
+
1199
+ /*
1200
+ * Small containers. `.zui-text-editor` and `.zui-text-editor-main` are
1201
+ * inline-size containers, so these rules follow the editor's own width,
1202
+ * not the viewport. Nothing here applies to a pane wider than 40rem.
1203
+ */
1204
+ @container (max-width: 40rem) {
1205
+ /* Toolbar: order matters, so it scrolls sideways (snapping) rather than wrapping */
1206
+ .zui-text-editor-toolbar {
1207
+ overflow-x: auto;
1208
+ overflow-y: hidden;
1209
+ scrollbar-width: none;
1210
+ scroll-snap-type: x proximity;
1211
+ scroll-padding-inline: 0.75rem;
1212
+ overscroll-behavior-x: contain;
1213
+ }
1214
+
1215
+ .zui-text-editor-toolbar::-webkit-scrollbar {
1216
+ display: none;
1217
+ }
1218
+
1219
+ .zui-text-editor-toolbar > * {
1220
+ scroll-snap-align: start;
1221
+ }
1222
+
1223
+ /* Outline: a collapsible strip above the document instead of a side column */
1224
+ .zui-text-editor-body {
1225
+ flex-direction: column;
1226
+ }
1227
+
1228
+ .zui-text-editor-outline {
1229
+ order: -1;
1230
+ position: static;
1231
+ align-self: stretch;
1232
+ width: auto;
1233
+ max-height: 30dvh;
1234
+ padding: 0.5rem 0.75rem;
1235
+ border-bottom: 1px solid color-mix(in srgb, currentColor 12%, transparent);
1236
+ }
1237
+
1238
+ .zui-text-editor-outline-item,
1239
+ .zui-text-editor-outline-empty {
1240
+ font-size: 0.875rem;
1241
+ }
1242
+
1243
+ /*
1244
+ * Tables: the table becomes its own horizontal scroll container (its
1245
+ * rows form an anonymous table inside) and every column keeps a
1246
+ * readable minimum, so a wide table scrolls within the pane instead of
1247
+ * squeezing or widening the page. `!important` beats the inline
1248
+ * `width: auto` of a content-width table.
1249
+ */
1250
+ .zui-table {
1251
+ display: block;
1252
+ width: fit-content !important;
1253
+ overflow: auto hidden;
1254
+ scrollbar-width: thin;
1255
+ }
1256
+
1257
+ .zui-table-cell {
1258
+ min-width: 6rem;
1259
+ }
1260
+
1261
+ /* Display sizes step down with the pane */
1262
+ .zui-text-editor-content .zui-heading-1 {
1263
+ font-size: 2rem;
1264
+ line-height: 1.15;
1265
+ }
1266
+
1267
+ .zui-text-editor-content .zui-heading-2 {
1268
+ font-size: 1.625rem;
1269
+ line-height: 1.2;
1270
+ }
1271
+
1272
+ .zui-text-editor-content .zui-heading-3 {
1273
+ font-size: 1.375rem;
1274
+ line-height: 1.25;
1275
+ }
1276
+ }
1277
+
1278
+ /*
1279
+ * Coarse pointers (touch). Targets grow to 44px — visibly for buttons,
1280
+ * as an invisible hit area (::before) for the small controls whose
1281
+ * geometry the layout depends on — and hover-revealed controls are shown
1282
+ * by default. Fine-pointer geometry is unchanged.
1283
+ */
1284
+ @media (pointer: coarse) {
1285
+ .zui-text-editor-toolbar-button,
1286
+ .zui-drawing-tool,
1287
+ .zui-text-editor-outline-toggle {
1288
+ min-width: 2.75rem;
1289
+ min-height: 2.75rem;
1290
+ }
1291
+
1292
+ .zui-drawing-popover-item,
1293
+ .zui-text-editor-mention-option,
1294
+ .zui-text-editor-link-input {
1295
+ min-height: 2.75rem;
1296
+ }
1297
+
1298
+ .zui-text-editor-mention-option {
1299
+ align-items: center;
1300
+ }
1301
+
1302
+ .zui-text-editor-outline-item {
1303
+ padding-block: 0.8125rem;
1304
+ }
1305
+
1306
+ .zui-text-editor-fold {
1307
+ opacity: 0.45;
1308
+ }
1309
+
1310
+ .zui-table-rail-handle,
1311
+ .zui-table-rail-handle.is-resting {
1312
+ opacity: 1;
1313
+ }
1314
+
1315
+ .zui-drawing-swatch {
1316
+ position: relative;
1317
+ width: 1.5rem;
1318
+ height: 1.5rem;
1319
+ margin: 0 0.25rem;
1320
+ }
1321
+
1322
+ .zui-table-rail-handle::before,
1323
+ .zui-text-editor-fold::before,
1324
+ .zui-drawing-swatch::before,
1325
+ .zui-drawing-resize::before {
1326
+ content: '';
1327
+ position: absolute;
1328
+ inset: -0.75rem;
1329
+ }
1330
+
1331
+ .zui-text-editor-fold::before {
1332
+ inset: -0.8125rem;
1333
+ }
1334
+
1335
+ .zui-drawing-swatch::before {
1336
+ inset: -0.625rem;
1337
+ }
1338
+ }
1339
+
1340
+ /* Phones: editable text renders at 16px or more, so iOS does not zoom on focus */
1341
+ @media (max-width: 40rem) {
1342
+ .zui-text-editor-content,
1343
+ .zui-text-editor-textarea,
1344
+ .zui-text-editor-link-input,
1345
+ .zui-drawing-popover-item {
1346
+ font-size: max(16px, 1em);
1347
+ }
1348
+ }
1349
+
1350
+ /*
1351
+ * First-party plugins (mentions, comments, images). Appended to styles.css
1352
+ * at build time; every color comes from the host theme tokens.
1353
+ */
1354
+
1355
+ /* Mentions (MentionNode + MentionsPlugin) */
1356
+ .zui-mention {
1357
+ display: inline;
1358
+ padding-inline: 0.2em;
1359
+ border-radius: var(--radius-sm, 0.375rem);
1360
+ background: color-mix(in srgb, var(--primary, currentColor) 12%, transparent);
1361
+ color: var(--primary, inherit);
1362
+ font-weight: 500;
1363
+ white-space: nowrap;
1364
+ }
1365
+
1366
+ .zui-text-editor-mentions {
1367
+ position: absolute;
1368
+ z-index: 6;
1369
+ min-width: min(12rem, calc(100cqw - 0.5rem));
1370
+ /* Inside the pane (`cqw` is .zui-text-editor-main's width) and above a soft keyboard */
1371
+ max-width: min(20rem, calc(100cqw - 0.5rem));
1372
+ max-height: min(18rem, 50dvh);
1373
+ overflow-y: auto;
1374
+ padding: 0.25rem;
1375
+ border: 1px solid var(--border, color-mix(in srgb, currentColor 12%, transparent));
1376
+ border-radius: var(--radius-md, 0.5rem);
1377
+ background: var(--popover, var(--background, #ffffff));
1378
+ color: var(--popover-foreground, inherit);
1379
+ box-shadow: var(--shadow-2, 0 1px 4px rgba(0, 0, 0, 0.08));
1380
+ font-size: 0.875rem;
1381
+ }
1382
+
1383
+ .zui-text-editor-mention-option {
1384
+ display: flex;
1385
+ align-items: baseline;
1386
+ gap: 0.5rem;
1387
+ padding: 0.375rem 0.5rem;
1388
+ border-radius: var(--radius-sm, 0.375rem);
1389
+ cursor: pointer;
1390
+ }
1391
+
1392
+ .zui-text-editor-mention-option.is-active {
1393
+ background: var(--accent, color-mix(in srgb, currentColor 8%, transparent));
1394
+ color: var(--accent-foreground, inherit);
1395
+ }
1396
+
1397
+ .zui-text-editor-mention-hint,
1398
+ .zui-text-editor-mention-empty {
1399
+ color: var(--muted-foreground, inherit);
1400
+ opacity: 0.8;
1401
+ font-size: 0.8125rem;
1402
+ }
1403
+
1404
+ .zui-text-editor-mention-empty {
1405
+ padding: 0.375rem 0.5rem;
1406
+ }
1407
+
1408
+ /* Comments (MarkNode + CommentsPlugin) */
1409
+ .zui-comment {
1410
+ background: color-mix(in srgb, var(--zui-comment-color, var(--warning, #eab308)) 28%, transparent);
1411
+ border-block-end: 2px solid color-mix(in srgb, var(--zui-comment-color, var(--warning, #eab308)) 70%, transparent);
1412
+ transition: background-color 120ms ease;
1413
+ }
1414
+
1415
+ .zui-comment.is-resolved {
1416
+ background: transparent;
1417
+ border-block-end-color: color-mix(in srgb, currentColor 20%, transparent);
1418
+ }
1419
+
1420
+ .zui-comment-overlap {
1421
+ background: color-mix(in srgb, var(--zui-comment-color, var(--warning, #eab308)) 45%, transparent);
1422
+ }
1423
+
1424
+ .zui-text-editor-comment-add {
1425
+ position: absolute;
1426
+ z-index: 6;
1427
+ }
1428
+
1429
+ .zui-text-editor-comment-add .zui-text-editor-toolbar-button {
1430
+ width: auto;
1431
+ gap: 0.375rem;
1432
+ padding-inline: 0.5rem;
1433
+ border: 1px solid var(--border, color-mix(in srgb, currentColor 12%, transparent));
1434
+ border-radius: var(--radius-md, 0.5rem);
1435
+ background: var(--popover, var(--background, #ffffff));
1436
+ color: var(--popover-foreground, inherit);
1437
+ box-shadow: var(--shadow-2, 0 1px 4px rgba(0, 0, 0, 0.08));
1438
+ font-size: 0.8125rem;
1439
+ }
1440
+
1441
+ .zui-text-editor-comments {
1442
+ position: absolute;
1443
+ z-index: 6;
1444
+ display: flex;
1445
+ flex-direction: column;
1446
+ gap: 0.375rem;
1447
+ min-width: min(14rem, calc(100cqw - 0.5rem));
1448
+ max-width: min(22rem, calc(100cqw - 0.5rem));
1449
+ padding: 0.5rem;
1450
+ border: 1px solid var(--border, color-mix(in srgb, currentColor 12%, transparent));
1451
+ border-radius: var(--radius-md, 0.5rem);
1452
+ background: var(--popover, var(--background, #ffffff));
1453
+ color: var(--popover-foreground, inherit);
1454
+ box-shadow: var(--shadow-2, 0 1px 4px rgba(0, 0, 0, 0.08));
1455
+ font-size: 0.8125rem;
1456
+ }
1457
+
1458
+ .zui-text-editor-comment-quote {
1459
+ overflow: hidden;
1460
+ text-overflow: ellipsis;
1461
+ white-space: nowrap;
1462
+ font-style: italic;
1463
+ color: var(--muted-foreground, inherit);
1464
+ }
1465
+
1466
+ .zui-text-editor-comment.is-resolved .zui-text-editor-comment-quote {
1467
+ text-decoration: line-through;
1468
+ }
1469
+
1470
+ .zui-text-editor-comment-state {
1471
+ font-style: normal;
1472
+ font-weight: 500;
1473
+ }
1474
+
1475
+ .zui-text-editor-comment-actions {
1476
+ display: flex;
1477
+ gap: 0.25rem;
1478
+ margin-block-start: 0.25rem;
1479
+ }
1480
+
1481
+ .zui-text-editor-comment-actions .zui-text-editor-toolbar-button {
1482
+ width: auto;
1483
+ padding-inline: 0.5rem;
1484
+ font-size: 0.8125rem;
1485
+ }
1486
+
1487
+ /* Images (ImageNode + ImagesPlugin) */
1488
+ .zui-image {
1489
+ display: inline-block;
1490
+ max-width: 100%;
1491
+ vertical-align: middle;
1492
+ }
1493
+
1494
+ .zui-paragraph > .zui-image:only-child {
1495
+ display: block;
1496
+ margin-block: 0.5rem;
1497
+ }
1498
+
1499
+ .zui-image-frame {
1500
+ position: relative;
1501
+ display: inline-block;
1502
+ max-width: 100%;
1503
+ border-radius: var(--radius-md, 0.5rem);
1504
+ }
1505
+
1506
+ .zui-image-frame img {
1507
+ display: block;
1508
+ max-width: 100%;
1509
+ height: auto;
1510
+ border-radius: inherit;
1511
+ }
1512
+
1513
+ .zui-image-frame.is-selected {
1514
+ box-shadow: 0 0 0 2px var(--ring, var(--primary, currentColor));
1515
+ }
1516
+
1517
+ .zui-image-alt {
1518
+ position: absolute;
1519
+ inset-inline-start: 0;
1520
+ inset-block-start: calc(100% + 0.25rem);
1521
+ z-index: 6;
1522
+ display: flex;
1523
+ max-width: calc(100cqw - 1rem);
1524
+ padding: 0.25rem;
1525
+ border: 1px solid var(--border, color-mix(in srgb, currentColor 12%, transparent));
1526
+ border-radius: var(--radius-md, 0.5rem);
1527
+ background: var(--popover, var(--background, #ffffff));
1528
+ color: var(--popover-foreground, inherit);
1529
+ box-shadow: var(--shadow-2, 0 1px 4px rgba(0, 0, 0, 0.08));
1530
+ }
1531
+
1532
+ .zui-image-pending {
1533
+ display: inline-flex;
1534
+ align-items: center;
1535
+ justify-content: center;
1536
+ width: 8rem;
1537
+ height: 5rem;
1538
+ border: 1px dashed var(--border, color-mix(in srgb, currentColor 25%, transparent));
1539
+ border-radius: var(--radius-md, 0.5rem);
1540
+ background: var(--muted, color-mix(in srgb, currentColor 5%, transparent));
1541
+ vertical-align: middle;
1542
+ }
1543
+
1544
+ .zui-image-spinner {
1545
+ width: 1.25rem;
1546
+ height: 1.25rem;
1547
+ border: 2px solid color-mix(in srgb, currentColor 20%, transparent);
1548
+ border-block-start-color: var(--primary, currentColor);
1549
+ border-radius: var(--radius-full, 9999px);
1550
+ animation: zui-image-spin 0.8s linear infinite;
1551
+ }
1552
+
1553
+ @keyframes zui-image-spin {
1554
+ to {
1555
+ transform: rotate(360deg);
1556
+ }
1557
+ }
1558
+
1559
+ @media (prefers-reduced-motion: reduce) {
1560
+ .zui-comment {
1561
+ transition: none;
1562
+ }
1563
+ .zui-image-spinner {
1564
+ animation: none;
1565
+ border-block-start-color: currentColor;
1566
+ }
1567
+ }