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

              
                
<div id="app">
  <div class="blanket" v-if="!loaded"></div>
  <button class="mail-open" v-on:click="openInbox" v-if="!messages.length"><div class="icon icon--mail">
<svg width="100%" viewBox="0 0 24 24">
  <path d="M20,8L12,13L4,8V6L12,11L20,6M20,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V6C22,4.89 21.1,4 20,4Z" />
</svg>
</div>
  </button>
  <div class="mail-inbox" v-if="messages.length">
    <header class="mail-screen-header">
      <button class="mail-close" v-on:click="closeInbox"><svg class='icon icon--back' preserveAspectRatio="xMinYMin" height="24" width="24" viewBox="0 0 24 24">
    <path d="M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z"></path>
</svg>
      </button>
      <h1 class="mail-header__title">Inbox</h1>
      <button class="mail-message__action" v-on:click="openSettings"><svg preserveAspectRatio="xMinYMin" class="icon icon--gear" height="24" width="24" viewBox="0 0 24 24">
    <path d="M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.67 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z" />
</svg>
      </button>
    </header>
    <div class="mail-settings" v-if="openingSettings || settingsOpen">
      <header class="mail-screen-header">
        <button class="mail-close" v-on:click="closeSettings"><svg preserveAspectRation="xMinYMin" class="icon icon--close" height="24" width="24" viewBox="0 0 24 24">
    <path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" />
</svg>
        </button>
        <h1 class="mail-header__title">Settings</h1>
      </header>
      <div class="mail-screen-content">
        <div class="mail-settings__setting">
          <input type="color" id="theme" :value="theme" v-on:change="updateTheme"/>
          <label for="theme">Theme</label>
        </div>
      </div>
    </div>
    <div class="mail-container" ref="mailContainer" v-if="activeMessage" v-bind:class="{'mail-message--opening': activeMessage &amp;&amp; !messageOpen, 'mail-message--open': messageOpen}">
      <header class="mail-screen-header mail-screen-header--fake" ref="fakeHeader">
        <button class="mail-close" v-on:click="closeInbox"><svg class='icon icon--back' preserveAspectRatio="xMinYMin" height="24" width="24" viewBox="0 0 24 24">
    <path d="M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z"></path>
</svg>
        </button>
        <h1 class="mail-header__title">Inbox</h1>
        <button class="mail-message__action" v-on:click="openSettings"><svg preserveAspectRatio="xMinYMin" class="icon icon--gear" height="24" width="24" viewBox="0 0 24 24">
    <path d="M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.67 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z" />
</svg>
        </button>
      </header>
      <ul class="fake-messages fake-messages--top" ref="fakeTop" v-if="activeMessage &amp;&amp; !messageOpen" v-bind:style="{bottom: fakeOnePos}">
        <li class="mail-message" v-for="(message, idx) in messages.slice(activeMessageIndex - 8 &gt; 0 ? activeMessageIndex - 8 : 0, activeMessageIndex)">
          <button class="mail-message"><img class="mail-message__avatar" :src="message.from.avatar"/>
            <div class="mail-message__info">
              <div class="mail-message__sender">{{message.from.name}}</div>
              <div class="mail-message__subject">{{message.subject}}</div>
              <div class="mail-message__timestamp">{{message.received}}</div>
            </div>
          </button>
        </li>
      </ul>
      <ul class="fake-messages fake-messages--bottom" ref="fakeBottom" v-if="activeMessage &amp;&amp; !messageOpen" v-bind:style="{top: fakeTwoPos}">
        <li class="mail-message" v-for="(message, idx) in messages.slice(activeMessageIndex + 1, activeMessageIndex + 9 &lt; messages.length ? activeMessageIndex + 9 : messages.length)">
          <button class="mail-message"><img class="mail-message__avatar" :src="message.from.avatar"/>
            <div class="mail-message__info">
              <div class="mail-message__sender">{{message.from.name}}</div>
              <div class="mail-message__subject">{{message.subject}}</div>
              <div class="mail-message__timestamp">{{message.received}}</div>
            </div>
          </button>
        </li>
      </ul>
      <header class="mail-screen-header mail-container__header">
        <button class="mail-message__action" v-on:click="closeMessage"><svg class='icon icon--back' preserveAspectRatio="xMinYMin" height="24" width="24" viewBox="0 0 24 24">
    <path d="M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z"></path>
