DEV Community

Cover image for Pure CSS Timed Cards Opening
Prahalad S
Prahalad S

Posted on

Pure CSS Timed Cards Opening

Pure CSS Timed Cards Opening

The Pure CSS Timed Cards Carousel displays multiple cards that slide horizontally across the screen at fixed intervals.
Each card includes:

  • Title
  • Description
  • Image
  • Optional button
  • Auto-expanding animation
  • Timed text reveal
  • Reflection + blend modes

Each card uses a CSS variable --timer to control staggered animation timing.

HTML Structure

<!-- responsive coming soon   
 -->
<span class="hamburger">&#9776</span> 

    <section>
        <div class="carousel">
            <ul class="gallery">
                <li class="card" style="--timer: 1; ">
                    <h2>Moments in Motion</h2>
                    <p>Experience the world as it moves — where every frame tells a story of life in motion.</p>
                    <!-- <button>View More</button> --><span></span>

                    <img src="https://img.freepik.com/free-vector/silhouette-twilight-forest-landscape-background_1308-69379.jpg"
                        alt="">
                </li>

                <li class="card" style="--timer: 2">
                    <h2>Through the Lens</h2>
                    <p>A glimpse into the unseen beauty captured by the eye of imagination.</p>
                    <!-- <button>View More</button> --><span></span>
                    <img src="https://img.freepik.com/free-vector/flat-design-forest-landscape_23-2149162735.jpg"
                        alt="">
                </li>

                <li class="card" style="--timer: 3">
                    <h2>Wanderlust Chronicles</h2>
                    <p>Embark on a journey through breathtaking landscapes and untold adventures.</p>
                    <!-- <button>View More</button> --><span></span>
                    <img src="https://img.freepik.com/free-vector/natural-landscape-wallpaper-video-conferencing_23-2148651571.jpg"
                        alt="">
                </li>

                <li class="card" style="--timer: 4">
                    <h2>Adventure Awaits</h2>
                    <p>Step into the wild and discover nature’s finest hidden gems.</p>
                    <!-- <button>View More</button> --><span></span>
                    <img src="https://img.freepik.com/free-vector/natural-landscape-wallpaper-video-conferencing_23-2148651572.jpg"
                        alt="">
                </li>

                <li class="card" style="--timer: 5">
                    <h2>Footprints Around the World</h2>
                    <p>Every step tells a story — from the dunes to the valleys beyond the horizon.</p>
                    <!-- <button>View More</button> --><span></span>
                    <img src="https://img.freepik.com/free-vector/flat-design-adventure-background_23-2149059266.jpg"
                        alt="">
                </li>

                <li class="card" style="--timer: 6">
                    <h2>Dreams in Color</h2>
                    <p>A vibrant collection of landscapes painted by light, nature, and emotion.</p>
                    <!-- <button>View More</button> --><span></span>
                    <img src="https://img.freepik.com/free-vector/gradient-adventure-background_23-2149048608.jpg"
                        alt="">
                </li>

                <li class="card" style="--timer: 7">
                    <h2>Canvas of Emotions</h2>
                    <p>Where every hue, shade, and line reflects the pulse of the natural world.</p>
                    <!-- <button>View More</button> -->
                    <span></span>
                    <img src="https://img.freepik.com/free-vector/flat-design-forest-landscape_23-2149162734.jpg"
                        alt="">
                </li>

                <li class="card" style="--timer: 8">
                    <h2>The Story of Us</h2>
                    <p>A timeless tale of connection — between people, nature, and the places we call home.</p>
                    <!-- <button>View More</button> --><span></span>
                    <img src="https://img.freepik.com/free-vector/hand-drawn-mountain-landscape_23-2149156633.jpg"
                        alt="">
                </li>

                <li class="card" style="--timer: 9">
                    <h2>Moments of Magic</h2>
                    <p>Capture the beauty of fleeting instants that linger forever in our hearts.</p>
                    <!-- <button>View More</button> --><span></span>
                    <img src="https://img.freepik.com/free-vector/gradient-winter-solstice-illustration_23-2149201708.jpg"
                        alt="">
                </li>
            </ul>
        </div>

    </section>
Enter fullscreen mode Exit fullscreen mode

How the Timing System Works

animation-delay: calc(
  var(--duration) * ((var(--timer) - 1) / var(--num) - 1)
);

Enter fullscreen mode Exit fullscreen mode

Animation Sequence Logic:

✔ Phase 1 — Card enters

  • Card slides in from right
  • Title and paragraph are hidden
  • Image is dimmed

