Question

How to overlap flex items in a fixed width?

The final result I want is this and the result I am able to obtained is this.

Basically I want the cards to overlap in a fixed width, when it is overflowing its parent. And when it is not overflowing, I want them to retain the width according to aspect ratio. I have fixed the height, so width also should be constant.

You can click on each card and it will be deleted in each link. But in the second/not working link, the card becomes too big which is also a problem.

How can I solve this?

<div class="container">
  <div class="player player-1"> p1 </div>
  <div class="card_area card_area-1">card-1</div>

  <div class="player player-2">p2</div>
  <div class="card_area card_area-2">card-2</div>

  <div class="player player-3">p3</div>
  <div class="card_area card_area-3">card-3</div>

  <div class="player player-4">p4</div>
  <div id="render_cards" class="card_area  card_area-4">
    <div class="card card-odd card-1">card-1</div>
    <div class="card card-even card-2">card-2</div>
    <div class="card card-odd card-3">card-3</div>
    <div class="card card-even card-4">card-4</div>
    <div class="card card-odd card-5">card-5</div>
    <div class="card card-even card-6">card-6</div>
    <div class="card card-odd card-7">card-7</div>
    <div class="card card-even card-8">card-8</div>
    <div class="card card-odd card-9">card-9</div>
    <div class="card card-even card-10">card-10</div>
    <div class="card card-odd card-11">card-11</div>
    <div class="card card-even card-12">card-12</div>
    <div class="card card-odd card-13">card-13</div>

  </div>
</div>
.container {
  width: 60%;
  margin: 5px auto;
  border: 2px solid green;
  aspect-ratio: 3/2;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  grid-template-rows: repeat(6, minmax(0, 1fr));
}

.player {
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: bisque;
}

.player-1 {
  grid-row: 1;
  grid-column: 3;
}

.card_area-1 {
  grid-row: 1;
  grid-column: 4;
}

.player-2 {
  grid-row: 4;
  grid-column: 6;
}
.card_area-2 {
  grid-row: 3;
  grid-column: 6;
}

.player-3 {
  grid-row: 3;
  grid-column: 1;
}
.card_area-3 {
  grid-row: 4;
  grid-column: 1;
}

.player-4 {
  grid-row: 6;
  grid-column: 2;
}
.card_area-4 {
  grid-row: 6;
  grid-column: 3/6;
}
.card_area {
  background-color: rgb(232, 127, 127);
}

.card:hover {
  background-color: aqua;
}

.card-odd {
  background-color: gray;
}
.card-even {
  background-color: darkkhaki;
}

.card_area-4 {
  width: 100%;
}
.card:last-child {
  flex: 0 0 auto;
}

.card {
  flex: 1;

  aspect-ratio: 2/3;
  text-align: center;

  height: 110%;
  transform: translateY(-10%);
  flex-start: start;
  min-width: 100px;
  max-width: auto;
}
.card {
  display: flex;
  justify-content: center;
  align-items: center;
} 

.card_area-4 {
  
  display: flex;
  justify-content: center;
}
let cards = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
const card_els = Array.from(document.getElementsByClassName("card"));
console.log(cards)
card_els.forEach((card) => {
  card.addEventListener("click", (e) => {
    e.target.remove();
    console.log("haha")
  });
});
 2  66  2
1 Jan 1970

Solution

 1

You can’t overlap flex items directly, but you can add absolutely-positioned child elements to those flex items, and these child elements can overlap.

So you can get a pretty good CSS solution by using an additional wrapper element for each card. The outer element for each card is positioned and shrunk with flexbox; the inner element is positioned absolutely and has a fixed width, resulting in overlap (which is what you want).

The cards fit the container approximately, but not exactly. I have added 50px of right padding (two-thirds the width of one card) to the container so that the cards fit in most scenarios. Getting an exact fit would require us to calculate the right padding based on the number of cards which are in the container; this is not possible in CSS. But the approximate fit is likely to be good enough for your purpose.

enter image description here

body {
  background: #888;
}

.cards {
  display: flex;
  margin-bottom: 1em;
  border: 3px dashed white;
  width: 250px;
  color: white;
  justify-content: start;
  padding-right: 50px;
}

.cards > div {
  height: 100px;
  flex-basis: 75px;
  position: relative;
  border: 3px solid black;
  box-sizing: border-box;
}

