Building a Cyberpunk Glitch UI with CSS & JS (Source Code)

Published: (January 1, 2026 at 11:49 AM EST)
7 min read
Source: Dev.to

Source: Dev.to

There is something undeniably cool about the Cyberpunk aesthetic. The neon lights and high‑tech vibe scream future. In this tutorial we’ll build a Cyberpunk Glitch Card that feels like it was ripped straight out of a sci‑fi game UI.

The result? A 3‑D interactive profile card that reacts to mouse movement and features a jagged, glitching text animation.

Why build a Cyberpunk Glitch Card?

This isn’t just a static image. Here’s what makes the card special:

  • Holographic Glitch Text – CSS keyframes + clip give the text a malfunctioning look.
  • 3‑D Tilt Effect – The card follows the cursor in 3‑D space.
  • Angled UIclip‑path cuts the corners for a sharp, industrial sci‑fi feel.

Demo →

The Source Code

You can copy‑paste the code below to use this card in your own portfolio or project.

HTML Structure

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Cyberpunk Glitch Card</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="container">
        <div class="card" id="cyber-card">
            <div class="glitch-wrapper">
                <img class="avatar" src="https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?q=80&w=1000" alt="Cyber Avatar">
            </div>

            <h2 class="cyber-title" data-text="ALEX_MERCER">ALEX_MERCER</h2>
            <p class="cyber-role">SYSTEM_ARCHITECT // LVL.99</p>

            <div class="stats">
                <div class="stat-box">
                    <span class="label">SPEED</span>
                    <span class="value">98%</span>
                </div>
                <div class="stat-box">
                    <span class="label">HACK</span>
                    <span class="value">100%</span>
                </div>
                <div class="stat-box">
                    <span class="label">STEALTH</span>
                    <span class="value">85%</span>
                </div>
            </div>

            <button class="cyber-btn">INITIALIZE **</button>

            <!-- Decorative corners -->
            <div class="corner top-left"></div>
            <div class="corner top-right"></div>
            <div class="corner bottom-left"></div>
            <div class="corner bottom-right"></div>
        </div>
    </div>
</body>
</html>

CSS (The Magic)

@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Share+Tech+Mono&display=swap');

:root {
    --primary:   #00f3ff; /* Cyan Neon */
    --secondary: #ff0055; /* Pink Neon */
    --bg:        #050505;
    --card-bg:   #111;
}

/* Reset & basic layout */
* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}
body {
    background-color: var(--bg);
    background-image:
        linear-gradient(rgba(0, 243, 255, 0.03) 1px, transparent 1px),
        linear-gradient(90deg, rgba(0, 243, 255, 0.03) 1px, transparent 1px);
    background-size: 30px 30px;
    height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    font-family: 'Share Tech Mono', monospace;
    overflow: hidden;
}
.container {
    perspective: 1000px;
}

/* Card */
.card {
    width: 350px;
    padding: 40px 30px;
    background: var(--card-bg);
    border: 1px solid rgba(0, 243, 255, 0.2);
    position: relative;
    transform-style: preserve-3d;
    cursor: pointer;
    box-shadow: 0 0 20px rgba(0, 243, 255, 0.1);
    clip-path: polygon(
        0 0,
        100% 0,
        100% 85%,
        90% 100%,
        0 100%
    );
}

/* Avatar & glitch wrapper */
.glitch-wrapper {
    width: 120px;
    height: 120px;
    margin: 0 auto 20px;
    position: relative;
}
.avatar {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border: 2px solid var(--primary);
    filter: grayscale(100%);
    transition: .3s;
}
.card:hover .avatar {
    filter: grayscale(0%);
    border-color: var(--secondary);
}

/* Typography */
.cyber-title {
    font-family: 'Orbitron', sans-serif;
    color: #fff;
    text-align: center;
    font-size: 1.8rem;
    margin-bottom: 5px;
    position: relative;
    letter-spacing: 2px;
}

