0

I have 2 cards with a class card that are displayed inside a card-stack. There can be N number of cards like this.

<div class="card-stack">
  <div class="clear"><button name="clear" value="Clear all" onclick="removeAllCards(this)">Clear All</button></div>
  <div class="card" style="--y:1" onclick="remove(this)">
    <div class="card-header">
      <div class="card-avatar">
        <img src="https://static.thenounproject.com/png/363633-200.png">
      </div>
      <div class="card-title">Announcement 1</div>
    </div>
    <div class="card-body">
      This is announcement 1.
    </div>
  </div>
  <div class="card" style="--y:2" onclick="remove(this)">
    <div class="card-header">
      <div class="card-avatar">
        <img src="https://static.thenounproject.com/png/363633-200.png">
      </div>
      <div class="card-title">Announcement 1</div>
    </div>
    <div class="card-body">
      This is announcement 1.
    </div>
  </div>
</div>

When a user hovers on card-stack I apply some CSS to each card

.card-stack:hover  .card{
  transform: translateY(calc((var(--y) * -105% + 200px)));
  box-shadow: none;
 }

I would like to apply the HOVER css only if there are more than 1 elements with the class card. How do I do that using CSS?

CodePen for the below snippet

function remove(element) {
  element.remove();
}

function removeAllCards(element) {
  element.remove();
  document.querySelectorAll(".card").forEach(el => el.remove());
}
body {
  background-color: #e8eaed;
}

.card {
  width: 300px;
  min-height: 150px;
  background-color: white;
  position: fixed;
  top: 75vh;
  right: 100px;
  transform: translate(-50%, -50%);
  display: grid;
  border-radius: 5px;
  font-family: sans-serif;
  font-size: 14px;
  color: #00000080;
  box-shadow: 0 5px 15px 0 #00000040, 0 5px 5px 0#00000020;
  transition: transform 200ms;
  padding:15px;
  cursor:pointer;
}

.card {
  transform: translateY(calc((var(--y) * 20px) - 50%)) scale(calc(1.0 + var(--y) * 0.05));
}

.card-avatar {
  display:block;
}
.card-avatar img {
  width:40px;
  height:40px;
  float:left;
}
.card-title {
  font-size:16px;
  display:inline-block;
  margin-top:10px;
  margin-left:10px;
}

.card-header {
  height:30px;
  display: inline-block;
}
.card-body {
  margin-top:-30px;
}

.card-stack:hover  .card{
  transform: translateY(calc((var(--y) * -105% + 200px)));
  box-shadow: none;
 }

.card-stack:hover .clear {
  visibility: visible;
  transform: translateY(calc((var(--y) * -105% + 200px)));
  position:absolute;
  top:2vh;
  right:40px;
}

.card-stack {
  position:fixed;
  width: 400px;
  min-height: 100vh;
  position: fixed;
  right: 65px;
}

.clear {
  visibility:hidden;
}
<div class="card-stack">
  <div class="clear"><button name="clear" value="Clear all" onclick="removeAllCards(this)">Clear All</button></div>
  <div class="card" style="--y:1" onclick="remove(this)">
    <div class="card-header">
      <div class="card-avatar">
        <img src="https://static.thenounproject.com/png/363633-200.png">
      </div>
      <div class="card-title">Announcement 1</div>
    </div>
    <div class="card-body">
      This is announcement 1.
    </div>
  </div>
  <div class="card" style="--y:2" onclick="remove(this)">
    <div class="card-header">
      <div class="card-avatar">
        <img src="https://static.thenounproject.com/png/363633-200.png">
      </div>
      <div class="card-title">Announcement 1</div>
    </div>
    <div class="card-body">
      This is announcement 1.
    </div>
  </div>
