Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/apursley2012/digital-domain/llms.txt

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

The Contact page makes the act of submitting a message into a theatrical event. Rather than instantly confirming submission, it stages a deliberate 2.5-second “sending over a 56k modem” animation — a playful nod to the era when waiting for a web page to load was a significant part of the online experience. The form cycles through three distinct states — idle, sending, and success — each animated in and out by Framer Motion’s AnimatePresence. The success screen rewards the sender with bouncing text and spinning emoji stars.

Window Container

The page renders a single Window component titled “Send_Message.exe” with a Mail Lucide icon:
<div className="max-w-2xl mx-auto">
  <Window
    title="Send_Message.exe"
    icon={<Mail size={14} />}
    defaultSize={{ width: "100%", height: "auto" }}
    className="!static"
  >
    {/* Form content */}
  </Window>
</div>
Visual appearance: A standard Win98 window with a blue title bar, a mail envelope icon, and the filename “Send_Message.exe”. The !static class keeps it in document flow. The inner content area is p-6 bg-white font-comic — a white padded area in Comic Sans throughout.

Form State Management

A single useState hook manages the three-state form lifecycle:
const [status, setStatus] = useState("idle"); // "idle" | "sending" | "success"
The onSubmit handler prevents default form submission and triggers the state transitions:
const handleSubmit = (e) => {
  e.preventDefault();
  setStatus("sending");
  setTimeout(() => setStatus("success"), 2500);
};
After e.preventDefault(), the status immediately moves to "sending". A setTimeout with a 2500ms delay (matching the progress bar animation duration) then transitions to "success". There is no actual HTTP request — the form is entirely front-end with simulated network delay.

State: Idle — Contact Form

When status === "idle", the form is displayed:
<motion.form
  key="form"
  initial={{ opacity: 0 }}
  animate={{ opacity: 1 }}
  exit={{ opacity: 0 }}
  onSubmit={handleSubmit}
  className="space-y-4"
>

Form Header

<h2 className="font-vt323 text-3xl text-retro-purple">Drop me a line!</h2>
<p className="text-sm">My ICQ number is 12345678 (just kidding, use the form)</p>

Input Fields

Three form fields with Win98 inset-shadow styling:
{/* Name */}
<label className="block font-bold mb-1">Name:</label>
<input
  required
  type="text"
  className="w-full border-2 border-win-gray shadow-win-in p-1 focus:outline-none focus:bg-yellow-50"
/>

{/* Email */}
<label className="block font-bold mb-1">Email:</label>
<input
  required
  type="email"
  className="w-full border-2 border-win-gray shadow-win-in p-1 focus:outline-none focus:bg-yellow-50"
/>

{/* Message */}
<label className="block font-bold mb-1">Message:</label>
<textarea
  required
  rows={4}
  className="w-full border-2 border-win-gray shadow-win-in p-1 focus:outline-none focus:bg-yellow-50"
/>
Visual appearance: Each input has a border-2 border-win-gray shadow-win-in inset bevel — the characteristic sunken field appearance from Win98 dialog boxes. All three fields are full width. On focus, the background shifts to a very light yellow (focus:bg-yellow-50). All three fields are required, so the browser’s native validation will prevent submission if any are empty.

Submit Button

<BeveledButton type="submit" className="text-xl py-2 px-8 flex items-center gap-2 bg-win-gray">
  <Send size={20} /> SUBMIT!!
</BeveledButton>
The button uses the Lucide Send icon (a paper-plane shape) and the double exclamation mark for maximum retro enthusiasm.

State: Sending — Modem Animation

When status === "sending", the form fades out and is replaced by the modem progress screen:
<motion.div
  key="sending"
  initial={{ opacity: 0 }}
  animate={{ opacity: 1 }}
  exit={{ opacity: 0 }}
  className="py-12 flex flex-col items-center gap-4"
>
  <p className="font-vt323 text-2xl animate-pulse">
    Sending data packets over 56k modem...
  </p>

  <div className="w-full max-w-md h-6 bg-win-gray shadow-win-in p-[2px]">
    <motion.div
      initial={{ width: "0%" }}
      animate={{ width: "100%" }}
      transition={{ duration: 2.5, ease: "linear" }}
      className="h-full bg-titlebar"
    />
  </div>
</motion.div>
Visual appearance: The pulsing “Sending data packets over 56k modem…” text is displayed in font-vt323 text-2xl with animate-pulse making it fade in and out continuously. Below it, a Win98-style progress bar: a grey inset-shadow track (bg-win-gray shadow-win-in) containing a blue fill (bg-titlebar) that animates from 0% to 100% width over exactly 2.5 seconds at a linear pace — matching the setTimeout delay precisely so the bar completes exactly as the success screen appears.
The AnimatePresence wrapper with mode="wait" ensures that the exiting state fully fades out before the entering state fades in, preventing overlap during transitions. The key prop on each motion element is what triggers the mount/unmount animation cycle.

State: Success — Confirmation Screen

When status === "success", the success screen is displayed:
<motion.div
  key="success"
  initial={{ scale: 0.8, opacity: 0 }}
  animate={{ scale: 1, opacity: 1 }}
  className="py-12 flex flex-col items-center gap-4 text-center"
>
  <h3 className="font-vt323 text-4xl text-retro-pink animate-bounce">
    MESSAGE SENT!
  </h3>

  <p>Thanks for reaching out! I'll reply as soon as I log back onto AOL.</p>

  <div className="flex gap-2 mt-4">
    <span className="text-2xl animate-spin-slow"></span>
    <span className="text-2xl animate-spin-slow" style={{ animationDelay: "0.2s" }}>💖</span>
    <span className="text-2xl animate-spin-slow" style={{ animationDelay: "0.4s" }}></span>
  </div>

  <BeveledButton onClick={() => setStatus("idle")} className="mt-6">
    Send Another
  </BeveledButton>
</motion.div>
Visual appearance: The success screen enters with a scale-up animation from 80% to 100% size combined with an opacity fade-in. The “MESSAGE SENT!” heading is in font-vt323 text-4xl text-retro-pink with animate-bounce making it continuously hop up and down. Three emoji spin using animate-spin-slow with staggered animationDelay values (0s, 0.2s, 0.4s): ⭐ 💖 ⭐. A grey “Send Another” BeveledButton resets status to "idle", restoring the form.

AnimatePresence and Transition Flow

idle   ──(submit)──▶  sending  ──(2500ms)──▶  success  ──(Send Another)──▶  idle
The full AnimatePresence wrapper:
<AnimatePresence mode="wait">
  {status === "idle"    && <motion.form    key="form"    ...>...</motion.form>}
  {status === "sending" && <motion.div     key="sending" ...>...</motion.div>}
  {status === "success" && <motion.div     key="success" ...>...</motion.div>}
</AnimatePresence>

Component Dependencies

ComponentSourceUsage
Window (p)components/Window.jsSend_Message.exe frame
BeveledButton (d)components/BeveledButton.jsSubmit and Send Another buttons
AnimatePresence (E)framer-motionState transition orchestration
motion.form / motion.divframer-motionPer-state animated containers
mail (Lucide)lucide-reactWindow title bar icon
send (Lucide)lucide-reactSubmit button icon
useStateReact 18Form status state

Build docs developers (and LLMs) love