.face
  .face__eyes
    .eye.eye--left
    .eye.eye--right
  .face__mouth
View Compiled
*
  box-sizing border-box

:root
  --height 20vmin
  --width 85vmin
  --feature 15vmin
  --stroke hsl(0, 0%, 10%)
  --white hsl(0, 0%, 98%)
  --red hsl(10, 90%, 50%)
  --bg hsl(145, 50%, 60%)
  
body
  min-height 100vh
  display grid
  place-items center
  background var(--bg)
  
.face
  height var(--height)
  position relative
  width var(--width)
  
  &__eyes
    position absolute
    top 0
    left 0
    right 0
    height var(--feature)
    transform translate(calc(var(--x, 0) * 1px), calc(var(--y, 0) * 1px)) scaleY(1)
    
  &__mouth
    height var(--feature)
    width calc(var(--feature) * 1.5)
    background var(--stroke)
    border-radius 50%
    position absolute
    left 50%
    bottom 0
    transform translateX(-50%) translate(calc(var(--x, 0) * 0.5px), calc(var(--y, 0) * 0.5px))
    clip-path inset(50% 0 0 0)
    overflow hidden
    
    &:after
      content ''
      position absolute
      height 50%
      width 50%
      background var(--red)
      top 75%
      left 60%
      border-radius 50%
      
    
.eye
  position absolute
  height var(--feature)
  width var(--feature)
  background var(--stroke)
  border-radius 50%
  top 0
  overflow hidden
  animation blink 6s infinite
  
  &:after
    content ''
    position absolute
    top 15%
    left 20%
    background var(--white)
    border-radius 50%
    height 20%
    width 20%
  
  &--left
    left 0
  &--right
    right 0
    
 @keyframes blink
   0%, 46%, 48%, 50%, 100%
     transform scaleY(1)
   47%, 49%
     transform scaleY(0.1)
View Compiled
const mapRange = (inputLower, inputUpper, outputLower, outputUpper) => {
  const INPUT_RANGE = inputUpper - inputLower
  const OUTPUT_RANGE = outputUpper - outputLower
  return value => outputLower + (((value - inputLower) / INPUT_RANGE) * OUTPUT_RANGE || 0)
}

const FACE = document.querySelector('.face')

// Alternatively
// const X_MAPPER = mapRange(0, window.innerWidth, -10, 10)
const BOUNDS = 20
const update = ({ x, y }) => {
  const POS_X = mapRange(0, window.innerWidth, -BOUNDS, BOUNDS)(x)
  const POS_Y = mapRange(0, window.innerHeight, -BOUNDS, BOUNDS)(y)
  FACE.style.setProperty('--x', POS_X)
  FACE.style.setProperty('--y', POS_Y)  
}
document.addEventListener('pointermove', 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.