Lytbox Academy Logo
Community1:1 CoachingBlogContact
Join the AcademySign In
Elementor Tuts

Changing Headers On Scroll With Elementor (2026 SEO Safe Version)

jeffrey at Lytbox
Table of Contents

Change your header on scroll in Elementor without duplicate headers. A clean, SEO-safe approach.

Most header scroll effects in Elementor rely on duplicate headers or sticky hacks that work visually but create long-term problems like messy markup, harder maintenance, and unnecessary SEO risks.

In this tutorial, we’re taking a clean 2026 approach. We’ll change the header on scroll using one single header, no duplicates, no sticky conflicts, and no SEO compromises. The header scrolls away naturally, then reappears after a defined scroll point with a new style. By default, it shrinks into a compact version, and optionally it can switch into a floating pill-style header.

This tutorial is designed to be followed alongside the video below. The goal here is not to overwhelm you with explanations, but to give you a clear place to copy the code and follow the exact setup steps while following the video tutorial.

Step 1: Create Your Header Structure in Elementor

Start with a normal Elementor header. You only need one header for this entire setup. Next, add the class below to the outer container wrapping your header:

lytbox-header

This class is required. It tells the JavaScript and CSS which header to control.

Step 2: Add CSS Classes to Header Elements

First, we need to add two logos to your header. This way we can change logos on scroll. Logo 1, this is for the first header. Logo 2, this is for the second header that changes.

Inside the header add these classes to your logos (using the Image Widgets), WordPress Menu and Button widgets.

WordPress Menu Widget:

lytbox-nav

Button Widget:

lytbox-btn

Classes for your logos (watch the video below to see how all of this works if you’re feeling lost.)

logo-1
logo-2

That’s all you need for the default changing header behavior.

Step 3: Add the JavaScript Snippet

The JavaScript controls when the header hides, when it reveals, and when the header switches into its scrolled state.

Paste the JavaScript snippet below into your snippet plugin or site-wide JavaScript area.

(() => {
  // =========================
  // EDIT THESE VALUES
  // =========================
  const STICKY_AFTER_PX = 1000;   // when header reveal starts (sticky kicks in)
  const SCROLLED_AFTER_PX = 1000; // MUST be <= STICKY_AFTER_PX to avoid top strip flash
  const ANIM_MS = 180;            // keep synced with CSS var --lytbox-anim-ms
  const OVERLAY_MODE = true;      // TRUE if header overlays hero (absolute / negative margin)
  // =========================

  const header = document.querySelector('.lytbox-header');
  if (!header) return;

  document.documentElement.style.setProperty('--lytbox-anim-ms', `${ANIM_MS}ms`);

  // Admin bar offset (only affects logged-in admins)
  const getAdminOffset = () => {
    if (!document.body.classList.contains('admin-bar')) return 0;
    return window.innerWidth <= 782 ? 46 : 32;
  };

  const setAdminOffsetVar = () => {
    document.documentElement.style.setProperty('--lytbox-admin-offset', `${getAdminOffset()}px`);
  };

  // Spacer prevents page jump when header becomes fixed (disabled in overlay mode)
  const spacer = document.createElement('div');
  spacer.className = 'lytbox-header-spacer';
  spacer.style.height = '0px';
  spacer.style.pointerEvents = 'none';
  header.insertAdjacentElement('afterend', spacer);

  const setSpacer = (px) => {
    spacer.style.height = OVERLAY_MODE ? '0px' : `${px}px`;
  };

  let isSticky = false;
  let isAnimating = false;
  let scrolledClassOn = false;

  const setScrolled = (on) => {
    if (on === scrolledClassOn) return;
    header.classList.toggle('header-scrolled', on);
    scrolledClassOn = on;
  };

  const showSticky = () => {
    header.classList.add('lytbox-is-sticky', 'lytbox-no-trans');
    header.classList.remove('lytbox-show');

    void header.offsetHeight;

    requestAnimationFrame(() => {
      header.classList.remove('lytbox-no-trans');
      header.classList.add('lytbox-show');
    });
  };

  const hideSticky = () => {
    header.classList.remove('lytbox-show');
  };

  const makeSticky = () => {
    if (isSticky) return;
    isSticky = true;

    if (!OVERLAY_MODE) setSpacer(header.offsetHeight);

    // Ensure correct mode BEFORE first reveal
    setScrolled(window.scrollY >= SCROLLED_AFTER_PX);

    showSticky();
  };

  const removeSticky = () => {
    if (!isSticky) return;
    isSticky = false;

    hideSticky();

    window.setTimeout(() => {
      header.classList.remove('lytbox-is-sticky');
      setSpacer(0);

      // Reset at top
      header.classList.remove('header-scrolled');
      scrolledClassOn = false;
    }, ANIM_MS);
  };

  const swapScrolledWhileHidden = (shouldBeScrolled) => {
    if (!isSticky) return;
    if (isAnimating) return;
    if (shouldBeScrolled === scrolledClassOn) return;

    isAnimating = true;

    // Hide first
    hideSticky();

    window.setTimeout(() => {
      // Swap class while hidden
      setScrolled(shouldBeScrolled);

      void header.offsetHeight;

      // Show again
      requestAnimationFrame(() => header.classList.add('lytbox-show'));

      window.setTimeout(() => {
        isAnimating = false;
        if (!OVERLAY_MODE) setSpacer(header.offsetHeight);
      }, ANIM_MS);
    }, ANIM_MS);
  };

  const onScroll = () => {
    const shouldBeSticky = window.scrollY >= STICKY_AFTER_PX;

    if (shouldBeSticky) {
      if (!isSticky) makeSticky();
      swapScrolledWhileHidden(window.scrollY >= SCROLLED_AFTER_PX);
    } else {
      removeSticky();
    }
  };

  // Init
  setAdminOffsetVar();

  window.addEventListener('resize', () => {
    setAdminOffsetVar();
    if (isSticky && !OVERLAY_MODE) setSpacer(header.offsetHeight);
  });

  window.addEventListener('scroll', onScroll, { passive: true });
  onScroll();
})();

