<div class="wrapper">
  <div class="intro">
    <h1 class="heading">Iris Wipe</h1>
    <p class="nudge">hover/tap to see transition</p>
  </div>

  <div class="scenes" tabindex="0">
    <div class="scene-1">
      <h2 class="scene-title">Captain Phasma</h2>
    </div>
    <div class="scene-2">
      <h2 class="scene-title">Porg</h2>
    </div>
  </div>

  <div class="visualizer">
    <div class="visualizer-frame">
      <div class="visualizer-mask"></div>
    </div>
  </div>
</div>
/* scenes (the good stuff) */

@property --radius {
  syntax: '<percentage>';
  inherits: true;
  initial-value: -5%;
}

@keyframes scene-transition {
  to { --radius: 105%; }
}

.scenes {
  position: relative;
  aspect-ratio: 2.4 / 1;
  outline: 2px solid #ccc;
}

.scene-1, .scene-2 {
  position: absolute;
  inset: 0;
  background-size: cover;
}

.scene-1 {
  background-image: url(https://assets.codepen.io/77020/sw-iris-wipe-scene-1.jpg);
}

.scene-2 {
  background-image: url(https://assets.codepen.io/77020/sw-iris-wipe-scene-2.jpg);
  z-index: -1;
  -webkit-mask-image: radial-gradient(
    circle,
    #fff calc(var(--radius) - 5%),
    transparent calc(var(--radius) + 5%)
  );
}

.scenes:is(:hover, :focus) .scene-2 {
  z-index: 1;
  animation: scene-transition 2s linear forwards;
}

.scene-title {
  position: absolute;
  max-width: 45%;
  margin: min(40px, 4vw);
  font-size: 1.25em;
}

.scene-1 .scene-title {
  top: 0;
  right: 0;
  color: #fff;
  text-align: right;
}

.scene-2 .scene-title {
  bottom: 0;
  left: 0;
  color: #fff;
  text-align: left;
}

/* everything else */

*, *::before, *::after {
  box-sizing: border-box;
}

body {
  display: grid;
  place-items: center;
  min-height: 100vh;
  margin: 0;
  padding: 20px;
  color: #fff;
  background-color: #111;
  font: clamp(0.75rem, 0.3rem + 1.9vw, 1.5rem)/1.1 'Koulen', sans-serif;
  letter-spacing: 0.1em;
}

.wrapper {
  display: grid;
  gap: 30px;
  width: min(1000px, 100%);
}

.intro {
  text-align: center;
}

.heading {
  margin: 0;
  font-size: 2.5em;
}

.heading::before, .heading::after {
  content: ' — ';
}

.nudge {
  margin: 0;
  color: #888;
  font-size: 1.25em;
}

.visualizer {
  display: flex;
  justify-content: center;
}

.visualizer-frame {
  position: relative;
  width: min(150px, 25vw);
  aspect-ratio: 2.4 / 1;
  outline: 2px solid #ccc;
  background-color: #333;
  background-image:
    linear-gradient(45deg, #444 25%, transparent 25%, transparent 75%, #444 75%),
    linear-gradient(45deg, #444 25%, transparent 25%, transparent 75%, #444 75%);
  background-size:
    16px 16px,
    16px 16px;
  background-position:
    0 0,
    8px 8px;
}

.visualizer-mask {
  position: absolute;
  inset: 0;
  box-shadow: inset 0 0 0 2px #111;
}

.scenes:is(:hover, :focus) + .visualizer .visualizer-mask {
  background-image: radial-gradient(circle, #fff calc(var(--radius) - 5%), transparent calc(var(--radius) + 5%));
  animation: scene-transition 2s linear forwards;
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.