Spotlight Glowing Cards

You can use these as standard cards and ass icons, heading, buttons and more but I used these to call attention to my three website design ideals that govern every project.

Here is the finished result of my implementation:

Now for the code to get this running on your website.

First the HTML:

<div class="container">
	<main class="flow main">
		<div class="cards">
			<div class="cards_inner">
				<div class="card">
					<h2 class="card_heading">
						User experience is key to fostering conversions & improving efficiency
					</h2>
				</div>
				<div class="card">
					<h2 class="card_heading">
						Usability & intuitive function create the backbone to good design
					</h2>
				</div>
				<div class="card">
					<h2 class="card_heading">
						Focus on delivering the best possible result & giving more than expected
					</h2>
				</div>
			</div>
			<div class="cards_inner overlay"></div>
		</div>
	</main>
</div>

The CSS:

@import url(https://fonts.googleapis.com/css2?family=Rubik:wght@300;400;500&display=swap);

* {
	margin: 0;
	padding: 0;
	box-sizing: border-box;
}

body {
	overflow: hidden;
	background-color: #121212;
}

.container {
	height: 100vh;
	display: flex;
	align-items: center;
	justify-content: center;
}

.main {
	padding: 2rem;
	display: flex;
	align-items: center;
	justify-content: center;
}

.cards {
	position: relative;
}

.cards_inner {
	display: flex;
	flex-wrap: wrap;
	gap: 2.5rem;
}

.card {
	--flow-space: 0.5em;
	--hsl: var(--hue), var(--saturation), var(--lightness);
	flex: 1 1 14rem;
	padding: 1rem 1rem;
	display: grid;
	align-items: center;
	gap: 1.25rem;
	color: #fff;
	background-color: #292929;
	border: 1px solid #eceff133;
	border-radius: 15px;
}

.card:nth-child(1) {
	--hue: 0;
	--saturation: 70%;
	--lightness: 50%;
}

.card:nth-child(2) {
	--hue: 135;
	--saturation: 100%;
	--lightness: 100%;
}

.card:nth-child(3) {
	--hue: 0.69;
	--saturation: 85%;
	--lightness: 70.04%;
}

.card_heading {
	font-family: "Rubik", sans-serif;
	font-size: 1.5rem;
	line-height: normal;
	font-weight: 400;
}

.flow > * + * {
	margin-top: var(--flow-space, 1.25em);
}

.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));
}

:not(.overlay) > .card {
	transition: 400ms background ease;
	will-change: background;
}

:not(.overlay) > .card:hover {
	--lightness: 95%;
	background: hsla(var(--hsl), 0.1);
}

And last, the Javascript:

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.clientX - cardsContainer.getBoundingClientRect().left + window.scrollX;
	const y =
		e.clientY - cardsContainer.getBoundingClientRect().top + window.scrollY;

	overlayEl.style = `--opacity: 1; --x: ${x}px; --y:${y}px;`;
};

const createOverlayCta = (overlayCard, ctaEl) => {
	const overlayCta = document.createElement("div");

	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);

You can check this out for yourself and be able to edit the code on CodePen