- const VARIANTS = ['panda', 'plain', 'default', 'dark', 'polar']

.boppin-bears
  - let b = 0
  while (b < VARIANTS.length)
    img.boppin-bears__bear.boppin-bear(src=`https://assets.codepen.io/605876/boppin-bear--${VARIANTS[b]}.png` style=`--index: ${b};`)
    - b++

input(type='range', min="0", max="7", value="0")

h1 No delay
h1 Delay
h1 Negative Delay
h1 Staggered
h1 Staggered w/ Negative Delay
h1 Reverse Stagger
h1 Reverse Stagger w/ Negative Delay
h1 User Defined Delay
p Animate simulataneously with no delay
p Animate simulataneously with a delay
p Animate simulataneously with a negative delay
p Use a variable to create a stagger. The variable represents a bears' index
p Use an offset to make the delays negative so that the elements retain stagger but don't stagger in
p Use the total amount of bears to reverse the stagger
p Use a negative coefficient to create a stagger in the opposite direction
p Set custom delays with custom property scope
pre
  code.language-css.
    .bear {
      animation-delay: 0s;
    }
pre
  code.language-css.
    .bear {
      animation-delay: 1s;
    }
pre
  code.language-css.
    .bear {
      animation-delay: -0.5s;
    }
pre
  code.language-css.
    .bear {
      animation-delay: calc(var(--index) * 0.1s);
    }
pre
  code.language-css.
    .bear {
      /* 5 is the number of bears */
      animation-delay: calc((var(--index) - 5) * 0.1s);
    }
pre
  code.language-css.
    .bear {
      animation-delay: calc((5 - var(--index)) * 0.1s);
    }
pre
  code.language-css.
    .bear {
      animation-delay: calc(var(--index) * -0.1s);
    }
pre
  code.language-css.
    .bear {
      animation-delay: calc(var(--delay) * -0.1s);
    }
    .bear:nth-of-type(even) {
      --delay: 2;
    }
    .bear:nth-of-type(odd) {
      --delay: 1;
    }
View Compiled
/* PrismJS 1.23.0
https://prismjs.com/download.html#themes=prism-tomorrow&languages=css+css-extras&plugins=line-numbers+inline-color+toolbar+copy-to-clipboard */
/**
 * prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML
 * Based on https://github.com/chriskempson/tomorrow-theme
 * @author Rose Pritchard
 */

code[class*="language-"],
pre[class*="language-"] {
  color: #ccc;
  background: none;
  font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
  font-size: 1em;
  text-align: left;
  white-space: pre;
  word-spacing: normal;
  word-break: normal;
  word-wrap: normal;
  line-height: 1.5;

  -moz-tab-size: 4;
  -o-tab-size: 4;
  tab-size: 4;

  -webkit-hyphens: none;
  -moz-hyphens: none;
  -ms-hyphens: none;
  hyphens: none;

}

/* Code blocks */
pre[class*="language-"] {
  padding: 2rem;
  margin: 0;
  overflow: auto;
  outline: transparent;
}

:not(pre) > code[class*="language-"],
pre[class*="language-"] {
  background: #2d2d2d;
}

/* Inline code */
:not(pre) > code[class*="language-"] {
  padding: .1em;
  border-radius: .3em;
  white-space: normal;
}

pre {
  height: 100%;
  display: flex;
  align-items: center;
}

.token.comment,
.token.block-comment,
.token.prolog,
.token.doctype,
.token.cdata {
  color: #999;
}

.token.punctuation {
  color: #ccc;
}

.token.tag,
.token.attr-name,
.token.namespace,
.token.deleted {
  color: #e2777a;
}

.token.function-name {
  color: #6196cc;
}

.token.boolean,
.token.number,
.token.function {
  color: #f08d49;
}

.token.property,
.token.class-name,
.token.constant,
.token.symbol {
  color: #f8c555;
}

.token.selector,
.token.important,
.token.atrule,
.token.keyword,
.token.builtin {
  color: #cc99cd;
}

.token.string,
.token.char,
.token.attr-value,
.token.regex,
.token.variable {
  color: #7ec699;
}

.token.operator,
.token.entity,
.token.url {
  color: #67cdcc;
}

.token.important,
.token.bold {
  font-weight: bold;
}
.token.italic {
  font-style: italic;
}

.token.entity {
  cursor: help;
}

