mixin ghostOne()
  svg.ghost__image-body(preserveAspectRatio='xMinYMin', viewBox='0 0 100 130')
    g#layer1(transform='translate(0,-922.36216)')
      path#path4149.ghost__path(d='m 87.104126,985.99028 c 3e-6,18.29482 23.310064,51.08502 7.251515,64.80642 -4.508673,3.8525 -10.735588,-6.2194 -16.533188,-5.808 -4.489803,0.3185 -7.636172,5.7797 -12.112396,6.2655 -5.152434,0.559 -9.879869,-4.3961 -15.050164,-4.066 -4.493939,0.2869 -8.076853,5.0762 -12.578149,5.1722 -4.822196,0.1028 -8.903453,-4.2416 -13.677721,-4.9617 -6.489307,-0.9788 -15.1515178,5.9929 -19.6454524,0.9765 -14.2159436,-15.8688 8.0865824,-46.7737 6.6754904,-65.00824 -1.7280703,-22.33068 -1.210964,-29.61106 6.765096,-41.60022 7.976059,-11.98915 18.994883,-19.4046 31.165941,-19.4046 12.171058,0 23.189882,7.41545 31.165941,19.4046 7.97606,11.98916 6.573083,25.92871 6.573087,44.22354 z', style='fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1')
      circle#path4152.ghost__eyes(r='5', cy='961.0423', cx='19.056681', style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1')
      circle#circle4156.ghost__eyes(style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1', cx='76.471283', cy='960.05237', r='5')
      path#path4158(d='m 79.58852,970.29888 c -5e-6,2.4602 -14.18229,4.45458 -31.677023,4.45458 -17.494733,0 -31.677018,-1.99438 -31.677023,-4.45458 5e-6,-2.4602 14.380271,0.099 31.875004,0.099 17.494733,0 31.479037,-2.55919 31.479042,-0.099 z', style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1')
      g#g4167.ghost__eyes.ghost__eyes--busted
        rect#rect4162(style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1', width='2', height='15', x='692.03461', y='658.58441', transform='matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)')
        rect#rect4165(transform='matrix(-0.70710678,0.70710678,-0.70710678,-0.70710678,0,0)', y='-700.53461', x='665.08441', height='15', width='2', style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1')
      g#g4171.ghost__eyes.ghost__eyes--busted(transform='translate(57.414617,-0.98990468)')
        rect#rect4173(transform='matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)', y='658.58441', x='692.03461', height='15', width='2', style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1')
        rect#rect4175(style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1', width='2', height='15', x='665.08441', y='-700.53461', transform='matrix(-0.70710678,0.70710678,-0.70710678,-0.70710678,0,0)')
mixin ghostTwo(className)
  svg.ghost__image-body(viewBox='0 0 100 130', preserveAspectRatio='xMinYMin', class=className)
    g#layer1(transform='translate(0,-922.36216)')
      path#path4149.ghost__path(d='m 87.104126,985.99028 c 3e-6,18.29482 23.310064,51.08502 7.251515,64.80642 -4.508673,3.8525 -10.735588,-6.2194 -16.533188,-5.808 -4.489803,0.3185 -7.636172,5.7797 -12.112396,6.2655 -5.152434,0.559 -9.879869,-4.3961 -15.050164,-4.066 -4.493939,0.2869 -8.076853,5.0762 -12.578149,5.1722 -4.822196,0.1028 -8.903453,-4.2416 -13.677721,-4.9617 -6.489307,-0.9788 -15.1515178,5.9929 -19.6454524,0.9765 -14.2159436,-15.8688 8.0865824,-46.7737 6.6754904,-65.00824 -1.7280703,-22.33068 -1.210964,-29.61106 6.765096,-41.60022 7.976059,-11.98915 18.994883,-19.4046 31.165941,-19.4046 12.171058,0 23.189882,7.41545 31.165941,19.4046 7.97606,11.98916 6.573083,25.92871 6.573087,44.22354 z', style='fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1')
      circle#path4152.ghost__eyes(r='4.9010091', cy='957.77557', cx='41.527569', style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1')
      circle#circle4156.ghost__eyes(style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1', cx='64.691391', cy='955.00385', r='8.6626558')
      path#path4158.ghost__mouth(d='m 74.836967,972.6979 c -3e-6,11.63211 -14.670414,22.44764 -25.440609,22.44764 -10.770196,0 -19.303184,-0.71848 -19.303187,-12.35059 3e-6,-11.63211 8.852855,-13.5886 19.62305,-13.5886 10.770195,0 25.120743,-8.14052 25.120746,3.49155 z', style='fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1')
      g#g4167.ghost__eyes.ghost__eyes--busted(transform='matrix(1.5105663,0,0,1.5105663,35.905034,-496.71423)')
        rect#rect4162(style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1', width='2', height='15', x='692.03461', y='658.58441', transform='matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)')
        rect#rect4165(transform='matrix(-0.70710678,0.70710678,-0.70710678,-0.70710678,0,0)', y='-700.53461', x='665.08441', height='15', width='2', style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1')
      g#g4171.ghost__eyes.ghost__eyes--busted(transform='translate(22.470903,-3.2666991)')
        rect#rect4173(transform='matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)', y='658.58441', x='692.03461', height='15', width='2', style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1')
        rect#rect4175(style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1', width='2', height='15', x='665.08441', y='-700.53461', transform='matrix(-0.70710678,0.70710678,-0.70710678,-0.70710678,0,0)')
