Zoeken...  ⌘K GitHub

NewsletterBar Forms

Compacte e-mail aanmeldbalk. Inline of gestapeld.

/newsletter-bar
src/components/forms/NewsletterBar.astro
---
/**
 * NewsletterBar
 * Compacte e-mail aanmeldbalk. Inline of gestapeld.
 *
 * Props:
 * - headline?: string
 * - placeholder?: string
 * - btnLabel?: string
 * - action: string — form action (Mailchimp, ConvertKit, eigen API)
 * - disclaimer?: string — privacy tekst
 * - layout?: 'inline' | 'stacked'
 */
interface Props {
  headline?: string;
  placeholder?: string;
  btnLabel?: string;
  action: string;
  disclaimer?: string;
  layout?: 'inline' | 'stacked';
}

const {
  headline = 'Blijf op de hoogte',
  placeholder = 'jouw@email.nl',
  btnLabel = 'Aanmelden',
  action,
  disclaimer,
  layout = 'inline',
} = Astro.props;
---

<section class={`newsletter newsletter--${layout}`}>
  <div class="newsletter__inner">
    {headline && <p class="newsletter__headline">{headline}</p>}
    <form class="newsletter__form" action={action} method="POST" data-newsletter>
      <div class="newsletter__fields">
        <input
          class="newsletter__input"
          type="email"
          name="email"
          required
          placeholder={placeholder}
          autocomplete="email"
        />
        <button type="submit" class="newsletter__btn">{btnLabel}</button>
      </div>
      {disclaimer && <p class="newsletter__disclaimer">{disclaimer}</p>}
      <p class="newsletter__success" hidden aria-live="polite">Gelukt! Check je inbox.</p>
    </form>
  </div>
</section>

<style>
  .newsletter {
    padding: 3rem 1.5rem;
    background: color-mix(in srgb, var(--color-text) 5%, transparent);
  }
  .newsletter__inner {
    max-width: 640px;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    gap: 1rem;
  }
  .newsletter--inline .newsletter__inner {
    flex-direction: row;
    align-items: center;
    max-width: 900px;
    gap: 2rem;
    flex-wrap: wrap;
  }
  .newsletter__headline {
    font-weight: 700;
    font-size: 1.125rem;
    margin: 0;
    white-space: nowrap;
  }
  .newsletter--inline .newsletter__form { flex: 1; min-width: 280px; }
  .newsletter__fields {
    display: flex;
    gap: 0.5rem;
  }
  .newsletter__input {
    flex: 1;
    padding: 0.75rem 1rem;
    border: 1px solid color-mix(in srgb, var(--color-text) 20%, transparent);
    border-radius: var(--radius);
    background: var(--color-bg);
    color: var(--color-text);
    font-size: 1rem;
    font-family: inherit;
    min-width: 0;
  }
  .newsletter__input:focus {
    outline: none;
    border-color: var(--color-accent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 20%, transparent);
  }
  .newsletter__btn {
    padding: 0.75rem 1.25rem;
    background: var(--color-accent);
    color: #fff;
    border: none;
    border-radius: var(--radius);
    font-weight: 700;
    font-size: 0.9375rem;
    cursor: pointer;
    white-space: nowrap;
    font-family: inherit;
    transition: opacity 0.2s;
  }
  .newsletter__btn:hover { opacity: 0.85; }
  .newsletter__disclaimer {
    font-size: 0.75rem;
    color: var(--color-muted);
    margin: 0;
  }
  .newsletter__success {
    font-size: 0.9375rem;
    color: #15803d;
    font-weight: 600;
    margin: 0;
  }
</style>

<script>
  const form = document.querySelector('[data-newsletter]') as HTMLFormElement;
  const success = form?.querySelector('.newsletter__success') as HTMLElement;
  form?.addEventListener('submit', async (e) => {
    e.preventDefault();
    const btn = form.querySelector('button') as HTMLButtonElement;
    btn.disabled = true;
    try {
      await fetch(form.action, { method: 'POST', body: new FormData(form), headers: { Accept: 'application/json' } });
      form.querySelector('.newsletter__fields')?.setAttribute('hidden', '');
      if (form.querySelector('.newsletter__disclaimer')) {
        (form.querySelector('.newsletter__disclaimer') as HTMLElement).hidden = true;
      }
      success.hidden = false;
    } catch {
      btn.disabled = false;
    }
  });
</script>

Props

Prop Type Default Beschrijving
action * string Form submit URL
headline string Boven het formulier
placeholder string 'jouw@email.nl' Input placeholder
btnLabel string 'Aanmelden' Knop tekst
disclaimer string Privacy tekst onder formulier
layout 'inline' | 'stacked' 'inline' Inline of gestapeld

* = verplicht