Zoeken...  ⌘K GitHub

PricingCompact Sections

Compacte horizontale pricing tabel — plannen als kolommen, features als rijen, check/cross symbolen.

/pricing-compact
src/components/sections/PricingCompact.astro
---
/**
 * PricingCompact
 * Compacte horizontale pricing: plannen naast elkaar in één rij,
 * features als checklijst, highlighted plan in het midden.
 * Geen toggle — simpel en snel scanbaar.
 */
interface Feature {
  label: string;
  plans: (boolean | string)[];
}

interface Plan {
  name: string;
  price: string;
  period?: string;
  description?: string;
  ctaLabel?: string;
  ctaHref?: string;
  highlighted?: boolean;
  badge?: string;
}

interface Props {
  preHeadline?: string;
  headline: string;
  sub?: string;
  plans: Plan[];
  features?: Feature[];
  note?: string;
}

const {
  preHeadline,
  headline,
  sub,
  plans = [],
  features = [],
  note,
} = Astro.props;
---

<section class="pc" data-component="pricing-compact">
  <div class="pc__inner">

    <div class="pc__header">
      {preHeadline && <p class="pc__pre">{preHeadline}</p>}
      <h2 class="pc__headline" set:html={headline} />
      {sub && <p class="pc__sub">{sub}</p>}
    </div>

    <div class="pc__table-wrap">
      <div class="pc__table" style={`--cols:${plans.length + 1}`}>

        <!-- Header row: plan names -->
        <div class="pc__row pc__row--header">
          <div class="pc__cell pc__cell--label"></div>
          {plans.map(plan => (
            <div class:list={['pc__cell pc__cell--plan', { 'pc__cell--highlighted': plan.highlighted }]}>
              {plan.badge && <span class="pc__badge">{plan.badge}</span>}
              <span class="pc__plan-name">{plan.name}</span>
              <div class="pc__price-wrap">
                <span class="pc__price">{plan.price}</span>
                {plan.period && <span class="pc__period">/{plan.period}</span>}
              </div>
              {plan.description && <p class="pc__plan-desc">{plan.description}</p>}
              <a href={plan.ctaHref ?? '#'} class:list={['pc__cta', { 'pc__cta--primary': plan.highlighted }]}>
                {plan.ctaLabel ?? 'Kies dit plan'}
              </a>
            </div>
          ))}
        </div>

        <!-- Feature rows -->
        {features.map((feature, fi) => (
          <div class="pc__row">
            <div class="pc__cell pc__cell--label">{feature.label}</div>
            {plans.map((_, pi) => (
              <div class:list={['pc__cell', { 'pc__cell--highlighted': plans[pi].highlighted }]}>
                {feature.plans[pi] === true && (
                  <svg class="pc__check" width="18" height="18" viewBox="0 0 18 18" fill="none">
                    <path d="M3.5 9l4 4 7-7" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"/>
                  </svg>
                )}
                {feature.plans[pi] === false && (
                  <svg class="pc__cross" width="18" height="18" viewBox="0 0 18 18" fill="none">
                    <path d="M5 5l8 8M13 5l-8 8" stroke="currentColor" stroke-width="1.75" stroke-linecap="round"/>
                  </svg>
                )}
                {typeof feature.plans[pi] === 'string' && (
                  <span class="pc__val">{feature.plans[pi]}</span>
                )}
              </div>
            ))}
          </div>
        ))}

      </div>
    </div>

    {note && <p class="pc__note">{note}</p>}
  </div>
</section>

<style>
  .pc {
    padding: 6rem 1.5rem;
    background: var(--color-bg, #fff);
  }

  .pc__inner { max-width: 1100px; margin: 0 auto; }

  .pc__header {
    text-align: center;
    max-width: 600px;
    margin: 0 auto 4rem;
  }

  .pc__pre {
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--color-accent, #6366f1);
    margin-bottom: 0.875rem;
  }

  .pc__headline {
    font-size: clamp(1.75rem, 3.5vw, 3rem);
    font-weight: 800;
    letter-spacing: -0.035em;
    line-height: 1.1;
    color: var(--color-primary, #0a0a0a);
    margin-bottom: 0.875rem;
  }

  .pc__headline :global(em) {
    font-style: normal;
    color: var(--color-accent, #6366f1);
  }

  .pc__sub {
    font-size: 1rem;
    line-height: 1.65;
    color: var(--color-muted, #6b7280);
  }

  /* Table */
  .pc__table-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    border-radius: 1rem;
    border: 1px solid rgba(0,0,0,0.07);
  }

  .pc__table {
    min-width: 640px;
    display: grid;
    grid-template-columns: 1.5fr repeat(calc(var(--cols) - 1), 1fr);
  }

  .pc__row {
    display: contents;
  }

  .pc__cell {
    padding: 1.125rem 1.25rem;
    border-bottom: 1px solid rgba(0,0,0,0.05);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.9375rem;
    color: var(--color-primary, #0a0a0a);
  }

  .pc__cell--label {
    justify-content: flex-start;
    font-size: 0.875rem;
    color: var(--color-muted, #6b7280);
  }

  .pc__cell--highlighted {
    background: rgba(99,102,241,0.04);
  }

  .pc__row--header .pc__cell {
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    padding: 2rem 1.25rem;
    border-bottom: 2px solid rgba(0,0,0,0.07);
  }

  .pc__row--header .pc__cell--highlighted {
    background: rgba(99,102,241,0.06);
    border-bottom-color: var(--color-accent, #6366f1);
  }

  .pc__badge {
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    background: var(--color-accent, #6366f1);
    color: #fff;
    padding: 0.1875rem 0.625rem;
    border-radius: 999px;
  }

  .pc__plan-name {
    font-size: 0.875rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    color: var(--color-primary, #0a0a0a);
  }

  .pc__price-wrap {
    display: flex;
    align-items: baseline;
    gap: 0.1875rem;
  }

  .pc__price {
    font-size: 2rem;
    font-weight: 900;
    letter-spacing: -0.04em;
    color: var(--color-primary, #0a0a0a);
    line-height: 1;
  }

  .pc__period {
    font-size: 0.8125rem;
    color: var(--color-muted, #6b7280);
  }

  .pc__plan-desc {
    font-size: 0.8125rem;
    color: var(--color-muted, #6b7280);
    text-align: center;
    line-height: 1.4;
  }

  .pc__cta {
    display: inline-flex;
    align-items: center;
    background: rgba(0,0,0,0.06);
    color: var(--color-primary, #0a0a0a);
    padding: 0.625rem 1.25rem;
    border-radius: var(--radius, 0.5rem);
    font-size: 0.875rem;
    font-weight: 700;
    text-decoration: none;
    transition: background 0.2s;
    margin-top: 0.25rem;
  }

  .pc__cta:hover { background: rgba(0,0,0,0.1); }

  .pc__cta--primary {
    background: var(--color-accent, #6366f1);
    color: #fff;
  }

  .pc__cta--primary:hover {
    background: #4f52d4;
  }

  .pc__check { color: var(--color-accent, #6366f1); }
  .pc__cross { color: rgba(0,0,0,0.2); }

  .pc__val {
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--color-primary, #0a0a0a);
  }

  .pc__note {
    text-align: center;
    font-size: 0.8125rem;
    color: var(--color-muted, #6b7280);
    margin-top: 1.5rem;
  }
</style>

Props

Prop Type Default Beschrijving
plans * Plan[] Plannen als kolommen
features Feature[] Features met per plan: true/false/string
headline string Sectie headline

* = verplicht