@stsdti/approvable-vue 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.
@@ -0,0 +1,1171 @@
1
+ /* ==========================================================================
2
+ laravel-approvable — the Approvable theme
3
+
4
+ One stylesheet, light and dark. Every colour, radius and shadow is a custom
5
+ property declared on `.approvable`-rooted blocks, so an application that
6
+ wants a different accent overrides the property rather than the rule.
7
+
8
+ Dark mode follows the viewer's system setting, and `[data-theme]` (or a
9
+ `.dark` / `.light` class) on an ancestor wins over it in both directions —
10
+ an application with its own theme toggle stamps one of those and the panel
11
+ follows.
12
+ ========================================================================== */
13
+
14
+ /* --------------------------------------------------------------------------
15
+ Tokens
16
+ -------------------------------------------------------------------------- */
17
+ .approvable,
18
+ .approvable-actions,
19
+ .approvable-select,
20
+ .approvable-timeline,
21
+ .approvable-dialog-backdrop {
22
+ --approvable-font: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
23
+ --approvable-radius: 18px;
24
+ --approvable-radius-sm: 10px;
25
+
26
+ --approvable-surface: #fff;
27
+ --approvable-surface-raised: #fff;
28
+ --approvable-border: transparent;
29
+ --approvable-divider: oklch(0.95 0.005 260);
30
+
31
+ --approvable-ink: oklch(0.28 0.015 260);
32
+ --approvable-ink-soft: oklch(0.5 0.015 260);
33
+ --approvable-muted: oklch(0.62 0.012 260);
34
+ --approvable-muted-soft: oklch(0.66 0.012 260);
35
+
36
+ --approvable-accent: oklch(0.55 0.16 255);
37
+ --approvable-accent-ink: oklch(0.5 0.12 255);
38
+ --approvable-accent-tint: oklch(0.95 0.04 250);
39
+ --approvable-accent-tint-ink: oklch(0.45 0.14 255);
40
+ --approvable-accent-on: #fff;
41
+ --approvable-accent-dashed: oklch(0.68 0.11 255);
42
+
43
+ --approvable-success-tint: oklch(0.92 0.07 150);
44
+ --approvable-success-ink: oklch(0.46 0.14 150);
45
+ --approvable-success-text: oklch(0.5 0.11 150);
46
+
47
+ --approvable-danger-tint: oklch(0.94 0.05 25);
48
+ --approvable-danger-ink: oklch(0.5 0.16 25);
49
+ --approvable-danger-text: oklch(0.5 0.16 25);
50
+
51
+ --approvable-warn-tint: oklch(0.94 0.06 75);
52
+ --approvable-warn-ink: oklch(0.5 0.13 65);
53
+
54
+ --approvable-neutral-tint: oklch(0.955 0.004 260);
55
+ --approvable-neutral-ink: oklch(0.62 0.01 260);
56
+
57
+ --approvable-control: #fff;
58
+ --approvable-control-border: oklch(0.88 0.012 260);
59
+ --approvable-control-ink: oklch(0.45 0.02 260);
60
+ --approvable-control-hover: oklch(0.97 0.004 260);
61
+
62
+ --approvable-skeleton: oklch(0.94 0.006 260);
63
+ --approvable-skeleton-soft: oklch(0.955 0.005 260);
64
+
65
+ --approvable-shadow: rgba(0, 0, 0, 0.16) 1px 1px 7px;
66
+ --approvable-shadow-lg: 0 30px 70px -20px oklch(0.3 0.03 260 / 0.5);
67
+ --approvable-scrim: oklch(0.25 0.015 260 / 0.42);
68
+ }
69
+
70
+ /* Dark — the system preference first. Scoped so that an application which has
71
+ explicitly asked for light does not get dark anyway. */
72
+ @media (prefers-color-scheme: dark) {
73
+ :root:not([data-theme="light"]):not(.light) .approvable,
74
+ :root:not([data-theme="light"]):not(.light) .approvable-actions,
75
+ :root:not([data-theme="light"]):not(.light) .approvable-select,
76
+ :root:not([data-theme="light"]):not(.light) .approvable-timeline,
77
+ :root:not([data-theme="light"]):not(.light) .approvable-dialog-backdrop {
78
+ color-scheme: dark;
79
+
80
+ --approvable-surface: oklch(0.235 0.012 260);
81
+ --approvable-surface-raised: oklch(0.26 0.012 260);
82
+ --approvable-border: oklch(0.3 0.012 260);
83
+ --approvable-divider: oklch(0.3 0.012 260);
84
+
85
+ --approvable-ink: oklch(0.93 0.005 260);
86
+ --approvable-ink-soft: oklch(0.74 0.008 260);
87
+ --approvable-muted: oklch(0.63 0.01 260);
88
+ --approvable-muted-soft: oklch(0.58 0.01 260);
89
+
90
+ --approvable-accent: oklch(0.62 0.16 258);
91
+ --approvable-accent-ink: oklch(0.85 0.09 255);
92
+ --approvable-accent-tint: oklch(0.32 0.065 258);
93
+ --approvable-accent-tint-ink: oklch(0.85 0.09 255);
94
+ --approvable-accent-dashed: oklch(0.55 0.11 258);
95
+
96
+ --approvable-success-tint: oklch(0.33 0.075 152);
97
+ --approvable-success-ink: oklch(0.82 0.13 150);
98
+ --approvable-success-text: oklch(0.82 0.13 150);
99
+
100
+ --approvable-danger-tint: oklch(0.34 0.085 25);
101
+ --approvable-danger-ink: oklch(0.78 0.14 25);
102
+ --approvable-danger-text: oklch(0.78 0.14 25);
103
+
104
+ --approvable-warn-tint: oklch(0.34 0.075 75);
105
+ --approvable-warn-ink: oklch(0.83 0.13 80);
106
+
107
+ --approvable-neutral-tint: oklch(0.28 0.012 260);
108
+ --approvable-neutral-ink: oklch(0.63 0.01 260);
109
+
110
+ --approvable-control: oklch(0.28 0.012 260);
111
+ --approvable-control-border: oklch(0.38 0.012 260);
112
+ --approvable-control-ink: oklch(0.74 0.008 260);
113
+ --approvable-control-hover: oklch(0.32 0.012 260);
114
+
115
+ --approvable-skeleton: oklch(0.29 0.01 260);
116
+ --approvable-skeleton-soft: oklch(0.255 0.01 260);
117
+
118
+ --approvable-shadow: 0 1px 2px oklch(0 0 0 / 0.3), 0 16px 34px -22px oklch(0 0 0 / 0.7);
119
+ --approvable-shadow-lg: 0 30px 70px -20px oklch(0 0 0 / 0.8);
120
+ --approvable-scrim: oklch(0.12 0.01 262 / 0.62);
121
+ }
122
+ }
123
+
124
+ /* …then an explicit choice, which wins in both directions. */
125
+ [data-theme="dark"] .approvable,
126
+ [data-theme="dark"] .approvable-actions,
127
+ [data-theme="dark"] .approvable-select,
128
+ [data-theme="dark"] .approvable-timeline,
129
+ [data-theme="dark"] .approvable-dialog-backdrop,
130
+ .dark .approvable,
131
+ .dark .approvable-actions,
132
+ .dark .approvable-select,
133
+ .dark .approvable-timeline,
134
+ .dark .approvable-dialog-backdrop {
135
+ color-scheme: dark;
136
+
137
+ --approvable-surface: oklch(0.235 0.012 260);
138
+ --approvable-surface-raised: oklch(0.26 0.012 260);
139
+ --approvable-border: oklch(0.3 0.012 260);
140
+ --approvable-divider: oklch(0.3 0.012 260);
141
+
142
+ --approvable-ink: oklch(0.93 0.005 260);
143
+ --approvable-ink-soft: oklch(0.74 0.008 260);
144
+ --approvable-muted: oklch(0.63 0.01 260);
145
+ --approvable-muted-soft: oklch(0.58 0.01 260);
146
+
147
+ --approvable-accent: oklch(0.62 0.16 258);
148
+ --approvable-accent-ink: oklch(0.85 0.09 255);
149
+ --approvable-accent-tint: oklch(0.32 0.065 258);
150
+ --approvable-accent-tint-ink: oklch(0.85 0.09 255);
151
+ --approvable-accent-dashed: oklch(0.55 0.11 258);
152
+
153
+ --approvable-success-tint: oklch(0.33 0.075 152);
154
+ --approvable-success-ink: oklch(0.82 0.13 150);
155
+ --approvable-success-text: oklch(0.82 0.13 150);
156
+
157
+ --approvable-danger-tint: oklch(0.34 0.085 25);
158
+ --approvable-danger-ink: oklch(0.78 0.14 25);
159
+ --approvable-danger-text: oklch(0.78 0.14 25);
160
+
161
+ --approvable-warn-tint: oklch(0.34 0.075 75);
162
+ --approvable-warn-ink: oklch(0.83 0.13 80);
163
+
164
+ --approvable-neutral-tint: oklch(0.28 0.012 260);
165
+ --approvable-neutral-ink: oklch(0.63 0.01 260);
166
+
167
+ --approvable-control: oklch(0.28 0.012 260);
168
+ --approvable-control-border: oklch(0.38 0.012 260);
169
+ --approvable-control-ink: oklch(0.74 0.008 260);
170
+ --approvable-control-hover: oklch(0.32 0.012 260);
171
+
172
+ --approvable-skeleton: oklch(0.29 0.01 260);
173
+ --approvable-skeleton-soft: oklch(0.255 0.01 260);
174
+
175
+ --approvable-shadow: 0 1px 2px oklch(0 0 0 / 0.3), 0 16px 34px -22px oklch(0 0 0 / 0.7);
176
+ --approvable-shadow-lg: 0 30px 70px -20px oklch(0 0 0 / 0.8);
177
+ --approvable-scrim: oklch(0.12 0.01 262 / 0.62);
178
+ }
179
+
180
+ /* --------------------------------------------------------------------------
181
+ Reset, scoped to what this package renders
182
+ -------------------------------------------------------------------------- */
183
+ .approvable,
184
+ .approvable *,
185
+ .approvable-actions,
186
+ .approvable-actions *,
187
+ .approvable-select,
188
+ .approvable-select *,
189
+ .approvable-timeline,
190
+ .approvable-timeline *,
191
+ .approvable-dialog-backdrop,
192
+ .approvable-dialog-backdrop * {
193
+ box-sizing: border-box;
194
+ }
195
+
196
+ .approvable,
197
+ .approvable-actions,
198
+ .approvable-select,
199
+ .approvable-timeline,
200
+ .approvable-dialog {
201
+ font-family: var(--approvable-font);
202
+ color: var(--approvable-ink);
203
+ }
204
+
205
+ /* --------------------------------------------------------------------------
206
+ Panel
207
+ -------------------------------------------------------------------------- */
208
+ .approvable {
209
+ position: relative;
210
+ width: 100%;
211
+ background: var(--approvable-surface);
212
+ border: 1px solid var(--approvable-border);
213
+ border-radius: var(--approvable-radius);
214
+ box-shadow: var(--approvable-shadow);
215
+ overflow: hidden;
216
+ }
217
+
218
+ .approvable-sr-only {
219
+ position: absolute;
220
+ width: 1px;
221
+ height: 1px;
222
+ padding: 0;
223
+ margin: -1px;
224
+ overflow: hidden;
225
+ clip-path: inset(50%);
226
+ white-space: nowrap;
227
+ }
228
+
229
+ /* --------------------------------------------------------------------------
230
+ Header
231
+ -------------------------------------------------------------------------- */
232
+ .approvable-header {
233
+ display: flex;
234
+ align-items: center;
235
+ gap: 9px;
236
+ padding: 12px 15px;
237
+ }
238
+
239
+ .approvable-updated-at {
240
+ flex: 1;
241
+ min-width: 0;
242
+ font-size: 11.5px;
243
+ font-weight: 400;
244
+ line-height: 1.3;
245
+ color: var(--approvable-muted);
246
+ white-space: nowrap;
247
+ overflow: hidden;
248
+ text-overflow: ellipsis;
249
+ }
250
+
251
+ .approvable-history-btn {
252
+ flex: none;
253
+ padding: 0;
254
+ border: 0;
255
+ background: none;
256
+ font-family: inherit;
257
+ font-size: 11.5px;
258
+ font-weight: 600;
259
+ line-height: 1;
260
+ color: var(--approvable-accent-ink);
261
+ cursor: pointer;
262
+ }
263
+
264
+ .approvable-history-btn:hover {
265
+ text-decoration: underline;
266
+ text-underline-offset: 3px;
267
+ }
268
+
269
+ /* --------------------------------------------------------------------------
270
+ Status badge — the header pill, and anywhere else a status is named
271
+ -------------------------------------------------------------------------- */
272
+ .approvable-badge {
273
+ display: inline-flex;
274
+ align-items: center;
275
+ gap: 6px;
276
+ flex: none;
277
+ padding: 4px 10px;
278
+ border-radius: 999px;
279
+ background: var(--approvable-neutral-tint);
280
+ color: var(--approvable-neutral-ink);
281
+ font-size: 11px;
282
+ font-weight: 600;
283
+ line-height: 1.2;
284
+ white-space: nowrap;
285
+ }
286
+
287
+ /* The pulsing dot an in-flight approval carries. A pseudo-element, so the
288
+ badge stays a single text node in the markup. */
289
+ .approvable-badge--waiting,
290
+ .approvable-badge--draft {
291
+ padding-left: 8px;
292
+ }
293
+
294
+ .approvable-badge--waiting {
295
+ background: var(--approvable-accent-tint);
296
+ color: var(--approvable-accent-tint-ink);
297
+ }
298
+
299
+ .approvable-badge--draft {
300
+ background: var(--approvable-warn-tint);
301
+ color: var(--approvable-warn-ink);
302
+ }
303
+
304
+ .approvable-badge--waiting::before,
305
+ .approvable-badge--draft::before {
306
+ content: "";
307
+ width: 6px;
308
+ height: 6px;
309
+ flex: none;
310
+ border-radius: 999px;
311
+ background: currentColor;
312
+ animation: approvable-pulse 1.6s ease-in-out infinite;
313
+ }
314
+
315
+ .approvable-badge--waiting::before {
316
+ background: var(--approvable-accent);
317
+ }
318
+
319
+ .approvable-badge--success {
320
+ background: var(--approvable-success-tint);
321
+ color: var(--approvable-success-ink);
322
+ }
323
+
324
+ .approvable-badge--danger {
325
+ background: var(--approvable-danger-tint);
326
+ color: var(--approvable-danger-ink);
327
+ }
328
+
329
+ .approvable-badge--muted,
330
+ .approvable-badge--empty {
331
+ background: var(--approvable-neutral-tint);
332
+ color: var(--approvable-neutral-ink);
333
+ }
334
+
335
+ @keyframes approvable-pulse {
336
+ 0%, 100% { opacity: 0.35; transform: scale(0.85); }
337
+ 50% { opacity: 1; transform: scale(1); }
338
+ }
339
+
340
+ /* --------------------------------------------------------------------------
341
+ Step rows
342
+ -------------------------------------------------------------------------- */
343
+ .approvable-steps {
344
+ list-style: none;
345
+ margin: 0;
346
+ padding: 0 15px;
347
+ }
348
+
349
+ /* `isDirectionRow` lays the trail out horizontally. Row internals are
350
+ unchanged; only the flow is. */
351
+ .approvable-steps--row {
352
+ display: flex;
353
+ gap: 18px;
354
+ overflow-x: auto;
355
+ }
356
+
357
+ .approvable-steps--row .approvable-step {
358
+ flex: 0 0 auto;
359
+ max-width: 320px;
360
+ border-top: 0;
361
+ }
362
+
363
+ .approvable-step {
364
+ display: flex;
365
+ gap: 10px;
366
+ padding: 7px 0;
367
+ border-top: 1px solid var(--approvable-divider);
368
+ }
369
+
370
+ .approvable-step--single-line {
371
+ align-items: center;
372
+ min-height: 32px;
373
+ }
374
+
375
+ /* A step nobody can act on yet reads as background information. */
376
+ .approvable-step--pending {
377
+ opacity: 0.72;
378
+ }
379
+
380
+ .approvable-step-disc {
381
+ flex: none;
382
+ width: 18px;
383
+ height: 18px;
384
+ display: grid;
385
+ place-items: center;
386
+ border-radius: 999px;
387
+ background: var(--approvable-neutral-tint);
388
+ color: var(--approvable-neutral-ink);
389
+ }
390
+
391
+ /* On a multi-line row the disc sits level with the first line of text. */
392
+ .approvable-step:not(.approvable-step--single-line) > .approvable-step-disc {
393
+ margin-top: 7px;
394
+ }
395
+
396
+ .approvable-step-disc--success {
397
+ background: var(--approvable-success-tint);
398
+ color: var(--approvable-success-ink);
399
+ }
400
+
401
+ .approvable-step-disc--danger {
402
+ background: var(--approvable-danger-tint);
403
+ color: var(--approvable-danger-ink);
404
+ }
405
+
406
+ /* The step being waited on: a solid ring, no glyph. */
407
+ .approvable-step-disc--ring {
408
+ background: var(--approvable-surface);
409
+ border: 3px solid var(--approvable-accent);
410
+ }
411
+
412
+ /* A step further down the trail: a dashed outline. */
413
+ .approvable-step-disc--pending {
414
+ background: none;
415
+ border: 2px dashed var(--approvable-accent-dashed);
416
+ }
417
+
418
+ .approvable-step-disc svg {
419
+ width: 11px;
420
+ height: 11px;
421
+ display: block;
422
+ }
423
+
424
+ .approvable-step-body {
425
+ flex: 1;
426
+ min-width: 0;
427
+ }
428
+
429
+ /* Wraps rather than overflowing: in a narrow column the buttons drop to their
430
+ own line instead of being clipped by the panel. */
431
+ .approvable-step-line {
432
+ display: flex;
433
+ flex-wrap: wrap;
434
+ align-items: center;
435
+ gap: 8px;
436
+ min-height: 32px;
437
+ }
438
+
439
+ .approvable-step-name {
440
+ flex: none;
441
+ font-size: 13px;
442
+ font-weight: 500;
443
+ line-height: 1.3;
444
+ }
445
+
446
+ .approvable-step--active .approvable-step-name {
447
+ font-weight: 600;
448
+ }
449
+
450
+ /* The quiet half of the name pair. It takes the slack in the row and clips
451
+ before the status does, because the status is the shorter, load-bearing one. */
452
+ /* A basis rather than a bare `flex: 1`, so the role clips to an ellipsis
453
+ before it collapses to nothing. */
454
+ .approvable-step-role {
455
+ flex: 1 1 80px;
456
+ min-width: 0;
457
+ font-size: 11.5px;
458
+ font-weight: 400;
459
+ line-height: 1.3;
460
+ color: var(--approvable-muted);
461
+ white-space: nowrap;
462
+ overflow: hidden;
463
+ text-overflow: ellipsis;
464
+ }
465
+
466
+ /* No role on this step: the spacer still has to push the status right. */
467
+ .approvable-step-spacer {
468
+ flex: 1;
469
+ min-width: 0;
470
+ }
471
+
472
+ .approvable-step-status {
473
+ display: flex;
474
+ align-items: baseline;
475
+ gap: 5px;
476
+ flex: none;
477
+ font-size: 11px;
478
+ font-weight: 400;
479
+ line-height: 1.3;
480
+ color: var(--approvable-muted-soft);
481
+ }
482
+
483
+ .approvable-step--success .approvable-step-status {
484
+ color: var(--approvable-success-text);
485
+ }
486
+
487
+ .approvable-step--danger .approvable-step-status {
488
+ color: var(--approvable-danger-text);
489
+ }
490
+
491
+ .approvable-step-date {
492
+ color: var(--approvable-muted-soft);
493
+ }
494
+
495
+ .approvable-step-comment {
496
+ margin: 0 0 3px;
497
+ font-size: 12px;
498
+ font-weight: 400;
499
+ line-height: 1.5;
500
+ color: var(--approvable-ink-soft);
501
+ text-wrap: pretty;
502
+ }
503
+
504
+ .approvable-step-comment-more {
505
+ padding: 0;
506
+ border: 0;
507
+ background: none;
508
+ font-family: inherit;
509
+ font-size: 12px;
510
+ font-weight: 600;
511
+ line-height: 1;
512
+ color: var(--approvable-accent-ink);
513
+ cursor: pointer;
514
+ }
515
+
516
+ .approvable-step-actions {
517
+ display: flex;
518
+ flex-wrap: wrap;
519
+ align-items: center;
520
+ justify-content: flex-end;
521
+ gap: 6px;
522
+ /* Stays right-aligned on the line it wraps onto. */
523
+ margin-left: auto;
524
+ }
525
+
526
+ .approvable-btn-group {
527
+ display: flex;
528
+ align-items: center;
529
+ gap: 6px;
530
+ }
531
+
532
+ .approvable-btn-group:empty {
533
+ display: none;
534
+ }
535
+
536
+ /* --------------------------------------------------------------------------
537
+ Footer
538
+ -------------------------------------------------------------------------- */
539
+ .approvable-footer {
540
+ padding: 10px 15px 13px;
541
+ border-top: 1px solid var(--approvable-divider);
542
+ font-size: 11.5px;
543
+ font-weight: 400;
544
+ line-height: 1.3;
545
+ color: var(--approvable-muted);
546
+ }
547
+
548
+ /* --------------------------------------------------------------------------
549
+ Buttons
550
+ -------------------------------------------------------------------------- */
551
+ .approvable-btn {
552
+ display: inline-flex;
553
+ align-items: center;
554
+ justify-content: center;
555
+ gap: 5px;
556
+ flex: none;
557
+ padding: 5px 11px;
558
+ border: 1px solid var(--approvable-control-border);
559
+ border-radius: 999px;
560
+ background: var(--approvable-control);
561
+ color: var(--approvable-control-ink);
562
+ font-family: inherit;
563
+ font-size: 11.5px;
564
+ font-weight: 600;
565
+ line-height: 1;
566
+ text-decoration: none;
567
+ cursor: pointer;
568
+ user-select: none;
569
+ }
570
+
571
+ .approvable-btn:hover:not(:disabled) {
572
+ background: var(--approvable-control-hover);
573
+ }
574
+
575
+ .approvable-btn:focus-visible,
576
+ .approvable-history-btn:focus-visible,
577
+ .approvable-step-comment-more:focus-visible,
578
+ .approvable-timeline-refresh:focus-visible,
579
+ .approvable-dialog-close:focus-visible {
580
+ outline: 2px solid var(--approvable-accent);
581
+ outline-offset: 2px;
582
+ }
583
+
584
+ .approvable-btn:disabled,
585
+ .approvable-btn.disabled {
586
+ opacity: 0.5;
587
+ pointer-events: none;
588
+ }
589
+
590
+ .approvable-btn--primary {
591
+ border-color: transparent;
592
+ background: var(--approvable-accent);
593
+ color: var(--approvable-accent-on);
594
+ }
595
+
596
+ .approvable-btn--primary:hover:not(:disabled) {
597
+ background: var(--approvable-accent);
598
+ filter: brightness(1.06);
599
+ }
600
+
601
+ /* A button carrying nothing but an icon collapses to a circle. */
602
+ .approvable-btn--icon {
603
+ width: 24px;
604
+ height: 24px;
605
+ padding: 0;
606
+ }
607
+
608
+ /* An icon-font glyph. The package ships no font: whichever one the
609
+ application loads (Tabler, Font Awesome, an in-house set) renders here,
610
+ because a button's `icon` is a class name from the workflow definition. */
611
+ .approvable-btn i {
612
+ display: inline-flex;
613
+ font-size: 13px;
614
+ line-height: 1;
615
+ }
616
+
617
+ .approvable-btn-icon,
618
+ .approvable-icon {
619
+ width: 12px;
620
+ height: 12px;
621
+ display: block;
622
+ flex: none;
623
+ }
624
+
625
+ /* --------------------------------------------------------------------------
626
+ Loading — the initial skeleton (no content yet) and the refresh veil
627
+ -------------------------------------------------------------------------- */
628
+ .approvable-skeleton-row {
629
+ display: flex;
630
+ align-items: center;
631
+ gap: 10px;
632
+ min-height: 32px;
633
+ padding: 7px 0;
634
+ border-top: 1px solid var(--approvable-divider);
635
+ }
636
+
637
+ .approvable-skeleton-disc {
638
+ flex: none;
639
+ width: 18px;
640
+ height: 18px;
641
+ border-radius: 999px;
642
+ background: var(--approvable-skeleton);
643
+ animation: approvable-shimmer 1.4s ease-in-out infinite;
644
+ }
645
+
646
+ .approvable-skeleton-bar {
647
+ height: 12px;
648
+ border-radius: 999px;
649
+ background: var(--approvable-skeleton);
650
+ animation: approvable-shimmer 1.4s ease-in-out infinite;
651
+ }
652
+
653
+ .approvable-skeleton-bar--soft {
654
+ height: 11px;
655
+ background: var(--approvable-skeleton-soft);
656
+ }
657
+
658
+ .approvable-skeleton-bar--pill {
659
+ width: 92px;
660
+ height: 20px;
661
+ }
662
+
663
+ .approvable-skeleton-spacer {
664
+ flex: 1;
665
+ }
666
+
667
+ @keyframes approvable-shimmer {
668
+ 0%, 100% { opacity: 0.45; }
669
+ 50% { opacity: 1; }
670
+ }
671
+
672
+ .approvable-loading {
673
+ position: absolute;
674
+ inset: 0;
675
+ z-index: 5;
676
+ display: flex;
677
+ align-items: center;
678
+ justify-content: center;
679
+ margin: 0;
680
+ padding: 0;
681
+ border: none;
682
+ background: color-mix(in oklab, var(--approvable-surface) 78%, transparent);
683
+ color: var(--approvable-accent);
684
+ cursor: wait;
685
+ }
686
+
687
+ .approvable-spinner {
688
+ display: inline-block;
689
+ width: 16px;
690
+ height: 16px;
691
+ border: 2px solid currentColor;
692
+ border-right-color: transparent;
693
+ border-radius: 50%;
694
+ animation: approvable-rotation 0.8s linear infinite;
695
+ }
696
+
697
+ @keyframes approvable-rotation {
698
+ to { transform: rotate(360deg); }
699
+ }
700
+
701
+ @media (prefers-reduced-motion: reduce) {
702
+ .approvable-badge--waiting::before,
703
+ .approvable-badge--draft::before,
704
+ .approvable-skeleton-bar,
705
+ .approvable-skeleton-disc,
706
+ .approvable-spinner {
707
+ animation: none;
708
+ }
709
+ }
710
+
711
+ /* --------------------------------------------------------------------------
712
+ Comment popover
713
+ -------------------------------------------------------------------------- */
714
+ .approvable-popover {
715
+ position: relative;
716
+ display: inline-flex;
717
+ }
718
+
719
+ .approvable-popover-panel {
720
+ position: absolute;
721
+ top: calc(100% + 8px);
722
+ right: 0;
723
+ z-index: 1050;
724
+ min-width: 280px;
725
+ padding: 13px;
726
+ background: var(--approvable-surface-raised);
727
+ border: 1px solid var(--approvable-border);
728
+ border-radius: var(--approvable-radius-sm);
729
+ box-shadow: var(--approvable-shadow);
730
+ font-size: 12.5px;
731
+ line-height: 1.45;
732
+ text-align: left;
733
+ white-space: normal;
734
+ cursor: auto;
735
+ }
736
+
737
+ .approvable-popover-title {
738
+ margin-bottom: 6px;
739
+ font-size: 13px;
740
+ font-weight: 600;
741
+ }
742
+
743
+ .approvable-popover-meta {
744
+ display: flex;
745
+ align-items: center;
746
+ gap: 5px;
747
+ font-size: 11.5px;
748
+ color: var(--approvable-muted);
749
+ }
750
+
751
+ .approvable-popover-author {
752
+ color: var(--approvable-ink-soft);
753
+ }
754
+
755
+ .approvable-popover-actions {
756
+ display: flex;
757
+ justify-content: flex-end;
758
+ margin-top: 10px;
759
+ }
760
+
761
+ .approvable-textarea {
762
+ display: block;
763
+ width: 100%;
764
+ min-height: 36px;
765
+ margin: 8px 0 0;
766
+ padding: 8px 11px;
767
+ font-family: inherit;
768
+ font-size: 12.5px;
769
+ line-height: 1.5;
770
+ color: var(--approvable-ink);
771
+ background: var(--approvable-control);
772
+ border: 1px solid var(--approvable-control-border);
773
+ border-radius: var(--approvable-radius-sm);
774
+ resize: none;
775
+ overflow: hidden;
776
+ }
777
+
778
+ .approvable-textarea:focus {
779
+ outline: none;
780
+ border-color: var(--approvable-accent);
781
+ }
782
+
783
+ .approvable-textarea::placeholder {
784
+ color: var(--approvable-muted-soft);
785
+ }
786
+
787
+ /* --------------------------------------------------------------------------
788
+ Bulk action block (ApprovableButtons)
789
+ -------------------------------------------------------------------------- */
790
+ .approvable-divider {
791
+ margin: 8px 0;
792
+ border: 0;
793
+ border-top: 1px solid var(--approvable-divider);
794
+ }
795
+
796
+ .approvable-actions-list {
797
+ display: flex;
798
+ flex-wrap: wrap;
799
+ align-items: center;
800
+ gap: 6px;
801
+ }
802
+
803
+ /* --------------------------------------------------------------------------
804
+ Status select
805
+ -------------------------------------------------------------------------- */
806
+ .approvable-select {
807
+ display: block;
808
+ }
809
+
810
+ .approvable-select-label {
811
+ display: block;
812
+ margin-bottom: 5px;
813
+ font-size: 11.5px;
814
+ font-weight: 600;
815
+ color: var(--approvable-muted);
816
+ }
817
+
818
+ .approvable-select-input {
819
+ display: block;
820
+ width: 100%;
821
+ min-height: 36px;
822
+ padding: 7px 12px;
823
+ font-family: inherit;
824
+ font-size: 12.5px;
825
+ line-height: 1.4;
826
+ color: var(--approvable-ink);
827
+ background: var(--approvable-control);
828
+ border: 1px solid var(--approvable-control-border);
829
+ border-radius: var(--approvable-radius-sm);
830
+ cursor: pointer;
831
+ }
832
+
833
+ .approvable-select-input:hover,
834
+ .approvable-select-input:focus {
835
+ outline: none;
836
+ border-color: var(--approvable-accent);
837
+ }
838
+
839
+ .approvable-select-option {
840
+ display: flex;
841
+ align-items: center;
842
+ }
843
+
844
+ /* --------------------------------------------------------------------------
845
+ History dialog — the panel's geometry, in a modal
846
+ -------------------------------------------------------------------------- */
847
+ .approvable-dialog-backdrop {
848
+ position: fixed;
849
+ inset: 0;
850
+ z-index: 120;
851
+ display: flex;
852
+ align-items: center;
853
+ justify-content: center;
854
+ padding: 24px;
855
+ background: var(--approvable-scrim);
856
+ }
857
+
858
+ .approvable-dialog {
859
+ display: flex;
860
+ flex-direction: column;
861
+ width: min(460px, 100%);
862
+ max-height: 82vh;
863
+ overflow: hidden;
864
+ background: var(--approvable-surface);
865
+ border: 1px solid var(--approvable-border);
866
+ border-radius: var(--approvable-radius);
867
+ box-shadow: var(--approvable-shadow-lg);
868
+ }
869
+
870
+ .approvable-dialog-header {
871
+ display: flex;
872
+ align-items: center;
873
+ gap: 9px;
874
+ padding: 12px 15px;
875
+ }
876
+
877
+ /* The "‹ History" chip that titles the dialog. */
878
+ .approvable-dialog-eyebrow {
879
+ display: inline-flex;
880
+ align-items: center;
881
+ gap: 5px;
882
+ flex: none;
883
+ margin: 0;
884
+ padding: 4px 10px 4px 7px;
885
+ border-radius: 999px;
886
+ background: var(--approvable-neutral-tint);
887
+ color: var(--approvable-control-ink);
888
+ font-size: 11px;
889
+ font-weight: 600;
890
+ line-height: 1.2;
891
+ }
892
+
893
+ .approvable-dialog-eyebrow svg {
894
+ width: 11px;
895
+ height: 11px;
896
+ flex: none;
897
+ }
898
+
899
+ .approvable-dialog-title {
900
+ flex: 1;
901
+ min-width: 0;
902
+ margin: 0;
903
+ font-size: 11.5px;
904
+ font-weight: 400;
905
+ line-height: 1.3;
906
+ color: var(--approvable-muted);
907
+ white-space: nowrap;
908
+ overflow: hidden;
909
+ text-overflow: ellipsis;
910
+ }
911
+
912
+ .approvable-dialog-close {
913
+ flex: none;
914
+ width: 24px;
915
+ height: 24px;
916
+ display: grid;
917
+ place-items: center;
918
+ padding: 0;
919
+ border: 1px solid var(--approvable-control-border);
920
+ border-radius: 999px;
921
+ background: var(--approvable-control);
922
+ color: var(--approvable-control-ink);
923
+ cursor: pointer;
924
+ }
925
+
926
+ .approvable-dialog-close:hover {
927
+ background: var(--approvable-control-hover);
928
+ }
929
+
930
+ .approvable-dialog-body {
931
+ overflow-y: auto;
932
+ }
933
+
934
+ .approvable-dialog-footer,
935
+ .approvable-timeline-footer {
936
+ padding: 10px 15px 13px;
937
+ border-top: 1px solid var(--approvable-divider);
938
+ font-size: 11.5px;
939
+ line-height: 1.3;
940
+ color: var(--approvable-muted);
941
+ }
942
+
943
+ /* --------------------------------------------------------------------------
944
+ Audit timeline — the trail's row rhythm, newest first
945
+ -------------------------------------------------------------------------- */
946
+ /* Standalone (outside the dialog) it needs its own frame. */
947
+ .approvable-timeline--panel {
948
+ background: var(--approvable-surface);
949
+ border: 1px solid var(--approvable-border);
950
+ border-radius: var(--approvable-radius);
951
+ box-shadow: var(--approvable-shadow);
952
+ overflow: hidden;
953
+ }
954
+
955
+ .approvable-timeline-header {
956
+ display: flex;
957
+ align-items: center;
958
+ gap: 9px;
959
+ padding: 12px 15px;
960
+ }
961
+
962
+ .approvable-timeline-eyebrow {
963
+ flex: none;
964
+ margin: 0;
965
+ padding: 4px 10px;
966
+ border-radius: 999px;
967
+ background: var(--approvable-neutral-tint);
968
+ color: var(--approvable-control-ink);
969
+ font-size: 11px;
970
+ font-weight: 600;
971
+ line-height: 1.2;
972
+ }
973
+
974
+ .approvable-timeline-title {
975
+ flex: 1;
976
+ min-width: 0;
977
+ margin: 0;
978
+ font-size: 11.5px;
979
+ font-weight: 400;
980
+ line-height: 1.3;
981
+ color: var(--approvable-muted);
982
+ white-space: nowrap;
983
+ overflow: hidden;
984
+ text-overflow: ellipsis;
985
+ }
986
+
987
+ .approvable-timeline-refresh {
988
+ flex: none;
989
+ padding: 0;
990
+ border: 0;
991
+ background: none;
992
+ font-family: inherit;
993
+ font-size: 11.5px;
994
+ font-weight: 600;
995
+ line-height: 1;
996
+ color: var(--approvable-accent-ink);
997
+ cursor: pointer;
998
+ }
999
+
1000
+ .approvable-timeline-refresh:disabled {
1001
+ opacity: 0.5;
1002
+ cursor: default;
1003
+ }
1004
+
1005
+ .approvable-timeline-state {
1006
+ display: flex;
1007
+ align-items: center;
1008
+ justify-content: center;
1009
+ gap: 10px;
1010
+ min-height: 96px;
1011
+ padding: 0 15px 15px;
1012
+ font-size: 12px;
1013
+ color: var(--approvable-muted);
1014
+ }
1015
+
1016
+ .approvable-timeline-error {
1017
+ margin: 0 15px 15px;
1018
+ padding: 9px 11px;
1019
+ border-radius: var(--approvable-radius-sm);
1020
+ background: var(--approvable-danger-tint);
1021
+ color: var(--approvable-danger-ink);
1022
+ font-size: 12px;
1023
+ }
1024
+
1025
+ .approvable-timeline-list {
1026
+ list-style: none;
1027
+ margin: 0;
1028
+ padding: 0 15px;
1029
+ }
1030
+
1031
+ .approvable-timeline-item {
1032
+ display: flex;
1033
+ gap: 10px;
1034
+ padding: 7px 0;
1035
+ border-top: 1px solid var(--approvable-divider);
1036
+ }
1037
+
1038
+ .approvable-timeline-item--single-line {
1039
+ align-items: center;
1040
+ min-height: 32px;
1041
+ }
1042
+
1043
+ /* Bookkeeping events are context, not news. */
1044
+ .approvable-timeline-item--quiet {
1045
+ opacity: 0.6;
1046
+ }
1047
+
1048
+ .approvable-timeline-marker {
1049
+ flex: none;
1050
+ width: 18px;
1051
+ height: 18px;
1052
+ display: grid;
1053
+ place-items: center;
1054
+ border-radius: 999px;
1055
+ background: var(--approvable-neutral-tint);
1056
+ color: var(--approvable-neutral-ink);
1057
+ }
1058
+
1059
+ .approvable-timeline-item:not(.approvable-timeline-item--single-line) > .approvable-timeline-marker {
1060
+ margin-top: 7px;
1061
+ }
1062
+
1063
+ .approvable-timeline-marker svg {
1064
+ width: 11px;
1065
+ height: 11px;
1066
+ display: block;
1067
+ }
1068
+
1069
+ .approvable-timeline-marker--approve {
1070
+ background: var(--approvable-success-tint);
1071
+ color: var(--approvable-success-ink);
1072
+ }
1073
+
1074
+ .approvable-timeline-marker--reject {
1075
+ background: var(--approvable-danger-tint);
1076
+ color: var(--approvable-danger-ink);
1077
+ }
1078
+
1079
+ .approvable-timeline-marker--comment,
1080
+ .approvable-timeline-marker--status,
1081
+ .approvable-timeline-marker--signal {
1082
+ background: var(--approvable-accent-tint);
1083
+ color: var(--approvable-accent-tint-ink);
1084
+ }
1085
+
1086
+ .approvable-timeline-marker--undo {
1087
+ background: var(--approvable-warn-tint);
1088
+ color: var(--approvable-warn-ink);
1089
+ }
1090
+
1091
+ .approvable-timeline-content {
1092
+ flex: 1;
1093
+ min-width: 0;
1094
+ }
1095
+
1096
+ .approvable-timeline-line {
1097
+ display: flex;
1098
+ align-items: center;
1099
+ gap: 8px;
1100
+ min-height: 32px;
1101
+ }
1102
+
1103
+ .approvable-timeline-actor {
1104
+ flex: none;
1105
+ margin: 0;
1106
+ font-size: 13px;
1107
+ font-weight: 600;
1108
+ line-height: 1.3;
1109
+ }
1110
+
1111
+ .approvable-timeline-operation {
1112
+ flex: 1;
1113
+ min-width: 0;
1114
+ margin: 0;
1115
+ font-size: 11.5px;
1116
+ font-weight: 400;
1117
+ line-height: 1.3;
1118
+ color: var(--approvable-muted);
1119
+ white-space: nowrap;
1120
+ overflow: hidden;
1121
+ text-overflow: ellipsis;
1122
+ }
1123
+
1124
+ .approvable-timeline-time {
1125
+ flex: none;
1126
+ font-size: 11px;
1127
+ line-height: 1.3;
1128
+ color: var(--approvable-muted-soft);
1129
+ }
1130
+
1131
+ .approvable-timeline-comment {
1132
+ margin: 0 0 3px;
1133
+ font-size: 12px;
1134
+ line-height: 1.5;
1135
+ color: var(--approvable-ink-soft);
1136
+ text-wrap: pretty;
1137
+ }
1138
+
1139
+ .approvable-timeline-change {
1140
+ margin: 0 0 3px;
1141
+ font-size: 11.5px;
1142
+ line-height: 1.6;
1143
+ color: var(--approvable-muted);
1144
+ }
1145
+
1146
+ .approvable-timeline-change-label {
1147
+ margin-right: 6px;
1148
+ font-weight: 600;
1149
+ }
1150
+
1151
+ .approvable-timeline-value {
1152
+ padding: 1px 6px;
1153
+ border-radius: 999px;
1154
+ background: var(--approvable-neutral-tint);
1155
+ font-size: 11px;
1156
+ }
1157
+
1158
+ .approvable-timeline-value--old {
1159
+ color: var(--approvable-muted-soft);
1160
+ text-decoration: line-through;
1161
+ }
1162
+
1163
+ .approvable-timeline-value--new {
1164
+ color: var(--approvable-ink-soft);
1165
+ font-weight: 500;
1166
+ }
1167
+
1168
+ .approvable-timeline-arrow {
1169
+ padding: 0 4px;
1170
+ color: var(--approvable-muted-soft);
1171
+ }