Skip to main content

Documentation Index

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

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

The Arcana page lives at route /skills and is rendered by the SkillsPage component (minified export _e). It presents six technical skill areas as an orbital wheel of glowing buttons surrounding a central information panel. Clicking any skill orb replaces the center panel content with a description of that school, animated with Framer Motion’s AnimatePresence. The page opens with a centered header block:
  • Title"Arcana & Expertise" in Cormorant Unicase (font-heading text-5xl)
  • Subtitle"Schools of Magic Mastered" in JetBrains Mono (font-code text-sm text-witch-turquoise/70)
No entrance animation is applied to the header. It renders immediately at full opacity.

The Orbital Wheel

The wheel lives inside a relative w-full max-w-3xl flex items-center justify-center h-[500px] container. All positioning inside it is absolute, so the outer ring, skill buttons, and center panel stack on top of each other at the center point.

Rotating Outer Ring

A motion.div sized at exactly 400×400px (w-[400px] h-[400px]) rotates continuously from 0 → 360° over 60 seconds on a linear infinite loop. It has a border-2 border-witch-plum/20 rounded-full frame and contains an SVG hexagram:
// SVG hexagram inside the rotating ring
<svg viewBox="0 0 100 100" stroke="currentColor" strokeWidth="0.5" fill="none">
  {/* Outer hexagon */}
  <polygon points="50,5 89,27.5 89,72.5 50,95 11,72.5 11,27.5" />
  {/* Star of David upward triangle */}
  <polygon points="50,5 89,72.5 11,72.5" />
  {/* Star of David downward triangle */}
  <polygon points="50,95 89,27.5 11,27.5" />
</svg>
The SVG uses text-witch-plum/10 for its stroke color, making it a very subtle watermark behind the skill buttons.

Skill Button Placement

Each of the six skill buttons is positioned by computing its x and y offset from center using trigonometry. The orbital radius is 200px. Starting at the top (-90° offset), buttons are evenly distributed at 60° intervals:
const angle = (index * (360 / 6) - 90) * (Math.PI / 180);
const radius = 200;
const x = Math.cos(angle) * radius;
const y = Math.sin(angle) * radius;
Each button is a motion.button with:
  • style={{ x, y }} for Framer Motion’s transform-based positioning
  • whileHover={{ scale: 1.1 }} and whileTap={{ scale: 0.95 }}
  • A 64×64px circle (w-16 h-16 rounded-full) with a text-2xl emoji icon inside
  • Active state: border-witch-amber bg-witch-amber/10 shadow-[0_0_20px_rgba(245,158,11,0.4)]
  • Inactive state: border-witch-turquoise/30 bg-witch-dark hover:border-witch-turquoise

The Six Skill Schools

The skillSchools array is defined as a module-level constant (minified as L):
const skillSchools = [
  {
    id: "frontend",
    label: "Illusion",
    icon: "👁️",
    desc: "The art of shaping what mortals see. React, Vue, CSS, Framer Motion.",
  },
  {
    id: "backend",
    label: "Conjuration",
    icon: "⚡",
    desc: "Summoning data from the void. Node.js, Python, PostgreSQL, Redis.",
  },
  {
    id: "devops",
    label: "Warding",
    icon: "🛡️",
    desc: "Protecting the sanctum. Docker, AWS, CI/CD, Kubernetes.",
  },
  {
    id: "design",
    label: "Enchantment",
    icon: "✨",
    desc: "Weaving aesthetics to captivate the mind. Figma, UI/UX, Accessibility.",
  },
  {
    id: "testing",
    label: "Divination",
    icon: "🔮",
    desc: "Foreseeing and preventing disaster. Jest, Cypress, Playwright.",
  },
  {
    id: "architecture",
    label: "Alchemy",
    icon: "⚗️",
    desc: "Transmuting complex requirements into elegant systems. System Design, Microservices.",
  },
];

👁️ Illusion

Frontend craft. React, Vue, CSS animations, and Framer Motion. The school of shaping perception.

⚡ Conjuration

Backend summoning. Node.js, Python, PostgreSQL, Redis. Data pulled from the void on demand.

🛡️ Warding

DevOps protection. Docker, AWS, CI/CD pipelines, Kubernetes. Guarding the sanctum from chaos.

✨ Enchantment

Design and UX. Figma, accessibility practices, UI polish. Making interfaces captivating.

🔮 Divination

Testing and QA. Jest, Cypress, Playwright. Foreseeing failure before it reaches production.

⚗️ Alchemy

Architecture. System design, microservices, and the transmutation of complex requirements.

Center Panel

The center panel is a 256×256px circle (w-64 h-64 rounded-full) pinned at absolute z-10 in the middle of the wheel. It uses .box-glow-plum, bg-witch-dark/80 backdrop-blur-sm, and border-witch-turquoise/20. The panel’s content is wrapped in <AnimatePresence mode="wait">, which ensures the outgoing content fully fades out before the incoming content fades in — no overlap occurs:
<AnimatePresence mode="wait">
  <motion.div
    key={activeSkill.id}       // key change triggers exit → enter
    initial={{ opacity: 0, scale: 0.8 }}
    animate={{ opacity: 1, scale: 1 }}
    exit={{ opacity: 0, scale: 0.8 }}
    transition={{ duration: 0.3 }}
  >
    <h3 className="font-heading text-2xl text-witch-amber mb-2">
      {activeSkill.label}
    </h3>
    <p className="text-sm text-witch-moonlight/80 font-body leading-relaxed">
      {activeSkill.desc}
    </p>
  </motion.div>
</AnimatePresence>
The initial active skill on mount is skillSchools[0] (Illusion / frontend), set via useState(skillSchools[0]).
The key prop on the inner motion.div is set to activeSkill.id. Changing the key forces React to unmount the old element and mount a new one, which is what triggers AnimatePresence to run both the exit animation on the old and the initial → animate animation on the new.

Customization

1

Add a new skill school

Append a new object to the skillSchools array. The wheel computes button positions dynamically from array length, so adding a seventh item changes the angular step from 60° to approximately 51.4° automatically:
{
  id: "mobile",
  label: "Astral Projection",
  icon: "📱",
  desc: "Building cross-platform mobile experiences. React Native, Expo, Swift.",
},
Beyond 8 skill schools the orbital buttons begin to overlap at a 200px radius. Either reduce the number of schools or increase the radius constant from 200 to 240 or higher.
2

Adjust the orbital radius

In SkillsPage, find the line const radius = 200 (or const a = 200 in the minified build) inside the .map() that renders skill buttons. Increase this value to push buttons further from center, or decrease it to pull them closer. The outer ring is fixed at 400×400px (200px radius from center) — the default 200 intentionally places button centers right at the ring border. Increase radius to 220 or higher to move buttons outside the ring, or decrease it to nest them inside.
3

Change the center panel default

useState(skillSchools[0]) initializes Illusion as the active skill. Change skillSchools[0] to any other array item (e.g. skillSchools[4] for Divination) to start on a different school.
4

Adjust transition speed

The center panel fade is duration: 0.3. Increase this for a slower, more dramatic reveal. The outer ring’s rotation speed is duration: 60 on its transition — lower this number for a faster spin.

Build docs developers (and LLMs) love