'Number of sibling elements in a calculation in SCSS

I'd like to customize an animation in SCSS based on how many sibling elements the animated element has. The full SCSS code is here for reference, however the relevant part is in the @keyframes. In the calculation for top, I'd like to set it to something like:

top: calc( $sibling-count * ( $fixed-image-height + $margin) )px;

Here is the full SCSS file

$fixed-image-height: 200px;
$margin: 25px;

.up,
.down {
  animation-duration: 5000ms;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
}

.up {
  animation-name: slide-up;
}

.down {
  animation-name: slide-down;
}


@keyframes slide-up {
  from {
    top: 100%;
    opacity: 1;
  }

  to {
    top: calc(100% - 900px);
    opacity: 1;
  }
}

@keyframes slide-down {
  from {
    top: 0px;
    opacity: 1;
  }

  to {
    top: calc(100% - 900px);
    opacity: 1;
  }
}


Update: I ended up refactoring my code a bit so that I constrained the max number of elements that could appear on the page, and then implementing a for loop in CSS that generated classes up to that max number. I also set a fixed height for each image so that I could make some assumptions about when to kick off each animation (via animation-delay):

@import '../styles/breakpoints.scss';
@import '../styles/colors.scss';

// Dev Note:
// increasing $speed will slow down the reel

$fixed-image-height: 200px;
$margin: 25px;
$max-images-in-view: 10;
$speed: 30;
$offset: calc($speed * 1000ms);

.vertical-image-reel {
  position: relative;
  height: 100%;
  width: 100%;
  display: flex;
  justify-content: center;

  .image-container {
    position: absolute;
    animation-iteration-count: infinite;
    animation-timing-function: linear;
    animation-duration: #{$speed}s;

    .image {
      height: $fixed-image-height;
      width: 100%;
    }
  }

  @for $i from 0 through 19 {
    .up-#{$i} {
      animation-delay: calc(
        1000ms * $speed/$max-images-in-view * (#{$i} + 1) - $offset
      );
      animation-name: slide-up-#{$i};
      top: 100%;
    }
    .down-#{$i} {
      animation-delay: calc(
        1000ms * $speed/$max-images-in-view * (#{$i} + 1) - $offset
      );
      animation-name: slide-down-#{$i};
      top: -100%;
    }

    @keyframes slide-up-#{$i} {
      from {
        top: 100%;
        opacity: 1;
      }

      to {
        top: calc(100% - $max-images-in-view * ($fixed-image-height + $margin));
        opacity: 1;
      }
    }

    @keyframes slide-down-#{$i} {
      from {
        top: calc(100% - $max-images-in-view * ($fixed-image-height + $margin));
        opacity: 1;
      }

      to {
        top: 100%;
        opacity: 1;
      }
    }
  }
}


Solution 1:[1]

Although not possible (as far as I know) to calculate siblings, if you know the approximate range, you could iterate through them, and use the General sibling combinator to target each, along side the index from the loop.

Here's an example:

@mixin iterate-siblings($min, $max) {
  @for $i from $min through $max {
    &:first-child:nth-last-child(#{$i}),
    &:first-child:nth-last-child(#{$i}) ~ & {
      top: calc( $i * ( $fixed-image-height + $margin) )px;
    }
  }
}

@keyframes slide-down {
  from {
    top: 0px;
    opacity: 1;
  }
  to {
    @include iterate-siblings(1, 6);
    opacity: 1;
  }
}

If you can share a runnable snippet of your current code, I can update the answer to match your situation

Solution 2:[2]

Update: I ended up refactoring my code a bit so that I constrained the max number of elements that could appear on the page, and then implementing a for loop in CSS that generated classes up to that max number. I also set a fixed height for each image so that I could make some assumptions about when to kick off each animation (via animation-delay):

@import '../styles/breakpoints.scss';
@import '../styles/colors.scss';

// Dev Note:
// increasing $speed will slow down the reel

$fixed-image-height: 200px;
$margin: 25px;
$max-images-in-view: 10;
$speed: 30;
$offset: calc($speed * 1000ms);

.vertical-image-reel {
  position: relative;
  height: 100%;
  width: 100%;
  display: flex;
  justify-content: center;

  .image-container {
    position: absolute;
    animation-iteration-count: infinite;
    animation-timing-function: linear;
    animation-duration: #{$speed}s;

    .image {
      height: $fixed-image-height;
      width: 100%;
    }
  }

  @for $i from 0 through 19 {
    .up-#{$i} {
      animation-delay: calc(
        1000ms * $speed/$max-images-in-view * (#{$i} + 1) - $offset
      );
      animation-name: slide-up-#{$i};
      top: 100%;
    }
    .down-#{$i} {
      animation-delay: calc(
        1000ms * $speed/$max-images-in-view * (#{$i} + 1) - $offset
      );
      animation-name: slide-down-#{$i};
      top: -100%;
    }

    @keyframes slide-up-#{$i} {
      from {
        top: 100%;
        opacity: 1;
      }

      to {
        top: calc(100% - $max-images-in-view * ($fixed-image-height + $margin));
        opacity: 1;
      }
    }

    @keyframes slide-down-#{$i} {
      from {
        top: calc(100% - $max-images-in-view * ($fixed-image-height + $margin));
        opacity: 1;
      }

      to {
        top: 100%;
        opacity: 1;
      }
    }
  }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Lissy93
Solution 2 Ashwin