</svg>
        </button>
        <button class="mail-message__action"><svg preserveAspectRatio="xMinYMin" class='icon icon--reply' height="24" width="24"  viewBox="0 0 24 24">
  <path d="M9,22A1,1 0 0,1 8,21V18H4A2,2 0 0,1 2,16V4C2,2.89 2.9,2 4,2H20A2,2 0 0,1 22,4V16A2,2 0 0,1 20,18H13.9L10.2,21.71C10,21.9 9.75,22 9.5,22V22H9Z"></path>
</svg>
        </button>
        <button class="mail-message__action"><svg preserveAspectRatio="xMinYMin" class='icon icon--star' height="24" width="24"  viewBox="0 0 24 24">
  <path d="M12,17.27L18.18,21L16.54,13.97L22,9.24L14.81,8.62L12,2L9.19,8.62L2,9.24L7.45,13.97L5.82,21L12,17.27Z"></path>
</svg>
        </button>
        <button class="mail-message__action"><svg preserveAspectRatio="xMinYMin" class="icon icon--trash" height="24" width="24"  viewBox="0 0 24 24">
    <path d="M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z" />
</svg>
        </button>
        <button class="mail-message__action"><svg preserveAspectRatio="xMinYMin" class='icon icon--warning' height="24" width="24"  viewBox="0 0 24 24">
  <path d="M13,14H11V10H13M13,18H11V16H13M1,21H23L12,2L1,21Z"></path>
</svg>
        </button>
      </header>
      <div class="mail-message__header mail-container__message-header" ref="mailHeader" v-bind:style="{top: messageTop + 'px'}"><img class="mail-message__avatar" ref="mailAvatar" :src="activeMessage.from.avatar"/>
        <div class="mail-message__info">
          <div class="mail-message__sender" ref="mailSender">{{activeMessage.from.name}}</div>
          <div class="mail-message__subject" ref="mailSubject">{{activeMessage.subject}}</div>
          <div class="mail-message__timestamp" ref="mailTimestamp">{{activeMessage.received}}</div>
        </div>
      </div>
      <article class="mail-message__message" ref="mailContent">{{activeMessage.message}}</article>
    </div>
    <ul class="mail-messages mail-messages--main" v-bind:class="{'mail-messages--load': !mailOpened}">
      <li v-for="(message, idx) in messages">
        <button class="mail-message" :id="'message--' + idx" v-on:click="openMessage(message, idx)"><img class="mail-message__avatar" :src="message.from.avatar"/>
          <div class="mail-message__info">
            <div class="mail-message__sender">{{message.from.name}}</div>
            <div class="mail-message__subject">{{message.subject}}</div>
            <div class="mail-message__timestamp">{{message.received}}</div>
          </div>
        </button>
      </li>
    </ul>
    <button class="mail-compose-button" v-on:click="openComposer" v-if="!composerOpen"><svg class='icon icon--compose' preserveAspectRatio="xMinYMin" height="24" width="24" viewBox="0 0 24 24">
  <path d="M20.71,4.04C21.1,3.65 21.1,3 20.71,2.63L18.37,0.29C18,-0.1 17.35,-0.1 16.96,0.29L15,2.25L18.75,6M17.75,7L14,3.25L4,13.25V17H7.75L17.75,7Z"></path>
</svg>
    </button>
    <div class="mail-composer" v-if="openingComposer || composerOpen">
      <header class="mail-screen-header">
        <button class="mail-message__action" v-on:click="closeComposer"><svg preserveAspectRation="xMinYMin" class="icon icon--close" height="24" width="24" viewBox="0 0 24 24">
    <path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" />
</svg>
        </button>
        <h1 class="mail-header__title mail-composer__title">Compose</h1>
        <button class="mail-message__action"><svg preserveAspectRatio="xMinYMin" class='icon icon--paperclip' height="24" width="24"  viewBox="0 0 24 24">
    <path d="M7.5,18A5.5,5.5 0 0,1 2,12.5A5.5,5.5 0 0,1 7.5,7H18A4,4 0 0,1 22,11A4,4 0 0,1 18,15H9.5A2.5,2.5 0 0,1 7,12.5A2.5,2.5 0 0,1 9.5,10H17V11.5H9.5A1,1 0 0,0 8.5,12.5A1,1 0 0,0 9.5,13.5H18A2.5,2.5 0 0,0 20.5,11A2.5,2.5 0 0,0 18,8.5H7.5A4,4 0 0,0 3.5,12.5A4,4 0 0,0 7.5,16.5H17V18H7.5Z" />
</svg>
        </button>
        <button class="mail-message__action"><svg preserveAspectRatio="xMinYMin" class='icon icon--send' height="24" width="24"  viewBox="0 0 24 24">
    <path d="M2,21L23,12L2,3V10L17,12L2,14V21Z" />
