> ## 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.

# Card Resize

> A transitions.dev-inspired card resize primitive that expands a compact card into a detail panel

export const InstallCommand = ({command}) => {
  const [copied, setCopied] = React.useState(false);
  const copy = async () => {
    try {
      if (navigator.clipboard && window.isSecureContext) {
        await navigator.clipboard.writeText(command);
      } else {
        const previous = document.activeElement;
        const scratch = document.createElement("textarea");
        scratch.value = command;
        scratch.setAttribute("readonly", "");
        scratch.style.position = "fixed";
        scratch.style.opacity = "0";
        document.body.appendChild(scratch);
        scratch.select();
        document.execCommand("copy");
        document.body.removeChild(scratch);
        previous?.focus?.();
      }
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    } catch {}
  };
  return <div className="hf-install-command not-prose my-4 flex items-stretch overflow-hidden rounded-xl border border-zinc-200 bg-zinc-50 dark:border-zinc-800 dark:bg-zinc-900">
      <code className="flex-1 overflow-x-auto whitespace-nowrap border-r border-zinc-200 px-4 py-3 font-mono text-sm text-zinc-800 dark:border-zinc-800 dark:text-zinc-100">
        {command}
      </code>
      <button type="button" onClick={copy} data-copied={copied ? "true" : "false"} aria-label={`Copy ${command} to the clipboard`} className="hf-install-copy">
        <svg className="hf-install-copy-clipboard" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 18 18" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
          <path d="M14.25 5.25H7.25C6.14543 5.25 5.25 6.14543 5.25 7.25V14.25C5.25 15.3546 6.14543 16.25 7.25 16.25H14.25C15.3546 16.25 16.25 15.3546 16.25 14.25V7.25C16.25 6.14543 15.3546 5.25 14.25 5.25Z" />
          <path d="M2.80103 11.998L1.77203 5.07397C1.61003 3.98097 2.36403 2.96397 3.45603 2.80197L10.38 1.77297C11.313 1.63397 12.19 2.16297 12.528 3.00097" />
        </svg>
        <svg className="hf-install-copy-check" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 18 18" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
          <path d="M2.75 9.5L6.5 13.25L15.25 4.5" />
        </svg>
      </button>
      <span className="hf-install-copy-status" role="status" aria-live="polite">
        {copied ? "Copied" : ""}
      </span>
    </div>;
};

<iframe className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800" title="card-resize preview" loading="lazy" srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@0.7/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/components/card-resize.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/components/card-resize.png");document.body.appendChild(p)});<\/script></body></html>`} />

## Install

<InstallCommand command="npx hyperframes add card-resize" />

That writes one file: `compositions/components/card-resize.html`.

## Variables

Every one of these has a default, so the piece works untouched. Set the ones you
want to change on the element:

| Variable    | Default  | Accepts                      | What it does                                                                                                         |
| ----------- | -------- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `surface`   | `paper`  | `paper`, `ink`               | Card colour scheme: paper is the original white card, ink is a dark card for dark frames.                            |
| `accent`    | `ocean`  | `ocean`, `forest`, `ember`   | Treatment of the media thumbnail: a plain plate, a hatched surface, or an image plate with a horizon-and-disc glyph. |
| `elevation` | `raised` | `flat`, `raised`, `floating` | How far the card lifts off the frame, from a contact shadow to a deep drop.                                          |

Set them with `data-variable-values` on the element that mounts it. These are the
defaults, so this behaves exactly like the preview above until you change one:

```html wrap theme={null}
<div
  data-composition-id="card-resize"
  data-composition-src="compositions/components/card-resize.html"
  data-variable-values='{"surface":"paper","accent":"ocean","elevation":"raised"}'
