Join our Discord community — connect, share, and grow with us! Join Now

Formulare & Interaktive Rechner

Um Formulare einzubinden, braucht man heute keine eigene Software mehr zu installieren.

//

TABLE_OF_CONTENTS

Unser Ansatz

Die heutigen Möglichkeiten mit KI erlauben es, einfache Dinge wie ein Formular schnell zu entwickeln und direkt als HTML/CSS/JS in die Webseite einzubinden was auch vorteilhaft für das Conversion-Tracking ist.

Für Entwickler bietet es zudem den Vorteil, dass sie das Frontend-Framework ihrer Wahl nutzen können.

Um ein solches Formular zu generieren, kopieren Sie einfach das angefügte Beispiel in Orbitype Intelligence und fragen Sie nach Anpassungen. Danach pflegen Sie es auf der Webseite in einem "HTML"-Block ein.

Beispiel:

Hier ist ein minimales Beispiel:

Hinweis: Stile können durch das Design Ihrer Website überschrieben werden – Sie können sie frei anpassen.

Wichtig: Vergessen Sie nicht, Ihre Konfiguration unterhalb der Markierung <!-- DEMO --> (am Ende des Codes) einzugeben:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Form Snippet Demo</title>
</head>
<body style="margin:0;padding:0;background:#f8fafc">

<div id="fs-demo" style="height:100vh"></div>

<!-- SNIPPET START -->

