Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/apursley2012/magical/llms.txt

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

Magical Portfolio is designed to be a template you fully inhabit — not just a theme you drop your name into. Every element, from the SVG stroke-animation that writes your name on the home portal to the tagline beneath it, is intentionally customizable. This guide walks through each layer of identity you can change to make the portfolio genuinely yours.

1. Personal Identity

Your name, tagline, initials, and home-page quote are all hardcoded as string literals in assets/main.js. Use your editor’s find-and-replace to swap them out.

Name (SVG Text Animation)

The hero name on the home page is drawn as an SVG stroke-dash animation — the characters appear to be written by an invisible quill. Locate the string "Elara Nightshade" in assets/main.js and replace it with your own name:
// Find:
children: "Elara Nightshade"

// Replace with:
children: "Your Full Name"
The viewBox="0 0 600 120" on the surrounding <svg> element accommodates names up to roughly 18–20 characters at the default font size. For significantly longer names, also adjust the viewBox width (e.g., "0 0 800 120") to prevent clipping.

Tagline

Directly beneath the animated name, a monospace tagline is displayed. Find and replace "Software Alchemist & Digital Weaver":
// Find:
children: "Software Alchemist & Digital Weaver"

// Replace with:
children: "Your Tagline Here"
The top-left logo in the navigation bar shows "E.G." (styled initials). Find this string in components/Navigation.js and replace it with your own initials:
// Find (in components/Navigation.js):
children: "E.G."

// Replace with:
children: "Y.N."  // Your initials

Home Page Quote

Below the tagline, a small italic quote is displayed in text-slate-400. The default text is:
"No actual spells, just suspiciously organized files and a concerning amount of coffee."
Find this string in assets/main.js and replace it with your own quip or motto.

2. Color Palette

The entire visual identity is built on a custom Tailwind color scale called mystic. In a source-based project, these tokens are defined in tailwind.config.js:
// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      colors: {
        mystic: {
          950: '#0a0612',   // Deepest background — page body
          900: '#14091f',   // Card backgrounds, surface elements
          800: '#2d1b4e',   // Borders, dividers, subtle fills
          violet: '#a855f7', // Primary accent — headings, glows, highlights
          teal:   '#2dd4bf', // Secondary accent — icons, progress fills
          mint:   '#a7f3d0', // Tertiary accent — hover states, success tones
        }
      }
    }
  }
}

Example Alternate Theme: Ocean

To shift the palette toward an ocean feel (navy / cyan / white), replace the hex values:
mystic: {
  950: '#020b18',   // Deep ocean floor
  900: '#041d33',   // Dark navy surface
  800: '#0a3a5c',   // Mid-water borders
  violet: '#0ea5e9', // Cerulean primary (sky/water)
  teal:   '#06b6d4', // Cyan secondary
  mint:   '#e0f2fe', // Ice-blue highlights
}
After changing the config in a source project, run npm run build to recompile the CSS. In the pre-built assets/main.css, you would need to find-and-replace the hex color values directly throughout the stylesheet.

3. Fonts

The three typefaces are loaded via a single Google Fonts @import at the very top of assets/main.css:
@import "https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500&family=Inter:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500&display=swap";
The three families and how they are used throughout the portfolio:
FamilyTailwind classCSS usage
Cormorant Garamondfont-serifPage titles, card headings, nav logo
Interfont-sansBody copy, descriptions, labels
JetBrains Monofont-monoCategory badges, subtitles, code blocks

Swapping Fonts

  1. Visit fonts.google.com and select your preferred families.
  2. Copy the new @import URL from the “Use on the web” panel.
  3. Replace the existing @import line at the top of assets/main.css.
  4. Update the font-family fallbacks in your Tailwind config (in the source project):
// tailwind.config.js
fontFamily: {
  serif: ['"Playfair Display"', 'serif'],
  sans:  ['"DM Sans"', 'sans-serif'],
  mono:  ['"Fira Code"', 'monospace'],
},

