Glin UI
Primitive / MoleculeAdapted from Magic UIbeta

Terminal

Terminal window with sequenced typing and output, a full transcript and copy.

Edit on GitHub
~/my-app
pnpm dlx glinui init
Detected Next.js and Tailwind CSS
Wrote glinui.json and theme tokens
pnpm dlx glinui add button card
Added 2 components in 1.2s

Installation

Install from
pnpm dlx @glinui/cli@latest add terminal

Credits. Adapted from Magic UI (MIT, (c) Magic UI). Timers and CSS instead of motion, static first render, full transcript for assistive tech, copy button, token colors and glass. Attribution details.

Prefer the package?

Usage

Improvements: no animation library, timers with cleanup and Strict Mode safety, a static first render for SSR and reduced motion, a screen-reader transcript instead of live typing, a built-in copy button, glass variant and tokenised colors.

Engines: this component drives its main animation through the pluggable motion engine. Pass `engine` (css, motion or gsap), wrap a subtree in `MotionEngineProvider`, or set `data-glin-engine` on `<html>`. The css engine is built in and dependency free; motion and gsap are opt-in via `@glinui/motion/register/*`. `motion="none"` renders the final state and `subtle` is opacity only.

Variants

Every vocabulary variant on the default tone. Omit variant to follow the ambient design style.

glinr
$ pnpm add @glinui/ui
solid
$ pnpm add @glinui/ui
plain
$ pnpm add @glinui/ui
soft
$ pnpm add @glinui/ui
outline
$ pnpm add @glinui/ui
ghost
$ pnpm add @glinui/ui
gradient
$ pnpm add @glinui/ui
import { AnimatedSpan, Terminal } from "@glinui/ui"
export function TerminalVariantsDemo() {
return (
<div className="grid gap-4 md:grid-cols-2">
<Terminal variant="glinr" title="glinr" sequence={false} showCopy={false}><AnimatedSpan>$ pnpm add @glinui/ui</AnimatedSpan></Terminal>
<Terminal variant="solid" title="solid" sequence={false} showCopy={false}><AnimatedSpan>$ pnpm add @glinui/ui</AnimatedSpan></Terminal>
<Terminal variant="plain" title="plain" sequence={false} showCopy={false}><AnimatedSpan>$ pnpm add @glinui/ui</AnimatedSpan></Terminal>
<Terminal variant="soft" title="soft" sequence={false} showCopy={false}><AnimatedSpan>$ pnpm add @glinui/ui</AnimatedSpan></Terminal>
<Terminal variant="outline" title="outline" sequence={false} showCopy={false}><AnimatedSpan>$ pnpm add @glinui/ui</AnimatedSpan></Terminal>
<Terminal variant="ghost" title="ghost" sequence={false} showCopy={false}><AnimatedSpan>$ pnpm add @glinui/ui</AnimatedSpan></Terminal>
<Terminal variant="gradient" title="gradient" sequence={false} showCopy={false}><AnimatedSpan>$ pnpm add @glinui/ui</AnimatedSpan></Terminal>
</div>
)
}

No sequence

Every line starts on its own delay.

one
two
<Terminal sequence={false} showCopy={false}>
<AnimatedSpan delay={200}>one</AnimatedSpan>
<AnimatedSpan delay={900}>two</AnimatedSpan>
</Terminal>

Glass (opt-in)

Glass is opt-in and needs a backdrop behind it. Switch the preview stage to Vivid or Photo to see the frosted surface.

build
$ pnpm build
Compiled in 1.2s
<Terminal variant="glass" title="build">...</Terminal>

Engine comparison

The same component on the css, motion and gsap engines. Replay restarts all three.

CSS

pnpm add @glinui/ui
Done in 2s

Motion

pnpm add @glinui/ui
Done in 2s

GSAP

