Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/apursley2012/dyed-in-the-wool/llms.txt

Use this file to discover all available pages before exploring further.

The Contact component (internally function X) presents a minimal contact form styled around the dye-splash aesthetic. On submission, the button transitions to a “Soaking…” loading state for 1 500 ms before the background animates with an SVG contact-dye displacement filter and a success message replaces the form.

What It Displays

The page contains:
  1. Section header — title “LEAVE A MARK” and subtitle “Got a project in mind? Let’s mix some colors together.”
  2. Form — three fields:
    • Nametype="text", required
    • Emailtype="email", required
    • Message<textarea>, required
  3. Submit button — labeled “Send Message” at rest. During the simulated async delay it shows “Soaking…” with a disabled state.
  4. Success state — after the 1 500 ms delay the form is replaced by the heading “Message soaked in!” and the paragraph “I’ll get back to you before the dye dries.” The background simultaneously transitions to the contact-dye SVG filter effect (a feTurbulence + feDisplacementMap applied to a teal rect and magenta circle).
The form submit is a UI-only simulation. There is no backend, API route, or email service connected. The 1 500 ms delay and success message are triggered by a setTimeout — no data is sent anywhere. To make the form functional, you must integrate a form service such as Formspree, EmailJS, or a custom API endpoint. Replace the setTimeout mock with your real fetch / axios call inside the submit handler.

Content Data

All strings are inline in the component JSX:
// Header
title:    "LEAVE A MARK"
subtitle: "Got a project in mind? Let's mix some colors together."

// Button states
idle:     "Send Message"
loading:  "Soaking..."

// Success message (two elements)
heading:  "Message soaked in!"
body:     "I'll get back to you before the dye dries."
Form field labels and placeholder attributes are also inline strings — edit them directly in the JSX.

Animation Details

ElementTechniqueDetails
Section title + form blockFramer Motion initial → animate on mounty: 20 → 0, opacity: 0 → 1
Submit buttonhover:scale-[1.02] / active:scale-[0.98]CSS utility scale classes; inner fill uses Framer Motion whileHover radial expand
Loading stateState-driven re-renderisLoading state swap replaces button text and disables interaction
Success messageState-driven swapisSuccess state replaces the form with the success block; Framer Motion initial: {opacity:0, scale:0.9} → animate: {opacity:1, scale:1}
Background filterFramer Motion animate opacityOn success, the contact-dye SVG filter overlay fades from opacity: 0 → 1 over 2 s

Customization

Wire up a real form service (Formspree example) Replace the setTimeout mock in the submit handler with a real fetch call:
const handleSubmit = async (e) => {
  e.preventDefault();
  setLoading(true);
  try {
    const res = await fetch("https://formspree.io/f/YOUR_FORM_ID", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ name, email, message }),
    });
    if (res.ok) setSuccess(true);
  } catch (err) {
    console.error(err);
  } finally {
    setLoading(false);
  }
};
Wire up EmailJS
import emailjs from "@emailjs/browser";

const handleSubmit = async (e) => {
  e.preventDefault();
  setLoading(true);
  await emailjs.send("SERVICE_ID", "TEMPLATE_ID", { name, email, message }, "PUBLIC_KEY");
  setLoading(false);
  setSuccess(true);
};
Change the success message Locate the success-state render block and update the strings:
// Before
<h3>Message soaked in!</h3>
<p>I'll get back to you before the dye dries.</p>

// After
<h3>Thanks!</h3>
<p>I'll be in touch within 48 hours.</p>
Change the loading delay (mock only) While you’re still using the mock, the delay is set via setTimeout:
setTimeout(() => {
  setLoading(false);
  setSuccess(true);
}, 1500);   // ← change to 800 for a shorter wait
Add a new form field Add a controlled <input> or <textarea> element with its own useState entry, include it in the validation check, and pass it to your form service payload.
If you add real form submission, consider adding a visible error state alongside the success state. A third isError branch using the same state-swap pattern makes it straightforward to display a failure message with matching styling.

Build docs developers (and LLMs) love