How to animate display of dynamic-size elements with CSS only?

12

I'm building a website that has some state transitions using only CSS. In one of them the user clicks on the header and the div below displays or hides. In the other there is a sequence of images and clicking on the current image it moves to the next one. The technique for both is similar: a input hidden with one or more label s attached to it, and the CSS selectors of "brothers" ( + and ~ ). Examples:

  • Sections (uses checkboxes to hide / show each section)
  • .escondido { display: none; }
    .box { display: none; width: 200px; height: 100px; }
    .escondido:checked + .box { display: block; }
    
    h2 { width: 200px; background-color: lightgray; cursor: pointer; }
    .a { background-color: red; }
    .b { background-color: blue; }
    <label for="aba1"><h2>Seção 1</h2></label>
    <input id="aba1" class="escondido" type="checkbox">
    <div class="box a"></div>
    
    <label for="aba2"><h2>Seção 2</h2></label>
    <input id="aba2" class="escondido" type="checkbox">
    <div class="box b"></div>
  • Images (uses radio buttons to cycle between images)
  • .escondido { display: none; }
    .imagem { display:none; width: 200px; height: 100px; cursor: pointer; }
    .escondido:checked + label .imagem { display: block; }
    
    .a { background-color: red; }
    .b { background-color: blue; }
    .c { background-color: green; }
    .d { background-color: yellow; }
    
    .mini { display: inline-block; width: 10px; height: 10px; border-radius: 5px; }
    
    .escondido.a:checked ~ label .mini.a { border: 2px solid black; }
    .escondido.b:checked ~ label .mini.b { border: 2px solid black; }
    .escondido.c:checked ~ label .mini.c { border: 2px solid black; }
    .escondido.d:checked ~ label .mini.d { border: 2px solid black; }
    <!-- Imagens -->
    <input id="img1" class="escondido a" type="radio" name="imgs" checked>
    <label for="img2"><div class="imagem a"></div></label>
    
    <input id="img2" class="escondido b" type="radio" name="imgs">
    <label for="img3"><div class="imagem b"></div></label>
    
    <input id="img3" class="escondido c" type="radio" name="imgs">
    <label for="img4"><div class="imagem c"></div></label>
    
    <input id="img4" class="escondido d" type="radio" name="imgs">
    <label for="img1"><div class="imagem d"></div></label>
    
    <!-- Miniaturas -->
    <label for="img1"><div class="imagem mini a"></div></label>
    <label for="img2"><div class="imagem mini b"></div></label>
    <label for="img3"><div class="imagem mini c"></div></label>
    <label for="img4"><div class="imagem mini d"></div></label>

    I would now like to animate the transition between the "displayed" and "hidden" states. I know it's not possible to animate the display property directly: jQuery for example at the time of displaying first reduces the element size to zero, passes display from "hidden" to "displayed", then animates element size from zero to its actual size. I can not do something similar with just CSS, because you can not say "first do this, then do that" ...

    I tried to avoid the display and simply put the total height of div to zero. It worked fine if div has fixed size, but not if size is variable (it "jumped" straight between zero and maximum sizes):

    .escondido { display: none; }
    .box { height: 0; width: 200px; overflow: hidden; transition: height 2s; }
    
    h2 { width: 200px; background-color: lightgray; cursor: pointer; }
    .a { background-color: red; }
    .b { background-color: lightgray; }
    
    .escondido:checked + .a { display: block; height: 100px; }
    .escondido:checked + .b { display: block; height: auto; }
    <label for="aba1"><h2>funciona</h2></label>
    <input id="aba1" class="escondido" type="checkbox">
    <div class="box a"></div>
    
    <label for="aba2"><h2>não funciona</h2></label>
    <input id="aba2" class="escondido" type="checkbox">
    <div class="box b">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</div>

    (I also tried with max-height , and it also worked on div fixed, but in this case the variable size was not displayed ...)

    Is it possible to do this with CSS only, without having to specify an absolute size? (and without compromising responsiveness, ie if by adjusting the width of the window the height of the% change, keep it working) What properties could I try to animate to create the illusion of "hidden / displayed" with smooth transition? >

    Note: In the case of images, the exchange would be horizontal and not vertical, but because it is easier to deal with images (since it is usually known), this case can be left out of the question (but suggestions for this would also be very welcome).

        
    asked by anonymous 03.12.2015 / 03:58

    2 answers

    8

    Some time ago I created an article / a> about this same subject in my Blog.
    You can do this using max-height as follows:

    .box { max-height: 0; transition: max-height 2s;}
    .escondido:checked + .b { max-height:100px; }
    

    Here is an example below:

    .escondido { display: none; }
    .box { max-height: 0; width: 200px; overflow: hidden; transition: max-height 2s; }
    
    h2 { width: 200px; background-color: lightgray; cursor: pointer; }
    .b { background-color: lightgray; }
    
    .escondido:checked + .b { display: block; max-height:100px; }
    <label for="aba2"><h2>Seção 2</h2></label>
    <input id="aba2" class="escondido" type="checkbox">
    <div class="box b">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</div>
        
    03.12.2015 / 04:35
    2

    I did an experiment using the scaleY function of the transform together in the transition, the effect is not the same but "solves" the relative height question. It can suddenly serve as a starting point for a truly definitive solution.

    .escondido { display: none;}
    .box {  
      height:0px;
    	transform: scaleY(0);
    	transform-origin: top;
      
      width: 200px; overflow: auto; 	
      transition: transform 1s, height 1s;
     
      }
    
    h2 { width: 200px; background-color: lightgray; cursor: pointer; }
    .a { background-color: red; }
    .b { background-color: lightgray; }
    
    
    .escondido:checked + .a { display: block; transform: scaleY(1); height:100px; }
    .escondido:checked + .b { display: block; transform: scaleY(1); height: 100%; }
    <label for="aba1"><h2>funciona</h2></label>
    <input id="aba1" class="escondido" type="checkbox">
    <div class="box a"></div>
    
    <label for="aba2"><h2>quase funciona</h2></label>
    <input id="aba2" class="escondido" type="checkbox">
    <div class="box b">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</div>
        
    11.12.2015 / 17:32