The Skills page translates the traditional skill-percentage chart into a set of tarot cards. Each card holds a category label, a dashed-square icon that spins on hover, the skill name in Cinzel serif italic, and a horizontal progress bar whose fill animates from zero when the card scrolls into the viewport. TheDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/apursley2012/systems-witch/llms.txt
Use this file to discover all available pages before exploring further.
aspect-[2/3] ratio keeps all cards uniformly tall-and-narrow regardless of their content, enforcing the tarot deck metaphor.
Data structure
The six default skills are defined in aconst skills array above the component:
| Skill | Level | Category |
|---|---|---|
| React / Next.js | 90% | Frontend |
| TypeScript | 85% | Core |
| Node.js | 75% | Backend |
| WebGL / Three.js | 60% | Creative |
| Tailwind CSS | 95% | Design |
| System Architecture | 80% | Core |
Grid layout
Cards are arranged in agrid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 — one column on mobile, two from 640px, three from 1024px. Each card uses aspect-[2/3] to maintain its portrait ratio across all breakpoints.
Card anatomy
Each card is amotion.div with a spring entry animation and a full hover effect:
flex flex-col items-center justify-between to anchor three content zones — top, middle, and bottom — to the card extremities.
Top row: index and category
01, 02, …) with String.padStart. The category string is right-aligned.
Center icon: circle and dashed square
- The outer circle border transitions from graphite to acid and gains
shadow-glow(box-shadow: 0 0 10px rgba(164,255,61,.5)) - The inner dashed square border transitions from lilac to acid and rotates 180° over 1000ms
Bottom zone: name and progress bar
The skill name usesfont-serif italic text-xl text-bone, rendering in Cinzel italic — the same typeface used for the Witch part of the home page wordmark. On hover it transitions to text-acid.
The progress bar is a two-layer construction:
h-1 bg-graphite bar. The fill is a motion.div with whileInView — it starts at width: 0 and animates to ${skill.level}% when the card enters the viewport. viewport: { once: true } means the animation fires only on first scroll-into-view, not on every re-entry. The 0.5-second delay lets the card’s own entry animation finish before the bar starts filling.
A font-mono text-xs text-right text-acid readout below the bar shows the numeric percentage.
Hover effects summary
| Element | Default | On group-hover |
|---|---|---|
| Card border | border-graphite | border-acid |
| Card position | in-flow | lifted hover:-translate-y-4 |
| Outer ring border | border-graphite | border-acid + shadow-glow |
| Inner dashed square | border-lilac | border-acid + rotate-180 |
| Skill name | text-bone | text-acid |
The
hover:-translate-y-4 lift and hover:z-10 elevation are both applied directly via Tailwind utility classes, not Framer Motion — this is intentional CSS hover, not an animation. The Framer Motion on the card handles only the scroll-entry animation.Customization
Add or modify a skill: Edit theskills array. Each entry needs three fields:
name— displayed in Cinzel italic; keep it short enough to fit on one linelevel— integer 0–100; drives both the progress bar width and the percentage readoutcategory— displayed top-right in the card header intext-lilac; no validation, any string works
skills array to change the visual sequence. The stagger delay is index-based, so the new first card will always animate in first.
Change animation timing: The progress bar duration: 1 and delay: 0.5 can be adjusted per card by moving the animation config inside the .map() callback. To trigger the bar on mount instead of scroll, replace whileInView with animate.