CARDS

3D Smartwatch Fitness Dashboard Reveal Card

An interactive smartwatch fitness card with a glowing dial, animated health stats, product pop-out and smooth 3D reveal.

Futuristic smartwatch displaying heart rate and fitness stats
YOUR HEALTH • YOUR POWER

STAY ACTIVE LIVE BETTER

Track your steps, heart rate and calories with a smarter fitness experience.

Explore Watch →
8,432 Steps
98 Heart Rate
320 Calories
HTML
<div class="watch-stage">

  <div class="watch-card" id="watchCard">

    <button
      class="watch-trigger"
      id="watchTrigger"
      aria-label="Open smartwatch fitness dashboard"
    >
      <img
        src="https://getcoderipple.com/wp-content/uploads/2026/09/Smart-Watch-Dial.png"
        alt="Neon smartwatch fitness dial with running icon"
      >
    </button>

    <div class="watch-content">

      <img
        src="https://getcoderipple.com/wp-content/uploads/2026/09/Smart-Watch-1.png"
        class="watch-product"
        alt="Futuristic smartwatch displaying heart rate and fitness stats"
      >

      <div class="watch-info">

        <span class="watch-kicker">
          YOUR HEALTH • YOUR POWER
        </span>

        <h2>
          STAY ACTIVE
          <span>LIVE BETTER</span>
        </h2>

        <p>
          Track your steps, heart rate and calories
          with a smarter fitness experience.
        </p>

        <a href="#" class="watch-btn">
          Explore Watch →
        </a>

      </div>

      <div class="watch-stats">

        <div class="watch-stat watch-stat-steps">

          <div class="watch-stat-icon">◆</div>

          <div class="watch-stat-text">
            <strong>8,432</strong>
            <span>Steps</span>
          </div>

          <div class="watch-bars">
            <i></i>
            <i></i>
            <i></i>
            <i></i>
          </div>

        </div>

        <div class="watch-stat watch-stat-heart">

          <div class="watch-stat-icon">♥</div>

          <div class="watch-stat-text">
            <strong>98</strong>
            <span>Heart Rate</span>
          </div>

          <div class="watch-pulse">
            <i></i>
            <i></i>
            <i></i>
            <i></i>
            <i></i>
          </div>

        </div>

        <div class="watch-stat watch-stat-calories">

          <div class="watch-stat-icon">●</div>

          <div class="watch-stat-text">
            <strong>320</strong>
            <span>Calories</span>
          </div>

          <div class="watch-bars">
            <i></i>
            <i></i>
            <i></i>
            <i></i>
          </div>

        </div>

      </div>

    </div>

  </div>

</div>
CSS
.watch-stage{
  width:100%;
  min-height:620px;
  display:flex;
  align-items:center;
  justify-content:center;
  padding:130px 50px;
  background:#070d19;
  overflow:visible;
}

.watch-card{
  position:relative;
  width:150px;
  height:150px;
  border-radius:50%;
  background:#0a1220;
  border:1px solid rgba(34,211,238,.3);
  box-shadow:
    0 20px 50px rgba(0,0,0,.5),
    0 0 35px rgba(34,211,238,.12);
  overflow:visible;
  transform-style:preserve-3d;
  transition:
    width .7s cubic-bezier(.22,.9,.34,1),
    height .7s cubic-bezier(.22,.9,.34,1),
    border-radius .6s ease,
    box-shadow .5s ease,
    transform .15s ease;
}

.watch-trigger{
  position:absolute;
  inset:0;
  width:150px;
  height:150px;
  padding:0;
  border:0;
  border-radius:50%;
  background:transparent;
  cursor:pointer;
  z-index:10;
  transition:
    opacity .35s ease,
    transform .5s ease;
}

.watch-trigger img{
  width:100%;
  height:100%;
  display:block;
  object-fit:contain;
  filter:
    drop-shadow(
      0 0 18px rgba(34,211,238,.3)
    );
}

.watch-trigger:hover{
  transform:scale(1.06);
}

.watch-content{
  position:absolute;
  inset:0;
  display:flex;
  align-items:center;
  opacity:0;
  pointer-events:none;
  transition:
    opacity .35s ease .2s;
}

.watch-product{
  position:absolute;
  left:-105px;
  bottom:-55px;
  width:350px;
  max-width:none;
  opacity:0;
  z-index:4;
  pointer-events:none;
  transform:
    translateX(-90px)
    translateY(35px)
    rotate(-10deg)
    scale(.65);
  filter:
    drop-shadow(
      0 32px 26px rgba(0,0,0,.5)
    );
  transition:
    opacity .4s ease .15s,
    transform .7s cubic-bezier(.18,.9,.3,1.12) .08s;
}