></div>
```

## Source

<Accordion title={`card-resize.html`}>
  ```html theme={null}
  <!--
    Card Resize - transitions.dev-inspired primitive for HyperFrames.

    Paste the markup, CSS and script into a composition. Use the timeline
    integration note at the bottom as the starting point for deterministic GSAP
    animation.

    Variables. The script reads each one, falls back to the declared default on
    anything missing or unrecognised, and writes the result as a custom property
    the CSS above consumes. The timeline recipe is untouched by them: it keeps
    driving the same width, height and fade, and these decide how the card that
    expands is dressed.

      - surface (paper | ink, default paper): the card colour scheme. paper is
        the original white card with near-black type, ink is a dark card with
        light type for dark frames.
      - accent (ocean | forest | ember, default ocean): the treatment of the media
        thumbnail. ocean is a plain plate, forest adds a fine achromatic hatch,
        ember adds a horizon-and-disc glyph so the plate reads as a photo. All
        three are built from the surface's own ink at fixed alphas, so the
        thumbnail carries no hue on either surface. The three value names are
        inherited from the hue families they used to paint and no longer describe
        what they render; renaming them is a separate, breaking change.
      - elevation (flat | raised | floating, default raised): how far the card
        lifts off the frame, from a tight contact shadow to a deep drop.

    On every default the result is a white card with a quiet ink-tinted thumbnail
    plate under a soft wide drop shadow. Layout, sizing and motion are unchanged.
  -->

  <div
    class="hf-transition-card-resize"
    data-composition-variables='[
      { "id": "surface", "type": "enum", "role": "style", "label": "Surface", "description": "Card colour scheme: paper is the original white card, ink is a dark card for dark frames.", "default": "paper", "options": [{ "value": "paper", "label": "Paper" }, { "value": "ink", "label": "Ink" }] },
      { "id": "accent", "type": "enum", "role": "style", "label": "Accent", "description": "Treatment of the media thumbnail: a plain plate, a hatched surface, or an image plate with a horizon-and-disc glyph.", "default": "ocean", "options": [{ "value": "ocean", "label": "Ocean" }, { "value": "forest", "label": "Forest" }, { "value": "ember", "label": "Ember" }] },
      { "id": "elevation", "type": "enum", "role": "style", "label": "Elevation", "description": "How far the card lifts off the frame, from a contact shadow to a deep drop.", "default": "raised", "options": [{ "value": "flat", "label": "Flat" }, { "value": "raised", "label": "Raised" }, { "value": "floating", "label": "Floating" }] }
    ]'
  >
    <div class="hf-transition-card-media"></div>
    <div class="hf-transition-card-copy">
      <span>Project status</span>
      <strong>Ready to render</strong>
      <p>All scenes validated and queued for export.</p>
    </div>
  </div>

  <style>
    .hf-transition-card-resize {
      --hf-card-w: 330px;
      --hf-card-h: 132px;
      /* Placeholder ramp: the surface's own ink at fixed alphas, so the media
         plate inverts with the surface instead of needing a second palette. */
      --hf-card-l2: rgba(24, 24, 27, 0.45);
      --hf-card-l3: rgba(24, 24, 27, 0.18);
      --hf-card-hair: rgba(24, 24, 27, 0.14);
      display: grid;
      grid-template-columns: 92px 1fr;
      gap: 18px;
      width: var(--hf-card-w);
      height: var(--hf-card-h);
      padding: 18px;
      overflow: hidden;
      border: 1px solid var(--hf-card-border, #e4e4e7);
      border-radius: 26px;
      background: var(--hf-card-bg, #ffffff);
      color: var(--hf-card-ink, #18181b);
      font-family: Inter, system-ui, sans-serif;
      box-shadow: var(--hf-card-shadow, 0 26px 80px rgba(0, 0, 0, 0.12));
      will-change: width, height, transform;
    }
    .hf-transition-card-media {
      width: 92px;
      height: 92px;
      border-radius: 20px;
      background: var(--hf-card-media, var(--hf-card-l3));
      box-shadow: inset 0 0 0 1px var(--hf-card-hair);
    }
    .hf-transition-card-copy {
      display: grid;
      align-content: center;
      gap: 7px;
      min-width: 0;
    }
    .hf-transition-card-copy span {
      color: var(--hf-card-muted, #71717a);
      font-size: 13px;
      font-weight: 850;
      text-transform: uppercase;
    }
    .hf-transition-card-copy strong {
      white-space: nowrap;
      font-size: 22px;
      letter-spacing: -0.01em;
    }
    .hf-transition-card-copy p {
      margin: 0;
      color: var(--hf-card-muted, #71717a);
      font-size: 15px;
      line-height: 1.45;
    }
  </style>

  <script>
    (function () {
      "use strict";

      var vars =
        window.__hyperframes && window.__hyperframes.getVariables
          ? window.__hyperframes.getVariables()
          : {};

      // Unrecognised overrides return to their declared defaults.
      function pick(table, value, fallback) {
        return Object.prototype.hasOwnProperty.call(table, value) ? value : fallback;
      }

      var surfaces = {
        paper: {
          bg: "#ffffff",
          ink: "#18181b",
          muted: "#71717a",
          border: "#e4e4e7",
          l2: "rgba(24, 24, 27, 0.45)",
          l3: "rgba(24, 24, 27, 0.18)",
          hair: "rgba(24, 24, 27, 0.14)",
        },
        ink: {
          bg: "#16161a",
          ink: "#fafafa",
          muted: "#a1a1aa",
          border: "#2f2f37",
          l2: "rgba(250, 250, 250, 0.45)",
          l3: "rgba(250, 250, 250, 0.18)",
          hair: "rgba(250, 250, 250, 0.14)",
        },
      };
      // Media treatments, not hue families: the plate itself, the plate hatched,
      // and the plate with a horizon-and-disc glyph that reads as a photo. Each
      // one is written in the surface's own ink ramp, so both surfaces work.
      var medias = {
        ocean: "var(--hf-card-l3)",
        forest:
          "repeating-linear-gradient(135deg, var(--hf-card-hair) 0 2px, transparent 2px 4px), var(--hf-card-l3)",
        ember:
          "radial-gradient(circle 11px at 64px 28px, var(--hf-card-l2) 0 100%, transparent 100%), linear-gradient(to top, var(--hf-card-l2) 0 24px, transparent 24px), var(--hf-card-l3)",
      };
      var shadows = {
        flat: "0 2px 10px rgba(0, 0, 0, 0.1)",
        raised: "0 26px 80px rgba(0, 0, 0, 0.12)",
        floating: "0 46px 120px rgba(0, 0, 0, 0.3)",
      };

      var surface = surfaces[pick(surfaces, vars.surface, "paper")];
      var media = medias[pick(medias, vars.accent, "ocean")];
      var shadow = shadows[pick(shadows, vars.elevation, "raised")];

      var roots = document.querySelectorAll(".hf-transition-card-resize");
      for (var i = 0; i < roots.length; i += 1) {
        roots[i].style.setProperty("--hf-card-bg", surface.bg);
        roots[i].style.setProperty("--hf-card-ink", surface.ink);
        roots[i].style.setProperty("--hf-card-muted", surface.muted);
        roots[i].style.setProperty("--hf-card-border", surface.border);
        roots[i].style.setProperty("--hf-card-l2", surface.l2);
        roots[i].style.setProperty("--hf-card-l3", surface.l3);
        roots[i].style.setProperty("--hf-card-hair", surface.hair);
        roots[i].style.setProperty("--hf-card-media", media);
        roots[i].style.setProperty("--hf-card-shadow", shadow);
      }
    })();
  </script>

  <!--
    Timeline integration:
    tl.fromTo('.hf-transition-card-resize', { '--hf-card-w': '330px', '--hf-card-h': '132px', opacity: 0, y: 24 }, { '--hf-card-w': '560px', '--hf-card-h': '210px', opacity: 1, y: 0, duration: 0.68, ease: 'power3.inOut' }, 0.35);
    tl.fromTo('.hf-transition-card-copy p', { opacity: 0, y: 12 }, { opacity: 1, y: 0, duration: 0.28, ease: 'power2.out' }, 0.82);
  -->
  ```
</Accordion>

## Usage

Open `compositions/components/card-resize.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.

Tagged `video-primitive` `transition-primitive` `transitions-dev-port` `card` `layout`.

## Related topics

* [Browse the complete Catalog](/catalog)
* [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
* [Build a richer composition](/go-further)


## Related topics

- [Keyboard shortcuts](/studio/shortcuts.md)
- [Animate with GSAP](/guides/gsap-animation.md)
- [Troubleshooting](/guides/troubleshooting.md)
