> ## Documentation Index
> Fetch the complete documentation index at: https://hyperframes.heygen.com/llms.txt
> Use this file to discover all available pages before exploring further.

# HTML Schema Reference

> Complete reference for authoring Hyperframes HTML compositions.

This is the full schema reference for Hyperframes compositions. For a gentler introduction, see [Compositions](/concepts/compositions) and [Data Attributes](/concepts/data-attributes).

## Overview

Hyperframes uses HTML as the source of truth for describing a video:

* **HTML clips** = video, image, audio, composition
* **[Data attributes](/concepts/data-attributes)** = timing, metadata, styling
* **CSS** = positioning and appearance
* **GSAP timeline** = animations and playback sync (see [GSAP Animation](/guides/gsap-animation))

## Framework-Managed Behavior

The framework reads data attributes and automatically manages:

* **Primitive clip timeline entries** — reads `data-start`, `data-duration`, and `data-track-index` from clips and adds them to the GSAP timeline
* **Media playback** (play, pause, seek) for `<video>` and `<audio>`
* **Clip lifecycle** — clips are mounted/unmounted based on `data-start` and `data-duration`
* **Timeline synchronization** — keeps media in sync with the GSAP master timeline
* **Media loading** — waits for all media to load before resolving timing

Mounting/unmounting controls **presence**, not appearance. Transitions (fade in, slide in) are animated in scripts.

<Warning>
  Do not manually call `video.play()`, `video.pause()`, set `audio.currentTime`, or mount/unmount clips in scripts. The framework owns media playback and clip lifecycle. See [Common Mistakes](/guides/common-mistakes) for more details.
</Warning>

## Viewport

Every composition must include `data-width` and `data-height` on the root element:

```html theme={null}
<div id="main" data-composition-id="my-video"
     data-start="0" data-width="1920" data-height="1080">
  <!-- clips -->
</div>
```

Common sizes:

* **Landscape**: `data-width="1920" data-height="1080"`
* **Portrait**: `data-width="1080" data-height="1920"`

## All Clip Attributes

