release: v0.20.0
This commit is contained in:
@@ -11,6 +11,17 @@
|
||||
animation: lightbox-fade-in var(--transition-normal) ease forwards;
|
||||
}
|
||||
|
||||
/* The overlay is translucent (0.85–0.92), so the fixed page chrome under it
|
||||
showed through: the header's wordmark and red dot, and the proofing bar,
|
||||
as ghosts beside the photo. They cannot be used while the viewer is open,
|
||||
so they are hidden for as long as it is mounted (it is portaled straight
|
||||
into <body>). */
|
||||
:global(body):has(> .overlay) :global(.header),
|
||||
:global(body):has(> .overlay) :global(.proofing-sticky-bar),
|
||||
:global(body):has(> .overlay) :global(.scroll-to-top) {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
/* The overlay is dark in both modes — see "Studio-Inspired Lightbox Defaults"
|
||||
below and the studio-modern block, which both paint it near-black. In light
|
||||
mode the page's text tokens are near-black too, so every control that reads
|
||||
@@ -634,7 +645,7 @@
|
||||
}
|
||||
|
||||
:global([data-preset='studio-modern']) .close:hover {
|
||||
color: var(--accent);
|
||||
color: var(--accent-text-dark, var(--accent));
|
||||
}
|
||||
|
||||
:global([data-preset='studio-modern']) .close svg {
|
||||
@@ -660,7 +671,7 @@
|
||||
:global([data-preset='studio-modern']) .nav:hover {
|
||||
background: none;
|
||||
border-color: var(--accent);
|
||||
color: var(--accent);
|
||||
color: var(--accent-text-dark, var(--accent));
|
||||
}
|
||||
|
||||
:global([data-preset='studio-modern']) .navPrev {
|
||||
@@ -689,7 +700,7 @@
|
||||
:global([data-preset='studio-modern']) .infoToggle:hover {
|
||||
background: none;
|
||||
border-color: var(--accent);
|
||||
color: var(--accent);
|
||||
color: var(--accent-text-dark, var(--accent));
|
||||
}
|
||||
|
||||
/* The EXIF strip owns the bottom edge in this preset, so the panel opens from
|
||||
@@ -797,3 +808,123 @@
|
||||
right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ══ Zoom (#467) ═════════════════════════════════════════════════
|
||||
The photo keeps its fit-size box; zooming is a transform on the images in
|
||||
it (hooks/useZoom.ts), so the layout never moves. */
|
||||
|
||||
/* The viewer owns every touch on a zoomable photo: a pinch zooms the photo,
|
||||
not the page. Swipes still arrive as touch events (hooks/useSwipe.ts). */
|
||||
.zoomable {
|
||||
touch-action: none;
|
||||
cursor: zoom-in;
|
||||
}
|
||||
|
||||
.zoomable.zoomed {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.zoomable.zoomed:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
/* Once zoom has been used the photo may outgrow its box; the overlay, which
|
||||
covers the viewport, does the clipping instead. */
|
||||
.zoomEngaged {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.overlayZoom {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* The full-resolution file sits exactly on the preview's box. It takes no
|
||||
pointer events, so gestures always land on the same element. */
|
||||
.zoomImage {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
-webkit-user-drag: none;
|
||||
}
|
||||
|
||||
.zoomImageLoaded {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.zoomToggle {
|
||||
padding: 8px 10px;
|
||||
}
|
||||
|
||||
.zoomSpinner {
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border: 2px solid currentColor;
|
||||
border-right-color: transparent;
|
||||
border-radius: 50%;
|
||||
animation: zoom-spin 800ms linear infinite;
|
||||
}
|
||||
|
||||
@keyframes zoom-spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* "Not available" — a toast that fades on its own. */
|
||||
.zoomNotice {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
bottom: 76px;
|
||||
z-index: 1001;
|
||||
max-width: min(28rem, calc(100vw - 32px));
|
||||
padding: 8px 14px;
|
||||
transform: translateX(-50%);
|
||||
background: rgba(20, 20, 20, 0.92);
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
border-radius: var(--radius-md);
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
font-family: var(--font-sans);
|
||||
font-size: 0.8rem;
|
||||
text-align: center;
|
||||
pointer-events: none;
|
||||
animation: zoom-notice 4s ease forwards;
|
||||
}
|
||||
|
||||
@keyframes zoom-notice {
|
||||
0%,
|
||||
80% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
:global([data-preset='studio-modern']) .zoomNotice {
|
||||
top: 76px;
|
||||
bottom: auto;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
/* Still shown long enough to read, then gone without a fade. */
|
||||
.zoomNotice {
|
||||
animation: zoom-notice-static 4s steps(1, end) forwards;
|
||||
}
|
||||
|
||||
.zoomSpinner {
|
||||
animation-duration: 2s;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zoom-notice-static {
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user