Skip to content
Black FridayBlack Friday launch price: Lifetime $149 until 30 Nov. Regular price $299 from 1 Dec.ends in 15d 02:37:30Get Lifetime →
loopkit
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
$
Read the docs
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 Pro

How 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.