mixin ghostThree()
  svg.ghost__image-body(viewBox='0 0 100 130', preserveAspectRatio='xMinYMin')
    g#layer1(transform='translate(0,-922.36216)')
      path#path4149.ghost__path(d='m 87.104126,985.99028 c 3e-6,18.29482 23.310064,51.08502 7.251515,64.80642 -4.508673,3.8525 -10.735588,-6.2194 -16.533188,-5.808 -4.489803,0.3185 -7.636172,5.7797 -12.112396,6.2655 -5.152434,0.559 -9.879869,-4.3961 -15.050164,-4.066 -4.493939,0.2869 -8.076853,5.0762 -12.578149,5.1722 -4.822196,0.1028 -8.903453,-4.2416 -13.677721,-4.9617 -6.489307,-0.9788 -15.1515178,5.9929 -19.6454524,0.9765 -14.2159436,-15.8688 8.0865824,-46.7737 6.6754904,-65.00824 -1.7280703,-22.33068 -1.210964,-29.61106 6.765096,-41.60022 7.976059,-11.98915 18.994883,-19.4046 31.165941,-19.4046 12.171058,0 23.189882,7.41545 31.165941,19.4046 7.97606,11.98916 6.573083,25.92871 6.573087,44.22354 z', style='fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1')
      path#path4158(d='m 64.541935,968.77294 c -10e-7,1.76535 -11.355147,5.78254 -17.039796,5.78254 -5.684647,0 -16.023096,-4.26665 -16.023098,-6.03199 2e-6,-1.76535 11.896477,3.67918 17.581124,3.67918 5.684648,0 15.481769,-5.19507 15.48177,-3.42973 z', style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1')
      g#g4171.ghost__eyes.ghost__eyes--busted(transform='translate(18.907238,-4.0586249)')
        rect#rect4173(transform='matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)', y='658.58441', x='692.03461', height='15', width='2', style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1')
        rect#rect4175(style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1', width='2', height='15', x='665.08441', y='-700.53461', transform='matrix(-0.70710678,0.70710678,-0.70710678,-0.70710678,0,0)')
      g#g4215.ghost__eyes.ghost__eyes--busted(transform='translate(38.309414,-3.8606434)')
        rect#rect4217(style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1', width='2', height='15', x='692.03461', y='658.58441', transform='matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)')
        rect#rect4219(transform='matrix(-0.70710678,0.70710678,-0.70710678,-0.70710678,0,0)', y='-700.53461', x='665.08441', height='15', width='2', style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1')
      path#circle4221.ghost__eyes(d='m 62.069108,959.55738 c 0,2.70675 -2.194256,-3.01825 -4.901009,-3.01825 -2.706753,0 -4.901009,5.725 -4.901009,3.01825 0,-2.70675 2.194256,-4.90101 4.901009,-4.90101 2.706753,0 4.901009,2.19426 4.901009,4.90101 z', style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1')
      path#path4224.ghost__eyes(style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1', d='m 42.666932,959.55738 c 0,2.70675 -2.194256,-3.01825 -4.901009,-3.01825 -2.706753,0 -4.901009,5.725 -4.901009,3.01825 0,-2.70675 2.194256,-4.90101 4.901009,-4.90101 2.706753,0 4.901009,2.19426 4.901009,4.90101 z')
