Zoeken...  ⌘K GitHub

PricingMenu Sections

Menu-stijl prijslijst per categorie. Ideaal voor diensten en consultancy.

/pricing-menu
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