✔ Phase 2 — Card becomes active

  • Card enlarges (scaleEffect)
  • Title grows to 50px (scaleText)
  • Description fades in (paraText)
  • Button becomes visible (button optional)

✔ Phase 3 — Card exits

  • Text fades out
  • Card shrinks
  • Image fades to 0
  • Card slides out left
  • Then the next card begins.

CSS

* {
  margin: 0;
  padding: 0;
}

:root {
  --num: 9;
  /* number of images */
  --wd: 130;
  /* width of card div, in px */
  --gap: 20px;
  /* gap between cards */
  --duration: 27s;
  /* total animation duration */
}

body {
  background-color: #000000;
  font-family: "Poppins", sans-serif;
}

section {
  width: 100%;
  margin: 0 auto;
}

.carousel {
  display: flex;
  align-items: flex-start;
  height: 100vh;
  overflow: hidden;
  /* mask-image: linear-gradient(to bottom, transparent, #000 5%, #000 80%, transparent); */
  justify-content: center;
  width: 100%;
}

.carousel .gallery {
  list-style-type: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  position: relative;
}

.carousel .gallery .card {
  width: calc(var(--wd) / 1.2 * 1px);
  height: auto;
  position: absolute;
  left: 100%;
  animation: slide var(--duration) linear infinite,
    scaleEffect var(--duration) ease-in-out infinite;
  /* Stagger cards using --timer (should be 1 to 9 for your HTML cards) */
  animation-delay: calc(
    var(--duration) * ((var(--timer) - 1) / var(--num) - 1)
  );
}

.carousel .gallery .card h2 {
  position: absolute;
  font-weight: 800;
  z-index: 2;
  top: 100px;
  font-size: 20px;
  transform: translate(0px, 100px);
  color: white;
  left: 0;
  width: 130px;
  text-transform: uppercase;
  animation: scaleText var(--duration) ease-in-out infinite;
  /* Stagger cards using --timer (should be 1 to 9 for your HTML cards) */
  animation-delay: calc(
    var(--duration) * ((var(--timer) - 1) / var(--num) - 1)
  );
  transition: transform 0.5s ease;
  mix-blend-mode: overlay;
}

.carousel .gallery .card p {
  opacity: 1;
  z-index: 2;
  position: absolute;
  top: 350px;
  font-weight: 200;
  color: white;
  transform: translate(0px, 200px);
  animation: paraText var(--duration) ease-in-out infinite;
  animation-delay: calc(
    var(--duration) * ((var(--timer) - 1) / var(--num) - 1)
  );
  transition: transform 0.5s ease;
  mix-blend-mode: overlay;
}

.carousel .gallery .card img {
  width: 100%;
  border-radius: 5px;
  filter: brightness(0.7);
  transform: translate(130px, 100px);
  border: 1pt dashed #bcbcbc;
  animation: moveImg var(--duration) ease-in-out infinite;
  /* Stagger cards using --timer (should be 1 to 9 for your HTML cards) */
  animation-delay: calc(
    var(--duration) * ((var(--timer) - 1) / var(--num) - 1)
  );
  -webkit-box-reflect: below 0px
    linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.4));
  transition: transform var(--duration) s ease;
}

button {
  background: transparent;
  padding: 15px;
  color: white;
  text-align: center;
  text-transform: uppercase;
  border: 0;
  border-radius: 20px;
  position: absolute;
  top: 270px;
  mix-blend-mode: color-dodge;
  border: 0.05rem dashed white;
  opacity: 0;
  z-index: 2;
  transform: translate(130px, 200px);
  animation: button var(--duration) ease-in-out infinite;
  animation-delay: calc(
    var(--duration) * ((var(--timer) - 1) / var(--num) - 1)
  );
  transition: transform 0.5s ease;
}

.carousel .gallery .card h2:before {
  content: "";
  width: 10px;
  height: 5px;
  background: rgb(192, 192, 192);
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  opacity: 1;
  transform-origin: left;
  /* important */
  animation: loader var(--duration) ease-in-out infinite;
  animation-delay: calc(
    var(--duration) * ((var(--timer) - 1) / var(--num) - 1)
  );
}

.hamburger {
  color: #ffffff;
  position: fixed;
  right: 2%;
  top: 2%;
  font-size: 30px;
  font-weight: lighter;
}

