@inkbar/theme 0.4.0 → 0.6.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 (3) hide show
  1. package/README.md +7 -8
  2. package/package.json +1 -1
  3. package/theme.css +278 -70
package/README.md CHANGED
@@ -20,7 +20,7 @@
20
20
 
21
21
  ---
22
22
 
23
- `@inkbar/theme` styles the viewer, toolbar controls, note threads and suggestion cards from `@inkbar/vue`, `@inkbar/react`, `@inkbar/svelte` and `createViewer` in `@inkbar/core`. Import it and ship, or override any part of it with your own CSS. If your app already defines shadcn tokens, the theme picks up your colours and radius.
23
+ `@inkbar/theme` styles the viewer, toolbar controls and note threads from `@inkbar/vue`, `@inkbar/react`, `@inkbar/svelte` and `createViewer` in `@inkbar/core`. Import it and ship, or override any part of it with your own CSS. If your app already defines shadcn tokens, the theme picks up your colours and radius.
24
24
 
25
25
  ## Install
26
26
 
@@ -60,13 +60,12 @@ Colour tokens must hold complete CSS colour values. Tokens written as bare HSL c
60
60
 
61
61
  Set these variables on `[data-ink-root]` to change the default colour for new annotations:
62
62
 
63
- | Variable | Used for |
64
- | -------------------------------------------------------------------------- | ----------------------------------- |
65
- | `--ink-highlight`, `--ink-underline`, `--ink-strikeout`, `--ink-squiggly` | Text markup |
66
- | `--ink-note`, `--ink-free-text` | Notes and text boxes |
67
- | `--ink-shape`, `--ink-ink` | Shapes and freehand drawing |
68
- | `--ink-redact`, `--ink-redact-fill` | Redaction outline and fill |
69
- | `--ink-suggestion-low`, `--ink-suggestion-medium`, `--ink-suggestion-high` | Suggested redactions, by confidence |
63
+ | Variable | Used for |
64
+ | ------------------------------------------------------------------------- | --------------------------- |
65
+ | `--ink-highlight`, `--ink-underline`, `--ink-strikeout`, `--ink-squiggly` | Text markup |
66
+ | `--ink-note`, `--ink-free-text` | Notes and text boxes |
67
+ | `--ink-shape`, `--ink-ink` | Shapes and freehand drawing |
68
+ | `--ink-redact`, `--ink-redact-fill` | Redaction outline and fill |
70
69
 
71
70
  Inkbar reads a colour when you create the annotation and stores it on the annotation, because the colour goes into the PDF. Changing a variable later affects new annotations only.
