Skip to content

Instantly share code, notes, and snippets.

@fedikhatib
Last active March 18, 2024 00:54
Show Gist options
  • Save fedikhatib/d09a04e4c5f80f17c44a1b2594974a12 to your computer and use it in GitHub Desktop.
Save fedikhatib/d09a04e4c5f80f17c44a1b2594974a12 to your computer and use it in GitHub Desktop.
Proximity Glow Cards
<div class="container">
<article>
<div class="glows"></div>
<span class="header">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6">
<path fill-rule="evenodd" d="M17.303 5.197A7.5 7.5 0 006.697 15.803a.75.75 0 01-1.061 1.061A9 9 0 1121 10.5a.75.75 0 01-1.5 0c0-1.92-.732-3.839-2.197-5.303zm-2.121 2.121a4.5 4.5 0 00-6.364 6.364.75.75 0 11-1.06 1.06A6 6 0 1118 10.5a.75.75 0 01-1.5 0c0-1.153-.44-2.303-1.318-3.182zm-3.634 1.314a.75.75 0 01.82.311l5.228 7.917a.75.75 0 01-.777 1.148l-2.097-.43 1.045 3.9a.75.75 0 01-1.45.388l-1.044-3.899-1.601 1.42a.75.75 0 01-1.247-.606l.569-9.47a.75.75 0 01.554-.68z" clip-rule="evenodd" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6">
<path fill-rule="evenodd" d="M17.303 5.197A7.5 7.5 0 006.697 15.803a.75.75 0 01-1.061 1.061A9 9 0 1121 10.5a.75.75 0 01-1.5 0c0-1.92-.732-3.839-2.197-5.303zm-2.121 2.121a4.5 4.5 0 00-6.364 6.364.75.75 0 11-1.06 1.06A6 6 0 1118 10.5a.75.75 0 01-1.5 0c0-1.153-.44-2.303-1.318-3.182zm-3.634 1.314a.75.75 0 01.82.311l5.228 7.917a.75.75 0 01-.777 1.148l-2.097-.43 1.045 3.9a.75.75 0 01-1.45.388l-1.044-3.899-1.601 1.42a.75.75 0 01-1.247-.606l.569-9.47a.75.75 0 01.554-.68z" clip-rule="evenodd" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6">
<path fill-rule="evenodd" d="M17.303 5.197A7.5 7.5 0 006.697 15.803a.75.75 0 01-1.061 1.061A9 9 0 1121 10.5a.75.75 0 01-1.5 0c0-1.92-.732-3.839-2.197-5.303zm-2.121 2.121a4.5 4.5 0 00-6.364 6.364.75.75 0 11-1.06 1.06A6 6 0 1118 10.5a.75.75 0 01-1.5 0c0-1.153-.44-2.303-1.318-3.182zm-3.634 1.314a.75.75 0 01.82.311l5.228 7.917a.75.75 0 01-.777 1.148l-2.097-.43 1.045 3.9a.75.75 0 01-1.45.388l-1.044-3.899-1.601 1.42a.75.75 0 01-1.247-.606l.569-9.47a.75.75 0 01.554-.68z" clip-rule="evenodd" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6">
<path fill-rule="evenodd" d="M17.303 5.197A7.5 7.5 0 006.697 15.803a.75.75 0 01-1.061 1.061A9 9 0 1121 10.5a.75.75 0 01-1.5 0c0-1.92-.732-3.839-2.197-5.303zm-2.121 2.121a4.5 4.5 0 00-6.364 6.364.75.75 0 11-1.06 1.06A6 6 0 1118 10.5a.75.75 0 01-1.5 0c0-1.153-.44-2.303-1.318-3.182zm-3.634 1.314a.75.75 0 01.82.311l5.228 7.917a.75.75 0 01-.777 1.148l-2.097-.43 1.045 3.9a.75.75 0 01-1.45.388l-1.044-3.899-1.601 1.42a.75.75 0 01-1.247-.606l.569-9.47a.75.75 0 01.554-.68z" clip-rule="evenodd" />
</svg>
</span>
<span class="badge">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6">
<path fill-rule="evenodd" d="M9 4.5a.75.75 0 01.721.544l.813 2.846a3.75 3.75 0 002.576 2.576l2.846.813a.75.75 0 010 1.442l-2.846.813a3.75 3.75 0 00-2.576 2.576l-.813 2.846a.75.75 0 01-1.442 0l-.813-2.846a3.75 3.75 0 00-2.576-2.576l-2.846-.813a.75.75 0 010-1.442l2.846-.813A3.75 3.75 0 007.466 7.89l.813-2.846A.75.75 0 019 4.5zM18 1.5a.75.75 0 01.728.568l.258 1.036c.236.94.97 1.674 1.91 1.91l1.036.258a.75.75 0 010 1.456l-1.036.258c-.94.236-1.674.97-1.91 1.91l-.258 1.036a.75.75 0 01-1.456 0l-.258-1.036a2.625 2.625 0 00-1.91-1.91l-1.036-.258a.75.75 0 010-1.456l1.036-.258a2.625 2.625 0 001.91-1.91l.258-1.036A.75.75 0 0118 1.5zM16.5 15a.75.75 0 01.712.513l.394 1.183c.15.447.5.799.948.948l1.183.395a.75.75 0 010 1.422l-1.183.395c-.447.15-.799.5-.948.948l-.395 1.183a.75.75 0 01-1.422 0l-.395-1.183a1.5 1.5 0 00-.948-.948l-1.183-.395a.75.75 0 010-1.422l1.183-.395c.447-.15.799-.5.948-.948l.395-1.183A.75.75 0 0116.5 15z" clip-rule="evenodd" />
</svg>
<span>Pointer tracking glows</span>
</span>
<h2>Wherever you go,<br>it follows</h2>
<a href="">Learn more</a>
</article>
<article>
<div class="glows"></div>
<span class="header">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6">
<path fill-rule="evenodd" d="M17.303 5.197A7.5 7.5 0 006.697 15.803a.75.75 0 01-1.061 1.061A9 9 0 1121 10.5a.75.75 0 01-1.5 0c0-1.92-.732-3.839-2.197-5.303zm-2.121 2.121a4.5 4.5 0 00-6.364 6.364.75.75 0 11-1.06 1.06A6 6 0 1118 10.5a.75.75 0 01-1.5 0c0-1.153-.44-2.303-1.318-3.182zm-3.634 1.314a.75.75 0 01.82.311l5.228 7.917a.75.75 0 01-.777 1.148l-2.097-.43 1.045 3.9a.75.75 0 01-1.45.388l-1.044-3.899-1.601 1.42a.75.75 0 01-1.247-.606l.569-9.47a.75.75 0 01.554-.68z" clip-rule="evenodd" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6">
<path fill-rule="evenodd" d="M17.303 5.197A7.5 7.5 0 006.697 15.803a.75.75 0 01-1.061 1.061A9 9 0 1121 10.5a.75.75 0 01-1.5 0c0-1.92-.732-3.839-2.197-5.303zm-2.121 2.121a4.5 4.5 0 00-6.364 6.364.75.75 0 11-1.06 1.06A6 6 0 1118 10.5a.75.75 0 01-1.5 0c0-1.153-.44-2.303-1.318-3.182zm-3.634 1.314a.75.75 0 01.82.311l5.228 7.917a.75.75 0 01-.777 1.148l-2.097-.43 1.045 3.9a.75.75 0 01-1.45.388l-1.044-3.899-1.601 1.42a.75.75 0 01-1.247-.606l.569-9.47a.75.75 0 01.554-.68z" clip-rule="evenodd" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6">
<path fill-rule="evenodd" d="M17.303 5.197A7.5 7.5 0 006.697 15.803a.75.75 0 01-1.061 1.061A9 9 0 1121 10.5a.75.75 0 01-1.5 0c0-1.92-.732-3.839-2.197-5.303zm-2.121 2.121a4.5 4.5 0 00-6.364 6.364.75.75 0 11-1.06 1.06A6 6 0 1118 10.5a.75.75 0 01-1.5 0c0-1.153-.44-2.303-1.318-3.182zm-3.634 1.314a.75.75 0 01.82.311l5.228 7.917a.75.75 0 01-.777 1.148l-2.097-.43 1.045 3.9a.75.75 0 01-1.45.388l-1.044-3.899-1.601 1.42a.75.75 0 01-1.247-.606l.569-9.47a.75.75 0 01.554-.68z" clip-rule="evenodd" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6">
<path fill-rule="evenodd" d="M17.303 5.197A7.5 7.5 0 006.697 15.803a.75.75 0 01-1.061 1.061A9 9 0 1121 10.5a.75.75 0 01-1.5 0c0-1.92-.732-3.839-2.197-5.303zm-2.121 2.121a4.5 4.5 0 00-6.364 6.364.75.75 0 11-1.06 1.06A6 6 0 1118 10.5a.75.75 0 01-1.5 0c0-1.153-.44-2.303-1.318-3.182zm-3.634 1.314a.75.75 0 01.82.311l5.228 7.917a.75.75 0 01-.777 1.148l-2.097-.43 1.045 3.9a.75.75 0 01-1.45.388l-1.044-3.899-1.601 1.42a.75.75 0 01-1.247-.606l.569-9.47a.75.75 0 01.554-.68z" clip-rule="evenodd" />
</svg>
</span>
<span class="badge">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6">
<path fill-rule="evenodd" d="M9 4.5a.75.75 0 01.721.544l.813 2.846a3.75 3.75 0 002.576 2.576l2.846.813a.75.75 0 010 1.442l-2.846.813a3.75 3.75 0 00-2.576 2.576l-.813 2.846a.75.75 0 01-1.442 0l-.813-2.846a3.75 3.75 0 00-2.576-2.576l-2.846-.813a.75.75 0 010-1.442l2.846-.813A3.75 3.75 0 007.466 7.89l.813-2.846A.75.75 0 019 4.5zM18 1.5a.75.75 0 01.728.568l.258 1.036c.236.94.97 1.674 1.91 1.91l1.036.258a.75.75 0 010 1.456l-1.036.258c-.94.236-1.674.97-1.91 1.91l-.258 1.036a.75.75 0 01-1.456 0l-.258-1.036a2.625 2.625 0 00-1.91-1.91l-1.036-.258a.75.75 0 010-1.456l1.036-.258a2.625 2.625 0 001.91-1.91l.258-1.036A.75.75 0 0118 1.5zM16.5 15a.75.75 0 01.712.513l.394 1.183c.15.447.5.799.948.948l1.183.395a.75.75 0 010 1.422l-1.183.395c-.447.15-.799.5-.948.948l-.395 1.183a.75.75 0 01-1.422 0l-.395-1.183a1.5 1.5 0 00-.948-.948l-1.183-.395a.75.75 0 010-1.422l1.183-.395c.447-.15.799-.5.948-.948l.395-1.183A.75.75 0 0116.5 15z" clip-rule="evenodd" />
</svg>
<span>Pointer tracking glows</span>
</span>
<h2>Activate by,<br>getting closer</h2>
<a href="">Learn more</a>
</article>
</div>
import { GUI } from 'https://cdn.skypack.dev/dat.gui'
const CONTAINER = document.querySelector('.container')
const CARDS = document.querySelectorAll('article')
const CONFIG = {
proximity: 40,
spread: 80,
blur: 20,
gap: 32,
vertical: false,
opacity: 0,
}
const PROXIMITY = 10
const UPDATE = (event) => {
// get the angle based on the center point of the card and pointer position
for (const CARD of CARDS) {
// Check the card against the proximity and then start updating
const CARD_BOUNDS = CARD.getBoundingClientRect()
// Get distance between pointer and outerbounds of card
if (
event?.x > CARD_BOUNDS.left - CONFIG.proximity &&
event?.x < CARD_BOUNDS.left + CARD_BOUNDS.width + CONFIG.proximity &&
event?.y > CARD_BOUNDS.top - CONFIG.proximity &&
event?.y < CARD_BOUNDS.top + CARD_BOUNDS.height + CONFIG.proximity) {
// If within proximity set the active opacity
CARD.style.setProperty('--active', 1)
} else {
CARD.style.setProperty('--active', CONFIG.opacity)
}
const CARD_CENTER = [
CARD_BOUNDS.left + CARD_BOUNDS.width * 0.5,
CARD_BOUNDS.top + CARD_BOUNDS.height * 0.5
]
let ANGLE = Math.atan2(event?.y - CARD_CENTER[1], event?.x - CARD_CENTER[0]) * 180 / Math.PI
ANGLE = ANGLE < 0 ? ANGLE + 360 : ANGLE;
CARD.style.setProperty('--start', ANGLE + 90)
}
}
document.body.addEventListener('pointermove', UPDATE)
const RESTYLE = () => {
CONTAINER.style.setProperty('--gap', CONFIG.gap)
CONTAINER.style.setProperty('--blur', CONFIG.blur)
CONTAINER.style.setProperty('--spread', CONFIG.spread)
CONTAINER.style.setProperty('--direction', CONFIG.vertical ? 'column' : 'row')
}
const CTRL = new GUI({
width: 340,
})
CTRL.add(CONFIG, 'spread', 10, 180, 1).name('Spread (deg)').onChange(RESTYLE)
CTRL.add(CONFIG, 'proximity', 10, 180, 1).name('Active Proximity (px)').onChange(RESTYLE)
CTRL.add(CONFIG, 'gap', 10, 100, 1).name('Gap (px)').onChange(RESTYLE)
CTRL.add(CONFIG, 'blur', 0, 50, 1).name('Blur (px)').onChange(RESTYLE)
CTRL.add(CONFIG, 'opacity', 0, 1, 0.01).name('Inactive Opacity').onChange(RESTYLE)
CTRL.add(CONFIG, 'vertical').name('Vertical Layout').onChange(RESTYLE)
RESTYLE()
UPDATE()
@import "normalize.css";
@font-face {
font-family: "Geist Sans";
src: url("https://assets.codepen.io/605876/GeistVF.ttf") format("truetype");
}
:root {
--bg: hsl(246 44% 7%);
--border: hsl(280 10% 50% / 1);
--card: hsl(237 36% 10%);
--color: hsl(240 18% 80%);
--border-width: 2px;
--border-radius: 12px;
--gradient: conic-gradient(from 180deg at 50% 70%,hsla(0,0%,98%,1) 0deg,#eec32d 72.0000010728836deg,#ec4b4b 144.0000021457672deg,#709ab9 216.00000858306885deg,#4dffbf 288.0000042915344deg,hsla(0,0%,98%,1) 1turn);
}
*,
*:after,
*:before {
box-sizing: border-box;
}
@property --start {
syntax: '<number>';
inherits: true;
initial-value: 0;
}
body {
background: var(--bg);
display: grid;
place-items: center;
min-height: 100vh;
font-family: "Geist Sans", "SF Pro Text", "SF Pro Icons", "AOS Icons", "Helvetica Neue", Helvetica, Arial, sans-serif, system-ui;
font-weight: 70;
color: var(--color);
}
.container {
--spread: 60;
display: flex;
flex-wrap: wrap;
flex-direction: var(--direction);
gap: calc(var(--gap) * 1px);
margin: 0 auto;
justify-content: center;
place-items: center;
position: relative;
padding: 2rem;
touch-action: none;
}
article {
--active: 0.15;
--start: 0;
height: 100%;
background: var(--card);
padding: 2rem;
aspect-ratio: 330 / 400;
border-radius: var(--border-radius);
min-width: 280px;
max-width: 280px;
display: flex;
flex-direction: column;
gap: 0.25rem;
position: relative;
}
article:is(:hover, :focus-visible) {
z-index: 2;
}
.glows {
pointer-events: none;
position: absolute;
inset: 0;
filter: blur(calc(var(--blur) * 1px));
}
.glows::after,
.glows::before {
--alpha: 0;
content: "";
background: var(--gradient);
background-attachment: fixed;
position: absolute;
inset: -5px;
border: 10px solid transparent;
border-radius: var(--border-radius);
mask:
linear-gradient(#0000, #0000),
conic-gradient(from calc((var(--start) - (var(--spread) * 0.5)) * 1deg), #000 0deg, #fff, #0000 calc(var(--spread) * 1deg));
mask-composite: intersect;
mask-clip: padding-box, border-box;
opacity: var(--active);
transition: opacity 1s;
}
article::before {
position: absolute;
inset: 0;
border: var(--border-width) solid transparent;
content: "";
border-radius: var(--border-radius);
pointer-events: none;
background: var(--border);
background-attachment: fixed;
border-radius: var(--border-radius);
mask:
linear-gradient(#0000, #0000),
conic-gradient(
from calc(((var(--start) + (var(--spread) * 0.25)) - (var(--spread) * 1.5)) * 1deg),
hsl(0 0% 100% / 0.15) 0deg,
white,
hsl(0 0% 100% / 0.15) calc(var(--spread) * 2.5deg));
mask-clip: padding-box, border-box;
mask-composite: intersect;
opacity: var(--active);
transition: opacity 1s;
}
article::after {
--bg-size: 100%;
content: "";
pointer-events: none;
position: absolute;
background: var(--gradient);
background-attachment: fixed;
border-radius: var(--border-radius);
opacity: var(--active, 0);
transition: opacity 1s;
--alpha: 0;
inset: 0;
border: var(--border-width) solid transparent;
mask:
linear-gradient(#0000, #0000),
conic-gradient(from calc(((var(--start) + (var(--spread) * 0.25)) - (var(--spread) * 0.5)) * 1deg), #0000 0deg, #fff, #0000 calc(var(--spread) * 0.5deg));
filter: brightness(1.5);
mask-clip: padding-box, border-box;
mask-composite: intersect;
}
.badge {
border: 2px solid var(--border);
align-self: start;
border-radius: 100px;
padding: 0.5rem 0.7rem;
font-size: 0.675rem;
display: flex;
align-items: center;
gap: 0.25rem;
font-weight: 50;
}
a {
color: var(--color);
text-decoration: none;
opacity: 0.5;
display: inline-block;
align-self: start;
transition: opacity 0.2s;
}
a:is(:hover, :focus-visible) {
opacity: 1;
}
article h2 {
margin: 0;
padding: 1rem 0;
font-weight: 100;
font-size: 1.5rem;
}
.header {
position: relative;
flex: 1;
display: flex;
align-items: center;
}
.header svg {
--count: 4;
width: 106px;
}
.header svg:nth-of-type(2),
.header svg:nth-of-type(3),
.header svg:nth-of-type(4) {
position: absolute;
z-index: calc(var(--count) - var(--index));
translate: calc(var(--index) * 30%) 0;
opacity: calc(var(--count) / (2 * (var(--index) * 10)));
}
.header svg:nth-of-type(2) {
--index: 1;
}
.header svg:nth-of-type(3) {
--index: 2;
}
.header svg:nth-of-type(4) {
--index: 3;
}
.badge svg {
width: 16px;
}
.dg.ac {
z-index: 99999 !important;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment