Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/apursley2012/nightshade/llms.txt

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

The Journal section spans two routes: a listing grid at /blog where each post is a hoverable parchment card, and a full-page reader at /blog/:slug featuring a live scroll-progress bar and drop-cap opening paragraph. BlogPage (Ge) handles the listing; BlogPostPage (Xe) handles the reader.

Routes

RouteComponent
/blogBlogPage (Ge)
/blog/:slugBlogPostPage (Xe)

Listing Page — /blog

Layout

The listing page renders under a max-w-4xl mx-auto container. The header block displays “The Coven Journal” in font-heading and the subtitle “Papers, Essays, and Field Notes.” in font-code. Beneath it, a two-column md:grid-cols-2 grid holds the post cards, with staggered entrance animations (delay: index * 0.1).

Post Card

Each card is a motion.div with parchment-bg border border-witch-plum/30 rounded-sm. On hover the border changes to border-witch-turquoise and a small Candle component fades in at the top-right corner (opacity-0 → opacity-100). Clicking the card navigates to /blog/:slug via useNavigate. Card content from top to bottom:
  1. Meta rowfont-code text-xs bar with the category label (left, text-witch-plum uppercase tracking-widest) and date (right, text-witch-moonlight/40).
  2. Titlefont-heading text-2xl that transitions to text-witch-amber on hover.
  3. “Read entry →” footer — a small font-code text-xs text-witch-turquoise/50 line whose arrow nudges right (translate-x-2) on hover.

Post Data

const blogPosts = [
  {
    slug: "anatomy-of-a-hook",
    title: "The Anatomy of a Custom Hook",
    category: "Paper",
    date: "Oct 31, 2023",
  },
  {
    slug: "css-grid-rituals",
    title: "CSS Grid Rituals for Complex Layouts",
    category: "Field Notes",
    date: "Sep 15, 2023",
  },
  {
    slug: "ethics-of-ai",
    title: "The Ethics of Summoning AI in Production",
    category: "Essay",
    date: "Aug 02, 2023",
  },
];

Post Reader — /blog/:slug

Scroll Progress Bar

A motion.div is fixed to the very top of the viewport for the duration of the article. The component uses a locally-bundled useScroll implementation (qe) that tracks scrollYProgress, and a custom useSpring (le) imported from FamiliarCursor.js — both mirror the Framer Motion API exactly:
const { scrollYProgress } = useScroll();
const scaleX = useSpring(scrollYProgress, {
  stiffness: 100,
  damping: 30,
  restDelta: 0.001,
});
The bar is styled fixed top-0 left-0 right-0 h-1 bg-witch-turquoise/50 blur-[1px] origin-left z-50 and driven by style={{ scaleX }}. As the reader scrolls down the page, the bar extends left-to-right from scaleX: 0 to scaleX: 1.

Title Derivation

The post title is derived from the URL slug at render time, following the same pattern as the case study detail page:
const title = slug
  .split("-")
  .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
  .join(" ");

Article Header

Centered above the article body, inside a border-b border-witch-plum/20 pb-8 divider:
  • h1 in font-heading text-4xl text-witch-moonlight
  • A font-code text-witch-plum text-sm byline: "A Field Note • Oct 31, 2023"

Drop-Cap Opening Paragraph

The first paragraph uses Tailwind’s first-letter pseudo-element utilities to create a large decorative initial:
first-letter:float-left
first-letter:text-6xl
first-letter:pr-4
first-letter:font-heading
first-letter:text-witch-amber
first-letter:leading-[0.8]
first-line:uppercase
first-line:tracking-widest
This floats the first character left at 6xl size in the amber accent color, with the first full line rendered in uppercase small-caps.

Article Prose

The article body is wrapped in prose prose-invert prose-lg max-w-none font-body text-witch-moonlight/80. Subsequent headings render in font-heading text-witch-turquoise; blockquotes use a left border of border-witch-plum with italic text-witch-moonlight/60 text.

Back Navigation

A ← Close Journal button above the article header calls useNavigate to return to /blog:
<button onClick={() => navigate("/blog")}>
  ← Close Journal
</button>

Customization

Adding a New Blog Post

Step 1 — Append an entry to blogPosts:
{
  slug: "the-art-of-memoization",
  title: "The Art of Memoization",
  category: "Paper",
  date: "Nov 15, 2023",
},
Step 2 — The reader page renders automatically. Like the case study detail, BlogPostPage derives its content from the slug. The body prose is currently static placeholder text inside the component. To show per-post content, add a content field to each entry or fetch it from an external source (MDX file, CMS, etc.) keyed on the slug.
The category label renders as plain text — it is not filtered or linked anywhere. You can use any short string ("Tutorial", "Deep Dive", "Rant") without breaking the layout.

Build docs developers (and LLMs) love