Skip to main content

Documentation Index

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

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

The Spell Affinities page (ye component) is the portfolio’s interactive skills showcase. It renders a fully SVG-based radar chart with 8 spokes — one per skill — and places a hoverable hexagonal tile at the tip of each spoke. Hovering a tile expands it outward, contracts its neighbors, and reveals the skill name alongside its proficiency level label ("Lvl [N]"). The page title reads “Spell Affinities” (styled with text-glow-purple) and the subtitle instructs visitors: “Hover over the sigils to reveal proficiency levels.”

Skills Data

The full skills array is defined as a constant p at the top of the component module:
const p = [
  { name: "React.js",      level: 90, category: "Frontend"  },
  { name: "TypeScript",    level: 85, category: "Frontend"  },
  { name: "Node.js",       level: 75, category: "Backend"   },
  { name: "CSS/Tailwind",  level: 95, category: "Frontend"  },
  { name: "PostgreSQL",    level: 65, category: "Database"  },
  { name: "GraphQL",       level: 70, category: "API"       },
  { name: "Framer Motion", level: 80, category: "Animation" },
  { name: "Docker",        level: 50, category: "DevOps"    },
];
Each entry has three fields:
FieldTypeDescription
namestringDisplay name shown on hover
levelnumberProficiency percentage (0–100); controls spoke reach
categorystringGrouping label (Frontend, Backend, Database, etc.)
The 8 skills span 6 categories: Frontend (3 entries), Backend, Database, API, Animation, and DevOps.

Radar Chart Geometry

The radar chart is rendered as a single <svg> element. Key geometry constants:
  • Viewport: 600 × 600 viewBox units
  • Center: { x: 300, y: 300 }
  • Max radius: 250 units
  • Spoke count: 8 (one per skill)
  • Angular spacing: 360 / 8 = 45° per spoke
Each spoke angle is computed as:
const angle = index * (360 / 8) * (Math.PI / 180);

Concentric Grid Rings

Four concentric rings are drawn at 25%, 50%, 75%, and 100% of the max radius to provide visual reference:
RingRadiusStroke colorstrokeDasharray
100%250 px#2d1b4enone (solid)
75%187.5 px#1a0b2e4 4 (dashed)
50%125 px#1a0b2e4 4 (dashed)
25%62.5 px#1a0b2e4 4 (dashed)
Only the outermost 100% ring is drawn solid; all three inner rings use a dashed stroke (4 4). The outermost ring also uses a slightly lighter purple (#2d1b4e) while the inner rings use a deeper near-black purple (#1a0b2e), keeping visual emphasis on the boundary and the skill polygon within it.

Skill Polygon

The filled polygon overlaid on the grid traces the actual proficiency of each skill. Its vertex on spoke i is placed at:
const r = maxRadius * (skill.level / 100);
const x = center.x + r * Math.cos(angle);
const y = center.y + r * Math.sin(angle);
Visual properties of the polygon:
fill        = "#a3e635"   // lime-400 — acid green
opacity     = 0.2         // semi-transparent fill
stroke      = "#84cc16"   // lime-500 — slightly deeper green
strokeWidth = 2
// + SVG drop-shadow filter for glow effect
The combination of the semi-transparent lime fill and the glowing stroke creates the signature acid-green “spell sigil” appearance against the dark background.

Hexagonal Skill Tiles

At the tip of each spoke — positioned at the full maxRadius distance from the center regardless of proficiency — sits a hexagonal tile. Each tile is rendered as an SVG <polygon> with a hexagon point arrangement forming a small hex icon.

Hover Animation

Tiles use Framer Motion for their interaction. The animate prop drives three states:
StateTransform
Default (rest)translateR(0px)
Hovered tiletranslateR(+20px) (outward along spoke)
Adjacent tilestranslateR(-10px) (inward, pulled back)
When a tile is hovered, it also reveals a label overlay showing:
  • The skill name (e.g., "React.js")
  • The proficiency tag: "Lvl 90"
The tile-outward / neighbor-inward animation creates a “bloom” effect — only the active skill tile pushes to the foreground while the rest subtly recede, preventing visual clutter on a dense 8-spoke chart.

Adding or Updating a Skill

Each skill entry follows this shape:
{
  name: string,      // Display label shown on hover
  level: number,     // 0–100 proficiency percentage
  category: string,  // Grouping tag (cosmetic only)
}
To update a proficiency, change the level value. The polygon vertex for that spoke will automatically move closer to or farther from the center. To add a new skill, push a new object into the p array:
const p = [
  // ...existing skills
  { name: "Rust", level: 40, category: "Systems" },
];
Skills are evenly spaced at 360 / p.length degrees per spoke. Adding or removing a skill reshuffles every spoke angle — all tile positions and polygon vertices recalculate. If you have an existing screenshot or animation cached, it will no longer match after a count change.
To remove a skill, delete its entry from the array. The remaining skills will redistribute evenly around the full 360°.
The category field is currently cosmetic — it appears in the tile hover label but does not affect chart grouping or coloring. All skills share the same acid-green polygon style regardless of category.

Build docs developers (and LLMs) love