72
71
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inkbar/theme",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Default stylesheet for Inkbar. Zero specificity, so your CSS wins.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://inkbar.dev",
package/theme.css CHANGED
@@ -15,7 +15,7 @@
15
15
  :where(
16
16
  [data-ink-root],
17
17
  [data-ink-thread-popover],
18
- [data-ink-suggestion-card],
18
+ [data-ink-popover],
19
19
  [data-ink-select-content],
20
20
  [data-ink-signature-dialog]
21
21
  ) {
@@ -29,9 +29,6 @@
29
29
  --ink-ink: #151820;
30
30
  --ink-redact: #c0392b;
31
31
  --ink-redact-fill: #000000;
32
- --ink-suggestion-low: #d4a017;
33
- --ink-suggestion-medium: #e67e22;
34
- --ink-suggestion-high: #c0392b;
35
32
 
36
33
  --ink-bg: var(--muted, #f1f2f5);
37
34
  --ink-fg: var(--foreground, #151820);
@@ -52,7 +49,7 @@
52
49
  :where(
53
50
  [data-ink-root]:not(.light, .light *),
54
51
  [data-ink-thread-popover]:not(.light *),
55
- [data-ink-suggestion-card]:not(.light *),
52
+ [data-ink-popover]:not(.light *),
56
53
  [data-ink-select-content]:not(.light *),
57
54
  [data-ink-signature-dialog]:not(.light *)
58
55
  ) {
@@ -73,7 +70,7 @@
73
70
  .dark [data-ink-root]:not(.light, .dark .light *),
74
71
  [data-ink-root].dark,
75
72
  .dark [data-ink-thread-popover]:not(.dark .light *),
76
- .dark [data-ink-suggestion-card]:not(.dark .light *),
73
+ .dark [data-ink-popover]:not(.dark .light *),
77
74
  .dark [data-ink-select-content]:not(.dark .light *),
78
75
  .dark [data-ink-signature-dialog]:not(.dark .light *)
79
76
  ) {
@@ -244,10 +241,11 @@
244
241
  background: transparent;
245
242
  }
246
243
 
247
- /* Tools: the active tool's cursor is set on the root; text spans follow it unless text is the target. */
244
+ /* Tools: the active tool's cursor is set on the root; page text follows it, except under
245
+ select text, where text is the target. */
248
246
  :where(
249
- [data-ink-tool='redact-area'] .ink-text-layer span,
250
- [data-ink-tool='note'] .ink-text-layer span
247
+ [data-ink-tool]:not([data-ink-tool='select-text']) .ink-text-layer span,
248
+ [data-ink-tool]:not([data-ink-tool='select-text']) .ink-text-layer br
251
249
  ) {
252
250
  cursor: inherit;
253
251
  }
@@ -280,7 +278,8 @@
280
278
  fill: none;
281
279
  }
282
280
 
283
- /* Edit handles on a selected annotation: shown with the select tools only. */
281
+ /* Edit handles on a selected annotation: shown with the select tools, and on what a creation
282
+ tool just made (`data-ink-made`). */
284
283
  :where(.ink-handle) {
285
284
  box-sizing: border-box;
286
285
  border: 1px solid var(--ink-accent);
@@ -289,7 +288,11 @@
289
288
  }
290
289
 
291
290
  :where(
292
- [data-ink-tool]:not([data-ink-tool='select'], [data-ink-tool='select-text'])
291
+ [data-ink-tool]:not(
292
+ [data-ink-tool='select'],
293
+ [data-ink-tool='select-text'],
294
+ [data-ink-made]
295
+ )
293
296
  .ink-handle
294
297
  ) {
295
298
  display: none;
@@ -303,9 +306,16 @@
303
306
  cursor: nesw-resize;
304
307
  }
305
308
 
309
+ :where(
310
+ .ink-handle[data-handle='start'],
311
+ .ink-handle[data-handle='end'],
312
+ .ink-handle[data-handle='callout']
313
+ ) {
314
+ cursor: crosshair;
315
+ }
316
+
306
317
  :where(.ink-handle[data-handle='start'], .ink-handle[data-handle='end']) {
307
318
  border-radius: 50%;
308
- cursor: move;
309
319
  }
310
320
 
311
321
  /* Annotations */
@@ -313,23 +323,73 @@
313
323
  z-index: 1;
314
324
  }
315
325
 
326
+ /* Plugin layers sit over the overlay (after it, at the same level). Presses that miss a
327
+ plugin's marks go through to the annotations and tools underneath. */
328
+ :where(.ink-plugin-layer) {
329
+ z-index: 1;
330
+ pointer-events: none;
331
+ }
332
+
333
+ :where(.ink-plugin-layer > *) {
334
+ pointer-events: auto;
335
+ }
336
+
316
337
  :where(.ink-annotation) {
317
338
  border-radius: 2px;
318
339
  outline: 1px solid transparent;
319
340
  outline-offset: 1px;
320
341
  }
321
342
 
343
+ /* An annotation's SVGs (its shape, and a text box's edge band over the text) cover its box. */
322
344
  :where(.ink-annotation:not([data-custom]) > svg) {
323
345
  position: absolute;
324
346
  left: 0;
325
347
  top: 0;
326
348
  }
327
349
 
350
+ /* Cursors say what a press will do: move an annotation, edit a text box's text, or (text
351
+ markup under select text) select text. Under rectangle select a press on text markup
352
+ selects and drags it, so it shows move there. The creation tools' cursor shows through
353
+ elsewhere. */
328
354
  :where(
329
355
  [data-ink-tool='select'] .ink-annotation,
330
- [data-ink-tool='select-text'] .ink-annotation
356
+ [data-ink-tool='select-text'] .ink-annotation,
357
+ [data-ink-made] .ink-annotation[data-state='selected']
331
358
  ) {
332
- cursor: pointer;
359
+ cursor: move;
360
+ }
361
+
362
+ :where(
363
+ [data-ink-tool='select-text']
364
+ .ink-annotation:is(
365
+ [data-type='highlight'],
366
+ [data-type='underline'],
367
+ [data-type='strikeout'],
368
+ [data-type='squiggly']
369
+ )
370
+ ) {
371
+ cursor: text;
372
+ }
373
+
374
+ :where(
375
+ [data-ink-tool='select'] .ink-annotation:not([data-locked]) .ink-free-text,
376
+ [data-ink-tool='select-text'] .ink-annotation:not([data-locked]) .ink-free-text,
377
+ [data-ink-made]
378
+ .ink-annotation[data-state='selected']:not([data-locked])
379
+ .ink-free-text
380
+ ) {
381
+ cursor: text;
382
+ }
383
+
384
+ /* The band along a text box's edge presses to move it, not to edit. */
385
+ :where(
386
+ [data-ink-tool='select'] .ink-annotation:not([data-locked]) [data-ink-edge],
387
+ [data-ink-tool='select-text'] .ink-annotation:not([data-locked]) [data-ink-edge],
388
+ [data-ink-made]
389
+ .ink-annotation[data-state='selected']:not([data-locked])
390
+ [data-ink-edge]
391
+ ) {
392
+ cursor: move;
333
393
  }
334
394
 
335
395
  :where(.ink-annotation[data-state='hover']) {
@@ -345,6 +405,13 @@
345
405
  cursor: default;
346
406
  }
347
407
 
408
+ /* While an annotation is moved the whole viewport shows it, over any element's own cursor.
409
+ The one rule here that is not zero specificity: it has to beat them. */
410
+ [data-ink-dragging],
411
+ [data-ink-dragging] * {
412
+ cursor: move !important;
413
+ }
414
+
348
415
  :where(.ink-annotation[data-locked][data-state='selected']) {
349
416
  outline-style: dotted;
350
417
  }
@@ -379,10 +446,6 @@
379
446
  outline-style: dotted;
380
447
  }