/* Glitch text effect */
.cyber-title::before,
.cyber-title::after {
    content: attr(data-text);
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: var(--card-bg);
}
.cyber-title::before {
    left: 2px;
    text-shadow: -1px 0 var(--secondary);
    clip: rect(0, 900px, 0, 0);
    animation: glitch-anim 2s infinite linear alternate-reverse;
}
.cyber-title::after {
    left: -2px;
    text-shadow: 1px 0 var(--primary);
    clip: rect(0, 900px, 0, 0);
    animation: glitch-anim 2s infinite linear alternate-reverse;
}

/* Glitch animation */
@keyframes glitch-anim {
    0%   { clip: rect(42px, 9999px, 44px, 0); }
    10%  { clip: rect(12px, 9999px, 14px, 0); }
    20%  { clip: rect(62px, 9999px, 64px, 0); }
    30%  { clip: rect(22px, 9999px, 24px, 0); }
    40%  { clip: rect(72px, 9999px, 74px, 0); }
    50%  { clip: rect(32px, 9999px, 34px, 0); }
    60%  { clip: rect(82px, 9999px, 84px, 0); }
    70%  { clip: rect(42px, 9999px, 44px, 0); }
    80%  { clip: rect(12px, 9999px, 14px, 0); }
    90%  { clip: rect(62px, 9999px, 64px, 0); }
    100% { clip: rect(22px, 9999px, 24px, 0); }
}

/* Role text */
.cyber-role {
    text-align: center;
    color: var(--primary);
    font-size: .9rem;
    margin-bottom: 20px;
}

/* Stats */
.stats {
    display: flex;
    justify-content: space-between;
    margin-bottom: 20px;
}
.stat-box {
    text-align: center;
}
.stat-box .label {
    display: block;
    font-size: .7rem;
    color: #777;
}
.stat-box .value {
    font-size: 1.1rem;
    color: #fff;
}

/* Button */
.cyber-btn {
    width: 100%;
    padding: 10px;
    background: var(--primary);
    border: none;
    color: #000;
    font-weight: bold;
    cursor: pointer;
    transition: background .3s;
}
.cyber-btn:hover {
    background: var(--secondary);
}

/* Angled corners (decorative) */
.corner {
    position: absolute;
    width: 20px;
    height: 20px;
    border: 2px solid var(--primary);
}
.top-left    { top: -2px; left: -2px; border-right: none; border-bottom: none; }
.top-right   { top: -2px; right: -2px; border-left: none; border-bottom: none; }
.bottom-left { bottom: -2px; left: -2px; border-right: none; border-top: none; }
.bottom-right{ bottom: -2px; right: -2px; border-left: none; border-top: none; }

JavaScript (optional tilt effect)

If you’d like the card to follow the mouse, add the following script just before the closing </body> tag:

<script>
const card = document.getElementById('cyber-card');