</div>
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
Jake
  • 25,479
  • 31
  • 107
  • 168
  • 1
    That was a bad edit, 1) you already have a runnable snippet under your pen link so there was no need to add another 2) that snippet the other user added was non-functional anyway. Looks like a textbook case of people going around converting every HTML/CSS block they see into a snippet without bothering to check if it runs in the first place. – BoltClock Mar 09 '21 at 16:38

5 Answers5

2

One solution would be to target classes that are both first-child and last-child at the same time

.myClass {
  color: tomato;
}

.myClass:first-child:last-child {
  color: DarkSlateGrey;
}
<div>
  <span class="myClass">I'm alone</span>
</div>

<div>
  <span class="myClass">We are two</span>
  <span class="myClass">We are two</span>
</div>
GuCier
  • 6,919
  • 1
  • 29
  • 36
  • Or in your example `.card-stack:hover .card:first-child:last-child{}` – GuCier Mar 09 '21 at 16:16
  • That wouldn't do - the .clear is in the way. Perhaps something using either + or :nth-child(2):last-child would work. But the styles are intended to be applied when there is more than one such element. – BoltClock Mar 09 '21 at 16:20
  • Oh yes you are right! Didn't read the code carefully enough! – GuCier Mar 09 '21 at 16:22
1

You can do what Creaforge says and override it for when there is only one .card element, except you'd need to specify the transform and box shadow twice, which means code duplication. And those transform and box shadow values are pretty long.

There are two much cleaner ways to do this:

  1. With a selector. Normally I'd recommend .card-stack:hover .card:not(:nth-child(2):last-child), but level 4 :not() support is still a little incomplete so if you're worried about that you need to do it the old way which looks like this:

    .card-stack:hover .card:not(:nth-child(2)), .card-stack:hover .card:not(:last-child) {
      transform: translateY(calc((var(--y) * -105% + 200px)));
      box-shadow: none;
    }
    
  2. Move the .clear element out of the .card-stack element. There doesn't appear to be a very good reason it needs to be in there. Then change the selector to

    .card-stack:hover .card:not(:only-child) {
      transform: translateY(calc((var(--y) * -105% + 200px)));
      box-shadow: none;
    }
    

    (again, outside of level 4 :not() you can't do :not(:first-child:last-child) like Creaforge says, but at the very least CSS gives you :only-child to use)

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
  • I needed the Clear button to be displayed and clicked on, when the user hovers on `card-stack`. The button should be stacked properly though on top-right of the cards depending on how many cards there are. I can try moving it to the bottom of the card-stack in HTML and position it to at top right of the cards using `margin-top` – Jake Mar 09 '21 at 17:04
  • @Jake: I see, if it needs to be stacked relatively to the cards then that's a good reason. You can use my first method without having to move anything around. – BoltClock Mar 09 '21 at 17:08
0

If your trying to seperate them you can add id="Card1" and id="Card2" and in css just style the id's with #Card1 and #Card2, hope this helps :)

Peacock
  • 1
  • 1
0

Maybe you can apply :nth-child(n+1) to your card class. It will only select every element after the first one.

See here for more information: https://css-tricks.com/useful-nth-child-recipies/#select-all-but-the-first-five

proto
  • 31
  • 5
0

Maybe you can add a class using javascript instead? That is, renaming the css for hover and applying that class only if card-stack has more than two children. Then utilizing "onmouseover" prop.

<div class="card-stack" onmouseover="onHover(this)">
...
</div>
.card-stack-h:hover  .card{
  transform: translateY(calc((var(--y) * -105% + 200px)));
  box-shadow: none;
 }

.card-stack-h:hover .clear {
  visibility: visible;
  transform: translateY(calc((var(--y) * -105% + 200px)));
  position:absolute;
  top:2vh;
  right:40px;
}
function onHover(stack) {
  let cards = stack.querySelectorAll(".card")
  if(cards.length > 1) stack.classList.add("card-stack-h")
  else stack.classList.remove("card-stack-h");
}

Codepen

howtopythonpls
  • 770
  • 1
  • 6
  • 18