/art: thickness affects rotated fullscreen; collapsible writeup on mobile

- Fullscreen (rotated landscape view) ignored the thickness slider because I'd
  hard-coded the frame rail/mat to fit after rotation. Let them scale with
  thickness again and pull the image's short-edge cap in (60vw) so the whole
  frame still fits even at max thickness.
- Mobile: the writeup is now a collapsible "About this piece" accordion between
  the artwork and the controls (open by default). Collapse it and the frame /
  thickness / palette controls rise up beside the artwork, so you can see frame
  changes live. Desktop unchanged (writeup always shown in the left column).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jay
2026-06-23 21:09:28 -04:00
parent 420150a532
commit 89987b8316
+37 -11
View File
@@ -82,6 +82,8 @@
});
let dlName = $derived(art ? `${(art.title || 'artwork').replace(/[^\w \-]+/g, '').trim()}.jpg` : 'artwork.jpg');
let detailsOpen = $state(true); // mobile: collapse the writeup to bring the controls up near the art
let copied = $state(false);
async function share() {
const url = art?.source_url || location.href;
@@ -127,14 +129,24 @@
{#if attribution}<p class="attribution">{attribution}</p>{/if}
</div>
<div class="body">
{#if art.blurb}<p class="blurb">{art.blurb}</p>{/if}
<div class="meta-cols">
<div class="meta"><span class="meta-label">Collection</span><span class="meta-val">{art.museum}</span></div>
{#if art.license}<div class="meta"><span class="meta-label">Rights</span><span class="meta-val">{art.license}</span></div>{/if}
<button class="details-toggle" onclick={() => (detailsOpen = !detailsOpen)}
aria-expanded={detailsOpen} aria-controls="art-details">
About this piece
<svg class="chev" class:open={detailsOpen} viewBox="0 0 24 24" width="14" height="14"
fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M6 9l6 6 6-6" />
</svg>
</button>
<div class="body-content" id="art-details" class:collapsed={!detailsOpen}>
{#if art.blurb}<p class="blurb">{art.blurb}</p>{/if}
<div class="meta-cols">
<div class="meta"><span class="meta-label">Collection</span><span class="meta-val">{art.museum}</span></div>
{#if art.license}<div class="meta"><span class="meta-label">Rights</span><span class="meta-val">{art.license}</span></div>{/if}
</div>
{#if art.source_url}
<a class="cta" href={art.source_url} target="_blank" rel="noopener">View at {art.museum}</a>
{/if}
</div>
{#if art.source_url}
<a class="cta" href={art.source_url} target="_blank" rel="noopener">View at {art.museum}</a>
{/if}
</div>
</div>
@@ -270,7 +282,9 @@
box-shadow: 0 16px 40px -22px rgba(120, 95, 50, 0.32);
}
.left { flex: 1; min-width: 0; padding: clamp(28px, 4vw, 50px) clamp(24px, 3.4vw, 46px); display: flex; flex-direction: column; justify-content: center; }
.head, .body { display: flex; flex-direction: column; } /* so .cta align-self works (no overlap) */
.head, .body, .body-content { display: flex; flex-direction: column; } /* so .cta align-self works */
.details-toggle { display: none; } /* desktop: the writeup is always shown in the left column */
.chev { transition: transform 0.2s ease; }
.kicker { display: inline-flex; align-items: center; gap: 9px; font-family: 'Space Mono', monospace; font-size: 12px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--accent); }
.kicker-rule { width: 18px; height: 2px; background: var(--accent); border-radius: 2px; }
.art-title { font-family: 'Newsreader', Georgia, serif; font-weight: 500; font-size: clamp(2rem, 4.2vw, 2.9rem); line-height: 1.05; letter-spacing: -0.01em; color: var(--ink); margin: 14px 0 0; }
@@ -313,7 +327,17 @@
.left, .right { display: contents; } /* promote head/body/art-stage/controls */
.head { order: 1; padding: clamp(22px, 5vw, 28px) clamp(18px, 5vw, 22px) 14px; }
.art-stage { order: 2; background: var(--art-band); padding: 22px 18px; box-sizing: border-box; }
.body { order: 3; padding: 20px clamp(18px, 5vw, 22px) 4px; }
.body { order: 3; padding: 6px clamp(18px, 5vw, 22px) 4px; }
/* writeup collapses (tap "About this piece") so the controls rise up beside the art */
.details-toggle {
display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%;
background: none; border: none; cursor: pointer; padding: 14px 0;
font-family: 'Space Mono', monospace; font-size: 11px; letter-spacing: 0.14em;
text-transform: uppercase; color: var(--label); -webkit-tap-highlight-color: transparent;
border-top: 1px solid rgba(120, 95, 50, 0.16);
}
.chev.open { transform: rotate(180deg); }
.body-content.collapsed { display: none; }
.controls { order: 4; width: auto; max-width: none; box-sizing: border-box;
margin: 16px clamp(14px, 4vw, 18px) clamp(20px, 5vw, 24px);
background: #fff; border: 1px solid #ece3d0; border-radius: 16px; padding: 18px 16px; }
@@ -513,8 +537,10 @@
/* The image's SHORT edge + the frame's rail/mat map to the phone's narrow width, so
cap it there (max-height, pre-rotation) with a modest fixed frame so the whole
moulding always fits; the long edge (max-width → screen height) stays generous. */
.lightbox.rotate .lb-frame { max-width: 92vh; --rail: 16px; --mat: 16px; }
.lightbox.rotate .lb-frame img { max-width: 88vh; max-height: 74vw; }
.lightbox.rotate .lb-frame { max-width: 92vh; } /* rail/mat keep scaling with thickness */
/* short edge (→ screen width after rotation) capped low enough that the frame fits even
at max thickness; the long edge stays generous */
.lightbox.rotate .lb-frame img { max-width: 88vh; max-height: 60vw; }
.lightbox.rotate .lb-frame.frame--none img { max-width: 94vh; max-height: 92vw; }
}
</style>