Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/apursley2012/windows-xp-developer/llms.txt

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

The Contact page packages a standard contact form inside the most recognisable software UI of the early 2000s — the Outlook “New Message” composer. An AquaWindow provides the chrome, complete with a title bar reading “New Message”. Inside, a structured form lays out four fields exactly as Outlook did: To, From, Subject, and a large Message body area. Submitting the form triggers a two-state send animation that ends with a satisfying green checkmark.

Visual overview

The AquaWindow is centred on the page and sized to approximate a real Outlook compose window. Each field has a label on the left and an input on the right, separated by a subtle horizontal rule between fields. The To field is pre-filled with alex@developer.com and is read-only. The Send Message button sits at the bottom right of the form. The two form states cycle from idle → sent on submission, with a 1.5-second simulated delay between them.

Idle state

All editable fields are active. The Send Message button is enabled. The To field is pre-filled and read-only.

Sending state

The button label changes to “Sending…” and gains a disabled attribute. Lasts 1.5 seconds.

Success state

A green circular checkmark icon scales in with a spring animation (scale: 0.9 → 1). Text reads “Message Sent!” with a flavour note. A “Send Another” button resets back to idle.

Reset

Clicking “Send Another” hides the success panel and shows the blank form again.

Component structure

// Simplified Contact page structure
import { AquaWindow } from '../components/AquaWindow';
import { AquaButton } from '../components/AquaButton';
import { motion }     from 'framer-motion';
import { useState }   from 'react';

export default function Contact() {
  const [isSending, setIsSending] = useState(false);
  const [isSent, setIsSent]       = useState(false);

  function handleSend(e) {
    e.preventDefault();
    setIsSending(true);

    // Simulate a 1.5 s network request
    setTimeout(() => {
      setIsSending(false);
      setIsSent(true);
    }, 1500);
  }

  function handleReset() {
    setIsSent(false);
  }

  return (
    <AquaWindow title="New Message" icon={<MailIcon />}>
      <div className="p-4 bg-slate-50">
        {isSent ? (
          // Success panel
          <motion.div
            initial={{ opacity: 0, scale: 0.9 }}
            animate={{ opacity: 1, scale: 1 }}
            className="success-panel text-center py-12"
          >
            <div className="check-circle">
              <CheckIcon size={24} className="text-white" />
            </div>
            <h3>Message Sent!</h3>
            <p>I'll get back to you as soon as my dial-up connects.</p>
            <AquaButton onClick={handleReset}>Send Another</AquaButton>
          </motion.div>
        ) : (
          // Form
          <form onSubmit={handleSend}>
            <div className="field-row">
              <label>To:</label>
              <span className="to-chip">alex@developer.com</span>
            </div>
            <div className="field-row">
              <label>From:</label>
              <input
                type="email"
                required
                disabled={isSending}
                placeholder="your@email.com"
              />
            </div>
            <div className="field-row">
              <label>Subject:</label>
              <input
                type="text"
                required
                disabled={isSending}
                placeholder="Hello!"
              />
            </div>
            <div className="pt-2">
              <textarea
                required
                rows={6}
                disabled={isSending}
                placeholder="Write your message here..."
              />
            </div>
            <div className="flex justify-end pt-2">
              <AquaButton type="submit" disabled={isSending}>
                {isSending ? 'Sending...' : 'Send Message'}
              </AquaButton>
            </div>
          </form>
        )}
      </div>
    </AquaWindow>
  );
}

The send flow — step by step

1

Idle

The form renders with the To field pre-filled with alex@developer.com and marked read-only. The visitor fills in From, Subject, and Message.
2

Submit

The visitor clicks Send Message. handleSend fires, calls e.preventDefault(), and sets isSending to true.
3

Sending (1.5 s)

The button label swaps to “Sending…” and gains the disabled attribute so the form cannot be re-submitted. A setTimeout of 1 500 ms simulates a network round-trip.
4

Success

isSent becomes true. The form is replaced by the success panel — a green circular checkmark icon scales from 0.9 → 1.0 with a spring entrance, followed by “Message Sent!” and a flavour line reading “I’ll get back to you as soon as my dial-up connects.”
5

Reset

Clicking “Send Another” calls handleReset, setting isSent back to false. The blank form re-appears.

Customization

The recipient address and send delay live in the Contact route component in assets/main.js. Locate it by searching for "alex@developer.com":
// In assets/main.js — locate the Contact component (search for "alex@developer.com")
const recipientAddress = 'alex@developer.com'; // The pre-filled, read-only To field
const sendDelay        = 1500;                 // Milliseconds for the simulated send delay

// To connect a real email backend, replace the setTimeout in handleSend:
async function handleSend(e) {
  e.preventDefault();
  setIsSending(true);
  try {
    await fetch('/api/contact', {
      method: 'POST',
      body: JSON.stringify({ from, subject, message }),
      headers: { 'Content-Type': 'application/json' },
    });
    setIsSent(true);
  } catch {
    setIsSending(false); // Or add an error state
  }
}
To wire up a real backend, swap the setTimeout mock in handleSend for a fetch call to your preferred email API (Resend, SendGrid, Formspree, etc.). The two-state machine (isSendingisSent) works identically with a real async call.
The default build uses a client-side setTimeout mock with no real email delivery. Before publishing the portfolio, replace the mock with a real form submission endpoint, or add a mailto: fallback link.

Key interactions

InteractionBehaviour
Page enterAquaWindow springs in with its standard entrance animation
Hover on Send Message buttonAquaButton gloss highlight brightens; scale: 1.04 lift
Form submitisSending → true; button label changes to “Sending…”; button disables
After 1.5 sForm is replaced by the success panel with a spring scale entrance
Success iconGreen checkmark scales from 0.9 → 1.0 with Framer Motion spring
”Send Another”Sets isSent to false; form re-appears

Build docs developers (and LLMs) love