| Attribute              | Applies To        | Required        | Description                                                                                                                                                    |
| ---------------------- | ----------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                   | All               | Yes             | Unique identifier (e.g., `"el-1"`). Used for relative timing references and CSS targeting.                                                                     |
| `class="clip"`         | Visible elements  | Yes             | Enables runtime visibility management. Omit for audio-only clips.                                                                                              |
| `data-start`           | All               | Yes             | Start time in seconds (e.g., `"0"`, `"5.5"`), or a clip ID reference for [relative timing](#relative-timing) (e.g., `"intro"`).                                |
| `data-duration`        | video, img, audio | See below       | Duration in seconds. **Required** for images. Optional for video/audio (defaults to source duration). Not used on compositions.                                |
| `data-track-index`     | All               | Yes             | Timeline track number. Controls z-ordering (higher = in front). Clips on the same track cannot overlap.                                                        |
| `data-media-start`     | video, audio      | No              | Playback offset / trim point in source file (seconds). Default: `0`. See [Data Attributes](/concepts/data-attributes).                                         |
| `data-volume`          | audio, video      | No              | Volume level from `0` to `1`. Default: `1`.                                                                                                                    |
| `data-composition-id`  | div               | On compositions | Unique composition ID. Must match the key used in `window.__timelines`.                                                                                        |
| `data-composition-src` | div               | No              | Path to external composition HTML file (for [nested compositions](#composition-clips)).                                                                        |
| `data-variable-values` | div               | No              | JSON object of values passed to a nested composition. The framework carries the values through, but your composition script must read and apply them manually. |
| `data-width`           | div               | On compositions | Composition width in pixels.                                                                                                                                   |
| `data-height`          | div               | On compositions | Composition height in pixels.                                                                                                                                  |

## Clip Types

<AccordionGroup>
  <Accordion title="Video Clips">
    Video clips embed `<video>` elements with timing and playback attributes.

    ```html theme={null}
    <video
      id="el-1"
      data-start="0"
      data-duration="15"
      data-track-index="0"
      data-media-start="0"
      src="./assets/video.mp4"
    ></video>
    ```

    **Key behavior:**

    * `data-duration` is **optional** — defaults to the remaining duration of the source file from `data-media-start`
    * If source media runs out before `data-duration`, the clip shows the last frame (freeze frame)
    * `data-media-start` trims the beginning of the source video — `data-media-start="5"` starts playback 5 seconds into the source file
    * `data-volume` controls the audio volume of the video — set to `"0"` for silent video
    * Do **not** add `class="clip"` to video elements — the framework manages their visibility directly

    <Warning>
      Do not animate `width`, `height`, `top`, or `left` directly on `<video>` elements with GSAP. This can cause Chrome to stop rendering video frames. Wrap the video in a `<div>` and animate the wrapper instead. See [Common Mistakes](/guides/common-mistakes).
    </Warning>
  </Accordion>

  <Accordion title="Image Clips">
    Image clips display static images with controlled timing.

    ```html theme={null}
    <img
      id="el-2"
      class="clip"
      data-start="5"
      data-duration="4"
      data-track-index="1"
      src="./assets/overlay.png"
    />
    ```

    **Key behavior:**

    * `data-duration` is **required** for images (unlike video/audio, there is no source duration to default to)
    * `class="clip"` is **required** — this enables the runtime to show/hide the image based on timing
    * Supported formats: PNG, JPG, WebP, SVG, GIF. Animated GIFs are prepared as timeline-synced video for preview and render; `data-loop` can override GIF loop metadata.
    * Position and size with CSS — the image renders at its natural size unless styled otherwise
  </Accordion>

  <Accordion title="Audio Clips">
    Audio clips add sound to the composition without any visual element.

    ```html theme={null}
    <audio
      id="el-4"
      data-start="0"
      data-duration="30"
      data-track-index="2"
      src="./assets/music.mp3"
    ></audio>
    ```

    **Key behavior:**

    * `data-duration` is **optional** — defaults to the remaining duration of the source file from `data-media-start`
    * Audio clips are invisible — do not add `class="clip"` (there is nothing to show/hide)
    * `data-volume` controls volume — use `"0.5"` for background music at 50% volume
    * `data-media-start` trims the beginning of the audio source, just like video
    * Multiple audio clips can overlap on different tracks for layered sound design
  </Accordion>

  <Accordion title="Composition Clips (Nested)">
    Composition clips embed one composition inside another, enabling modular, reusable video building blocks.

    ```html theme={null}
    <div
      id="el-5"
      data-composition-id="intro-anim"
      data-composition-src="compositions/intro-anim.html"
      data-start="0"
      data-track-index="3"
    ></div>
    ```

    **Key behavior:**

    * Compositions do **not** use `data-duration` — duration is determined by the composition's GSAP timeline (`tl.duration()`)
    * External compositions are loaded from `data-composition-src` and wrapped in `<template>` tags
    * Each nested composition has its own `window.__timelines` entry, registered by its own `<script>` block
    * The framework automatically nests sub-timelines — do not manually add them to the parent timeline
    * Any composition can be nested inside any other — there is no special "root" type
    * Per-instance values can be passed with `data-variable-values`, but the nested composition must read and apply those values itself

    For more on how compositions work, see [Compositions](/concepts/compositions).
  </Accordion>
</AccordionGroup>

## Relative Timing

Reference another clip's ID in `data-start` to mean "start when that clip ends":

```html theme={null}
<video id="intro" data-start="0" data-duration="10" data-track-index="0" src="..."></video>
<video id="main" data-start="intro" data-duration="20" data-track-index="0" src="..."></video>
```

`main` starts at second 10 (when `intro` ends).

**Offsets** let you add gaps or overlaps:

```html theme={null}
<!-- 2-second gap after intro -->
<video id="main" data-start="intro + 2" data-duration="20" data-track-index="0" src="..."></video>

<!-- 0.5-second overlap with intro -->
<video id="main" data-start="intro - 0.5" data-duration="20" data-track-index="0" src="..."></video>
```

For a deeper explanation, see the [relative timing section](/concepts/data-attributes#relative-timing) in the Data Attributes concept page.

## Timeline Contract

The framework initializes `window.__timelines = {}` before any scripts run. Every composition must register a GSAP timeline at the key matching its `data-composition-id`:

```javascript theme={null}
const tl = gsap.timeline({ paused: true });

// Add animations
tl.to("#title", { opacity: 1, duration: 0.5 }, 0);
tl.to("#title", { opacity: 0, duration: 0.5 }, 4.5);

// Register the timeline
window.__timelines["<data-composition-id>"] = tl;
```

### Rules

* Every composition needs a `<script>` block that creates and registers its timeline
* All timelines must start paused (`{ paused: true }`)
* The framework auto-nests sub-timelines into the parent — do **not** manually add them
* Duration comes from `tl.duration()` — do **not** add `data-duration` on composition elements
* Timelines must be finite (no infinite loops or repeats)
* The timeline ID must exactly match the `data-composition-id` attribute on the root element

For a complete guide to working with GSAP timelines, see [GSAP Animation](/guides/gsap-animation).

## Caption Discoverability

For caption compositions, add these attributes to the root node so the framework can identify and special-case caption rendering:

```html theme={null}
<div
  data-composition-id="captions"
  data-timeline-role="captions"
  data-caption-root="true"
  ...
>
```

## Output Checklist

<Check>
  Before rendering, verify your composition meets these requirements:

  * Every composition has `data-width` and `data-height` on the root element
  * Each reusable composition is in its own HTML file
  * External compositions are loaded via `data-composition-src`
  * Each external composition file uses a `<template>` wrapper
  * All GSAP timelines are registered in `window.__timelines` with the correct ID
  * Timed visible elements (images, divs) have `class="clip"`
  * Video elements do **not** have `class="clip"` (framework manages them directly)
  * All `data-start` references point to existing clip IDs
  * Run `npx hyperframes lint` to catch structural issues automatically
</Check>
