fix(theme): a group jump lands its tile below the sticky rail, not under it

Heid bug-hunt finding (Gróa, relayed by booth-dev). The rail is sticky
and nothing set a scroll margin, so a fragment jump left the target tile,
and the :target reticle that marks it, hidden under the rail.

The rail wraps, so no CSS value can know its height. A small additive
script publishes the measured height as --rail-h, and a ResizeObserver
keeps it current across widths. .item's scroll-margin-top adds 12px to
that. With JS off, a 120px fallback applies.

Also styles the new empty-filter row (397ea89): the filter name in
heading ink, and a gap before the way back.

Measured, 76 group jumps across 2 booths x 4 widths (rail 48-205px):
- JS on: 0 under the rail, minimum clearance 11px.
- JS off: 0 at desktop widths. 19 at 390px, where a wrapped rail is
  143-205px tall and taller than the fallback.
Positive control: the pre-retheme skin fails 74/76.
Merged onto main 1826d19: 660 passed, mutation_check 20/20.
This commit is contained in:
vh
2026-09-23 08:12:54 -07:00
parent 744fa5263e
commit e8e49ceb14
+23 -1
View File
@@ -101,6 +101,9 @@
.empty{border:1.5px dashed var(--border-strong);border-radius:var(--radius-xl);
padding:48px 24px;text-align:center;color:var(--text-muted);background:var(--surface-sunken);
font-family:var(--font-mono);font-size:var(--size-sm)}
.empty b{color:var(--text-heading);font-weight:600}
.empty a{margin-left:6px}
.rail+.empty{margin-bottom:16px}
/* ---- upload / pickup -------------------------------------------------- */
.uploader{display:flex;gap:var(--space-3);align-items:stretch;margin-bottom:28px;flex-wrap:wrap}
@@ -339,6 +342,10 @@
/* THE RAIL. Sticky, opaque, and counted in mono because the counts are
records. The active filter is a place you are, not an armed thing, so it
is lifted rather than coloured. */
/* A fragment jump or a cursor move must land its tile BELOW the stuck rail,
not under it (heid bug-hunt, 2026-09-23). The rail wraps, so its height is
not knowable in CSS: --rail-h is measured by the script at the foot of
this template, and the 120px fallback clears a three-line desktop rail with JS off. */
.rail{position:sticky;top:0;z-index:5;display:flex;gap:6px 8px;align-items:center;flex-wrap:wrap;
padding:10px 0;margin:0 0 16px;background:var(--surface-base);
border-bottom:1px solid var(--border-subtle);font-family:var(--font-mono);font-size:var(--size-caption)}
@@ -361,7 +368,7 @@
.gallery{display:grid;gap:var(--space-5);grid-template-columns:repeat(auto-fill,minmax(320px,1fr));align-items:start}
.item{position:relative;margin:0;background:var(--surface-card);border:1px solid var(--border-default);
border-radius:var(--radius-xl);overflow:hidden;display:flex;flex-direction:column;box-shadow:var(--shadow-sm);
scroll-margin-top:72px}
scroll-margin-top:calc(var(--rail-h, 120px) + 12px)}
.item img,.item video{width:100%;height:auto;display:block;background:var(--surface-sunken)}
.item audio{width:100%;margin:20px 14px 6px;max-width:calc(100% - 28px)}
.item .dl{padding:22px 14px;font-family:var(--font-mono);font-size:var(--size-sm);word-break:break-all}
@@ -631,6 +638,21 @@
<span class="tagline">ephemeral media · auto-wipes {{ ttl_hours }}h · kept boards don't</span>
</header>
<main>{% block content %}{% endblock %}</main>
<script>
/* The sticky rail's real height, published as --rail-h for .item's
scroll-margin. ADDITIVE: with JS off the static fallback applies, and a
page without a rail does nothing. Re-measured whenever the rail's height
changes, so a rail that wraps differently at another width stays right. */
(function () {
var rail = document.querySelector('.rail');
if (!rail) return;
function set() {
document.documentElement.style.setProperty('--rail-h', rail.offsetHeight + 'px');
}
set();
if (window.ResizeObserver) new ResizeObserver(set).observe(rail);
})();
</script>
<footer class="foot">
drop a folder into <code>{{ data_dir }}</code>{% if host %} · {{ host }}{% endif %}
</footer>