src/components/video/VideoGrid.astro
---
interface VideoGridItem {
href?: string;
poster?: string;
alt: string;
title: string;
duration?: string;
category?: string;
}
interface Props {
videos?: VideoGridItem[];
columns?: number;
heading?: string;
}
const {
videos = [
{ alt: 'Case 1', title: 'TechFlow: van 0 naar 280% meer leads', duration: '4:12', category: 'Case' },
{ alt: 'Case 2', title: 'Mode & Meer: 5.1x ROAS op Meta', duration: '3:50', category: 'Case' },
{ alt: 'Tutorial 1', title: 'Google Ads structuur opzetten in 2024', duration: '9:20', category: 'Tutorial' },
{ alt: 'Tutorial 2', title: 'SEO fundament bouwen stap voor stap', duration: '11:05', category: 'Tutorial' },
{ alt: 'Interview 1', title: 'Gesprek met onze Strategy Director', duration: '7:33', category: 'Interview' },
{ alt: 'Podcast', title: 'BLURR Podcast: e-commerce groei', duration: '28:14', category: 'Podcast' },
],
columns = 3,
heading = 'Video library',
} = Astro.props;
---
<section class="vgr">
{heading && <h2 class="vgr__heading">{heading}</h2>}
<div class="vgr__grid" style={`--vgr-cols: ${columns}`}>
{videos.map((v) => (
<a class="vgr__item" href={v.href ?? '#'} aria-label={v.title}>
<div class="vgr__media">
{v.poster ? (
<img class="vgr__poster" src={v.poster} alt={v.alt} />
) : (
<div class="vgr__placeholder" aria-label={v.alt}></div>
)}
<span class="vgr__play" aria-hidden="true">▶</span>
{v.duration && <span class="vgr__duration">{v.duration}</span>}
{v.category && <span class="vgr__category">{v.category}</span>}
</div>
<p class="vgr__title">{v.title}</p>
</a>
))}
</div>
</section>
<style>
:root {
--color-accent: #6366f1;
--color-primary: #0a0a0a;
}
.vgr { padding: 2rem 0; }
.vgr__heading {
font-size: 2rem;
font-weight: 700;
color: var(--color-primary, #0a0a0a);
margin: 0 0 2rem;
}
.vgr__grid {
display: grid;
grid-template-columns: repeat(var(--vgr-cols, 3), 1fr);
gap: 1.5rem;
}
.vgr__item { text-decoration: none; }
.vgr__media {
position: relative;
aspect-ratio: 16/9;
border-radius: 8px;
overflow: hidden;
background: #111;
margin-bottom: 0.75rem;
}
.vgr__poster { width: 100%; height: 100%; object-fit: cover; transition: transform 0.3s; }
.vgr__item:hover .vgr__poster { transform: scale(1.04); }
.vgr__placeholder {
width: 100%;
height: 100%;
background: linear-gradient(135deg, #1a1a30 0%, #2a2a50 100%);
}
.vgr__play {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
color: rgba(255,255,255,0.8);
}
.vgr__duration {
position: absolute;
bottom: 0.5rem;
right: 0.5rem;
background: rgba(0,0,0,0.75);
color: #fff;
font-size: 0.7rem;
font-weight: 700;
padding: 0.15rem 0.4rem;
border-radius: 3px;
}
.vgr__category {
position: absolute;
top: 0.5rem;
left: 0.5rem;
background: var(--color-accent, #6366f1);
color: #fff;
font-size: 0.65rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
padding: 0.2rem 0.5rem;
border-radius: 3px;
}
.vgr__title {
font-size: 0.9rem;
font-weight: 600;
color: var(--color-primary, #0a0a0a);
margin: 0;
line-height: 1.4;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
@media (max-width: 768px) { .vgr__grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 480px) { .vgr__grid { grid-template-columns: 1fr; } }
</style>
Props
| Prop | Type | Default | Beschrijving |
|---|---|---|---|
videos | { href?: string; poster?: string; alt: string; title: string; duration?: string; category?: string }[] | — | Video-items |
columns | number | — | Aantal kolommen |
heading | string | — | Sectietitel |
* = verplicht