Zoeken...  ⌘K GitHub

HeroMagazine Hero

Editorial magazine-hero: meta-kolom (vol/issue, byline, tags), center met kop + deck + tekstlink, beeld rechts en een lopende ticker.

/hero-magazine
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>&nbsp;</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