Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/apursley2012/dev-mode/llms.txt

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

dev-mode layers two animation systems on top of each other to sell the retro-arcade illusion: a set of seven CSS keyframe utilities (registered as Tailwind classes) that handle ambient CRT effects and looping UI polish, and Framer Motion’s AnimatePresence which drives smooth page-to-page transitions. This page documents every animation class, its duration, and exactly where and how to modify or disable it.

CSS Keyframe Animations

All custom keyframes are defined in src/assets/main.css and exposed as animate-* Tailwind utilities. Two of the seven (animate-pulse, animate-bounce) are Tailwind built-ins that dev-mode reuses without modification; the remaining five are fully custom.
ClassKeyframeDurationEffectWhere used
animate-flickerflicker4s infiniteSubtle opacity oscillation (0.95 → 0.85 → 1) simulating CRT tube instabilityHero section overlay, logo
animate-scanlinescanline-scroll8s linear infiniteA semi-transparent band sweeps from translateY(-100%) to translateY(100%)ScanlineOverlay component
animate-marqueemarquee20s linear infiniteTranslates content from 100% to -100% for a horizontal ticker scrollTech-stack ticker bar
animate-color-cyclecolor-cycle6s infiniteCycles text color through lime → magenta → cyan → lime with matching glow shadowsAnimated accent headings
animate-blinkblink1.5s infiniteOpacity 1 for first half, 0 for second half — hard cursor blinkTerminal cursor _ character
animate-pulseTailwind built-in2sOpacity drops to 0.5 at 50% of each cycleAvailability badge pulse dot
animate-bounceTailwind built-in1sVertical translate bounce loopEmoji/sprite decorations

animate-flicker

The flicker animation mimics the imperceptible brightness variation of a CRT phosphor. It runs indefinitely at low intensity so it stays subliminal — the viewer feels the screen is “alive” without consciously noticing the flicker.
@keyframes flicker {
  0%   { opacity: 0.95; }
  5%   { opacity: 0.85; }
  10%  { opacity: 0.95; }
  15%  { opacity: 1;    }
  100% { opacity: 1;    }
}
<div className="animate-flicker">
  {/* Hero content */}
</div>
To slow down or intensify the effect, change the utility’s duration in tailwind.config.js:
// tailwind.config.js
animation: {
  flicker: 'flicker 8s infinite', // slower = more subtle
}

animate-scanline

A single translucent horizontal band moves top-to-bottom across the screen on an 8-second linear loop. It is rendered inside the ScanlineOverlay component (see CRT Overlay below) as a separate layer, so it never interferes with pointer events on the content beneath it.
@keyframes scanline-scroll {
  from { transform: translateY(-100%); }
  to   { transform: translateY(100%); }
}
{/* Moving scanline band */}
<div className="pointer-events-none fixed inset-0 animate-scanline opacity-10 bg-white/5" />

animate-marquee

The marquee animation scrolls a continuous row of tech-stack badges from right to left across the full viewport width. Items are duplicated in the DOM so the loop appears seamless when the first copy exits the left edge.
@keyframes marquee {
  from { transform: translate(100%);  }
  to   { transform: translate(-100%); }
}
<div className="overflow-hidden whitespace-nowrap">
  <span className="inline-block animate-marquee font-mono text-arcade-cyan">
    React &nbsp;·&nbsp; TypeScript &nbsp;·&nbsp; Node.js &nbsp;·&nbsp;
  </span>
</div>

animate-color-cycle

This animation cycles a heading’s color and glow shadow through the three neon accents in sequence, creating an eye-catching rainbow-neon shimmer used sparingly on hero text.
@keyframes color-cycle {
  0%   { color: var(--neon-lime); text-shadow: 0 0 10px var(--neon-lime); }
  33%  { color: var(--magenta);   text-shadow: 0 0 10px var(--magenta);   }
  66%  { color: var(--cyan);      text-shadow: 0 0 10px var(--cyan);      }
  100% { color: var(--neon-lime); text-shadow: 0 0 10px var(--neon-lime); }
}
<h2 className="font-pixel text-4xl animate-color-cycle">
  COMBO UNLOCKED
</h2>
A hard on/off blink at 1.5-second intervals, identical to the cursor blink rate of a classic terminal. Used exclusively on the _ cursor character appended to typewriter-style text.
@keyframes blink {
  0%, 49%  { opacity: 1; }
  50%, 100% { opacity: 0; }
}
<span className="font-pixel text-arcade-lime">
  &gt; {typedText}<span className="animate-blink">_</span>
</span>

Framer Motion Page Transitions

