Source: 04-UI-Glow
Xem live preview & sao chép mã nguồn.
Live Preview
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>04 — UI Glow</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main class="wrap">
<h1>Button Glow</h1>
<p class="sub">Hiệu ứng nút phát sáng + ripple</p>
<div class="btns">
<button class="btn-glow" data-variant="primary">Mua ngay</button>
<button class="btn-glow" data-variant="success">Dùng thử</button>
<button class="btn-glow" data-variant="warning">Tìm hiểu</button>
</div>
</main>
<script src="script.js"></script>
</body>
</html>
:root {
--bg: #0b1020;
--card: #0f1a30;
--txt: #e8ecf8;
--primary: #6ea8fe;
--success: #6edea8;
--warning: #ffd36e;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
background: radial-gradient(
60% 60% at 50% 0%,
#121a35 0%,
#090e1b 60%,
#05070d 100%
);
color: var(--txt);
font: 500 16px/1.5 ui-sans-serif, system-ui;
}
.wrap {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px;
}
h1 {
margin: 0 0 6px;
font-size: 42px;
letter-spacing: 0.5px;
}
.sub {
margin: 0 0 28px;
opacity: 0.7;
}
.btns {
display: flex;
gap: 16px;
flex-wrap: wrap;
justify-content: center;
}
.btn-glow {
--c: var(--primary);
position: relative;
padding: 12px 20px;
border-radius: 14px;
border: 0;
color: #0a0f1f;
background: linear-gradient(#fff, #eee);
font-weight: 700;
cursor: pointer;
isolation: isolate;
transition: transform 0.2s ease, box-shadow 0.2s ease;
box-shadow: 0 0 0 rgba(0, 0, 0, 0);
}
.btn-glow[data-variant="success"] {
--c: var(--success);
}
.btn-glow[data-variant="warning"] {
--c: var(--warning);
}
.btn-glow::before {
content: "";
position: absolute;
inset: -2px;
z-index: -1;
border-radius: 16px;
background: radial-gradient(60% 100% at 50% 0%, var(--c) 0%, transparent 70%);
filter: blur(12px);
opacity: 0.65;
transition: opacity 0.2s ease;
}
.btn-glow:hover {
transform: translateY(-1px);
box-shadow: 0 6px 24px rgba(0, 0, 0, 0.25);
}
.btn-glow:active {
transform: translateY(0);
}
.ripple {
position: absolute;
border-radius: 50%;
transform: translate(-50%, -50%);
pointer-events: none;
background: radial-gradient(circle, var(--c), transparent 60%);
width: 12px;
height: 12px;
animation: r 600ms ease-out forwards;
opacity: 0.7;
}
@keyframes r {
to {
width: 280px;
height: 280px;
opacity: 0;
}
}
// Ripple effect on click
document.querySelectorAll(".btn-glow").forEach((btn) => {
btn.addEventListener("click", (e) => {
const r = document.createElement("span");
r.className = "ripple";
r.style.left = e.offsetX + "px";
r.style.top = e.offsetY + "px";
btn.appendChild(r);
setTimeout(() => r.remove(), 650);
});
});