No changes are required unless you want to adjust scroll distance or animation timing.

Step 4: Add the CSS Snippet

The CSS controls how the header looks when it reappears: background, spacing, text size, button styling, and transitions.

Paste the CSS snippet below into your global CSS or snippet plugin (I recommend FluentSnippets or WP Codebox for managing code in WordPress like this).

/* =====================================================
   LYTB0X – CHANGE HEADER ON SCROLL
   Single header • SEO safe • Elementor
===================================================== */


/* =====================================================
   VARIABLES – EASY TO EDIT
===================================================== */

:root {

  /* Core animation */
  --lytbox-anim-ms: 180ms;
  --lytbox-admin-offset: 0px; /* set by JS */

  /* =====================================================
     SHRINK MODE (DEFAULT)
     Full-width compact sticky header
  ===================================================== */
  --shrink-pad-y: .6rem; /* adjust the inner top/bottom padding */
  --shrink-bg: rgba(249, 251, 253, 0.5); /* adjust the shrink menu background color */
  --shrink-blur: 12px; /* adjust the background blur */
  --shrink-shadow: 0 10px 30px rgba(18, 22, 30, 0.15); /* adjust the shrink menu box shadow */

  /* =====================================================
     PILL MODE (OPTIONAL)
     Floating pill-style header
  ===================================================== */
  --pill-top-space: 1.5rem; /* adjust top spacing*/
  --pill-max-w: 50rem; /* adjust the full width */
  --pill-inner-pad-y: .75rem; /* adjust the inner top/bottom padding */
  --pill-inner-pad-x: .75rem; /* adjust the inner left/right padding */
  --pill-radius: 999px; /* adjust the border radius */

  --pill-bg: rgba(249, 251, 253, 0.5); /* adjust the pill menu background color */
  --pill-blur: 12px; /* adjust the background blur */
  --pill-shadow: 0 10px 30px rgba(18, 22, 30, 0.15); /* adjust the pill menu box shadow */
  --pill-border: none; /* adjust the pill menu border */

  /* =====================================================
     MENU (add class to Nav Menu widget)
     .lytbox-nav
  ===================================================== */
  --menu-color: #010712; /* adjust the menu text color */
  --menu-color-hover: #3A00E7; /* adjust the menu text hover color */
  --menu-font-size: 1rem; /* adjust the menu font size */
  --menu-font-weight: 700; /* adjust the menu font weight */
  --menu-letter-spacing: 0em; /* adjust the menu text letter spacing */
  --menu-gap: 2.5rem; /* adjust the space between menu items */

  /* =====================================================
     CTA BUTTON (add class to Button wrapper)
     .lytbox-btn
  ===================================================== */
  --btn-font-size: .85rem; /* adjust the button font size */
  --btn-font-weight: 700; /* adjust the button font weight */
  --btn-pad-y: .75rem; /* adjust the button top/bottom padding */
  --btn-pad-x: 1.15rem; /* adjust the button right/left padding */
  --btn-radius: 999px; /* adjust the button border radius */

  --btn-bg: #010712; /* adjust the button background color */
  --btn-color: #fff; /* adjust the button font color */
  --btn-border: none; /* adjust the button border */

  --btn-color-hover: #030319; /* adjust the button font hover color */
  --btn-bg-hover: #57FBF5; /* adjust the button background hover color */
}


/* =====================================================
   CORE STICKY REVEAL (DO NOT EDIT)
===================================================== */

.lytbox-no-trans {
  transition: none !important;
}

.lytbox-header.lytbox-is-sticky {
  position: fixed;
  top: var(--lytbox-admin-offset);
  left: 0;
  right: 0;
  z-index: 9999;

  transform: translateY(-110%);
  transition: transform var(--lytbox-anim-ms) ease;
  will-change: transform;
}

.lytbox-header.lytbox-is-sticky.lytbox-show {
  transform: translateY(0);
}


/* =====================================================
   LOGO SWAP (OPTIONAL)
   logo-1 = default
   logo-2 = scrolled
===================================================== */