381
448
 
382
- :where(.ink-annotation[data-origin='suggested'] .ink-annotation-shape > *) {
383
- stroke-dasharray: 4 3;
384
- }
385
-
386
449
  :where(.ink-note-icon) {
387
450
  position: absolute;
388
451
  left: 0;
@@ -537,6 +600,54 @@
537
600
  cursor: default;
538
601
  }
539
602
 
603
+ /*
604
+ * A plugin command button (createCommandButton): the button looks like a toolbar button
605
+ * wherever it sits, and its status text goes beside it.
606
+ */
607
+ :where(.ink-command) {
608
+ display: inline-flex;
609
+ align-items: center;
610
+ gap: 0.5rem;
611
+ }
612
+
613
+ :where(.ink-command button) {
614
+ display: inline-flex;
615
+ align-items: center;
616
+ justify-content: center;
617
+ min-width: var(--ink-control-size, 2.25rem);
618
+ height: var(--ink-control-size, 2.25rem);
619
+ padding: 0 0.5rem;
620
+ border: 1px solid transparent;
621
+ border-radius: calc(var(--ink-radius) - 2px);
622
+ background: transparent;
623
+ color: inherit;
624
+ font: inherit;
625
+ cursor: pointer;
626
+ }
627
+
628
+ :where(.ink-command button:hover) {
629
+ background: color-mix(in srgb, var(--ink-fg) 8%, transparent);
630
+ }
631
+
632
+ :where(.ink-command button:disabled, .ink-command button[aria-disabled='true']) {
633
+ opacity: 0.5;
634
+ cursor: default;
635
+ }
636
+
637
+ :where(.ink-command button[aria-busy='true']) {
638
+ cursor: progress;
639
+ }
640
+
641
+ :where(.ink-command button:focus-visible) {
642
+ outline: 2px solid var(--ink-ring);
643
+ outline-offset: 2px;
644
+ }
645
+
646
+ :where(.ink-command-status) {
647
+ opacity: 0.75;
648
+ font-size: 0.875rem;
649
+ }
650
+
540
651
  :where(.ink-toolbar input) {
541
652
  box-sizing: border-box;
542
653
  height: var(--ink-control-size, 2.25rem);
@@ -870,16 +981,48 @@
870
981
  border-color: var(--ink-border);
871
982
  }
