Search code examples
cssrowoverlapoverlappingplaying-cards

In CSS, how do I horizontally center child elements, overlapping them as needed to fit in the parent's width without wrapping?


I'm trying to create an HTML/CSS element that represents a hand of playing cards.

I have a parent <div> container with a fixed height and a 100% width. It contains a variable number of child <div>s (cards), each having the same height and the same width as each other.

I'm trying to do this:

  • Keep the child <div>s on a single horizontal row (no wrapping)
  • Center the child <div>s
  • Horizontally overlap the child <div>s as needed whenever the parent container isn't wide enough to show them all side-by-side.
  • Preserve the parent <div>'s dimensions. (The parent box shouldn't change, regardless of how many child <div>s it contains)

Is this possible?

I tried something like this, but no luck:

.parent {
  height: 110px;
  text-align: center;
  white-space: nowrap;
  width: 100%;
}

.child {
  border: 1px gray solid;
  display: inline-block;
  height: 100px;
  width: 80px;
}
<div class='parent'>
  <div class='child'></div>
  <div class='child'></div>
  <div class='child'></div>
  ...
</div>

^ This does a fine job whenever the parent contain is wide enough to fit all of the child <div>s side-by-side. But, when the parent isn't that wide, the child <div>s do not overlap with each other. Instead, the children exceed the parent's boundaries. (Kids today, amiright?)

I've also tried 'float'ing the child divs and using margin-left:calc(20% - 100px), but that doesn't work for a couple reasons: floated elements can wrap to new lines, and the margin-left value would need to be different depending on how many child elements there are (which can change).


Solution

  • If you want to stick with a CSS-only solution, you could try out flexbox. Below I applied display: flex to the .parent which allowed the .child nodes to be flexed within .parent forcing them to equal widths. Within each child I added position: absolute to a pseudo element (could just as easily be done with a real element) to act as the actual content. In your case the card display.

    * {
      box-sizing: border-box;
    }
    
    .parent {
      display: flex;
      height: 110px;
      align-items: center;
      justify-content: center;
      white-space: nowrap;
      width: calc(100% - 96px);
      padding: 48px;
    }
    
    .child {
      position: relative;
      height: 100px;
      width: 80px;
    }
    
    .child:hover:after {
      z-index: 1;
    }
    
    .child:after {
      content: "A♠";
      position: absolute;
      border: 4px solid red;
      background: white;
      display: inline-block;
      height: 100px;
      width: 80px;
      left: 0; 
      top: 0;
      border-radius: 8px;
      z-index: 0;
    }
    <div class='parent'>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
      <div class='child'></div>
    </div>