Skip to main content

Documentation Index

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

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

The Contact page — titled Summon Me — reframes a standard contact form as a ritual invocation. Each form field has a floating label that rises and shrinks on focus using CSS peer classes. The submit button reads Cast Spell, transitions to Casting… during the async delay, and then the entire form is replaced by an animated success card. The right panel shows The Ledger — a mock guestbook with three sample entries.

Route

/contact
<motion.h1
  initial={{ opacity: 0, x: -20 }}
  animate={{ opacity: 1, x: 0 }}
  className="font-display text-5xl md:text-7xl text-neon-lime glow-text-lime mb-4"
>
  Summon Me
</motion.h1>
<p className="font-mono text-slate-400 mb-12">
  Send a raven, or just use this form.
</p>
This is the only page header that uses an x axis entrance animation (x: -20 → 0) instead of the standard y axis slide-up. The title uses text-neon-lime with glow-text-lime.

Page Layout

The page uses a two-column grid layout on large screens:
<div class="max-w-5xl mx-auto py-12 grid grid-cols-1 lg:grid-cols-2 gap-16">
  <!-- Left: Form panel -->
  <!-- Right: Guestbook panel -->
</div>
On mobile (grid-cols-1), the form stacks above the guestbook. On lg: and above, they sit side by side with gap-16 between them.

State

The page uses two useState hooks:
const [isSubmitting, setIsSubmitting] = useState(false);
const [isSuccess,    setIsSuccess]    = useState(false);
  • isSubmitting disables the submit button and changes its label to Casting…
  • isSuccess hides the form entirely and renders the success card

Submit Handler

const handleSubmit = (e) => {
  e.preventDefault();
  setIsSubmitting(true);
  setTimeout(() => {
    setIsSubmitting(false);
    setIsSuccess(true);
  }, 1500);
};
After a 1500ms delay, the submitting state clears and the success state activates. There is no real network request in the current implementation — it is a simulated async flow.

Form Fields

The form contains three fields, all using the same floating-label pattern via Tailwind’s peer modifier classes.

[Identifier / Name]

<div className="relative group">
  <input
    type="text"
    required
    placeholder="Your Name"
    className="w-full bg-transparent border-b-2 border-slate-700 py-3 px-2
               font-mono text-slate-200 focus:outline-none focus:border-neon-lime
               transition-colors peer placeholder-transparent"
  />
  <label
    className="absolute left-2 top-3 font-mono text-slate-500 text-sm transition-all
               peer-focus:-top-4 peer-focus:text-xs peer-focus:text-neon-lime
               peer-valid:-top-4  peer-valid:text-xs  peer-valid:text-neon-lime"
  >
    [Identifier / Name]
  </label>
</div>

[Return Address / Email]

<div className="relative group">
  <input
    type="email"
    required
    placeholder="Your Email"
    className="w-full bg-transparent border-b-2 border-slate-700 py-3 px-2
               font-mono text-slate-200 focus:outline-none focus:border-neon-lime
               transition-colors peer placeholder-transparent"
  />
  <label
    className="absolute left-2 top-3 font-mono text-slate-500 text-sm transition-all
               peer-focus:-top-4 peer-focus:text-xs peer-focus:text-neon-lime
               peer-valid:-top-4  peer-valid:text-xs  peer-valid:text-neon-lime"
  >
    [Return Address / Email]
  </label>
</div>

[Incantation / Message]

The textarea field uses a slightly different label positioning because it has a top border on all sides (not just the bottom):
<div className="relative group pt-4">
  <textarea
    required
    rows={4}
    placeholder="Your Message"
    className="w-full bg-transparent border-2 border-slate-700 p-3
               font-mono text-slate-200 focus:outline-none focus:border-neon-lime
               transition-colors peer placeholder-transparent resize-none"
  />
  <label
    className="absolute left-3 top-7 font-mono text-slate-500 text-sm transition-all
               peer-focus:top-0 peer-focus:bg-void peer-focus:px-1 peer-focus:text-xs peer-focus:text-neon-lime
               peer-valid:top-0  peer-valid:bg-void  peer-valid:px-1  peer-valid:text-xs  peer-valid:text-neon-lime"
  >
    [Incantation / Message]
  </label>
</div>
The textarea label animates to top: 0 (overlapping the border) and adds bg-void px-1 to create a “cut-out” effect where the label sits in a gap in the border line.

Floating Label Animation

All three labels use the CSS peer pattern — the <input> or <textarea> gets peer and the <label> uses peer-focus: and peer-valid: variants:
StateLabel PositionFont SizeColor
Default (empty, unfocused)top: 12px, left: 8pxtext-smtext-slate-500
Focused-top-4 (text/email) or top-0 (textarea)text-xstext-neon-lime
Valid (has content)Same as focusedtext-xstext-neon-lime
placeholder-transparent hides the native placeholder text, leaving only the floating label visible.

Submit Button

<button
  type="submit"
  disabled={isSubmitting}
  className="w-full py-4 border-2 border-neon-lime text-neon-lime font-display text-xl
             hover:bg-neon-lime hover:text-void transition-all duration-300
             relative overflow-hidden group disabled:opacity-50"
