Hey Programmers! In this post, we will make Gradient Glowing Cards With Hover Effects with pure HTML, and CSS and by using a little bit of JavaScript. If you are creating a modern-looking website with a dark theme then these visually striking cards are perfect for showcasing pricing plans or features.
In this tutorial, we will build three cards that have a glowing effect when we hover. Each card will have a unique gradient color scheme that changes a little bit on hover. That gives a modern and polished look to the webpage.
Also Read: Animated Wallet Card With HTML & CSS
We will understand the code step by step with the code box so that you can use key features used in this tutorial like gradient glowing effect, interactive hover states & overlay interaction in your other projects.
At the end, I will share the entire source code of Gradient Glowing cards with hover effects so that you can run this on your local device.
--r
) and displays an icon along with a textual description.data-text
attribute specifies the text to be displayed on hover.--r
).Also Read: Glassmorphism Cards With Hover Animation
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: #0f222f;
}
.container {
position: relative;
display: flex;
justify-content: center;
align-items: center;
}
.container .glass {
position: relative;
width: 200px;
height: 240px;
background: linear-gradient(#fff2, transparent);
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 25px 25px rgba(0, 0, 0, 0.25);
backdrop-filter: blur(10px);
display: flex;
justify-content: center;
align-items: center;
transition: 0.5s;
border-radius: 10px;
margin: 0 -45px;
transform: rotate(calc(var(--r) * 1deg));
}
.container:hover .glass {
transform: rotate(0deg);
margin: 0 20px;
}
.container .glass:before {
content: attr(data-text);
position: absolute;
bottom: 0;
width: 100%;
height: 40px;
background: rgba(255, 255, 255, 0.05);
display: flex;
justify-content: center;
align-items: center;
color: #fff;
}
content
property to insert the value of the data-text
attribute as content before the glass element..container .glass i {
font-size: 4em;
color: #fff;
}
HTML:
<main class="main flow">
<h1 class="main__heading">Pricing</h1>
<div class="main__cards cards">
<div class="cards__inner">
<div class="cards__card card">
<h2 class="card__heading">Basic</h2>
<p class="card__price">$9.99</p>
<ul role="list" class="card__bullets flow">
<li>Access to standard workouts and nutrition plans</li>
<li>Email support</li>
</ul>
<a href="#basic" class="card__cta cta">Get Started</a>
</div>
<div class="cards__card card">
<h2 class="card__heading">Pro</h2>
<p class="card__price">$19.99</p>
<ul role="list" class="card__bullets flow">
<li>Access to advanced workouts and nutrition plans</li>
<li>Priority Email support</li>
<li>Exclusive access to live Q&A sessions</li>
</ul>
<a href="#pro" class="card__cta cta">Upgrade to Pro</a>
</div>
<div class="cards__card card">
<h2 class="card__heading">Ultimate</h2>
<p class="card__price">$29.99</p>
<ul role="list" class="card__bullets flow">
<li>Access to all premium workouts and nutrition plans</li>
<li>24/7 Priority support</li>
<li>1-on-1 virtual coaching session every month</li>
<li>Exclusive content and early access to new features</li>
</ul>
<a href="#ultimate" class="card__cta cta">Go Ultimate</a>
</div>
</div>
<div class="overlay cards__inner"></div>
</div>
</main>
CSS:
<style>
@import url("https://fonts.googleapis.com/css2?family=League+Spartan:wght@400;500;600;700;800;900&display=swap");
*,
*::after,
*::before {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
min-height: 100vh;
}
body {
display: grid;
place-items: center;
font-family: "League Spartan", system-ui, sans-serif;
font-size: 1.1rem;
line-height: 1.2;
background-color: #212121;
color: #ddd;
}
ul {
list-style: none;
}
.main {
max-width: 75rem;
padding: 3em 1.5em;
}
.main__heading {
font-weight: 600;
font-size: 2.25em;
margin-bottom: 0.75em;
text-align: center;
color: #eceff1;
}
.cards {
position: relative;
}
.cards__inner {
display: flex;
flex-wrap: wrap;
gap: 2.5em;
}
.card {
--flow-space: 0.5em;
--hsl: var(--hue), var(--saturation), var(--lightness);
flex: 1 1 14rem;
padding: 1.5em 2em;
display: grid;
grid-template-rows: auto auto auto 1fr;
align-items: start;
gap: 1.25em;
color: #eceff1;
background-color: #2b2b2b;
border: 1px solid #eceff133;
border-radius: 15px;
}
.card:nth-child(1) {
--hue: 165;
--saturation: 82.26%;
--lightness: 51.37%;
}
.card:nth-child(2) {
--hue: 291.34;
--saturation: 95.9%;
--lightness: 61.76%;
}
.card:nth-child(3) {
--hue: 338.69;
--saturation: 100%;
--lightness: 48.04%;
}
.card__bullets {
line-height: 1.4;
}
.card__bullets li::before {
display: inline-block;
content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512' width='16' title='check' fill='%23dddddd'%3E%3Cpath d='M173.898 439.404l-166.4-166.4c-9.997-9.997-9.997-26.206 0-36.204l36.203-36.204c9.997-9.998 26.207-9.998 36.204 0L192 312.69 432.095 72.596c9.997-9.997 26.207-9.997 36.204 0l36.203 36.204c9.997 9.997 9.997 26.206 0 36.204l-294.4 294.401c-9.998 9.997-26.207 9.997-36.204-.001z' /%3E%3C/svg%3E");
transform: translatey(0.25ch);
margin-right: 1ch;
}
.card__heading {
font-size: 1.05em;
font-weight: 600;
}
.card__price {
font-size: 1.75em;
font-weight: 700;
}
.flow > * + * {
margin-top: var(--flow-space, 1.25em);
}
.cta {
display: block;
align-self: end;
margin: 1em 0 0.5em 0;
text-align: center;
text-decoration: none;
color: #fff;
background-color: #0d0d0d;
padding: 0.7em;
border-radius: 10px;
font-size: 1rem;
font-weight: 600;
}
.overlay {
position: absolute;
inset: 0;
pointer-events: none;
user-select: none;
opacity: var(--opacity, 0);
-webkit-mask: radial-gradient(
25rem 25rem at var(--x) var(--y),
#000 1%,
transparent 50%
);
mask: radial-gradient(
25rem 25rem at var(--x) var(--y),
#000 1%,
transparent 50%
);
transition: 400ms mask ease;
will-change: mask;
}
.overlay .card {
background-color: hsla(var(--hsl), 0.15);
border-color: hsla(var(--hsl), 1);
box-shadow: 0 0 0 1px inset hsl(var(--hsl));
}
.overlay .cta {
display: block;
grid-row: -1;
width: 100%;
background-color: hsl(var(--hsl));
box-shadow: 0 0 0 1px hsl(var(--hsl));
}
:not(.overlay) > .card {
transition: 400ms background ease;
will-change: background;
}
:not(.overlay) > .card:hover {
--lightness: 95%;
background: hsla(var(--hsl), 0.1);
}
</style>
JavaScript:
<script>
const cardsContainer = document.querySelector(".cards");
const cardsContainerInner = document.querySelector(".cards__inner");
const cards = Array.from(document.querySelectorAll(".card"));
const overlay = document.querySelector(".overlay");
const applyOverlayMask = (e) => {
const overlayEl = e.currentTarget;
const x = e.pageX - cardsContainer.offsetLeft;
const y = e.pageY - cardsContainer.offsetTop;
overlayEl.style = `--opacity: 1; --x: ${x}px; --y:${y}px;`;
};
const createOverlayCta = (overlayCard, ctaEl) => {
const overlayCta = document.createElement("div");
overlayCta.classList.add("cta");
overlayCta.textContent = ctaEl.textContent;
overlayCta.setAttribute("aria-hidden", true);
overlayCard.append(overlayCta);
};
const observer = new ResizeObserver((entries) => {
entries.forEach((entry) => {
const cardIndex = cards.indexOf(entry.target);
let width = entry.borderBoxSize[0].inlineSize;
let height = entry.borderBoxSize[0].blockSize;
if (cardIndex >= 0) {
overlay.children[cardIndex].style.width = `${width}px`;
overlay.children[cardIndex].style.height = `${height}px`;
}
});
});
const initOverlayCard = (cardEl) => {
const overlayCard = document.createElement("div");
overlayCard.classList.add("card");
createOverlayCta(overlayCard, cardEl.lastElementChild);
overlay.append(overlayCard);
observer.observe(cardEl);
};
cards.forEach(initOverlayCard);
document.body.addEventListener("pointermove", applyOverlayMask);
</script>
Last Updated: June 18, 2024