ExamplesScroll

Scroll Direction: Hide Header

A sticky header that hides when scrolling down and reappears when scrolling up. Uses useMotionValueEvent to detect scroll direction.

Vue

Source code

<script setup lang="ts">
import { motion, useScroll, useMotionValueEvent } from "motion-v"
import { ref } from "vue"

const { scrollY } = useScroll()
const hidden = ref(false)

useMotionValueEvent(scrollY, "change", (current: number) => {
    const previous = scrollY.getPrevious() ?? 0
    if (current > previous && current > 150) {
        hidden.value = true
    } else {
        hidden.value = false
    }
})
</script>

<template>
    <div id="example">
        <motion.header
            class="header"
            :animate="{
                y: hidden ? -140 : 0,
                opacity: hidden ? 0 : 1,
            }"
            :transition="{ duration: 0.3, ease: 'easeInOut' }"
        >
            <div class="header-content">
                <div class="logo">
                    <svg
                        xmlns="http://www.w3.org/2000/svg"
                        viewBox="0 0 1260 454"
                        class="logo-icon"
                        fill="currentColor"
                    >
                        <path d="M475.753 0L226.8 453.6L0 453.6L194.392 99.4116C224.526 44.5081 299.724 0 362.353 0L475.753 0Z" />
                        <path d="M1031.93 113.4C1031.93 50.7709 1082.7 0 1145.33 0C1207.96 0 1258.73 50.7709 1258.73 113.4C1258.73 176.029 1207.96 226.8 1145.33 226.8C1082.7 226.8 1031.93 176.029 1031.93 113.4Z" />
                        <path d="M518.278 0L745.078 0L496.125 453.6L269.325 453.6L518.278 0Z" />
                        <path d="M786.147 0L1012.95 0L818.555 354.188C788.422 409.092 713.223 453.6 650.594 453.6L537.194 453.6L786.147 0Z" />
                    </svg>
                </div>
                <nav>
                    <a href="#">Docs</a>
                    <a href="#">Examples</a>
                    <a href="#">Blog</a>
                </nav>
            </div>
        </motion.header>

        <main class="content">
            <section class="hero">
                <p>
                    Scroll down to hide header.<br />Scroll up to reveal
                    header.
                </p>
            </section>

            <section
                v-for="i in 6"
                :key="i"
                class="placeholder-section"
            >
                <svg
                    xmlns="http://www.w3.org/2000/svg"
                    viewBox="0 0 1260 454"
                    class="placeholder-logo"
                    fill="currentColor"
                >
                    <path d="M475.753 0L226.8 453.6L0 453.6L194.392 99.4116C224.526 44.5081 299.724 0 362.353 0L475.753 0Z" />
                    <path d="M1031.93 113.4C1031.93 50.7709 1082.7 0 1145.33 0C1207.96 0 1258.73 50.7709 1258.73 113.4C1258.73 176.029 1207.96 226.8 1145.33 226.8C1082.7 226.8 1031.93 176.029 1031.93 113.4Z" />
                    <path d="M518.278 0L745.078 0L496.125 453.6L269.325 453.6L518.278 0Z" />
                    <path d="M786.147 0L1012.95 0L818.555 354.188C788.422 409.092 713.223 453.6 650.594 453.6L537.194 453.6L786.147 0Z" />
                </svg>
            </section>
        </main>
    </div>
</template>

<style>
#example {
    height: auto;
    overflow: visible;
}

.header {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    padding-top: 80px;
    background: rgba(11, 16, 17, 0.9);
    border-bottom: 1px solid var(--border);
    z-index: 100;
    backdrop-filter: blur(12px);
}

.header-content {
    max-width: 900px;
    margin: 0 auto;
    height: 60px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 24px;
}

.logo {
    display: flex;
    align-items: center;
    color: var(--white);
}

.logo-icon {
    height: 18px;
    width: auto;
}

nav {
    display: flex;
    gap: 32px;
}

nav a {
    color: var(--white);
    text-decoration: none;
    font-size: 14px;
    opacity: 0.6;
    transition: opacity 0.2s;
}

nav a:hover {
    opacity: 1;
}

.content {
    padding-top: 140px;
}

.hero {
    height: 40vh;
    display: flex;
    justify-content: center;
    align-items: center;
    text-align: center;
    padding: 20px;
}

.hero p {
    font-size: 16px;
    color: var(--white);
    margin: 0;
}

.placeholder-section {
    height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
}

.placeholder-logo {
    width: clamp(200px, 40vw, 400px);
    height: auto;
    color: var(--white);
    opacity: 0.04;
}

@media (max-width: 600px) {
    nav {
        gap: 20px;
    }

    nav a {
        font-size: 13px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .header {
        transition: none;
    }
}
</style>

Related examples

Latest in Vue