Files
hyperframes/registry/components/drift-hold/drift-hold.html
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

193 lines
6.9 KiB
HTML
Vendored

<!doctype html>
<!--
drift-hold: HyperFrames video primitive (camera / ambient / hold)
Concept: a centered content card never becomes fully static. Sub-degree
rotation, restrained scale breathing, and a soft light sweep each follow
one complete sine cycle across the mounted duration.
Variables:
- text (string, default "Always alive"): the card label.
- intensity (whisper | standard, default "standard"): selects the
amplitudes for rotation, breathing, and the light sweep.
Envelope:
IN = 0s
HOLD = elastic = the complete mounted duration
OUT = 0s
HOLD owns exactly one whole cycle at every duration. The endpoint phase
is explicitly wrapped to zero so the pose at t=0 exactly equals the pose
at t=duration.
Mount contract: the runtime clones only this template. #root fills the
host box, establishes the container query basis, has no data-width or
data-height, and registers one paused timeline under the hardcoded
drift-hold id.
-->
<html
lang="en"
data-composition-id="drift-hold"
data-composition-duration="4"
data-composition-variables='[
{ "id": "text", "type": "string", "role": "content", "label": "Text", "description": "Text displayed in the default centered card.", "default": "Always alive" },
{ "id": "intensity", "type": "enum", "role": "motion", "label": "Intensity", "description": "Amplitude of the ambient rotation, breathing, and light sweep.", "default": "standard", "options": [{ "value": "whisper", "label": "Whisper" }, { "value": "standard", "label": "Standard" }] }
]'
>
<head>
<meta charset="UTF-8" />
<title>Drift Hold</title>
</head>
<body>
<template>
<div id="root" data-composition-id="drift-hold" 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;
}
.dh-clip {
position: absolute;
inset: 0;
display: grid;
place-items: center;
overflow: hidden;
}
.dh-card {
position: relative;
display: grid;
width: 68cqw;
min-height: 44cqh;
place-items: center;
overflow: hidden;
padding: var(--space-3, 5cqh) var(--space-3, 6cqw);
border: 0.16cqmin solid color-mix(in srgb, var(--border, #475569) 82%, transparent);
border-radius: var(--radius, 3.2cqmin);
background: color-mix(in srgb, var(--surface, #172033) 94%, var(--accent, #38bdf8));
box-shadow: 0 3.5cqh 10cqh color-mix(in srgb, var(--bg, #05070d) 58%, transparent);
transform-origin: 50% 50%;
will-change: transform;
}
.dh-sweep {
position: absolute;
top: -45%;
left: 42%;
width: 20cqw;
height: 190%;
background: linear-gradient(
90deg,
transparent 0%,
color-mix(in srgb, var(--accent, #38bdf8) 10%, transparent) 25%,
color-mix(in srgb, var(--fg, #f8fafc) 28%, transparent) 50%,
color-mix(in srgb, var(--accent, #38bdf8) 10%, transparent) 75%,
transparent 100%
);
filter: blur(1.8cqmin);
will-change: transform, opacity;
}
.dh-label {
position: relative;
z-index: 1;
max-width: 56cqw;
overflow: hidden;
color: var(--fg, #f8fafc);
font-family: var(--font-display, Inter, system-ui, sans-serif);
font-size: clamp(20px, 9cqmin, 76px);
font-weight: 720;
line-height: 1.05;
letter-spacing: -0.035em;
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>
<div
id="drift-hold-clip"
class="dh-clip clip"
data-start="0"
data-duration="4"
data-track-index="0"
>
<div class="dh-card">
<div class="dh-sweep" aria-hidden="true"></div>
<div class="dh-label"></div>
</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 card = root.querySelector(".dh-card");
var sweep = root.querySelector(".dh-sweep");
var label = root.querySelector(".dh-label");
var vars =
window.__hyperframes && window.__hyperframes.getVariables
? window.__hyperframes.getVariables()
: {};
var text = vars.text == null ? "Always alive" : String(vars.text);
var intensity = vars.intensity === "whisper" ? "whisper" : "standard";
var amplitudes = {
whisper: { rotation: 0.24, scale: 0.006, sweep: 30, light: 0.04 },
standard: { rotation: 0.6, scale: 0.015, sweep: 42, light: 0.08 },
};
var motion = amplitudes[intensity];
label.textContent = text;
var TAU = Math.PI * 2;
var duration = Math.max(0.001, parseFloat(root.dataset.duration || "4"));
var state = { phase: 0 };
function renderPose() {
// Literal phase zero at the endpoint removes floating point
// residue from Math.sin(2 * PI) and makes the seam exact.
var phase = state.phase === TAU ? 0 : state.phase;
var rotation = Math.sin(phase + Math.PI / 6) * motion.rotation;
var scale = 1 + Math.sin(phase + Math.PI / 2) * motion.scale;
var sweepX = Math.sin(phase - Math.PI / 2) * motion.sweep;
var sweepOpacity = 0.12 + Math.sin(phase + Math.PI / 3) * motion.light;
gsap.set(card, { rotation: rotation, scale: scale });
gsap.set(sweep, {
x: sweepX + "cqw",
rotation: 14,
opacity: sweepOpacity,
});
}
var tl = gsap.timeline({ paused: true });
// HOLD: uniform phase travel drives only whole-cycle sine motion.
tl.to(state, { phase: TAU, duration: duration, ease: "none", onUpdate: renderPose }, 0);
renderPose();
tl.seek(0);
window.__timelines = window.__timelines || {};
window.__timelines["drift-hold"] = tl;
})();
</script>
</div>
</template>
</body>
</html>