PricingMenu Sections
Menu-stijl prijslijst per categorie. Ideaal voor diensten en consultancy.
src/components/sections/PricingMenu.astro
---
/**
* PricingMenu
* Menu-stijl pricing — gestructureerde lijsten per categorie, ideaal voor diensten.
*/
interface PriceItem {
name: string;
description?: string;
price: string;
unit?: string;
popular?: boolean;
}
interface PriceCategory {
heading: string;
items: PriceItem[];
}
interface Props {
eyebrow?: string;
headline?: string;
sub?: string;
categories: PriceCategory[];
ctaLabel?: string;
ctaHref?: string;
note?: string;
}
const {
eyebrow,
headline,
sub,
categories = [],
ctaLabel,
ctaHref,
note,
} = Astro.props;
---
<section class="pm" data-component="pricing-menu">
<div class="pm__inner">
{(eyebrow || headline || sub) && (
<div class="pm__header">
{eyebrow && <p class="pm__eyebrow">{eyebrow}</p>}
{headline && <h2 class="pm__title" set:html={headline} />}
{sub && <p class="pm__sub">{sub}</p>}
</div>
)}
<div class="pm__categories">
{categories.map(cat => (
<div class="pm__category">
<h3 class="pm__cat-heading">{cat.heading}</h3>
<div class="pm__items">
{cat.items.map(item => (
<div class:list={['pm__item', { 'pm__item--popular': item.popular }]}>
<div class="pm__item-left">
<div class="pm__item-name">
{item.name}
{item.popular && <span class="pm__popular">Populair</span>}
</div>
{item.description && <div class="pm__item-desc">{item.description}</div>}
</div>
<div class="pm__item-price">
<span class="pm__price">{item.price}</span>
{item.unit && <span class="pm__unit">/{item.unit}</span>}
</div>
</div>
))}
</div>
</div>
))}
</div>
{(ctaLabel || note) && (
<div class="pm__footer">
{note && <p class="pm__note">{note}</p>}
{ctaLabel && (
<a href={ctaHref ?? '#'} class="pm__cta">{ctaLabel}</a>
)}
</div>
)}
</div>
</section>
<style>
.pm {
background: var(--color-bg, #fff);
padding: 5rem 1.5rem;
}
.pm__inner {
max-width: 800px;
margin: 0 auto;
}
.pm__header {
text-align: center;
margin-bottom: 3.5rem;
}
.pm__eyebrow {
font-size: 0.75rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--color-accent, #6366f1);
margin-bottom: 0.75rem;
}
.pm__title {
font-size: clamp(1.875rem, 3vw, 2.75rem);
font-weight: 800;
letter-spacing: -0.03em;
color: var(--color-primary, #0a0a0a);
margin-bottom: 0.875rem;
}
.pm__title :global(em) { font-style: normal; color: var(--color-accent, #6366f1); }
.pm__sub {
font-size: 1.0625rem;
color: var(--color-muted, #6b7280);
line-height: 1.6;
}
.pm__categories {
display: flex;
flex-direction: column;
gap: 2.5rem;
}
.pm__cat-heading {
font-size: 0.75rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--color-muted, #6b7280);
margin-bottom: 0.75rem;
padding-bottom: 0.625rem;
border-bottom: 1px solid rgba(0,0,0,0.07);
}
.pm__items {
display: flex;
flex-direction: column;
}
.pm__item {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 1rem;
padding: 1.25rem 0;
border-bottom: 1px solid rgba(0,0,0,0.05);
}
.pm__item:last-child { border-bottom: none; }
.pm__item--popular {
background: color-mix(in srgb, var(--color-accent, #6366f1) 5%, transparent);
margin: 0 -1rem;
padding: 1.25rem 1rem;
border-radius: var(--radius, 0.5rem);
border-bottom: none;
}
.pm__item-name {
font-size: 1rem;
font-weight: 600;
color: var(--color-primary, #0a0a0a);
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.25rem;
}
.pm__popular {
display: inline-block;
font-size: 0.6875rem;
font-weight: 700;
background: var(--color-accent, #6366f1);
color: #fff;
padding: 0.15rem 0.5rem;
border-radius: 100px;
}
.pm__item-desc {
font-size: 0.875rem;
color: var(--color-muted, #6b7280);
line-height: 1.5;
}
.pm__item-price {
text-align: right;
flex-shrink: 0;
}
.pm__price {
font-size: 1.25rem;
font-weight: 800;
letter-spacing: -0.02em;
color: var(--color-primary, #0a0a0a);
}
.pm__unit {
font-size: 0.8125rem;
color: var(--color-muted, #6b7280);
}
.pm__footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
padding-top: 2.5rem;
border-top: 1px solid rgba(0,0,0,0.07);
margin-top: 2.5rem;
flex-wrap: wrap;
}
.pm__note {
font-size: 0.875rem;
color: var(--color-muted, #6b7280);
}
.pm__cta {
display: inline-block;
background: var(--color-accent, #6366f1);
color: #fff;
padding: 0.8125rem 1.75rem;
border-radius: var(--radius, 0.5rem);
font-weight: 700;
font-size: 0.9375rem;
text-decoration: none;
transition: filter 0.2s;
}
.pm__cta:hover { filter: brightness(1.1); }
</style>
Props
| Prop | Type | Default | Beschrijving |
|---|---|---|---|
categories * | PriceCategory[] | — | Prijscategorieën met items |
headline | string | — | Sectie headline |
ctaLabel | string | — | CTA knop onder tabel |
note | string | — | Noot onderaan (bv. excl. BTW) |
* = verplicht