BUTTONS

Energy Portal Download Button

A futuristic download button with rotating energy rings, glowing particles, a portal burst, and a smooth completion state. Copy the HTML, CSS and JavaScript for your own project.

HTML
<div class="cr-portal-stage">

  <button
    class="cr-portal-btn"
    id="crPortalBtn"
    type="button"
  >

    <span class="cr-portal-rings" aria-hidden="true">
      <span class="cr-ring cr-ring-one"></span>
      <span class="cr-ring cr-ring-two"></span>
      <span class="cr-ring cr-ring-three"></span>
    </span>

    <span
      class="cr-energy-core"
      aria-hidden="true"
    ></span>

    <span
      class="cr-download-icon"
      aria-hidden="true"
    >
      <span class="cr-arrow-line"></span>
      <span class="cr-arrow-head"></span>
    </span>

    <span
      class="cr-portal-particles"
      aria-hidden="true"
    >
      <i></i>
      <i></i>
      <i></i>
      <i></i>
      <i></i>
      <i></i>
    </span>

    <span class="cr-portal-label">
      Download File
    </span>

    <span class="cr-portal-success">
      <span class="cr-success-check">✓</span>
      <span>Download Complete</span>
    </span>

  </button>

</div>
CSS
.cr-portal-stage {
  min-height: 360px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background:
    radial-gradient(
      circle at 50% 50%,
      rgba(34, 211, 238, 0.12),
      transparent 34%
    ),
    #070d19;
}

.cr-portal-btn {
  position: relative;
  width: 260px;
  height: 78px;
  padding: 0;
  border: 1px solid rgba(103, 232, 249, 0.38);
  border-radius: 22px;
  overflow: hidden;
  cursor: pointer;
  isolation: isolate;
  background:
    linear-gradient(
      135deg,
      #101d32,
      #09111f
    );
  color: #ffffff;
  font-family: Inter, system-ui, sans-serif;
  box-shadow:
    0 0 0 1px rgba(139, 92, 246, 0.08),
    0 18px 50px rgba(0, 0, 0, 0.4),
    inset 0 0 24px rgba(103, 232, 249, 0.04);
  transition:
    border-color 0.35s ease,
    box-shadow 0.35s ease,
    transform 0.25s ease;
}

.cr-portal-btn:hover {
  transform: translateY(-2px);
  border-color: rgba(103, 232, 249, 0.72);
  box-shadow:
    0 0 25px rgba(34, 211, 238, 0.12),
    0 20px 55px rgba(0, 0, 0, 0.45);
}

.cr-portal-label {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: center;
  padding-left: 34px;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: 0.2px;
  transition:
    opacity 0.25s ease,
    transform 0.35s ease;
}

.cr-download-icon {
  position: absolute;
  z-index: 6;
  left: 43px;
  top: 50%;
  width: 24px;
  height: 30px;
  transform: translateY(-50%);
}

.cr-arrow-line {
  position: absolute;
  left: 50%;
  top: 1px;
  width: 3px;
  height: 17px;
  border-radius: 10px;
  background: #67e8f9;
  transform: translateX(-50%);
  box-shadow:
    0 0 8px rgba(103, 232, 249, 0.8);
}

.cr-arrow-head {
  position: absolute;
  left: 50%;
  top: 10px;
  width: 11px;
  height: 11px;
  border-right: 3px solid #67e8f9;
  border-bottom: 3px solid #67e8f9;
  transform:
    translateX(-50%)
    rotate(45deg);
  filter:
    drop-shadow(
      0 0 4px rgba(103, 232, 249, 0.7)
    );
}

.cr-energy-core {
  position: absolute;
  z-index: 1;
  left: 50%;
  top: 50%;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #ffffff;
  opacity: 0;
  transform:
    translate(-50%, -50%)
    scale(0);
  box-shadow:
    0 0 12px #ffffff,
    0 0 28px #67e8f9,
    0 0 55px #8b5cf6;
}

.cr-portal-rings {
  position: absolute;
  z-index: 2;
  left: 50%;
  top: 50%;
  width: 64px;
  height: 64px;
  opacity: 0;
  transform:
    translate(-50%, -50%)
    scale(0.4);
  pointer-events: none;
}

.cr-ring {
  position: absolute;
  border-radius: 50%;
}

.cr-ring-one {
  inset: 0;
  border:
    2px solid rgba(103, 232, 249, 0.9);
  border-left-color: transparent;
  border-bottom-color: rgba(139, 92, 246, 0.8);
}

.cr-ring-two {
  inset: 8px;
  border:
    2px solid rgba(139, 92, 246, 0.9);
  border-top-color: transparent;
  border-right-color: #67e8f9;
}

.cr-ring-three {
  inset: 18px;
  border:
    1px solid rgba(255, 255, 255, 0.8);
  box-shadow:
    0 0 12px rgba(103, 232, 249, 0.7);
}

.cr-portal-particles {
  position: absolute;
  z-index: 3;
  left: 50%;
  top: 50%;
  width: 1px;
  height: 1px;
  pointer-events: none;
}

