CSS Sticky Toggle Button: Sliding Animation on Scroll
Create smooth sliding animation for CSS sticky toggle button that fills space when hiding content on scroll (>50px). Flexbox, grid, JS solutions with code for position sticky scroll animation CSS and toggle button CSS.
How to add a sliding animation to a toggle button so it moves up when hiding content on scroll and moves down when showing content?
I have a sticky container where content hides after scrolling down 50px (or 250px), revealing a toggle button at the bottom. There’s empty space above the button when content is hidden. I want a smooth animation where the button slides up to fill the space when content hides, and slides down below the content when it shows.
Current behavior:
- On scroll down >50px: content opacity/visibility transitions to hidden, button displays.
- On scroll up: button hides, content shows.
- Button toggle works but no smooth position change.
Desired animation: Sliding transition for button position (e.g., using CSS transforms or height changes).
Current JavaScript:
document.addEventListener("DOMContentLoaded", function() {
const contentBlock = document.querySelector('.content');
const toggleButton = document.querySelector('.toggle-button');
// Manually switching the visibility of a block (by clicking a button)
function toggleVisibility() {
if (contentBlock.classList.contains('hidden-content')) {
contentBlock.classList.remove('hidden-content');
contentBlock.classList.add('show-content');
} else {
contentBlock.classList.remove('show-content');
contentBlock.classList.add('hidden-content');
}
}
// Button click event handler
toggleButton.addEventListener('click', () => {
toggleVisibility();
});
// Handling browser window scrolling
window.addEventListener('scroll', () => {
if (window.scrollY > 50) { // If you scrolled down the page by 50px
toggleButton.style.display = 'block'; // The appearance of the button
contentBlock.classList.remove('show-content'); // The content block is hidden
contentBlock.classList.add('hidden-content');
} else { // If you scrolled back up
toggleButton.style.display = 'none'; // The button is hidden
contentBlock.classList.remove('hidden-content'); // The content block appears again
contentBlock.classList.add('show-content');
}
});
});
Current CSS:
.sticky-category {
position: sticky;
top: 10px;
z-index: 999;
transition: all .2s ease-in-out;
}
.content {
transition: opacity 0.5s ease-in-out;
}
.toggle-button {
display: none;
max-width: 200px;
margin: 0 auto;
padding: 10px;
background-color: rgb(191, 79, 203);
border-radius: 50px;
border: none;
cursor: pointer;
transition: all 0.3s ease;
color: #fff;
}
.toggle-button:hover {
background-color: rgb(143, 62, 184);
}
.hidden-content {
opacity: 0;
visibility: hidden;
}
.show-content {
opacity: 1;
visibility: visible;
}
Current HTML:
<div class="sticky-category">
<span class="content">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed in felis in felis placerat pulvinar. Sed eget fermentum justo. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Quisque placerat condimentum laoreet. Mauris facilisis mi tellus, a blandit magna viverra in. Aenean varius pretium elit vitae finibus. Fusce vel cursus tellus, non sollicitudin mi.</span>
<button class="toggle-button">Show/Hide</button>
</div>
<div style="height: 500px;"></div>
What CSS/JS changes are needed for the sliding effect (e.g., transform: translateY, max-height, or flex layout)?
To create a smooth sliding animation for your toggle button in a CSS sticky container, switch to a flexbox layout on the .sticky-category so the content smoothly collapses (using flex-basis or max-height transitions), allowing the button to slide up and fill the space naturally—no more empty gap or abrupt jumps. Update your JavaScript to toggle classes on scroll instead of display styles, and let CSS handle the scroll animation. This toggle button CSS approach works with your existing HTML, delivers the sliding animation toggle button scroll effect at 50px (or 250px), and feels buttery smooth across browsers.
Contents
- Understanding CSS Sticky and Scroll Animation Challenges
- Why Height Auto Doesn’t Animate: Common Pitfalls
- Pure CSS Solution: Flexbox for Sliding Toggle Button
- Advanced CSS: Grid Template Rows for Smooth Content Collapse
- JavaScript-Enhanced Animation with ScrollHeight
- Integrating Scroll Detection and Toggle Functionality
- Full Working Code Demo and Best Practices
- Sources
- Conclusion
Understanding CSS Sticky and Scroll Animation Challenges
Ever coded a CSS sticky element and watched it behave like a stubborn toddler on scroll? Your setup nails the basics—sticky top positioning, opacity fades for content, and a toggle button that pops in. But here’s the rub: toggling opacity and visibility (or display) doesn’t trigger layout reflow. The container keeps its full height, leaving that awkward empty space above the button. No slide happens because nothing pushes or pulls the button position.
Position sticky shines for headers or navs that “stick” during scroll, but pairing it with animations needs layout tricks. As CSS-Tricks demonstrates with sliding sticky effects, flexbox or grid lets child elements resize fluidly, so your button glides up when content vanishes. Your JS already detects window.scrollY > 50 perfectly— we just need to swap direct styles for class toggles to unlock scroll animation CSS.
Think of it this way: without flex or grid, you’re animating visuals only. With them? Real space-filling motion. And it plays nice with your manual toggle too—clicking “Show/Hide” will expand/collapse content, shoving the button down smoothly.
Why Height Auto Doesn’t Animate: Common Pitfalls
Frustrating, right? You want height: 0 to height: auto for that perfect collapse, but CSS says no—auto values won’t transition. Browsers can’t interpolate between pixels and “whatever fits,” so you get instant snaps.
This trips up hide content on scroll CSS setups everywhere. CSS-Tricks breaks it down: reflows on every frame kill perf, and max-height hacks (like max-height: 0 to 500px) clip tall content or overshoot short bits. Transforms like translateY(-100%) fake motion but skip layout shift—the space stays empty.
Your current opacity/visibility falls into this trap. Quick fixes? Estimate max-height, but that’s brittle. Better: layout methods below. Pro tip: Always test on mobile—sticky + scroll events can jank without throttling.
| Pitfall | Why It Fails | Fix Preview |
|---|---|---|
height: auto |
Non-numeric, no transition | Flex/Grid fractions |
| Opacity only | No layout change | Collapse actual height |
display: none/block |
Instant, no anim | Classes + transitions |
Fixed max-height |
Clips content | JS scrollHeight |
Pure CSS Solution: Flexbox for Sliding Toggle Button
Flexbox is your low-hanging fruit—turns the sticky container into a column stack where content shrinks to nothing, sliding the toggle button up seamlessly. No JS height calcs needed for basics.
Key changes:
- Set
.sticky-category { display: flex; flex-direction: column; } - Content gets
flex: 1 1 auto; transition: flex-basis 0.5s ease-in-out; overflow: hidden; .hidden-content { flex-basis: 0 !important; opacity: 0; }(flex-basis transitions smoothly)
Why flex-basis? It animates from auto equivalent to 0, collapsing space perfectly. Button stays at bottom, slides up as content vanishes. Works in your sticky setup since flex kids dictate container height dynamically.
.sticky-category {
position: sticky;
top: 10px;
display: flex; /* NEW */
flex-direction: column; /* NEW */
z-index: 999;
transition: all 0.2s ease-in-out;
}
.content {
flex: 1 1 auto; /* NEW: Grows/shrinks */
transition: flex-basis 0.5s ease-in-out, opacity 0.5s ease-in-out; /* Smooth slide */
overflow: hidden; /* NEW: No overflow during collapse */
}
.hidden-content {
flex-basis: 0 !important; /* Collapse */
opacity: 0;
}
.toggle-button {
display: block !important; /* Always show, flex handles pos */
/* Your existing styles... */
}
JS just toggles .hidden-content class—no display fiddling. Boom: sticky button animation on scroll down >50px, button fills space. Toggle click reverses it, pushing button down. Caveat: Flex-basis needs !important override sometimes; browsers love it (95%+ support).
Test it—scroll down, watch the lorem ipsum squeeze away, button glides up like magic.
Advanced CSS: Grid Template Rows for Smooth Content Collapse
Flex great? Grid’s slicker for precise rows, especially modern browsers. Use grid-template-rows: 1fr auto normally, switch to 0fr auto on hide. Content row fractions collapse infinitely smooth—no clipping, true flexbox height transition vibe but row-based.
From Theo Soti’s height transition guide, fr units animate beautifully: 1fr to 0fr shrinks to zero without knowing content height.
.sticky-category {
position: sticky;
top: 10px;
display: grid; /* NEW */
grid-template-rows: 1fr auto; /* Content full, button auto */
z-index: 999;
transition: all 0.2s ease-in-out;
/* Gap if needed: gap: 10px; */
}
.content {
transition: opacity 0.5s ease-in-out;
overflow: hidden;
padding-bottom: 10px; /* Polish */
}
.hidden-content {
grid-row: 1 / 1; /* Or class toggles rows to 0fr */
opacity: 0;
}
.sticky-category.has-hidden { /* JS toggles this */
grid-template-rows: 0fr auto;
}
.toggle-button {
grid-row: 2; /* Fixed bottom row */
/* Styles... */
}
Toggle .has-hidden class on container via JS. Even cleaner for animate height auto CSS—grid handles the math. Safari/Chrome shine; IE no-go (use flex fallback).
Which to pick? Flex for broad support, grid for future-proof polish.
JavaScript-Enhanced Animation with ScrollHeight
Pure CSS rocks until content varies wildly. Enter JS: Measure scrollHeight, set max-height dynamically for pixel-perfect transitions. CSS-Tricks nails this workaround: On toggle, content.style.maxHeight = content.scrollHeight + 'px'; then swap class to max-height: 0.
Robust for your dynamic lorem ipsum (or real content). Add to flex/grid for hybrid win.
function collapseContent(shouldHide) {
const content = document.querySelector('.content');
if (shouldHide) {
content.style.maxHeight = content.scrollHeight + 'px';
requestAnimationFrame(() => { // Force reflow
content.classList.add('hidden-content');
content.style.maxHeight = '0px';
});
} else {
content.classList.remove('hidden-content');
content.style.maxHeight = content.scrollHeight + 'px';
}
}
Reset maxHeight: '' on transitionend. Handles CSS sticky toggle button precisely, no estimates.
Integrating Scroll Detection and Toggle Functionality
Your JS is solid but direct styles kill anims. Refactor to class toggles + throttle for perf (scroll fires 60fps!). Detect direction? Bonus for pro feel, per Frontend Masters sticky toggle tips.
let ticking = false;
let lastScrollY = 0;
window.addEventListener('scroll', () => {
if (!ticking) {
requestAnimationFrame(() => {
const scrollY = window.scrollY;
const content = document.querySelector('.content');
const container = document.querySelector('.sticky-category');
if (scrollY > 50) { // Or 250
collapseContent(true); // Or content.classList.add('hidden-content')
container.classList.add('scrolled'); // For grid
} else {
collapseContent(false);
container.classList.remove('scrolled');
}
lastScrollY = scrollY;
ticking = false;
});
ticking = true;
}
});
Toggle stays: toggleVisibility() calls collapseContent(!content.classList.contains('hidden-content')). Threshold tweak? Swap 50. Handles 50px/250px easy.
Full Working Code Demo and Best Practices
Here’s your code, flexbox-powered (swap grid/JS as needed). Drop in, scroll test—sliding animation toggle button scroll live.
Updated HTML (unchanged):
<div class="sticky-category">
<span class="content">Lorem ipsum...</span>
<button class="toggle-button">Show/Hide</button>
</div>
<div style="height: 500px;"></div>
Full CSS (flex version):
/* All previous + flex changes from #flexbox-solution */
.show-content { flex-basis: auto; opacity: 1; max-height: none; }
Full JS:
// DOMContentLoaded wrapper...
function collapseContent(hide) {
const content = document.querySelector('.content');
if (hide) {
content.style.maxHeight = content.scrollHeight + 'px';
requestAnimationFrame(() => {
content.classList.add('hidden-content');
content.style.maxHeight = '0';
});
} else {
content.classList.remove('hidden-content');
content.style.maxHeight = content.scrollHeight + 'px';
}
}
toggleButton.addEventListener('click', () => {
const isHidden = contentBlock.classList.contains('hidden-content');
collapseContent(isHidden);
});
// Throttled scroll as above
| Method | Pros | Cons | Best For |
|---|---|---|---|
| Flexbox | Simple, broad support | Less precise for huge content | Your setup |
| Grid | Modern, clean rows | No IE | New projects |
| JS scrollHeight | Exact heights | More code | Dynamic content |
Best practices: Throttle scroll, overflow: hidden, ARIA for toggle (aria-expanded), test iOS Safari sticky bugs.
Sources
- Using CSS Transitions on Auto Dimensions — JS workaround for height:auto transitions: https://css-tricks.com/using-css-transitions-auto-dimensions/
- Height Transitions — Grid fr units and flex for collapsing animations: https://theosoti.com/blog/height-transition/
- Creating Sliding Effects Using Sticky Positioning — Flexbox sticky sliding examples: https://css-tricks.com/creating-sliding-effects-using-sticky-positioning/
- Toggle Position Sticky to Position Fixed on Scroll — Scroll event optimization for sticky: https://frontendmasters.com/blog/toggle-position-sticky-to-position-fixed-on-scroll/
- How to Hide/Reveal a Sticky Bottom Nav Bar on Scroll — Direction-aware scroll hiding: https://webdesign.tutsplus.com/how-to-hidereveal-a-sticky-bottom-nav-bar-on-scroll-with-javascript--cms-107947t
- CSS Position Sticky: How it Really Works — Sticky behavior on scroll thresholds: https://elad.medium.com/css-position-sticky-how-it-really-works-54cd01dc2d46
Conclusion
Flexbox delivers the quickest win for your CSS sticky sliding toggle—content collapses on scroll >50px (or 250px), button glides up filling space, toggle reverses it flawlessly. Scale to grid or JS for edge cases, always throttle scroll for silkiness. You’ll ditch those empty gaps forever; tweak thresholds and test—your users will love the polish.