Check Section Theme on Scroll

Watches the scroll position against a navbar-height offset and updates data-theme-nav and data-bg-nav on the body as the user scrolls through sections tagged with data-theme-section and data-bg-section. Drive nav colour and background with pure CSS attribute selectors.

scrollthemenavigationdata-attributesjavascriptcss

Code

index.html
html
<body data-theme-nav="light" data-bg-nav="light">
  <nav data-nav-bar-height></nav>
  <section class="section-header" data-theme-section="dark" data-bg-section="blue"></section>
  <section class="section-work" data-theme-section="dark" data-bg-section="orange"></section>
  <section class="section-about" data-theme-section="light" data-bg-section="yellow"></section>
  <section class="section-contact" data-theme-section="light" data-bg-section="light"></section>
</body>
styles.css
css
/* Nav Theme */
[data-theme-nav="light"] .nav__inner {
  color: #131313;
}

[data-theme-nav="dark"] .nav__inner {
  color: #EFEEEC;
}

/* Nav Background */
[data-bg-nav="pink"] .nav__inner {
  background-color: #562041;
}

[data-bg-nav="black"] .nav__inner {
  background-color: #1F1F1F;
}

[data-bg-nav="lightblue"] .nav__inner {
  background-color: #204b63;
}

[data-bg-nav="darkgreen"] .nav__inner {
  background-color: #C9FC7D;
}
script.js
javascript
function initCheckSectionThemeScroll() {

  // Get detection offset, in this case the navbar
  const navBarHeight = document.querySelector("[data-nav-bar-height]");
  const themeObserverOffset = navBarHeight ? navBarHeight.offsetHeight / 2 : 0;

  function checkThemeSection() {
    const themeSections = document.querySelectorAll("[data-theme-section]");

    themeSections.forEach(function(themeSection) {
      const rect = themeSection.getBoundingClientRect();
      const themeSectionTop = rect.top;
      const themeSectionBottom = rect.bottom;

      // If the offset is between the top & bottom of the current section
      if (themeSectionTop <= themeObserverOffset && themeSectionBottom >= themeObserverOffset) {

        // Check [data-theme-section]
        const themeSectionActive = themeSection.getAttribute("data-theme-section");
        document.querySelectorAll("[data-theme-nav]").forEach(function(elem) {
          if (elem.getAttribute("data-theme-nav") !== themeSectionActive) {
            elem.setAttribute("data-theme-nav", themeSectionActive);
          }
        });

        // Check [data-bg-section]
        const bgSectionActive = themeSection.getAttribute("data-bg-section");
        document.querySelectorAll("[data-bg-nav]").forEach(function(elem) {
          if (elem.getAttribute("data-bg-nav") !== bgSectionActive) {
            elem.setAttribute("data-bg-nav", bgSectionActive);
          }
        });
      }
    });
  }

  function startThemeCheck() {
    document.addEventListener("scroll", checkThemeSection);
  }

  // Initial check and start listening for scroll
  checkThemeSection();
  startThemeCheck();
}

// Initialize Check Section Theme on Scroll
document.addEventListener('DOMContentLoaded', () => {
  initCheckSectionThemeScroll();
});

Guide

How It Works

On every scroll event, the script measures the vertical midpoint of the navbar (data-nav-bar-height) and checks which section contains that point. It then updates data-theme-nav and data-bg-nav on matching elements (typically the body) to reflect the active section's values.

data-theme-nav & data-bg-nav

Add both to the <body> (or any persistent element like the nav). These are the attributes that change as the user scrolls, and which your CSS listens to in order to apply the correct nav styles.

data-theme-section & data-bg-section

Add to each <section> that should influence the nav. Set data-theme-section to a theme name (e.g. "light" or "dark") and data-bg-section to a background identifier (e.g. "blue", "orange"). The values are arbitrary — they just need to match your CSS selectors.

data-nav-bar-height

Add to the navbar element. The script reads its offsetHeight and uses half of that as the detection threshold — the point in the viewport where a section is considered "active". If omitted, the threshold falls back to 0 (the very top of the viewport).

Animating

Target [data-theme-nav] and [data-bg-nav] in CSS with attribute selectors to change nav colours, backgrounds, or any other property. Add CSS transitions to the nav's inner element for smooth changes between sections.