- var items = [
- 'Home',
- 'About',
- 'Work',
- 'Playground',
- 'Contact',
- ]

.Container
  ul.Menu
    each item in items
      li.Menu-item
        a.Menu-link(href='#')
          span.Menu-bullet
          span.Menu-chars.Menu-chars--default
            each letter in item
              span= letter
          span.Menu-chars.Menu-chars--active
            each letter in item
              span= letter
View Compiled
$font-family = 'Quicksand', sans-serif
$background-color = #EEAF1B

$link-color = lighten($background-color, 90%)
$link-active-color = darken($background-color, 60%)
$link-transition-duration = 250ms

$chars-fz = 24px
$chars-fw = 300
$chars-active-fz = 36px
$chars-active-fw = 700
$chars-height = $chars-active-fz * 5/3
$chars-max-length = 15
$chars-max-y = 75%
$chars-duration = 350ms
$chars-in-delay = 100ms
$chars-stagger = 20ms
$chars-easing = cubic-bezier(0.175, 0.885, 0.32, 1.275)

$bullet-w = 18px
$bullet-h = 1px
$bullet-active-h = 4px
$bullet-ml = 10px
$bullet-max-x = 400%
$bullet-duration = 250ms
$bullet-in-delay = 195ms
$bullet-in-easing = cubic-bezier(0.6, -0.28, 0.735, 0.045)
$bullet-out-easing = cubic-bezier(0.175, 0.885, 0.32, 1.275)

chars-initial-in()
    span
        transform translateY($chars-max-y)
        opacity 0
    for index in 1..$chars-max-length
        $delay = $chars-in-delay + ($chars-stagger * (index - 1))
        span:nth-child({index})
            animation $chars-duration $delay chars-in $chars-easing forwards,
                      $chars-duration $delay fade-in $chars-easing forwards

chars-initial-out()
    span
        transform translateY(0%)
        opacity 1
    for index in 1..$chars-max-length
        $delay = $chars-stagger * (index - 1)
        span:nth-child({index})
            animation $chars-duration $delay chars-out $chars-easing forwards,
                      $chars-duration $delay fade-out $chars-easing forwards

html
body
    width 100%
    height 100%
    font-family $font-family
    -webkit-font-smoothing antialiased
    -moz-osx-font-smoothing grayscale
    background-color $background-color

.Container
    display flex
    align-items center
    justify-content center
    width 100%
    height 100%

.Menu
    opacity 0
    animation fade-in 500ms 500ms forwards

.Menu-link
    position relative
    display block
    padding-left ($bullet-w + $bullet-ml)
    color $link-color
    text-decoration none
    transition color $link-transition-duration ease-out
    -webkit-tap-highlight-color transparent
    &.Menu-link--focus
    &:hover
        .Menu-chars--default
            chars-initial-out()
        .Menu-chars--active
            chars-initial-in()

.Menu-link--active
    color $link-active-color
    cursor default
    .Menu-bullet
        opacity 0
        animation bullet-in $bullet-duration $bullet-in-delay $bullet-out-easing forwards,
                  fade-in $bullet-duration $bullet-in-delay $bullet-out-easing forwards
    &.Menu-link--focus
        .Menu-bullet
            top 'calc(50% - %s)' % ($bullet-active-h / 2)
            height $bullet-active-h
            border-radius ($bullet-active-h / 2)

.Menu-bullet
    position absolute
    left 0
    width $bullet-w
    height $bullet-h
    background-color $link-active-color
    transition top $bullet-duration,
               height $bullet-duration
    transform-origin center right
    opacity 1
    animation bullet-out $bullet-duration $bullet-in-easing forwards
    if $bullet-h > 1
        top 'calc(50% - %s)' % ($bullet-h / 2)
        border-radius ($bullet-h / 2)
    else
        top 50%

.Menu-chars
    display flex
    align-items center
    height $chars-height
    pointer-events none
    span
        display inline-block

.Menu-chars--default
    font-size $chars-fz
    font-weight $chars-fw
    chars-initial-in()

.Menu-chars--active
    font-size $chars-active-fz
    font-weight $chars-active-fw
    margin-top -($chars-height)
    chars-initial-out()

@keyframes fade-in
    0%
        opacity 0
    100%
        opacity 1

@keyframes fade-out
    0%
        opacity 1
    100%
        opacity 0

@keyframes chars-in
    0%
        transform translateY(-($chars-max-y))
    100%
        transform translateY(0%)

@keyframes chars-out
    0%
        transform translate(0%)
    100%
        transform translateY($chars-max-y)

@keyframes bullet-in
    0%
        transform translateX(-($bullet-max-x))
    100%
        transform translateX(0%)

@keyframes bullet-out
    0%
        transform scaleX(1)
    100%
        transform scaleX(0)
View Compiled
class Menu {
    constructor() {
        this._links = [
            ...document.querySelectorAll('.Menu-link')
        ]
        this._activeItem = this._findActive()
    }

    init() {
        const link = this._findActive()
        this._setActive(link)
        this._bindEvents()
    }

    _findActive() {
        const { _links } = this
        const active = _links.filter(link => {
            return link.classList.contains('Menu-link--active')
        })[0]
        if (active) return active
        else return _links[0]
    }

    _setActive(link) {
        link.classList.add('Menu-link--active')
        link.classList.add('Menu-link--focus')
        this._activeItem = link
    }

    _updateActive(e) {
        const { _activeItem } = this
        _activeItem.classList.remove('Menu-link--active')
        _activeItem.classList.remove('Menu-link--focus')
        this._setActive(e.target)
    }

    _bindEvents() {
        this._links.forEach(link => {
            link.addEventListener('mouseenter', this._removeFocus.bind(this))
            link.addEventListener('mouseleave', this._addFocus.bind(this))
            link.addEventListener('click', this._updateActive.bind(this))
        })
    }

    _removeFocus(e) {
        const { _activeItem } = this
        if (e.target !== _activeItem) {
            _activeItem.classList.remove('Menu-link--focus')
        }
    }

    _addFocus() {
        const { _activeItem } = this
        _activeItem.classList.add('Menu-link--focus')
    }
}

const menu = new Menu()
menu.init()
View Compiled
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.