Colorful website sections changing background color on scroll with JavaScript
Blog

How to Change Section Background Color on Scroll with JavaScript

Create a smooth color transition as readers move through your page. This tutorial uses CSS custom properties and vanilla JavaScript’s Intersection Observer API, so it works without a jQuery dependency or a scroll event running on every frame.

Try the Live DemoDownload Source ZIP

How the scroll color effect works

Each section has a data-demo-color attribute. An observer detects which section crosses the middle of the viewport and updates a CSS custom property on a wrapper. CSS animates the color change. The wrapper keeps the effect local, so it will not change the article or site theme.

Live demo

Scroll through the panels below. The colors transition when each panel reaches the center of the demo area.

Indigo panel

Teal panel

Green panel

Step 1: Add the section markup

Repeat the section element for each area and use a valid CSS color value in the data attribute. Keep all sections inside one wrapper.

<div class="color-demo">
  <section class="color-panel" data-demo-color="#e8edff">Indigo</section>
  <section class="color-panel" data-demo-color="#def7f5">Teal</section>
  <section class="color-panel" data-demo-color="#e9f7e9">Green</section>
</div>

Step 2: Add the CSS transition

The custom property holds the active color. The media query respects visitors who ask their device to reduce motion.

.color-demo {
  --active-color: #e8edff;
  background-color: var(--active-color);
  transition: background-color 500ms ease;
}

.color-panel {
  min-height: 16rem;
  display: grid;
  place-items: center;
  padding: 2rem;
}

@media (prefers-reduced-motion: reduce) {
  .color-demo { transition: none; }
}

Step 3: Observe the active section

Intersection Observer watches the panels. The center band is set with rootMargin; adjust it to change when the transition starts.

const demo = document.querySelector('.color-demo');
const panels = demo.querySelectorAll('.color-panel');

const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      demo.style.setProperty('--active-color', entry.target.dataset.demoColor);
    }
  });
}, { rootMargin: '-40% 0px -40% 0px' });

panels.forEach((panel) => observer.observe(panel));

Adapt it to your website

  1. Replace .color-demo and .color-panel with your component’s selectors.
  2. Set a theme color on every section.
  3. Change the wrapper custom property in the observer callback.
  4. Check text contrast against every background color.

Troubleshooting

  • Nothing changes: Confirm the script runs after the page markup and the selectors match.
  • The switch is early or late: Adjust the top and bottom values in rootMargin.
  • Text is hard to read: Choose accessible foreground colors for every state.
  • Multiple demos interfere: Initialize each wrapper independently and keep its selectors scoped.

Frequently asked questions

Does this need jQuery?

No. The example uses native JavaScript and the browser’s Intersection Observer API.

Can I change the full page background?

Yes. Target a page-level container in the callback, then check that all text and controls stay readable in each state.

Can the background be a gradient?

Yes. Use a valid gradient as the custom property value. For broad browser support of animated gradients, cross-fade layered elements instead of transitioning the gradient directly.

Try the Live DemoDownload Source ZIP