Adding Depth Like a Movie - Using Bootstrap 5.3
Bootstrap 5.3 CSS Parallax JavaScript Parallax
Scroll to see the effect
Parallax scrolling makes backgrounds move slower than foreground content as you scroll. It's like looking out a car window: trees (background) move slower than the road (foreground). This adds depth and makes sites feel dynamic.
HTML with Bootstrap:
<div class="parallax-section d-flex align-items-center justify-content-center text-white">
<div class="container">
<h2 class="display-3 fw-bold">Parallax Content</h2>
</div>
</div>
Custom CSS for Parallax:
.parallax-section {
background-image: url('...');
min-height: 500px;
background-attachment: fixed; /* KEY! */
background-position: center;
background-size: cover;
}
d-flex align-items-center – Flexbox centeringcontainer – Responsive widthdisplay-3 fw-bold – Typographytext-white – Text colorpy-5 – Vertical paddingprefers-reduced-motionDoes the parallax effect make this page feel more "alive"? Why or why not? Does it enhance your understanding of the content, or is it purely decorative? Consider the balance between visual interest and accessibility.
Compare with the parallax section above
The green section above uses background-attachment: scroll (the default).
Notice how it scrolls with the content instead of staying fixed. This is the traditional approach.
| Technique | Implementation | Mobile Support | Complexity |
|---|---|---|---|
| Parallax (fixed) | background-attachment: fixed |
Limited | ⭐ Simple |
| Normal (scroll) | background-attachment: scroll |
✅ Works | ⭐ Simple |
Is the parallax effect truly enhancing the user experience, or could it be a barrier? Consider users with vestibular
disorders who may experience discomfort with motion effects. How might we offer alternatives or respect the
prefers-reduced-motion media query?
The section above uses JavaScript with transform: translateY() to move three separate layers at different speeds.
This creates a more sophisticated depth effect than pure CSS.
Each layer has a data-speed attribute:
data-speed="0.2" - Moves very slowly (distant background)data-speed="0.5" - Moves at medium speeddata-speed="0.8" - Moves fastest (closest to viewer)
<div class="parallax-js-section">
<!-- Each layer moves at different speed -->
<div class="parallax-layer layer-back" data-speed="0.2">🌌</div>
<div class="parallax-layer layer-middle" data-speed="0.5">☁️</div>
<div class="parallax-layer layer-front" data-speed="0.8">Text</div>
</div>
// 1. Select all layers
const parallaxLayers = document.querySelectorAll('.parallax-layer');
// 2. On scroll, update each layer's position
function updateParallax() {
parallaxLayers.forEach((layer) => {
// Read the speed value from HTML attribute
const speed = parseFloat(layer.dataset.speed);
// Calculate how much to move this layer
const scrolled = window.pageYOffset;
const yPos = scrolled * speed * 0.5;
// Apply the transform
layer.style.transform = `translateY(${yPos}px)`;
});
}
// 3. Listen to scroll events efficiently
window.addEventListener('scroll', () => {
requestAnimationFrame(updateParallax);
}, { passive: true });
requestAnimationFrame for performanceprefers-reduced-motion
Parallax effects can cause motion sickness, dizziness, or vestibular disorders for some users.
It's essential to respect the prefers-reduced-motion media query.
prefers-reduced-motion is a CSS media query that detects if a user has requested reduced motion
in their operating system settings. Users can enable this in:
This demo automatically disables parallax effects when users have motion reduction enabled:
// 1. Detect user's motion preference
const prefersReducedMotion = window.matchMedia(
'(prefers-reduced-motion: reduce)'
);
let isReducedMotion = prefersReducedMotion.matches;
// 2. Disable CSS parallax if needed
function handleMotionPreference() {
if (isReducedMotion) {
// Change fixed backgrounds to scroll normally
document.querySelectorAll('.parallax-bg').forEach((el) => {
el.style.backgroundAttachment = 'scroll';
});
}
}
// 3. Don't initialize JavaScript parallax if reduced motion
function initParallax() {
if (isReducedMotion) {
console.log('Parallax disabled: user prefers reduced motion');
return; // Exit without setting up animations
}
// ... rest of parallax code
}
// 4. Listen for preference changes during session
prefersReducedMotion.addEventListener('change', (e) => {
isReducedMotion = e.matches;
// Update animations accordingly
});
window.matchMedia('(prefers-reduced-motion: reduce)').matchestrue if reduce motion is enabled, false otherwise