All components
57

Handle credit

Crediting whoever built the demo on screen: an X handle, a GitHub repo, a YouTube channel

Prompt for your agentpaste into Claude Code or Codex
Add the "Handle credit" video component (X, light) from everestchris.com to this project.

1. Download https://ai-component-library-dun.vercel.app/projects/handle-credit.zip?v=976775bc794b and unzip it into components/handle-credit/. Read the README.md and CLAUDE.md in it.
2. Render it: cd components/handle-credit && npx hyperframes@0.8.162 render . --variables '{"variant":"0","theme":"light"}' -o handle-credit.mp4
3. Give me the path of the MP4. The text, names and timings live in the CONFIG block at the top of the script in index.html; change nothing there unless I ask.

Code

One file. The project download adds the fonts, skins and motion kit it runs on.

index.htmlhtmlDownload
1<!doctype html>
2<html lang="en" data-composition-variables='[{"id":"variant","label":"Variant","type":"enum","default":"0","options":[{"value":"0","label":"X handle"},{"value":"1","label":"GitHub repo"},{"value":"2","label":"YouTube channel"}]},{"id":"theme","label":"Theme","type":"enum","default":"light","options":[{"value":"light","label":"Light"},{"value":"dark","label":"Dark"}]}]'>
3<head><meta charset="UTF-8"><meta name="viewport" content="width=1920, height=1080"><link rel="stylesheet" href="assets/fonts/fonts.css">
4<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script><script src="assets/kit/motion/motion.js"></script>
5<style>
6*{box-sizing:border-box;margin:0;padding:0}html,body{width:1920px;height:1080px;overflow:hidden;background:transparent}
7#root{position:relative;width:1920px;height:1080px;font-family:Inter,sans-serif;-webkit-font-smoothing:antialiased}
8#aroll{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
9#overlay{position:absolute;inset:0;--surface:#fff;--ink:#0a0a0a;--muted:#606060;--line:rgba(0,0,0,.08);--shadow:0 1px 2px rgba(0,0,0,.08),0 10px 24px -12px rgba(0,0,0,.3)}
10#overlay[data-theme=dark]{--surface:#18181a;--ink:#fafafa;--muted:#bababa;--line:rgba(255,255,255,.1);--shadow:0 1px 2px rgba(0,0,0,.25),0 10px 24px -12px rgba(0,0,0,.5)}
11#credit{position:absolute;left:80px;bottom:80px;width:max-content;max-width:1760px;opacity:0;color:var(--ink)}
12#plate{position:absolute;inset:0;border:1px solid var(--line);border-radius:16px;background:var(--surface);box-shadow:var(--shadow)}
13#row{position:relative;display:flex;align-items:center;gap:16px;padding:20px 24px;white-space:nowrap}
14#mark{width:32px;height:32px;flex:none;background:currentColor;mask:var(--mark) center/contain no-repeat;-webkit-mask:var(--mark) center/contain no-repeat}
15#youtube{display:none;width:40px;height:28px;flex:none}
16#identity{font-size:34px;font-weight:500;line-height:40px;letter-spacing:-.02em}
17#credit.repo #identity{font-family:'JetBrains Mono',monospace;font-size:32px;letter-spacing:-.03em;font-weight:400}
18#credit.channel #mark{display:none}#credit.channel #youtube{display:block}
19</style></head>
20<body><div id="root" data-composition-id="main" data-start="0" data-duration="8" data-width="1920" data-height="1080">
21<video id="aroll" class="clip" src="assets/still/aroll-right.mp4" muted playsinline data-start="0" data-duration="8" data-track-index="0"></video>
22<div id="overlay" class="clip" data-start="0" data-duration="8" data-track-index="1"><div id="credit"><div id="plate"></div><div id="row"><span id="mark"></span><img id="youtube" src="assets/marks/youtube.svg" alt=""><span id="identity"></span></div></div></div></div>
23<script>
24const CONFIG={duration:8,aroll:'assets/still/aroll-right.mp4',timing:{enter:.4,reveal:.3,out:7.3,exit:.3},variants:{
25'0':{kind:'handle',identity:'@OpenAI',mark:'assets/logos/x-si.svg',url:'https://x.com/OpenAI'},
26'1':{kind:'repo',identity:'openai/codex',mark:'assets/logos/github.svg',url:'https://github.com/openai/codex'},
27'2':{kind:'channel',identity:'OpenAI',mark:'assets/marks/youtube.svg',url:'https://www.youtube.com/OpenAI'}
28}};
29const vars=window.__hyperframes?.getVariables?.()||{};const vi=['0','1','2'].includes(String(vars.variant))?String(vars.variant):'0';const theme=vars.theme==='dark'?'dark':'light';const V=CONFIG.variants[vi],$=s=>document.querySelector(s);
30$('#overlay').dataset.theme=theme;$('#credit').className=V.kind;$('#identity').textContent=V.identity;$('#mark').style.setProperty('--mark',`url("${V.mark}")`);if(V.kind==='channel')$('#youtube').src=V.mark;
31if(CONFIG.aroll)$('#aroll').src=CONFIG.aroll;else $('#aroll').remove();
32document.fonts.ready.then(()=>{const K=KitMotion,T=CONFIG.timing,tl=gsap.timeline({paused:true});
33K.fadeUp(tl,'#credit',T.enter,{distance:6,blur:0,duration:T.reveal});K.fadeOut(tl,'#credit',T.out,{duration:T.exit});
34// Fade samples have intentionally reduced composited contrast; audit the fully held identity.
35K.drive(tl,0,CONFIG.duration,p=>{const t=p*CONFIG.duration,el=$('#credit');if(t<T.enter+T.reveal||t>T.out)el.setAttribute('data-layout-ignore','');else el.removeAttribute('data-layout-ignore');});
36// fadeOut primes opacity 1 at build time. Restore the empty initial state after registering it.
37gsap.set('#credit',{opacity:0,y:6,filter:'blur(0px)'});tl.set({}, {},CONFIG.duration);window.__timelines=window.__timelines||{};window.__timelines.main=tl;tl.seek(0);
38});
39</script></body></html>
Download project13 MB