h1.status 408
.chat
  .header.chat__header
    .header__title New Request
    .header__members
      .header__member
      .header__member.header__member--server
  .message.message--browser
    .message__content Hey Server 👋
  .message.message--server
    .message__content Hey Browser 👋
  .message.message--browser
    .message__content Got a quick one for ya 😅
  .message.message--server
    .message__content Sure 👍
  .message.message--server
    .message__content Fire away 🔫
  .message.message--browser
    .message__content Jus' a sec 🕥
  .message.message--server
    .message__content OK 👌
  .message.message--server
    .message__content ???
  .message.message--server
    .message__content You still there??
  .message.message--server
    .message__content Ughh 😞
  .message.message--left
    .message__content Server left the chat 💅
  .message.message--typing.message--browser
    .message__content
      div
      div
      div
View Compiled
@import url('https://fonts.googleapis.com/css?family=Lato')

*
  box-sizing border-box

$primary = #e0bbe4
$secondary = #fec8d8
body
  font-family 'Lato', sans-serif
  margin 0
  padding 0
  min-height 100vh
  display flex
  align-items center
  justify-content center
  flex-direction column
  background lighten($secondary, 60%)

h1
  margin 0


.status
  font-size 100px
  left 50%
  top 50%
  color lighten($secondary, 30%)
  text-shadow 0 0 5px $secondary
  margin-bottom 2rem

  @media(min-width 768px)
    font-size 200px
    transform translate(-50%, -50%) rotate(-90deg) translate(0, -140px) translate(0, -50%)
    position absolute


.chat
  border-radius 15px
  box-shadow 0 0 20px $primary
  background #fff
  width 280px
  height 250px
  display flex
  justify-content flex-end
  flex-direction column
  overflow hidden
  position relative
  padding 0 15px

  @media(min-width 768px)
    width 280px
    height 400px

  &__header
    position absolute
    top 0
    left 0


.header
  align-items center
  border-radius 15px 15px 0 0
  background darken($primary, 25%)
  color #fff
  display flex
  height 60px
  padding 20px
  width 100%
  text-transform uppercase
  z-index 2
  transform translate(0, 0)

  &__members
    display flex
    align-items center
    padding-left 15px

  &__member
    height 30px
    width 30px
    border-radius 100%
    background linear-gradient(25deg, grey, white)
    background-image url(https://source.unsplash.com/JelL3CneNDY/50x50)
    background-size cover
    margin-right 4px

    &--server
      background-image url(https://source.unsplash.com/uWaRsN-CqY0/50x50)


.message
  display none
  flex 0 0 auto
  margin-bottom 5px

  &--browser
    justify-content flex-start

    .message__content
      background #eee

  &--server
    justify-content flex-end

    .message__content
      background $primary
      color #fff
      justify-content flex-end
      text-align right


  &__content
    border-radius 15px
    min-height 50px
    align-items center
    display inline-flex
    background white
    padding 10px

  &--left
    justify-content center

    .message__content
      background #ffffff
      color #ccc

  &--typing .message__content
    div
      animation wave .5s ease infinite
      border-radius 100%
      height 8px
      width 8px
      background darken(#eee, 25%)
      margin 2px

      for $d in (1..3)
        &:nth-of-type({$d})
          animation-delay $d * .1s


    @keyframes wave
      50%
        transform translate(0, -150%)

View Compiled
const FourOhEight = new TimelineMax({ repeatDelay: 3 })
const typing = document.querySelector('.message--typing')
const left = document.querySelector('.message--left')
const avatar = document.querySelector('.header__member--server')
const messages = document.querySelectorAll(
  '.message:not(.message--typing):not(.message--left)'
)
const msgDuration = 0.25
const random = (max, min) => Math.floor(Math.random() * (max - min + 1)) + min
const generateChatTL = messages => {
  const chatTL = new TimelineMax()
  let stagger = 0
  for (let m = 0; m < messages.length; m++) {
    const message = messages[m]
    const differentRecipient =
      message.classList.contains('message--server') &&
      messages[m - 1] &&
      !messages[m - 1].classList.contains('message--server')
    if (
      m !== 0 &&
      m !== messages.length - 2 &&
      (m === 5 || m === 6 || m === 7 || Math.random() > 0.5)
    ) {
      const deliberation =
        m === 5 || m === 6 || m === 7 ? Math.random() * 3 : Math.random()
      chatTL.set(typing, { display: 'flex' })
      chatTL.add(
        TweenMax.to(typing, deliberation, { display: 'none' }),
        stagger
      )
      stagger += deliberation
    }
    chatTL.set(message, { scale: 0 })
    chatTL.add(
      TweenMax.to(message, msgDuration, {
        scale: 1,
        onStart: () => (message.style.display = 'flex'),
      }),
      stagger
    )
    let step = differentRecipient ? random(3, 1) : Math.random()
    if (m === messages.length - 4 || m === messages.length - 2)
      step = random(4, 2)
    stagger += m === 0 ? 2 : step
  }
  return chatTL
}

FourOhEight.set([...messages, left], { display: 'none', scale: 0 })
  .add(generateChatTL(messages))
  .set(left, { scale: 0 })
  .add(
    TweenMax.to(left, msgDuration, {
      delay: 2,
      scale: 1,
      onStart: () => (left.style.display = 'flex'),
    })
  )
  .add(
    TweenMax.to(avatar, msgDuration, {
      scale: 0,
    })
  )
  .repeat(-1)
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js