872
983
 
873
- /* Shape menu: a split button, the tool part beside a narrow chevron. */
984
+ /*
985
+ * Split buttons (shape, highlight, signature): one control with two parts, the tool button
986
+ * beside a narrow chevron. Hovering either part tints the whole control and the part under
987
+ * the pointer a shade more; with the tool on, both parts take the accent. Plain sibling
988
+ * selectors, not :has(), which costs frames on long documents.
989
+ */
874
990
  :where(.ink-split-button) {
875
991
  display: inline-flex;
876
992
  align-items: stretch;
993
+ border-radius: calc(var(--ink-radius) - 2px);
994
+ }
995
+
996
+ :where(.ink-split-button > .ink-tool-button) {
997
+ border-start-end-radius: 0;
998
+ border-end-end-radius: 0;
877
999
  }
878
1000
 
879
- :where(.ink-split-button .ink-menu-trigger) {
1001
+ :where(.ink-split-button > .ink-menu-trigger) {
880
1002
  min-width: 0;
881
1003
  width: 1.25rem;
882
1004
  padding-inline: 0.125rem;
1005
+ border-start-start-radius: 0;
1006
+ border-end-start-radius: 0;
1007
+ }
1008
+
1009
+ :where(.ink-split-button:hover) {
1010
+ background: color-mix(in srgb, var(--ink-fg) 8%, transparent);
1011
+ }
1012
+
1013
+ /* The tool button's on state already takes the accent; the chevron beside it follows. */
1014
+ :where(.ink-split-button > .ink-tool-button[data-state='on'] + .ink-menu-trigger) {
1015
+ background: var(--ink-accent);
1016
+ color: var(--ink-accent-fg);
1017
+ /* The line between the parts, only while the accent fills both. */
1018
+ box-shadow: inset 1px 0 color-mix(in srgb, var(--ink-accent-fg) 35%, transparent);
1019
+ }
1020
+
1021
+ :where(
1022
+ .ink-split-button > .ink-tool-button[data-state='on']:hover,
1023
+ .ink-split-button > .ink-tool-button[data-state='on'] + .ink-menu-trigger:hover
1024
+ ) {
1025
+ background: color-mix(in srgb, var(--ink-accent-fg) 16%, var(--ink-accent));
883
1026
  }
884
1027
 
885
1028
  :where(.ink-shape-grid) {
@@ -910,12 +1053,54 @@
910
1053
  padding-right: 0.5rem;
911
1054
  }
912
1055
 
1056
+ /*
1057
+ * The colour row reads as one more item: the swatch sits in the icon column and "Colour" in
1058
+ * the label column. The colour menu's trigger covers the whole row, so the row is the target;
1059
+ * the label shows through it.
1060
+ */
913
1061
  :where(.ink-highlight-colour) {
1062
+ position: relative;
914
1063
  display: flex;
915
1064
  align-items: center;
916
- justify-content: space-between;
917
- gap: 0.5rem;
918
- padding: 0.25rem 0.25rem 0.25rem 0.5rem;
1065
+ padding: 0.25rem;
1066
+ }
1067
+
1068
+ :where(.ink-highlight-colour-label) {
1069
+ padding: 0.375rem 0.5rem 0.375rem 2rem;
1070
+ }
1071
+
1072
+ :where(.ink-highlight-colour > .ink-menu-trigger) {
1073
+ position: absolute;
1074
+ inset: 0.25rem;
1075
+ display: flex;
1076
+ align-items: center;
1077
+ justify-content: flex-start;
1078
+ width: auto;
1079
+ min-width: 0;
1080
+ height: auto;
1081
+ padding: 0 0.5rem;
1082
+ border: 0;
1083
+ border-radius: calc(var(--ink-radius) - 4px);
1084
+ background: transparent;
1085
+ }
1086
+
1087
+ :where(
1088
+ .ink-highlight-colour > .ink-menu-trigger:hover,
1089
+ .ink-highlight-colour > .ink-menu-trigger:focus-visible,
1090
+ .ink-highlight-colour > .ink-menu-trigger[data-state='open']
1091
+ ) {
1092
+ background: color-mix(in srgb, var(--ink-fg) 8%, transparent);
1093
+ }
1094
+
1095
+ /* A markup colour is a fill, so its swatch is a disc, not the border ring. */
1096
+ :where(.ink-highlight-colour .ink-swatch) {
1097
+ width: 1rem;
1098
+ height: 1rem;
1099
+ }
1100
+
1101
+ :where(.ink-highlight-colour .ink-swatch[data-kind='color']) {
1102
+ border: 1px solid color-mix(in srgb, var(--ink-fg) 20%, transparent);
1103
+ background: var(--ink-swatch);
919
1104
  }
920
1105
 
921
1106
  /* Stamps: an image filling its box; the overlay sets its geometry and opacity. */
@@ -1159,8 +1344,8 @@
1159
1344
  outline-offset: 2px;
1160
1345
  }
