Documentation Index
Fetch the complete documentation index at: https://mintlify.com/apursley2012/digital-coven/llms.txt
Use this file to discover all available pages before exploring further.
ProjectTiles is the display layer for the /projects page (“Grimoire of Repos”). It exports three visually distinct card components — HexagonTile, TerminalTile, and RunestoneTile — each tailored to match the personality of the project it represents. On desktop the cards are absolutely positioned to overlap dramatically; on mobile they stack as a centred column. Every card shares hover-reveal image behaviour and a shared ActionBar with three CTA buttons.
Shared Props Interface
All three tile components accept the same prop set:
interface TileProps {
title: string // Project name shown in the card heading
desc: string // Short description (body copy)
stack: string[] // Array of technology names rendered as badges
note: string // Flavour annotation shown above the title
image: string // URL for the background/overlay image (Unsplash)
className: string // Positioning classes injected by the page layout
}
Project Data
Projects are defined in main.js inside the mt() page function:
const projects = [
{
type: "hexagon",
title: "Soul_Catcher_API",
desc: "Harvests user data with suspicious efficiency.",
stack: ["Node.js", "Express", "MongoDB", "Dark Magic"],
note: "uses sample souls",
image: "https://images.unsplash.com/photo-1550751827-4bd374c3f58b?...",
className: "md:absolute md:top-10 md:left-10",
},
{
type: "terminal",
title: "Void_Protocol",
desc: "A CLI tool that definitely doesn't delete your system32.",
stack: ["Rust", "CLI", "WebAssembly"],
note: "run at own risk",
image: "https://images.unsplash.com/photo-1526374965328-7f61d4dc18c5?...",
className: "md:absolute md:top-40 md:right-10",
},
{
type: "runestone",
title: "Hex_Weaver_UI",
desc: "A component library for modern witches.",
stack: ["React", "Tailwind", "Framer Motion"],
note: "100% accessible",
image: "https://images.unsplash.com/photo-1518770660439-4636190af475?...",
className: "md:absolute md:top-[400px] md:left-1/3",
},
]
Each card is wrapped in a motion.div with a staggered entrance:
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ delay: index * 0.2, type: "spring" }}
className={project.className}
>
{/* tile component */}
</motion.div>
Shared Action Bar
All three tiles include the same ActionBar component (m in source) rendered at the bottom of each card:
const ActionBar = () => (
<div className="flex flex-wrap gap-2 mt-4 relative z-20">
<button className="px-3 py-1 text-xs font-mono border border-neon-lime
text-neon-lime hover:bg-neon-lime hover:text-void transition-colors">
Run Demo
</button>
<button className="px-3 py-1 text-xs font-mono border border-electric-purple
text-electric-purple hover:bg-electric-purple hover:text-void transition-colors">
Source.exe
</button>
<button className="px-3 py-1 text-xs font-mono border border-cyan
text-cyan hover:bg-cyan hover:text-void transition-colors">
Read Scrolls
</button>
</div>
)
All buttons use a fill-on-hover effect: the border/text colour floods the background on hover and the text inverts to --void.
HexagonTile (je / H)
A magenta-accented hexagon cut out via CSS clip-path. The project image fades in on hover as a semi-transparent overlay inside the polygon.
<motion.div
onHoverStart={() => setHovered(true)}
onHoverEnd={() => setHovered(false)}
whileHover={{ scale: 1.05, zIndex: 10 }}
className={`relative w-[400px] h-[450px] flex items-center justify-center ${className}`}
>
<div
className="absolute inset-0 bg-deep-void border border-magenta/50
shadow-[0_0_20px_rgba(255,43,214,0.2)] transition-all duration-500"
style={{ clipPath: "polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%)" }}
>
{/* Image overlay — visible at 40% opacity on hover */}
<div className={`absolute inset-0 transition-opacity duration-700 ${hovered ? "opacity-40" : "opacity-0"}`}>
<img src={image} alt={title} className="w-full h-full object-cover" />
<div className="absolute inset-0 bg-[radial-gradient(circle_at_center,transparent_0%,var(--deep-void)_100%)]" />
</div>
{/* Content layer */}
<div className="absolute inset-0 p-12 flex flex-col justify-center text-center">
<div className="... bg-magenta/20 text-magenta text-[10px] font-mono rounded-full">
{note}
</div>
<h3 className="font-display text-2xl text-magenta mb-2 mt-4">{title}</h3>
<p className="font-mono text-sm text-slate-400 mb-4">{desc}</p>
<div className="flex flex-wrap justify-center gap-2 mb-4">
{stack.map(tech => (
<span className="text-xs text-slate-300 bg-white/5 px-2 py-1 rounded">{tech}</span>
))}
</div>
<ActionBar />
</div>
</div>
</motion.div>
Dimensions: 400 × 450 px
Clip path: Regular hexagon (polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%))
Accent colour: --magenta
TerminalTile (Ne / T)
Styled as a macOS-style terminal window with a title-bar chrome strip, three traffic-light dots, and filename header. The image blends in with mix-blend-luminosity and a cyan tint overlay.
<motion.div
onHoverStart={() => setHovered(true)}
onHoverEnd={() => setHovered(false)}
whileHover={{ scale: 1.05, zIndex: 10 }}
className={`relative w-[450px] h-[350px] bg-[#0a0a0a] border border-cyan/40
rounded-lg overflow-hidden shadow-[0_0_20px_rgba(34,211,238,0.1)] ${className}`}
>
{/* Title bar */}
<div className="bg-[#1a1a1a] border-b border-cyan/20 px-4 py-2 flex items-center gap-2">
<div className="w-3 h-3 rounded-full bg-red-500/80" />
<div className="w-3 h-3 rounded-full bg-yellow-500/80" />
<div className="w-3 h-3 rounded-full bg-green-500/80" />
<span className="ml-4 font-mono text-xs text-cyan">{title}.sh</span>
</div>
{/* Body */}
<div className="relative p-6 h-full">
{/* Image with luminosity blend + cyan overlay */}
<div className={`absolute inset-0 transition-opacity duration-700 ${hovered ? "opacity-30" : "opacity-0"}`}>
<img src={image} alt={title} className="w-full h-full object-cover mix-blend-luminosity" />
<div className="absolute inset-0 bg-cyan/20 mix-blend-overlay" />
</div>
<div className="relative z-10">
<div className="inline-block px-2 py-1 bg-cyan/10 text-cyan text-[10px] font-mono mb-4 border border-cyan/20">
{note}
</div>
<h3 className="font-display text-2xl text-slate-200 mb-2">{title}</h3>
<p className="font-mono text-sm text-slate-400 mb-4">{`> ${desc}`}</p>
<div className="flex flex-wrap gap-2 mb-6">
{stack.map(tech => (
<span className="text-xs text-cyan/70 border border-cyan/30 px-2 py-1">{tech}</span>
))}
</div>
<ActionBar />
</div>
</div>
</motion.div>
Dimensions: 450 × 350 px
Accent colour: --cyan
Stack badge style: border border-cyan/30 text-cyan/70 (no background fill)
RunestoneTile (we / R)
An ancient stone tablet aesthetic with an organic rounded shape, neon-lime accents, and a sepia + hue-rotated green image treatment on hover.
<motion.div
onHoverStart={() => setHovered(true)}
onHoverEnd={() => setHovered(false)}
whileHover={{ scale: 1.05, zIndex: 10 }}
className={`relative w-[350px] h-[450px] bg-slate-900 border-2 border-neon-lime/30
rounded-[40px_10px_40px_10px] overflow-hidden
shadow-[0_0_20px_rgba(163,255,18,0.1)] ${className}`}
>
{/* Sepia + hue-rotate image — 40% opacity on hover */}
<div className={`absolute inset-0 transition-opacity duration-700 ${hovered ? "opacity-40" : "opacity-0"}`}>
<img src={image} alt={title} className="w-full h-full object-cover sepia hue-rotate-90" />
</div>
<div className="relative z-10 p-8 h-full flex flex-col">
{/* Rune glyph */}
<div className="text-neon-lime text-4xl mb-4 text-center font-display">⧋</div>
<div className="self-center px-2 py-1 bg-neon-lime/10 text-neon-lime text-[10px] font-mono rounded mb-4">
{note}
</div>
<h3 className="font-display text-2xl text-neon-lime mb-2 text-center">{title}</h3>
<p className="font-mono text-sm text-slate-400 mb-4 text-center">{desc}</p>
<div className="flex flex-wrap justify-center gap-2 mb-auto">
{stack.map(tech => (
<span className="text-[10px] text-slate-300 uppercase tracking-widest">{tech}</span>
))}
</div>
<div className="mt-4 flex justify-center">
<ActionBar />
</div>
</div>
</motion.div>
Dimensions: 350 × 450 px
Border radius: rounded-[40px_10px_40px_10px] — asymmetric organic corners
Accent colour: --neon-lime
Stack badge style: text-[10px] uppercase tracking-widest (text-only, no border)
Image treatment: sepia hue-rotate-90 for a mossy-green aged look
Hover Image Behaviour (All Tiles)
All three tiles use an identical pattern: a boolean hovered state toggled via onHoverStart/onHoverEnd controls an image overlay’s opacity via a Tailwind conditional class:
// opacity-0 → opacity-40 (HexagonTile, RunestoneTile)
// opacity-0 → opacity-30 (TerminalTile)
className={`absolute inset-0 transition-opacity duration-700 ${hovered ? "opacity-40" : "opacity-0"}`}
The 700 ms transition duration creates a slow, atmospheric fade rather than an abrupt swap.
Page Layout Container
The projects page uses a min-h-[1200px] relative container that allows absolute positioning for desktop but falls back to a centred flex column on mobile:
<div class="relative w-full h-full flex flex-col md:block items-center gap-12 mt-12">
On md: and above, each tile’s className prop supplies absolute position values:
- HexagonTile —
md:absolute md:top-10 md:left-10
- TerminalTile —
md:absolute md:top-40 md:right-10
- RunestoneTile —
md:absolute md:top-[400px] md:left-1/3
The overlapping absolute layout is intentional — tiles are designed to layer over one another on desktop, reinforcing the “grimoire of artefacts” aesthetic. The whileHover: { zIndex: 10 } value elevates the active card above its neighbours.
Color Tokens Summary
| Token | HexagonTile | TerminalTile | RunestoneTile |
|---|
--magenta | ✓ (primary) | — | — |
--cyan | — | ✓ (primary) | — |
--neon-lime | ✓ (ActionBar) | ✓ (ActionBar) | ✓ (primary) |
--electric-purple | ✓ (ActionBar) | ✓ (ActionBar) | ✓ (ActionBar) |
--deep-void | ✓ (fill) | — | — |