.watch-info{
  position:relative;
  z-index:3;
  width:35%;
  margin-left:245px;
  opacity:0;
  transform:translateY(18px);
  transition:
    opacity .4s ease .28s,
    transform .5s ease .25s;
}

.watch-kicker{
  display:inline-block;
  margin-bottom:12px;
  color:#22d3ee;
  font-size:10px;
  font-weight:800;
  letter-spacing:1.8px;
}

.watch-info h2{
  margin:0 0 13px;
  color:#ffffff;
  font-size:32px;
  line-height:1.05;
}

.watch-info h2 span{
  display:block;
  color:#a855f7;
}

.watch-info p{
  margin:0 0 20px;
  max-width:240px;
  color:#aab4c8;
  font-size:13px;
  line-height:1.6;
}

.watch-btn{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  min-height:42px;
  padding:0 18px;
  border:1px solid rgba(34,211,238,.45);
  border-radius:999px;
  background:
    linear-gradient(
      135deg,
      #a855f7,
      #22d3ee
    );
  color:#ffffff;
  font-size:13px;
  font-weight:700;
  text-decoration:none !important;
  border-bottom:0 !important;
  box-shadow:
    0 0 20px rgba(34,211,238,.15);
  transition:
    transform .2s ease,
    box-shadow .2s ease;
}

.watch-btn:hover{
  color:#ffffff;
  transform:translateY(-2px);
  box-shadow:
    0 0 28px rgba(34,211,238,.25);
}

.watch-stats{
  position:absolute;
  right:28px;
  top:50%;
  width:180px;
  display:flex;
  flex-direction:column;
  gap:10px;
  opacity:0;
  transform:
    translateY(-50%)
    translateX(25px);
  transition:
    opacity .4s ease .35s,
    transform .5s ease .3s;
}

.watch-stat{
  min-height:68px;
  display:flex;
  align-items:center;
  gap:10px;
  padding:10px 12px;
  background:
    rgba(10,20,36,.85);
  border:
    1px solid rgba(255,255,255,.1);
  border-radius:14px;
  backdrop-filter:blur(12px);
}

.watch-stat-icon{
  width:28px;
  text-align:center;
  color:#22d3ee;
  font-size:20px;
}

.watch-stat-heart .watch-stat-icon{
  color:#ff4778;
}

.watch-stat-calories .watch-stat-icon{
  color:#f59e0b;
}

.watch-stat-text{
  display:flex;
  flex-direction:column;
  min-width:60px;
}

.watch-stat-text strong{
  color:#ffffff;
  font-size:18px;
  line-height:1.1;
}

.watch-stat-text span{
  margin-top:3px;
  color:#8ea0bb;
  font-size:10px;
}

.watch-bars{
  height:34px;
  display:flex;
  align-items:flex-end;
  gap:3px;
  margin-left:auto;
}

.watch-bars i{
  display:block;
  width:3px;
  height:10px;
  border-radius:4px;
  background:#22d3ee;
  animation:
    watchBars .7s ease-in-out infinite alternate;
  animation-play-state:paused;
}

.watch-bars i:nth-child(2){
  animation-delay:-.2s;
}

.watch-bars i:nth-child(3){
  animation-delay:-.4s;
}

.watch-bars i:nth-child(4){
  animation-delay:-.1s;
}

.watch-stat-calories .watch-bars i{
  background:#f59e0b;
}

.watch-pulse{
  height:34px;
  display:flex;
  align-items:center;
  gap:2px;
  margin-left:auto;
}

.watch-pulse i{
  display:block;
  width:3px;
  height:10px;
  border-radius:5px;
  background:#ff4778;
  animation:
    watchPulse .55s ease-in-out infinite alternate;
  animation-play-state:paused;
}

.watch-pulse i:nth-child(2){
  animation-delay:-.2s;
}

.watch-pulse i:nth-child(3){
  animation-delay:-.4s;
}

.watch-pulse i:nth-child(4){
  animation-delay:-.1s;
}

.watch-pulse i:nth-child(5){
  animation-delay:-.3s;
}

@keyframes watchBars{
  from{
    height:8px;
  }

  to{
    height:30px;
  }
}

@keyframes watchPulse{
  from{
    height:8px;
  }

  to{
    height:30px;
  }
}

