# Contact Form Pattern (Web3Forms) Static-site contact form that emails the business owner directly. No backend, no calendar, no payment. ## Form Fields Keep it SHORT. Every extra field costs conversions. | Field | Required | Type | Notes | |-------|----------|------|-------| | Name | Yes | text | `autocomplete="name"` | | Phone | Yes | tel | THE most important field for phone-first businesses | | Email | No | email | Optional — don't require it, it adds friction | | Service Needed | Yes | select | Dropdown with all service types + "Other" | | Project Details | Yes | textarea | 3-line minimum, gives context before callback | | Lead Source | No | select | Google/Facebook/Neighbor/Other — helps measure marketing | **Deliberately missing:** address, calendar, file upload, CAPTCHA (add only if spam), budget field. ## HTML Structure ```html

Thanks, friend!

We'll reach out within 2 hours.

🚨 Emergency? Call now: (XXX) XXX-XXXX
``` ## CSS States ```css .form-input.error, .form-select.error, .form-textarea.error { border-color: var(--color-emergency); box-shadow: 0 0 0 3px rgba(220,38,38,0.1); } .field-error { display: none; } .field-error.visible { display: block; color: var(--color-emergency); } .form-error { display: none; background: var(--color-emergency-light); } .form-error.visible { display: block; } .form-success { display: none; } .form-success.visible { display: block; } .submit-btn.loading .spinner { display: inline-block; } .submit-btn.loading .btn-text { display: none; } ``` ## JavaScript ```javascript var WEB3FORMS_KEY = 'YOUR_ACCESS_KEY'; // Replace with real key var WEB3FORMS_URL = 'https://api.web3forms.com/submit'; // Validation function validate() { var valid = true; if (!nameField.value.trim()) { showErr('name'); valid = false; } if (!phoneField.value.trim()) { showErr('phone'); valid = false; } if (!detailsField.value.trim()) { showErr('details'); valid = false; } if (!serviceField.value) { showErr('service'); valid = false; } return valid; } // Clear errors on input inputs.forEach(function(el) { el.addEventListener('input', function() { el.classList.remove('error'); getErrorEl(el.name).classList.remove('visible'); }); }); // Submit form.addEventListener('submit', function(e) { e.preventDefault(); if (!validate()) return; submitBtn.disabled = true; submitBtn.classList.add('loading'); var payload = new FormData(form); payload.append('access_key', WEB3FORMS_KEY); payload.append('subject', 'New Quote Request — Business Name'); payload.append('from_name', 'Business Website'); fetch(WEB3FORMS_URL, { method: 'POST', body: payload }) .then(function(res) { return res.json(); }) .then(function(data) { if (data.success) { document.getElementById('success-name').textContent = nameField.value.trim().split(' ')[0] || 'friend'; form.style.display = 'none'; successDiv.classList.add('visible'); } else { throw new Error(data.message || 'Submission failed'); } }) .catch(function(err) { submitBtn.disabled = false; submitBtn.classList.remove('loading'); errorBanner.classList.add('visible'); console.error('Form error:', err); }); }); ``` ## Setup 1. Go to https://web3forms.com/ — create free account 2. Copy access key 3. Replace `YOUR_ACCESS_KEY` in the form script 4. Configure destination email in Web3Forms dashboard ## Testing - `file://` CORS blocks fetch() — expected. Test from a real web server. - Click submit with empty fields → validation errors on all required fields - Fill all required fields, submit → loading spinner → success card (if key valid) or error banner (if invalid key) - Success card shows customer's first name + emergency phone fallback