.token.inserted {
  color: green;
}

pre[class*="language-"].line-numbers {
  position: relative;
  padding-left: 3.8em;
  counter-reset: linenumber;
}

pre[class*="language-"].line-numbers > code {
  position: relative;
  white-space: inherit;
}

.line-numbers .line-numbers-rows {
  position: absolute;
  pointer-events: none;
  top: 0;
  font-size: 100%;
  left: -3.8em;
  width: 3em; /* works for line-numbers below 1000 lines */
  letter-spacing: -1px;
  border-right: 1px solid #999;

  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;

}

  .line-numbers-rows > span {
    display: block;
    counter-increment: linenumber;
  }

    .line-numbers-rows > span:before {
      content: counter(linenumber);
      color: #999;
      display: block;
      padding-right: 0.8em;
      text-align: right;
    }

span.inline-color-wrapper {
  /*
   * The background image is the following SVG inline in base 64:
   *
   * <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 2">
   *     <path fill="gray" d="M0 0h2v2H0z"/>
   *     <path fill="white" d="M0 0h1v1H0zM1 1h1v1H1z"/>
   * </svg>
   *
   * SVG-inlining explained:
   * https://stackoverflow.com/a/21626701/7595472
   */
  background: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyIDIiPjxwYXRoIGZpbGw9ImdyYXkiIGQ9Ik0wIDBoMnYySDB6Ii8+PHBhdGggZmlsbD0id2hpdGUiIGQ9Ik0wIDBoMXYxSDB6TTEgMWgxdjFIMXoiLz48L3N2Zz4=");
  /* This is to prevent visual glitches where one pixel from the repeating pattern could be seen. */
  background-position: center;
  background-size: 110%;

  display: inline-block;
  height: 1.333ch;
  width: 1.333ch;
  margin: 0 .333ch;
  box-sizing: border-box;
  border: 1px solid white;
  outline: 1px solid rgba(0,0,0,.5);
  overflow: hidden;
}

span.inline-color {
  display: block;
  /* To prevent visual glitches again */
  height: 120%;
  width: 120%;
}

.copy-to-clipboard-button {
  display: none;
}

div.code-toolbar {
  position: relative;
}

div.code-toolbar > .toolbar {
  position: absolute;
  top: .3em;
  right: .2em;
  opacity: 1;
}

div.code-toolbar:hover > .toolbar {
  opacity: 1;
}

/* Separate line b/c rules are thrown out if selector is invalid.
   IE11 and old Edge versions don't support :focus-within. */
div.code-toolbar:focus-within > .toolbar {
  opacity: 1;
}

div.code-toolbar > .toolbar .toolbar-item {
  display: inline-block;
}

div.code-toolbar > .toolbar a {
  cursor: pointer;
}

div.code-toolbar > .toolbar button {
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
  line-height: normal;
  overflow: visible;
  padding: 0;
  -webkit-user-select: none; /* for button */
  -moz-user-select: none;
  -ms-user-select: none;
}

div.code-toolbar > .toolbar a,
div.code-toolbar > .toolbar button {
  color: #bbb;
  font-size: 1rem;
  padding: 0.5rem;
  font-family: sans-serif;
  background: hsl(0, 0%, 25%);
  border-radius: .5em;
  outline: transparent;
  cursor: pointer;
}

div.code-toolbar > .toolbar a:hover,
div.code-toolbar > .toolbar a:focus,
div.code-toolbar > .toolbar button:hover,
div.code-toolbar > .toolbar button:focus,
div.code-toolbar > .toolbar span:hover,
div.code-toolbar > .toolbar span:focus {
  background: hsl(0, 0%, 40%);
  text-decoration: none;
}


// *
// *:after
// *:before
//   box-sizing border-box

// :root
//   --bg hsl(210, 40%, 75%)

// body
//   min-height 100vh
//   display grid
//   place-items center
//   background var(--bg)
  
// .boppin-bears
//   display flex
//   gap 0.5rem
//   align-items flex-end
//   justify-content center
//   transform-style preserve-3d
//   perspective 500px

// .boppin-bear
//   animation bop 0.5s calc(var(--index) * 0.05s) infinite ease-in-out
//   width 15vmin
  
// @keyframes bop
//   50%
//     transform translate(0, 35%) rotateX(-22deg)
    
*
*:after
*:before
  box-sizing border-box