mixin ghostFour()
  svg.ghost__image-body(viewBox='0 0 100 130', preserveAspectRatio='xMinYMin')
    g#layer1(transform='translate(0,-922.36216)')
      path#path4149.ghost__path(d='m 87.104126,985.99028 c 3e-6,18.29482 23.310064,51.08502 7.251515,64.80642 -4.508673,3.8525 -10.735588,-6.2194 -16.533188,-5.808 -4.489803,0.3185 -7.636172,5.7797 -12.112396,6.2655 -5.152434,0.559 -9.879869,-4.3961 -15.050164,-4.066 -4.493939,0.2869 -8.076853,5.0762 -12.578149,5.1722 -4.822196,0.1028 -8.903453,-4.2416 -13.677721,-4.9617 -6.489307,-0.9788 -15.1515178,5.9929 -19.6454524,0.9765 -14.2159436,-15.8688 8.0865824,-46.7737 6.6754904,-65.00824 -1.7280703,-22.33068 -1.210964,-29.61106 6.765096,-41.60022 7.976059,-11.98915 18.994883,-19.4046 31.165941,-19.4046 12.171058,0 23.189882,7.41545 31.165941,19.4046 7.97606,11.98916 6.573083,25.92871 6.573087,44.22354 z', style='fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1')
      g#g4171.ghost__eyes.ghost__eyes--busted(transform='matrix(1.6637807,0,0,1.6637807,28.456943,-641.01528)')
        rect#rect4173(transform='matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)', y='658.58441', x='692.03461', height='15', width='2', style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1')
        rect#rect4175(style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1', width='2', height='15', x='665.08441', y='-700.53461', transform='matrix(-0.70710678,0.70710678,-0.70710678,-0.70710678,0,0)')
      circle#path4245.ghost__eyes(r='7.5', cy='958.1463', cx='38.385101', style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1')
      g#g4247.ghost__eyes.ghost__eyes--busted(transform='matrix(1.6637807,0,0,1.6637807,6.6789885,-640.81728)')
        rect#rect4249(style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1', width='2', height='15', x='692.03461', y='658.58441', transform='matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)')
        rect#rect4251(transform='matrix(-0.70710678,0.70710678,-0.70710678,-0.70710678,0,0)', y='-700.53461', x='665.08441', height='15', width='2', style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1')
      circle#circle4253.ghost__eyes(style='fill:#000000;fill-opacity:1;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1', cx='60.163055', cy='957.9483', r='7.5')

mixin ghost()
  - const speed = (Math.random() * 3) + 0.75
  - const delay = Math.floor(Math.random() * 5) + 1
  - const isColor = Math.random() > 0.85
  - const color = isColor ? ['#c5a3ff', '#ffbebc', '#c4fafb', '#ace7ff'][Math.floor(Math.random() * 4)] : '#fff'
  - const score = isColor ? 5 : 1
  .ghost__container(style=`--speed: ${speed}; --delay: ${delay}; --color: ${color}; --score: ${score}`)
    input.ghost(type='checkbox')
    .ghost__image
      - const idx = Math.floor(Math.random() * 4) + 1
      if (idx === 1)
        +ghostOne()
      else if (idx === 2)
        +ghostTwo()
      else if (idx === 3)
        +ghostThree()
      else
        +ghostFour()
    block

//- Use a form so we can use the "reset" trick to reset the game
form
  input.menu(type='checkbox')
  .title-screen
    .title-screen__content
      +ghostTwo('title-ghost')
      h1.title Ghost <br/> Bustin'
      p.description.
        You'll get 15 seconds to bust as many ghosts as you can.
        Colored ghosts score more points!
      button Start
  .backdrop
  - const gPerRow = 3
  .game(style=`--rows: ${gPerRow}; --cols: ${gPerRow}`)
    - for (let g = 0; g < gPerRow * gPerRow; g++)
      .ghost-pen
        +ghost()
          +ghost()
            +ghost()

  .end
    h1.title Good job!
    h1.title.score You scored &nbsp;
    button(type='reset') Try again?
    .credit-ghost
      +ghostTwo()
View Compiled
@import url('https://fonts.googleapis.com/css?family=Lato')

*
  box-sizing border-box

