Search code examples
cssflexboxspacing

Shrink vertical spacing between fixed-size boxes to fit list within parent's bounds


Making a card game web app, I am trying to put as much rendering logic as possible in CSS (possibly just as a challenge to myself). One of the harder challenges is the following: as in solitaire, I need to display vertical stacks of cards (having a fixed size and aspect ratio), as in the image below. Stacks 1 and 2 show the principle. However, if a stack gets very large, the vertical spacing between the cards might need to be shrunk, for the entire stack to fit within the parent's bounds (stack 3 in the image). To achieve this shrinking, solely in the case that the stack would overflow the parent's bounds, is the content of this question.

An 'intermediate' result is that I'm able to make the stacks behave like 3 and 4 in the image, by putting each card within a 0-height container, vertically evenly spaced within the stack with flex, and then linearly interpolating the margin-top of the cards from -0% to -100%. This setting of the margin-top has to be done with JS, outside of the CSS, which is a concession -- but it's not the worst solution, because it doesn't require the JS to respond to the rendering itself. This intermediate result obviously only works well for the overflow-case (stack 3), and not for the ordinary case (stacks 1 and 2 in the image), in which case it is ugly (stack 4).

The question is, then, whether it is possible to achieve this automatic shrinking only after having passed a certain critical break-off point, with CSS only, and/or possibly some 'before-render' attributes/styles set with JS.

Problem sketch (a working version of which can be found at http://jsfiddle.net/kelleyvanevert/5fkac3s7/1/):

Problem sketch

Flex-box 'intermediate result' of stacks 3 and 4:

CSS

.stack {
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}
.surr {
  height: 0;
}

HTML

<div class="stack">
  <div class="surr"><div class="item" style="margin-top: -0%;"></div></div>
  <div class="surr"><div class="item" style="margin-top: -25%;"></div></div>
  <div class="surr"><div class="item" style="margin-top: -50%;"></div></div>
  <div class="surr"><div class="item" style="margin-top: -75%;"></div></div>
  <div class="surr"><div class="item" style="margin-top: -100%;"></div></div>
</div>

Solution

  • (See comments below for some additional results)

    You can use a pseudo-element (:after) that will help you to keep your stack as you want when having more cards. Then you can simply control the flex value of this element to control how much the cards should shrink as it will control how much space this element should take:

    .box {
      margin-top: 25px;
      border: 2px solid #000;
      height: 300px;
      display: flex;
      flex-direction: row;
    }
    
    .stack {
      position: relative;
      box-sizing: border-box;
      width: 15%;
      height: 100%;
      padding: 5px 5px 0 5px;
      margin: 0 10px;
      background: #fff;
      border-left: 1px solid #ddd;
      border-right: 1px solid #ddd;
        display: flex;
      flex-direction: column;
      justify-content: space-between;
    }
    
    .label {
      position: absolute;
      top: -20px;
      left: 50%;
      width: 20px;
      height: 20px;
      margin-left: -10px;
      text-align: center;
    }
    
    .item {
      box-sizing: border-box;
      height: 0;
      padding-top: 80%;
      /* aspect ratio */
      margin-bottom: 5px;
      background: #fff;
      border: 1px solid #333;
    }
    
    .stack:after {
      content: "";
      flex: 5;
    }
    
    .surr {
      height: 0;
      flex: 1
    }
    <div class="box">
      <div class="stack">
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="label">D</div>
      </div>
      <div class="stack">
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="label">E</div>
      </div>
      <div class="stack">
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>    
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="label">E</div>
      </div>
      <div class="stack">
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>    
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="surr"><div class="item" ></div></div>
        <div class="label">E</div>
      </div>
      
    </div>