@keyframes slide {
  0% {
    left: 100%;
  }

  /* scroll 1s, pause 5s per card (≈1.85% scroll, 9.26% pause per cycle) */
  1.85% {
    left: calc(100% - (1 * (100% + var(--wd) * 1px) / var(--num)));
  }

  11.11% {
    left: calc(100% - (1 * (100% + var(--wd) * 1px) / var(--num)));
  }

  12.96% {
    left: calc(100% - (2 * (100% + var(--wd) * 1px) / var(--num)));
  }

  22.22% {
    left: calc(100% - (2 * (100% + var(--wd) * 1px) / var(--num)));
  }

  24.07% {
    left: calc(100% - (3 * (100% + var(--wd) * 1px) / var(--num)));
  }

  33.33% {
    left: calc(100% - (3 * (100% + var(--wd) * 1px) / var(--num)));
  }

  35.18% {
    left: calc(100% - (4 * (100% + var(--wd) * 1px) / var(--num)));
  }

  44.44% {
    left: calc(100% - (4 * (100% + var(--wd) * 1px) / var(--num)));
  }

  46.29% {
    left: calc(100% - (5 * (100% + var(--wd) * 1px) / var(--num)));
  }

  55.55% {
    left: calc(100% - (5 * (100% + var(--wd) * 1px) / var(--num)));
  }

  57.4% {
    left: calc(100% - (6 * (100% + var(--wd) * 1px) / var(--num)));
  }

  66.66% {
    left: calc(100% - (6 * (100% + var(--wd) * 1px) / var(--num)));
  }

  68.51% {
    left: calc(100% - (7 * (100% + var(--wd) * 1px) / var(--num)));
  }

  77.77% {
    left: calc(100% - (7 * (100% + var(--wd) * 1px) / var(--num)));
  }

  79.62% {
    left: calc(100% - (8 * (100% + var(--wd) * 1px) / var(--num)));
  }

  88.88% {
    left: calc(100% - (9 * (100% + var(--wd) * 1px) / var(--num)));
  }

  90.73% {
    left: calc(-1 * var(--wd) * 1px);
    font-size: 40px;
  }

  100% {
    left: calc(-1 * var(--wd) * 1px);
  }
}

@keyframes scaleEffect {
  0%,
  80% {
    width: 130px;
    z-index: 0;
    opacity: 1;
    margin-left: 520px;
  }

  81%,
  100% {
    width: calc(100% + 260px);
    margin-left: -130px;
    z-index: -1;
    opacity: 1;
  }
}

@keyframes scaleText {
  0%,
  80% {
    font-size: 14px;
    transform: translate(0px, 100px);
    width: 130px;
    left: 0;
    top: 120px;
  }

  81%,
  88.88% {
    font-size: 50px;
    width: auto;
    left: 15%;
    top: 370px;
    opacity: 1;
    transform: translate(65px, 100px);
  }

  61%,
  87% {
    transform: translate(0px, 100px);
  }

  88.89%,
  100% {
    opacity: 0;
    transform: translate(0px, 100px);
  }
}

@keyframes paraText {
  0%,
  60% {
    font-size: 1rem;
    transform: translate(0px, 200px);
    width: 130px;
    left: 15%;
    top: 350px;
    opacity: 0;
  }

  61%,
  87% {
    font-size: 2rem;
    width: 30%;
    left: 15%;
    top: 350px;
    opacity: 1;
    transform: translate(0px, 200px);
  }

  88%,
  100% {
    opacity: 0;
    transform: translate(130px, 200px);
  }
}

@keyframes button {
  0%,
  80% {
    transform: translate(0px, 200px);
    left: 15%;
    top: 500px;
    opacity: 0;
  }

  81%,
  88.88% {
    left: 15%;
    top: 500px;
    opacity: 1;
  }

  88.89%,
  100% {
    opacity: 0;
    transform: translate(130px, 200px);
  }
}

@keyframes loader {
  0%,
  70% {
    transform: scaleX(0);
    opacity: 1;
    left: -200%;
    top: 300%;
    background:orange;
  }

  71% {
    transform: scaleX(50);
    opacity: 1;
    left: -200%;
    top: 300%;
      background:orange;
  }

  78%,
  100% {
    transform: scaleX(0);
    opacity: 0;
    left: -200%;
    top: 300%; background:black;
  }
}

@keyframes moveImg {
  0%,
  88% {
    transform: translate(0px, 100px);
    opacity: 1;
    mix-blend-mode: overlay;
    z-index: 0;
  }

  99%,
  100% {
    opacity: 0;
    z-index: -1;
    transform: translate(0px, 100px);
  }
}

Enter fullscreen mode Exit fullscreen mode

Codepen Demo

Top comments (0)