featureVideo.html
<div class="featureVideo accent-primary">
	<div class="featureVideo__media">
		<div class="media">
			<p-lazy>
				<video class="media__content" inert controls autoplay playsinline>
					<source
						data-src="https://www.w3schools.com/html/mov_bbb.mp4"
						type="video/mp4"
					/>
					Your browser does not support the video tag.
				</video>
				<template #placeholder="{ load, modifier }">
					<picture class="media__mask" :class="modifier">
						<img
							srcset="https://unsplash.it/960/540?random&gravity=center"
							alt=""
						/>
					</picture>
					<button class="media__trigger" @click="load" :class="modifier">
						<span class="srOnly">play</span>
						<svg class="media__icon icon -circle" viewBox="0 0 96 96">
							<use href="/main-icons-sprite.svg#play" />
						</svg>
					</button>
				</template>
			</p-lazy>
		</div>
	</div>
	<div class="featureVideo__background"></div>
	<div class="featureVideo__content">
		<h3 class="featureVideo__title h3">Heading Placeholder</h3>
		<p class="featureVideo__text">
			Signissim lacus in eros convallis porta. Pellentesque hendrerit neque a
			cursus tristique. Phasellus eleifend consectetur felis, in eleifend
			nulla vulputate eget.
		</p>
	</div>
</div>
index.scss
@use "@imarc/pronto/resources/styles/imported" as *;

.featureVideo {

  color: var(--accent-color-text);
  display: grid;
  gap: #{fluid-rems(1.5, 2)};
  padding: 0 #{fluid-rems(1.5, 2, 2.5)} #{fluid-rems(2.5, 3, 4.5)};
  position: relative;
  width: 100%;
  min-width: 0;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr 1fr auto;

  > * {
    grid-column: 1;
  }

  &__media {
    display: grid;
    justify-items: center;
    width: 100%;
    min-width: 0;
    grid-row: 1 / 3;
    z-index: 2;

    .media {
      margin: 0;
      width: 100%;
      min-width: 200px;
      overflow: hidden;

      @include at(md) {
        width: min(100%, 80%);
      }
    }

    .media__mask img,
    .media__content,
    iframe {
      width: 100%;
      aspect-ratio: 16 / 9;
      object-fit: cover;
      display: block;
      box-shadow: 0 16px 64px 0 rgba(0, 0, 0, 0.05);
    }
  }

  &__background {
    background: var(--accent-color);
    grid-row: 2 / -1;
    z-index: 0;
  }

  &__content {
    display: grid;
    gap: .75rem;
    text-align: center;
    justify-items: center;
    width: 100%;
    max-width: 42rem;
    margin: 0 auto;
    padding: 0 1rem #{fluid-rems(1.5, 3, 4)};
    align-self: start;
    grid-row: 3 / -1;
    z-index: 1;
  }

  &__title,
  &__text {
    margin: 0;
  }

}
PLazy.vue
<script setup>
  import { computed, ref, nextTick, useTemplateRef } from 'vue'

  /**
   * PLazy is a vue component that lazy loads its content after it's been clicked.
   *
   * @slot default - the content to lazyload. It will remove inert attributes, change data-src
   *                 attributes to src attributes, and call .load() on videos.
   * @slot placeholder - the content to display before it's lazyloaded. The following properties
   *                     are exposed to this slot:
   *       load {function} - function to call to load the content.
   *       loaded {boolean} - whether the content is loaded or not.
   *       modifier {string} - either '-loaded' or '' based on loaded.
   */

  const loaded = ref(false)
  const content = useTemplateRef('content')
  const modifier = computed(() => loaded.value ? '-loaded' : '')

  const load = async () => {
    if (loaded.value) return

    await nextTick()

    const promises = []

    /* Change data-src attributes to src attributes */
    content.value.querySelectorAll('[data-src]')
      .forEach(element => element.setAttribute('src', element.dataset.src))

    /* remove inert attribute */
    content.value.querySelectorAll('[inert]')
      .forEach(element => element.inert = false)

    /* load videos */
    content.value.querySelectorAll('video')
      .forEach(element => {
        element.load()
        promises.push(new Promise(resolve => {
          element.addEventListener('loadeddata', resolve, { once: true })
        }))
      })

    /* load iframes */
    content.value.querySelectorAll('iframe')
      .forEach(element => {
        promises.push(new Promise(resolve => {
          element.addEventListener('load', resolve, { once: true })
        }))
      })

    /* wait for all videos and iframes to load, then update loaded.value */
    Promise.all(promises).then(() => loaded.value = true)
  }
</script>

<template>
  <div ref="content">
    <slot name="default"></slot>
  </div>
  <slot name="placeholder" v-bind="{ load, loaded, modifier }"></slot>
</template>

<style scoped>
div {
  container-type: size;
}
</style>