HeroMagazine Hero
Editorial magazine-hero: meta-kolom (vol/issue, byline, tags), center met kop + deck + tekstlink, beeld rechts en een lopende ticker.
src/components/hero/HeroMagazine.astro
---
/**
* HeroMagazine
* Editorial magazine-hero: meta-kolom links (vol/issue, byline, tags), center
* met kop (cursief accent) + deck + tekstlink-CTA, beeld rechts, en een
* lopende ticker onderaan. De layout ligt binnen .bl-inner (rail-uitgelijnd).
*
* Props: zie interface. Alle copy is prop-driven.
*/
interface Props {
vol?: string;
issue?: string;
byline?: string;
tags?: string[];
headline: string;
accent?: string;
deck?: string;
cta?: { label: string; href: string };
image?: { src: string; alt?: string };
caption?: string;
ticker?: string;
}
const {
vol = 'Vol. 01',
issue,
byline,
tags = [],
headline,
accent,
deck,
cta,
image,
caption,
ticker,
} = Astro.props;
const tickerText = ticker ?? `${headline}${accent ? ' ' + accent : ''}`;
const tickerItems = Array.from({ length: 8 });
---
<section class:list={['bl-section', 'hm']} data-component="hero-magazine">
<div class="bl-inner hm__inner">
<div class="hm__meta">
<div class="hm__meta-top">
<span class="hm__issue">{vol}</span>
{issue && <span class="hm__issue-num">{issue}</span>}
</div>
{byline && (
<div class="hm__byline-wrap">
<div class="hm__byline-line"></div>
<p class="hm__byline">{byline}</p>
</div>
)}
{tags.length > 0 && (
<div class="hm__tags">
{tags.map((tag) => <span class="hm__tag">{tag}</span>)}
</div>
)}
</div>
<div class="hm__center">
<div class="hm__rule-top"></div>
<h1 class="hm__headline">{headline}{accent && <em> {accent}</em>}</h1>
<div class="hm__rule-bot"></div>
{deck && <p class="hm__deck">{deck}</p>}
{cta && (
<a href={cta.href} class="hm__cta">
<span>{cta.label}</span>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8h10M9 4l4 4-4 4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></svg>
</a>
)}
</div>
{image && (
<div class="hm__right">
<div class="hm__img-frame">
<img data-allow-img src={image.src} alt={image.alt ?? ''} class="hm__img" loading="eager" fetchpriority="high" decoding="async" />
</div>
{caption && <div class="hm__img-caption"><span>{caption}</span></div>}
</div>
)}
</div>
<div class="hm__ticker" aria-hidden="true">
<div class="hm__ticker-track">
{tickerItems.map(() => (
<span class="hm__ticker-item">{tickerText} <span class="hm__ticker-dot">◆</span> </span>
))}
</div>
</div>
</section>
<style>
.hm{min-height:100vh;display:flex;flex-direction:column;background:var(--color-bg, #fff);overflow:hidden;border-bottom:1px solid var(--color-primary, #0a0a0a);padding-block:0}
.hm__inner{flex:1;display:grid;grid-template-columns:120px 1fr 340px;gap:0;padding-inline:0}
.hm__meta{border-right:1px solid var(--color-primary, #0a0a0a);padding:3rem 1.5rem;display:flex;flex-direction:column;gap:3rem;animation:hm-slide-in .7s cubic-bezier(.22,1,.36,1) both}
.hm__meta-top{display:flex;flex-direction:column;gap:.25rem}
.hm__issue{font-size:.75rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--color-primary, #0a0a0a);writing-mode:vertical-lr;transform:rotate(180deg)}
.hm__issue-num{font-size:.75rem;letter-spacing:.08em;color:var(--color-muted, #6b7280);writing-mode:vertical-lr;transform:rotate(180deg)}
.hm__byline-wrap{display:flex;flex-direction:column;gap:.75rem;align-items:center}
.hm__byline-line{width:1px;height:48px;background:var(--color-primary, #0a0a0a);opacity:.3}
.hm__byline{font-size:.75rem;line-height:1.4;color:var(--color-muted, #6b7280);text-align:center;writing-mode:vertical-lr;transform:rotate(180deg);letter-spacing:.04em}
.hm__tags{display:flex;flex-direction:column;gap:.375rem;margin-top:auto}
.hm__tag{font-size:.6875rem;letter-spacing:.1em;text-transform:uppercase;color:var(--color-accent, #6366f1);font-weight:700;writing-mode:vertical-lr;transform:rotate(180deg)}
.hm__center{padding:4rem 3.5rem;display:flex;flex-direction:column;justify-content:center;border-right:1px solid var(--color-primary, #0a0a0a)}
.hm__rule-top,.hm__rule-bot{width:100%;height:2px;background:var(--color-primary, #0a0a0a)}
.hm__rule-top{animation:hm-rule-grow .7s .15s cubic-bezier(.22,1,.36,1) both;transform-origin:left}
.hm__rule-bot{animation:hm-rule-grow .7s .5s cubic-bezier(.22,1,.36,1) both;transform-origin:left}
.hm__headline{font-size:clamp(3.5rem, 5vw, 5rem);font-weight:900;line-height:.95;letter-spacing:-.05em;color:var(--color-primary, #0a0a0a);margin:1.5rem 0;animation:hm-fade-up .7s .25s cubic-bezier(.22,1,.36,1) both}
.hm__headline em{font-style:normal;color:var(--color-accent, #6366f1)}
.hm__deck{font-size:1rem;line-height:1.7;color:var(--color-muted, #6b7280);max-width:44ch;margin-bottom:2.5rem;animation:hm-fade-up .6s .45s cubic-bezier(.22,1,.36,1) both}
.hm__cta{display:inline-flex;align-items:center;gap:.625rem;font-size:.875rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--color-primary, #0a0a0a);text-decoration:none;border-bottom:2px solid var(--color-primary, #0a0a0a);padding-bottom:.25rem;width:fit-content;transition:gap .2s,color .2s,border-color .2s;animation:hm-fade-up .6s .55s cubic-bezier(.22,1,.36,1) both}
.hm__cta:hover{gap:1rem;color:var(--color-accent, #6366f1);border-color:var(--color-accent, #6366f1)}
.hm__right{display:flex;flex-direction:column;animation:hm-slide-in-right .9s .1s cubic-bezier(.22,1,.36,1) both}
.hm__img-frame{flex:1;overflow:hidden}
.hm__img{width:100%;height:100%;object-fit:cover;display:block;transition:transform .6s cubic-bezier(.22,1,.36,1)}
.hm__img-frame:hover .hm__img{transform:scale(1.03)}
.hm__img-caption{padding:.75rem 1rem;border-top:1px solid rgba(0,0,0,.1);font-size:.75rem;color:var(--color-muted, #6b7280);letter-spacing:.05em;min-height:36px}
.hm__ticker{border-top:1px solid var(--color-primary, #0a0a0a);background:var(--color-primary, #0a0a0a);overflow:hidden;height:44px;display:flex;align-items:center}
.hm__ticker-track{display:flex;white-space:nowrap;animation:hm-ticker 25s linear infinite}
.hm__ticker-item{font-size:.75rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#d4d4d4;padding-right:1rem}
.hm__ticker-dot{color:var(--color-accent, #6366f1)}
@media (prefers-reduced-motion: reduce){
.hm__meta,.hm__headline,.hm__deck,.hm__cta,.hm__right,.hm__rule-top,.hm__rule-bot,.hm__ticker-track{animation:none}
}
@media (max-width:1080px){
.hm__inner{grid-template-columns:80px 1fr 260px}
}
@media (max-width:768px){
.hm__inner{grid-template-columns:1fr}
.hm__meta{border-right:none;border-bottom:1px solid var(--color-primary, #0a0a0a);flex-direction:row;align-items:center;padding:1rem 1.5rem;gap:1.5rem}
.hm__issue,.hm__issue-num,.hm__byline,.hm__tag{writing-mode:horizontal-tb;transform:none}
.hm__byline-wrap{flex-direction:row}
.hm__byline-line{width:24px;height:1px}
.hm__tags{flex-direction:row;margin-top:0}
.hm__center{padding:2.5rem 1.5rem;border-right:none}
.hm__right{height:60vw}
}
</style>
Props
| Prop | Type | Default | Beschrijving |
|---|---|---|---|
vol | string | 'Vol. 01' | Volume-label |
issue | string | - | Issue-nummer |
byline | string | - | Byline |
tags | string[] | - | Tags in de meta-kolom |
headline * | string | - | H1-kop |
accent | string | - | Cursief accent direct na de kop |
deck | string | - | Deck / intro-tekst |
cta | { label: string; href: string } | - | Tekstlink-CTA |
image | { src: string; alt?: string } | - | Beeld rechts |
caption | string | - | Bijschrift onder het beeld |
ticker | string | - | Ticker-tekst (default: kop + accent) |
* = verplicht