>
  <span className="relative z-10">
    {isSubmitting ? "Casting..." : "Cast Spell"}
  </span>
  {/* Slide-in fill on hover */}
  <div className="absolute inset-0 bg-neon-lime transform scale-x-0
                  group-hover:scale-x-100 transition-transform origin-left" />
</button>
The button uses a CSS reveal trick: a bg-neon-lime overlay div starts with scale-x-0 and expands to scale-x-100 on hover via origin-left, creating a left-to-right fill sweep. The text sits above it on z-10.
  • Default: border-neon-lime, text-neon-lime, transparent background
  • Hover: bg-neon-lime fill sweeps left-to-right; text color switches to text-void
  • Submitting: disabled:opacity-50, label changes to Casting…

Success State

When isSuccess is true, the form is replaced by:
<motion.div
  initial={{ opacity: 0, scale: 0.9 }}
  animate={{ opacity: 1, scale: 1 }}
  className="bg-neon-lime/10 border border-neon-lime p-8 text-center"
>
  <div className="text-4xl mb-4"></div>
  <h3 className="font-display text-2xl text-neon-lime mb-2">
    Message Cast Successfully
  </h3>
  <p className="font-mono text-sm text-slate-300">
    I will consult the oracles and reply shortly.
  </p>
</motion.div>
The success card scales in from 0.9 to 1 with opacity: 0 → 1.
1

User fills out the form

All three fields are required. The floating labels rise and turn neon-lime as fields gain focus and valid content.
2

User clicks Cast Spell

handleSubmit fires, isSubmitting becomes true. The button shows Casting… at 50% opacity.
3

1500ms delay

setTimeout resolves, isSubmitting resets to false, isSuccess becomes true.
4

Success card appears

The form unmounts and the ✨ Message Cast Successfully card scales in via Framer Motion.
Below the form (or success card), three social link icons are rendered:
{["GitHub", "LinkedIn", "Email"].map((platform) => (
  <a href="#" key={platform} className="group flex flex-col items-center gap-2">
    <div className="w-12 h-12 border border-slate-700 rounded-full flex items-center justify-center
                    text-slate-400 group-hover:border-cyan group-hover:text-cyan
                    group-hover:shadow-[0_0_15px_rgba(34,211,238,0.3)] transition-all duration-300">
      {platform === "GitHub"   && <span className="font-display text-xl"></span>}
      {platform === "LinkedIn" && <span className="font-display text-xl"></span>}
      {platform === "Email"    && <span className="font-display text-xl"></span>}
    </div>
    <span className="font-mono text-xs text-slate-500 group-hover:text-cyan">
      {platform}
    </span>
  </a>
))}
PlatformGlyphHover Color
GitHubtext-cyan, border-cyan, cyan glow
LinkedIntext-cyan, border-cyan, cyan glow
Emailtext-cyan, border-cyan, cyan glow

The Ledger (Guestbook)

The right panel is a styled guestbook with three mock entries:
<div className="bg-[#0a0a0a] border border-electric-purple/30 p-8 relative overflow-hidden">
  <div className="absolute top-0 right-0 w-32 h-32 bg-electric-purple/10 blur-3xl" />
  <h3 className="font-display text-2xl text-electric-purple mb-8 border-b border-electric-purple/20 pb-4">
    The Ledger (Guestbook)
  </h3>
  {/* entries */}
</div>

Guestbook Entries

const entries = [
  {
    name: "Senior Dev",
    role: "Tech Lead",
    msg:  "The code is clean, but the aesthetic is concerningly dark. 10/10.",
    time: "2 hours ago",
  },
  {
    name: "Recruiter",
    role: "Talent Acquisition",
    msg:  "Impressive portfolio! Do you have 10 years of experience in React 18?",
    time: "1 day ago",
  },
  {
    name: "Anonymous",
    role: "Wandering Spirit",
    msg:  "I came for the spells, stayed for the clean architecture.",
    time: "3 days ago",
  },
];
Each entry animates in from the right with a staggered delay:
<motion.div
  initial={{ opacity: 0, x: 20 }}
  animate={{ opacity: 1, x: 0 }}
  transition={{ delay: index * 0.2 + 0.5 }}
  className="bg-deep-void p-4 border-l-2 border-electric-purple"
>
  <div className="flex justify-between items-start mb-2">
    <div>
      <span className="font-bold text-slate-200 font-mono text-sm">{entry.name}</span>
      <span className="text-electric-purple text-xs ml-2">[{entry.role}]</span>
    </div>
    <span className="text-slate-600 text-xs font-mono">{entry.time}</span>
  </div>
  <p className="text-slate-400 text-sm font-mono">{entry.msg}</p>
</motion.div>
Entries use border-l-2 border-electric-purple as a left accent, with bg-deep-void fill. The role is displayed in brackets: [Tech Lead], [Talent Acquisition], [Wandering Spirit].
The 0.5 base delay on guestbook entries (delay: index * 0.2 + 0.5) ensures they begin animating only after the page header has finished its own entrance animation.
The guestbook panel has a overflow-hidden container so the decorative bg-electric-purple/10 blur-3xl corner glow blob does not bleed outside the panel boundary.

Build docs developers (and LLMs) love