LogoGrid Social Proof
Statisch CSS grid van klantlogo's. Grayscale met kleur op hover. 3-6 kolommen. Geen scrollanimatie.
src/components/social-proof/LogoGrid.astro
---
interface Logo {
name: string;
src: string;
href?: string;
}
interface Props {
eyebrow?: string;
headline?: string;
logos: Logo[];
columns?: 3 | 4 | 5 | 6;
grayscale?: boolean;
size?: 'sm' | 'md' | 'lg';
}
const {
eyebrow,
headline,
logos,
columns = 5,
grayscale = true,
size = 'md',
} = Astro.props;
---
<section class={`lgr__section lgr__cols-${columns} lgr__size-${size} ${grayscale ? 'lgr--grayscale' : ''}`}>
{(eyebrow || headline) && (
<header class="lgr__header">
{eyebrow && <p class="lgr__eyebrow">{eyebrow}</p>}
{headline && <h2 class="lgr__headline">{headline}</h2>}
</header>
)}
<ul class="lgr__grid" role="list">
{logos.map((logo) => (
<li class="lgr__cell">
{logo.href ? (
<a class="lgr__link" href={logo.href} target="_blank" rel="noopener noreferrer" aria-label={logo.name}>
<img class="lgr__logo" src={logo.src} alt={logo.name} loading="lazy" />
</a>
) : (
<span class="lgr__link">
<img class="lgr__logo" src={logo.src} alt={logo.name} loading="lazy" />
</span>
)}
</li>
))}
</ul>
</section>
<style>
:root {
--color-primary: #0a0a0a;
--color-accent: #6366f1;
--color-bg: #fff;
--color-muted: #6b7280;
--radius: 0.5rem;
}
.lgr__section {
padding: 4rem 1.5rem;
max-width: 1200px;
margin: 0 auto;
}
.lgr__header {
text-align: center;
margin-bottom: 2.5rem;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
}
.lgr__eyebrow {
margin: 0;
font-size: 0.75rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--color-muted);
}
.lgr__headline {
margin: 0;
font-size: clamp(1.25rem, 2vw, 1.75rem);
font-weight: 700;
color: var(--color-primary);
}
.lgr__grid {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 0;
}
.lgr__cols-3 .lgr__grid { grid-template-columns: repeat(3, 1fr); }
.lgr__cols-4 .lgr__grid { grid-template-columns: repeat(4, 1fr); }
.lgr__cols-5 .lgr__grid { grid-template-columns: repeat(5, 1fr); }
.lgr__cols-6 .lgr__grid { grid-template-columns: repeat(6, 1fr); }
.lgr__cell {
display: flex;
align-items: center;
justify-content: center;
padding: 1.5rem 2rem;
border: 1px solid rgba(0, 0, 0, 0.06);
margin: -0.5px;
}
.lgr__link {
display: flex;
align-items: center;
justify-content: center;
text-decoration: none;
}
.lgr__logo {
object-fit: contain;
display: block;
transition: filter 0.25s ease;
}
/* Size variants */
.lgr__size-sm .lgr__logo { max-height: 28px; max-width: 120px; }
.lgr__size-md .lgr__logo { max-height: 40px; max-width: 160px; }
.lgr__size-lg .lgr__logo { max-height: 56px; max-width: 200px; }
/* Grayscale */
.lgr--grayscale .lgr__logo {
filter: grayscale(100%) opacity(0.5);
}
.lgr--grayscale .lgr__cell:hover .lgr__logo {
filter: none;
}
/* Responsive */
@media (max-width: 1024px) {
.lgr__cols-6 .lgr__grid { grid-template-columns: repeat(4, 1fr); }
.lgr__cols-5 .lgr__grid { grid-template-columns: repeat(4, 1fr); }
}
@media (max-width: 768px) {
.lgr__cols-6 .lgr__grid,
.lgr__cols-5 .lgr__grid,
.lgr__cols-4 .lgr__grid { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 480px) {
.lgr__grid { grid-template-columns: repeat(2, 1fr) !important; }
}
@media (prefers-reduced-motion: reduce) {
* {
animation: none !important;
transition: none !important;
}
}
</style>
Props
| Prop | Type | Default | Beschrijving |
|---|---|---|---|
logos * | { name: string; src: string; href?: string }[] | — | Logo items met naam en afbeelding URL |
columns | 3 | 4 | 5 | 6 | 4 | Aantal kolommen |
grayscale | boolean | true | Grayscale filter, kleur op hover |
size | 'sm' | 'md' | 'lg' | 'md' | Logo hoogte |
* = verplicht