$bg = linear-gradient(35deg, #333, #000)
$grass = #1e824c

body
  align-items center
  background-color darken($grass, 75%)
  color #fff
  counter-reset score
  display flex
  font-family 'Lato', sans-serif
  justify-content center
  min-height 100vh

.game
  display grid
  grid-gap 20px
  grid-template-columns repeat(var(--cols), 1fr)
  grid-template-rows repeat(var(--rows), 1fr)
  height 250px
  position relative
  width 250px

  // Make the game a little bigger on the bigger viewport
  @media(min-width 768px)
    grid-gap 25px
    height 400px
    width 400px

.ghost-pen
  position relative
  transform translate(0, 50%)


.ghost
.ghost__image
  animation bob calc(var(--speed) * 1s) infinite ease
  animation-delay calc(var(--delay) * 1s)
  height 45px
  transform translate(0, 100%) scale(1)
  width 30px

  @media(min-width 768px)
    height 75px
    width 60px

.ghost
  cursor pointer
  position absolute
  z-index 2
  opacity 0

  // SVG related styling for the ghosts
  &__path
    fill var(--color)

  &__mouth
    fill pink

  &__eyes--busted
    display none

  &__image-body
    // needs explicit no animation so that reset resets it
    animation none
    height 100%
    width 100%

  &__container
    align-items center
    display flex
    height 100%
    justify-content center
    position absolute
    width 100%
    z-index 3

    &:after
      background #6c7a89
      border-radius 50% 50% 0 0
      box-shadow 10px 0 0 #2e3131
      bottom 0
      color #2e3131
      content 'R.I.P'
      text-align center
      line-height 60px
      left 0
      position absolute
      right 0
      top 0
      transform translate(0, 50%)
      z-index 3

      @media(min-width 768px)
        font-size 1.5rem
        line-height 80px

    // Contained containers are hidden by default.
    // This trick allows us to have many levels per a slot.
    .ghost__container
      display none

  // When a ghost is checked, increment the score by the declared
  // var score and float that ghost out of there
  &:checked
    animation busted 1.75s .25s both
    counter-increment score var(--score)

    // At the same time, our image needs to get out of there
    ~ .ghost__image
      animation busted 1.75s .25s both

      // Use the inner SVG to animate the wiggle
      .ghost__image-body
        animation wiggle 1.75s .25s
        // And cross out the ghost eyes by showing and hiding SVG elements
        .ghost__eyes
          display none
        .ghost__eyes--busted
          display block

    // When it gets checked, bring in the next one if there is one.
    // The trick here is to do a stepped animation on the container
    // of the duration of the delay so there's no overlap whilst the
    // checked ghost floats out of view
    ~ .ghost__container
      display flex
      animation fadeIn calc(var(--delay) * 1s) both steps(2)


.menu
  opacity 0

  &:checked
  &:checked ~ .title-screen
    display none

  // This starts the game timer
  &:checked ~ .end
    animation slideUp 15s both

    .credit-ghost
      animation float 2s 15s both

      svg
        animation wiggle 2s 15s
.end
.title-screen
.backdrop
  position fixed
  top 0
  right 0
  bottom 0
  left 0

.title-screen
.end
  display flex
  align-items center
  justify-content center
  flex-direction column
  z-index 5
  background $bg

.backdrop
  animation flash 10s infinite linear
  @keyframes flash
    71%, 73%, 75%, 77%, 79%
      background rgba(255, 255, 204, .5)
    0%, 70%, 72%, 74%, 76%, 78%, 80%, 100%
      background transparent


.menu
  height 100vh
  width 100vw
  position fixed
  top 50%
  left 50%
  transform translate(-50%, -50%)
  z-index 6

.end
  background $bg
  transform translate(0, 100%)

.credit-ghost
  position absolute
  left 75%
  top 100%
  animation none

.title
  letter-spacing 4px
  font-size 1.75rem
  margin 0
  text-transform uppercase

  @media(min-width 768px)
    font-size 3rem

.title-screen__content
  display grid
  grid-gap 0 1rem
  align-content center
  align-items center
  grid-template-columns auto 1fr
  grid-template-rows repeat(3, auto)
  max-width 90vw

.description
  grid-column 2
  text-align justify
  max-width 210px

button
  cursor pointer
  grid-column 2
  background white
  color black
  text-transform uppercase
  letter-spacing 4px
  padding 8px 12px

.credit-ghost
.title-ghost
  height 65px
  width 50px
  .ghost__path
    fill white

.score
  margin-bottom 2rem

  &:after
    content counter(score)



// All the different keyframes required
@keyframes bob
  50%
    transform translate(0, -100%) scale(1.25)

@keyframes slideUp
  95%
    transform translate(0, 100%)
  100%
    transform translate(0, 0)

@keyframes float
  to
    transform translate(0, -125vh)

@keyframes busted
  0%
    transform translate(0, -100%) scale(1.25)
  100%
    opacity 0
    transform translate(0, -100vh) scale(0)

@keyframes wiggle
  25%, 75%
    transform translate(-50%, 0)
  50%
    transform translate(50%, 0)

@keyframes fadeIn
  from
    opacity 0
View Compiled
// NOT FOUND 👻
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.