Between every route change, dev-mode wraps the outgoing and incoming page in a Framer Motion AnimatePresence with mode="wait". The "wait" mode ensures the exit animation of the old page completes fully before the enter animation of the new page begins — this prevents two pages from being visible simultaneously and gives each transition a clean, discrete feel.

Transition Config

import { AnimatePresence, motion } from 'framer-motion'

function PageWrapper({ children }) {
  return (
    <AnimatePresence mode="wait">
      <motion.div
        key={routeKey}
        initial={{ opacity: 0, scale: 0.98, filter: 'blur(10px)' }}
        animate={{ opacity: 1, scale: 1,    filter: 'blur(0px)'  }}
        exit={{    opacity: 0, scale: 1.02, filter: 'blur(10px)' }}
        transition={{ duration: 0.3, ease: 'easeInOut' }}
      >
        {children}
      </motion.div>
    </AnimatePresence>
  )
}
The three-property tween (opacity, scale, filter) creates a zoom-and-defocus effect:
  • Enter — the new page fades in, scales up from 98%, and snaps into focus from a 10 px blur.
  • Exit — the old page fades out while slightly over-scaling to 102% and blurring out, giving the impression the content is receding into the screen.

Adjusting Duration

Change the duration value (in seconds) on the transition prop:
transition={{ duration: 0.15, ease: 'easeInOut' }} // snappier
transition={{ duration: 0.6,  ease: 'easeInOut' }} // more cinematic

Disabling Transitions

To remove Framer Motion transitions entirely while keeping the component tree intact, replace the motion.div with a plain div and drop the AnimatePresence wrapper:
function PageWrapper({ children }) {
  return <div>{children}</div>
}

CRT Overlay — ScanlineOverlay

The ambient CRT effect seen across every page is rendered by a ScanlineOverlay component that stacks three distinct layers, each assigned pointer-events-none and fixed inset-0 so they never capture clicks:
LayerCSS classEffect
Static scanlinesscanlinesRepeating 4 px horizontal stripe pattern with 50% dark rows
Moving bandanimate-scanlineSingle translucent band sweeping top-to-bottom every 8 s
Edge vignettecrt-vignetteinset 0 0 100px rgba(0,0,0,0.9) box-shadow darkens all four screen edges
/* Static scanline grid */
.scanlines {
  background: linear-gradient(
    to bottom,
    transparent,
    transparent 50%,
    rgba(0, 0, 0, 0.3) 50%,
    rgba(0, 0, 0, 0.3)
  );
  background-size: 100% 4px;
}

/* Dark edge vignette */
.crt-vignette {
  box-shadow: inset 0 0 100px rgba(0, 0, 0, 0.9);
}
function ScanlineOverlay() {
  return (
    <>
      {/* 1 — static scanline grid */}
      <div className="pointer-events-none fixed inset-0 scanlines opacity-20 z-50" />

      {/* 2 — moving scanline band */}
      <div className="pointer-events-none fixed inset-0 animate-scanline bg-white/5 z-50" />

      {/* 3 — CRT edge vignette */}
      <div className="pointer-events-none fixed inset-0 crt-vignette z-50" />
    </>
  )
}
To reduce the intensity of the CRT effect, lower the opacity on the scanlines layer or increase the background-size to spread the lines further apart:
.scanlines {
  background-size: 100% 8px; /* wider gaps = subtler effect */
}

Accessibility — reducing motion. Users who have enabled the “Reduce Motion” system preference expect animations to be toned down or disabled. Wrap all non-essential CSS animations in a prefers-reduced-motion media query, and use Framer Motion’s useReducedMotion() hook to conditionally skip page transitions:
@media (prefers-reduced-motion: reduce) {
  .animate-flicker,
  .animate-scanline,
  .animate-marquee,
  .animate-color-cycle,
  .animate-blink {
    animation: none;
  }
}
import { useReducedMotion } from 'framer-motion'

function PageWrapper({ children }) {
  const reduceMotion = useReducedMotion()

  return (
    <AnimatePresence mode="wait">
      <motion.div
        initial={reduceMotion ? false : { opacity: 0, scale: 0.98, filter: 'blur(10px)' }}
        animate={{ opacity: 1, scale: 1, filter: 'blur(0px)' }}
        exit={reduceMotion ? {} : { opacity: 0, scale: 1.02, filter: 'blur(10px)' }}
        transition={{ duration: reduceMotion ? 0 : 0.3, ease: 'easeInOut' }}
      >
        {children}
      </motion.div>
    </AnimatePresence>
  )
}

Build docs developers (and LLMs) love