Zoeken...  ⌘K GitHub

HeroPortfolio Hero

Donkere portfolio-hero: tekstkolom links (eyebrow-pill, kop met accent, sub, CTA) en rechts een stapel case-cards (beeld + categorie + titel).

/hero-portfolio
src/components/hero/HeroPortfolio.astro
---
/**
 * HeroPortfolio
 * Donkere portfolio-hero: tekstkolom links (eyebrow-pill, kop met cursief
 * accent, sub, CTA) en rechts een stapel case-cards (beeld + categorie + titel).
 * Content ligt op de pagina-rail (.bl-inner).
 *
 * Props: zie interface. Alle copy is prop-driven; cases is een array.
 */
interface Case {
  image: string;
  alt?: string;
  category: string;
  title: string;
  href?: string;
}
interface Props {
  eyebrow?: string;
  headline: string;
  accent?: string;
  sub?: string;
  cta?: { label: string; href: string };
  cases?: Case[];
}

const {
  eyebrow,
  headline,
  accent,
  sub,
  cta,
  cases = [],
} = Astro.props;
---

<section class:list={['bl-section', 'hpf__root']} aria-label="Hero">
  <div class="bl-inner hpf__container">
    <div class="hpf__left">
      {eyebrow && <span class="hpf__eyebrow">{eyebrow}</span>}
      <h1 class="hpf__headline">{headline}{accent && <em> {accent}</em>}</h1>
      {sub && <p class="hpf__sub">{sub}</p>}
      {cta && (
        <a href={cta.href} class="hpf__cta">
          <span>{cta.label}</span>
          <svg class="hpf__cta-arrow" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M3 8h10M9 4l4 4-4 4" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"></path></svg>
        </a>
      )}
    </div>
    {cases.length > 0 && (
      <div class="hpf__right" aria-label="Portfolio cases">
        {cases.map((c, i) => (
          <article class="hpf__card" data-index={i}>
            <a href={c.href ?? '#'} class="hpf__card-link">
              <div class="hpf__card-img-wrap">
                <img data-allow-img src={c.image} alt={c.alt ?? c.title} class="hpf__card-img" loading={i === 0 ? 'eager' : 'lazy'} fetchpriority={i === 0 ? 'high' : 'auto'} decoding="async" />
              </div>
              <div class="hpf__card-body">
                <span class="hpf__card-cat">{c.category}</span>
                <h2 class="hpf__card-title">{c.title}</h2>
              </div>
            </a>
          </article>
        ))}
      </div>
    )}
  </div>
</section>

<style>
.hpf__root{background:var(--color-primary, #0a0a0a);color:#fff;min-height:100svh;display:flex;align-items:center;overflow:hidden}
.hpf__container{display:grid;grid-template-columns:1fr 1fr;gap:clamp(3rem, 6vw, 5rem);align-items:center}
.hpf__left{display:flex;flex-direction:column;gap:1.5rem}
.hpf__eyebrow{display:inline-block;font-size:.8125rem;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:var(--color-accent, #6366f1);padding:.3rem .875rem;border:1px solid var(--color-accent, #6366f1);border-radius:999px;width:fit-content}
.hpf__headline{font-size:clamp(3.5rem, 5.5vw, 5rem);font-weight:900;line-height:1;letter-spacing:-.035em;color:#fff;margin:0}
.hpf__headline em{font-style:normal;color:var(--color-accent, #6366f1)}
.hpf__sub{font-size:clamp(1rem,1.4vw,1.1875rem);color:#cfcfcf;line-height:1.7;margin:0;max-width:44ch}
.hpf__cta{display:inline-flex;align-items:center;gap:.5rem;background:var(--color-accent, #6366f1);color:#fff;text-decoration:none;font-weight:600;font-size:.9375rem;padding:.875rem 1.875rem;border-radius:var(--radius, .5rem);width:fit-content;transition:opacity .2s,transform .2s}
.hpf__cta:hover{opacity:.88;transform:translateY(-1px)}
.hpf__cta-arrow{flex-shrink:0;transition:transform .2s}
.hpf__cta:hover .hpf__cta-arrow{transform:translate(3px)}
.hpf__right{display:flex;flex-direction:column;gap:1.25rem;position:relative}
.hpf__card{border-radius:calc(var(--radius, .5rem) * 1.5);overflow:hidden;background:#161616;border:1px solid rgba(255,255,255,.07);transition:transform .28s cubic-bezier(.16,1,.3,1);will-change:transform}
.hpf__card:hover{transform:translateY(-4px)}
.hpf__card[data-index="0"]{flex:0 0 auto}
.hpf__card-link{display:block;text-decoration:none;color:inherit}
.hpf__card-img-wrap{width:100%;overflow:hidden;aspect-ratio:16 / 7;background:#222}
.hpf__card[data-index="0"] .hpf__card-img-wrap{aspect-ratio:16 / 8}
.hpf__card-img{width:100%;height:100%;object-fit:cover;display:block;transition:transform .5s cubic-bezier(.16,1,.3,1)}
.hpf__card:hover .hpf__card-img{transform:scale(1.04)}
.hpf__card-body{padding:1.125rem 1.375rem 1.25rem;display:flex;flex-direction:column;gap:.375rem}
.hpf__card-cat{font-size:.75rem;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--color-accent, #6366f1)}
.hpf__card-title{font-size:clamp(1rem,1.5vw,1.1875rem);font-weight:700;color:#fff;margin:0;line-height:1.3}
@media (max-width:768px){
  .hpf__container{grid-template-columns:1fr}
  .hpf__right{flex-direction:row;overflow-x:auto;gap:1rem;padding-bottom:.5rem;scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch}
  .hpf__card{min-width:min(280px,78vw);scroll-snap-align:start}
  .hpf__card-img-wrap,.hpf__card[data-index="0"] .hpf__card-img-wrap{aspect-ratio:4 / 3}
}
</style>

Props

Prop Type Default Beschrijving
eyebrow string - Eyebrow-pill boven de kop
headline * string - H1-kop
accent string - Cursief accent direct na de kop
sub string - Ondertitel
cta { label: string; href: string } - CTA-knop
cases { image: string; alt?: string; category: string; title: string; href?: string }[] - Case-cards rechts

* = verplicht