.logo-2 { display: none; }
.header-scrolled .logo-1 { display: none; }
.header-scrolled .logo-2 { display: inline-block; }


/* =====================================================
   BASE STRUCTURE
===================================================== */

.lytbox-header__inner {
  width: 100%;
  margin: 0 auto;
}

.header-scrolled.lytbox-header .e-con-inner {
  position: relative;
}


/* =====================================================
   SHRINK MODE (DEFAULT)
   Applies when:
   - no data-header-mode attribute
   - OR data-header-mode="shrink"
===================================================== */

.header-scrolled.lytbox-header:not([data-header-mode]),
.header-scrolled.lytbox-header[data-header-mode="shrink"] {
  background: var(--shrink-bg);
  backdrop-filter: blur(var(--shrink-blur));
  box-shadow: var(--shrink-shadow);
}

.header-scrolled.lytbox-header:not([data-header-mode]) .e-con-inner,
.header-scrolled.lytbox-header[data-header-mode="shrink"] .e-con-inner {
  padding-top: var(--shrink-pad-y);
  padding-bottom: var(--shrink-pad-y);
}

.header-scrolled.lytbox-header:not([data-header-mode]) .lytbox-header__inner,
.header-scrolled.lytbox-header[data-header-mode="shrink"] .lytbox-header__inner {
  max-width: none;
  padding: 0;
  background: none;
  box-shadow: none;
  border-radius: 0;
}


/* =====================================================
   PILL MODE (OPTIONAL)
   Activate with:
   data-header-mode="pill"
===================================================== */

.header-scrolled.lytbox-header[data-header-mode="pill"] {
  background: transparent;
  backdrop-filter: none;
  box-shadow: none;
}

.header-scrolled.lytbox-header[data-header-mode="pill"] .e-con-inner {
  padding-top: var(--pill-top-space);
}

.header-scrolled.lytbox-header[data-header-mode="pill"] .lytbox-header__inner {
  max-width: var(--pill-max-w);
  padding: var(--pill-inner-pad-y) var(--pill-inner-pad-x);
  border-radius: var(--pill-radius);

  background: var(--pill-bg);
  backdrop-filter: blur(var(--pill-blur));
  box-shadow: var(--pill-shadow);
  border: var(--pill-border);
}


/* =====================================================
   MENU STYLING
===================================================== */

.header-scrolled .lytbox-nav a {
  color: var(--menu-color)!important;
  font-size: var(--menu-font-size)!important;
  font-weight: var(--menu-font-weight)!important;
  letter-spacing: var(--menu-letter-spacing)!important;
}

.header-scrolled .lytbox-nav a:hover {
  color: var(--menu-color-hover)!important;
}

.header-scrolled .lytbox-nav nav ul {
  gap: var(--menu-gap)!important;
}

.header-scrolled .lytbox-nav .elementor-nav-menu>li:not(:first-child)>a
 {
    margin-inline-start: 0!important;
}

.header-scrolled .lytbox-nav .elementor-nav-menu>li:not(:last-child)>a 
 {
    margin-inline-end: 0!important;
}


/* =====================================================
   CTA BUTTON
===================================================== */

.header-scrolled .lytbox-btn .elementor-button {
  font-size: var(--btn-font-size)!important;
  font-weight: var(--btn-font-weight)!important;
  padding: var(--btn-pad-y) var(--btn-pad-x)!important;
  border-radius: var(--btn-radius)!important;
  background: var(--btn-bg)!important;
  color: var(--btn-color)!important;
  border: var(--btn-border)!important;
}

.header-scrolled .lytbox-btn .elementor-button:hover {
  background: var(--btn-bg-hover)!important;
  color: var(--btn-color-hover)!important;
}


/* =====================================================
   SMOOTH VISUAL POLISH
===================================================== */

.header-scrolled .lytbox-header__inner,
.header-scrolled .lytbox-nav a,
.header-scrolled .lytbox-btn .elementor-button {
  transition:
    padding 180ms ease,
    background 180ms ease,
    box-shadow 180ms ease,
    color 180ms ease,
    font-size 180ms ease;
}

Optional: Enable Pill-Style Header Mode

By default, this system uses a full-width header. This is the recommended setup for most sites.

If you want a floating pill-style header instead, there are two small changes.

First, on the same header container, add this attribute in Elementor:

data-header-mode|pill

This switches the styling logic to pill mode.

Next, inside your header, add one inner container that wraps your logo, menu, and button. Add this class to that inner container:

lytbox-header__inner

The pill style is applied to this inner container only. Shrink mode does not require an inner container.

Watch the Video Tutorial

This setup is easiest to follow visually. Check out the video below to see how to edit the code and style the header effects to match your style and website’s branding.

Join the Lytbox Newsletter

Small Subscription Form

Join the Lytbox Crew & Get Web Design Goodies.

By subscribing to the Lytbox newsletter you'll get web design updates, insights, and fun web design stuff!

Newsletter Popup Form

By subscribing, you agree to our Privacy Policy and consent to receive updates from Lytbox Academy.