Zoeken...  ⌘K GitHub

IconGrid icon

IconGrid component.

/icon-grid
src/components/icon/IconGrid.astro
---
/**
 * IconGrid
 * Sectie-header + grid van icon-kaarten. Elke kaart: SVG-icoon, titel, beschrijving.
 *
 * Props:
 * - eyebrow?: string
 * - headline?: string
 * - sub?: string
 * - columns?: 2 | 3 | 4
 * - size?: 'sm' | 'md' | 'lg'
 * - iconStyle?: 'plain' | 'circle' | 'square' | 'outlined'
 * - items?: Array<{ icon: string; title: string; desc: string }>   (icon = inline SVG via set:html)
 */
interface Props {
  eyebrow?: string;
  headline?: string;
  sub?: string;
  columns?: 2 | 3 | 4;
  size?: 'sm' | 'md' | 'lg';
  iconStyle?: 'plain' | 'circle' | 'square' | 'outlined';
  items?: { icon: string; title: string; desc: string }[];
}

const ICON_BARS = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/></svg>';
const ICON_USERS = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z"/></svg>';
const ICON_SEARCH = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/></svg>';
const ICON_CHART = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/></svg>';
const ICON_CODE = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"/></svg>';
const ICON_MAIL = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/></svg>';

const {
  eyebrow = 'Onze diensten',
  headline = 'Alles wat je nodig hebt om te groeien',
  sub,
  columns = 3,
  size = 'md',
  iconStyle = 'circle',
  items = [
    { icon: ICON_BARS, title: 'Search advertising', desc: 'Maximale ROI op elk advertentiebudget.' },
    { icon: ICON_USERS, title: 'Social advertising', desc: 'Bereik je ideale klant op de juiste kanalen.' },
    { icon: ICON_SEARCH, title: 'SEO', desc: 'Organische vindbaarheid die blijft werken.' },
    { icon: ICON_CHART, title: 'Analytics', desc: 'Inzicht in elk touchpoint van de customer journey.' },
    { icon: ICON_CODE, title: 'Webdesign', desc: 'Websites die converteren. Snel, mooi en meetbaar.' },
    { icon: ICON_MAIL, title: 'E-mailmarketing', desc: 'Geautomatiseerde flows die voor je verkopen.' },
  ],
} = Astro.props;
---

<section class={`bl-section igr__section igr__cols-${columns} igr__size-${size} igr__style-${iconStyle}`}>
  <div class="bl-inner igr__inner">
    {(eyebrow || headline || sub) && (
      <div class="igr__header">
        {eyebrow && <p class="igr__eyebrow">{eyebrow}</p>}
        {headline && <h2 class="igr__headline">{headline}</h2>}
        {sub && <p class="igr__sub">{sub}</p>}
      </div>
    )}
    <ul class="igr__grid" role="list">
      {items.map((item, i) => (
        <li class="igr__item" style={`--delay: ${i * 80}ms`}>
          <div class="igr__card">
            <span class="igr__icon-wrap" aria-hidden="true" set:html={item.icon}></span>
            <span class="igr__title">{item.title}</span>
            <span class="igr__desc">{item.desc}</span>
          </div>
        </li>
      ))}
    </ul>
  </div>
</section>

<style>
.igr__header{margin-bottom:3rem}
.igr__eyebrow{font-size:.75rem;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--color-accent);margin:0 0 .75rem}
.igr__headline{font-size:clamp(1.75rem,3vw,2.5rem);font-weight:800;color:var(--color-primary);margin:0 0 1rem;line-height:1.15}
.igr__sub{font-size:1.0625rem;color:var(--color-muted);max-width:40rem;line-height:1.65;margin-top:0;margin-bottom:0}
.igr__grid{display:grid;gap:1.5rem;list-style:none;padding:0;margin:0}
.igr__cols-2 .igr__grid{grid-template-columns:repeat(2,1fr)}
.igr__cols-3 .igr__grid{grid-template-columns:repeat(3,1fr)}
.igr__cols-4 .igr__grid{grid-template-columns:repeat(4,1fr)}
.igr__card{display:flex;flex-direction:column;gap:.875rem;padding:1.5rem;border-radius:var(--radius);border:1px solid rgba(0,0,0,.07);background:var(--color-bg);height:100%;transition:transform .22s ease,box-shadow .22s ease}
.igr__card--link{text-decoration:none;color:inherit}
.igr__card:hover{transform:translateY(-3px);box-shadow:0 8px 24px #00000014}
.igr__icon-wrap{display:flex;align-items:center;justify-content:center;flex-shrink:0;color:var(--color-accent)}
.igr__size-md .igr__icon-wrap{width:48px;height:48px}
.igr__size-sm .igr__icon-wrap{width:40px;height:40px}
.igr__size-lg .igr__icon-wrap{width:64px;height:64px}
.igr__icon-wrap svg{width:55%;height:55%}
.igr__style-plain .igr__icon-wrap{background:none;border:none}
.igr__style-circle .igr__icon-wrap{border-radius:50%;background:#6366f114}
.igr__style-square .igr__icon-wrap{border-radius:var(--radius);background:#6366f114}
.igr__style-outlined .igr__icon-wrap{border-radius:var(--radius);border:1.5px solid rgba(99,102,241,.35);background:transparent}
.igr__title{font-size:1.0625rem;font-weight:700;color:var(--color-primary);line-height:1.3}
.igr__desc{font-size:.9375rem;color:var(--color-muted);line-height:1.65}
.igr__item{opacity:0;transform:translateY(20px)}
.igr__item--visible{animation:igr-fadein .5s ease forwards;animation-delay:var(--delay, 0ms)}
@keyframes igr-fadein{to{opacity:1;transform:translateY(0)}}
@media (max-width: 900px){.igr__cols-4 .igr__grid{grid-template-columns:repeat(2,1fr)}}
@media (max-width: 600px){.igr__cols-2 .igr__grid,.igr__cols-3 .igr__grid,.igr__cols-4 .igr__grid{grid-template-columns:1fr}}
</style>