.cr-portal-particles i {
  position: absolute;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: #67e8f9;
  opacity: 0;
  box-shadow:
    0 0 9px #67e8f9;
}

.cr-portal-particles i:nth-child(1) {
  --x: -74px;
  --y: -25px;
}

.cr-portal-particles i:nth-child(2) {
  --x: 72px;
  --y: -29px;
}

.cr-portal-particles i:nth-child(3) {
  --x: -88px;
  --y: 17px;
}

.cr-portal-particles i:nth-child(4) {
  --x: 88px;
  --y: 18px;
}

.cr-portal-particles i:nth-child(5) {
  --x: -48px;
  --y: 37px;
}

.cr-portal-particles i:nth-child(6) {
  --x: 51px;
  --y: 38px;
}

.cr-portal-success {
  position: absolute;
  inset: 0;
  z-index: 8;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  font-size: 16px;
  font-weight: 700;
  opacity: 0;
  transform: scale(0.82);
  pointer-events: none;
  transition:
    opacity 0.35s ease,
    transform 0.45s
      cubic-bezier(.2, .8, .2, 1.2);
}

.cr-success-check {
  width: 27px;
  height: 27px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  border:
    1px solid rgba(103, 232, 249, 0.75);
  color: #67e8f9;
  box-shadow:
    0 0 15px rgba(103, 232, 249, 0.25);
}

.cr-portal-btn.cr-activating {
  border-color: rgba(103, 232, 249, 0.9);
  box-shadow:
    0 0 25px rgba(34, 211, 238, 0.22),
    0 0 55px rgba(139, 92, 246, 0.12),
    inset 0 0 30px rgba(34, 211, 238, 0.08);
}

.cr-portal-btn.cr-activating
.cr-portal-label {
  opacity: 0;
  transform: translateY(-14px);
}

.cr-portal-btn.cr-activating
.cr-download-icon {
  animation:
    cr-arrow-drop 0.7s
    cubic-bezier(.4, 0, .2, 1)
    forwards;
}

@keyframes cr-arrow-drop {
  0% {
    opacity: 1;
    transform:
      translateY(-50%)
      scale(1);
  }

  65% {
    opacity: 1;
    transform:
      translateY(8px)
      scale(0.8);
  }

  100% {
    opacity: 0;
    transform:
      translateY(18px)
      scale(0.3);
  }
}

.cr-portal-btn.cr-activating
.cr-portal-rings {
  animation:
    cr-portal-open 1.5s
    cubic-bezier(.2, .8, .2, 1)
    forwards;
}

@keyframes cr-portal-open {
  0% {
    opacity: 0;
    transform:
      translate(-50%, -50%)
      scale(0.3);
  }

  25% {
    opacity: 1;
  }

  70% {
    opacity: 1;
    transform:
      translate(-50%, -50%)
      scale(1);
  }

  100% {
    opacity: 0;
    transform:
      translate(-50%, -50%)
      scale(1.65);
  }
}

.cr-portal-btn.cr-activating
.cr-ring-one {
  animation:
    cr-spin 0.7s linear infinite;
}

.cr-portal-btn.cr-activating
.cr-ring-two {
  animation:
    cr-spin-reverse 0.55s linear infinite;
}

@keyframes cr-spin {
  to {
    transform: rotate(360deg);
  }
}

@keyframes cr-spin-reverse {
  to {
    transform: rotate(-360deg);
  }
}

.cr-portal-btn.cr-activating
.cr-energy-core {
  animation:
    cr-core-burst 1.45s ease-out forwards;
}

@keyframes cr-core-burst {
  0% {
    opacity: 0;
    transform:
      translate(-50%, -50%)
      scale(0);
  }

  35% {
    opacity: 1;
    transform:
      translate(-50%, -50%)
      scale(1);
  }

  70% {
    opacity: 0.9;
    transform:
      translate(-50%, -50%)
      scale(3);
  }

  100% {
    opacity: 0;
    transform:
      translate(-50%, -50%)
      scale(12);
  }
}

.cr-portal-btn.cr-activating
.cr-portal-particles i {
  animation:
    cr-particle-burst 0.85s
    0.55s ease-out forwards;
}

@keyframes cr-particle-burst {
  0% {
    opacity: 0;
    transform:
      translate(0, 0)
      scale(0);
  }

  25% {
    opacity: 1;
  }

  100% {
    opacity: 0;
    transform:
      translate(
        var(--x),
        var(--y)
      )
      scale(0.2);
  }
}

.cr-portal-btn.cr-complete
.cr-download-icon,
.cr-portal-btn.cr-complete
.cr-portal-label,
.cr-portal-btn.cr-complete
.cr-portal-rings,
.cr-portal-btn.cr-complete
.cr-energy-core {
  opacity: 0;
}

.cr-portal-btn.cr-complete
.cr-portal-success {
  opacity: 1;
  transform: scale(1);
}

