Skip to main content

Documentation Index

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

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

The Contact page (nav label: Summoning, route /contact) wraps a message form inside a crystal ball — a large glowing orb rendered entirely in CSS. Typing a message and clicking Cast triggers a two-second amber transmutation animation before the orb shifts into a confirmation state. The page is pure theatre: no data leaves the browser.
The form does not send any messages. Submitting it calls e.preventDefault() and runs a local setTimeout simulation — no HTTP request is made, no email is sent, and no backend receives the data. To make it functional you will need to integrate a real submission mechanism. Options include:
  • Email APIs: Resend, SendGrid, or Mailgun — call their REST API from a serverless function
  • Form services: Formspree or Netlify Forms — replace the setTimeout simulation with a fetch POST to the service endpoint
  • Custom backend: POST the message to your own API route (e.g., Next.js /api/contact) and send email from there
Until one of these is wired up, the “Summoning” page is a visual demo only.

What’s on This Page

Heading block:
Summoning
Project your thoughts into the ether.
Crystal Ball Orb A fixed-size w-80 h-80 (320 × 320 px) rounded-full circle centered on the page. Its glass appearance comes from layered CSS and Framer Motion effects:
LayerStyle
Outer glowbox-shadow: inset 0 0 50px rgba(107,63,160,0.5), 0 0 30px rgba(107,63,160,0.2)
Animated inner gradientradial-gradient(circle, rgba(107,63,160,0.8) 0%, transparent 60%), rotates on a 20-second infinite loop with a breathing scale: [1, 1.2, 1]
White glareAn absolutely positioned w-32 h-16 white ellipse at top-4 left-8, rotated -30deg, blurred — purely cosmetic
Decorative baseA gradient gray → black ellipse beneath the orb
The Form (inside the orb) All form elements are rendered as absolutely-positioned children centered inside the orb:
  • Textareatransparent bg, border-none, resize-none, centered font-serif text-xl, placeholder: "Whisper your intent..."
  • Submit buttonborder-amber/50 rounded-full, label "Cast", disabled when the textarea is empty or state is "casting"

Key Interactions

Three submission states:
1

idle

The orb glows violet. The textarea is active. The “Cast” button is enabled as soon as the textarea contains non-whitespace text.
2

casting

Triggered on form submit (only if message.trim() is non-empty). The orb’s box-shadow animates from violet (rgba(107,63,160,...)) to amber (rgba(245,178,91,...)). The textarea and button are both disabled. This state lasts exactly 2 seconds (via setTimeout).
3

sent

After the 2-second casting delay, the form is replaced with a confirmation message:
  • “Message Received” (large accent heading)
  • “The spirits will deliver it shortly.” (muted subtitle)
The textarea content is cleared. After a further 3 seconds the state resets to "idle" automatically.
The state transition in code:
// src/pages/Contact.jsx
const handleSubmit = (e) => {
  e.preventDefault();
  if (!message.trim()) return;
  setFormState('casting');
  setTimeout(() => {
    setFormState('sent');
    setMessage('');
    setTimeout(() => setFormState('idle'), 3000);
  }, 2000);
};

Customizing

Changing placeholder and button text:
// src/pages/Contact.jsx
<textarea
  placeholder="Whisper your intent..."   {/* ← update here */}
  className="w-full h-32 bg-transparent border-none resize-none
             text-center font-serif text-xl text-bone
             placeholder:text-bone/30 focus:outline-none focus:ring-0"
  value={message}
  onChange={(e) => setMessage(e.target.value)}
  disabled={formState === 'casting'}
/>

<motion.button
  type="submit"
  disabled={formState === 'casting' || !message.trim()}
  className="mt-4 px-6 py-2 border border-amber/50 rounded-full
             font-accent text-xl text-amber ..."
>
  {formState === 'casting' ? 'Casting...' : 'Cast'}  {/* ← update labels */}
</motion.button>
Changing the confirmation message:
{formState === 'sent' && (
  <motion.div initial={{ opacity: 0, scale: 0.5 }} animate={{ opacity: 1, scale: 1 }}>
    <p className="font-accent text-3xl text-amber mb-2">Message Received</p>
    <p className="font-sans text-sm text-bone/70">
      The spirits will deliver it shortly.   {/* ← update here */}
    </p>
  </motion.div>
)}
Adjusting the casting duration:
// Shorter: 1 second casting animation
setTimeout(() => { setFormState('sent'); ... }, 1000);

// Longer: 4 second casting animation (more dramatic)
setTimeout(() => { setFormState('sent'); ... }, 4000);
The animated inner gradient inside the orb uses opacity: 0.3 on its wrapper so it never overpowers the form content. If you increase the opacity for a more dramatic glow, test that the textarea placeholder text remains legible — there is no text-shadow fallback on the form elements.

Build docs developers (and LLMs) love