<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

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.