Skip to main content

Documentation Index

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

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

The Contact page transforms a standard enquiry form into a casting ritual. Three minimalist fields — each with only a gold bottom border — collect the visitor’s name, email, and message. Pressing “Cast the Spell” blurs the form and summons an animated SVG sigil: a five-pointed star traces itself over three seconds inside a closing circle. When the sigil completes, the form is replaced by a “Sigil Received” success state with an animated checkmark. A “Cast Another” button resets the entire ritual.

Overview

The Contact page lives at route /contact and is titled “Send a Sigil”. Its opening quote — “Inscribe your message. I shall divine a response.” — frames the act of sending a message as an act of inscription. Two booleans manage the UI: isCasting (true while the sigil animation plays) and isSuccess (true after the animation completes). The onSubmit handler prevents the default form action, sets isCasting to true, then after 3000 ms sets isCasting to false and isSuccess to true. AnimatePresence mode="wait" orchestrates the transitions between form, sigil overlay, and success state.
The form does not include a real form submission — the onSubmit handler only manages UI state. To wire up a real backend, call your API (e.g., Formspree, EmailJS, or a custom endpoint) inside onSubmit before starting the animation timer, and handle error states by resetting isCasting if the request fails.

Layout & Components

Form fields (×3)

Three div wrappers each hold a label and an input or textarea. All inputs have background: transparent, border-bottom: 1px solid gold/30, and focus:border-gold for a minimalist look. All are disabled when isCasting is true, preventing re-submission during the animation.

Submit button

A full-width button with a gold border and serif uppercase text. It shows “Cast the Spell” with a Send icon in its idle state, and “Casting…” (no icon) during isCasting. The button is disabled during casting.

Sigil SVG overlay

When isCasting is true, an absolutely-positioned motion.div renders over the blurred form. It contains a motion.svg with two motion.path / motion.circle children: a five-pointed star and a surrounding circle. Both paths animate pathLength from 0 to 1. The circle uses a delay: 0.5 so it begins drawing after the star.

Success state

The success state renders a 64×64 gold circle with an animated motion.svg checkmark path inside. The circle scales in from scale: 0 via a spring. The checkmark’s pathLength animates from 0 to 1 with a delay: 0.5. Below it, “Sigil Received” and a flavour text paragraph complete the confirmation.

Data Structure

The form fields are hardcoded in JSX rather than driven by a data array. Here is their effective structure for reference:
const formFields = [
  {
    label: "Your True Name",
    type: "text",
    name: "name",
    placeholder: "e.g. John Doe",
    required: true,
  },
  {
    label: "Ethereal Address (Email)",
    type: "email",
    name: "email",
    placeholder: "john@example.com",
    required: true,
  },
  {
    label: "The Incantation (Message)",
    type: "textarea",
    name: "message",
    placeholder: "Speak your purpose...",
    rows: 4,
    required: true,
  },
];
The sigil paths are defined inline within the JSX:
// Five-pointed star path (viewBox 0 0 100 100)
const sigilStarPath = "M50 10 L90 90 L10 40 L90 40 L10 90 Z";

// Outer circle (cx=50, cy=50, r=45)
// Animated separately with delay: 0.5

Animations

The motion.form uses animate: { filter: isCasting ? "blur(4px)" : "blur(0px)" }. This is a CSS filter applied by Framer Motion as an inline style, so the blur transition runs through the Framer Motion animation pipeline and can be interrupted cleanly if the component re-renders.
The motion.path for the star uses initial: { pathLength: 0 } and animate: { pathLength: 1 } with transition: { duration: 2, ease: "easeInOut" }. A drop-shadow filter on the SVG container (drop-shadow-[0_0_20px_rgba(201,169,110,0.8)]) gives the drawing stroke a strong gold radiance.
The motion.circle animates pathLength from 0 to 1 with initial: { pathLength: 0, opacity: 0 } and animate: { pathLength: 1, opacity: 0.5 }. The delay: 0.5 means the circle begins closing around the already-drawing star, framing it.
At delay: 2.5 (just as the sigil finishes drawing), a motion.div with mix-blend-mode: overlay and a gold background flashes opacity: [0, 0.8, 0] over duration: 0.5. This creates a brief “seal” flash before the success state transitions in, punctuating the ritual completion.
The success circle scales in via initial: { scale: 0, opacity: 0 }animate: { scale: 1, opacity: 1 } with a spring transition. The checkmark motion.svg path draws from pathLength: 0 to pathLength: 1 over 1 s with a delay: 0.5. Both animations are orchestrated by AnimatePresence mode="wait".

Customization

1

Wire up a real email service

Inside the onSubmit handler, collect form values from event.target elements (or use useRef / controlled inputs), then await a fetch call to Formspree, EmailJS, or your own API endpoint. Start the sigil animation only after the request resolves successfully. On failure, reset isCasting to false and display an error message.
2

Change the sigil geometry

Replace the star path "M50 10 L90 90 L10 40 L90 40 L10 90 Z" with any SVG d attribute. Pentacles, hexagrams, and spirals all work well in a 100×100 viewBox. Keep the path relatively simple — highly complex paths with many points can look jittery during the pathLength draw animation.
3

Adjust animation timing

The sigil animation takes 2 s (star) + 0.5 s delay for circle start = effectively 2.5 s before the flash. The setTimeout that triggers the success state is set to 3000 ms. If you lengthen the star duration, increase the timeout accordingly to avoid the success state appearing before the animation completes.
4

Add field validation feedback

The current form relies on native HTML5 required validation. For custom error messages that match the gold aesthetic, add controlled state for each field and render a small motion.p below each input that fades in when the field is empty on submit. Use initial: { height: 0, opacity: 0 } and animate: { height: "auto", opacity: 1 } for the error reveal.
The “Cast Another” button in the success state calls setIsSuccess(false), which returns the user to the form. If you want the form to reset its field values on this action, add a key prop to the motion.form element that increments each time the user resets (key={formKey}). Changing the key causes React to unmount and remount the form, clearing all uncontrolled inputs.

Build docs developers (and LLMs) love