<style>
/* === iaha design tokens === */
.fs-root{--fs-primary-50:#f0f4ff;--fs-primary-100:#e1e9ff;--fs-primary-200:#c3d3ff;--fs-primary-400:#87a7ff;--fs-primary-500:#aac1fe;--fs-primary-600:#8ba3e8;--fs-primary-800:#4d67bc;--fs-secondary-50:#f8fafc;--fs-secondary-100:#f1f5f9;--fs-secondary-200:#e2e8f0;--fs-secondary-300:#cbd5e1;--fs-secondary-400:#94a3b8;--fs-secondary-500:#64748b;--fs-secondary-600:#475569;--fs-secondary-700:#334155;--fs-secondary-900:#0f172a;--fs-error-300:#fca5a5;--fs-error-500:#ef4444;--fs-error-600:#dc2626;--fs-success-600:#16a34a;--fs-gray-200:#e5e7eb;--fs-gray-400:#9ca3af;--fs-gray-800:#1f2937;font-family:system-ui,-apple-system,sans-serif;color:var(--fs-secondary-900);box-sizing:border-box}
.fs-root *,.fs-root *::before,.fs-root *::after{box-sizing:inherit;margin:0;padding:0}

/* --- Steps mode: full-height container --- */
.fs-steps{display:flex;flex-direction:column;height:100%;min-height:400px;background:#fff;position:relative;overflow:hidden}

/* Progress bar */
.fs-progress-track{position:absolute;top:0;left:0;right:0;height:3px;background:var(--fs-secondary-100);z-index:10}
.fs-progress-fill{height:100%;background:var(--fs-primary-500);transition:width .3s ease-out}

/* Step content area */
.fs-step-body{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:48px 24px}
.fs-step-inner{width:100%;max-width:640px}

/* Question label */
.fs-question{font-size:clamp(18px,3vw,28px);font-weight:600;line-height:1.3;color:var(--fs-secondary-900);margin-bottom:32px}

/* --- Choice option cards --- */
.fs-options{display:flex;flex-direction:column;gap:12px}
.fs-option{display:flex;align-items:center;gap:16px;width:100%;padding:16px 20px;border:2px solid var(--fs-secondary-200);border-radius:12px;background:#fff;cursor:pointer;text-align:left;font:inherit;color:var(--fs-secondary-900);transition:all .2s ease;outline:none}
.fs-option:hover{border-color:var(--fs-primary-400);background:var(--fs-primary-50);box-shadow:0 4px 12px rgba(0,0,0,.08)}
.fs-option:focus-visible{border-color:var(--fs-primary-500);box-shadow:0 0 0 3px rgba(170,193,254,.3)}
.fs-option.fs-selected{border-color:var(--fs-primary-500);background:var(--fs-primary-50)}
.fs-option-badge{display:flex;align-items:center;justify-content:center;width:40px;height:40px;flex-shrink:0;border-radius:8px;background:var(--fs-secondary-100);font-size:14px;font-weight:600;color:var(--fs-secondary-700);transition:all .2s}
.fs-option:hover .fs-option-badge,.fs-option.fs-selected .fs-option-badge{background:var(--fs-primary-200);color:var(--fs-primary-800)}
.fs-option-text{flex:1;font-size:clamp(15px,2vw,18px);line-height:1.4}
.fs-option-check{flex-shrink:0;width:24px;height:24px;color:var(--fs-primary-600);display:none}
.fs-option.fs-selected .fs-option-check{display:block}

/* --- Input fields --- */
.fs-input-wrap{display:flex;flex-direction:column;gap:4px}
.fs-label{font-size:14px;font-weight:500;color:var(--fs-secondary-700)}
.fs-label .fs-req{color:var(--fs-error-500);margin-left:4px}
.fs-input,.fs-textarea,.fs-select{display:block;width:100%;padding:12px 16px;font-size:16px;line-height:1.5;font-family:inherit;border:1px solid var(--fs-gray-200);border-radius:8px;background:#fff;color:var(--fs-secondary-900);box-shadow:0 1px 2px rgba(0,0,0,.05);transition:border-color .2s,box-shadow .2s;outline:none}
.fs-input::placeholder,.fs-textarea::placeholder{color:var(--fs-secondary-400)}
.fs-input:focus,.fs-textarea:focus,.fs-select:focus{border-color:var(--fs-primary-500);box-shadow:0 0 0 3px rgba(170,193,254,.35)}
.fs-input.fs-has-error,.fs-textarea.fs-has-error,.fs-select.fs-has-error{border-color:var(--fs-error-300)}
.fs-input.fs-has-error:focus,.fs-textarea.fs-has-error:focus{border-color:var(--fs-error-500);box-shadow:0 0 0 3px rgba(239,68,68,.2)}
.fs-textarea{resize:vertical;min-height:80px}
.fs-select{appearance:none;padding-right:40px;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='%239ca3af' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M19 9l-7 7-7-7'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 12px center;background-size:20px}
.fs-error-msg{font-size:14px;color:var(--fs-error-600);margin-top:2px}

/* --- Buttons --- */
.fs-btn{display:inline-flex;align-items:center;justify-content:center;height:40px;padding:0 16px;font-size:14px;font-weight:500;font-family:inherit;border-radius:8px;border:none;cursor:pointer;transition:all .2s;outline:none}
.fs-btn:focus-visible{box-shadow:0 0 0 2px #000,0 0 0 4px #fff}
.fs-btn:disabled{opacity:.5;cursor:not-allowed}
.fs-btn-primary{background:#000;color:#fff;box-shadow:0 1px 3px rgba(0,0,0,.12)}
.fs-btn-primary:hover:not(:disabled){background:var(--fs-gray-800);box-shadow:0 4px 8px rgba(0,0,0,.15)}
.fs-btn-lg{height:48px;padding:0 24px;font-size:16px}

/* --- Bottom bar (steps mode) --- */
.fs-bottom{flex-shrink:0;display:flex;align-items:center;justify-content:space-between;gap:16px;padding:12px 24px;border-top:1px solid var(--fs-secondary-100);background:rgba(248,250,252,.5)}
.fs-back-btn{display:inline-flex;align-items:center;gap:8px;font-size:14px;font-family:inherit;color:var(--fs-secondary-600);background:none;border:none;cursor:pointer;padding:4px 0;transition:color .2s}
.fs-back-btn:hover{color:var(--fs-secondary-900)}
.fs-back-btn svg{width:16px;height:16px}
.fs-kbd-hints{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:10px;font-size:12px;color:var(--fs-secondary-400)}
.fs-kbd{display:inline-block;padding:2px 8px;font-family:ui-monospace,monospace;font-size:11px;border:1px solid var(--fs-secondary-300);border-radius:4px;background:#fff}
.fs-kbd-sep{color:var(--fs-secondary-300)}

/* --- Step slide transition --- */
.fs-step-enter{opacity:0;transform:translateX(24px)}
.fs-step-leave{opacity:0;transform:translateX(-24px)}

/* --- Success --- */
.fs-success{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:48px 24px;flex:1;min-height:0;animation:fs-fade-up .5s ease}
.fs-success-icon{width:64px;height:64px;border-radius:50%;background:var(--fs-primary-50);display:flex;align-items:center;justify-content:center;margin-bottom:24px;box-shadow:0 0 0 8px color-mix(in srgb,var(--fs-primary-200) 40%,transparent)}
.fs-success-icon svg{width:32px;height:32px;color:var(--fs-primary-800)}
.fs-success-title{font-size:24px;font-weight:700;color:var(--fs-secondary-900);margin-bottom:8px;line-height:1.3}
.fs-success-sub{font-size:15px;color:var(--fs-secondary-500);max-width:340px;line-height:1.5}
@keyframes fs-fade-up{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}

/* --- Classic mode --- */
.fs-classic{display:flex;flex-direction:column;gap:20px;background:#fff;border-radius:12px;padding:32px 28px;box-shadow:0 2px 12px rgba(0,0,0,.06)}
.fs-classic .fs-label{margin-bottom:6px}
.fs-classic .fs-options{gap:8px}
.fs-classic .fs-option{padding:12px 16px}
.fs-classic .fs-option-badge{width:32px;height:32px;font-size:12px;border-radius:6px}

/* --- Mode toggle --- */
.fs-mode-bar{display:flex;justify-content:center;padding:12px 0 0;position:relative;z-index:20}
.fs-steps .fs-mode-bar{position:absolute;top:10px;left:0;right:0;padding:0}
.fs-mode-toggle{display:inline-flex;background:var(--fs-secondary-100);border-radius:8px;padding:3px;gap:2px}
.fs-mode-btn{padding:6px 16px;font-size:13px;font-weight:500;font-family:inherit;border:none;border-radius:6px;cursor:pointer;color:var(--fs-secondary-500);background:transparent;transition:all .2s;outline:none;width:auto}
.fs-mode-btn:hover{color:var(--fs-secondary-700)}
.fs-mode-btn.fs-mode-active{background:#fff;color:var(--fs-secondary-900);box-shadow:0 1px 3px rgba(0,0,0,.1)}
.fs-mode-btn:focus-visible{box-shadow:0 0 0 2px var(--fs-primary-500)}

/* --- Submitting state --- */
.fs-submitting{opacity:.6;pointer-events:none}

/* --- Spinner --- */
@keyframes fs-spin{to{transform:rotate(360deg)}}
.fs-spinner{display:inline-block;width:16px;height:16px;margin-right:8px;border:2px solid rgba(255,255,255,.3);border-top-color:#fff;border-radius:50%;animation:fs-spin .6s linear infinite}
</style>

<script>
/*! FormSnippet v2 — iaha-styled embeddable form → webhook */
;(function(){
"use strict";

var CHECK_SVG = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 13l4 4L19 7"/></svg>';
var BACK_SVG = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 19l-7-7 7-7"/></svg>';

class FormSnippet {
  constructor(config) {
    this.c = Object.assign({
      el: '#fs-root',
      mode: 'steps',
      fields: [],
      webhook: '',
      submitLabel: 'Submit',
      backLabel: 'Back',
      nextLabel: 'Next',
      successMessage: 'Thank you!',
      successSubtitle: '',
      onSuccess: null,
      onError: null,
    }, config);
    this.values = {};
    this.errors = {};
    this.step = 0;
    this.submitted = false;
    this.submitting = false;
    this.focusedOption = -1;
    this.modeLocked = false;
    this.root = document.querySelector(this.c.el);
    if (!this.root) return;
    this.root.classList.add('fs-root');
    this.c.fields.forEach(function(f) { this.values[f.name] = f.value || ''; }.bind(this));
    this._onKey = this._handleKey.bind(this);
    this.render();
  }

  // --- Mode toggle -------------------------------------------------------

  _lockMode() {
    if (this.modeLocked) return;
    this.modeLocked = true;
    // Remove toggle from DOM without full re-render
    var bar = this.root.querySelector('.fs-mode-bar');
    if (bar) bar.remove();
  }

  _renderModeToggle() {
    if (this.modeLocked) return null;
    var self = this;
    var bar = this._el('div', 'fs-mode-bar');
    var toggle = this._el('div', 'fs-mode-toggle');

    var btnSteps = this._el('button', 'fs-mode-btn' + (this.c.mode === 'steps' ? ' fs-mode-active' : ''));
    btnSteps.textContent = 'Steps';
    btnSteps.type = 'button';
    btnSteps.onclick = function() { if (self.c.mode !== 'steps') { self.c.mode = 'steps'; self.step = 0; self.render(); } };

    var btnClassic = this._el('button', 'fs-mode-btn' + (this.c.mode === 'classic' ? ' fs-mode-active' : ''));
    btnClassic.textContent = 'Classic';
    btnClassic.type = 'button';
    btnClassic.onclick = function() { if (self.c.mode !== 'classic') { self.c.mode = 'classic'; self.step = 0; self.render(); } };

    toggle.appendChild(btnSteps);
    toggle.appendChild(btnClassic);
    bar.appendChild(toggle);
    return bar;
  }

  // --- Render -----------------------------------------------------------

  render() {
    this.root.innerHTML = '';
    if (this.submitted) { this._renderSuccess(); return; }
    if (this.c.mode === 'steps') this._renderSteps();
    else this._renderClassic();
  }

  _renderSteps() {
    var wrap = this._el('div', 'fs-steps' + (this.submitting ? ' fs-submitting' : ''));

    // Progress bar
    var track = this._el('div', 'fs-progress-track');
    var fill = this._el('div', 'fs-progress-fill');
    var pct = this.c.fields.length > 0 ? ((this.step + 1) / this.c.fields.length) * 100 : 0;
    fill.style.width = pct + '%';
    track.appendChild(fill);
    wrap.appendChild(track);

    // Mode toggle
    var toggleBar = this._renderModeToggle();
    if (toggleBar) wrap.appendChild(toggleBar);

    // Step body
    var body = this._el('div', 'fs-step-body');
    var inner = this._el('div', 'fs-step-inner');
    var field = this.c.fields[this.step];

    var q = this._el('h2', 'fs-question');
    q.textContent = field.label;
    inner.appendChild(q);
    inner.appendChild(this._renderField(field));

    // Input step: add next/submit button below (not for card-rendered fields)
    var isCardField = field.type === 'choice' || field.type === 'select';
    if (!isCardField) {
      var btn = this._el('button', 'fs-btn fs-btn-primary fs-btn-lg');
      btn.style.marginTop = '16px';
      var isLast = this.step === this.c.fields.length - 1;
      btn.textContent = isLast ? this.c.submitLabel : this.c.nextLabel;
      var val = (this.values[field.name] || '').trim();
      if (field.required && !val) btn.disabled = true;
      btn.onclick = isLast ? this.submit.bind(this) : this.next.bind(this);
      inner.appendChild(btn);
    }

    body.appendChild(inner);
    wrap.appendChild(body);

    // Bottom bar
    var bottom = this._el('div', 'fs-bottom');

    if (this.step > 0) {
      var back = this._el('button', 'fs-back-btn');
      back.innerHTML = BACK_SVG + ' ' + this.c.backLabel;
      back.onclick = this.prev.bind(this);
      bottom.appendChild(back);
    } else {
      bottom.appendChild(this._el('div'));
    }

    // Keyboard hints
    var hints = this._el('div', 'fs-kbd-hints');
    if (field.type === 'choice' || field.type === 'select') {
      hints.innerHTML =
        '<span>' + this._kbd('\u2191\u2193') + ' Navigate</span>' +
        '<span class="fs-kbd-sep">\u00b7</span>' +
        '<span>' + this._kbd('Enter') + ' Select</span>' +
        '<span class="fs-kbd-sep">\u00b7</span>' +
        '<span>' + this._kbd('A') + ' \u2013 ' + this._kbd(String.fromCharCode(64 + Math.min(26, (field.options || []).length))) + ' Direct</span>';
    } else {
      hints.innerHTML = '<span>' + this._kbd('\u2190') + ' Back</span>';
    }
    bottom.appendChild(hints);
    bottom.appendChild(this._el('div'));

    wrap.appendChild(bottom);
    this.root.appendChild(wrap);

    document.removeEventListener('keydown', this._onKey);
    document.addEventListener('keydown', this._onKey);
  }

  _renderClassic() {
    var form = this._el('div', 'fs-classic' + (this.submitting ? ' fs-submitting' : ''));
    var self = this;
    // Mode toggle
    var toggleBar = this._renderModeToggle();
    if (toggleBar) form.appendChild(toggleBar);
    this.c.fields.forEach(function(f) {
      var fieldWrap = self._el('div');
      var isCards = f.type === 'choice';
      var label = self._el('label', 'fs-label');
      label.textContent = f.label;
      if (f.required) { var req = self._el('span', 'fs-req'); req.textContent = isCards ? ' *' : '*'; label.appendChild(req); }
      fieldWrap.appendChild(label);
      fieldWrap.appendChild(self._renderField(f));
      fieldWrap.style.display = 'flex';
      fieldWrap.style.flexDirection = 'column';
      fieldWrap.style.gap = '4px';
      form.appendChild(fieldWrap);
    });
    var btn = this._el('button', 'fs-btn fs-btn-primary fs-btn-lg');
    btn.style.width = '100%';
    btn.style.marginTop = '8px';
    if (this.submitting) { btn.innerHTML = '<span class="fs-spinner"></span>Sending...'; btn.disabled = true; }
    else btn.textContent = this.c.submitLabel;
    btn.onclick = this.submit.bind(this);
    form.appendChild(btn);
    this.root.appendChild(form);
  }

  _renderField(field) {
    var self = this;
    var val = this.values[field.name] || '';

    // Render as option cards: choice always, select in steps mode
    var renderAsCards = field.type === 'choice' || (field.type === 'select' && this.c.mode === 'steps');
    if (renderAsCards) {
      var row = this._el('div', 'fs-options');
      (field.options || []).forEach(function(opt, i) {
        var isSelected = val === opt;
        var btn = self._el('button', 'fs-option' + (isSelected ? ' fs-selected' : '') + (self.focusedOption === i && self.c.mode === 'steps' ? ' fs-selected' : ''));
        btn.type = 'button';

        var badge = self._el('span', 'fs-option-badge');
        badge.textContent = String.fromCharCode(65 + i);
        btn.appendChild(badge);

        var text = self._el('span', 'fs-option-text');
        text.textContent = opt;
        btn.appendChild(text);

        var check = self._el('span', 'fs-option-check');
        check.innerHTML = CHECK_SVG;
        btn.appendChild(check);

        btn.onclick = function() {
          self._lockMode();
          self.values[field.name] = opt;
          self.errors[field.name] = '';
          if (self.c.mode === 'steps') {
            // Auto-advance on choice selection (typeform behavior)
            var isLast = self.step === self.c.fields.length - 1;
            if (isLast) self.submit();
            else { self.step++; self.focusedOption = -1; self.render(); }
          } else {
            self.render();
          }
        };
        btn.onmouseenter = function() { if (self.c.mode === 'steps') { self.focusedOption = i; } };
        btn.onmouseleave = function() { if (self.c.mode === 'steps') { self.focusedOption = -1; } };
        row.appendChild(btn);
      });
      return row;
    }

    var wrap = this._el('div', 'fs-input-wrap');

    if (field.type === 'textarea') {
      var ta = this._el('textarea', 'fs-textarea' + (this.errors[field.name] ? ' fs-has-error' : ''));
      ta.placeholder = field.placeholder || '';
      ta.value = val;
      ta.oninput = function() {
        self._lockMode();
        self.values[field.name] = ta.value;
        self.errors[field.name] = '';
        // Update button disabled state in steps mode
        if (self.c.mode === 'steps') self._updateStepBtn(field);
      };
      wrap.appendChild(ta);
    } else if (field.type === 'select') {
      var sel = this._el('select', 'fs-select' + (this.errors[field.name] ? ' fs-has-error' : ''));
      var ph = this._el('option'); ph.value = ''; ph.textContent = field.placeholder || 'Select...'; ph.disabled = true; ph.selected = !val;
      sel.appendChild(ph);
      (field.options || []).forEach(function(opt) {
        var o = self._el('option'); o.value = opt; o.textContent = opt; if (val === opt) o.selected = true;
        sel.appendChild(o);
      });
      sel.onchange = function() {
        self._lockMode();
        self.values[field.name] = sel.value;
        self.errors[field.name] = '';
        if (self.c.mode === 'steps') self._updateStepBtn(field);
      };
      wrap.appendChild(sel);
    } else {
      var inp = this._el('input', 'fs-input' + (this.errors[field.name] ? ' fs-has-error' : ''));
      inp.type = field.type || 'text';
      inp.placeholder = field.placeholder || '';
      inp.value = val;
      if (field.autocomplete) inp.autocomplete = field.autocomplete;
      if (field.name) inp.name = field.name;
      inp.oninput = function() {
        self._lockMode();
        self.values[field.name] = inp.value;
        self.errors[field.name] = '';
        if (self.c.mode === 'steps') self._updateStepBtn(field);
      };
      inp.onkeydown = function(e) {
        if (e.key === 'Enter') { e.preventDefault(); var isLast = self.step === self.c.fields.length - 1; isLast ? self.submit() : self.next(); }
      };
      wrap.appendChild(inp);
      setTimeout(function() { inp.focus(); }, 50);
    }

    if (this.errors[field.name]) {
      var err = this._el('div', 'fs-error-msg');
      err.textContent = this.errors[field.name];
      wrap.appendChild(err);
    }
    return wrap;
  }

  _renderSuccess() {
    var cls = this.c.mode === 'steps' ? 'fs-steps' : 'fs-classic';
    var box = this._el('div', cls);
    if (cls === 'fs-classic') { box.style.minHeight = '360px'; box.style.justifyContent = 'center'; box.style.alignItems = 'center'; }
    var msg = this._el('div', 'fs-success');

    /* checkmark icon */
    var iconWrap = this._el('div', 'fs-success-icon');
    iconWrap.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"/></svg>';
    msg.appendChild(iconWrap);

    /* title */
    var title = this._el('div', 'fs-success-title');
    title.textContent = this.c.successMessage || 'Thank you!';
    msg.appendChild(title);

    /* subtitle */
    if (this.c.successSubtitle) {
      var sub = this._el('div', 'fs-success-sub');
      sub.textContent = this.c.successSubtitle;
      msg.appendChild(sub);
    }

    box.appendChild(msg);
    this.root.appendChild(box);
    document.removeEventListener('keydown', this._onKey);
  }

  _updateStepBtn(field) {
    var btn = this.root.querySelector('.fs-btn-primary');
    if (!btn || !field.required) return;
    var val = (this.values[field.name] || '').trim();
    btn.disabled = !val;
  }

  // --- Validation -------------------------------------------------------

  validate(name) {
    var self = this;
    var fields = name ? this.c.fields.filter(function(f) { return f.name === name; }) : this.c.fields;
    var valid = true;
    fields.forEach(function(f) {
      var v = (self.values[f.name] || '').trim();
      if (f.required && !v) { self.errors[f.name] = 'This field is required'; valid = false; return; }
      if (f.type === 'email' && v && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v)) { self.errors[f.name] = 'Please enter a valid email'; valid = false; return; }
      if (f.pattern && v && !f.pattern.test(v)) { self.errors[f.name] = 'Invalid format'; valid = false; return; }
      self.errors[f.name] = '';
    });
    return valid;
  }

  // --- Navigation -------------------------------------------------------

  next() {
    var field = this.c.fields[this.step];
    if (!this.validate(field.name)) { this.render(); return; }
    if (this.step < this.c.fields.length - 1) { this.step++; this.focusedOption = -1; this.render(); }
  }

  prev() {
    if (this.step > 0) { this.step--; this.focusedOption = -1; this.render(); }
  }

  // --- Submit ------------------------------------------------------------

  submit() {
    var self = this;
    if (!this.validate()) { this.render(); return; }
    if (!this.c.webhook) { this.submitted = true; this.render(); if (this.c.onSuccess) this.c.onSuccess(this.values); return; }
    this.submitting = true;
    this.render();
    fetch(this.c.webhook, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(this.values),
    })
    .then(function(res) {
      if (!res.ok) throw new Error('HTTP ' + res.status);
      self.submitting = false;
      self.submitted = true;
      self.render();
      if (self.c.onSuccess) self.c.onSuccess(self.values);
    })
    .catch(function(err) {
      self.submitting = false;
      self.render();
      if (self.c.onError) self.c.onError(err);
      else console.error('FormSnippet submit error:', err);
    });
  }

  // --- Keyboard ----------------------------------------------------------

  _handleKey(e) {
    if (this.c.mode !== 'steps' || this.submitted || this.submitting) return;
    var field = this.c.fields[this.step];
    if (!field) return;

    // Back: ArrowLeft or Backspace (if not in an input)
    if (e.key === 'ArrowLeft' || e.key === 'Backspace') {
      if (document.activeElement && document.activeElement.tagName === 'INPUT') return;
      e.preventDefault(); this.prev(); return;
    }

    if (field.type === 'choice' || field.type === 'select') {
      var opts = field.options || [];

      if (e.key === 'ArrowDown' || e.key === 'ArrowRight') {
        e.preventDefault();
        this.focusedOption = this.focusedOption < opts.length - 1 ? this.focusedOption + 1 : 0;
        this.render(); return;
      }
      if (e.key === 'ArrowUp') {
        e.preventDefault();
        this.focusedOption = this.focusedOption > 0 ? this.focusedOption - 1 : opts.length - 1;
        this.render(); return;
      }
      if (e.key === 'Enter' && this.focusedOption >= 0 && this.focusedOption < opts.length) {
        e.preventDefault();
        this._lockMode();
        this.values[field.name] = opts[this.focusedOption];
        this.errors[field.name] = '';
        var isLast = this.step === this.c.fields.length - 1;
        if (isLast) this.submit(); else { this.step++; this.focusedOption = -1; this.render(); }
        return;
      }
      // Letter keys A-Z
      var letter = e.key.toUpperCase();
      var idx = letter.charCodeAt(0) - 65;
      if (letter.length === 1 && idx >= 0 && idx < opts.length) {
        e.preventDefault();
        this._lockMode();
        this.values[field.name] = opts[idx];
        this.errors[field.name] = '';
        var isLast = this.step === this.c.fields.length - 1;
        if (isLast) this.submit(); else { this.step++; this.focusedOption = -1; this.render(); }
      }
    }
  }

  // --- Helpers -----------------------------------------------------------

  _el(tag, cls) { var el = document.createElement(tag); if (cls) el.className = cls; return el; }
  _kbd(text) { return '<span class="fs-kbd">' + text + '</span>'; }
}

window.FormSnippet = FormSnippet;
})();
</script>

<!-- SNIPPET END -->


<!-- DEMO -->
<script>
new FormSnippet({
  el: '#fs-demo',
  mode: 'steps',
  webhook: 'https://core.orbitype.com/workflows/webhook?webhookSecret={{place your orbitype webhook id here}}',
  fields: [
    { name: 'name', label: 'What is your name?', type: 'text', required: true, placeholder: 'Jane Doe', autocomplete: 'name' },
    { name: 'plan', label: 'Which plan interests you?', type: 'choice', options: ['Starter', 'Professional', 'Enterprise'], required: true },
    { name: 'company_size', label: 'How large is your company?', type: 'select', options: ['1-10', '11-50', '51-200', '200+'], required: true, placeholder: 'Select company size...' },
    { name: 'message', label: 'Anything else you want us to know?', type: 'textarea', placeholder: 'Tell us more...' },
    { name: 'email', label: 'What is your email address?', type: 'email', required: true, placeholder: 'jane@company.com', autocomplete: 'email' },
  ],
  submitLabel: 'Send',
  nextLabel: 'Next',
  backLabel: 'Back',
  successMessage: 'Thanks! We will be in touch.',
  successSubtitle: 'We have received your information and will get back to you shortly.',
  onSuccess: function(values) { console.log('Form submitted:', values); },
  onError: function(err) { console.error('Form error:', err); },
});
</script>

</body>
</html>

Learn more

Cookbook - Sektionen

Moderne Webseiten bestehen oft aus vielen verschiedenen Sektionen.

Zum Beispiel Hero-Banner, Testimonials, FAQs oder einfache Textblöcke.

Entwickler setzen diese als wiederverwendbare Komponenten mit Props um.

Aber wie ermöglichen wir es Nicht-Technikern, sie in Orbitype zu nutzen?

Konto kündigen

Wir möchten sicherstellen, dass du Orbitype problemlos nutzen kannst. Falls du Fragen hast oder auf Schwierigkeiten stößt, zögere nicht, uns zu kontaktieren. Wir sind für dich da – schreib uns einfach eine Nachricht an support@orbitype.com, und wir helfen dir gerne weiter.

Detaillierte Preisgestaltung

Orbitype nutzt ein einfaches Credit-Modell. 1 USD entspricht 100 Credits. Jeder Workspace hat einen Sponsor-User, der alle Seats und die gesamte Nutzung bezahlt. Credits können prepaid gekauft und flexibel für Seats und Usage genutzt werden.