Forms & Interactive calculators
Nowadays, you no longer need to install your own software to integrate forms.
TABLE_OF_CONTENTS
Our approach
Today's AI capabilities allow you to quickly develop simple things like forms and integrate them directly into your website as HTML/CSS/JS, which is also beneficial for conversion tracking.
For developers, it also offers the advantage of being able to use the front-end framework of their choice.
To generate such a form, simply copy the attached example into Orbitype Intelligence and request any adjustments. Then maintain it on the website in an “HTML” block.
Example:
Here's a minimal example:
Note: Styles can be overridden by your website's design—you are free to adjust them.
Important: don't forget to fill in your Configuration below the <!-- DEMO --> marker (at the bottom of the code:
<!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>
/* === 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
Cancel Account
To ensure you’re comfortable using Orbitype, we want to make sure everything is clear. If you have any questions or encounter issues understanding how something works, please don’t hesitate to reach out. We’re here to assist you—just send us a message at support@orbitype.com, and we’ll be happy to help you with any questions you have.