</svg>
        </button>
      </header>
      <form class="mail-screen-content">
        <input type="text" placeholder="To"/>
        <input type="text" placeholder="Subject"/>
        <textarea type="text" placeholder="Compose"></textarea>
      </form>
    </div>
  </div>
</div>
              
            
!

CSS

              
                @import url('https://fonts.googleapis.com/css?family=Lato:300,400')

$bg = #6c7a89
$appWhite = #ecf0f1
$appAccent = #a7dde9
$textColor = #707070
$borderColor = lighten(#b5c5c9, 50%)
$textColorSecondary = #7e7e7e
$animDuration = .15s

:root
  --accent $appAccent

*
  box-sizing border-box
  outline 0

html
body
  align-items      center
  background       linear-gradient(45deg, #f39c12, #913d88)
  color $textColor
  display          flex
  flex-direction    column
  font-size        16px
  font-family 'Lato', sans-serif
  justify-content  center
  margin           0
  overflow          auto
  padding          0
  width            100vw
  min-height       100vh

label
  color $textColor

input
label
  cursor pointer

input[type="color"]
  height 40px
  width 40px
  border 0
  margin-right 10px

form
  width 100%
  padding 0 10px 10px
  display flex
  flex-direction column
  margin 0

  input
  textarea
    border-radius 4px
    border 0
    margin-bottom 4px
    padding 4px 12px
    font-family 'Lato', sans-serif
    width 100%
    line-height 30px

    &::placeholder
      color #c3c3c3

  textarea
    flex 1
    resize none

button
  background-color var(--accent)
  cursor pointer
  border 0
  display flex
  justify-content center
  align-items center

.blanket
  background-color $appAccent
  z-index 10
  position absolute
  top 0
  right 0
  bottom 0
  left 0

  &:after
    box-sizing border-box
    content ''
    height 50px
    width 50px
    border 5px solid white
    border-radius 100%
    border-left-color transparent
    border-right-color transparent
    position absolute
    top 50%
    left 50%
    margin-left -25px
    margin-top -25px
    animation rotate .5s infinite linear


.mail-screen-content
  // padding 10px
  padding-left 10px
  width 100%
  height 100%
  overflow auto
  position relative

.icon
  fill $appWhite
  position absolute
  height 24px
  width 24px

  &:hover
    fill darken($appWhite, 2%)


#app
  height 100vh
  width 100vw
  position relative
  background-color var(--accent)
  overflow hidden

  @media(min-width 768px)
    height 480px
    width 320px

.mail-inbox
  height 100%
  background-color $appWhite

.mail-settings
  height 100%
  width 100%
  background-color $appWhite
  position absolute
  top 0
  left 0
  z-index 3

  &__setting
    display flex
    flex-direction row
    align-items center

.mail-close
  position absolute
  left 0
  height 50px
  width 50px

.mail-header__title
  margin 0
  padding 0
  line-height 50px
  font-size 1.2rem
  color $appWhite
  flex 1
  text-align left
  padding-left 54px

.mail-messages
.fake-messages
  background $appWhite
  padding 0
  margin 0
  list-style none
  overflow auto
  position absolute
  top 50px
  left 0
  right 0
  bottom 0
  padding-bottom 50px

.fake-messages
  bottom auto
  z-index 2
  top auto
  padding 0


/**
  * Mail messages should animate on first load
*/
.mail-messages--load
  .mail-message
    opacity 0
    animation fadeIn $animDuration ease 0s
    animation-fill-mode forwards

  for $message in (1..15)
    li:nth-child({$message})
      button
        animation-delay ($message * .1s)


.mail-screen-header
  background-color var(--accent)
  position relative
  height 50px
  display flex
  align-items center
  justify-content flex-end
  overflow hidden
  width 100%

  button:first-child
    position absolute
    left 0

  &--fake
    z-index 3

/**
  * Message styling
*/
.mail-message--focus .mail-message__info
  height 60px
  padding-right 0

  .mail-message__subject
    white-space pre-wrap

.mail-container
  background $appWhite
  position absolute
  top 0
  right 0
  bottom 0
  left 0

  &__header
    position absolute
    top 0

  &__message-header
    padding 10px 0 10px 10px
    top 0

    .mail-message__avatar
      height 50px
      width 50px
    .mail-message__subject
      overflow visible
      white-space initial
      overflow initial
      font-weight bolder
      font-size 1.25rem
    .mail-message__sender
      font-size 0.75rem
    .mail-message__timestamp
      transform translateY(-20px)

button.mail-message
  padding 0
  padding-left 10px

  &:hover
    background-color darken($appWhite, 2%)

.mail-message
  background-color $appWhite
  height 60px
  width 100%

  &__action
    height 50px
    width 50px

  &--opening
  &--open
    position absolute
    z-index 3
    border 0


  &__header
    display flex
    flex-direction row
    align-items center
    min-height 60px
    position absolute
    width 100%
    padding-left 10px
    background $appWhite
    top 50px

  &__content
    overflow auto

  &__message
    color $textColorSecondary
    font-weight 300
    padding 0 10px 50px 10px
    opacity 0
    overflow auto
    position absolute
    top 100%

  &__avatar
    height 34px
    width  34px
    background-color var(--accent)
    border-radius 100%
    overflow hidden

  &__info
    display flex
    flex-direction column
    align-items flex-start
    flex 1
    padding-left 10px
    min-height 60px
    justify-content center
    padding-right 60px
    position relative
    overflow hidden

  &__timestamp
    height 50px
    width 50px
    position absolute
    right 10px
    top 5px
    font-size 0.75rem
    display flex
    align-items center
    justify-content center
    color $textColor

  &__sender
    font-size 1rem
    font-weight 400
    color $textColor

  &__subject
    font-size .75rem
    font-weight 300
    color $textColorSecondary
    white-space nowrap
    text-overflow ellipsis
    overflow hidden
    width 100%
    text-align left



.mail-compose-button
  height 44px
  width 44px
  background-color var(--accent)
  border-radius 100%
  border 0
  cursor pointer
  bottom 10px
  right 10px
  position absolute
  z-index 2
  box-shadow 0 2px 2px #c3c3c3


.mail-composer
  height 44px
  width 44px
  border-radius 100%
  border none
  position absolute
  bottom 10px
  right 10px
  transform-origin bottom right
  background-color var(--accent)
  display flex
  flex-direction column
  z-index 4

.mail-open
  height 50px
  width 50px
  position absolute
  top 50%
  left 50%
  margin-left -25px
  margin-top -25px
  border-radius 100%
  border 5px solid $appWhite
  animation pulse 4s infinite ease
  background-color var(--accent)

  .icon:hover
    fill $appWhite


@keyframes pulse
  0%, 100%
    transform scale(1)
    box-shadow 0px 2px 2px black
  50%
    transform scale(1.05)
    box-shadow 0px 4px 4px black

@keyframes fadeIn
  from
    opacity 0
  to
    opacity 1

@keyframes rotate
  to
    transform rotate(360deg)
              
            
!

JS

              
                const ANIMATION_DURATION = 0.25
const CLASSES = {
  COMPOSER: 'mail-composer',
  HEADER: 'mail-screen-header',
  SETTINGS: 'mail-settings',
}
let messages = []
for (let i = 0; i < 15; i++) {
  messages.push({
    from: {
      avatar: faker.image.avatar(),
      name: faker.name.findName(),
    },
    received: moment(faker.date.recent()),
    subject: faker.company.bs(),
    message: faker.lorem.paragraphs(Math.floor(Math.random() * 10 + 2)),
  })
}
messages = messages.sort((a, b) => a.received.diff(b.received)).reverse()

for (let message of messages) {
  if (message.received.isAfter(moment().subtract(5, 'hours'))) {
    message.received = message.received.format('HH:mm')
  } else {
    message.received = message.received.format('D MMM')
  }
}

const app = new Vue({
  el: '#app',
  data: {
    theme: '#a7dde9',
    openingComposer: false,
    closingComposer: false,
    composerOpen: false,
    messageOpen: false,
    activeMessage: undefined,
    activeMessageIndex: undefined,
    fakeOnePos: undefined,
    fakeTwoPos: undefined,
    messageTop: undefined,
    mailOpened: false,
    openingSettings: false,
    closingSettings: false,
    settingsOpen: false,
    loaded: false,
    messages: [],
  },
  mounted: function() {
    this.loaded = true
  },
  methods: {
    openComposer: function() {
      this.openingComposer = !this.openingComposer
      this.$nextTick(() => {
        const composer = document.querySelector(`.${CLASSES.COMPOSER}`)
        const composerTl = new TimelineMax({
          onComplete: () => {
            this.openingComposer = false
            this.composerOpen = true
            this.mailOpened = true
          },
        })
        composerTl.add(
          TweenMax.to(composer, ANIMATION_DURATION, {
            right: 0,
            bottom: 0,
            height: '100%',
            width: '100%',
            borderRadius: 0,
          })
        )
      })
    },
    closeComposer: function() {
      this.closingComposer = !this.closingComposer
      const composer = document.querySelector(`.${CLASSES.COMPOSER}`)
      const composerTl = new TimelineMax({
        onComplete: () => {
          this.composerOpen = false
          this.closingComposer = false
        },
      })
      composerTl.add(
        TweenMax.to(composer, ANIMATION_DURATION, {
          bottom: 10,
          right: 10,
          height: 44,
          width: 44,
          borderRadius: '100%',
        })
      )
    },
    openInbox: function() {
      this.messages = messages
    },
    closeInbox: function() {
      this.messages = []
      this.mailOpened = false
    },
    closeMessage: function() {
      TweenMax.to(this.$refs.mailContainer, ANIMATION_DURATION, {
        onComplete: () => {
          this.activeMessage = null
          this.activeMessageIndex = null
          this.messageOpen = false
        },
        x: '-100%',
      })
    },
    openMessage: function(message, idx) {
      const el = document.getElementById(`message--${idx}`)
      const list = document.querySelector('.mail-messages--main')
      const header = document.querySelector(`.${CLASSES.HEADER}`)
      this.mailOpened = true
      this.activeMessage = message
      this.activeMessageIndex = idx
      this.fakeTwoPos =
        el.offsetTop + el.offsetHeight + header.offsetHeight - list.scrollTop
      this.messageTop = el.offsetTop + header.offsetHeight - list.scrollTop
      this.fakeOnePos = list.offsetHeight - el.offsetTop + list.scrollTop
      this.$nextTick(() => {
        const el = document.querySelector('.mail-message--opening')
        const {
          fakeTop,
          fakeBottom,
          fakeHeader,
          mailHeader,
          mailAvatar,
          mailSubject,
          mailSender,
          mailTimestamp,
          mailContainer,
          mailContent,
        } = this.$refs
        const fakeHeaderPos = fakeHeader.getBoundingClientRect()
        const mailHeaderPos = mailHeader.getBoundingClientRect()
        const mailContainerPos = mailContainer.getBoundingClientRect()
        const openTl = new TimelineMax({
          onComplete: () => {
            this.messageOpen = true
          },
        })
        openTl
          // Move fakes out of the way
          .to(fakeHeader, ANIMATION_DURATION, { y: `-${this.messageTop}px` }, 0)
          .to(fakeTop, ANIMATION_DURATION, { bottom: '100%' }, 0)
          .to(fakeBottom, ANIMATION_DURATION, { top: '100%' }, 0)
          // Move header to top and change sizing
          .to(
            mailHeader,
            ANIMATION_DURATION,
            { y: `-${this.messageTop - fakeHeaderPos.height}`, paddingTop: 10 },
            0
          )
          .from(
            mailAvatar,
            ANIMATION_DURATION,
            { height: '34px', width: '34px' },
            0
          )
          .from(
            mailTimestamp,
            ANIMATION_DURATION,
            { fontSize: '0.75rem', y: 0 },
            0
          )
          .from(
            mailSubject,
            ANIMATION_DURATION,
            {
              overflow: 'hidden',
              fontSize: '0.75rem',
              fontWeight: '400',
              color: '#7e7e7e',
            },
            0
          )
          .from(mailSender, ANIMATION_DURATION, { fontSize: '1rem' }, 0)
          // Animate in the article
          .to(
            mailContent,
            ANIMATION_DURATION,
            {
              height: `${mailContainerPos.height -
                (mailHeaderPos.height + fakeHeaderPos.height)}px`,
              opacity: 1,
              y: `-${mailContainerPos.height -
                (mailHeaderPos.height + fakeHeaderPos.height)}px`,
            },
            0
          )
      })
    },
    openSettings: function() {
      this.openingSettings = !this.openingSettings
      this.$nextTick(() => {
        const el = document.querySelector(`.${CLASSES.SETTINGS}`)
        TweenMax.from(el, ANIMATION_DURATION, {
          onComplete: () => {
            this.openingSettings = false
            this.settingsOpen = true
          },
          x: '100%',
        })
      })
    },
    closeSettings: function() {
      const el = document.querySelector(`.${CLASSES.SETTINGS}`)
      TweenMax.to(el, ANIMATION_DURATION, {
        onComplete: () => {
          this.openeingSettings = false
          this.settingsOpen = false
        },
        x: '100%',
      })
    },
    updateTheme: function(e) {
      this.theme = e.target.value
      document.documentElement.style.setProperty('--accent', e.target.value)
    },
  },
})
              
            
!
999px

Console