mixin icon(key)
  -
    const PATH_MAP = {
      Twitter: "M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z",
      SitePoint: "M2.471 10.533l1.771 1.688 5.598 5.141 2.4-2.291c.21-.297.194-.705-.046-.985L9.99 12.184l.01-.005-2.371-2.266c-.279-.314-.27-.78.021-1.079l6.39-6.076L11.146 0 2.475 8.238c-.664.633-.664 1.66 0 2.295h-.004zm19.056 2.937l-1.77-1.691-5.595-5.142-2.411 2.291c-.221.3-.207.705.045.985l2.205 1.891h-.006l2.369 2.265c.27.314.27.766-.029 1.064l-6.391 6.075L12.855 24l8.67-8.238c.664-.633.666-1.659 0-2.295l.002.003z",
      DevTo: "M7.42 10.05c-.18-.16-.46-.23-.84-.23H6l.02 2.44.04 2.45.56-.02c.41 0 .63-.07.83-.26.24-.24.26-.36.26-2.2 0-1.91-.02-1.96-.29-2.18zM0 4.94v14.12h24V4.94H0zM8.56 15.3c-.44.58-1.06.77-2.53.77H4.71V8.53h1.4c1.67 0 2.16.18 2.6.9.27.43.29.6.32 2.57.05 2.23-.02 2.73-.47 3.3zm5.09-5.47h-2.47v1.77h1.52v1.28l-.72.04-.75.03v1.77l1.22.03 1.2.04v1.28h-1.6c-1.53 0-1.6-.01-1.87-.3l-.3-.28v-3.16c0-3.02.01-3.18.25-3.48.23-.31.25-.31 1.88-.31h1.64v1.3zm4.68 5.45c-.17.43-.64.79-1 .79-.18 0-.45-.15-.67-.39-.32-.32-.45-.63-.82-2.08l-.9-3.39-.45-1.67h.76c.4 0 .75.02.75.05 0 .06 1.16 4.54 1.26 4.83.04.15.32-.7.73-2.3l.66-2.52.74-.04c.4-.02.73 0 .73.04 0 .14-1.67 6.38-1.8 6.68z"
    }
  svg.button__icon(role='img' aria-hidden="true" xmlns='http://www.w3.org/2000/svg' viewbox='0 0 24 24')
    title= `${key} Icon`
    path(d=PATH_MAP[key])

.scene
  button.button
    span.button__shadow
      +icon('DevTo')
    span.button__content
      span.button__text SitePoint
      +icon('DevTo')
      span.button__shine
View Compiled
*
  box-sizing border-box
  transform-style preserve-3d

:root
  --blur 6px
  --shine-blur calc(var(--blur) * 4)
  --size 25vmin
  --transition 0.1s
  --depth max(25px, 3vmin)
  --icon-size 75%
  --radius 24%
  --shine hsla(0, 0%, 100%, 0.85)
  --button-bg hsla(0, 0%, 0%, 0.025)
  --icon-fill hsl(196, 0%, 0%)
  --shadow-bg hsla(0, 0%, 0%, 0.115)
  --shadow-alpha 0.4
  --shadow-icon hsla(0, 0%, 0%, 0.5)
  --bg hsl(196, 65%, 95%)
  
body
  min-height 100vh
  display grid
  place-items center
  transform-style preserve-3d
  touch-action none
  overflow hidden
  
  &:before
    content ''
    position absolute
    height 300vh
    width 300vw
    z-index -1
    top 50%
    left 50%
    transform translate3d(-50%, -50%, calc(var(--size) * -1))
    background var(--bg)
 
.scene
  height var(--size)
  position relative
  min-height 150px
  min-width 150px
  width var(--size)
  transform rotateX(-34deg) rotateY(22deg) rotateX(90deg)    
    
.button
  appearance none
  background none
  border 0
  cursor pointer
  height 100%
  outline transparent
  position absolute
  width 100%
  
  &__content
  &__shadow
    height 100%
    width 100%
    position absolute
    display grid
    place-items center
    border-radius var(--radius)

  &__content
    --fill var(--icon-fill)
    background var(--button-bg)
    overflow hidden
    backdrop-filter blur(calc(var(--blur) * 0.25))
    transform translate3d(0, 0, var(--depth))
    transition transform var(--transition), backdrop-filter var(--transition)
    
  &__shadow
    --shadow-fill 'hsla(0, 0%, 0%, %s)' % var(--shadow-alpha)
    --fill var(--shadow-fill)
    background var(--shadow-bg)
    filter blur(var(--blur))
    transition filter var(--transition)
    
  &__text
    position absolute
    width 1px
    height 1px
    padding 0
    margin -1px
    overflow hidden
    clip rect(0, 0, 0, 0)
    white-space nowrap
    border-width 0
    
  &__shine
    --shine-size calc(var(--size) * 0.5)
    background var(--shine)
    border-radius 50%
    height var(--shine-size)
    filter blur(var(--shine-blur)) brightness(1.25)
    min-height 75px
    min-width 75px
    position absolute
    left 0
    top 0
    transform translate3d(-50%, -50%, 1vmin) translate(calc(var(--x, -150) * 1%), calc(var(--y, -150) * 1%))
    width var(--shine-size)
  
  &__icon
    height var(--icon-size)
    fill var(--fill)
    width var(--icon-size)
    transition fill var(--transition)
    
  &:active
    --depth 0
    --blur 0
    --shadow-alpha 1
View Compiled
import gsap from 'https://cdn.skypack.dev/gsap'



const BUTTON = document.querySelector('.button')
const CONTENT = document.querySelector('.button__content')
const SHINE = document.querySelector('.button__shine')

const buttonSet = gsap.quickSetter(BUTTON, 'css')
const xySet = gsap.quickSetter(SHINE, 'css')
const LIMIT = 10

const UPDATE = ({x, y}) => {
  const BOUNDS = CONTENT.getBoundingClientRect()
  const POS_X = ((x - BOUNDS.x) / BOUNDS.width) * 200
  const POS_Y = ((y - BOUNDS.y) / BOUNDS.height) * 200
  xySet({
    '--x': POS_X,
    '--y': POS_Y
  })
  const xPercent = gsap.utils.mapRange(
    0,
    window.innerWidth,
    -LIMIT,
    LIMIT,
    x
  )
  const yPercent = gsap.utils.mapRange(
    0,
    window.innerHeight,
    -LIMIT,
    LIMIT,
    y
  )
  buttonSet({
    xPercent,
    yPercent,
  })
}

document.addEventListener('pointermove', UPDATE)
document.addEventListener('pointerdown', UPDATE)
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.