Zoeken...  ⌘K GitHub

FormMultiStep Forms

Meerstaps formulier met voortgangsindicator.

/form-multi-step
src/components/forms/FormMultiStep.astro
---
interface Props {
  heading?: string;
  steps?: string[];
}

const {
  heading = "Stap voor stap naar jouw marketingplan",
  steps = ["Jouw bedrijf", "Doelstellingen", "Budget & planning"],
} = Astro.props;
---

<div class="fms-wrap">
  {heading && <h2 class="fms-heading">{heading}</h2>}

  <div class="fms-stepper" aria-label="Voortgang">
    {steps.map((step, i) => (
      <div class={`fms-step${i === 0 ? " fms-step--active" : ""}`}>
        <div class="fms-step-circle">{i + 1}</div>
        <span class="fms-step-label">{step}</span>
      </div>
    ))}
    <div class="fms-step-track" aria-hidden="true"></div>
  </div>

  <form class="fms-form" onsubmit="return false;" novalidate>
    <!-- Step 1 -->
    <div class="fms-panel fms-panel--active" data-step="1">
      <h3 class="fms-panel-title">Vertel ons over jouw bedrijf</h3>
      <div class="fms-row">
        <div class="fms-field">
          <label class="fms-label" for="fms-company">Bedrijfsnaam *</label>
          <input class="fms-input" type="text" id="fms-company" name="company" placeholder="Jouw bedrijf BV" required />
        </div>
        <div class="fms-field">
          <label class="fms-label" for="fms-sector">Branche *</label>
          <select class="fms-select" id="fms-sector" name="sector" required>
            <option value="">Selecteer branche</option>
            <option>E-commerce</option>
            <option>B2B dienstverlening</option>
            <option>SaaS / Tech</option>
            <option>Zorg & welzijn</option>
            <option>Retail</option>
            <option>Horeca & toerisme</option>
            <option>Anders</option>
          </select>
        </div>
      </div>
      <div class="fms-field">
        <label class="fms-label" for="fms-website">Website URL</label>
        <input class="fms-input" type="url" id="fms-website" name="website" placeholder="https://jouwbedrijf.nl" />
      </div>
      <div class="fms-field">
        <label class="fms-label" for="fms-employees">Aantal medewerkers</label>
        <select class="fms-select" id="fms-employees" name="employees">
          <option value="">Selecteer</option>
          <option>1–5</option>
          <option>6–25</option>
          <option>26–100</option>
          <option>100+</option>
        </select>
      </div>
      <div class="fms-actions">
        <button class="fms-btn fms-btn--next" type="button">Volgende stap →</button>
      </div>
    </div>

    <!-- Step 2 -->
    <div class="fms-panel" data-step="2">
      <h3 class="fms-panel-title">Wat zijn jouw marketingdoelen?</h3>
      <div class="fms-field">
        <label class="fms-label">Primaire doelstelling *</label>
        <div class="fms-radio-group">
          {["Meer leads genereren", "Omzet verhogen", "Naamsbekendheid opbouwen", "Bestaande klanten activeren"].map(g => (
            <label class="fms-radio-item">
              <input class="fms-radio" type="radio" name="goal" value={g} required />
              {g}
            </label>
          ))}
        </div>
      </div>
      <div class="fms-field">
        <label class="fms-label" for="fms-channels">Huidige kanalen</label>
        <div class="fms-checks">
          {["Google Ads", "SEO", "Social Ads", "E-mail", "Geen"].map(c => (
            <label class="fms-check-item">
              <input class="fms-check" type="checkbox" name="current_channels" value={c} />
              {c}
            </label>
          ))}
        </div>
      </div>
      <div class="fms-field">
        <label class="fms-label" for="fms-challenge">Grootste uitdaging</label>
        <textarea class="fms-textarea" id="fms-challenge" name="challenge" rows="3" placeholder="Beschrijf je belangrijkste marketinguitdaging..."></textarea>
      </div>
      <div class="fms-actions fms-actions--between">
        <button class="fms-btn fms-btn--back" type="button">← Terug</button>
        <button class="fms-btn fms-btn--next" type="button">Volgende stap →</button>
      </div>
    </div>

    <!-- Step 3 -->
    <div class="fms-panel" data-step="3">
      <h3 class="fms-panel-title">Budget en planning</h3>
      <div class="fms-field">
        <label class="fms-label" for="fms-budget">Maandelijks marketingbudget *</label>
        <select class="fms-select" id="fms-budget" name="budget" required>
          <option value="">Selecteer budget</option>
          <option>Minder dan €1.000</option>
          <option>€1.000 – €2.500</option>
          <option>€2.500 – €5.000</option>
          <option>€5.000 – €10.000</option>
          <option>Meer dan €10.000</option>
        </select>
      </div>
      <div class="fms-row">
        <div class="fms-field">
          <label class="fms-label" for="fms-name">Jouw naam *</label>
          <input class="fms-input" type="text" id="fms-name" name="name" placeholder="Voornaam achternaam" required autocomplete="name" />
        </div>
        <div class="fms-field">
          <label class="fms-label" for="fms-email">E-mailadres *</label>
          <input class="fms-input" type="email" id="fms-email" name="email" placeholder="jij@bedrijf.nl" required autocomplete="email" />
        </div>
      </div>
      <div class="fms-field">
        <label class="fms-label" for="fms-start">Gewenste startdatum</label>
        <input class="fms-input" type="date" id="fms-start" name="start_date" />
      </div>
      <div class="fms-actions fms-actions--between">
        <button class="fms-btn fms-btn--back" type="button">← Terug</button>
        <button class="fms-btn fms-btn--submit" type="submit">Plan versturen ✓</button>
      </div>
    </div>
  </form>
