Skip to main content

Documentation Index

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

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

DoorbellForm is a theatrical four-stage contact experience: visitors first see a physical doorbell mounted on a wood panel, press it to trigger a “DING… DONG…” animation, and are then presented with a “Visitor’s Log” form. On submission the page flickers and a confirmation message appears. The component manages all UI state internally and requires no props.

Component Stages

The form cycles through four internal states managed by a single useState hook:
StateWhat the user sees
doorbellA wood-grained panel with a round brass doorbell button and “Press to enter” label.
ringingThe same panel, button disabled, with a pulsing “DING… DONG…” text below.
formAn off-white “Visitor’s Log” panel with Name, Email, and Message fields.
submittedA “Message Received” confirmation with a “Return to Porch” reset button.
The transition from ringing to form is automatic — a 2-second setTimeout fires after the button press, so visitors experience the doorbell ring before the form slides in.

Fields

The Visitor’s Log form contains three required fields:
LabelInput typePlaceholderTailwind classes
Name (Living or Dead)textJohn Doeborder-b-2 border-night/20 focus:border-pumpkin outline-none transition-colors
Ethereal Address (Email)emailghost@example.comsame as above
Message from beyondtextareaI summon thee for a project...same + resize-none
All fields use a transparent background against the cream panel (bg-[#f4f1ea]), with only a bottom border visible — the colour transitions from border-night/20 to border-pumpkin on focus.

Submit Button

The “Leave Message” button is styled to match the brass doorbell aesthetic:
<button
  type="submit"
  className="w-full bg-night text-pumpkin font-spooky text-2xl py-3 rounded
             hover:bg-pumpkin hover:text-night transition-colors
             flex items-center justify-center gap-2 group"
>
  Leave Message
  <Send className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
</button>
On submit, the component adds animate-flicker to document.body for a 1.5-second light-flicker effect before switching to the submitted state.

Doorbell Button Design

The doorbell itself uses a nested shadow trick to simulate a physical press:
<motion.button
  whileHover={{ scale: 1.05 }}
  whileTap={{ scale: 0.95 }}
  className="w-16 h-16 rounded-full bg-tombstone border-4 border-tombstone-dark
             shadow-[0_4px_0_#2a2a2a]
             active:shadow-[0_0px_0_#2a2a2a] active:translate-y-1
             transition-all"
>
  <div className="w-8 h-8 rounded-full bg-pumpkin shadow-inner" />
</motion.button>
The shadow-[0_4px_0_#2a2a2a] creates a raised-button illusion; active:translate-y-1 combined with removing the shadow on press makes it feel like the button physically depresses.

Usage

import { DoorbellForm } from './components/DoorbellForm';

export default function ContactPage() {
  return (
    <div className="w-full max-w-4xl mx-auto py-12 flex flex-col items-center">
      <h1 className="font-spooky text-5xl text-pumpkin mb-4">
        Leave a message at the doorbell
      </h1>
      <DoorbellForm />
    </div>
  );
}

Backend Integration

The form is client-side only by default — the onSubmit handler triggers the flicker animation and advances to the submitted state without sending data anywhere. Wire up your preferred form service by replacing the onSubmit logic in DoorbellForm.js.
// Replace the <form> element's onSubmit with a Formspree action:
<form action="https://formspree.io/f/YOUR_ID" method="POST">
  {/* fields stay the same */}
</form>
The submitted state renders a “Return to Porch” button that calls setState('doorbell'), resetting the entire flow. If you integrate a real backend, make sure your success/error handling also resets the form fields — you can do this by giving the <form> element a key prop tied to a submission counter.

Animations Summary

All stage transitions are wrapped in AnimatePresence with mode="wait", so the exiting stage fully animates out before the next one enters.
Transitioninitialanimateexit
Doorbell → Formopacity: 0, scale: 0.8opacity: 1, scale: 1opacity: 0, scale: 1.2, blur: 10px
Form → Submittedopacity: 0, y: 20opacity: 1, y: 0opacity: 0, y: -20
Submitted → Resetopacity: 0, scale: 0.8opacity: 1, scale: 1

Build docs developers (and LLMs) love