card.addEventListener('mousemove', (e) => {
    const rect = card.getBoundingClientRect();
    const x = e.clientX - rect.left - rect.width / 2;
    const y = e.clientY - rect.top - rect.height / 2;
    const rotateX = (y / rect.height) * 20;
    const rotateY = -(x / rect.width) * 20;
    card.style.transform = `rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;
});

card.addEventListener('mouseleave', () => {
    card.style.transform = 'rotateX(0) rotateY(0)';
});
</script>

Now you have a fully functional Cyberpunk Glitch Card ready to drop into any project. Enjoy the neon glow!

CSS – Card Styling & Glitch Effect

/* --- Card Container --- */
.cyber-card {
  width: 350px;
  height: 500px;
  background: rgba(0, 0, 0, 0.6);
  border: 2px solid var(--primary);
  border-radius: 12px;
  overflow: hidden;
  position: relative;
  perspective: 1000px;
  transform-style: preserve-3d;
}

/* --- Title with Glitch --- */
.cyber-title {
  position: relative;
  font-family: 'Orbitron', sans-serif;
  font-size: 2rem;
  color: var(--primary);
  text-align: center;
  margin-top: 30px;
  text-shadow: 0 0 5px var(--primary);
}
.cyber-title::before,
.cyber-title::after {
  content: attr(data-text);
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
.cyber-title::before {
  left: 2px;
  text-shadow: -1px 0 var(--secondary);
  clip: rect(24px, 550px, 90px, 0);
  animation: glitch-anim-2 3s infinite linear alternate-reverse;
}
.cyber-title::after {
  left: -2px;
  text-shadow: -1px 0 var(--primary);
  clip: rect(85px, 550px, 140px, 0);
  animation: glitch-anim 2.5s infinite linear alternate-reverse;
}

/* --- Role / Subtitle --- */
.cyber-role {
  text-align: center;
  color: var(--primary);
  font-size: 0.9rem;
  margin-bottom: 30px;
  opacity: 0.8;
}

/* --- Stats Grid --- */
.stats {
  display: flex;
  justify-content: space-between;
  margin-bottom: 30px;
  border-top: 1px solid rgba(255,255,255,0.1);
  border-bottom: 1px solid rgba(255,255,255,0.1);
  padding: 15px 0;
}
.stat-box {
  text-align: center;
}
.stat-box .label {
  display: block;
  font-size: 0.7rem;
  color: #888;
  margin-bottom: 5px;
}
.stat-box .value {
  font-size: 1.1rem;
  color: #fff;
  font-weight: bold;
}

/* --- Button --- */
.cyber-btn {
  width: 100%;
  padding: 15px;
  background: transparent;
  border: 1px solid var(--primary);
  color: var(--primary);
  font-family: 'Orbitron', sans-serif;
  font-weight: bold;
  letter-spacing: 2px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: 0.3s;
}
.cyber-btn::before {
  content: '';
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: var(--primary);
  transition: 0.3s;
  z-index: -1;
}
.cyber-btn:hover {
  color: #000;
  box-shadow: 0 0 15px var(--primary);
}
.cyber-btn:hover::before {
  left: 0;
}

/* --- Decorative Corners --- */
.corner {
  position: absolute;
  width: 10px;
  height: 10px;
  border: 2px solid var(--primary);
  transition: 0.3s;
}
.top-left {
  top: -2px;
  left: -2px;
  border-right: none;
  border-bottom: none;
}
.top-right {
  top: -2px;
  right: -2px;
  border-left: none;
  border-bottom: none;
}
.bottom-left {
  bottom: -2px;
  left: -2px;
  border-right: none;
  border-top: none;
}
.bottom-right {
  bottom: -2px;
  right: -2px;
  border-left: none;
  border-top: none;
  width: 20px;
  border-color: var(--secondary);
}
.card:hover .corner {
  width: 100%;
  height: 100%;
  opacity: 0.1;
}

/* --- Keyframes --- */
@keyframes glitch-anim {
  0%   { clip: rect(10px, 9999px, 30px, 0);  transform: skew(0.5deg); }
  10%  { clip: rect(50px, 9999px, 70px, 0);  transform: skew(0.2deg); }
  20%  { clip: rect(20px, 9999px, 60px, 0);  transform: skew(0.8deg); }
  100% { clip: rect(80px, 9999px, 100px, 0); transform: skew(0.1deg); }
}
@keyframes glitch-anim-2 {
  0%   { clip: rect(60px, 9999px, 80px, 0);  transform: skew(0.6deg); }
  10%  { clip: rect(10px, 9999px, 30px, 0);  transform: skew(0.3deg); }
  100% { clip: rect(90px, 9999px, 100px, 0); transform: skew(0.1deg); }
}

JavaScript – Interaction

const card = document.getElementById('cyber-card');
Back to Blog

Related posts

Read more »

v2: When GSAP Stopped Being the Problem

When I shipped v1 of my portfolio, I chose simplified motion because reliability mattered more than ambition. I respected GSAP, but I didn’t yet understand it—i...