proNew209 lines · framer-motion
CTA Terminal
An install command that types itself out. A call-to-action styled as a fake terminal window: traffic-light dots, a monospace command that types out character by character once it scrolls into view, a one-line result once it finishes, then two CTA buttons below the window.
- Use it for
- A developer-tool or CLI landing page section where a believable install command does the convincing better than another headline.
- Themes
- Light + dark
- License
- Commercial use
Live
Get started
Install the toolkit, ship the rest
One command wires up the CLI, the component registry and the local dev server for your invented brand.
terminal
$
tsx
"use client";
import * as React from "react";
import { motion, useReducedMotion } from "framer-motion";
import { cn } from "@/lib/utils";
function useReducedMotionSafe() {
const prefersReduced = useReducedMotion();
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => setMounted(true), []);
return mounted ? !!prefersReduced : false;
}
/**
* A CTA styled as a fake terminal/code window: traffic-light dots, a
* monospace command line that types out character by character once the
* block is in view, a one-line result once typing finishes, then the CTA
* buttons below the window. Typing is driven by a `setInterval` stepping a
* numeric index in state — no `Math.random()`/`Date.now()` in render — and
* is fully paused/cleaned up offscreen, on tab-hide, on unmount, and skipped
* entirely under `prefers-reduced-motion` (which shows the finished state).
*/The rest is in Pro.
Full source, the rebuild prompt and registry install unlock with Pro or Lifetime.
Get ProHow it behaves
- →Typing is driven by a setInterval that increments a numeric index held in state on every tick, one character of the fixed command string at a time — no Math.random()/Date.now() involved, so the sequence is fully deterministic.
- →An IntersectionObserver starts the interval only once the window is in view and stops it the moment it scrolls offscreen; a visibilitychange listener does the same when the tab is hidden; both resume typing from where it left off.
- →Once typing reaches the end of command, the blinking caret stops and the result line fades in below it.
- →The primary button copies command to the clipboard and shows a 'Copied ✓' confirmation for a couple of seconds; onPrimaryClick/onSecondaryClick fire alongside for the host app to wire real navigation.
- →prefers-reduced-motion skips the whole typing/caret animation and renders the command and result fully typed out immediately.
- →typingSpeed scales how many characters are revealed per second.
Accessibility
- ✓The three traffic-light dots are purely decorative `aria-hidden` spans, never native `<select>`/`confirm()`/`alert()` UI.
- ✓The result line is `aria-hidden` while still typing so screen readers don't announce an empty line, and is read normally once revealed.
- ✓Both CTAs are real, keyboard-activatable elements (`<button>` for copy, `<a href="#">` with `preventDefault` for the secondary link) with visible focus rings.
- ✓The blinking caret is a decorative `aria-hidden` element; the command text itself is always present as real text, not an image.
- ✓Honours prefers-reduced-motion.