1161
1346
 
1162
- /* Comment threads and suggestion cards */
1163
- :where(.ink-thread-popover, .ink-suggestion-card) {
1347
+ /* Comment threads */
1348
+ :where(.ink-thread-popover) {
1164
1349
  z-index: 50;
1165
1350
  width: 20rem;
1166
1351
  max-width: calc(100vw - 1rem);
@@ -1216,13 +1401,13 @@
1216
1401
  resize: vertical;
1217
1402
  }
1218
1403
 
1219
- :where(.ink-thread-actions, .ink-suggestion-actions) {
1404
+ :where(.ink-thread-actions) {
1220
1405
  display: flex;
1221
1406
  gap: 0.375rem;
1222
1407
  margin-top: 0.5rem;
1223
1408
  }
1224
1409
 
1225
- :where(.ink-thread-actions button, .ink-suggestion-actions button) {
1410
+ :where(.ink-thread-actions button) {
1226
1411
  padding: 0.375rem 0.75rem;
1227
1412
  border: 1px solid var(--ink-border);
1228
1413
  border-radius: calc(var(--ink-radius) - 2px);
@@ -1232,75 +1417,98 @@
1232
1417
  cursor: pointer;
1233
1418
  }
1234
1419
 
1235
- :where(
1236
- .ink-thread-actions button[type='submit'],
1237
- .ink-suggestion-actions button:first-child
1238
- ) {
1420
+ :where(.ink-thread-actions button[type='submit']) {
1239
1421
  border-color: var(--ink-accent);
1240
1422
  background: var(--ink-accent);
1241
1423
  color: var(--ink-accent-fg);
1242
1424
  }
1243
1425
 
1244
- :where(.ink-select-item:focus-visible) {
1245
- outline: none;
1246
- }
1247
-
1248
- :where(.ink-thread-actions button:disabled) {
1249
- opacity: 0.5;
1250
- cursor: default;
1251
- }
1252
-
1253
- :where(
1254
- .ink-toolbar button:focus-visible,
1255
- .ink-toolbar select:focus-visible,
1256
- .ink-select-trigger:focus-visible,
1257
- .ink-toolbar input:focus-visible,
1258
- .ink-thread-actions button:focus-visible,
1259
- .ink-suggestion-actions button:focus-visible,
1260
- .ink-thread-reply textarea:focus-visible
1261
- ) {
1262
- outline: 2px solid var(--ink-ring);
1263
- outline-offset: 2px;
1426
+ /* Plugin cards: the card a plugin mark opens, a core popover like the thread's. */
1427
+ :where(.ink-card) {
1428
+ z-index: 50;
1429
+ width: 20rem;
1430
+ max-width: calc(100vw - 1rem);
1431
+ padding: 0.75rem;
1432
+ border: 1px solid var(--ink-border);
1433
+ border-radius: var(--ink-radius);
1434
+ background: var(--ink-surface);
1435
+ color: var(--ink-fg);
1436
+ box-shadow: 0 8px 24px rgb(0 0 0 / 0.15);
1437
+ font-size: 0.875rem;
1264
1438
  }
1265
1439
 
1266
- :where(.ink-suggestion-reason, .ink-suggestion-explanation) {
1440
+ :where(.ink-card-title, .ink-card-meta, .ink-card-body) {
1267
1441
  margin: 0;
1268
1442
  }
1269
1443
 
1270
- :where(.ink-suggestion-explanation) {
1271
- margin-top: 0.25rem;
1272
- color: var(--ink-muted-fg);
1444
+ :where(.ink-card-title) {
1445
+ font-weight: 600;
1273
1446
  }
1274
1447
 
1275
- /* The label text keeps the body colour for contrast; the confidence colour is a swatch. */
1276
- :where(.ink-suggestion-confidence) {
1277
- display: inline-flex;
1448
+ /* The meta line keeps the body colour for contrast; any colour of its own is the swatch. */
1449
+ :where(.ink-card-meta) {
1450
+ display: flex;
1278
1451
  align-items: center;
1279
1452
  gap: 0.375rem;
1280
- margin-left: 0.5rem;
1281
- color: var(--ink-fg);
1453
+ margin-top: 0.125rem;
1282
1454
  font-size: 0.75rem;
1283
- text-transform: capitalize;
1284
1455
  }
1285
1456
 
1286
- :where(.ink-suggestion-confidence)::before {
1287
- content: '';
1457
+ :where(.ink-card-swatch) {
1458
+ flex: none;
1288
1459
  width: 0.5rem;
1289
1460
  height: 0.5rem;
1290
1461
  border-radius: 50%;
1291
- background: currentColor;
1292
1462
  }
1293
1463
 
1294
- :where(.ink-suggestion-confidence[data-confidence='low'])::before {
1295
- background: var(--ink-suggestion-low);
1464
+ :where(.ink-card-body) {
1465
+ margin-top: 0.25rem;
1466
+ color: var(--ink-muted-fg);
1296
1467
  }
1297
1468
 
1298
- :where(.ink-suggestion-confidence[data-confidence='medium'])::before {
1299
- background: var(--ink-suggestion-medium);
1469
+ :where(.ink-card-actions) {
1470
+ display: flex;
1471
+ gap: 0.375rem;
1472
+ margin-top: 0.5rem;
1300
1473
  }
1301
1474
 
1302
- :where(.ink-suggestion-confidence[data-confidence='high'])::before {
1303
- background: var(--ink-suggestion-high);
1475
+ :where(.ink-card-actions button) {
1476
+ padding: 0.375rem 0.75rem;
1477
+ border: 1px solid var(--ink-border);
1478
+ border-radius: calc(var(--ink-radius) - 2px);
1479
+ background: var(--ink-surface);
1480
+ color: inherit;
1481
+ font: inherit;
1482
+ cursor: pointer;
1483
+ }
1484
+
1485
+ /* The first action is the primary one. */
1486
+ :where(.ink-card-actions button:first-child) {
1487
+ border-color: var(--ink-accent);
1488
+ background: var(--ink-accent);
1489
+ color: var(--ink-accent-fg);
1490
+ }
1491
+
1492
+ :where(.ink-select-item:focus-visible) {
1493
+ outline: none;
1494
+ }
1495
+
1496
+ :where(.ink-thread-actions button:disabled) {
1497
+ opacity: 0.5;
1498
+ cursor: default;
1499
+ }
1500
+
1501
+ :where(
1502
+ .ink-toolbar button:focus-visible,
1503
+ .ink-toolbar select:focus-visible,
1504
+ .ink-select-trigger:focus-visible,
1505
+ .ink-toolbar input:focus-visible,
1506
+ .ink-thread-actions button:focus-visible,
1507
+ .ink-thread-reply textarea:focus-visible,
1508
+ .ink-card-actions button:focus-visible
1509
+ ) {
1510
+ outline: 2px solid var(--ink-ring);
1511
+ outline-offset: 2px;
1304
1512
  }
1305
1513
 
1306
1514
  @media (prefers-reduced-motion: no-preference) {