@gallerist/theme-default 0.3.2 → 0.4.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.
package/README.md CHANGED
@@ -33,6 +33,19 @@ skin (`gallery.css`) and the plugin add-ons (`grid.css` for plugin-grid, `pins.c
33
33
  plugin-pins). To build a skin from scratch, import `base.css` plus the add-ons for the plugins you
34
34
  use, then your own tokens.
35
35
 
36
+ On a phone (600px wide or less) the bar leaves out the zoom tool (`@gallerist/plugin-zoom`): a
37
+ pinch or a double tap zooms, and the renderer's reset button shows while zoomed. An overlay viewer's
38
+ bar leaves out fullscreen (`@gallerist/plugin-fullscreen`) too, as the overlay already fills the
39
+ screen; an inline viewer keeps it. Keep them with `--gallerist-phone-zoom: flex` and
40
+ `--gallerist-phone-fullscreen: grid` on `.gallerist` (each tool's own display; there the zoom tool
41
+ shows − and + only). A bar whose only tools are these two (and `controls: false`) still shows on a
42
+ phone, empty: leave the plugins out there, or keep the tools with the tokens.
43
+
44
+ The theme follows `forced-colors: active` (Windows High Contrast): the View popover's chosen segment
45
+ takes the system's selection colours and the focus rings the text colour. The rail's
46
+ item clusters (`thumbnails({ cluster })`) use `--gallerist-cluster-inner` (2px) inside a cluster and
47
+ `--gallerist-cluster-gap` (8px) between two.
48
+
36
49
  Full docs: the [monorepo README](https://github.com/shafayet9780/gallerist#readme) and [`docs/`](https://github.com/shafayet9780/gallerist/tree/main/docs).
37
50
 
38
51
  ## License
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gallerist/theme-default",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "Default Gallerist theme — pure CSS, no JS dependency. Driven by --gallerist-* custom properties.",
5
5
  "license": "MIT",
6
6
  "sideEffects": [
package/src/base.css CHANGED
@@ -32,6 +32,7 @@
32
32
  --gallerist-dur-snappy: 340ms;
33
33
  --gallerist-dur-gentle: 280ms;
34
34
  --gallerist-dur-exit: 180ms; /* exits run shorter than entrances */
35
+ --gallerist-dur-panel: 220ms; /* the rail collapsing, the item column opening (plugin-thumbnails) */
35
36
 
36
37
  /* the accent: each skin names its accents as a dark/light pair, the scheme picks one */
37
38
  --gallerist-accent: var(--gallerist-accent-dark);
@@ -79,6 +80,21 @@
79
80
  --gallerist-enter-group: gallerist-enter-group;
80
81
  --gallerist-enter-item: gallerist-enter-item;
81
82
  --gallerist-rail-track-gap: var(--gallerist-rail-bg); /* the ring that cuts the track at a tick */
83
+ /* the line between a flat rail's groups: the hairline, unless a skin's dark scheme lifts it (a
84
+ hairline that reads on a light band all but disappears on a dark one) */
85
+ --gallerist-divider: var(--gallerist-hairline);
86
+ --gallerist-rail-fade: 40px; /* how far a rail's end fades where it has more beyond it */
87
+ /* item clusters (thumbnails `cluster`): the gap inside one, and between two */
88
+ --gallerist-cluster-inner: 2px;
89
+ --gallerist-cluster-gap: 8px;
90
+
91
+ /* popovers (the View menu, the shortcut list) sit a step above the bands; a segmented control
92
+ is a recessed track with its checked segment raised */
93
+ --gallerist-popover-bg: var(--gallerist-panel);
94
+ --gallerist-popover-shadow: var(--gallerist-shadow);
95
+ --gallerist-seg-track: var(--gallerist-bg);
96
+ --gallerist-seg-on: var(--gallerist-surface);
97
+ --gallerist-seg-on-shadow: 0 0 0 1px var(--gallerist-hairline), 0 1px 2px rgb(0 0 0 / 0.25);
82
98
 
83
99
  /* hover: unchanged unless a skin sets one */
84
100
  --gallerist-arrow-hover-bg: var(--gallerist-arrow-bg);
@@ -355,16 +371,56 @@
355
371
  pointer-events: auto;
356
372
  }
357
373
  .gallerist-region-toolbar {
374
+ --_gap: 6px; /* between tools: ::before and ::after cancel it */
358
375
  display: flex;
359
376
  align-items: center;
360
- gap: 6px;
361
- margin-inline-start: auto; /* tools sit at the end of the bar, after the title */
377
+ gap: var(--_gap);
378
+ /* a scroller clips at its padding box, both ways: room for a focused tool's ring (2px + 2px
379
+ offset; +1px at the ends, where Chromium rounds an RTL scroll range down), given back by the
380
+ margin so nothing moves. A tool focus scrolls in stops as far in. The end's room is ::after. */
381
+ padding: 4px 0;
382
+ padding-inline-start: 5px;
383
+ margin: -4px -5px;
384
+ scroll-padding-inline: 5px;
362
385
  /* on a narrow screen the tools scroll sideways so the counter and Close always fit */
363
- flex: 0 1 auto;
386
+ flex: 1 1 auto;
364
387
  min-width: 0;
365
388
  overflow-x: auto;
366
389
  scrollbar-width: none;
367
- pointer-events: auto;
390
+ /* no pointer-events: like every region it passes them through but on its tools, so the bar's
391
+ free space it spans is the bar's (in immersive, the image's under it), and so is the 1px its
392
+ margin takes from what is before it on a phone (the bar's gap is 4px there) */
393
+ }
394
+ /* tools sit at the end of the bar: the strip takes the free space (a shown title shares it), its
395
+ tools after this auto margin (0 once they overflow; the gap after it cancelled) */
396
+ .gallerist-region-toolbar::before {
397
+ content: "";
398
+ margin-inline: auto calc(-1 * var(--_gap));
399
+ }
400
+ /* the room after the last tool, as a box: WebKit leaves a flex scroller's end padding out of its
401
+ scroll range */
402
+ .gallerist-region-toolbar::after {
403
+ content: "";
404
+ flex: none;
405
+ width: 5px;
406
+ margin-inline-start: calc(-1 * var(--_gap));
407
+ }
408
+ /* no tools, or (where :has() works) none shown: no room either, so nothing else moves */
409
+ .gallerist-region-toolbar:empty {
410
+ padding: 0;
411
+ margin: 0;
412
+ }
413
+ .gallerist-region-toolbar:empty::before,
414
+ .gallerist-region-toolbar:empty::after {
415
+ display: none;
416
+ }
417
+ .gallerist-region-toolbar:not(:has(> :not([hidden]))) {
418
+ padding: 0;
419
+ margin: 0;
420
+ }
421
+ .gallerist-region-toolbar:not(:has(> :not([hidden])))::before,
422
+ .gallerist-region-toolbar:not(:has(> :not([hidden])))::after {
423
+ display: none;
368
424
  }
369
425
  .gallerist-region-toolbar::-webkit-scrollbar {
370
426
  display: none;
@@ -415,26 +471,120 @@
415
471
 
416
472
  /* ── thumbnails rail (plugin-thumbnails) ───────────────────────────────────── */
417
473
  /* the region wrapper: holds the scrollable rail plus the floating item/peek panels as siblings, so
418
- the panels escape the rail's overflow-clip yet still ride along when the region is re-mounted. */
474
+ the panels escape the rail's overflow-clip yet still ride along when the region is re-mounted. It
475
+ paints the rail's band, so the strip inside can fade its ends without fading the band. */
419
476
  .gallerist-thumbs-region {
420
477
  position: relative;
421
478
  display: flex;
422
479
  align-items: center;
423
480
  min-width: 0;
481
+ background: var(--gallerist-rail-bg);
482
+ border-block-start: var(--gallerist-rail-border);
483
+ backdrop-filter: var(--gallerist-blur);
424
484
  }
425
485
  .gallerist-thumbs-region > .gallerist-thumbs {
426
486
  flex: 1 1 auto;
427
487
  min-width: 0;
428
488
  }
489
+ /* the strip: no scrollbar (a mouse drags it, a wheel scrolls it, ‹ › page it; plugin-thumbnails),
490
+ no text or image selection while it's pulled; an end with more beyond it fades */
429
491
  .gallerist-thumbs {
492
+ --_fade-to: to right;
493
+ --_fade-start: 0px;
494
+ --_fade-end: 0px;
430
495
  display: flex;
431
496
  gap: 8px;
432
497
  align-items: center;
433
498
  padding: 8px 12px;
434
499
  overflow-x: auto;
435
- background: var(--gallerist-rail-bg);
436
- border-block-start: var(--gallerist-rail-border);
500
+ scrollbar-width: none;
501
+ -webkit-user-select: none;
502
+ user-select: none;
503
+ }
504
+ .gallerist-thumbs::-webkit-scrollbar {
505
+ display: none;
506
+ }
507
+ .gallerist-thumbs img {
508
+ -webkit-user-drag: none;
509
+ }
510
+ .gallerist-thumbs[data-gallerist-scroll="y"] {
511
+ --_fade-to: to bottom;
512
+ }
513
+ .gallerist[data-gallerist-dir="rtl"] .gallerist-thumbs[data-gallerist-scroll="x"] {
514
+ --_fade-to: to left;
515
+ }
516
+ .gallerist-thumbs[data-gallerist-more-start] {
517
+ --_fade-start: var(--gallerist-rail-fade);
518
+ }
519
+ .gallerist-thumbs[data-gallerist-more-end] {
520
+ --_fade-end: var(--gallerist-rail-fade);
521
+ }
522
+ .gallerist-thumbs:is([data-gallerist-more-start], [data-gallerist-more-end]) {
523
+ -webkit-mask-image: linear-gradient(
524
+ var(--_fade-to),
525
+ transparent,
526
+ #000 var(--_fade-start),
527
+ #000 calc(100% - var(--_fade-end)),
528
+ transparent
529
+ );
530
+ mask-image: linear-gradient(
531
+ var(--_fade-to),
532
+ transparent,
533
+ #000 var(--_fade-start),
534
+ #000 calc(100% - var(--_fade-end)),
535
+ transparent
536
+ );
537
+ }
538
+ @media (pointer: fine) {
539
+ .gallerist-thumbs:is([data-gallerist-more-start], [data-gallerist-more-end]) {
540
+ cursor: grab;
541
+ }
542
+ }
543
+ .gallerist-thumbs[data-gallerist-dragging],
544
+ .gallerist-thumbs[data-gallerist-dragging] * {
545
+ cursor: grabbing;
546
+ }
547
+ /* ‹ › at a sideways strip's ends, while there's more that way: for a mouse, on hover (the keys
548
+ already walk the rail, so they're out of the tab order and hidden from assistive tech) */
549
+ .gallerist-rail-page {
550
+ position: absolute;
551
+ z-index: 3;
552
+ display: grid;
553
+ place-items: center;
554
+ width: 30px;
555
+ height: 30px;
556
+ margin-inline: 6px;
557
+ padding: 0;
558
+ translate: 0 -50%;
559
+ border: 1px solid var(--gallerist-hairline);
560
+ border-radius: 50%;
561
+ background: var(--gallerist-chrome-bg);
562
+ color: var(--gallerist-fg);
563
+ box-shadow: 0 2px 8px rgb(0 0 0 / 0.25);
437
564
  backdrop-filter: var(--gallerist-blur);
565
+ cursor: pointer;
566
+ opacity: 0;
567
+ pointer-events: none;
568
+ transition: opacity var(--gallerist-dur-exit) var(--gallerist-ease-out);
569
+ }
570
+ .gallerist-rail-page[hidden] {
571
+ display: none;
572
+ }
573
+ .gallerist-rail-page .gallerist-icon {
574
+ width: 16px;
575
+ height: 16px;
576
+ }
577
+ .gallerist[data-gallerist-dir="rtl"] .gallerist-rail-page .gallerist-icon {
578
+ transform: scaleX(-1);
579
+ }
580
+ @media (hover: hover) and (pointer: fine) {
581
+ .gallerist-thumbs-region:hover > .gallerist-rail-page {
582
+ opacity: 1;
583
+ pointer-events: auto;
584
+ }
585
+ .gallerist-rail-page:hover {
586
+ background: var(--gallerist-tool-hover-bg, var(--gallerist-chrome-bg));
587
+ }
438
588
  }
439
589
  /* cards stack image + (optional) caption band; height grows for the caption */
440
590
  .gallerist-thumb {
@@ -551,8 +701,10 @@
551
701
  padding: 8px;
552
702
  padding-inline-start: 0;
553
703
  }
554
- /* collapsing the rail puts its item stack away too (fullscreen, `t`, rail: 'hidden') */
555
- .gallerist[data-gallerist-rail="collapsed"] .gallerist-thumbs-aside {
704
+ /* collapsing the rail puts its item stack away too (fullscreen, `t`, rail: 'hidden'); a rail switched
705
+ to flat or index leaves the column hidden */
706
+ .gallerist[data-gallerist-rail="collapsed"] .gallerist-thumbs-aside:not([data-gallerist-glide]),
707
+ .gallerist-thumbs-aside[hidden] {
556
708
  display: none;
557
709
  }
558
710
  .gallerist-thumb-item {
@@ -588,18 +740,16 @@
588
740
  .gallerist-thumbs[data-timeline] {
589
741
  align-items: flex-start;
590
742
  }
591
- .gallerist-thumbs[data-timeline] .gallerist-thumb-wrap {
592
- flex-direction: column;
593
- gap: 6px;
594
- }
595
743
  .gallerist-thumb-time {
596
744
  position: relative;
597
745
  display: flex;
598
746
  align-items: center;
599
747
  gap: 6px;
600
748
  height: 14px;
601
- /* a label never widens its card's column: long ones end in an ellipsis */
602
- max-width: calc(var(--gallerist-thumb-h) * 1.4);
749
+ /* a label longer than its card (a range with a host's meta at its end) runs past it: its column
750
+ widens, so it shows whole and clear of the next card's label; past three card heights it ends
751
+ in an ellipsis, the whole label in its title (as a flat rail's group label does) */
752
+ max-width: calc(var(--gallerist-thumb-h) * 3);
603
753
  font: 500 11px var(--gallerist-mono);
604
754
  font-variant-numeric: tabular-nums;
605
755
  color: var(--gallerist-fg-muted);
@@ -610,22 +760,31 @@
610
760
  overflow: hidden;
611
761
  text-overflow: ellipsis;
612
762
  }
613
- /* a side rail runs top to bottom: no horizontal track between the cards */
763
+ /* a side rail runs top to bottom: no horizontal track between the cards, and a label keeps to the
764
+ rail's width (a card's) */
765
+ .gallerist[data-gallerist-orientation="vertical"] .gallerist-thumb-time {
766
+ max-width: calc(var(--gallerist-thumb-h) * 1.4);
767
+ }
614
768
  .gallerist[data-gallerist-orientation="vertical"] .gallerist-thumb-time::after {
615
769
  display: none;
616
770
  }
617
- /* a timeline label sits above the card, so the touch expand caret moves down onto the card */
771
+ /* a timeline label sits above the card, so the touch expand caret moves down onto the card (its
772
+ left corner: right to left, the card keeps to the right of a column a long label widened) */
618
773
  .gallerist-thumbs[data-timeline] .gallerist-thumb-expand {
619
774
  top: calc(14px + 6px + 4px);
620
775
  }
776
+ .gallerist[data-gallerist-dir="rtl"] .gallerist-thumbs[data-timeline] .gallerist-thumb-expand {
777
+ left: auto;
778
+ right: calc(var(--gallerist-thumb-h) * 1.4 - 26px);
779
+ }
621
780
  /* a side rail's band edge faces the stage, not the bar */
622
- .gallerist[data-gallerist-orientation="vertical"] .gallerist-thumbs {
781
+ .gallerist[data-gallerist-orientation="vertical"] .gallerist-thumbs-region {
623
782
  border-block-start: 0 none;
624
783
  }
625
- .gallerist[data-gallerist-rail-pos="left"] .gallerist-thumbs {
784
+ .gallerist[data-gallerist-rail-pos="left"] .gallerist-thumbs-region {
626
785
  border-right: var(--gallerist-rail-border);
627
786
  }
628
- .gallerist[data-gallerist-rail-pos="right"] .gallerist-thumbs {
787
+ .gallerist[data-gallerist-rail-pos="right"] .gallerist-thumbs-region {
629
788
  border-left: var(--gallerist-rail-border);
630
789
  }
631
790
  .gallerist-thumb-time::before {
@@ -708,6 +867,8 @@
708
867
  position: relative;
709
868
  flex: 0 0 auto;
710
869
  display: flex;
870
+ flex-direction: column; /* a label over the card, a host's meta under it (the caret floats) */
871
+ gap: 6px;
711
872
  }
712
873
  .gallerist-thumb-expand {
713
874
  position: absolute;
@@ -746,13 +907,21 @@
746
907
  justify-content: flex-end; /* the item rows line up, labelled or not */
747
908
  gap: 6px;
748
909
  }
749
- /* the divider: the same room on both sides of it (the rail's gap, then this padding) */
750
- .gallerist-thumb-group + .gallerist-thumb-group {
910
+ /* the divider: the same room on both sides of it (the rail's gap, then this padding); a marker
911
+ between two groups has one on each side */
912
+ /* (as specific as 0.3's `.gallerist-thumb-group + .gallerist-thumb-group`, so a page's rule for it
913
+ still wins) */
914
+ .gallerist-thumbs:where([data-display="flat"])
915
+ > :where(.gallerist-thumb-group, .gallerist-rail-marker)
916
+ + :is(.gallerist-thumb-group, .gallerist-rail-marker) {
751
917
  padding-inline-start: 12px;
752
- border-inline-start: 1px solid var(--gallerist-hairline);
918
+ border-inline-start: 1px solid var(--gallerist-divider);
753
919
  }
920
+ /* a label wider than its items widens its group (a one-item group under a time range), so it shows
921
+ whole and clear of the next group's divider; past three card heights it ends in an ellipsis (the
922
+ whole label in its title) */
754
923
  .gallerist-thumb-group-cap {
755
- contain: inline-size; /* a long label ends in an ellipsis instead of widening its group */
924
+ max-width: calc(var(--gallerist-thumb-h) * 3);
756
925
  font: 500 11px / 14px var(--gallerist-font);
757
926
  color: var(--gallerist-fg-muted);
758
927
  white-space: nowrap;
@@ -762,7 +931,7 @@
762
931
  .gallerist-thumbs[data-timeline] .gallerist-thumb-group-cap {
763
932
  font-family: var(--gallerist-mono);
764
933
  }
765
- .gallerist-thumb-group[data-active] > .gallerist-thumb-group-cap {
934
+ .gallerist-thumb-group[data-active] .gallerist-thumb-group-cap {
766
935
  color: var(--gallerist-accent);
767
936
  }
768
937
  .gallerist-thumb-group-row {
@@ -782,10 +951,154 @@
782
951
  flex-direction: column;
783
952
  align-items: flex-start;
784
953
  }
785
- .gallerist[data-gallerist-orientation="vertical"] .gallerist-thumb-group + .gallerist-thumb-group {
954
+ /* down a side rail a group is the rail's width: a long label ends in an ellipsis there */
955
+ .gallerist[data-gallerist-orientation="vertical"]
956
+ :is(.gallerist-thumb-group-cap, .gallerist-thumb-group-head) {
957
+ contain: inline-size;
958
+ }
959
+ .gallerist[data-gallerist-orientation="vertical"]
960
+ .gallerist-thumbs:where([data-display="flat"])
961
+ > :where(.gallerist-thumb-group, .gallerist-rail-marker)
962
+ + :is(.gallerist-thumb-group, .gallerist-rail-marker) {
786
963
  padding: 12px 0 0;
787
964
  border-inline-start: 0;
788
- border-block-start: 1px solid var(--gallerist-hairline);
965
+ border-block-start: 1px solid var(--gallerist-divider);
966
+ }
967
+
968
+ /* ── host decorations (thumbnails `renderGroupMeta`, `renderItemBadge`, `cluster`, `markers`) ──── */
969
+ /* a group's meta: at the end of its label line (flat: the group widens for it, the label still
970
+ capped at three card heights), under a stacked card at its width, or at the end of its time label */
971
+ .gallerist-thumb-meta {
972
+ display: flex;
973
+ align-items: center;
974
+ gap: 4px;
975
+ min-width: 0;
976
+ font: 500 11px / 14px var(--gallerist-mono);
977
+ font-variant-numeric: tabular-nums;
978
+ color: var(--gallerist-fg-muted);
979
+ white-space: nowrap;
980
+ }
981
+ .gallerist-thumb-group-head {
982
+ display: flex;
983
+ align-items: center;
984
+ gap: 8px;
985
+ min-height: 14px;
986
+ }
987
+ .gallerist-thumb-group-head[hidden] {
988
+ display: none;
989
+ }
990
+ :is(.gallerist-thumb-group-head, .gallerist-thumb-time) > .gallerist-thumb-meta {
991
+ flex: none;
992
+ margin-inline-start: auto;
993
+ }
994
+ .gallerist-thumb-wrap > .gallerist-thumb-meta {
995
+ box-sizing: border-box;
996
+ width: calc(var(--gallerist-thumb-h) * 1.4);
997
+ min-height: 14px;
998
+ }
999
+ /* an item's badge: the top end corner (a number from `numberItems` has the start) */
1000
+ .gallerist-thumb-badge {
1001
+ position: absolute;
1002
+ top: 3px;
1003
+ inset-inline-end: 3px;
1004
+ display: flex;
1005
+ align-items: center;
1006
+ gap: 2px;
1007
+ height: 14px;
1008
+ padding: 0 3px;
1009
+ box-sizing: border-box;
1010
+ border-radius: 2px;
1011
+ background: var(--gallerist-badge-bg);
1012
+ color: var(--gallerist-fg);
1013
+ font: 600 9px / 14px var(--gallerist-mono);
1014
+ pointer-events: none;
1015
+ }
1016
+ .gallerist-thumb-badge svg {
1017
+ width: 10px;
1018
+ height: 10px;
1019
+ }
1020
+ /* clusters: close inside one, a wider gap where a new one starts (the rows' gap is 6px) */
1021
+ .gallerist-thumb-item[data-cluster="in"] {
1022
+ --_cluster: calc(var(--gallerist-cluster-inner) - 6px);
1023
+ }
1024
+ .gallerist-thumb-item[data-cluster="start"] {
1025
+ --_cluster: calc(var(--gallerist-cluster-gap) - 6px);
1026
+ }
1027
+ .gallerist-thumb-item[data-cluster]:not(:first-child) {
1028
+ margin-inline-start: var(--_cluster);
1029
+ }
1030
+ /* 2px apart, a neighbour would paint over part of a focused thumb's ring */
1031
+ .gallerist-thumb-item:focus-visible {
1032
+ z-index: 1;
1033
+ }
1034
+ :is(
1035
+ .gallerist-thumb-items[data-axis="vertical"],
1036
+ .gallerist-thumb-peek:not([data-axis="horizontal"]),
1037
+ .gallerist[data-gallerist-orientation="vertical"] .gallerist-thumb-group-row,
1038
+ .gallerist[data-gallerist-orientation="vertical"] .gallerist-thumb-items
1039
+ )
1040
+ > .gallerist-thumb-item[data-cluster]:not(:first-child) {
1041
+ margin-inline-start: 0;
1042
+ margin-block-start: var(--_cluster);
1043
+ }
1044
+ /* a marker between groups (not an item): a dashed box the height of the rail's cards, under a
1045
+ label line like a group's */
1046
+ .gallerist-rail-marker {
1047
+ flex: 0 0 auto;
1048
+ display: flex;
1049
+ flex-direction: column;
1050
+ justify-content: flex-end; /* flat: in line with the item rows */
1051
+ gap: 6px;
1052
+ -webkit-touch-callout: none; /* a long press shows its tip, not the system menu */
1053
+ }
1054
+ .gallerist-rail-marker-cap {
1055
+ height: 14px;
1056
+ font: 500 11px / 14px var(--gallerist-font);
1057
+ color: var(--gallerist-fg-muted);
1058
+ white-space: nowrap;
1059
+ }
1060
+ .gallerist-rail-marker-body {
1061
+ display: flex;
1062
+ align-items: center;
1063
+ justify-content: center;
1064
+ gap: 4px;
1065
+ box-sizing: border-box;
1066
+ min-width: calc(var(--gallerist-thumb-h) * 0.8);
1067
+ height: calc(var(--gallerist-thumb-h) * 0.7 + 4px);
1068
+ padding: 0 10px;
1069
+ border: 1px dashed var(--gallerist-fg-muted);
1070
+ border-radius: var(--gallerist-thumb-radius);
1071
+ color: var(--gallerist-fg-muted);
1072
+ font: 500 11px / 1.2 var(--gallerist-mono);
1073
+ white-space: nowrap;
1074
+ }
1075
+ /* stacked: the cards' height, under the time labels' line */
1076
+ .gallerist-thumbs:not([data-display]) > .gallerist-rail-marker {
1077
+ justify-content: flex-start;
1078
+ }
1079
+ .gallerist-thumbs:not([data-display]) .gallerist-rail-marker-body {
1080
+ height: calc(var(--gallerist-thumb-h) + 4px);
1081
+ }
1082
+ /* down a side rail: a short box across the rail */
1083
+ .gallerist[data-gallerist-orientation="vertical"] .gallerist-rail-marker-body {
1084
+ height: 32px;
1085
+ }
1086
+ /* a marker's tip (hover, long press): in the rail's region, which doesn't clip */
1087
+ .gallerist-rail-tip {
1088
+ position: absolute;
1089
+ top: 0;
1090
+ left: 0;
1091
+ z-index: 8;
1092
+ box-sizing: border-box;
1093
+ max-width: min(280px, 80vw);
1094
+ padding: 8px 10px;
1095
+ border-radius: var(--gallerist-radius);
1096
+ background: var(--gallerist-flyout-bg);
1097
+ color: var(--gallerist-fg);
1098
+ box-shadow: var(--gallerist-shadow);
1099
+ backdrop-filter: var(--gallerist-blur);
1100
+ font: 12px / 1.4 var(--gallerist-font);
1101
+ pointer-events: none;
789
1102
  }
790
1103
 
791
1104
  /* ── chrome buttons (one look for bar tools, arrows, item navigator, plugin buttons) ───────── */
@@ -859,6 +1172,395 @@
859
1172
  pointer-events: none;
860
1173
  }
861
1174
 
1175
+ /* ── the zoom tool (plugin-zoom): − 100% + ───────────────────────────────────────── */
1176
+ .gallerist-zoom-tool {
1177
+ display: flex;
1178
+ align-items: center;
1179
+ gap: 2px;
1180
+ }
1181
+ .gallerist-zoom-tool[hidden] {
1182
+ display: none;
1183
+ }
1184
+ .gallerist-bar .gallerist-zoom-level {
1185
+ min-width: 4.5em;
1186
+ padding: 0 4px;
1187
+ font: 600 13px / 1 var(--gallerist-mono);
1188
+ font-variant-numeric: tabular-nums;
1189
+ }
1190
+ /* at fit there's nothing to reset: the percentage keeps its colour */
1191
+ .gallerist-bar .gallerist-zoom-level[aria-disabled="true"] {
1192
+ opacity: 1;
1193
+ cursor: default;
1194
+ }
1195
+ .gallerist-bar .gallerist-zoom-level[aria-disabled="true"]:hover {
1196
+ background: var(--gallerist-tool-bg);
1197
+ }
1198
+ /* the percentage resets the zoom: the renderer's own reset button would say it twice (while the
1199
+ tool shows: a hidden one, or one a phone leaves out, leaves the reset where it was) */
1200
+ .gallerist-bar:has(.gallerist-zoom-tool:not([hidden])) > .gallerist-zoom-reset {
1201
+ display: none;
1202
+ }
1203
+ /* a narrow screen keeps − and + (a pinch zooms there, a double tap fits), where it keeps the tool */
1204
+ @media (max-width: 600px) {
1205
+ .gallerist-bar .gallerist-zoom-level {
1206
+ display: none;
1207
+ }
1208
+ }
1209
+ /* A phone (600px wide or less) leaves the zoom tool out of the bar (a pinch or a double tap zooms),
1210
+ and an overlay viewer's fullscreen (it fills the screen already; an inline one keeps it). A page
1211
+ keeps them by setting --gallerist-phone-zoom: flex and --gallerist-phone-fullscreen: grid (their
1212
+ own display). The renderer's reset shows there while zoomed, as without the tool: the percentage,
1213
+ which resets, is the part of the tool a phone never shows. */
1214
+ @media (max-width: 600px) {
1215
+ .gallerist-bar .gallerist-zoom-tool:not([hidden]) {
1216
+ display: var(--gallerist-phone-zoom, none);
1217
+ }
1218
+ .gallerist-bar:has(.gallerist-zoom-tool:not([hidden])) > .gallerist-zoom-reset:not([hidden]) {
1219
+ display: grid;
1220
+ }
1221
+ .gallerist[data-gallerist-mode="overlay"] .gallerist-bar .gallerist-fullscreen-btn:not([hidden]) {
1222
+ display: var(--gallerist-phone-fullscreen, none);
1223
+ }
1224
+ }
1225
+
1226
+ /* ── the View menu (plugin-view): a popover under its bar tool ───────────────────── */
1227
+ /* The plugin puts it in the viewer root (the toolbar strip scrolls, so it would clip it) and sets its
1228
+ top / left under the button; it is as small as its choices, so it covers only the stage's top edge.
1229
+ A grid of two columns: each setting's name, then its segmented control; the shortcut row spans. */
1230
+ .gallerist-view-menu {
1231
+ position: absolute;
1232
+ top: 0;
1233
+ left: 0;
1234
+ z-index: 10;
1235
+ box-sizing: border-box;
1236
+ display: grid;
1237
+ grid-template-columns: auto minmax(0, 1fr);
1238
+ align-items: center;
1239
+ gap: 12px 10px;
1240
+ width: max-content;
1241
+ max-width: calc(100% - 16px);
1242
+ overflow-y: auto;
1243
+ padding: 12px 12px 6px;
1244
+ padding-inline-start: 16px;
1245
+ border: 1px solid var(--gallerist-hairline);
1246
+ border-radius: calc(var(--gallerist-radius) + 3px);
1247
+ background: var(--gallerist-popover-bg);
1248
+ backdrop-filter: var(--gallerist-blur);
1249
+ box-shadow: var(--gallerist-popover-shadow);
1250
+ color: var(--gallerist-fg);
1251
+ font: 400 12.5px / 1.3 var(--gallerist-font);
1252
+ }
1253
+ .gallerist-view-label {
1254
+ color: var(--gallerist-fg-muted);
1255
+ font-weight: 500;
1256
+ }
1257
+ /* a segmented control: a recessed track, the checked segment raised on it */
1258
+ .gallerist-view-seg {
1259
+ display: grid;
1260
+ grid-auto-columns: 1fr;
1261
+ grid-auto-flow: column;
1262
+ gap: 2px;
1263
+ padding: 2px;
1264
+ border: 1px solid var(--gallerist-hairline);
1265
+ border-radius: calc(var(--gallerist-radius) + 1px);
1266
+ background: var(--gallerist-seg-track);
1267
+ }
1268
+ .gallerist-view-item {
1269
+ display: inline-flex;
1270
+ align-items: center;
1271
+ justify-content: center;
1272
+ gap: 7px;
1273
+ height: 28px;
1274
+ margin: 0;
1275
+ padding: 0 12px;
1276
+ border: 0;
1277
+ border-radius: max(0px, var(--gallerist-radius) - 1px);
1278
+ background: transparent;
1279
+ color: var(--gallerist-fg-muted);
1280
+ font: inherit;
1281
+ font-weight: 500;
1282
+ white-space: nowrap;
1283
+ cursor: pointer;
1284
+ user-select: none;
1285
+ -webkit-user-select: none;
1286
+ }
1287
+ .gallerist-view-icon {
1288
+ flex: none;
1289
+ width: 15px;
1290
+ height: 15px;
1291
+ stroke-width: 1.75;
1292
+ }
1293
+ .gallerist-view-item:hover {
1294
+ color: var(--gallerist-fg);
1295
+ }
1296
+ /* the checked segment: raised, full-strength text, its glyph in the selection colour */
1297
+ .gallerist-view-item[aria-checked="true"] {
1298
+ background: var(--gallerist-seg-on);
1299
+ box-shadow: var(--gallerist-seg-on-shadow);
1300
+ color: var(--gallerist-fg);
1301
+ }
1302
+ .gallerist-view-item[aria-checked="true"] .gallerist-view-icon {
1303
+ color: var(--gallerist-selection);
1304
+ }
1305
+ /* the keyboard's segment takes the skin's focus ring, never the selection colour, so focus and the
1306
+ checked value don't look alike */
1307
+ .gallerist-view-item:focus,
1308
+ .gallerist-view-foot:focus {
1309
+ outline: none;
1310
+ }
1311
+ .gallerist-view-item:focus-visible,
1312
+ .gallerist-view-foot:focus-visible {
1313
+ position: relative;
1314
+ outline: 2px solid var(--gallerist-focus);
1315
+ outline-offset: 1px;
1316
+ }
1317
+ /* the shortcut list's row (plugin-shortcuts): spans both columns under a hairline */
1318
+ .gallerist-view-foot {
1319
+ grid-column: 1 / -1;
1320
+ display: flex;
1321
+ align-items: center;
1322
+ justify-content: space-between;
1323
+ gap: 12px;
1324
+ margin: 0;
1325
+ margin-inline: -10px -6px;
1326
+ padding: 9px 6px 3px;
1327
+ padding-inline: 10px 6px;
1328
+ border: 0;
1329
+ border-block-start: 1px solid var(--gallerist-hairline);
1330
+ border-radius: 0 0 5px 5px;
1331
+ background: transparent;
1332
+ color: var(--gallerist-fg-muted);
1333
+ font: inherit;
1334
+ text-align: start;
1335
+ cursor: pointer;
1336
+ }
1337
+ .gallerist-view-foot:hover {
1338
+ color: var(--gallerist-fg);
1339
+ }
1340
+ /* a key, as the shortcut list draws them */
1341
+ .gallerist-view-foot kbd,
1342
+ .gallerist-shortcuts kbd {
1343
+ display: inline-grid;
1344
+ place-items: center;
1345
+ box-sizing: border-box;
1346
+ min-width: 22px;
1347
+ height: 22px;
1348
+ padding: 0 6px;
1349
+ border: 1px solid var(--gallerist-hairline);
1350
+ border-bottom-width: 2px;
1351
+ border-radius: 5px;
1352
+ background: var(--gallerist-surface);
1353
+ color: var(--gallerist-fg);
1354
+ font: 500 11.5px / 1 var(--gallerist-mono);
1355
+ }
1356
+ /* its button reads as held while the popover is open */
1357
+ .gallerist-bar button[aria-expanded="true"] {
1358
+ background: var(--gallerist-tool-hover-bg);
1359
+ }
1360
+ /* a finger gets taller segments, and no key to press */
1361
+ @media (pointer: coarse) {
1362
+ .gallerist-view-item {
1363
+ height: 36px;
1364
+ }
1365
+ .gallerist-view-foot kbd {
1366
+ display: none;
1367
+ }
1368
+ }
1369
+ /* forced colours drop the checked segment's raised surface and shadow: it takes the system's
1370
+ selection colours, and a focus ring the text colour, so focus and the choice still differ */
1371
+ @media (forced-colors: active) {
1372
+ .gallerist-view-item[aria-checked="true"] {
1373
+ forced-color-adjust: none;
1374
+ background: Highlight;
1375
+ color: HighlightText;
1376
+ }
1377
+ .gallerist-view-item[aria-checked="true"] .gallerist-view-icon {
1378
+ color: inherit;
1379
+ }
1380
+ :is(
1381
+ .gallerist-view-item,
1382
+ .gallerist-view-foot,
1383
+ .gallerist-shortcuts-close,
1384
+ .gallerist-shortcuts-body
1385
+ ):focus-visible {
1386
+ outline-color: CanvasText;
1387
+ }
1388
+ }
1389
+
1390
+ /* ── the shortcut list (plugin-shortcuts): a modal dialog over the viewer ────────── */
1391
+ /* The plugin puts a scrim over the whole viewer (the rest of it inert) and the dialog in its middle:
1392
+ a header (the title, Esc to close), then a column per section, the moves widest. On a narrow
1393
+ screen it is a sheet from the bottom edge, one column. */
1394
+ .gallerist-shortcuts-scrim {
1395
+ position: absolute;
1396
+ inset: 0;
1397
+ z-index: 20;
1398
+ /* one cell the scrim's size, so the dialog's max-height holds and its list scrolls */
1399
+ display: grid;
1400
+ grid-template: minmax(0, 1fr) / minmax(0, 1fr);
1401
+ place-items: center;
1402
+ padding: 16px;
1403
+ background: var(--gallerist-scrim);
1404
+ animation: gallerist-fade var(--gallerist-dur-exit) var(--gallerist-ease-out);
1405
+ }
1406
+ .gallerist-shortcuts {
1407
+ box-sizing: border-box;
1408
+ display: flex;
1409
+ flex-direction: column;
1410
+ width: min(720px, 100%);
1411
+ max-height: 100%;
1412
+ border: 1px solid var(--gallerist-hairline);
1413
+ border-radius: calc(var(--gallerist-radius) + 5px);
1414
+ background: var(--gallerist-popover-bg);
1415
+ backdrop-filter: var(--gallerist-blur);
1416
+ box-shadow: var(--gallerist-popover-shadow);
1417
+ color: var(--gallerist-fg);
1418
+ font: 400 13px / 1.35 var(--gallerist-font);
1419
+ }
1420
+ .gallerist-shortcuts:focus {
1421
+ outline: none; /* focus starts on the dialog, so a screen reader reads it whole; the ring is the close button's */
1422
+ }
1423
+ .gallerist-shortcuts-head {
1424
+ display: flex;
1425
+ align-items: center;
1426
+ gap: 10px;
1427
+ padding: 12px;
1428
+ padding-inline-start: 20px;
1429
+ border-block-end: 1px solid var(--gallerist-hairline);
1430
+ }
1431
+ .gallerist-shortcuts-head .gallerist-icon {
1432
+ flex: none;
1433
+ width: 17px;
1434
+ height: 17px;
1435
+ }
1436
+ .gallerist-shortcuts-head h2 {
1437
+ margin: 0;
1438
+ font-size: 14px;
1439
+ font-weight: 600;
1440
+ }
1441
+ .gallerist-shortcuts-close {
1442
+ display: inline-flex;
1443
+ align-items: center;
1444
+ gap: 8px;
1445
+ min-height: 32px;
1446
+ margin-inline-start: auto;
1447
+ padding: 0 8px;
1448
+ border: 0;
1449
+ border-radius: var(--gallerist-control-radius, 6px);
1450
+ background: transparent;
1451
+ color: var(--gallerist-fg-muted);
1452
+ font: inherit;
1453
+ font-size: 12.5px;
1454
+ cursor: pointer;
1455
+ }
1456
+ .gallerist-shortcuts-close:hover {
1457
+ background: var(--gallerist-tool-hover-bg);
1458
+ color: var(--gallerist-fg);
1459
+ }
1460
+ .gallerist-shortcuts-close:focus {
1461
+ outline: none;
1462
+ }
1463
+ .gallerist-shortcuts-close:focus-visible {
1464
+ outline: 2px solid var(--gallerist-focus);
1465
+ outline-offset: 1px;
1466
+ }
1467
+ .gallerist-shortcuts-body {
1468
+ display: flex;
1469
+ min-height: 0;
1470
+ flex-wrap: wrap;
1471
+ gap: 4px 28px;
1472
+ overflow-y: auto;
1473
+ padding: 16px 20px 18px;
1474
+ }
1475
+ /* the list takes focus (Tab) so the keys can scroll it when the viewer is short */
1476
+ .gallerist-shortcuts-body:focus {
1477
+ outline: none;
1478
+ }
1479
+ .gallerist-shortcuts-body:focus-visible {
1480
+ outline: 2px solid var(--gallerist-focus);
1481
+ outline-offset: -2px;
1482
+ }
1483
+ .gallerist-shortcuts-body section {
1484
+ flex: 1 1 150px;
1485
+ min-width: 0;
1486
+ }
1487
+ .gallerist-shortcuts-body section:first-child {
1488
+ flex: 1.7 1 250px;
1489
+ }
1490
+ .gallerist-shortcuts h3 {
1491
+ margin: 0 0 6px;
1492
+ color: var(--gallerist-fg-muted);
1493
+ font-size: 12px;
1494
+ font-weight: 500;
1495
+ }
1496
+ .gallerist-shortcuts dl {
1497
+ margin: 0;
1498
+ }
1499
+ .gallerist-shortcuts-row {
1500
+ display: flex;
1501
+ align-items: center;
1502
+ justify-content: space-between;
1503
+ gap: 12px;
1504
+ min-height: 34px;
1505
+ }
1506
+ .gallerist-shortcuts dd {
1507
+ display: flex;
1508
+ flex: none;
1509
+ gap: 4px;
1510
+ margin: 0;
1511
+ color: var(--gallerist-fg-muted);
1512
+ }
1513
+ .gallerist-shortcuts-note {
1514
+ margin: 0;
1515
+ padding: 11px 20px;
1516
+ border-block-start: 1px solid var(--gallerist-hairline);
1517
+ color: var(--gallerist-fg-muted);
1518
+ font-size: 12.5px;
1519
+ }
1520
+ /* the gestures: one list, a hairline between rows */
1521
+ .gallerist-shortcuts[data-gallerist-list="gestures"] .gallerist-shortcuts-body {
1522
+ padding-block: 4px;
1523
+ }
1524
+ .gallerist-shortcuts[data-gallerist-list="gestures"] .gallerist-shortcuts-row {
1525
+ min-height: 42px;
1526
+ }
1527
+ .gallerist-shortcuts[data-gallerist-list="gestures"]
1528
+ .gallerist-shortcuts-row
1529
+ + .gallerist-shortcuts-row {
1530
+ border-block-start: 1px solid var(--gallerist-hairline);
1531
+ }
1532
+ @media (max-width: 600px) {
1533
+ .gallerist-shortcuts-scrim {
1534
+ place-items: end stretch;
1535
+ padding: 16px 0 0;
1536
+ }
1537
+ .gallerist-shortcuts {
1538
+ width: 100%;
1539
+ border-width: 1px 0 0;
1540
+ border-radius: 14px 14px 0 0;
1541
+ }
1542
+ .gallerist-shortcuts-body {
1543
+ flex-direction: column;
1544
+ gap: 14px;
1545
+ padding-inline: 16px;
1546
+ }
1547
+ .gallerist-shortcuts-body section,
1548
+ .gallerist-shortcuts-body section:first-child {
1549
+ flex: none;
1550
+ }
1551
+ .gallerist-shortcuts-head {
1552
+ padding-inline-start: 16px;
1553
+ }
1554
+ .gallerist-shortcuts-note {
1555
+ padding: 12px 16px 20px;
1556
+ }
1557
+ }
1558
+ @media (prefers-reduced-motion: reduce) {
1559
+ .gallerist-shortcuts-scrim {
1560
+ animation: none;
1561
+ }
1562
+ }
1563
+
862
1564
  /* ── top bar: counter → title → tools → zoom reset → close ─────────────────────── */
863
1565
  .gallerist-bar {
864
1566
  grid-area: bar;
@@ -1333,9 +2035,17 @@
1333
2035
  display: flex;
1334
2036
  flex-direction: column;
1335
2037
  }
1336
- .gallerist[data-gallerist-rail="collapsed"] .gallerist-thumbs-region {
2038
+ .gallerist[data-gallerist-rail="collapsed"] .gallerist-thumbs-region:not([data-gallerist-glide]) {
1337
2039
  display: none;
1338
2040
  }
2041
+ /* gliding open or shut (plugin-thumbnails animates its size, clipped): the strip keeps to the edge
2042
+ by its handle, so it slides out past the viewer's edge rather than squeezing */
2043
+ .gallerist-thumbs-region[data-gallerist-glide] {
2044
+ align-items: flex-start;
2045
+ }
2046
+ .gallerist-thumbs-aside[data-gallerist-glide] {
2047
+ justify-content: flex-start;
2048
+ }
1339
2049
  /* the pull-handle: a grip tab on the rail's edge — clearly part of the gallery */
1340
2050
  .gallerist-rail-handle {
1341
2051
  flex: 0 0 auto;
@@ -1377,7 +2087,7 @@
1377
2087
  .gallerist-rail-toggle .gallerist-icon {
1378
2088
  width: 16px;
1379
2089
  height: 16px;
1380
- transition: transform var(--gallerist-dur-gentle, 280ms) var(--gallerist-ease-out, ease);
2090
+ transition: transform var(--gallerist-dur-panel, 220ms) var(--gallerist-ease-out, ease);
1381
2091
  }
1382
2092
  /* expanded = chevron points down (pull down to hide); collapsed = points up (pull up to show) */
1383
2093
  .gallerist-rail-toggle[aria-pressed="true"] .gallerist-icon {
@@ -1568,9 +2278,19 @@
1568
2278
  min-height: 0;
1569
2279
  }
1570
2280
  .gallerist[data-gallerist-orientation="vertical"][data-gallerist-rail="collapsed"]
1571
- .gallerist-thumbs-region {
2281
+ .gallerist-thumbs-region:not([data-gallerist-glide]) {
1572
2282
  display: none;
1573
2283
  }
2284
+ /* a side rail slides out sideways: its strip keeps to the handle's side (the stage's) */
2285
+ .gallerist[data-gallerist-rail-pos="left"] .gallerist-thumbs-region[data-gallerist-glide],
2286
+ .gallerist[data-gallerist-dir="rtl"][data-gallerist-rail-pos="right"]
2287
+ .gallerist-thumbs-region[data-gallerist-glide] {
2288
+ align-items: flex-end;
2289
+ }
2290
+ .gallerist[data-gallerist-dir="rtl"][data-gallerist-rail-pos="left"]
2291
+ .gallerist-thumbs-region[data-gallerist-glide] {
2292
+ align-items: flex-start;
2293
+ }
1574
2294
  .gallerist[data-gallerist-orientation="vertical"] .gallerist-thumbs {
1575
2295
  flex: 1 1 auto;
1576
2296
  min-height: 0;
@@ -1644,7 +2364,8 @@
1644
2364
  .gallerist[data-gallerist-preset="immersive"] .gallerist-caption {
1645
2365
  pointer-events: none;
1646
2366
  }
1647
- .gallerist[data-gallerist-preset="immersive"] .gallerist-bar > * {
2367
+ /* (the tools' strip, a region, passes them through itself: it spans the bar's free space) */
2368
+ .gallerist[data-gallerist-preset="immersive"] .gallerist-bar > :not(.gallerist-region-toolbar) {
1648
2369
  pointer-events: auto;
1649
2370
  }
1650
2371
  .gallerist[data-gallerist-preset="immersive"] .gallerist-caption {
@@ -1659,13 +2380,34 @@
1659
2380
  .gallerist-caption {
1660
2381
  transition: opacity var(--gallerist-dur-gentle, 280ms) var(--gallerist-ease-out, ease);
1661
2382
  }
1662
- .gallerist[data-gallerist-idle] .gallerist-bar,
1663
- .gallerist[data-gallerist-idle] .gallerist-controls > button,
1664
- .gallerist[data-gallerist-idle] .gallerist-itemnav,
1665
- .gallerist[data-gallerist-idle] .gallerist-navigator,
1666
- .gallerist[data-gallerist-idle] .gallerist-caption {
1667
- opacity: 0;
1668
- pointer-events: none;
2383
+ /* except what has keyboard focus, and the band holding it (WCAG 2.4.7): a keyboard user who pauses
2384
+ on a control still sees where focus is. Without :has() and :focus-visible (iOS Safari before
2385
+ 15.4) all of it fades, as before; an unforgiving :not() there would drop the whole rule. The
2386
+ exemption sits in :where(), so the rule keeps the specificity host overrides were written for. */
2387
+ @supports not selector(:has(:focus-visible)) {
2388
+ .gallerist[data-gallerist-idle] .gallerist-bar,
2389
+ .gallerist[data-gallerist-idle] .gallerist-controls > button,
2390
+ .gallerist[data-gallerist-idle] .gallerist-itemnav,
2391
+ .gallerist[data-gallerist-idle] .gallerist-navigator,
2392
+ .gallerist[data-gallerist-idle] .gallerist-caption {
2393
+ opacity: 0;
2394
+ pointer-events: none;
2395
+ }
2396
+ }
2397
+ @supports selector(:has(:focus-visible)) {
2398
+ .gallerist[data-gallerist-idle] .gallerist-bar:where(:not(:focus-visible, :has(:focus-visible))),
2399
+ .gallerist[data-gallerist-idle]
2400
+ .gallerist-controls
2401
+ > button:where(:not(:focus-visible, :has(:focus-visible))),
2402
+ .gallerist[data-gallerist-idle]
2403
+ .gallerist-itemnav:where(:not(:focus-visible, :has(:focus-visible))),
2404
+ .gallerist[data-gallerist-idle]
2405
+ .gallerist-navigator:where(:not(:focus-visible, :has(:focus-visible))),
2406
+ .gallerist[data-gallerist-idle]
2407
+ .gallerist-caption:where(:not(:focus-visible, :has(:focus-visible))) {
2408
+ opacity: 0;
2409
+ pointer-events: none;
2410
+ }
1669
2411
  }
1670
2412
 
1671
2413
  /* ── a11y live region (visually hidden) ────────────────────────────────────── */
@@ -1747,10 +2489,20 @@
1747
2489
  pointer-events: none; /* a fading viewer must not take clicks meant for the page */
1748
2490
  }
1749
2491
 
1750
- /* immersive preset: chrome AND the rail fade together on idle (UX-E). */
1751
- .gallerist[data-gallerist-preset="immersive"][data-gallerist-idle] .gallerist-region-thumbnails {
1752
- opacity: 0;
1753
- pointer-events: none;
2492
+ /* immersive preset: chrome AND the rail fade together on idle (UX-E), unless focus is in it (as
2493
+ above: where :has() is unsupported it fades all the same). */
2494
+ @supports not selector(:has(:focus-visible)) {
2495
+ .gallerist[data-gallerist-preset="immersive"][data-gallerist-idle] .gallerist-region-thumbnails {
2496
+ opacity: 0;
2497
+ pointer-events: none;
2498
+ }
2499
+ }
2500
+ @supports selector(:has(:focus-visible)) {
2501
+ .gallerist[data-gallerist-preset="immersive"][data-gallerist-idle]
2502
+ .gallerist-region-thumbnails:where(:not(:has(:focus-visible))) {
2503
+ opacity: 0;
2504
+ pointer-events: none;
2505
+ }
1754
2506
  }
1755
2507
  .gallerist[data-gallerist-preset="immersive"] .gallerist-region-thumbnails {
1756
2508
  transition: opacity var(--gallerist-dur-gentle) var(--gallerist-ease-out);
@@ -1947,6 +2699,7 @@
1947
2699
  .gallerist-region-thumbnails:has(.gallerist-thumbs[data-display="index"])
1948
2700
  .gallerist-thumbs-region {
1949
2701
  align-items: stretch;
2702
+ border-inline: 0 none;
1950
2703
  }
1951
2704
  .gallerist[data-gallerist-orientation="vertical"]
1952
2705
  .gallerist-region-thumbnails:has(.gallerist-thumbs[data-display="index"])
@@ -1960,7 +2713,6 @@
1960
2713
  padding: 8px 12px;
1961
2714
  overflow-x: auto;
1962
2715
  overflow-y: hidden;
1963
- border-inline: 0 none;
1964
2716
  }
1965
2717
  .gallerist[data-gallerist-orientation="vertical"]
1966
2718
  .gallerist-thumbs[data-display="index"]
@@ -1985,23 +2737,68 @@
1985
2737
 
1986
2738
  /* ── docked controls (`arrows: 'docked'`, the reviewer preset): nothing over the media ─────────
1987
2739
  Review needs every pixel, corners included. The group arrows sit in gutters the stage leaves at
1988
- its sides (none on touch, where swipes stand in for them). The item navigator is a grid item of
1989
- the root, the count over ↑ ↓: a header on the aside item column while that shows, else at the
1990
- end of the caption row, which makes room for it. While any group has several items it keeps its
1991
- room on a one-item group, so the stage and the item column hold still. Last in the file, so it
1992
- wins over the arrow layouts and the touch chip above. Where no row or column beside the stage is
1993
- free (immersive) it falls back to the stage's corner; the zoom minimap stays over the media (it
1994
- shows only while zoomed, and is the zoom tool itself). */
2740
+ its sides (on touch, where swipes stand in for them, only while one has keyboard focus). The
2741
+ item navigator is a grid item of the root, the count over ↑ ↓: a header on the aside item column
2742
+ while that shows, else at the end of the caption row, which makes room for it. While any group
2743
+ has several items it keeps its room on a one-item group, so the stage and the item column hold
2744
+ still. Last in the file, so it wins over the arrow layouts and the touch chip above. Where no row
2745
+ or column beside the stage is free (immersive) it falls back to the stage's corner; the zoom
2746
+ minimap stays over the media (it shows only while zoomed, and is the zoom tool itself). */
1995
2747
  .gallerist[data-gallerist-arrows="docked"]:has(.gallerist-prev) .gallerist-stage {
1996
2748
  inset-inline: calc(var(--gallerist-arrow-size) + 24px);
1997
2749
  }
1998
2750
  .gallerist[data-gallerist-arrows="docked"][data-gallerist-axis="vertical"] .gallerist-stage {
1999
2751
  inset-inline-start: 0; /* both group arrows stack at the inline end */
2000
2752
  }
2753
+ /* touch: the stage takes the full width. A keyboard or switch user on touch still reaches the
2754
+ arrows, which then come back beside the media, never over it: the stage makes room for them only
2755
+ while one has keyboard focus (:focus-visible; one focused by a script after a tap stays hidden,
2756
+ and takes no room), as much on each side, so letterboxed media holds still. Held sideways that is
2757
+ a gutter at each side (on a vertical group axis too, where a mouse gets only the end one);
2758
+ upright, where most photos and screenshots leave their spare room above and below, it is a band
2759
+ at the top and the bottom instead, the arrows in the bottom one (↑ in the top one on a vertical
2760
+ axis), 12px from either edge as in a gutter, which leaves the focus ring 8px clear; not in
2761
+ immersive, whose navigator has that corner. Sideways or upright is the screen's, not the
2762
+ viewer's: a container query would follow the viewer, but needs Safari 16. */
2001
2763
  @media (pointer: coarse) {
2002
2764
  .gallerist[data-gallerist-arrows="docked"]:has(.gallerist-prev) .gallerist-stage {
2003
2765
  inset-inline: 0;
2004
2766
  }
2767
+ .gallerist[data-gallerist-arrows="docked"]:has(
2768
+ .gallerist-controls > :is(.gallerist-prev, .gallerist-next):focus-visible
2769
+ )
2770
+ .gallerist-stage {
2771
+ inset-inline: calc(var(--gallerist-arrow-size) + 24px);
2772
+ }
2773
+ }
2774
+ @media (pointer: coarse) and (orientation: portrait) {
2775
+ .gallerist[data-gallerist-arrows="docked"]:not([data-gallerist-preset="immersive"]):has(
2776
+ .gallerist-controls > :is(.gallerist-prev, .gallerist-next):focus-visible
2777
+ )
2778
+ .gallerist-stage {
2779
+ inset: calc(var(--gallerist-arrow-size) + 24px) 0;
2780
+ }
2781
+ .gallerist[data-gallerist-arrows="docked"]:not([data-gallerist-preset="immersive"])
2782
+ .gallerist-controls
2783
+ > :is(.gallerist-prev, .gallerist-next):focus-visible {
2784
+ top: auto;
2785
+ bottom: 12px;
2786
+ transform: none;
2787
+ }
2788
+ .gallerist[data-gallerist-arrows="docked"][data-gallerist-axis="vertical"]:not(
2789
+ [data-gallerist-preset="immersive"]
2790
+ )
2791
+ .gallerist-controls
2792
+ > .gallerist-prev:focus-visible {
2793
+ top: 12px;
2794
+ bottom: auto;
2795
+ }
2796
+ }
2797
+ /* and above the zoom minimap, which can reach a gutter on a short stage */
2798
+ .gallerist[data-gallerist-arrows="docked"]
2799
+ .gallerist-controls
2800
+ > :is(.gallerist-prev, .gallerist-next):focus-visible {
2801
+ z-index: 5;
2005
2802
  }
2006
2803
  /* one group (and no linear walk): the group arrows have nowhere to go, so they and their gutters go */
2007
2804
  .gallerist[data-gallerist-arrows="docked"][data-gallerist-one-group]:not([data-gallerist-linear])
package/src/console.css CHANGED
@@ -23,6 +23,7 @@
23
23
  --gallerist-fg-muted: #a3a9b1;
24
24
  --gallerist-chrome-bg: #16181b; /* surface 1: bands, panels, arrows */
25
25
  --gallerist-hairline: #2a2e34;
26
+ --gallerist-divider: #464c54; /* 2.05:1 on the rail (the hairline is 1.30:1) */
26
27
  --gallerist-radius: 6px;
27
28
  --gallerist-toolbar-h: 56px;
28
29
  --gallerist-thumb-h: 76px;
@@ -106,6 +107,12 @@
106
107
  --gallerist-pressed-bg: #272b30;
107
108
  --gallerist-pressed-fg: var(--gallerist-accent);
108
109
  --gallerist-pressed-shadow: inset 0 -2px 0 var(--gallerist-accent);
110
+ /* popovers: one step above the bar; segments recessed, the checked one raised */
111
+ --gallerist-popover-bg: #1b1d21;
112
+ --gallerist-popover-shadow: 0 14px 36px rgb(0 0 0 / 0.55), 0 2px 6px rgb(0 0 0 / 0.35);
113
+ --gallerist-seg-track: #111215;
114
+ --gallerist-seg-on: #2b2f35;
115
+ --gallerist-seg-on-shadow: inset 0 1px 0 rgb(255 255 255 / 0.05), 0 1px 2px rgb(0 0 0 / 0.5);
109
116
  }
110
117
 
111
118
  /* accents */
@@ -131,6 +138,7 @@
131
138
  --gallerist-fg-muted: #4a5058;
132
139
  --gallerist-chrome-bg: #fff;
133
140
  --gallerist-hairline: #d3d6db;
141
+ --gallerist-divider: var(--gallerist-hairline);
134
142
  --gallerist-surface: #f4f5f6;
135
143
  --gallerist-cell-bg: #f4f5f6;
136
144
  --gallerist-panel: rgb(255 255 255 / 0.97);
@@ -149,6 +157,11 @@
149
157
  --gallerist-pressed-bg: #e3e5e8;
150
158
  --gallerist-accent: var(--gallerist-accent-light);
151
159
  --gallerist-accent-fg: var(--gallerist-accent-light-fg);
160
+ --gallerist-popover-bg: #fff;
161
+ --gallerist-popover-shadow: 0 14px 36px rgb(22 24 27 / 0.14), 0 2px 6px rgb(22 24 27 / 0.08);
162
+ --gallerist-seg-track: #eceef1;
163
+ --gallerist-seg-on: #fff;
164
+ --gallerist-seg-on-shadow: 0 1px 2px rgb(22 24 27 / 0.16), 0 0 0 1px rgb(22 24 27 / 0.06);
152
165
  }
153
166
 
154
167
  @media (prefers-color-scheme: light) {
@@ -160,6 +173,7 @@
160
173
  --gallerist-fg-muted: #4a5058;
161
174
  --gallerist-chrome-bg: #fff;
162
175
  --gallerist-hairline: #d3d6db;
176
+ --gallerist-divider: var(--gallerist-hairline);
163
177
  --gallerist-surface: #f4f5f6;
164
178
  --gallerist-cell-bg: #f4f5f6;
165
179
  --gallerist-panel: rgb(255 255 255 / 0.97);
@@ -178,6 +192,11 @@
178
192
  --gallerist-pressed-bg: #e3e5e8;
179
193
  --gallerist-accent: var(--gallerist-accent-light);
180
194
  --gallerist-accent-fg: var(--gallerist-accent-light-fg);
195
+ --gallerist-popover-bg: #fff;
196
+ --gallerist-popover-shadow: 0 14px 36px rgb(22 24 27 / 0.14), 0 2px 6px rgb(22 24 27 / 0.08);
197
+ --gallerist-seg-track: #eceef1;
198
+ --gallerist-seg-on: #fff;
199
+ --gallerist-seg-on-shadow: 0 1px 2px rgb(22 24 27 / 0.16), 0 0 0 1px rgb(22 24 27 / 0.06);
181
200
  }
182
201
  }
183
202
  }
package/src/editorial.css CHANGED
@@ -23,6 +23,7 @@
23
23
  --gallerist-fg-muted: #58544b;
24
24
  --gallerist-chrome-bg: #f3f0e9;
25
25
  --gallerist-hairline: #dad4c7;
26
+ --gallerist-divider: var(--gallerist-hairline); /* not Gallery's dark one */
26
27
  --gallerist-surface: #fbfaf6;
27
28
  --gallerist-cell-bg: #fbfaf6;
28
29
  --gallerist-panel: rgb(243 240 233 / 0.98);
@@ -139,6 +140,7 @@
139
140
  --gallerist-fg-muted: #aba597;
140
141
  --gallerist-chrome-bg: #191816;
141
142
  --gallerist-hairline: #36332e;
143
+ --gallerist-divider: #4d4840; /* 1.96:1 on the wall (the hairline is 1.41:1) */
142
144
  --gallerist-surface: #262421;
143
145
  --gallerist-cell-bg: #262421;
144
146
  --gallerist-panel: rgb(25 24 22 / 0.98);
@@ -168,6 +170,7 @@
168
170
  --gallerist-fg-muted: #aba597;
169
171
  --gallerist-chrome-bg: #191816;
170
172
  --gallerist-hairline: #36332e;
173
+ --gallerist-divider: #4d4840;
171
174
  --gallerist-surface: #262421;
172
175
  --gallerist-cell-bg: #262421;
173
176
  --gallerist-panel: rgb(25 24 22 / 0.98);
package/src/gallery.css CHANGED
@@ -22,6 +22,7 @@
22
22
  --gallerist-fg-muted: #b9b9b4;
23
23
  --gallerist-chrome-bg: rgb(24 24 26 / 0.6);
24
24
  --gallerist-hairline: rgb(255 255 255 / 0.12);
25
+ --gallerist-divider: rgb(255 255 255 / 0.22); /* 1.93:1 on the ground (the hairline is 1.34:1) */
25
26
  --gallerist-radius: 12px;
26
27
  --gallerist-font: "Instrument Sans", system-ui, -apple-system, sans-serif;
27
28
  --gallerist-mono: var(--gallerist-font); /* no monospace face: numerals are tabular sans */
@@ -121,6 +122,7 @@
121
122
  --gallerist-fg-muted: #55554f;
122
123
  --gallerist-chrome-bg: rgb(255 255 255 / 0.85);
123
124
  --gallerist-hairline: rgb(21 21 21 / 0.12);
125
+ --gallerist-divider: var(--gallerist-hairline);
124
126
  --gallerist-surface: #fff;
125
127
  --gallerist-cell-bg: #eceae4;
126
128
  --gallerist-panel: rgb(244 243 239 / 0.97);
@@ -148,6 +150,7 @@
148
150
  --gallerist-fg-muted: #55554f;
149
151
  --gallerist-chrome-bg: rgb(255 255 255 / 0.85);
150
152
  --gallerist-hairline: rgb(21 21 21 / 0.12);
153
+ --gallerist-divider: var(--gallerist-hairline);
151
154
  --gallerist-surface: #fff;
152
155
  --gallerist-cell-bg: #eceae4;
153
156
  --gallerist-panel: rgb(244 243 239 / 0.97);