Documentation Index
Fetch the complete documentation index at: https://mintlify.com/apursley2012/cosmic-developer/llms.txt
Use this file to discover all available pages before exploring further.
The Contact page (/contact) presents the portfolio’s inquiry form as an open comms channel — a two-column interface where the left side sets the scene and the right side holds a transmission form with three fields. Submitting the form triggers a two-second “Transmitting…” state with a vertical progress bar sweeping across the panel, which then resolves into a “Transmission Received” confirmation screen with a signal strength readout.
Layout Overview
The page uses a PageTransition wrapper with a two-column responsive layout:
flex-1 flex flex-col lg:flex-row gap-12 items-center justify-center
max-w-6xl mx-auto w-full min-h-[80vh]
| Column | Width | Content |
|---|
| Left | w-full lg:w-1/2 | Intro heading + flavour copy |
| Right | w-full lg:w-1/2 | Form or success panel (animated swap) |
On mobile the columns stack vertically; on large screens they sit side-by-side.
Left Column — Channel Introduction
The left panel contains:
- A pulsing
Radio icon (animate-pulse) beside the TeletypeText prompt in aurora-teal
- An H1: “Open Channel” —
"Channel" in text-aurora-teal
- Intro copy:
“Whether you have a mission proposal, need a co-pilot for a new project, or just want to discuss the Fermi Paradox, transmit your message below.”
Right Column — Form States
The right column is wrapped in a Framer Motion AnimatePresence with mode="wait", allowing a smooth transition between the form state and the success state.
Form State (idle / sending)
A glass-panel p-8 space-y-6 card contains the three form fields and submit button:
Form Fields
| Field | Type | Label | Placeholder |
|---|
name | text | Sender Callsign | Commander Shepard |
email | email | Frequency for Reply (Email) | shepard@normandy.sr2 |
message | textarea (rows: 4) | Your Transmission | We need your expertise in the Terminus Systems... |
All three fields share the same base styling:
w-full bg-cosmic-black/50 border border-star-dim/30 rounded-lg px-4 py-3
text-star-white focus:outline-none focus:border-aurora-teal transition-colors
Labels use font-mono text-xs text-star-dim uppercase tracking-wider. Fields are disabled during the sending state (disabled:opacity-50).
Submit Button
w-full bg-aurora-teal text-cosmic-black font-heading font-bold py-3 rounded-lg
hover:bg-aurora-teal/90 transition-colors flex items-center justify-center gap-2
| State | Button text | Icon |
|---|
idle | "Initiate Sequence" | Send (Lucide) |
sending | "Transmitting..." | Send (Lucide) |
Transmitting Progress Bar
When status === 'sending', a Framer Motion motion.div renders as a 1px-tall bar at the very top of the form panel:
<motion.div
className="absolute top-0 left-0 w-full h-1 bg-aurora-teal shadow-[0_0_15px_#3dd6c4] z-10"
animate={{ y: [0, 500] }}
transition={{ duration: 1.5, repeat: Infinity, ease: 'linear' }}
/>
The bar sweeps vertically downward from the top of the panel in a looping 1.5s cycle, simulating a progress scanner rather than a horizontal fill bar.
The transmitting state lasts exactly 2 seconds, after which status transitions to 'sent' and the form is replaced with the success panel via AnimatePresence.
Success State (sent)
After the 2-second send delay, AnimatePresence fades out the form and replaces it with a confirmation panel:
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0 }}
className="glass-panel p-12 text-center flex flex-col items-center justify-center min-h-[400px]"
>
The success panel contains:
| Element | Details |
|---|
| Icon container | w-16 h-16 rounded-full bg-aurora-teal/20 — holds the Send icon in text-aurora-teal |
| Heading | "Transmission Received" — text-2xl font-heading font-bold text-star-white |
| Subtext | "Signal strength: 100%. Expect a reply within 1-2 Earth days." — font-mono text-sm text-star-dim |
The success state auto-resets to idle after 5 additional seconds, allowing the form to be reused.
Form State Machine
The contact form uses a three-state string variable managed with React.useState:
const [status, setStatus] = useState('idle')
const handleSubmit = (e) => {
e.preventDefault()
setStatus('sending')
setTimeout(() => {
setStatus('sent')
setTimeout(() => setStatus('idle'), 5000)
}, 2000)
}
| State | What is shown |
|---|
idle | Form — all fields enabled, button reads “Initiate Sequence” |
sending | Form — all fields disabled, button reads “Transmitting…”, scan bar active |
sent | Success panel with checkmark and signal strength message |
Customization
Wiring up a real backend — replace the setTimeout blocks in handleSubmit with a fetch or axios call to your API endpoint:
const handleSubmit = async (e) => {
e.preventDefault()
setStatus('sending')
try {
await fetch('/api/contact', {
method: 'POST',
body: new FormData(e.target),
})
setStatus('sent')
} catch {
setStatus('idle') // handle error state
}
}
Changing the scan bar to horizontal — swap the y animation axis for x and set w-1/3 h-full instead of w-full h-1 to make the progress indicator sweep left-to-right.
Adding a fourth field — add a new <div> block with label + input inside the form, following the same pattern as the existing three fields. The space-y-6 container will automatically add spacing.
Because this is a client-side portfolio, no form submission actually occurs — the setTimeout simulates the round-trip. When deploying for real use, consider integrating Resend or Formspree for serverless email delivery without a dedicated backend.