.watch-card.active{
  width:760px;
  height:330px;
  border-radius:30px;
  background:
    radial-gradient(
      circle at 18% 80%,
      rgba(168,85,247,.16),
      transparent 32%
    ),
    radial-gradient(
      circle at 75% 15%,
      rgba(34,211,238,.1),
      transparent 35%
    ),
    linear-gradient(
      135deg,
      #08111f,
      #0c1424
    );
  box-shadow:
    0 35px 80px rgba(0,0,0,.55),
    0 0 45px rgba(34,211,238,.1),
    0 0 60px rgba(168,85,247,.08);
}

.watch-card.active .watch-trigger{
  opacity:0;
  transform:
    scale(.45)
    rotate(-20deg);
  pointer-events:none;
}

.watch-card.active .watch-content{
  opacity:1;
  pointer-events:auto;
}

.watch-card.active .watch-product{
  opacity:1;
  transform:
    translateX(0)
    translateY(0)
    rotate(-5deg)
    scale(1);
}

.watch-card.active .watch-info{
  opacity:1;
  transform:translateY(0);
}

.watch-card.active .watch-stats{
  opacity:1;
  transform:
    translateY(-50%)
    translateX(0);
}

.watch-card.active .watch-bars i,
.watch-card.active .watch-pulse i{
  animation-play-state:running;
}

@media(max-width:800px){

  .watch-stage{
    min-height:780px;
    padding:120px 18px;
  }

  .watch-card.active{
    width:92%;
    max-width:430px;
    height:600px;
  }

  .watch-product{
    left:50%;
    top:-85px;
    bottom:auto;
    width:280px;
    transform:
      translateX(-50%)
      translateY(-80px)
      rotate(-8deg)
      scale(.65);
  }

  .watch-card.active .watch-product{
    transform:
      translateX(-50%)
      translateY(0)
      rotate(-4deg)
      scale(1);
  }

  .watch-info{
    width:auto;
    margin:220px 26px 0;
  }

  .watch-stats{
    right:24px;
    left:24px;
    top:auto;
    bottom:25px;
    width:auto;
    transform:translateX(20px);
  }

  .watch-card.active .watch-stats{
    transform:translateX(0);
  }

}
JavaScript
document.addEventListener(
  'DOMContentLoaded',
  function() {

    var watchCard =
      document.getElementById('watchCard');

    var watchTrigger =
      document.getElementById('watchTrigger');

    if (watchCard) {

      if (watchTrigger) {

        watchTrigger.addEventListener(
          'click',
          function(e) {

            e.preventDefault();

            watchCard.classList.add('active');

          }
        );

      }


      document.addEventListener(
        'click',
        function(e) {

          if (!watchCard.contains(e.target)) {

            watchCard.classList.remove('active');

            watchCard.style.transform =
              'perspective(1000px) rotateX(0deg) rotateY(0deg)';

          }

        }
      );


      watchCard.addEventListener(
        'mousemove',
        function(e) {

          if (!watchCard.classList.contains('active')) {
            return;
          }

          var rect =
            watchCard.getBoundingClientRect();

          var mouseX =
            e.clientX - rect.left;

          var mouseY =
            e.clientY - rect.top;

          var centerX =
            rect.width / 2;

          var centerY =
            rect.height / 2;

          var rotateY =
            ((mouseX - centerX) / centerX) * 4;

          var rotateX =
            ((centerY - mouseY) / centerY) * 3;

          watchCard.style.transform =
            'perspective(1000px) rotateX(' +
            rotateX +
            'deg) rotateY(' +
            rotateY +
            'deg)';

        }
      );


      watchCard.addEventListener(
        'mouseleave',
        function() {

          watchCard.style.transform =
            'perspective(1000px) rotateX(0deg) rotateY(0deg)';

        }
      );

    }

  }
);

How This 3D Smartwatch Fitness Dashboard Reveal Card Works

The 3D Smartwatch Fitness Dashboard Reveal Card starts as a compact circular smartwatch dial rather than showing the full dashboard immediately. The outer .watch-card is initially 150px by 150px with a circular border-radius: 50%, a dark background, a subtle cyan border, and layered shadows. Inside it, the .watch-trigger button fills the same 150px circle and displays the smartwatch dial image. Hovering over this button scales it to 1.06, giving the closed state a small interactive response.

When the trigger is clicked, JavaScript adds the active class to the main card. This class changes the card into a 760px by 330px panel with a 30px border radius. The expanded background combines two radial gradients with a dark linear gradient, creating the cyan and purple glow around the fitness dashboard.

The reveal is handled through several coordinated CSS transitions. The card itself changes width and height over .7s, while the trigger fades out and scales down. The .watch-content becomes visible after a short delay, and the smartwatch product image moves in from the left. Its starting transform combines translateX(-90px), translateY(35px), rotate(-10deg), and scale(.65). In the active state, those values transition toward a larger, less rotated final position.

