<div class="grid">
<!-- Settings -->
<label for="wireframe" class="wireframe">Toggle wireframe</label>
<input type="checkbox" id="wireframe" class="wireframe" />
<!-- The button -->
<button class="glow">
<p>Glowing button</p>
</button>
</div>
button.glow {
/* Properties controlled by JavaScript
Fallback for browsers not supporting CSS Properties and Values API
*/
--glow-left: 50%;
--glow-top: 50%;
--glow-opacity: 0.75;
/* the button transition duration property */
--button-press-duration: 50ms;
/* the button transition delay property */
--button-pressed-duration: 10ms;
/* Colors of glow gradient in HSL
They're raw because they need to work with custom alpha
*/
--color-glow-from-raw: 344, 54%, 46%;
--color-glow-to-raw: 27, 88%, 64%;
/* Glass effect inner shadows */
--glass-shadows:
inset 0 2px 1px 0 hsla(0, 0%, 100%, 0.4),
inset 0 2em 2em 0 hsla(0, 0%, 100%, 0.12),
inset 0 -3px 0.25em 0 hsla(0, 0%, 100%, 0.12),
inset 0 -0.25em 1em 0 hsla(0, 0%, 100%, 0.12),
inset 0 -2em 2em 0 hsla(var(--color-background-raw), 0.05),
inset 0 0.25em 0.5em 0 hsla(0, 0%, 100%, 0.2);
/* Optical alignment corection */
padding: 1em 2em 1.05em 2em;
/* Make corners round btw. 50% won't work as expected */
border-radius: 999px;
/* Improve performance */
contain: content;
/* remove blue highligt on mobile chromium browsers */
-webkit-tap-highlight-color: transparent;
/* Add shadows */
box-shadow:
/* Use glass effect */
var(--glass-shadows),
/* Outer glow shadows */
-3.3px 0 5.9px hsla(var(--color-glow-from-raw), 0.01),
-11.2px 0 19.9px hsla(var(--color-glow-from-raw), 0.02),
-50px 0 89px hsla(var(--color-glow-from-raw), 0.1),
3.3px 0 5.9px hsla(var(--color-glow-to-raw), 0.01),
11.2px 0 19.9px hsla(var(--color-glow-to-raw), 0.02),
50px 0 89px hsla(var(--color-glow-to-raw), 0.1);
/* Make background glow slightly */
background: linear-gradient(60deg, hsla(var(--color-glow-from-raw), 0.2), hsla(var(--color-glow-to-raw), 0.2));
/* Make button stay pressed for a bit longer */
transition:
transform var(--button-press-duration) ease-in var(--button-pressed-duration),
box-shadow var(--button-press-duration) ease-in var(--button-pressed-duration);
will-change: transform, box-shadow;
transform-origin: bottom center;
/* Hide glow overflow */
overflow: hidden;
/* Glow will be relative to the button */
position: relative;
}
/* Glowing circle */
button.glow::after {
/* Make sure it shows */
content: "";
/* Position it */
width: 7em;
height: 7em;
position: absolute;
top: var(--glow-top);
left: var(--glow-left);
transform: translate(-50%, -50%);
z-index: -1;
border-radius: 50%;
/* Make background glow with a gradient */
background: linear-gradient(60deg, hsl(var(--color-glow-from-raw)), hsl(var(--color-glow-to-raw)));
/* Add large blur */
filter: blur(3em);
opacity: var(--glow-opacity);
transition:
transform var(--button-press-duration) ease-in var(--button-pressed-duration),
opacity var(--button-press-duration) ease-in var(--button-pressed-duration),
top 80ms linear,
left 80ms linear;
}
button.glow:hover, button.glow:focus {
/* Let's create anticipation before the button press */
transform: translatey(-1px);
/* Make the button glow a bit more */
background: linear-gradient(60deg, hsla(var(--color-glow-from-raw), 0.25), hsla(var(--color-glow-to-raw), 0.25));
box-shadow:
var(--glass-shadows),
-3.3px 0 5.9px hsla(var(--color-glow-from-raw), 0.08),
-11.2px 0 19.9px hsla(var(--color-glow-from-raw), 0.1),
-50px 0 89px hsla(var(--color-glow-from-raw), 0.18),
3.3px 0 5.9px hsla(var(--color-glow-to-raw), 0.08),
11.2px 0 19.9px hsla(var(--color-glow-to-raw), 0.1),
50px 0 89px hsla(var(--color-glow-to-raw), 0.18);
}
button.glow:active {
/* Transform acording to the perspective */
transform: translatey(0.18em) scale(0.98);
background: linear-gradient(60deg, hsla(var(--color-glow-from-raw), 0.4), hsla(var(--color-glow-to-raw), 0.4));
box-shadow:
var(--glass-shadows),
-3.3px 0 5.9px hsla(var(--color-glow-from-raw), 0.1),
-11.2px 0 19.9px hsla(var(--color-glow-from-raw), 0.2),
-50px 0 89px hsla(var(--color-glow-from-raw), 0.3),
3.3px 0 5.9px hsla(var(--color-glow-to-raw), 0.1),
11.2px 0 19.9px hsla(var(--color-glow-to-raw), 0.2),
50px 0 89px hsla(var(--color-glow-to-raw), 0.3);
transition:
transform var(--button-press-duration) ease-in,
box-shadow var(--button-press-duration) ease-in;
/* On touch screens make press transition faster */
@media (hover: none) and (pointer: coarse) {
transition:
transform calc(var(--button-press-duration) / 4) ease-in,
box-shadow calc(var(--button-press-duration) / 4) ease-in;
}
}
/* Use CSS Properties and Values API
It enables transitions for css properties
*/
@property --glow-left {
syntax: '<length-percentage>';
inherits: true;
initial-value: 50%;
}
@property --glow-top {
syntax: '<length-percentage>';
inherits: true;
initial-value: 50%;
}
@property --glow-opacity {
syntax: '<number>';
inherits: true;
initial-value: 0.75;
}
/* Generic button styles */
button {
font-family: 'Work Sans', sans-serif;
font-size: calc(1rem + 1.5vmin);
white-space: nowrap;
cursor: pointer;
border: 1px solid hsla(0, 0%, 100%, 0.2);
color: var(--color-on-primary);
background: var(--color-primary);
/* Don't forget about keyboard only styles */
&:focus-visible {
/* Add simple outline */
outline: 2px var(--color-primary) solid;
outline-offset: 0.5rem;
}
}
/* Style button's text */
button.glow p {
font-weight: 500;
mix-blend-mode: overlay;
text-rendering: optimizeSpeed;
pointer-events: none;
}
/* Make shadows stronger on dark background */
@media (prefers-color-scheme: dark) {
button.glow {
background: linear-gradient(60deg, hsla(var(--color-glow-from-raw), 0.15), hsla(var(--color-glow-to-raw), 0.15));
--glass-shadows:
inset 0 2px 1px 0 hsla(0, 0%, 100%, 0.2),
inset 0 2em 2em 0 hsla(0, 0%, 100%, 0.06),
inset 0 -3px 0.25em 0 hsla(0, 0%, 100%, 0.06),
inset 0 -0.25em 1em 0 hsla(0, 0%, 100%, 0.06),
inset 0 -2em 2em 0 hsla(var(--color-background-raw), 0.025),
inset 0 0.25em 0.5em 0 hsla(0, 0%, 100%, 0.1);
}
button.glow:hover {
background: linear-gradient(60deg, hsla(var(--color-glow-from-raw), 0.2), hsla(var(--color-glow-to-raw), 0.2));
box-shadow:
var(--glass-shadows),
-3.3px 0 5.9px hsla(var(--color-glow-from-raw), 0.04),
-11.2px 0 19.9px hsla(var(--color-glow-from-raw), 0.1),
-50px 0 89px hsla(var(--color-glow-from-raw), 0.16),
3.3px 0 5.9px hsla(var(--color-glow-to-raw), 0.04),
11.2px 0 19.9px hsla(var(--color-glow-to-raw), 0.1),
50px 0 89px hsla(var(--color-glow-to-raw), 0.16);
}
button.glow:active {
box-shadow:
var(--glass-shadows),
-3.3px 0 5.9px hsla(var(--color-glow-from-raw), 0.085),
-11.2px 0 19.9px hsla(var(--color-glow-from-raw), 0.125),
-50px 0 89px hsla(var(--color-glow-from-raw), 0.24),
3.3px 0 5.9px hsla(var(--color-glow-to-raw), 0.085),
11.2px 0 19.9px hsla(var(--color-glow-to-raw), 0.125),
50px 0 89px hsla(var(--color-glow-to-raw), 0.24);
}
}
/* Import Work Sans font from Google Fonts */
@import url('https://fonts.googleapis.com/css2?family=Work+Sans:wght@400;500&display=swap');
/* Setup color variables */
:root {
color-scheme: dark light;
/* Light mode colors */
--color-background-raw: 4, 9%, 69%;
--color-background: hsl(var(--color-background-raw));
--color-on-background: hsl(4, 3%, 7%);
--color-primary: hsla(4, 80%, 60%, 0.6);
--color-on-primary: hsl(4, 3%, 99%);
/* Dark mode colors */
@media (prefers-color-scheme: dark) {
--color-background-raw: 4, 12%, 16%;
--color-on-background: hsl(4, 3%, 99%);
--color-primary: hsla(4, 49%, 45%, 0.6);
--color-on-primary: hsl(4, 3%, 99%);
}
}
/* Setup body */
body {
display: flex;
justify-content: stretch;
align-items: stretch;
min-height: 100vh;
background: var(--color-background);
}
/* Center the button */
.grid {
display: flex;
flex-direction: column;
width: 100%;
@supports (display: grid){
display: grid;
grid-gap: 2rem 0.5rem;
grid-template-columns: auto auto 1fr;
grid-template-rows: auto 1fr;
& button {
grid-column: 1 / 4;
grid-row: 1 / 3;
place-self: center;
}
}
}
/* Import Work Sans font from Google Fonts */
@import url('https://fonts.googleapis.com/css2?family=Work+Sans:wght@400;500&display=swap');
/* Wireframe styles */
label.wireframe {
font-family: 'Work Sans', sans-serif;
font-size: calc(1rem + 1.5vmin);
color: var(--color-on-background);
place-self: center end;
opacity: 0.9;
cursor: pointer;
grid-row: 1;
grid-column: 1;
padding: 1rem 0 1rem 1rem;
font-size: 1.1rem;
}
label.wireframe:hover ~ input.wireframe,
input.wireframe:hover,
input.wireframe:focus {
filter: brightness(1.1);
}
input:focus-visible {
/* Add simple outline */
outline: 2px var(--color-primary) solid;
outline-offset: 0.5rem;
}
input.wireframe {
appearance: none;
grid-column: 2;
grid-row: 1;
cursor: pointer;
place-self: center start;
border: var(--color-primary) 2px solid;
width: 2.5rem;
height: 1.5rem;
border-radius: 999px;
position: relative;
}
input.wireframe::before {
content: '';
background: var(--color-primary);
border-radius: 999px;
position: absolute;
width: 1rem;
height: 1rem;
top: 0.125rem;
left: 0.125rem;
transform: scale(0.9);
transition: transform 60ms ease-in;
}
input.wireframe:checked::before {
transform: translatex(1rem);
}
/* Make wireframe affect the button */
button > div {
border: 1px transparent solid;
}
input.wireframe:checked ~ button,
input.wireframe:checked ~ button::after {
background: hsla(0, 0%, 100%, 0.05);
border: var(--color-on-background) 1px solid;
box-shadow: none;
color: var(--color-on-background);
filter: none;
}
@use postcss-preset-env(stage: 1, browsers: "last 2 versions");
View Compiled
// Select the button
const Button = document.querySelector(".glow");
// Set glow position css properties
const setGlowPosition = (event) => {
// Get the event's coordinates relative to the button.
const { left, top } = event.target.getBoundingClientRect();
const offsetX = event.clientX - left;
const offsetY = event.clientY - top;
// Prevent setting position on keyboard click
if (offsetX > 0 && offsetY > 0) {
requestAnimationFrame(() => {
event.target.style.setProperty("--glow-left", `${offsetX}px`);
event.target.style.setProperty("--glow-top", `${offsetY}px`);
});
}
};
// Set glow position on mousemove (mouse) and click (touch and keyboard)
Button.addEventListener("mousemove", setGlowPosition);
Button.addEventListener("click", setGlowPosition);
// Set glow opacity css properies
const setGlowOpacityFactory = (value) => (event) => {
requestAnimationFrame(() => {
event.target.style.setProperty("--glow-opacity", value);
});
};
// Set opacity to 0.75
const setGlowOpacity75 = setGlowOpacityFactory(0.75);
// Set opacity to 1
const setGlowOpacity100 = setGlowOpacityFactory(1);
// Set glow opacity to 0.75 on mouseleave (mouse) and focusout (touch and keyboard)
Button.addEventListener("mouseleave", setGlowOpacity75);
Button.addEventListener("focusout", setGlowOpacity75);
// Set glow opacity 1 on mouseenter (mouse) and focusin (touch and keyboard)
Button.addEventListener("mouseenter", setGlowOpacity100);
Button.addEventListener("focusin", setGlowOpacity100);
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.