- 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
This Pen doesn't use any external CSS resources.