The central information area follows slightly later. .watch-info begins with zero opacity and an 18px downward translation. Once the card becomes active, it fades in and moves to its normal position. The content includes a small health label, the “STAY ACTIVE / LIVE BETTER” heading, a description, and a gradient “Explore Watch” button.

Three dashboard stat cards sit on the right side of the expanded layout. They display steps, heart rate, and calories. Each panel uses a translucent dark background with backdrop-filter: blur(12px), a thin light border, and rounded corners. The steps and calories panels contain four animated bars, while the heart-rate panel uses five pulse bars.

These animations are defined with @keyframes watchBars and @keyframes watchPulse. Both change bar heights between 8px and 30px. Their animations are initially paused, so the dashboard does not animate while closed. The .active state switches animation-play-state to running, causing the fitness indicators to move only after the dashboard has been revealed.

JavaScript also provides the 3D pointer interaction. While the dashboard is active, the mousemove listener measures the pointer position with getBoundingClientRect(). Horizontal movement produces up to roughly four degrees of Y-axis rotation, while vertical movement controls approximately three degrees of X-axis rotation. The result is applied with perspective(1000px) rotateX(...) rotateY(...). Moving the pointer away resets both rotations to zero.

Clicking anywhere outside the card removes the active class and resets the transform, returning the component to its original circular state. The implementation relies on class changes rather than rebuilding the interface in JavaScript. For more information about this approach, see the MDN classList documentation.

The responsive rules switch the composition below 800px. Instead of the wide desktop card, the active panel becomes up to 430px wide and 600px tall. The smartwatch moves above the content, the information section gains top spacing, and the statistics are arranged near the bottom of the card.

Customizing This 3D Smartwatch Fitness Dashboard Reveal Card

The component can be adjusted mainly through its CSS values and the existing text and images in the HTML.

  • Change the closed card size by editing the 150px width and height used by .watch-card and .watch-trigger.
  • Adjust the expanded desktop dimensions from 760px × 330px in .watch-card.active.
  • Replace #22d3ee, #a855f7, #ff4778, and #f59e0b to change the cyan, purple, heart-rate, and calorie accent colors.
  • Modify the .7s expansion transition or the delayed opacity transitions to make the reveal faster or slower.
  • Change the 350px .watch-product width and its translation, rotation, or scale values to reposition the smartwatch artwork.
  • Edit the * 4 and * 3 JavaScript multipliers to increase or reduce the pointer-controlled 3D tilt.
  • Replace the smartwatch images, dashboard text, statistics, and button label directly in the existing HTML.
  • Adjust the 800px media query, 430px maximum width, or 600px mobile height to suit a different responsive layout.

Where to Use This 3D Smartwatch Fitness Dashboard Reveal Card

This reveal pattern works well when a product needs a compact starting state followed by richer information after interaction.

  • Smartwatch and wearable product landing pages.
  • Fitness app promotional sections.
  • Health-tracking dashboard previews.
  • Product feature showcases for steps, calories, or heart-rate monitoring.
  • Technology portfolio projects that demonstrate CSS transitions and pointer-based 3D effects.
  • E-commerce feature sections where product imagery and specifications should remain hidden until requested.

Because the dashboard opens on click and closes when the user clicks outside it, it can work as a focused product preview without permanently occupying a large amount of page space. For another product-focused interaction using a similar reveal concept, the 3D Headphone Audio Reveal Card is a closely related CodeRipple component.

Frequently Asked Questions

Does this smartwatch reveal card use JavaScript?

Yes. JavaScript adds the active class when the circular smartwatch trigger is clicked, removes it when the user clicks outside the card, and calculates the interactive 3D tilt while the pointer moves across the expanded panel.

How does the card create its 3D movement?

The script calculates the pointer position relative to the card center and converts that position into small rotateX() and rotateY() values. CSS perspective(1000px) is included in the inline transform to create depth.

Do the fitness bars animate while the card is closed?

No. The bar and pulse animations use animation-play-state: paused by default. They switch to running only when .watch-card.active is present.

Is the component responsive on smaller screens?

Yes. A media query at max-width: 800px changes the expanded dashboard into a vertical layout. The smartwatch is moved above the content, the card becomes taller, and the statistics move toward the bottom.

This component combines a compact smartwatch trigger, staged CSS transitions, animated fitness indicators, responsive styling, and JavaScript-controlled 3D movement in one interactive product card.

Scroll to Top