Alex Dunmow 98293cf8ab feat(magazine-bold): Wave A theme lane (WO-TF-021) — streetwear / fashion drops
Full Wave A build to the OVERRIDE-CONTRACT (frozen cms a0f07e2ae):

- 49 mandatory builtin overrides in templates/overrides/magazine-bold/,
  re-skinned to streetwear cover-story chrome (massive condensed uppercase
  Archivo Black headlines, heavy 2px rules, hard square edges, mono datelines,
  fluoro accent slabs). Every content field, provider var, custom tag and JS
  block preserved verbatim from the frozen builtin defaults; legacy button
  (link/style/label) and card (media/text) field reads already reconciled.
- 7 page templates: default, full-width, landing, article, blog-index,
  contact, auth.
- 4 persona blocks (deleted masthead/photo_essay/pull_quote/issue_archive/
  colophon — now overrides of navbar/gallery/quote/post-index/footer):
  cover_story, lookbook, drop_calendar, credits_ticker.
- 5 dual-mode presets (newsprint+voltage light / ink-black+fluoro dark), all
  19 tokens, dark tuned not inverted.
- Bundled OFL woff2: Archivo Black (display), Inter (body), IBM Plex Mono
  (labels); fonts.json + assets/fonts/LICENSES.md. All font-family via
  var(--font-heading|body|mono, fallback).
- Themed email-safe wrapper (tables, inline styles, streetwear defaults).
- Master pages (navbar/footer/slot chrome) + seed demo site (OFFCUT label:
  home, about, blog + 3 posts, contact with data table + row_inserted →
  email-admins workflow, login) in fashion-label voice.
- MID motion tier: marquee ticker, scroll reveal, duotone hover, scroll
  progress bar — all prefers-reduced-motion honored with static fallback,
  no canvas.
- Icon packs (lucide, simple-icons) declared in plugin.mod + RECOMMENDED_ICONS.

Verified: `make build` exit 0; `check-safety` 32 checks 19 ok 13 skip -> OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 12:38:05 +08:00

53 lines
5.5 KiB
Plaintext

