Pure CSS Slide Up and Slide Down

By  on  

If I can avoid using JavaScript for element animations, I'm incredibly happy and driven to do so.  They're more efficient, don't require a JavaScript framework to manage steps, and they're more elegant.  One effect that is difficult to nail down with pure CSS is sliding up and down, where the content is hidden when "up" and slides in when "down".  The reason it's difficult is because you may not know the content height.  After playing around with different CSS properties, I've found a way to make a pure CSS sliding effect.

The HTML

The effect really only requires one element, so we'll stick to that:

<div class="slider">Some content here....</div>

Add as much content as you'd like.

The CSS

Before we get into the trick, let's set the few obvious properties: overflow-y and the CSS animation properties:

.slider {
	overflow-y: hidden;
	max-height: 500px; /* approximate max height */

	transition-property: all;
	transition-duration: .5s;
	transition-timing-function: cubic-bezier(0, 1, 0.5, 1);
}

So now the fun:  the trick we'll use to make the height play nice is the max-height property.  We'll set that property value to a reasonable default and then create another class to set that max-height to 0, thus sliding the element in:

.slider.closed {
	max-height: 0;
}

After some tinkering, I really liked the cubic-bezier transition timing function above, though there may be a more scientific method for the animation.  At this point the only thing we need to do is toggle the closed class to slide the element in an out!

Not Perfect

So there comes the case where the height of the element could be 100px or 100000px.  In that case, we're at a bit of a loss because the element isn't fixed-height.  In that case, you have two options.  The first is admitting defeat and using a CSS framework.  The second is measuring the height of the DIV with JavaScript and injecting a max-height rule into a stylesheet so the animation is perfectly measured.  Also remember that 100% as a max-height rule will be unreliable as 100% is relative to width, not height.

Let me know if you've found a better solution!

Recent Features

  • By
    CSS Gradients

    With CSS border-radius, I showed you how CSS can bridge the gap between design and development by adding rounded corners to elements.  CSS gradients are another step in that direction.  Now that CSS gradients are supported in Internet Explorer 8+, Firefox, Safari, and Chrome...

  • By
    Send Text Messages with PHP

    Kids these days, I tell ya.  All they care about is the technology.  The video games.  The bottled water.  Oh, and the texting, always the texting.  Back in my day, all we had was...OK, I had all of these things too.  But I still don't get...

Incredible Demos

