Skip to main content

Documentation Index

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

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

The Contact page (contact.html) is the outreach endpoint for the portfolio. It renders two side-by-side sections: the interactive SummoningForm component for direct messages, and a collection of direct contact links — email address, GitHub profile, LinkedIn profile, and a downloadable résumé. Both sections are wrapped in the GlowFrame card container and share the magenta neon accent color used throughout contact.html.

SummoningForm

The SummoningForm component lives at components/contact/SummoningForm.js. It is a client-side React form with three fields, a submit button, and an animated success state. There is no backend — the form captures input and simulates transmission client-side.

Form Fields

The form contains three required fields, each with a themed label that uses a small rune sigil icon alongside the field name:
FieldHTML TypeLabel (Themed)Placeholder
Nametext”Entity Identifier” (rune-1 sigil)Your name
Emailemail”Return Frequency” (rune-2 sigil)your@email.com
Messagetextarea (5 rows)“Transmission Payload” (rune-3 sigil)What knowledge do you seek?
All three fields are marked required. The email field uses type="email" for native browser validation.

Submission Behavior

The form uses a simulated submission flow defined in the component’s onSubmit handler. When the submit button is clicked:
  1. The button immediately displays “Channeling…” and pulses via the animate-pulse Tailwind animation for 1,500ms.
  2. After 1,500ms, the entire form is replaced by an animated success state: a large rune-4 sigil icon fades and scales in (opacity: 0, scale: 0.9opacity: 1, scale: 1), followed by the heading “Signal Transmitted” and the line “The message has entered the void.”
  3. The success state auto-dismisses after 3,000ms and the form reappears.
The SummoningForm does not send any data anywhere. The onSubmit handler calls event.preventDefault() and runs a timer — no fetch, no mailto, no form action attribute. Before publishing a live portfolio, replace this behavior with a real form service. See the production note at the bottom of this page.
// Simplified submission handler from SummoningForm.js
const handleSubmit = (e) => {
  e.preventDefault();
  setIsLoading(true);              // Shows "Channeling..." state
  setTimeout(() => {
    setIsLoading(false);
    setIsSuccess(true);            // Shows "Signal Transmitted" state
    setTimeout(() => setIsSuccess(false), 3000);
  }, 1500);
};

Styling

The form is wrapped in a GlowFrame component set to color="magenta" with animate={true}, giving the entire card a pulsing magenta glow border. Individual input and textarea elements share a consistent neon-bordered style:
  • Background: Semi-transparent dark base (bg-base/50)
  • Border: 1px solid magenta at 30% opacity at rest; full neon-magenta on focus
  • Border radius: rounded-none — sharp square corners throughout
  • Focus ring: 1px magenta ring matching the border color, activated via :focus
  • Font: Inter (body font) for user-typed content; JetBrains Mono for labels
  • Submit button: A full-width GlyphButton component in solid magenta, labeled “Send The Signal”
The heading above the form — “Establish Connection” — uses the Tektur display font with a magenta text-glow effect (text-glow-magenta), and is preceded by the contact sigil icon centered above it at 40px. Alongside the SummoningForm, contact.html contains a direct links section for visitors who prefer not to use the form. This section is plain HTML in the page file and must be edited directly. The four link types included by default are:

Email Address

A mailto: link rendered as a styled anchor. Replace the placeholder href="#" with href="mailto:your@email.com" to make this functional.

GitHub Profile

A link to the portfolio owner’s GitHub profile. Replace href="#" with the full GitHub profile URL, e.g. href="https://github.com/yourusername".

LinkedIn Profile

A link to the LinkedIn profile. Replace href="#" with the full LinkedIn URL, e.g. href="https://linkedin.com/in/yourprofile".

Résumé / CV Download

A downloadable file link. Replace href="#" with the path to your résumé PDF, e.g. href="./files/resume.pdf", and store the file in the repository.
All four contact link href values default to "#" in the theme. A "#" href scrolls the page to the top and does nothing useful. Replace every placeholder before publishing. Visitors clicking a broken link will not know how to reach you.

How to Update the Contact Page

1

Open contact.html

Open contact.html in a text editor. The contact links section is visible as a set of anchor elements near the lower portion of the page body. Each link has an href="#" placeholder.
2

Replace the email link

Find the email anchor and update the href to use a mailto: URI:
<!-- Before -->
<a href="#">your@email.com</a>

<!-- After -->
<a href="mailto:your@email.com">your@email.com</a>
Update the visible link text to your actual address as well.
3

Replace the GitHub and LinkedIn links

Locate the GitHub and LinkedIn anchor elements and replace their href values with your full profile URLs:
<a href="https://github.com/yourusername">GitHub</a>
<a href="https://linkedin.com/in/yourprofile">LinkedIn</a>
4

Add your résumé file and update the download link

Place your résumé PDF in the repository, ideally in a files/ folder to keep the root tidy. Then update the résumé link:
<a href="./files/resume.pdf" download>Download Résumé</a>
The download attribute prompts the browser to download the file rather than open it inline.
5

Connect the SummoningForm to a real backend (recommended)

For a production portfolio, replace the simulated onSubmit handler in components/contact/SummoningForm.js with a real fetch call to a form service endpoint. See the production note below for service recommendations.

Accessibility Notes

The SummoningForm is built with accessibility in mind:
  • Every input has an explicit <label> element with a matching htmlFor / id pair (name, email, message). Screen readers announce the field purpose when focus moves into each input.
  • Focus rings use focus:ring-1 focus:ring-neon-magenta — a 1px magenta ring that is visible against the dark background and distinct from the resting border color.
  • The submit GlyphButton receives disabled={true} and the animate-pulse class during the loading state, preventing double submission and providing visual feedback.
  • The success state is rendered inside the same DOM container as the form, so screen readers that are monitoring the region will announce the state change.
The textarea element uses resize-none to disable manual resizing, which prevents layout breakage on smaller screens. If you want to allow resizing, remove the resize-none class from the textarea in SummoningForm.js and replace it with resize or resize-y.

Production Recommendation: Use a Form Service

Because the SummoningForm has no built-in backend, messages typed into it on a published site will never be received. For a live portfolio, wire the form to one of these services instead of a mailto: link — mailto: exposes your address to scrapers and requires the visitor to have a mail client configured.
Formspree provides a free tier for static site forms. Create an endpoint and replace the onSubmit handler with a fetch POST to your Formspree URL. Submissions arrive in your email inbox and optionally a Formspree dashboard.
const handleSubmit = async (e) => {
  e.preventDefault();
  const data = new FormData(e.target);
  await fetch("https://formspree.io/f/YOUR_FORM_ID", {
    method: "POST",
    body: data,
    headers: { Accept: "application/json" },
  });
  setIsSuccess(true);
};
If you deploy to Netlify instead of GitHub Pages, add netlify and name="contact" attributes to the <form> element. Netlify’s build system detects and registers the form automatically. No JavaScript changes are required for basic submission capture.
EmailJS sends form submissions directly to your email using browser-side JavaScript and an API key. It requires no server and integrates cleanly with the existing React component structure in SummoningForm.js.

Build docs developers (and LLMs) love