Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/apursley2012/neon-retro-web/llms.txt

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

The Contact page (/contact) is split into two columns at the md breakpoint. The left column (one third of the grid) renders a pixel-perfect AIM Instant Messenger 5.x Buddy List panel — complete with the navy-to-powder-blue gradient title bar, the _ and X window control buttons, a DevUser99 | Online status row, and a collapsible buddy list split into Socials (GitHub, LinkedIn, Twitter) and Direct (Email Me). A mocked ad banner fills the panel footer. The right column (two thirds of the grid) holds a Window component titled Sign_My_Guestbook.cgi with a full guestbook form. The form manages its own idle → submitting → success state machine with a simulated 1.5-second delay before showing the success screen.

Page Structure

// Contact page component (minified identifier: om)
const ContactPage = () => {
  const [formState, setFormState] = useState("idle"); // "idle" | "submitting" | "success"

  const handleSubmit = (e) => {
    e.preventDefault();
    setFormState("submitting");
    setTimeout(() => setFormState("success"), 1500); // ← simulated delay, no real backend
  };

  return (
    <motion.div
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
      className="max-w-4xl mx-auto grid grid-cols-1 md:grid-cols-3 gap-8 pb-16"
    >
      {/* AIM Buddy List — col-span-1 */}
      <div className="md:col-span-1">
        {/* AIM window chrome */}
      </div>

      {/* Guestbook Window — col-span-2 */}
      <div className="md:col-span-2">
        <Window title="Sign_My_Guestbook.cgi" icon={<PenSquare />} className="h-full">
          {/* Guestbook form or success state */}
        </Window>
      </div>
    </motion.div>
  );
};

AIM Buddy List Panel

The AIM panel is not the shared Window component — it replicates AIM 5.x chrome from scratch using hardcoded colours:
ElementColour / Class
Title bar backgroundbg-gradient-to-r from-[#0a246a] to-[#a6caf0]
Panel backgroundbg-[#ece9d8] (Windows XP tan)
Buddy list bodybg-white border-2 border-gray-500 border-r-white border-b-white
Window control buttonsbg-[#ece9d8] border border-white border-r-gray-500 border-b-gray-500
UsernameDevUser99 in bold, status Online in gray

Buddy List Groups

// AIM Buddy List — group structure
// "Socials (3/3)" group
<ul>
  <li><a href="#">GitHub</a></li>     // Github icon (Lucide)
  <li><a href="#">LinkedIn</a></li>   // Linkedin icon (Lucide)
  <li><a href="#">Twitter</a></li>    // Twitter icon (Lucide)
</ul>

// "Direct (1/1)" group
<ul>
  <li><a href="mailto:hello@example.com">Email Me</a></li>
</ul>
The buddy list footer shows a placeholder 120×60 ad banner image. Both href="#" links and the mailto: address need to be updated to real URLs.

Guestbook Form

The guestbook is inside a Window component. When formState === "idle" or "submitting", the form is rendered. When formState === "success", the form is replaced by a success screen.

Form Fields

name
string
required
Label: Name / Handle:Placeholder: xX_CoolDev_Xx. Required field (required attribute). Disabled while formState === "submitting".
email
string
Label: Email (Optional):Placeholder: nobody@example.com. Type email for browser-native validation. Not required. Disabled while submitting.
message
string
required
Label: Message:A <textarea> with min-h-[150px] and resize-none. Placeholder: Great site! Best viewed in Netscape Navigator.... Required. Disabled while submitting.

Form State Machine

// Form state transitions
"idle"user submits"submitting" (1.5s timeout) → "success"
"success"user clicks "Sign again?""idle"

// Submit handler — NOTE: no real backend, data is not transmitted
const handleSubmit = (e) => {
  e.preventDefault();
  setFormState("submitting");
  setTimeout(() => setFormState("success"), 1500);
};

Success Screen

When formState === "success", the form is replaced by a centered, animate-pulse success panel:
<div className="flex-1 flex flex-col items-center justify-center text-center animate-pulse">
  <h3 className="font-display text-2xl text-y2k-lime mb-2">MESSAGE SAVED!</h3>
  <p className="font-code text-sm text-gray-300">
    Thanks for dropping by my corner of the web.
  </p>
  <button onClick={() => setFormState("idle")} className="mt-6 font-code text-xs text-y2k-teal underline">
    Sign again?
  </button>
</div>

Customising This Page

This is a pre-built static site. The repository contains only compiled output — there is no src/ directory or editable source files. The customisation steps below describe changes that must be made in the original source project before rebuilding.
1

Update social links in the Buddy List

In your source project, replace the three <a href="#"> links in the Socials group and the <a href="mailto:hello@example.com"> in the Direct group with your actual GitHub, LinkedIn, and Twitter/X URLs. Change hello@example.com to your real email address.
2

Rename the AIM username

In the source project, the DevUser99 username and Online status label are plain text strings. Update both inside the AIM status header bar elements.
3

Change the buddy list group counts

The group headings read Socials (3/3) and Direct (1/1). These are static strings — update the counts in the source project if you add or remove buddy list entries.
4

Wire up a real form backend

The current handleSubmit fires a setTimeout instead of sending data anywhere. No message data is stored or transmitted. To collect guestbook submissions, replace the setTimeout in the source project with a real fetch call to one of these services:
  • Formspree: fetch("https://formspree.io/f/YOUR_ID", { method: "POST", body: new FormData(e.target) })
  • Netlify Forms: Add netlify and name="guestbook" attributes to the <form> tag — Netlify’s build step handles the rest.
  • EmailJS: Use emailjs.sendForm(serviceId, templateId, e.target) for client-side email delivery.
  • Custom API: POST to any REST endpoint with { name, email, message } as JSON body.
Wrap the fetch in a try/catch and call setFormState("success") in the .then() handler.
5

Add more direct contact methods

To add a Discord, Mastodon, or other contact method to the Direct group in the source project, add a new <li> inside the Direct <ul> with the appropriate icon and href. Import the icon from lucide-react at the top of the file.
The guestbook form has no real backend wired up. The 1.5-second setTimeout simulates a network request purely for visual effect — no message data is stored or transmitted. Before sharing the portfolio publicly, connect a real form service (Formspree, Netlify Forms, EmailJS, or a custom API) so visitors’ messages are actually delivered.
The AIM Buddy List ad banner at the bottom of the panel uses a via.placeholder.com image (120×60, black background, teal text “AD SPACE”). This service may not be reliable in all environments. In the source project, replace it with a locally hosted image or a self-promotional banner (e.g., a link to your resume PDF).
The guestbook header reads ~*~ GUESTBOOK ~*~ in font-display text-3xl text-y2k-magenta. The tilde-asterisk decoration is a plain text string — change it in the source project to any retro-style title format you like: [ GUESTBOOK ], :: sign my page ::, or *** LEAVE A NOTE *** all work with the neon aesthetic.

Build docs developers (and LLMs) love