Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Auto Save

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                - var word = 'pendeveloper'
- var colorSplit = 3
each val in ['random', 'enter', 'swap', 'fade', 'flip', 'slide', 'scale', 'turn', 'fold', 'bulge', 'far', 'teeth']
    .cascading-text(data-animated, class=`cascading-text--${val}`)
      .cascading-text__replay replay?
      each val, index in word.split('')
        if index < colorSplit
          .cascading-text__letter.tx--white= val
        else
          .cascading-text__letter.tx--red= val

              
            
!

CSS

              
                $travel     = 50px
$fontSize   = 25px
$wordLength = 13
$animationDuration = .5s
$animationInterval = .1s

.cascading-text
  position relative

  &__replay
    line-height $fontSize
    font-size 12px
    color #ffffff
    cursor pointer
    position absolute
    top 50%
    left 50%
    transform translateX(-50%)
    animation fadeIn .75s
    padding 5px
    opacity 0.5
    transition transform .25s

    &:hover
      opacity 1 !important
      transform translateX(-50%) translateY(-10%)

  &[data-animated] .cascading-text__replay
    display none

  /**
   * [data-animated] is merely used as a trigger
   * attribute for replay purposes. If you were going
   * to use cascading text effects on a site, just let
   * them play once on entry
  */
  &--enter[data-animated]
    .cascading-text__letter
      opacity        0
      transform      translateY($travel * -1)
      animation-name enter

  &--fade[data-animated]
    .cascading-text__letter
      opacity        0
      animation-name fade

  &--flip[data-animated]
    .cascading-text__letter
      transform      rotateY(180deg)
      animation-name flip

  &--scale[data-animated]
    .cascading-text__letter
      transform      scale(0)
      animation-name scale

  &--turn[data-animated]
    .cascading-text__letter
      transform-origin bottom
      transform rotateY(90deg)
      animation-name turn

  &--fold[data-animated]
    .cascading-text__letter
      transform-origin bottom
      transform rotateX(90deg)
      animation-name fold

  &--bulge[data-animated]
    .cascading-text__letter
      animation-name bulge

  &--swap[data-animated]
    .cascading-text__letter
      for $i in (1..$wordLength)
        &:nth-of-type({$i})
          if ($i % 2 eq 0)
            transform translateX(-15px)
            animation-name swap-right
          else
            transform translateX(15px)
            animation-name swap-left

  &--slide[data-animated]
    .cascading-text__letter
      animation-name slide
      opacity        0
      transform      translateX($travel * -1px)

      for $i in (1..$wordLength)
        &:nth-of-type({$i})
          animation-delay $animationInterval * ($wordLength - $i)

  &--far[data-animated]
    perspective 1000px
    .cascading-text__letter
      opacity 0
      transform translateX(-25px) translateY(-20px) translateZ(-800px)
      animation-name far

  &--teeth[data-animated]
    .cascading-text__letter
      opacity 0
      for $i in (1..$wordLength)
        &:nth-of-type({$i})
          if ($i % 2 eq 0)
            transform translateY(-100%)
            animation-name enter
          else
            transform translateY(100%)
            animation-name enter-bottom

  &--random[data-animated]
    random()
      return floor(math(0, 'random')*(250 - 25 + 1) + 25)
    .cascading-text__letter

      for $i in (1..$wordLength)
        animation-name random
        &:nth-of-type({$i})
          $x = random()
          $y = random()
          if ($x % 2 eq 1)
            $x = $x * -1
          if ($y % 2 eq 1)
            $y = $y * -1

          transform translateX($x * 1%) translateY($y * 1%)
  &__letter
    font-size       $fontSize
    line-height     $fontSize
    display         inline-block
    transform-style preserve-3d

    for $i in (1..$wordLength)
      &:nth-of-type({$i})
        animation-delay $animationInterval * $i

    animation-duration  $animationDuration
    animation-fill-mode forwards

@keyframes enter
  0%
    opacity   0
    transform translateY($travel * -1)
  25%
    opacity   1
  100%
    opacity   1
    transform translateY(0px)

@keyframes enter-bottom
  0%
    opacity   0
    transform translateY($travel)
  25%
    opacity   1
  100%
    opacity   1
    transform translateY(0px)


@keyframes fade
  from
    opacity 0
  to
    opacity 1

@keyframes flip
  from
    transform rotateY(180deg)
  to
    transform rotateY(0deg)

@keyframes slide
  0%
    opacity 0
    transform translateX($travel * -1px)
  25%
    opacity 1
  100%
    opacity 1
    transform translateX(0)

@keyframes scale
  from
    transform scale(0)
  to
    transform scale(1)

@keyframes fadeIn
  from
    opacity 0
  to
    opacity .5

@keyframes turn
  to
    transform rotateY(0)

@keyframes fold
  to
    transform rotateX(0)

@keyframes bulge
  25%
    transform scale(1.5)

@keyframes swap-right
  25%
    transform translateX(-15px) translateY(-100%)
  75%
    transform translateX(0) translateY(-100%)
  100%
    transform translateX(0) translateY(0)

@keyframes swap-left
  25%
    transform translateX(15px) translateY(100%)
  75%
    transform translateX(0) translateY(100%)
  100%
    transform translateX(0) translateY(0)

@keyframes far
  25%
    opacity 1
  100%
    opacity 1
    transform translateY(0) translateZ(0) translateX(0)

@keyframes random
  25%
    opacity 1
  100%
    opacity 1
    transform translateX(0) translateY(0)

/**
  * Theme
*/
*
  box-sizing border-box

body
  background      #111111
  width           100vw

.cascading-text
  line-height 150px
  text-align  center
  
  @media(min-width 768px)
    width       50%
    display     inline-block
    float       left
    
  @media(min-width 992px)
    width 25%


.tx--red
  color #e74c3c

.tx--white
  color #ffffff

              
            
!

JS

              
                /**
  * JS purely used for implementing replay feature
*/
const PROPS = {
  CONTAINER: 'cascading-text',
  LETTER   : 'cascading-text__letter',
  ANIMATED : 'data-animated',
  REPLAY   : 'cascading-text__replay',
}
const texts = document.querySelectorAll(`.${PROPS.CONTAINER}`)
const replays = document.querySelectorAll(`.${PROPS.REPLAY}`)

for (const replay of replays) {
  replay.addEventListener('click', () => {
    const text = replay.parentElement
    const letters = text.querySelectorAll(`.${PROPS.LETTER}`)
    const listeners = []
    for (const letter of letters) {
      const listener = new Promise((resolve, reject) => {
        letter.addEventListener('animationend', () => resolve())
      })
      listeners.push(listener)
    }
    Promise.all(listeners)
      .then(() => {
        text.removeAttribute(PROPS.ANIMATED)    
      })
    text.setAttribute(PROPS.ANIMATED, true)
  })
}

const textListeners = []

for (const text of texts) {
  const letters = text.querySelectorAll(`.${PROPS.LETTER}`)

  const listeners = []

  for (const letter of letters) {
    const listener = new Promise((resolve, reject) => {
      letter.addEventListener('animationend', () => {
        resolve()
      })
    })
    listeners.push(listener)
  }  
  
  const textListener = Promise.all(listeners)
    .then(() => {
      Promise.resolve()
    })
 
  textListeners.push(textListener)
}

Promise.all(textListeners)
  .then(() => {
    for (const text of texts) {
      text.removeAttribute(PROPS.ANIMATED)
    }
  })
              
            
!
999px

Console