{# gallery override — magazine-bold grid: plain hairline framing, generous whitespace, unchanged lightbox. Fields + lightbox JS mirror builtin verbatim: layout, gap, columns, lightbox, images[].src/.alt/.caption. #}
{% with layout=layout|default:"grid" gap=gap|default:"md" cols=columns|default:3 %}<div data-block-override="magazine-bold:gallery" class="bn-gallery my-8{% if class %} {{ class }}{% endif %}" data-bn-gallery data-lightbox="{% if lightbox %}true{% else %}false{% endif %}"><div class="{% if layout == "carousel" %}flex snap-x snap-mandatory overflow-x-auto pb-2 {% if gap == "sm" %}gap-2{% elif gap == "lg" %}gap-6{% else %}gap-4{% endif %}{% elif layout == "masonry" %}columns-2 {% if cols == 4 %}md:columns-4{% elif cols == 2 %}md:columns-2{% else %}md:columns-3{% endif %} {% if gap == "sm" %}gap-2{% elif gap == "lg" %}gap-6{% else %}gap-4{% endif %}{% else %}grid grid-cols-2 {% if cols == 4 %}md:grid-cols-4{% elif cols == 2 %}md:grid-cols-2{% else %}md:grid-cols-3{% endif %} {% if gap == "sm" %}gap-2{% elif gap == "lg" %}gap-6{% else %}gap-4{% endif %}{% endif %}">{% for image in images %}<a href="{{ image.src|mediaURL }}" data-bn-gallery-item data-caption="{{ image.caption|default:"" }}" class="group relative block overflow-hidden bg-muted focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 {% if layout == "carousel" %}w-64 shrink-0 snap-start sm:w-80{% elif layout == "masonry" %}mb-4 w-full break-inside-avoid{% endif %}">{% if layout == "grid" %}{% img image.src alt=image.alt|default:"" class="aspect-square w-full object-cover" %}{% else %}{% img image.src alt=image.alt|default:"" class="h-auto w-full" %}{% endif %}{% if image.caption %}<span class="sr-only">{{ image.caption }}</span>{% endif %}</a>{% endfor %}</div></div>{% if lightbox %}<script>
(function(){
if(window.__bnLightbox)return;window.__bnLightbox=true;
var reduce=window.matchMedia&&window.matchMedia('(prefers-reduced-motion: reduce)').matches;
var ov,imgEl,capEl,items=[],idx=0,lastFocus=null;
function build(){
ov=document.createElement('div');
ov.setAttribute('role','dialog');ov.setAttribute('aria-modal','true');ov.setAttribute('aria-label','Image viewer');ov.tabIndex=-1;
ov.style.cssText='position:fixed;inset:0;z-index:9999;display:none;align-items:center;justify-content:center;background:rgba(0,0,0,0.9);padding:2rem;';
var btn='position:absolute;width:2.75rem;height:2.75rem;display:inline-flex;align-items:center;justify-content:center;border-radius:9999px;border:none;background:hsl(var(--background));color:hsl(var(--foreground));cursor:pointer;';
ov.innerHTML=
'<button type="button" data-lb-close aria-label="Close" style="'+btn+'top:1rem;right:1rem;"><svg style="width:1.25rem;height:1.25rem" aria-hidden="true"><use href="/icons/lucide.svg#x"/></svg></button>'+
'<button type="button" data-lb-prev aria-label="Previous image" style="'+btn+'left:1rem;top:50%;transform:translateY(-50%);"><svg style="width:1.25rem;height:1.25rem" aria-hidden="true"><use href="/icons/lucide.svg#chevron-left"/></svg></button>'+
'<figure style="margin:0;max-width:92vw;max-height:92vh;display:flex;flex-direction:column;align-items:center;gap:0.75rem;"><img data-lb-img alt="" style="max-width:92vw;max-height:82vh;object-fit:contain;border-radius:0.5rem;'+(reduce?'':'transition:opacity .2s ease;')+'"><figcaption data-lb-cap style="color:hsl(var(--background));opacity:.85;font-size:.875rem;text-align:center;max-width:60ch;"></figcaption></figure>'+
'<button type="button" data-lb-next aria-label="Next image" style="'+btn+'right:1rem;top:50%;transform:translateY(-50%);"><svg style="width:1.25rem;height:1.25rem" aria-hidden="true"><use href="/icons/lucide.svg#chevron-right"/></svg></button>';
document.body.appendChild(ov);
imgEl=ov.querySelector('[data-lb-img]');capEl=ov.querySelector('[data-lb-cap]');
ov.addEventListener('click',function(e){if(e.target===ov||e.target.closest('[data-lb-close]'))close();});
ov.querySelector('[data-lb-prev]').addEventListener('click',function(e){e.stopPropagation();show(idx-1);});
ov.querySelector('[data-lb-next]').addEventListener('click',function(e){e.stopPropagation();show(idx+1);});
}
function show(i){
if(!items.length)return;
idx=(i+items.length)%items.length;
var a=items[idx];var im=a.querySelector('img');
imgEl.src=(im&&(im.currentSrc||im.src))||a.getAttribute('href');
imgEl.alt=im?im.alt:'';
var cap=a.getAttribute('data-caption')||'';capEl.textContent=cap;capEl.style.display=cap?'block':'none';
}
function open(g,link){
if(!ov)build();
items=Array.prototype.slice.call(g.querySelectorAll('[data-bn-gallery-item]'));
lastFocus=document.activeElement;ov.style.display='flex';
var multi=items.length>1;
ov.querySelector('[data-lb-prev]').style.display=multi?'inline-flex':'none';
ov.querySelector('[data-lb-next]').style.display=multi?'inline-flex':'none';
show(items.indexOf(link));ov.focus();
}
function close(){if(ov)ov.style.display='none';if(lastFocus&&lastFocus.focus)lastFocus.focus();}
document.addEventListener('click',function(e){
var link=e.target.closest('[data-bn-gallery][data-lightbox="true"] [data-bn-gallery-item]');
if(!link)return;e.preventDefault();open(link.closest('[data-bn-gallery]'),link);
});
document.addEventListener('keydown',function(e){
if(!ov||ov.style.display==='none')return;
if(e.key==='Escape'){e.preventDefault();close();}
else if(e.key==='ArrowLeft')show(idx-1);
else if(e.key==='ArrowRight')show(idx+1);
});
})();
</script>{% endif %}{% endwith %}