.cr-portal-btn.cr-complete {
  border-color: rgba(103, 232, 249, 0.72);
  box-shadow:
    0 0 28px rgba(34, 211, 238, 0.16),
    inset 0 0 25px rgba(103, 232, 249, 0.05);
}
JAVASCRIPT
(function () {

  var button =
    document.getElementById(
      'crPortalBtn'
    );

  if (!button) {
    return;
  }

  var running = false;

  button.addEventListener(
    'click',
    function () {

      if (running) {
        return;
      }

      running = true;

      button.classList.remove(
        'cr-complete'
      );

      button.classList.add(
        'cr-activating'
      );

      setTimeout(
        function () {

          button.classList.remove(
            'cr-activating'
          );

          button.classList.add(
            'cr-complete'
          );

        },
        1550
      );

      setTimeout(
        function () {

          button.classList.remove(
            'cr-complete'
          );

          running = false;

        },
        3600
      );

    }
  );

})();

How This Energy Portal Download Button Works

The Energy Portal Download Button is built around a single button element with several layered spans inside it. These layers handle the download icon, portal rings, glowing energy core, particles, normal “Download File” label, and the final “Download Complete” state. Decorative elements use aria-hidden="true" so they do not add unnecessary information for assistive technologies.

The button itself is 260px wide and 78px high with a 22px border radius. Its dark linear-gradient background is combined with cyan and violet accents. Multiple box-shadow values create the glow around the control, while a 0.25-second transform transition moves the button upward by 2px on hover.

The download arrow is drawn entirely with CSS. A narrow cyan element creates the vertical line, while another element uses right and bottom borders with a 45-degree rotation to form the arrowhead. When activation begins, the cr-arrow-drop animation moves this icon downward, scales it down, and fades it out over 0.7 seconds.

The portal effect is made from three circular rings positioned in the center of the button. During activation, cr-portal-open expands the ring container from a 0.3 scale to 1.65 while controlling its opacity. The outer ring rotates clockwise every 0.7 seconds, while the second ring rotates in reverse every 0.55 seconds. These continuous rotations run only while the cr-activating class is present.

At the same time, the small white energy core uses cr-core-burst to grow from scale 0 to scale 12 over 1.45 seconds. Its white, cyan, and violet shadows create the central burst. Six 5px particles also travel away from the center using individual --x and --y custom properties. For more information about this animation mechanism, see the MDN CSS animations documentation.

JavaScript controls the component by listening for the button’s click event. A running variable prevents repeated clicks from restarting the sequence while it is active. On click, JavaScript adds cr-activating. After 1550 milliseconds, that class is replaced with cr-complete, revealing the checkmark and “Download Complete” message. At 3600 milliseconds, the complete state is removed and the button becomes clickable again.

It is important to note that this code demonstrates the visual download interaction; the JavaScript does not fetch or download a file. An actual download action would need to be connected separately.

Customizing the Energy Portal Download Button

Most of the appearance and animation timing can be adjusted directly through the existing CSS values.

  • Change the button’s 260px width, 78px height, or 22px border radius to alter its overall proportions.
  • Replace #101d32 and #09111f to customize the dark button gradient.
  • Change the cyan #67e8f9 and violet #8b5cf6 values to create a different portal color scheme.
  • Adjust the 17px label and 16px success-message font sizes for different typography.
  • Modify the 0.7-second arrow animation, 1.5-second portal animation, or 1.45-second energy burst to change the pace of the effect.
  • Edit each particle’s --x and --y values to control its burst direction and distance.
  • Change the cr-spin and cr-spin-reverse durations of 0.7 and 0.55 seconds to make the portal rings rotate faster or slower.
  • Update the “Download File” and “Download Complete” text in the HTML to match the action used in your interface.

Where to Use the Energy Portal Download Button

This component fits interfaces where a download action benefits from a clearly visible processing and completion sequence.

  • File download pages for apps, templates, documents, or digital assets.
  • Developer resource pages that provide downloadable project files.
  • Game or sci-fi themed interfaces where the portal animation matches the visual direction.
  • Dashboard actions that need an animated confirmation state.
  • Landing pages offering downloadable software or resources.
  • Demo interfaces where you want to show an activation sequence before displaying completion feedback.

Because the current JavaScript controls animation states rather than file transfer, connect the button to your real download logic when using it in production. For another button with a different animated action sequence, the Animated Delivery Order Button is a related CodeRipple component worth exploring.

Frequently Asked Questions

Does this button actually download a file?

No. The supplied JavaScript manages the cr-activating and cr-complete visual states, but it does not contain file-download logic. You need to connect the interface to the appropriate download behavior separately.

How does the portal animation start?

JavaScript registers a click event listener on crPortalBtn. When clicked, it adds the cr-activating class, which activates the CSS animations for the arrow, rings, energy core, and particles.

Why can’t the animation be triggered repeatedly with fast clicks?

The script uses a running Boolean variable. Once the sequence starts, running becomes true, and additional clicks immediately return until the full 3600-millisecond sequence has finished.

How is the Download Complete message displayed?

The success layer starts with opacity: 0 and scale(0.82). When JavaScript adds cr-complete, CSS changes it to full opacity and scale(1), while hiding the normal label, download icon, rings, and energy core.

The Energy Portal Download Button combines layered HTML, state-based CSS animations, and a small JavaScript controller to turn a standard download control into a timed portal-style interaction while keeping the actual behavior easy to connect to application logic.

Scroll to Top