.cards > div > div {
  position: absolute;
  top: 0;
  left: 0;
  width: 75px;
  height: 100%;
  transform: rotate(-5deg);
  padding: 5px;
  box-sizing: border-box;
}

.cards > div:nth-child(odd) > div {
  background: #ff0000df;
}

.cards > div:nth-child(even) > div {
  background: #0000ffdf;
}

.wider {
  width: 350px;
}
<!-- 5 cards -->
<div class="cards">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>

<!-- 5 cards -->
<div class="cards">
  <div><div>1</div></div>
  <div><div>2</div></div>
  <div><div>3</div></div>
  <div><div>4</div></div>
  <div><div>5</div></div>
</div>

<!-- 8 cards -->
<div class="cards">
  <div><div>1</div></div>
  <div><div>2</div></div>
  <div><div>3</div></div>
  <div><div>4</div></div>
  <div><div>5</div></div>
  <div><div>6</div></div>
  <div><div>7</div></div>
  <div><div>8</div></div>
</div>

<!-- 11 cards -->
<div class="cards">
  <div><div>1</div></div>
  <div><div>2</div></div>
  <div><div>3</div></div>
  <div><div>4</div></div>
  <div><div>5</div></div>
  <div><div>6</div></div>
  <div><div>7</div></div>
  <div><div>8</div></div>
  <div><div>9</div></div>
  <div><div>10</div></div>
  <div><div>11</div></div>
</div>

<!-- 8 cards in a wider container -->
<div class="cards wider">
  <div><div>1</div></div>
  <div><div>2</div></div>
  <div><div>3</div></div>
  <div><div>4</div></div>
  <div><div>5</div></div>
  <div><div>6</div></div>
  <div><div>7</div></div>
  <div><div>8</div></div>
</div>

<!-- 4 cards in a wider container -->
<div class="cards wider">
  <div><div>1</div></div>
  <div><div>2</div></div>
  <div><div>3</div></div>
  <div><div>4</div></div>
</div>

For perfectly accurate results, you can use Javascript to determine exactly how many cards are in each container and calculate an exact offset for each one.

enter image description here

document.querySelectorAll('.contained').forEach(c => {
  const cards = Array.from(c.children)
  // calculate the total width of the cards, assuming they are all same width
  const widthOfCards = cards[0].clientWidth * cards.length
  // calculate the offset as the difference between the total width of the cards and the width of the container, divided by one less than the number of cards
  const offset = (widthOfCards - c.clientWidth) / (cards.length - 1)
  c.insertAdjacentHTML('afterbegin', `<span>container: ${c.clientWidth}<br>cards: ${widthOfCards}<br>offset: ${offset > 0 ? Math.round(offset * 1000) / 1000 : 'none'}</span>`)
  // if the container is wider than the cards, no adjustment is necessary
  if (offset <= 0) return
  for (i = 0; i < cards.length; i++) {
    const x = cards[i]
    x.style.left = `${-1 * i * offset}px`
  }
})
body {
  background: #888;
}

.cards {
  display: flex;
  margin-bottom: 1em;
  border: 3px dashed white;
  width: 250px;
  color: white;
  position: relative;
}

.cards > div {
  width: 75px;
  height: 100px;
  transform: rotate(-5deg);
  flex-shrink: 0;
  padding: 5px;
  box-sizing: border-box;
}

.cards > div:nth-child(odd) {
  background: #ff0000df;  
}

.cards > div:nth-child(even) {
  background: #0000ffdf;  
}

.contained > div {
  position: relative;
}

.wider {
  width: 350px;
}

.cards > span {
  font-size: 0.8em;
  position: absolute;
  left: 1em;
  bottom: 1em;
  z-index: 1;
}
<!-- normal scenario, cards overflow container -->
<div class="cards">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
</div>

<!-- 5 cards contained -->
<div class="cards contained">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
</div>

<!-- 8 cards contained -->
<div class="cards contained">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
  <div>7</div>
  <div>8</div>
</div>

<!-- 8 cards contained in a wider container -->
<div class="cards contained wider">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
  <div>7</div>
  <div>8</div>
</div>

<!-- 4 cards contained in a wider container -->
<div class="cards contained wider">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
</div>

2024-07-20
Brett Donald