ComparisonTable list
Drie-koloms vergelijking: wij vs. anderen. Vinkje/kruis per feature. Ons kolom geaccentueerd.
src/components/list/ComparisonTable.astro
---
interface Props {
eyebrow?: string;
headline?: string;
ourLabel?: string;
theirLabel?: string;
features: {
label: string;
ours: boolean | string;
theirs: boolean | string;
}[];
}
const {
eyebrow,
headline,
ourLabel = 'Wij',
theirLabel = 'Anderen',
features = [],
} = Astro.props;
const checkSVG = `<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><circle cx="9" cy="9" r="9" fill="rgba(34,197,94,0.12)"/><path d="M5.5 9.5L7.5 11.5L12.5 6.5" stroke="#16a34a" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
const crossSVG = `<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><circle cx="9" cy="9" r="9" fill="rgba(0,0,0,0.06)"/><path d="M6 6L12 12M12 6L6 12" stroke="#9ca3af" stroke-width="1.75" stroke-linecap="round"/></svg>`;
function renderCell(value: boolean | string) {
if (value === true) return checkSVG;
if (value === false) return crossSVG;
return `<span class="cmp__cell-text">${value}</span>`;
}
---
<section class="cmp__section">
{(eyebrow || headline) && (
<div class="cmp__header">
{eyebrow && <p class="cmp__eyebrow">{eyebrow}</p>}
{headline && <h2 class="cmp__headline">{headline}</h2>}
</div>
)}
<div class="cmp__table-wrap">
<table class="cmp__table" role="table">
<thead>
<tr class="cmp__head-row">
<th class="cmp__th cmp__th--label" scope="col"></th>
<th class="cmp__th cmp__th--ours" scope="col">
<span class="cmp__our-pill">{ourLabel}</span>
</th>
<th class="cmp__th cmp__th--theirs" scope="col">
<span class="cmp__their-label">{theirLabel}</span>
</th>
</tr>
</thead>
<tbody>
{features.map((feature, i) => (
<tr class={`cmp__row${i % 2 === 1 ? ' cmp__row--even' : ''}`}>
<td class="cmp__td cmp__td--label">{feature.label}</td>
<td class="cmp__td cmp__td--ours" set:html={renderCell(feature.ours)} />
<td class="cmp__td cmp__td--theirs" set:html={renderCell(feature.theirs)} />
</tr>
))}
</tbody>
</table>
</div>
</section>
<style>
:root {
--color-primary: #0a0a0a;
--color-accent: #6366f1;
--color-bg: #fff;
--color-muted: #6b7280;
--radius: 0.5rem;
}
.cmp__section {
padding: 5rem 1.5rem;
max-width: 56rem;
margin-inline: auto;
}
/* Header */
.cmp__header {
text-align: center;
margin-bottom: 2.5rem;
}
.cmp__eyebrow {
font-size: 0.75rem;
font-weight: 600;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--color-accent);
margin: 0 0 0.75rem;
}
.cmp__headline {
font-size: clamp(1.75rem, 3vw, 2.5rem);
font-weight: 800;
color: var(--color-primary);
margin: 0;
line-height: 1.15;
}
/* Table wrapper */
.cmp__table-wrap {
overflow-x: auto;
border-radius: 0.875rem;
border: 1px solid rgba(0, 0, 0, 0.09);
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
}
/* Table */
.cmp__table {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
background: var(--color-bg);
border-radius: 0.875rem;
overflow: hidden;
}
/* Head */
.cmp__head-row {
background: #f9f9fb;
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}
.cmp__th {
padding: 1rem 1.25rem;
text-align: center;
font-weight: 600;
font-size: 0.875rem;
color: var(--color-muted);
}
.cmp__th--label {
text-align: left;
width: 45%;
}
.cmp__th--ours {
background: var(--color-accent);
width: 27.5%;
}
.cmp__th--theirs {
width: 27.5%;
}
/* Our pill in header */
.cmp__our-pill {
display: inline-flex;
align-items: center;
padding: 0.25em 0.9em;
border-radius: 999px;
background: #fff;
color: var(--color-accent);
font-size: 0.8125rem;
font-weight: 700;
letter-spacing: 0.03em;
}
.cmp__their-label {
color: var(--color-muted);
}
/* Rows */
.cmp__row {
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
transition: background 0.15s ease;
}
.cmp__row:last-child {
border-bottom: none;
}
.cmp__row--even {
background: rgba(0, 0, 0, 0.017);
}
.cmp__row:hover {
background: rgba(99, 102, 241, 0.03);
}
/* Cells */
.cmp__td {
padding: 0.9rem 1.25rem;
font-size: 0.9375rem;
vertical-align: middle;
text-align: center;
}
.cmp__td--label {
text-align: left;
color: var(--color-primary);
font-weight: 500;
}
.cmp__td--ours {
background: rgba(99, 102, 241, 0.04);
}
.cmp__td--ours :global(svg),
.cmp__td--theirs :global(svg) {
display: inline-block;
vertical-align: middle;
}
.cmp__cell-text {
font-size: 0.875rem;
color: var(--color-primary);
font-weight: 500;
}
/* Responsive */
@media (max-width: 520px) {
.cmp__td,
.cmp__th {
padding: 0.65rem 0.75rem;
font-size: 0.8125rem;
}
.cmp__th--label,
.cmp__td--label {
font-size: 0.8125rem;
}
}
@media (prefers-reduced-motion: reduce) {
* { animation: none !important; transition: none !important; }
}
</style>
Props
| Prop | Type | Default | Beschrijving |
|---|---|---|---|
features * | { label: string; ours: boolean | string; theirs: boolean | string }[] | — | Vergelijkingspunten |
ourLabel | string | 'Wij' | Kolom header voor ons |
theirLabel | string | 'Anderen' | Kolom header voor anderen |
eyebrow | string | — | Label boven sectie |
headline | string | — | Sectie headline |
* = verplicht