pnpm add @glinui/ui
Done in 2s
// Same component, three engines. motion and gsap are opt-in:
// import "@glinui/motion/register/motion"
// import "@glinui/motion/register/gsap"
<Terminal engine="css" startOnView={false}>
<TypingAnimation>pnpm add @glinui/ui</TypingAnimation>
<AnimatedSpan>Done in 2s</AnimatedSpan>
</Terminal>
<Terminal engine="motion" startOnView={false}>
<TypingAnimation>pnpm add @glinui/ui</TypingAnimation>
<AnimatedSpan>Done in 2s</AnimatedSpan>
</Terminal>
<Terminal engine="gsap" startOnView={false}>
<TypingAnimation>pnpm add @glinui/ui</TypingAnimation>
<AnimatedSpan>Done in 2s</AnimatedSpan>
</Terminal>

Accessibility

  • The first render, SSR and reduced motion all show the full final text.
  • While animating, the visual layer is aria-hidden and a screen-reader-only transcript carries the full text, so nothing is announced character by character.
  • The copy button announces the result through a polite live region.

Keyboard Interactions

KeyAction
TabReaches the copy button; the output area has no extra tab stops.

ARIA Attributes

  • `role=group` with `aria-label`
  • `aria-live=off` on the animated layer
  • Copy status is `role=status`

Reduced Motion

Full motion types commands and fades lines in. Subtle fades lines in without typing. None, or prefers-reduced-motion, renders the full transcript at once. Timers are cleared on unmount.

Affected properties

typingline fadecaret blink

API Reference

Terminal

PropTypeRequiredDefaultDescription
variant"glinr" | "solid" | "plain" | "soft" | "outline" | "ghost" | "gradient" | "glass"-ambient (glinr)Surface variant. Omit to follow the ambient design style (glinr by default, plain for minimal, glass for glass).
tone"neutral" | "accent" | "success" | "warning" | "danger" | "info"-neutralColour tone for the vocabulary variants.
titlestring--Window title, also the default accessible name.
sequenceboolean-trueReveal animated children one after another.
startOnViewboolean-trueWait until the terminal is scrolled into view.
showCopyboolean-trueShow the copy button.
copyValuestring--Copied text. Defaults to the full transcript.
outputClassNamestring--Classes for the scrolling output.
delaynumberNo0Milliseconds to wait after this line's turn before it appears.

AnimatedSpan / TypingAnimation

PropTypeRequiredDefaultDescription
delaynumberNo0Milliseconds to wait after the item's turn.
durationnumber-60TypingAnimation: milliseconds per character.
childrenstring | ReactNode--TypingAnimation needs a string; AnimatedSpan takes any node.

Source

Import directly from the package or browse the source on GitHub. Click any file to view it.

Import

import { Terminal } from "@glinui/ui"

Dependencies

@glinui/ui@glinui/motionclass-variance-authority@phosphor-icons/react

Generated API Snapshot

Beta

Auto-extracted from TypeScript source in packages/ui/src/components/terminal.tsx. This section is in beta and may lag behind hand-curated docs. Regenerate with pnpm --filter @glinui/docs api:generate.

Generated: 2026-10-08T01:41:21.546Z · Full index: /docs/api-metadata

Primary Props Type

TerminalProps

AnimatedSpanProps

PropTypeRequiredDefaultDescription
delaynumberNo0Milliseconds to wait after this line's turn before it appears.

TerminalProps

PropTypeRequiredDefaultDescription
copyValuestringNo-Text copied by the button. Defaults to the full transcript.
engineEngineNameNo-Engine override: `css` (default), `motion`, `gsap` or a registered custom engine.
motionMotionLevelNo-Motion override. `none` renders the static final state, `subtle` is opacity only.
outputClassNamestringNo-Class for the scrolling output area.
sequencebooleanNotrueReveal animated children one after another.
showCopybooleanNotrueShow the copy button.
startOnViewbooleanNotrueWait until the terminal is scrolled into view before playing.
titlestringNo-Window title in the chrome.
toneSurfaceToneNo--
variantSurfaceVariant | "default" | "raised" | "frosted"No-Visual variant. Omit for the ambient design style (glinr: lift shell, raised chrome strip, inset output well; plain: flat bordered terminal).

TypingAnimationProps

PropTypeRequiredDefaultDescription
as"span" | "div" | "p"No"span"-
childrenstringYes-Text to type. Must be a string.
delaynumberNo0Milliseconds to wait after this line's turn before typing starts.
durationnumberNo60Milliseconds per character.