export function initContactForm(form) { const status = form.querySelector('[data-form-status]'); const submit = form.querySelector('[type="submit"]'); const submitLabel = submit.innerHTML; function setFieldError(name, message) { const input = form.elements[name]; const error = form.querySelector(`[data-error-for="${name}"]`); if (!input || !error) return; input.closest('.field').classList.toggle('field--invalid', Boolean(message)); error.textContent = message || ''; error.hidden = !message; if (message) { input.setAttribute('aria-invalid', 'true'); input.setAttribute('aria-describedby', error.id); } else { input.removeAttribute('aria-invalid'); input.removeAttribute('aria-describedby'); } } function showStatus(message, kind) { status.textContent = message; status.className = `form-status form-status--${kind}`; status.hidden = false; } form.addEventListener('submit', async (event) => { event.preventDefault(); form.querySelector('[role="alert"]')?.remove(); for (const name of ['name', 'email', 'message']) setFieldError(name, null); status.hidden = true; submit.disabled = true; submit.textContent = form.dataset.sending; try { const response = await fetch(form.action, { method: 'POST', headers: { Accept: 'application/json' }, body: new URLSearchParams(new FormData(form)), }); const data = await response.json(); if (data.ok) { const done = document.createElement('p'); done.className = 'form-status form-status--ok'; done.setAttribute('role', 'status'); done.tabIndex = -1; done.textContent = data.message; form.replaceWith(done); done.focus(); return; } for (const [name, message] of Object.entries(data.errors ?? {})) setFieldError(name, message); showStatus(data.message, 'error'); form.querySelector('[aria-invalid="true"]')?.focus(); } catch { showStatus(form.dataset.failed, 'error'); } finally { submit.disabled = false; submit.innerHTML = submitLabel; } }); }