Discussion

  1. I’d obviously love to give an example, but I have literally 60 seconds of free time right now. My first thought to avoid JS would be to use a checkbox to toggle the “up” and “down” states. If I have some time later I’ll mess with an example, although I’d be surprised if someone doesn’t beat me to the punch.

    • That works but the idea isn’t necessarily that you want it to be click-to-slide.

  2. David Hucklesby

    The trick I use is to use a max-height value that’s unlikely to be exceeded. This does cause a delay in closing that I overcome by making the transition to the “closed” state quite short compared to the opening.

    Love that cubic-bezier transition! Stealin’. :)

  3. Nice trick David.

    Although remember transitioning the max-height property is hitting the CPU which is produce less smooth animations than hitting the GPU. A 2D transform transition would be better for repaints/reflow. Something like from: transform(0, -100%) to: transform(0, 0%)

    Let me tweak something with a jsFiddle or so…

  4. Nailed it, works like a charm :)

    http://jsfiddle.net/jkneb/9uRKt/

    The thing even cooler is that the slider has no height, it takes the height of its parent.

    Enjoy!

    Cheers

    • It’s a very similar effect, although on David’s example, the text doesn’t move with the animation.

    • Darek

      I updated a little bit your code:
      http://jsfiddle.net/9uRKt/11/

    • Krishna

      instead of adding new class .opened this would minimize

      var flag = 1;
      var slider = document.querySelector('.slider');
      
      function toggleSlider(){
      	debugger;
      	
      	if(flag == 1){
      		slider.classList.add('closed');
      		flag = 0;
      	}
      	else{
      		flag = 1;
      		slider.classList.remove('closed');
      	}
      }
    • @David – how come you aren’t using height:auto; for the parent container?

      Got it to work dynamically modfying jkneb’s post

      https://jsfiddle.net/wuq91s6j/

  5. Such as jkneb, I like using translate(), too. Here the example I use translateY for this demo:

    http://codepen.io/dangvanthanh/pen/BsDbg

    With translate(), you can don’t need care about height.

  6. I think the mixed JS/CSS solution is a good compromise. There are two things that bother me about solely using the max height property — 1st, you get a slight delay (depending on how much higher your max height is set than the visible height of the element. 2nd, if you are setting a height that could be shorter than the content you are forced to set overflow: auto; and then get some wobble in the slide-open animation as the browser adds and removes the scrollbar. This was an awesome guide! Thanks.

  7. Paul

    What am I doing wrong here: http://jsfiddle.net/Dfswg/ ? I need it to start off hidden, then slide down on click….

    Slide down doesn’t work, it’s just showing like I was only toggling display hidden/block… Slides up fine though…

    Thanks…

  8. Ted

    Transitioning to and from “auto” is one of those things that needs to just work. It’s too valuable in too many cases to not be supported, even if it is pretty difficult to get right. Same goes for transitions to/from display: none;

  9. Clara

    Hm why is this “pure css” if you need JS to make it work?

    • Because there needs to be a trigger…

    • travis

      … Then it’s not pure css.

    • Thank you. PURE css.

    • astridparisUX

      I love that there seems to be a for-real pure-CSS way to to achieve this, but gregmatys’s example doesn’t animate down, only up.

    • actually, it is animating down, but the bezier function is like an easeIn, so going out looks fast and jumpy. Slow the animation down and you can see it perfectly.

  10. I tried using hover to trigger, but ran into issues with the infinite loop. Otherwise cool idea, since you are using JS to trigger you can do it in any direction and use absolute positioning, z-index, and overflow hidden trickery

  11. Sammy

    Nice!

  12. Hi there,

    I’m trying to setup a website where pages slide in and out, onlyt that they slide in from one idrection and slide out to the opposite direction. Any idea on how to do that?
    Thanks

  13. Avoiding JS and using CSS for animations as much as possible is high on my development criteria. Whilst it might not currently be perfect this article has caused me to revisit a little project I was working on to try some of the ideas. Great stuff, thanks David.

  14. Bojan

    As #Paul mentioned, how to make your window slide from closed to open where you could do it by mouseOver or by Toggle. I am having that problem that I have some field in the middle of page and want when I put my mouse over it to open, from up to down, much bigger window of same width but with full info about that Read more field. How to do that, it is easy with left-right transition where you can hide it with big – or + px, but I need it to be hidden positioned on some point and than just to appear when activated.

    Hope you can understand me.

  15. I have one div slider with 3 links. The Content in that div will be fetched using Ajax. I want that if i click 1st link the div slide down with that content and when i click other 2 links the div slide down with their respective content. Example is given below:

    Link 1
    Link 2
    Link 3
    
    $(".link1").click(function(){
    $(".slider").slideToggle();
    //Ajax Content for Link 1 will be fetched
    });
    $(".link2").click(function(){
    $(".slider").slideToggle();
    //Ajax Content for Link 2 will be fetched
    });
    $(".link3").click(function(){
    $(".slider").slideToggle();
    //Ajax Content for Link 3 will be fetched
    });
    

    The Problem is when i click on anyone link it opens or slide down but when i click on other link due to slideToggle it slides Up.

    Please help me through this problem….Sorry for my Bad English

    Thanks in Advance

  16. isimi taiwo

    this is good thanks PURE css.but anyway you make the botton bellow the wrap and slide?thank you

  17. There are two things that bother me about solely using the max height property — 1st, you get a slight delay .Otherwise cool idea, since you are using JS to trigger you can do it in any direction and use absolute positioning

  18. Brandon

    Great work! This is exactly what I was looking for. How would I have to modify the code to start out with the div closed and slide open when I click?

  19. Scott

    Brandon – change this:

    #toggle:checked ~ #wrap > #slider
    

    to this

    #toggle:not(:checked) ~ #wrap > #slider
    
    • Brandon

      Thanks Scott!

  20. I’ve come across this dynamic height issue before, one solution I’ve found can be really useful is transitioning between transform: scaleY(0); and transform: scaleY(1);

    This way you can avoid hacking your way around the delay in animation caused by using an arbitrary value on the max-height property.

  21. Lor

    Thank David Walsh, thank you jkneb, thank you gregmatys, thank you Adam Hollister, this is a great demonstration.

    I think that by combining all your inputs, it should even be possible to get a pure CSS parallax effect (i.e. trigger animation on two divs that are positioned one above the other with different z-index and have different transition-duration).

    I’ll try to put my hands at it if I some time to do it.

  22. Matt

    Unfortunately, the major issue with all of the CSS translate solutions is that you aren’t actually changing the height — you simply create a container that takes up the full height of the content even when the content is hidden! This ends up pushing any content below further down the page, which is usually not what you want in the case of something like an accordion form.

    I like David’s solution, although a fixed max-height is problematic, especially if you’re setting duration values for your transitions…you could end up with wildly-fast transitions that don’t end up looking all that pleasing to the eye…the stylesheet injection sounds promising though if a little complex.

  23. Thank you for this!

    I reversed it (initial state with height of 0px then applied height to the div to reveal) and used it to create pure CSS reveal on click search box for my minimal style theme. Also used it to create a click to reveal social share widget as well. Works beautifully and helped keep my theme’s JavaScript footprint as small as possible.

    I totally agree with trying to avoid JavaScript for element animations and am so in love with CSS3.

    Thanks again!

  24. Evgeniy

    Here is my funky css solution to emulate height auto transition:
    http://codepen.io/LasyAsCat/pen/KdxLRj#0

    Drawback is that you should use additional inner wrapper to hide content with overflow and use two transform properties instead one, but it works as it should.

  25. Shez

    Hey guys….you’re all amazing and thank you david for this site – it has helped me out a hell of a lot with my very first website I have to code! :)

    I do have a question though – saw this online and it’s what is requested for what I have to do: http://jsfiddle.net/9cdYR/

    but is it in any way possible to create this same effect using only with CSS, no JavaScript?

    Sorry for a possible stupid question – first time coder amongst you pros

  26. Jorgen

    You can quite easily make the max-height approach work for content of all heights with very little JavaScript.
    The trick is to have an outer sliding div with an inner content div. When the outer div is to slide, set the max-height property to the scrollHeight property and everything is fine.

    See my very basic example here… https://jsfiddle.net/j8da050k/4/

    • Jorgen,

      That solution is awesome. Thank you so much.

  27. noni

    Somebody mentioned the main example not being pure CSS and someone else posted a version with “actually pure CSS” but let’s get it straight CSS can’t really be pure in that sense because it needs HTML to work.

    Now. I needed couple elements to slide up and down into a box so I made a pure HTML version

    https://codepen.io/anon/pen/qmGNWQ

  28. Nice, but not accessible. Why? Because max-height doesn’t hide content from screen readers.

  29. Chris

    “pure css”? Ahhh … except the bit in your onclick? :/

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!