Files
hyperframes/registry/components/locked-nucleus-orbit/locked-nucleus-orbit.html
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

354 lines
12 KiB
HTML
Vendored

<!doctype html>
<!--
locked-nucleus-orbit: HyperFrames video primitive (intros / reveals / orbit)
One nucleus stays dead center for the full composition. Satellite rings and
copy chips fade in while orbiting on deterministic index-based phases, then
decelerate into a composed lockup. The nucleus is never a GSAP target.
Variables:
- label (string, default "core"): text inside the fixed nucleus.
- accent (green | blue | violet, default green): active color.
- satellites (number, 3 to 6, default 4): visible satellite count.
Envelope (fixed IN and ORBIT, elastic HOLD, no OUT):
IN_BASE = 0.45s, rings and chips fade in along their orbital arcs
ORBIT_BASE = 1.75s, satellites decelerate into their final phases
HOLD = max(0, D - (IN + ORBIT)), only ring opacity breathes
OUT = 0s, the composed lockup is the final state
If D is shorter than IN_BASE + ORBIT_BASE, IN and ORBIT compress together.
Mount contract: the runtime clones only this template. #root fills the host
box, has no data-width or data-height, and registers one paused timeline
under the hardcoded locked-nucleus-orbit id.
-->
<html
lang="en"
data-composition-id="locked-nucleus-orbit"
data-composition-duration="4"
data-composition-variables='[
{ "id": "label", "type": "string", "role": "content", "label": "Nucleus label", "description": "Text displayed inside the fixed center nucleus.", "default": "core" },
{ "id": "accent", "type": "enum", "role": "style", "label": "Accent", "description": "Color used by the nucleus, rings, and satellite markers.", "default": "green", "options": [{ "value": "green", "label": "Green" }, { "value": "blue", "label": "Blue" }, { "value": "violet", "label": "Violet" }] },
{ "id": "satellites", "type": "number", "role": "layout", "label": "Satellites", "description": "Number of orbiting copy chips.", "default": 4, "min": 3, "max": 6, "step": 1 }
]'
>
<head>
<meta charset="UTF-8" />
<title>Locked Nucleus Orbit</title>
</head>
<body>
<template>
<div id="root" data-composition-id="locked-nucleus-orbit" 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;
background: var(--bg, #0b0c0e);
color: var(--fg, #f8fafc);
font-family: var(--font-body, Inter, system-ui, sans-serif);
pointer-events: none;
}
.lno-clip {
position: absolute;
inset: 0;
overflow: hidden;
}
.lno-field {
position: absolute;
inset: 0;
}
.lno-ring {
position: absolute;
left: 50%;
top: 50%;
width: var(--lno-size);
aspect-ratio: 1;
border: 0.12cqmin solid color-mix(in srgb, var(--lno-accent) 34%, transparent);
border-radius: 50%;
transform: translate(-50%, -50%);
transform-origin: 50% 50%;
box-shadow: inset 0 0 3cqmin color-mix(in srgb, var(--lno-accent) 5%, transparent);
opacity: 0;
}
.lno-ring:nth-child(1) {
--lno-size: 46cqmin;
}
.lno-ring:nth-child(2) {
--lno-size: 58cqmin;
}
.lno-ring:nth-child(3) {
--lno-size: 70cqmin;
}
.lno-nucleus {
position: absolute;
left: 50%;
top: 50%;
width: 15cqmin;
aspect-ratio: 1;
display: grid;
place-items: center;
transform: translate(-50%, -50%);
border: 0.2cqmin solid color-mix(in srgb, var(--lno-accent) 68%, white 16%);
border-radius: 50%;
background: color-mix(in srgb, var(--surface, #172033) 86%, var(--lno-accent) 14%);
box-shadow:
0 0 2.8cqmin color-mix(in srgb, var(--lno-accent) 38%, transparent),
inset 0 0 2.2cqmin color-mix(in srgb, var(--lno-accent) 18%, transparent);
z-index: 3;
}
.lno-nucleus::before {
content: "";
position: absolute;
width: 2.4cqmin;
aspect-ratio: 1;
top: 2.5cqmin;
border-radius: 50%;
background: var(--lno-accent);
box-shadow: 0 0 1.8cqmin color-mix(in srgb, var(--lno-accent) 72%, transparent);
}
.lno-nucleus-label {
max-width: 11cqmin;
margin-top: 3.1cqmin;
overflow: hidden;
color: var(--fg, #f8fafc);
font-family: var(--font-mono, ui-monospace, "SF Mono", Menlo, monospace);
font-size: clamp(9px, 2cqmin, 20px);
font-weight: 700;
letter-spacing: 0.08em;
line-height: 1;
text-overflow: ellipsis;
text-transform: uppercase;
white-space: nowrap;
}
.lno-arm {
position: absolute;
left: 50%;
top: 50%;
width: 0;
height: 0;
transform-origin: 0 0;
}
.lno-anchor {
position: absolute;
left: var(--lno-radius);
top: 0;
transform: translate(-50%, -50%);
}
.lno-chip {
min-width: 12cqmin;
max-width: 22cqmin;
min-height: 5.2cqmin;
display: flex;
align-items: center;
gap: 1.25cqmin;
padding: 1.1cqmin 1.8cqmin;
border: 0.12cqmin solid
color-mix(in srgb, var(--lno-accent) 48%, var(--border, #334155));
border-radius: 999px;
background: color-mix(in srgb, var(--surface, #172033) 90%, transparent);
box-shadow: 0 1.2cqmin 3.6cqmin color-mix(in srgb, var(--bg, #0b0c0e) 60%, transparent);
opacity: 0;
will-change: transform, opacity;
}
.lno-marker {
flex: 0 0 auto;
width: 2.4cqmin;
aspect-ratio: 1;
border: 0.32cqmin solid var(--lno-accent);
border-radius: 50%;
box-shadow: 0 0 1.4cqmin color-mix(in srgb, var(--lno-accent) 56%, transparent);
}
.lno-copy {
overflow: hidden;
color: var(--fg, #f8fafc);
font-family: var(--font-mono, ui-monospace, "SF Mono", Menlo, monospace);
font-size: clamp(8px, 1.8cqmin, 18px);
font-weight: 650;
letter-spacing: 0.08em;
line-height: 1;
text-overflow: ellipsis;
text-transform: uppercase;
white-space: nowrap;
}
</style>
<div
id="locked-nucleus-orbit-clip"
class="lno-clip clip"
data-start="0"
data-duration="4"
data-track-index="0"
>
<div class="lno-field">
<div class="lno-ring" aria-hidden="true"></div>
<div class="lno-ring" aria-hidden="true"></div>
<div class="lno-ring" aria-hidden="true"></div>
<div class="lno-satellites"></div>
<div class="lno-nucleus">
<span class="lno-nucleus-label"></span>
</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 label = root.querySelector(".lno-nucleus-label");
var satelliteLayer = root.querySelector(".lno-satellites");
var rings = root.querySelectorAll(".lno-ring");
var vars =
window.__hyperframes && window.__hyperframes.getVariables
? window.__hyperframes.getVariables()
: {};
var accentColors = {
green: "var(--brand, #34d399)",
blue: "var(--accent, #38bdf8)",
violet: "var(--accent-2, #a78bfa)",
};
var accent = Object.prototype.hasOwnProperty.call(accentColors, vars.accent)
? vars.accent
: "green";
var countValue = Number(vars.satellites);
var satelliteCount =
vars.satellites != null && Number.isFinite(countValue)
? Math.max(3, Math.min(6, Math.round(countValue)))
: 4;
var labelText = vars.label == null ? "core" : String(vars.label);
var copy = ["signal", "motion", "frames", "output", "sync", "lock"];
var radii = [23, 29, 35];
root.style.setProperty("--lno-accent", accentColors[accent]);
label.textContent = labelText;
var satellites = [];
for (var index = 0; index < satelliteCount; index += 1) {
var arm = document.createElement("div");
var anchor = document.createElement("div");
var chip = document.createElement("div");
var marker = document.createElement("span");
var chipCopy = document.createElement("span");
arm.className = "lno-arm";
anchor.className = "lno-anchor";
chip.className = "lno-chip";
marker.className = "lno-marker";
chipCopy.className = "lno-copy";
arm.style.setProperty("--lno-radius", radii[index % radii.length] + "cqmin");
chipCopy.textContent = copy[index];
chip.appendChild(marker);
chip.appendChild(chipCopy);
anchor.appendChild(chip);
arm.appendChild(anchor);
satelliteLayer.appendChild(arm);
var finalPhase = -90 + (360 * index) / satelliteCount;
var travel = 210 + (index % 3) * 70;
satellites.push({
arm: arm,
chip: chip,
startPhase: finalPhase - travel,
finalPhase: finalPhase,
});
}
var IN_BASE = 0.45;
var ORBIT_BASE = 1.75;
var duration = Math.max(0.001, parseFloat(root.dataset.duration || "4"));
var motionBase = IN_BASE + ORBIT_BASE;
var motionScale = duration < motionBase ? duration / motionBase : 1;
var IN = IN_BASE * motionScale;
var ORBIT = ORBIT_BASE * motionScale;
var MOTION = IN + ORBIT;
var HOLD = Math.max(0, duration - MOTION);
var fadeStagger = Math.min(0.07, IN / (satelliteCount * 2));
var fadeDuration = Math.max(0.001, IN - fadeStagger * (satelliteCount - 1));
gsap.set(rings, { opacity: 0, scale: 0.86 });
satellites.forEach(function (satellite) {
gsap.set(satellite.arm, { rotation: satellite.startPhase });
gsap.set(satellite.chip, {
opacity: 0,
rotation: -satellite.startPhase,
scale: 0.82,
});
});
var tl = gsap.timeline({ paused: true });
tl.to(rings, { opacity: 0.38, scale: 1, duration: IN, ease: "power2.out" }, 0);
satellites.forEach(function (satellite, index) {
tl.to(
satellite.arm,
{ rotation: satellite.finalPhase, duration: MOTION, ease: "power2.out" },
0,
);
tl.to(
satellite.chip,
{ rotation: -satellite.finalPhase, duration: MOTION, ease: "power2.out" },
0,
);
tl.to(
satellite.chip,
{
opacity: 1,
scale: 1,
duration: fadeDuration,
ease: "back.out(1.7)",
},
index * fadeStagger,
);
});
if (HOLD > 0.2) {
tl.to(
rings,
{
opacity: 0.5,
duration: HOLD / 2,
ease: "sine.inOut",
yoyo: true,
repeat: 1,
},
MOTION,
);
}
tl.seek(0);
window.__timelines = window.__timelines || {};
window.__timelines["locked-nucleus-orbit"] = tl;
})();
</script>
</div>
</template>
</body>
</html>