12 CSS Retro UI Designs 08 / 12

ASCII Art Web Interface Code

A live, typeable bash shell with an ASCII logo, ANSI-coloured output and a blinking caret — type real commands (help, ls, neofetch, cowsay, echo, clear) and watch them execute.

CSS + JS MIT licensed
Live Demo Open in tab
Open in playground

The code

<div class="ret-08">
  <div class="ret-08__term">
    <div class="ret-08__bar"><i></i><i></i><i></i><span>guest@codefronts: ~/retro — bash</span></div>
    <div class="ret-08__body" id="ret-08-body">
<pre>   ___          _      ___              _
  / __|___  ___| |___ / __|_ _ ___ _ _| |_ ___
 | (__/ _ \/ _` / -_) _/ '_/ _ \ ' \  _(_-<
  \___\___/\__,_\___\__|_| \___/_||_\__/__/</pre>
      <div class="l"><span class="d">Last login: Tue Jun 03 on ttys001</span></div>
      <div class="l"><span class="d"># Type a command and hit Enter. Try: <span class="y">help</span>, <span class="y">ls</span>, <span class="y">neofetch</span>, <span class="y">cowsay hi</span></span></div>
      <div id="ret-08-out"></div>
      <div class="ret-08__input-line">
        <span class="ret-08__ps1">guest@codefronts<span class="p">:~$</span></span>
        <span class="ret-08__typed" id="ret-08-typed"></span><span class="ret-08__cur"></span>
      </div>
    </div>
  </div>
</div>
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;700&display=swap');

.ret-08, .ret-08 *, .ret-08 *::before, .ret-08 *::after { box-sizing: border-box; margin: 0; padding: 0; }
.ret-08 ::selection{background:#ffaf00;color:#0c0c0c}

.ret-08{
  --bg:#0c0c0c;
  --panel:#161616;
  --fg:#e4e4e4;
  --green:#33d17a;
  --blue:#5c9eff;
  --yellow:#ffaf00;
  --red:#f25f5c;
  --magenta:#c264f0;
  --dim:#6a6a6a;
  font-family:'IBM Plex Mono',monospace;
  background:repeating-linear-gradient(45deg,#0a0a0a 0 12px,#0c0c0c 12px 24px);
  min-height:100vh;display:grid;place-items:center;
  padding:clamp(12px,3vw,40px);color:var(--fg);
}
.ret-08__term{
  width:min(780px,100%);height:min(560px,82vh);display:flex;flex-direction:column;
  background:var(--bg);border-radius:10px;overflow:hidden;
  box-shadow:0 24px 60px rgba(0,0,0,.7),0 0 0 1px rgba(255,255,255,.06);
}
.ret-08__bar{display:flex;align-items:center;gap:8px;padding:10px 14px;background:linear-gradient(180deg,#2a2a2a,#1c1c1c);border-bottom:1px solid #000}
.ret-08__bar i{width:12px;height:12px;border-radius:50%}
.ret-08__bar i:nth-child(1){background:#ff5f56}
.ret-08__bar i:nth-child(2){background:#ffbd2e}
.ret-08__bar i:nth-child(3){background:#27c93f}
.ret-08__bar span{margin-left:10px;font-size:.78rem;color:var(--dim)}
.ret-08__body{flex:1;overflow-y:auto;padding:16px 18px;font-size:clamp(12px,1.5vw,14px);line-height:1.55}
.ret-08__body::-webkit-scrollbar{width:8px}
.ret-08__body::-webkit-scrollbar-thumb{background:#2a2a2a;border-radius:4px}
.ret-08 pre{white-space:pre;font-family:inherit;color:var(--magenta);line-height:1.1;font-size:clamp(8px,1.4vw,12px);margin-bottom:8px}
.ret-08 .l{white-space:pre-wrap;word-break:break-word}
.ret-08 .g{color:var(--green)}
.ret-08 .b{color:var(--blue)}
.ret-08 .y{color:var(--yellow)}
.ret-08 .r{color:var(--red)}
.ret-08 .m{color:var(--magenta)}
.ret-08 .d{color:var(--dim)}
.ret-08__prompt-l{color:var(--green);font-weight:700}
.ret-08__prompt-l .p{color:var(--blue)}
.ret-08__input-line{display:flex;align-items:baseline;gap:8px;margin-top:4px}
.ret-08__ps1{color:var(--green);font-weight:700;white-space:nowrap}
.ret-08__ps1 .p{color:var(--blue)}
.ret-08__typed{color:var(--fg)}
.ret-08__cur{display:inline-block;width:8px;height:1.05em;background:var(--fg);transform:translateY(2px);animation:ret-08-bl 1.1s steps(2) infinite}
@keyframes ret-08-bl{50%{opacity:0}}
.ret-08__hint{margin-top:10px;color:var(--dim);font-size:.74rem}
.ret-08 a{color:var(--blue);text-decoration:underline}
@media (prefers-reduced-motion: reduce){.ret-08__cur{animation:none}}
(() => {
  const root = document.querySelector('.ret-08');
  if (!root) return;
  const body = root.querySelector('#ret-08-body');
  const out  = root.querySelector('#ret-08-out');
  const typedEl = root.querySelector('#ret-08-typed');
  let buf = '';
  const esc = s => s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');

  const cmds = {
    help: () => `<span class="g">Available:</span> help · ls · pwd · whoami · date · neofetch · cowsay &lt;text&gt; · echo &lt;text&gt; · clear`,
    ls: () => `<span class="b">projects/</span>  <span class="b">art/</span>  readme.md  <span class="g">retro.sh</span>  .secret`,
    pwd: () => `/home/guest/retro`,
    whoami: () => `guest <span class="d">(uid=1000 groups=demos,retro)</span>`,
    date: () => new Date().toString(),
    neofetch: () => `<span class="m">  ___   </span> <span class="g">guest</span>@<span class="g">codefronts</span>
<span class="m"> / _ \\  </span> <span class="y">OS:</span>    RetroLinux 8.6 "Phosphor"
<span class="m">| | | | </span> <span class="y">Shell:</span> bash 5.1 · pure CSS+JS
<span class="m">| |_| | </span> <span class="y">Term:</span>  codefronts-term
<span class="m"> \\___/  </span> <span class="y">Theme:</span> ANSI green-on-black`,
    clear: () => { out.innerHTML=''; return null; }
  };

  function run(raw){
    const line = `<div class="l"><span class="ret-08__ps1">guest@codefronts<span class="p">:~$</span></span> ${esc(raw)}</div>`;
    let res = '';
    const trimmed = raw.trim();
    const [c, ...rest] = trimmed.split(/\s+/);
    const arg = rest.join(' ');
    if (trimmed === '') { res=''; }
    else if (c === 'echo') res = esc(arg);
    else if (c === 'cowsay') {
      const t = esc(arg || 'moo');
      const bar = '_'.repeat(t.length + 2);
      const bar2 = '-'.repeat(t.length + 2);
      res = `<span class="y"> ${bar}\n&lt; ${t} &gt;\n ${bar2}\n        \\   ^__^\n         \\  (oo)\\_______\n            (__)\\       )\\/\\\n                ||----w |\n                ||     ||</span>`;
    }
    else if (cmds[c]) { const r = cmds[c](); if (r===null){return;} res = r; }
    else res = `<span class="r">bash: ${esc(c)}: command not found</span> <span class="d">(try 'help')</span>`;
    out.insertAdjacentHTML('beforeend', line + (res!=='' ? `<div class="l">${res}</div>` : ''));
    body.scrollTop = body.scrollHeight;
  }

  window.addEventListener('keydown', (e) => {
    if (e.metaKey || e.ctrlKey || e.altKey) return;
    if (e.key === 'Enter') { run(buf); buf=''; }
    else if (e.key === 'Backspace') { buf = buf.slice(0,-1); e.preventDefault(); }
    else if (e.key.length === 1) { buf += e.key; }
    else return;
    typedEl.textContent = buf;
  });
  // seed one example
  run('neofetch');
})();

Search CodeFronts

Loading…