UI Components Easy
Terminal UI
Terminal / CLI output display with typed command animation, colored output lines, and macOS window chrome. Pure CSS + JS.
Open in Lab
MCP
vanilla-js css
Targets: JS HTML
Code
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
background: #1a1a2e;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 32px 16px;
gap: 20px;
}
.terminal {
width: 100%;
max-width: 640px;
background: #1e1e2e;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 24px 80px rgba(0, 0, 0, 0.5);
border: 1px solid rgba(255, 255, 255, 0.06);
}
.terminal-bar {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 16px;
background: #2a2a3e;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.dot {
width: 12px;
height: 12px;
border-radius: 50%;
}
.dot--red {
background: #ff5f56;
}
.dot--yellow {
background: #ffbd2e;
}
.dot--green {
background: #27c93f;
}
.terminal-title {
flex: 1;
text-align: center;
font-size: 12px;
color: rgba(255, 255, 255, 0.4);
font-family: "JetBrains Mono", "Fira Code", Menlo, monospace;
}
.terminal-body {
padding: 18px 20px 20px;
min-height: 300px;
max-height: 400px;
overflow-y: auto;
font-family: "JetBrains Mono", "Fira Code", Cascadia, Menlo, monospace;
font-size: 13px;
line-height: 1.75;
}
/* Output lines */
.t-line {
display: flex;
gap: 8px;
}
.t-prompt {
color: #cba6f7;
user-select: none;
}
.t-cmd {
color: #cdd6f4;
}
.t-stdout {
color: #a6e3a1;
}
.t-stderr {
color: #f38ba8;
}
.t-info {
color: #89b4fa;
}
.t-dim {
color: rgba(255, 255, 255, 0.3);
}
/* Cursor blink */
.cursor {
display: inline-block;
width: 8px;
height: 14px;
background: #cba6f7;
vertical-align: middle;
margin-left: 2px;
animation: blink 1s step-end infinite;
}
@keyframes blink {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0;
}
}
/* Controls */
.controls {
display: flex;
gap: 8px;
padding: 0 4px;
}
.ctrl-btn {
padding: 9px 22px;
background: #6366f1;
color: #fff;
border: none;
border-radius: 8px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: opacity 0.15s, transform 0.1s;
letter-spacing: 0.01em;
}
.ctrl-btn:hover {
opacity: 0.85;
transform: translateY(-1px);
}
.ctrl-btn:active {
transform: translateY(0);
}
.ctrl-btn--ghost {
background: rgba(255, 255, 255, 0.08);
color: rgba(255, 255, 255, 0.65);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.ctrl-btn--ghost:hover {
background: rgba(255, 255, 255, 0.13);
color: rgba(255, 255, 255, 0.9);
}const body = document.getElementById("termBody");
const SCRIPT = [
{ type: "cmd", text: "git clone https://github.com/acme/app.git", delay: 600 },
{ type: "stdout", text: "Cloning into 'app'...", delay: 400 },
{ type: "stdout", text: "remote: Enumerating objects: 342, done.", delay: 200 },
{ type: "stdout", text: "remote: Counting objects: 100% (342/342), done.", delay: 200 },
{
type: "stdout",
text: "Receiving objects: 100% (342/342), 1.4 MiB | 8.2 MiB/s, done.",
delay: 300,
},
{ type: "info", text: "", delay: 200 },
{ type: "cmd", text: "cd app && bun install", delay: 500 },
{ type: "stdout", text: "bun install v1.1.0 (bf3ee7a)", delay: 200 },
{ type: "stdout", text: "+ 148 packages installed [1.24s]", delay: 400 },
{ type: "info", text: "", delay: 200 },
{ type: "cmd", text: "bun run build", delay: 500 },
{ type: "info", text: "▶ Building for production...", delay: 300 },
{ type: "info", text: " dist/index.js 42.1 kB │ gzip: 14.2 kB", delay: 300 },
{ type: "info", text: " dist/style.css 8.3 kB │ gzip: 2.1 kB", delay: 200 },
{ type: "stdout", text: "✓ Build completed in 1.34s", delay: 300 },
{ type: "prompt", text: "", delay: 300 }, // final prompt
];
let timeouts = [];
let cursorEl = null;
function appendLine(type, text) {
const line = document.createElement("div");
if (type === "prompt") {
line.className = "t-line";
line.innerHTML = `<span class="t-prompt">❯</span><span class="cursor"></span>`;
cursorEl = line.querySelector(".cursor");
} else if (type === "cmd") {
line.className = "t-line";
line.innerHTML = `<span class="t-prompt">❯</span><span class="t-cmd">${text}</span>`;
} else if (type === "stdout") {
line.className = "t-stdout";
line.textContent = text;
} else if (type === "stderr") {
line.className = "t-stderr";
line.textContent = text;
} else if (type === "info") {
line.className = "t-info";
line.textContent = text;
}
body.appendChild(line);
line.scrollIntoView({ block: "nearest" });
}
function typeCommand(text, callback) {
const line = document.createElement("div");
line.className = "t-line";
const prompt = document.createElement("span");
prompt.className = "t-prompt";
prompt.textContent = "❯ ";
const cmd = document.createElement("span");
cmd.className = "t-cmd";
const cur = document.createElement("span");
cur.className = "cursor";
line.appendChild(prompt);
line.appendChild(cmd);
line.appendChild(cur);
body.appendChild(line);
let i = 0;
function next() {
if (i < text.length) {
cmd.textContent += text[i++];
line.scrollIntoView({ block: "nearest" });
const t = setTimeout(next, 28 + Math.random() * 20);
timeouts.push(t);
} else {
cur.remove();
callback();
}
}
next();
}
function run(index = 0) {
if (index >= SCRIPT.length) return;
const { type, text, delay } = SCRIPT[index];
const t = setTimeout(() => {
if (type === "cmd") {
typeCommand(text, () => run(index + 1));
} else {
appendLine(type, text);
run(index + 1);
}
}, delay);
timeouts.push(t);
}
function clear() {
timeouts.forEach(clearTimeout);
timeouts = [];
body.innerHTML = "";
}
run();
document.getElementById("replayBtn").addEventListener("click", () => {
clear();
run();
});
document.getElementById("clearBtn").addEventListener("click", clear);<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Terminal UI</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="demo">
<div class="terminal" id="terminal">
<div class="terminal-bar">
<span class="dot dot--red"></span>
<span class="dot dot--yellow"></span>
<span class="dot dot--green"></span>
<span class="terminal-title">zsh — ~/projects/myapp</span>
</div>
<div class="terminal-body" id="termBody">
<!-- Lines rendered by JS -->
</div>
</div>
<div class="controls">
<button class="ctrl-btn" id="replayBtn">▶ Replay</button>
<button class="ctrl-btn ctrl-btn--ghost" id="clearBtn">Clear</button>
</div>
</div>
<script src="script.js"></script>
</body>
</html>Terminal window component with macOS-style traffic light chrome, animated command typing, colored stdout/stderr output lines, and a blinking cursor. Great for documentation and landing pages.