Files
hyperframes/docs/catalog/components/decline-chart.mdx
T
Miguel Ángel 9734578e60 feat(registry): bring back the video-primitive moves (#3169)
Restores the 208 catalog items reverted after their previews 404'd in
production, this time on the payload mechanism rather than the .html files
that caused the outage.

The generator no longer writes a preview document to docs/public. That writer,
and the machinery under it, existed only to produce files the docs host
discards, so it is gone rather than bypassed. Items now embed the composition
itself via a payload, which is what the previous change already does for the
items that were already in the catalog.

The variables explorer is parked, not restored: it drove its preview through
the same unpublished .html path, so it would have shown an empty frame. Items
that declare variables get the live player plus the static variables table, and
reconnecting the explorer to payloads is a follow-up.
2026-08-10 18:46:03 -04:00

344 lines
12 KiB
Plaintext

---
title: "Decline Chart"
description: "A metric line draws downward as its value counts down and the ambient background darkens in lockstep."
---
import { InstallCommand } from "/snippets/install-command.jsx";
<iframe
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
title="decline-chart 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/decline-chart.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/decline-chart.png");document.body.appendChild(p)});<\/script></body></html>`}
/>
## Install
<InstallCommand command="npx hyperframes add decline-chart" />
That writes one file: `compositions/components/decline-chart.html`.
## Paste it into your composition
Open `compositions/components/decline-chart.html` and copy what is inside into your own composition.
A component has no size or duration of its own. It takes both from the composition
you paste it into.
## 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 |
| --- | --- | --- | --- |
| `label` | `Retention` | string | Label shown above the declining value. |
| `start_value` | `82` | 0 to 100, step 1 | Metric value at the start of the decline. |
| `end_value` | `34` | 0 to 100, step 1 | Metric value at the bottom of the decline. |
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
<div
data-composition-id="decline-chart"
data-composition-src="compositions/components/decline-chart.html"
data-variable-values='{"label":"Retention","start_value":82,"end_value":34}'
></div>
```
## Source
<Accordion title={`decline-chart.html`}>
```html
<!doctype html>
<!--
decline-chart: HyperFrames video primitive
One metric line draws downward while its numeric value counts down and the
ambient background dims and desaturates from the same progress value.
Envelope:
IN_BASE = 0.55s chart frame settles into view
HOLD = elastic decline draw, countdown, and ambient darkening
OUT_BASE = 0.45s bottomed-out endpoint locks in with no recovery
If D is shorter than IN_BASE + OUT_BASE, IN and OUT compress together
and HOLD collapses to zero. Longer mounts stretch HOLD only.
Variables:
label (string, default "Retention")
start_value (number, default 82)
end_value (number, default 34)
-->
<html
lang="en"
data-composition-id="decline-chart"
data-composition-duration="4"
data-composition-variables='[
{ "id": "label", "type": "string", "role": "content", "label": "Metric label", "description": "Label shown above the declining value.", "default": "Retention" },
{ "id": "start_value", "type": "number", "role": "content", "label": "Start value", "description": "Metric value at the start of the decline.", "default": 82, "min": 0, "max": 100, "step": 1 },
{ "id": "end_value", "type": "number", "role": "content", "label": "End value", "description": "Metric value at the bottom of the decline.", "default": 34, "min": 0, "max": 100, "step": 1 }
]'
>
<head>
<meta charset="UTF-8" />
<title>Decline Chart</title>
</head>
<body>
<template>
<div id="root" data-composition-id="decline-chart" data-duration="4" data-fps="30">
<style>
*,
*::before,
*::after {
box-sizing: border-box;
}
#root {
position: absolute;
inset: 0;
overflow: hidden;
container-type: size;
isolation: isolate;
color: var(--fg, #f8fafc);
font-family: var(--font-body, Inter, system-ui, sans-serif);
pointer-events: none;
}
.dc-clip,
.dc-ambient,
.dc-gloom {
position: absolute;
inset: 0;
}
.dc-clip {
overflow: hidden;
}
.dc-ambient {
background:
radial-gradient(circle at 24% 16%, rgba(47, 129, 150, 0.55), transparent 46%),
linear-gradient(145deg, #152f3c 0%, #101a25 48%, #0c1118 100%);
transform: scale(1.02);
transform-origin: center;
will-change: filter;
}
.dc-gloom {
background: #030507;
opacity: 0;
}
.dc-layout {
position: absolute;
inset: 9cqh 9cqw 8cqh;
display: grid;
/* minmax(0, 1fr): a bare 1fr row inflates to the svg's intrinsic
aspect height (min-height auto), overflowing the card. */
grid-template-rows: auto minmax(0, 1fr);
gap: 5cqh;
opacity: 0;
}
.dc-header {
display: flex;
align-items: end;
justify-content: space-between;
gap: 5cqw;
}
.dc-label {
max-width: 58cqw;
overflow: hidden;
color: rgba(226, 232, 240, 0.72);
font-size: clamp(12px, 3.6cqh, 38px);
font-weight: 620;
letter-spacing: 0.05em;
line-height: 1.1;
text-overflow: ellipsis;
text-transform: uppercase;
white-space: nowrap;
}
.dc-value {
flex: 0 0 auto;
color: #f8fafc;
font-family: var(--font-display, Inter, system-ui, sans-serif);
font-size: clamp(30px, 12cqh, 118px);
font-variant-numeric: tabular-nums;
font-weight: 760;
letter-spacing: -0.07em;
line-height: 0.8;
}
.dc-chart {
width: 100%;
height: 100%;
overflow: visible;
}
.dc-grid {
stroke: rgba(148, 163, 184, 0.18);
stroke-width: 1;
vector-effect: non-scaling-stroke;
}
.dc-line {
fill: none;
stroke: #fb7185;
stroke-linecap: round;
stroke-linejoin: round;
/* No non-scaling-stroke here: with preserveAspectRatio=none Chromium
applies the dash pattern in screen space, so the user-space
dasharray from getTotalLength() splits the draw into two visible
segments. Scaled stroke keeps dashes in user space. */
stroke-width: 4;
}
.dc-endpoint {
fill: #fecdd3;
opacity: 0;
transform-box: fill-box;
transform-origin: center;
}
</style>
<div
id="decline-chart-clip"
class="dc-clip clip"
data-start="0"
data-duration="4"
data-track-index="0"
>
<div class="dc-ambient"></div>
<div class="dc-gloom"></div>
<div class="dc-layout">
<div class="dc-header">
<div class="dc-label"></div>
<div class="dc-value"></div>
</div>
<svg
class="dc-chart"
viewBox="0 0 260 240"
preserveAspectRatio="none"
aria-hidden="true"
>
<line class="dc-grid" x1="8" y1="55" x2="252" y2="55"></line>
<line class="dc-grid" x1="8" y1="120" x2="252" y2="120"></line>
<line class="dc-grid" x1="8" y1="185" x2="252" y2="185"></line>
<path
class="dc-line"
d="M 8 24 C 28 28, 44 43, 61 56 S 93 76, 112 92 S 144 111, 163 134 S 193 159, 213 186 S 239 211, 252 222"
></path>
<circle class="dc-endpoint" cx="252" cy="222" r="6"></circle>
</svg>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<script>
(function () {
"use strict";
var root = document.getElementById("root");
var layout = root.querySelector(".dc-layout");
var ambient = root.querySelector(".dc-ambient");
var gloom = root.querySelector(".dc-gloom");
var label = root.querySelector(".dc-label");
var value = root.querySelector(".dc-value");
var line = root.querySelector(".dc-line");
var endpoint = root.querySelector(".dc-endpoint");
var lineLength = line.getTotalLength();
var vars =
window.__hyperframes && window.__hyperframes.getVariables
? window.__hyperframes.getVariables()
: {};
line.style.strokeDasharray = lineLength + " " + lineLength;
var startValue = Number.isFinite(vars.start_value) ? vars.start_value : 82;
var endValue = Number.isFinite(vars.end_value) ? vars.end_value : 34;
var labelText = vars.label == null ? "Retention" : String(vars.label);
label.textContent = labelText;
var IN_BASE = 0.55;
var OUT_BASE = 0.45;
var duration = Math.max(0.001, parseFloat(root.dataset.duration || "4"));
var fixedTotal = IN_BASE + OUT_BASE;
var phaseScale = duration < fixedTotal ? duration / fixedTotal : 1;
var IN = IN_BASE * phaseScale;
var OUT = OUT_BASE * phaseScale;
var HOLD = Math.max(0, duration - IN - OUT);
var OUT_START = IN + HOLD;
var progress = { value: 0 };
function renderProgress() {
var p = progress.value;
line.style.strokeDashoffset = String(lineLength * (1 - p));
ambient.style.filter =
"saturate(" + (1 - p * 0.72) + ") brightness(" + (1 - p * 0.52) + ")";
gloom.style.opacity = String(p * 0.46);
endpoint.style.opacity = String(Math.max(0, (p - 0.94) / 0.06));
value.textContent = String(Math.round(startValue + (endValue - startValue) * p));
}
renderProgress();
var tl = gsap.timeline({ paused: true });
tl.fromTo(
layout,
{ opacity: 0, y: "2.5cqh" },
{ opacity: 1, y: 0, duration: IN, ease: "power2.out" },
0,
);
if (HOLD > 0) {
tl.to(
progress,
{ value: 1, duration: HOLD, ease: "power2.out", onUpdate: renderProgress },
IN,
);
} else {
tl.call(
function () {
progress.value = 1;
renderProgress();
},
[],
IN,
);
}
tl.fromTo(
endpoint,
{ scale: 0.72 },
{ scale: 1, duration: OUT, ease: "power2.out" },
OUT_START,
);
tl.seek(0);
window.__timelines = window.__timelines || {};
window.__timelines["decline-chart"] = tl;
})();
</script>
</div>
</template>
</body>
</html>
```
</Accordion>
{/* hf:generated-footer */}
Tagged `motion-primitive` `data` `chart` `decline` `metric` `problem`.
## Related topics
- [Browse the complete Catalog](/catalog)
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
- [Build a richer composition](/go-further)