The Projects page is the primary portfolio surface. It renders a row of flag-style filter buttons above a two-column Framer Motion grid — when a filter is selected, React re-renders only the matching cards with a staggered entry animation. Cards share the customDocumentation 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.
ascii-border CSS class, which appends +----+ ruled lines above and below each card on md+ viewports, reinforcing the terminal-printout aesthetic.
Data structure
All project data lives in aconst projects array defined above the component in main.js:
| ID | Title | Tags | Description |
|---|---|---|---|
| 01 | NEURAL_WEAVE | --frontend, --webgl | Real-time data visualization for complex network topologies |
| 02 | VOID_PROTOCOL | --backend, --crypto | Decentralized identity management with zero-knowledge proofs |
| 03 | GRIMOIRE.UI | --frontend, --design | React component library with brutalist/occult aesthetics |
| 04 | ECHO_CHAMBER | --creative, --hardware | Generative audio-visual installation via terminal commands |
Filter bar
A separateconst filters array defines the available filter buttons:
useState("--all"). Each button conditionally applies one of two class strings:
The filter values in the
filters array must exactly match the strings in each project’s tags array — they are compared with Array.includes(). The --all filter bypasses this check entirely.Card anatomy
Each card is amotion.div inside a grid grid-cols-1 md:grid-cols-2 gap-8 container:
delay: 0, the second delay: 0.1, and so on. Re-filtering resets the animation because the component key changes with the filtered array.
Ghost ID number
An absolutely-positioned overlay sits atz-0 behind the card content. It is invisible (opacity-0) by default and fades to opacity-100 on group-hover, revealing the large ghost project ID:
rotate-12 and text-acid/20 (20% opacity acid green) give it a watermark quality. select-none prevents text selection on hover.
Card header
The title row usesflex justify-between items-start:
- Left:
<h3>infont-mono text-xl font-bold text-bone, turnstext-acidongroup-hover - Right:
[01]bracket notation infont-mono text-xs text-graphite
Description
A<p> in font-sans text-lilac text-sm with flex-1 fill — grows to push the footer to the card bottom in the flex column layout.
Card footer
Aborder-t border-graphite/50 row at the card bottom using flex items-center justify-between:
- Left: Tag pills — each tag rendered as
<span className="font-mono text-xs text-graphite"> - Right:
[ run.exe ]link infont-mono text-sm text-acid, with a>prefix that isopacity-0untilgroup/btn:hovertargets it toopacity-100
Customization
Add a new project: Append an object to theprojects array with the required fields:
filters array and the tags arrays of any project it should match. For example, adding "--mobile" to filters and ["--frontend", "--mobile"] to a project makes that project appear under both filters.
Link to live projects: Replace the "#" value in each project’s link field with the actual URL. The link renders as an <a href={project.link}> — for external URLs, you may also want to add target="_blank" rel="noopener noreferrer" to the anchor element in the card JSX.