Skip to main content

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

TokenHexagonTileTerminalTileRunestoneTile
--magenta✓ (primary)
--cyan✓ (primary)
--neon-lime✓ (ActionBar)✓ (ActionBar)✓ (primary)
--electric-purple✓ (ActionBar)✓ (ActionBar)✓ (ActionBar)
--deep-void✓ (fill)

Build docs developers (and LLMs) love