patx/afterdarklabs

improve formspree connect

Commit a427491 · patx · 2026-05-16T15:50:19-04:00

Changeset
a427491de5dd4e5f2a91e49a4acd004d7f5a041e
Parents
b6ac9ee0c8b3dd51ef2cb49daa7c3ea12983464a

View source at this commit

Comments

No comments yet.

Log in to comment

Diff

diff --git a/index.html b/index.html
index 571ff78..5e3c5ff 100644
--- a/index.html
+++ b/index.html
@@ -231,7 +231,7 @@
         <div class="max-w-3xl mx-auto text-center px-6">
             <h2 class="heading text-6xl font-bold tracking-tighter mb-6">Ready to build something exceptional?</h2>
             <p class="text-xl text-gray-400 mb-10">Tell us about your project. We reply the same day!</p>
-            <form class="space-y-6" action="https://formspree.io/f/xeedjery" method="POST">
+            <form id="contact-form" class="space-y-6" action="https://formspree.io/f/xeedjery" method="POST">
                 <input type="hidden" name="_subject" value="New project inquiry from After Dark Labs">
                 <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                     <input type="text" name="name" placeholder="Your Name" autocomplete="name" required class="bg-zinc-900 border border-white/10 focus:border-[#ccff00] rounded-2xl px-6 py-5 outline-none transition-colors">
@@ -242,6 +242,7 @@
                     Send Message
                 </button>
             </form>
+            <div id="contact-status" class="hidden rounded-3xl border border-white/10 bg-zinc-900 px-8 py-10 text-left" tabindex="-1" aria-live="polite"></div>
         </div>
     </section>
 
@@ -262,6 +263,83 @@
     </div>
 
     <script>
+        (function () {
+            const form = document.getElementById('contact-form');
+            const status = document.getElementById('contact-status');
+
+            if (!form || !status) return;
+
+            const submitButton = form.querySelector('button[type="submit"]');
+            const defaultButtonText = submitButton.textContent.trim();
+
+            function showStatus(type, title, message) {
+                const isSuccess = type === 'success';
+                status.className = [
+                    'rounded-3xl border px-8 py-10 text-left',
+                    isSuccess ? 'border-[#ccff00]/40 bg-[#ccff00]/10' : 'border-red-400/40 bg-red-950/30',
+                ].join(' ');
+                status.setAttribute('role', isSuccess ? 'status' : 'alert');
+
+                const eyebrow = document.createElement('p');
+                eyebrow.className = `text-sm font-semibold uppercase tracking-[0.2em] ${isSuccess ? 'text-[#ccff00]' : 'text-red-300'}`;
+                eyebrow.textContent = isSuccess ? 'Message sent' : 'Message not sent';
+
+                const heading = document.createElement('h3');
+                heading.className = 'heading mt-3 text-3xl font-semibold tracking-tighter text-white';
+                heading.textContent = title;
+
+                const body = document.createElement('p');
+                body.className = 'mt-4 text-lg leading-8 text-gray-300';
+                body.textContent = message;
+
+                status.replaceChildren(eyebrow, heading, body);
+                form.remove();
+                status.focus({ preventScroll: true });
+            }
+
+            form.addEventListener('submit', async event => {
+                event.preventDefault();
+
+                if (!form.reportValidity()) return;
+
+                submitButton.disabled = true;
+                submitButton.textContent = 'Sending...';
+                submitButton.classList.add('cursor-wait', 'opacity-80');
+
+                try {
+                    const response = await fetch(form.action, {
+                        method: form.method,
+                        body: new FormData(form),
+                        headers: { Accept: 'application/json' },
+                    });
+                    const data = await response.json().catch(() => ({}));
+
+                    if (!response.ok) {
+                        const errors = Array.isArray(data.errors)
+                            ? data.errors.map(error => error.message).filter(Boolean).join(' ')
+                            : '';
+                        throw new Error(errors || 'The message could not be submitted.');
+                    }
+
+                    showStatus(
+                        'success',
+                        'Thank you. We will be in touch soon.',
+                        'Your project details were sent successfully, and we will reply as soon as possible.'
+                    );
+                } catch (error) {
+                    showStatus(
+                        'error',
+                        'We could not send your message.',
+                        error.message || 'Please refresh the page and try again in a moment.'
+                    );
+                } finally {
+                    submitButton.disabled = false;
+                    submitButton.textContent = defaultButtonText;
+                    submitButton.classList.remove('cursor-wait', 'opacity-80');
+                }
+            });
+        })();
+
         (function () {
             const modal = document.getElementById('work-modal');
             const modalImage = document.getElementById('work-modal-image');