Skip to main content

Documentation Index

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

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

The Skills page transforms a conventional technology list into a gallery of arcane sigils. Each technology is represented by an SVG path — a geometric shape drawn in the style of a magical symbol — that sits dormant until the visitor interacts with it. Hovering a sigil traces the full gold path with a glowing drop-shadow over 1.5 seconds. Clicking the sigil fires twelve gold particle dots outward in a starburst pattern. The tech name and its D&D-inspired magic school label sit beneath each orb.

Overview

The Skills page lives at route /skills and is titled “Cast Spells”. Its opening quote — “Hover to trace the sigil. Click to cast the incantation.” — serves as the interaction manual. The page uses a 2×3 grid layout with six skill entries. Two pieces of state manage the experience: hoveredSkill (a string tracking which sigil is being hovered, used to drive the SVG path animation) and particles (an array of particle objects that self-destruct after 1 second).
The SVG path animation works by animating pathLength from 0 to 1 via Framer Motion’s motion.path. The same path string is drawn twice — once at low opacity as the “ghost” guide, and once as the animated gold trace.

Layout & Components

Sigil orbs (×6)

Each sigil is a 128×128 circular div with a dark background and a subtle gold border. Inside it sits an svg element with two path elements sharing the same d attribute: a static ghost path at rgba(201,169,110,0.1) stroke opacity, and a motion.path that animates pathLength on hover. A drop-shadow filter is applied to the motion.path for the gold glow.

Particle system

On click, twelve motion.div gold dots are created. Each is positioned at the click target’s center (via getBoundingClientRect). They animate radially outward along evenly spaced angles (index / 12 * 2π) to a random distance of 100–150 px, scaling to 0 and fading to opacity: 0 over 0.8 s. They are fixed-position children of a portal-like div with pointer-events-none.

Tech label & school

Below each orb, the technology name (h3) renders in gold serif, and the magic-school label (p) renders in uppercase gray serif. The magic school labels are: Evocation, Abjuration, Conjuration, Illusion, Divination, and Transmutation.

Entrance animation

Each skill group (motion.div) uses initial: { opacity: 0, scale: 0.8 } and animate: { opacity: 1, scale: 1 } with a stagger of index * 0.1 seconds, so sigils appear to materialize in sequence from top-left to bottom-right.

Data Structure

Each skill is a plain object with three fields. The path value is the SVG d attribute string defining the sigil geometry:
const skills = [
  {
    name: "React",
    type: "Evocation",
    path: "M 10 50 Q 50 10 90 50 Q 50 90 10 50 M 50 10 L 50 90 M 10 50 L 90 50",
  },
  {
    name: "TypeScript",
    type: "Abjuration",
    path: "M 20 20 L 80 20 L 50 80 Z M 35 40 L 65 40 M 50 20 L 50 80",
  },
  {
    name: "Node.js",
    type: "Conjuration",
    path: "M 50 10 A 40 40 0 1 1 49.9 10 M 50 30 A 20 20 0 1 1 49.9 30 M 50 10 L 50 30 M 50 70 L 50 90 M 10 50 L 30 50 M 70 50 L 90 50",
  },
  {
    name: "Tailwind",
    type: "Illusion",
    path: "M 20 80 Q 50 20 80 80 Q 50 50 20 80 M 30 60 Q 50 30 70 60",
  },
  {
    name: "GraphQL",
    type: "Divination",
    path: "M 50 10 L 90 30 L 90 70 L 50 90 L 10 70 L 10 30 Z M 50 10 L 50 50 M 90 30 L 50 50 M 90 70 L 50 50 M 50 90 L 50 50 M 10 70 L 50 50 M 10 30 L 50 50",
  },
  {
    name: "Python",
    type: "Transmutation",
    path: "M 30 30 C 30 10 70 10 70 30 C 70 50 30 50 30 70 C 30 90 70 90 70 70 M 40 20 A 2 2 0 1 1 39.9 20 M 60 80 A 2 2 0 1 1 59.9 80",
  },
];

Animations

The motion.path component uses animate to drive pathLength to 1 and opacity to 1 when hoveredSkill === skill.name, and back to 0 and 0 on mouse-leave. The transition is { duration: 1.5, ease: "easeInOut" }. The strokeLinecap: "round" and strokeLinejoin: "round" props ensure smooth endpoints as the path grows.
The animated path uses an inline style of filter: "drop-shadow(0 0 5px rgba(201,169,110,0.8))". This CSS filter applies the glow directly to the SVG stroke rather than to the container box, so the glow follows the exact shape of the sigil.
The click handler computes the container element’s center coordinates using getBoundingClientRect. It then creates 12 particle objects ({ id: Date.now() + index, x: centerX, y: centerY }). Each motion.div starts at (x, y) and animates to (x + cos(angle) * distance, y + sin(angle) * distance) while scaling from 1 to 0. A setTimeout of 1000 ms cleans up the stale particles from state.
The orb container uses group-hover CSS transitions (not Framer Motion) to change its border from border-gold/20 to border-gold/60 and its shadow from shadow-[0_0_15px_rgba(0,0,0,0.5)] to shadow-[0_0_30px_rgba(201,169,110,0.2)] on hover. This gives immediate tactile feedback before the 1.5 s trace completes.

Customization

1

Add a new skill

Push a new object into the skills array with a name, a type (choose any D&D school: Necromancy, Enchantment, Evocation, etc.), and a custom path. Design your sigil path in a viewBox="0 0 100 100" coordinate space. Tools like Inkscape or Figma’s SVG export are useful for sketching paths, but simple geometric shapes drawn by hand with L, M, Q, and A commands work well.
2

Change technology names

Replace the name field with any technology you use. The name renders as the label beneath the orb and is also the key used to match hover state — keep it unique within the array.
3

Reassign magic schools

The type field is purely flavour text. Assign any D&D spell school (or invent your own) to suit the feel of the technology. For example, Rust could be “Abjuration” (protection), CSS could be “Illusion”, and SQL could be “Divination”.
4

Increase particle count

The Array.from({ length: 12 }) in the click handler controls how many particles fire. Increase to 24 for a denser burst. Scale the distance formula accordingly (e.g., 80 + Math.random() * 60) to keep the particles visible within the viewport.
The particle div elements use position: fixed coordinates derived from getBoundingClientRect. If your layout has a sticky header that offsets the main content, adjust the y coordinate by subtracting the header height from the click’s clientY value to keep particles centered on the orb.

Build docs developers (and LLMs) love