:root
  --delay 0
  --duration 1
  --stagger-step 0
  --coefficient 1
  --offset 0

body
  accent-color hsl(130, 100%, 80%)
  background linear-gradient(-45deg, hsl(210, 60%, 20%), hsl(280, 60%, 70%))
  min-height 100vh
  display flex
  color hsl(0, 0%, 100%)
  align-items center
  font-family sans-serif
  font-size 1.2rem
  justify-content center
  flex-direction column
  text-align center
  overflow hidden

pre
code
  background hsl(0, 0%, 10%)
  font-weight bold
  font-size 1.25rem
  font-family monospace
pre
  padding 1rem
  border-radius 6px

.boppin-bear
  width 100%
  animation bop calc(var(--duration) * 1s) infinite both ease-in-out
  animation-delay calc((((var(--delay, 0) + (var(--index) * var(--stagger-step))) + var(--offset)) * var(--coefficient)) * 1s)

@keyframes bop
  10%, 90%
    transform translate(0, 0) rotateX(0deg)
  50%
    transform translate(0, 35%) rotateX(-22deg)

.reversed .boppin-bear
  animation-delay calc((((var(--delay, 0) + ((5 - var(--index)) * var(--stagger-step))) + var(--offset)) * var(--coefficient)) * 1s)
  
.defined .boppin-bear
  animation-delay calc((var(--delay) + (var(--stagger-step) * var(--coefficient))) * 1s)
  
.defined .boppin-bear:nth-of-type(even)
  --coefficient 1
.defined .boppin-bear:nth-of-type(odd)
  --coefficient 3

.boppin-bears
  display grid
  grid-template-columns repeat(5, 1fr)
  grid-gap 1vmin
  margin 4rem 0
  max-width 100%
  align-items end
  perspective 500px
  transform-style preserve-3d

h1
p
pre
  display none

h1
  margin-bottom 0.25rem

p
  max-width 550px

code
  line-height 1.75
  display block

[hidden] *
  animation none
View Compiled
// Get started!
const RANGE = document.querySelector('input')
const CONTAINER = document.querySelector('.boppin-bears')
const TITLES = [...document.querySelectorAll('h1')]
const BLURBS = [...document.querySelectorAll('p')]
const CODES = [...document.querySelectorAll('pre')]

// Each config reps delay, duration, stagger, coefficient, offset
const DURATION = 0.4
const STAGGER = DURATION / 5
const STEP_CONFIGS = [
  [0, DURATION, 0, 1, 0],
  [1, DURATION, 0, 1, 0],
  [-DURATION, DURATION, 0, 1, 0],
  [1, DURATION, STAGGER, 1, 0],
  [1, DURATION, STAGGER, 1, -5],
  [1, DURATION, STAGGER, 1, 0],
  [1, DURATION, STAGGER, -1, 0],
  [1, DURATION, STAGGER, 0, 0],
]

const update = () => {
  // Show/Hide elements
  for (let e = 0; e < TITLES.length; e++) {
    TITLES[e].style.display = BLURBS[e].style.display = CODES[e].style.display =
      e === parseInt(RANGE.value, 10) ? 'block' : 'none'
  }
  // Running the step function
  const CONFIG = STEP_CONFIGS[parseInt(RANGE.value, 10)]
  document.documentElement.style.setProperty('--delay', CONFIG[0])
  document.documentElement.style.setProperty('--duration', CONFIG[1])
  document.documentElement.style.setProperty('--stagger-step', CONFIG[2])
  document.documentElement.style.setProperty('--coefficient', CONFIG[3])
  document.documentElement.style.setProperty('--offset', CONFIG[4])
  document.documentElement.style.setProperty('--selection', RANGE.value)
  
  if (parseInt(RANGE.value, 10) === 5 || parseInt(RANGE.value, 10) === 6) {
    CONTAINER.classList.add('reversed')
  } else {
    CONTAINER.classList.remove('reversed')
  }
  if (parseInt(RANGE.value, 10) === 7) {
    CONTAINER.classList.add('defined')
  } else {
    CONTAINER.classList.remove('defined')
  }
  // Retrigger the animation
  CONTAINER.hidden = true
  requestAnimationFrame(() => (CONTAINER.hidden = false))
}

RANGE.addEventListener('change', update)
RANGE.addEventListener('input', update)
// Run the first time to show step 0 👍
update()
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://assets.codepen.io/605876/prism.js