/art: landscape paintings rotate to fill the screen in fullscreen on portrait phones

Conditional 90° rotate of the fullscreen stage (frame + caption together): only
when the artwork is landscape AND the screen is a narrow portrait phone — so a
wide painting fills the long axis (turn phone to view level, tap to close).
Portrait/square art and desktop stay upright. Landscape detected from the loaded
image's natural dimensions; CSS decides WHEN via (orientation: portrait). Image
caps use swapped vh/vw units post-rotation so the whole framed piece always fits.
No screen-orientation lock API (unreliable on mobile web) — pure CSS transform.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jay
2026-06-23 09:15:56 -04:00
parent 4720ded29c
commit 06dd293da0
+27 -7
View File
@@ -19,6 +19,10 @@
let frame = $state('walnut');
let thickness = $state(1); // frame-scale multiplier, 0.71.9 (mat caps at 1.5)
let lightboxEl = $state(null);
// Wide paintings fill a portrait phone far better turned sideways: when the art is
// landscape we rotate the fullscreen view 90° (CSS handles WHEN — only narrow+portrait
// screens; desktop and portrait art stay upright). Aspect is read off the loaded image.
let landscape = $state(false);
function onKey(e) {
if (e.key === 'Escape' && zoom) zoom = false;
@@ -103,7 +107,8 @@
onclick={() => (zoom = true)} aria-label="Expand artwork">
{#if isWood}{@render woodRails()}{/if}
<span class="mat">
<img src={art.image_url} alt={art.title} />
<img src={art.image_url} alt={art.title}
onload={(e) => (landscape = e.currentTarget.naturalWidth > e.currentTarget.naturalHeight)} />
<span class="hint">
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
@@ -154,12 +159,14 @@
</div>
{#if zoom && art}
<button class="lightbox" bind:this={lightboxEl} onclick={() => (zoom = false)} aria-label="Close artwork">
<span class="frame frame--{frame} lb-frame" style="--frame-scale:{thickness}">
{#if isWood}{@render woodRails()}{/if}
<span class="mat"><img src={art.image_url_large || art.image_url} alt={art.title} /></span>
<button class="lightbox" class:rotate={landscape} bind:this={lightboxEl} onclick={() => (zoom = false)} aria-label="Close artwork">
<span class="lb-stage">
<span class="frame frame--{frame} lb-frame" style="--frame-scale:{thickness}">
{#if isWood}{@render woodRails()}{/if}
<span class="mat"><img src={art.image_url_large || art.image_url} alt={art.title} /></span>
</span>
<span class="lb-cap">{art.title}{#if art.artist}<span class="sep">·</span>{art.artist}{/if}</span>
</span>
<span class="lb-cap">{art.title}{#if art.artist}<span class="sep">·</span>{art.artist}{/if}</span>
</button>
{/if}
@@ -390,8 +397,10 @@
included) reads, like a piece hung on a real wall. */
background: linear-gradient(180deg, #efe9dd, #e2dbcc);
display: flex; flex-direction: column;
align-items: center; justify-content: center; gap: 12px; padding: 2.5vmin;
align-items: center; justify-content: center; padding: 2.5vmin;
}
/* frame + caption travel together so a rotated view turns as one piece */
.lb-stage { display: flex; flex-direction: column; align-items: center; gap: 12px; }
/* The full-screen view wears the same frame. Rail + mat are only a touch larger than the
page (keeping the ~1:1 wood:white proportion), and the image is capped so the whole
framed piece — including the bottom rail — always fits on screen. */
@@ -414,4 +423,15 @@
.lb-frame img { max-width: 72vw; max-height: 60vh; }
.lb-frame.frame--none img { max-width: 92vw; max-height: 78vh; }
}
/* Landscape artwork on a PORTRAIT phone: turn the whole stage 90° so the painting
fills the long axis (turn the phone to view it level; tap anywhere to close). The
image caps are in SWAPPED units — vh drives the on-screen long edge after rotation,
vw the short edge — so it always fits. Desktop and portrait art never hit this. */
@media (max-width: 640px) and (orientation: portrait) {
.lightbox.rotate .lb-stage { transform: rotate(90deg); transform-origin: center; }
.lightbox.rotate .lb-frame { max-width: 90vh; }
.lightbox.rotate .lb-frame img { max-width: 82vh; max-height: 80vw; }
.lightbox.rotate .lb-frame.frame--none img { max-width: 92vh; max-height: 92vw; }
}
</style>