4. Navigation Labels

The top navigation bar items are defined in the navItems (compiled as Zp) array in components/Navigation.js:
const navItems = [
  { path: "/",            label: "Portal",    icon: SparklesIcon },
  { path: "/about",       label: "Chronicle", icon: BookOpenIcon },
  { path: "/projects",    label: "Grimoire",  icon: BookMarkedIcon },
  { path: "/skills",      label: "Affinities",icon: LayoutIcon },
  { path: "/writing",     label: "Tomes",     icon: BookMarkedIcon },
  { path: "/case-studies",label: "Rituals",   icon: FlaskIcon },
  { path: "/contact",     label: "Summon",    icon: SendIcon },
];
Edit the label string for any route to rename how it appears in the nav bar. The path values must stay in sync with the React Router <Route path="..."> declarations in assets/main.js. The icon field references an imported Lucide React component — swap it for any Lucide icon you prefer.

5. Home Page Navigation Cards

The six tarot-card-style tiles on the home page are driven by the cards array in assets/main.js. Each card has a title (large serif text), subtitle (small caps label), a path (route to navigate to), and a roman numeral:
const cards = [
  { id: "about",        title: "The Chronicle",  subtitle: "Origins & Lore",         path: "/about",        roman: "I"   },
  { id: "projects",     title: "The Grimoire",   subtitle: "Summoned Works",          path: "/projects",     roman: "II"  },
  { id: "skills",       title: "Affinities",     subtitle: "Arcane Competencies",     path: "/skills",       roman: "III" },
  { id: "writing",      title: "The Tomes",      subtitle: "Inscribed Thoughts",      path: "/writing",      roman: "IV"  },
  { id: "case-studies", title: "Rituals",         subtitle: "Deep Magic",              path: "/case-studies", roman: "V"   },
  { id: "contact",      title: "The Summoning",  subtitle: "Send a Raven",            path: "/contact",      roman: "VI"  },
];
Customize title and subtitle on any card to fit your personal vocabulary. The roman numeral and path values should remain unchanged unless you are also adding or removing routes.

6. About / Biography Text

The About page (/#/about) is built from three inline JSX section components defined in assets/main.js. Each section contains hardcoded prose that you should replace with your own story:
  • Volume I: The Healing Arts — The origin section. Currently describes a journey from nursing school. Replace the two <p> body paragraphs and the <h2> heading with your own origin story. The italic blockquote on the right side is also editable.
  • The Alchemical Pivot — A list of “ingredients” that catalysed a career change. Replace each { item, desc } object in the data array with your own turning-point moments.
  • Current Phases & Afflictions — Three cards describing current focus areas (“The Builder”, “The Detail Obsessed”, “The Debugger”). Replace the <h3> labels and <p> descriptions in each card with your own current roles or obsessions.
All three sections are standard JSX — find the relevant text strings in assets/main.js and edit them directly.
The contact form is located on the Summoning page (/#/contact) in assets/main.js. The form fields are “True Name” (name), “Scrying Address” (email), and “The Inscription” (message). To wire the form to a real backend, locate the handleSubmit function (the onSubmit handler on the <form> element) and replace the simulated timeout with a real fetch or axios call to your API endpoint or a form service such as Formspree or Resend:
// Find the simulated submission:
setTimeout(() => {
  setStatus("success");
}, 2000);

// Replace with your real submission logic:
const response = await fetch("https://formspree.io/f/your_form_id", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ name, email, message }),
});
if (response.ok) setStatus("success");
The contact form currently has no backend. By default it simulates a 2-second submission delay and then transitions to the success state (“Message Sent into the Aether”) without sending any data anywhere. This is intentional for the template — you must connect it to a real form-handling service (Formspree, EmailJS, Resend, a custom API route, etc.) before deploying to production.

Build docs developers (and LLMs) love