</div>

<style>
  .fms-wrap { max-width: 640px; padding: 2rem 0; }
  .fms-heading { font-size: 1.75rem; font-weight: 800; color: var(--color-primary, #0a0a0a); margin: 0 0 2rem; }
  .fms-stepper {
    display: flex; position: relative; margin-bottom: 2.5rem;
    justify-content: space-between;
  }
  .fms-step { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; flex: 1; position: relative; z-index: 1; }
  .fms-step-circle {
    width: 36px; height: 36px; border-radius: 50%; background: #e5e5e5;
    color: #999; display: flex; align-items: center; justify-content: center;
    font-weight: 700; font-size: 0.9rem; transition: all 0.2s;
  }
  .fms-step--active .fms-step-circle { background: var(--color-accent, #6366f1); color: #fff; }
  .fms-step-label { font-size: 0.75rem; color: #999; text-align: center; font-weight: 500; }
  .fms-step--active .fms-step-label { color: var(--color-accent, #6366f1); font-weight: 700; }
  .fms-step-track {
    position: absolute; top: 18px; left: 10%; right: 10%; height: 2px;
    background: #e5e5e5; z-index: 0;
  }
  .fms-panel { display: none; flex-direction: column; gap: 1.25rem; }
  .fms-panel--active { display: flex; }
  .fms-panel-title { font-size: 1.1rem; font-weight: 700; color: var(--color-primary, #0a0a0a); margin: 0; }
  .fms-row { display: grid; grid-template-columns: 1fr 1fr; gap: 1.25rem; }
  @media (max-width: 480px) { .fms-row { grid-template-columns: 1fr; } }
  .fms-field { display: flex; flex-direction: column; gap: 0.4rem; }
  .fms-label { font-size: 0.85rem; font-weight: 600; color: var(--color-primary, #0a0a0a); }
  .fms-input, .fms-select, .fms-textarea {
    padding: 0.7rem 0.9rem; border: 1.5px solid #ddd; border-radius: 8px;
    font-size: 0.95rem; color: var(--color-primary, #0a0a0a); background: #fff;
    transition: border-color 0.2s; font-family: inherit; box-sizing: border-box; width: 100%;
  }
  .fms-input:focus, .fms-select:focus, .fms-textarea:focus { outline: none; border-color: var(--color-accent, #6366f1); box-shadow: 0 0 0 3px rgba(99,102,241,0.12); }
  .fms-textarea { resize: vertical; min-height: 90px; }
  .fms-radio-group { display: flex; flex-direction: column; gap: 0.5rem; }
  .fms-radio-item { display: flex; align-items: center; gap: 0.5rem; font-size: 0.9rem; color: #333; cursor: pointer; padding: 0.6rem 1rem; background: #f9f9f9; border-radius: 8px; border: 1.5px solid transparent; }
  .fms-radio-item:has(input:checked) { border-color: var(--color-accent, #6366f1); background: #eef2ff; }
  .fms-radio { accent-color: var(--color-accent, #6366f1); }
  .fms-checks { display: flex; flex-wrap: wrap; gap: 0.5rem; }
  .fms-check-item { display: flex; align-items: center; gap: 0.4rem; padding: 0.35rem 0.8rem; background: #f5f5f5; border-radius: 20px; font-size: 0.83rem; cursor: pointer; border: 1.5px solid transparent; }
  .fms-check-item:has(input:checked) { border-color: var(--color-accent, #6366f1); background: #eef2ff; color: var(--color-accent, #6366f1); }
  .fms-check { display: none; }
  .fms-actions { display: flex; justify-content: flex-end; padding-top: 0.5rem; }
  .fms-actions--between { justify-content: space-between; }
  .fms-btn { padding: 0.8rem 1.5rem; border-radius: 8px; font-size: 0.9rem; font-weight: 600; cursor: pointer; border: none; transition: opacity 0.2s; }
  .fms-btn--back { background: #f5f5f5; color: #555; }
  .fms-btn--next { background: var(--color-accent, #6366f1); color: #fff; }
  .fms-btn--submit { background: #22c55e; color: #fff; }
  .fms-btn:hover { opacity: 0.85; }
</style>

Props

Prop Type Default Beschrijving
headline string Formuliertitel
steps string[] Stap labels

* = verplicht