.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(210, 80%, 50%)
  
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))
    
  &__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%)
    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
  
  &:after
    content ''
    position absolute
    top 15%
    left 20%
    background var(--white)
    border-radius 50%
    height 20%
    width 20%
  
  &--left
    left 0
  &--right
    right 0
View Compiled
import { GUI } from 'https://cdn.skypack.dev/dat.gui'

const CONFIG = {
  x: 0,
  y: 0,
}

const EYES = document.querySelector('.face__eyes')

const CONTROLLER = new GUI()

const UPDATE = () => {
  EYES.style.setProperty('--x', CONFIG.x)
  EYES.style.setProperty('--y', CONFIG.y)
}

CONTROLLER.add(CONFIG, 'x', -100, 100, 1).name('Translate X').onChange(UPDATE)
CONTROLLER.add(CONFIG, 'y', -100, 100, 1).name('Translate Y').onChange(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.