Motion · Light & emphasis
Dashboard Glow Highlight Pill
A gold wordmark hangs in a black void as a data dashboard rises up in perspective and keeps drifting in 3D; a gold light patch cruises from the right to the bottom, stretching into a pill, then traces the glowing outline of a popup from that pill.
Source: video-shotcraft (Vincent Wei) ↗ · Apache-2.0 · Remotion composition (React/TSX) — preview video, no in-browser player
When to use
A key-feature reveal for finance/data products; a premium "pay attention here" cue; the core beat of a black-and-gold brand film.
Intention
Use a beam of light to draw the viewer's attention from "the whole dashboard" down to "this one popup." The light patch first cruises between panels (announcing something is coming), stretches into a pill (winding up), then traces the popup's outline starting from the pill's landing point (the handoff) — three beats are one continuous deformation of the same beam of light; breaking any one of them turns it into three unrelated animations. The whole shot is only 2s, and every beat starts from where the last one landed.
Duration & energy
- Duration: About 2.0s (60f@30fps).
- Energy: High (four beats — rise, cruise, trace, popup — packed into 2s, handoffs must be seamless).
Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)
意图
用一束光把观众的注意力从"整个仪表盘"收到"这一个弹窗"上。光斑先在 面板间巡游(宣布有东西要来),拉成胶囊(蓄力),再从胶囊落点起笔 描出弹窗轮廓(交棒)——三段是同一束光的连续变形,任何一处断开就 变成三个不相干的动画。全片仅 2s,每段都靠上一段的落点做起点。
动效核心
- 金字 "Ready." 用
background-clip:text+drop-shadow三层辉光,
满亮保持到 t≈0.30 再 inQuad 淡出;辉光走 0.85±0.15·sin(t·π·9) 呼吸
- 仪表盘走 10 段 POSE 关键帧(
[t, rotateX, rotateY, tx%, ty%, scale],
段间 inOutQuad):0.300 时 rotateX 34°/scale 1.62 只露顶栏切片, 0.365 就基本摆正满幅,之后 yaw 由 −13° 缓扫到 +2.6°, 0.62→0.72 有一次 scale 0.943→0.707 的后拉让位给弹窗
- 背景虚化"先起后退":
blur 0→4.5px(0.58→0.66 inOutQuad)后在
0.80→0.93 退掉 52%,压暗层 dim 峰值 0.22 保留
- 光斑 6 段关键帧从
(80.5%,42%) 22×22单调走到(44.1%,67.4%) 96×16
——向左下 + 持续横向拉长,中途不回摆;0.655→0.678 淡出交棒
- 辉光描边
stroke-dashoffsetdraw-on(0.655→0.775 outQuad),起笔点
SX 由光斑末帧 x=44.1% 反算;0.79→0.93 收敛:笔宽 2.9→1.0px、 #fff0c4→#e6c887、辉光几乎撤掉,变成常驻金色细框
- 弹窗底板 0.665→0.75 先到(opacity×0.72),文字 0.715→0.84 才清晰;
弹窗与描边共用完全相同的 transform 链,并按相机姿态的 0.45/0.5 倍 做半幅漂移
参数表
| 参数 | 典型值 | 调节手感 | |------|--------|----------| | 金字淡出 | 满亮到 t≈0.30,0.30→0.355 inQuad | 要和仪表盘升入叠着走;先清场再升入就断了 | | 仪表盘升入 | rotateX 34°→5.5°、scale 1.62→1.055,t 0.300→0.365 | 0.065(≈4f)内摆正是故意的:慢了读作"面板飘上来"而非"镜头压下去" | | 常驻 3D 漂移 | yaw −13°→+2.6°,scale 0.943→0.707 | 漂移不能停:一静止就露出这是张贴图 | | 光斑巡游 | 6 帧关键帧,22×22 → 96×16 | 单调向左下 + 只拉长不回摆;回摆一次就读作"光在找东西" | | 背景虚化 | blur 峰值 4.5px,0.80→0.93 退掉 52%,dim 0.22 | 只起不退 = 结尾整幅糊;退到 0 又失去弹窗的层次 | | 描边 draw-on | dashoffset 0.655→0.775 outQuad,从底边起笔逆时针 | outQuad 快起步才接得住胶囊的"甩出去"的势 | | 描边收敛 | 0.79→0.93:2.9→1.0px、#fff0c4→#e6c887 | 收敛是"光变成 UI"的语义;不收敛就一直是特效 | | 弹窗淡入 | 底板 0.665→0.75(上限 0.72 透明度)、文字 0.715→0.84 | 底板先于文字,才有"框先在、内容后填"的顺序 |
已知坑
- 描边 SVG 与弹窗必须共用同一个盒子和 1:1 px 的 viewBox。曾用
300×220 + preserveAspectRatio="none",被非等比压成 x0.39/y0.49: 笔宽横竖不一、圆角拉成椭圆、整条描边浮在轮廓外——这是"画光位置和 弹窗不一致"的根因
- 光斑不能用
mix-blend-mode:screen:root 上有perspective会把混合
隔离掉导致光斑发灰;改用实心亮核 + box-shadow 外扩辉光
- 描边起笔点
SX必须由光斑末帧 x 反算。写死坐标后一改巡游路径,
"光拉成胶囊再描边"的交棒就断了
- 金字用
background-clip:text,辉光只能走filter: drop-shadow——
text-shadow 对透明字无效
- 仪表盘的订单簿/K 线/买卖面板与弹窗文案全是占位数据(确定性随机
游走生成),落地要换成项目真实界面
参考实现
demos/effects/dashboard-glow-highlight-pill/ (DashboardGlowHighlightPill.tsx)
Reference implementation (Remotion)
- demos/effects/dashboard-glow-highlight-pill/DashboardGlowHighlightPill.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/dashboard-glow-highlight-pill/DashboardGlowHighlightPill.tsx
The component imports shared fixtures from demos/_fixtures and helpers from assets/lib in the repo; SKILL.md there documents the render workflow.
Everything on this page is also machine-readable. Point your agent at the JSON, or add the gimgs MCP server and ask it to search motion templates.
- Fetch the JSON — it holds the shot recipe (timing, easing, parameter table, pitfalls) and the full TSX source.
- Clone the video-shotcraft repo; the component imports shared fixtures from demos/_fixtures and helpers from assets/lib.
- Register the component as a <Composition> in a Remotion project and render with `npx remotion render`, or re-implement the recipe in your own engine.
curl -s https://gimgs.net/motion/dashboard-glow-highlight-pill.jsongit clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/effects/dashboard-glow-highlight-pill/DashboardGlowHighlightPill.tsx
npx remotion render <CompositionId> out.mp4claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("dashboard-glow-highlight-pill")Prompt
Use the video-shotcraft shot recipe "Dashboard Glow Highlight Pill" (Remotion composition, Apache-2.0) published on gimgs.net. Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/dashboard-glow-highlight-pill.json Raw TSX: https://gimgs.net/motion/dashboard-glow-highlight-pill/template.html Repository: https://github.com/Vincentwei1021/video-shotcraft (component: demos/effects/dashboard-glow-highlight-pill/DashboardGlowHighlightPill.tsx; imports demos/_fixtures and assets/lib) Spec: 1920x1080, 30 fps, 2.5s 1. Read the JSON and follow its "prompt" section (when to use, intention, parameter table, known pitfalls). 2. Adapt it for: <describe what you want>. 3. Either clone the repo, register the component as a <Composition> in a Remotion project and run npx remotion render <CompositionId> out.mp4 — or re-implement the recipe in your own engine (HTML/WAAPI works: keep the same timing and easing).
// ===== demos/effects/dashboard-glow-highlight-pill/DashboardGlowHighlightPill.tsx =====
// dashboard-glow-highlight-pill — Glow Highlight Pill 金色胶囊指引(motion-lab 定稿转原生 Remotion)
// 金字 "Ready." 悬于黑场,数据仪表盘(订单簿+K线+买卖面板,内容为占位数据)自底带透视升入后
// 保持缓慢 rotateX/rotateY 3D 漂移;金色光斑从右侧面板巡游至底部中央拉成胶囊,背景虚化压暗
// (仪表盘继续漂),辉光描边从底边逆时针描出 Focus Mode 弹窗轮廓,内容淡入后描边收敛为金色细框。
// 设计坐标 480×270(DesignStage raster="zoom" 等比放大),参数表数值以此坐标系标定。
// 验收:SSIM mean=0.9583/min=0.9510(BORDERLINE,对照条逐帧肉眼一致)。余差为近黑底
// 的 x264 暗场量化:把首帧自渲 PNG 过同参数 x264 后与原样片比对得 0.9977——内容已
// 像素级一致,直接比对的 -0.04 全部来自样片解码端的暗部压缩(2-30 电平带被压到 ~0.8x)。
import React from 'react';
import { DesignStage, E, lerp, rand, seg, useT } from '../../_fixtures/Motion';
export const DASHBOARD_GLOW_HIGHLIGHT_PILL_DURATION = 60; // 2000ms @30fps
// ---- 订单簿行(上红下绿,渐变量条;种子与 effect.js 逐一对应) ----
const OB_ROWS = Array.from({ length: 7 }, (_, i) => ({
wRed: (30 + rand(i * 3 + 1) * 65).toFixed(0),
wGreen: (30 + rand(i * 7 + 4) * 65).toFixed(0),
priceRed: `1${(155.4 - i * 0.12).toFixed(2)}`,
priceGreen: `1${(155.0 - i * 0.12).toFixed(2)}`,
amtRed: (rand(i * 11) * 9 + 0.4).toFixed(3),
amtGreen: (rand(i * 13 + 6) * 9 + 0.4).toFixed(3),
}));
// ---- K 线(确定性随机游走,dv 负偏置 → 整体上行;SVG y 轴向下) ----
const CANDLES: Array<{ x: number; hi: string; lo: string; yTop: string; h: string; col: string; volY: string }> = (() => {
const out = [];
let px = 104;
for (let i = 0; i < 36; i++) {
const dv = (rand(i * 2.7 + 9) - 0.6) * 13;
const o = px;
const c = px + dv;
px = c;
const hi = Math.min(o, c) - rand(i * 5.1) * 5;
const lo = Math.max(o, c) + rand(i * 3.3) * 5;
const up = c < o;
out.push({
x: 6 + i * 7.6,
hi: hi.toFixed(1),
lo: lo.toFixed(1),
yTop: Math.min(o, c).toFixed(1),
h: Math.max(1.5, Math.abs(dv)).toFixed(1),
col: up ? '#2bbf8a' : '#d6455a',
volY: (128 - rand(i * 1.9 + 3) * 16).toFixed(1),
});
}
return out;
})();
// ---- Focus Mode 弹窗几何(原片逐帧实测,注释见 effect.js) ----
const MW = 24.5, MH = 40, MCX = 48.4, MCY = 47; // % of root
const MRAD = 5; // 弹窗圆角(px),描边共用
const RW = 480, RH = 270;
const BW = (RW * MW) / 100, BH = (RH * MH) / 100; // 弹窗盒子像素尺寸
const BO = 0.5; // 描边压在 1px 边框中线上
// 起笔点 = 胶囊停住的位置(BLOB 末帧 x 44.1%),换算成盒内坐标
const SX = ((44.1 - (MCX - MW / 2)) / MW) * BW;
// 描边路径:底边中点起、逆时针一整圈(与 effect.js 的 D 字符串逐字节一致)
const TRACE_D =
`M${SX.toFixed(1)} ${(BH - BO).toFixed(1)}` +
` L${(MRAD + BO).toFixed(1)} ${(BH - BO).toFixed(1)}` +
` A${MRAD} ${MRAD} 0 0 1 ${BO} ${(BH - MRAD - BO).toFixed(1)}` +
` L${BO} ${(MRAD + BO).toFixed(1)}` +
` A${MRAD} ${MRAD} 0 0 1 ${(MRAD + BO).toFixed(1)} ${BO}` +
` L${(BW - MRAD - BO).toFixed(1)} ${BO}` +
` A${MRAD} ${MRAD} 0 0 1 ${(BW - BO).toFixed(1)} ${(MRAD + BO).toFixed(1)}` +
` L${(BW - BO).toFixed(1)} ${(BH - MRAD - BO).toFixed(1)}` +
` A${MRAD} ${MRAD} 0 0 1 ${(BW - MRAD - BO).toFixed(1)} ${(BH - BO).toFixed(1)}` +
` L${SX.toFixed(1)} ${(BH - BO).toFixed(1)}`;
// pathLength 归一化:dashoffset 直接按百分比走,免去 getTotalLength 运行时测量
const P_L = 100;
// 关键帧插值(segments 间 inOutQuad;lerp 签名是 lerp(t, a, b))
const KF = (rows: number[][], t: number): number[] => {
if (t <= rows[0][0]) return rows[0].slice(1);
for (let i = 1; i < rows.length; i++) {
if (t <= rows[i][0]) {
const a = rows[i - 1], b = rows[i];
const p = E.inOutQuad((t - a[0]) / (b[0] - a[0]));
return a.slice(1).map((_, k) => lerp(p, a[k + 1], b[k + 1]));
}
}
return rows[rows.length - 1].slice(1);
};
// ---- 逐帧校准的姿态关键帧:[t, rotateX, rotateY, translateX%, translateY%, scale]
// 0.30 只有顶栏切片露在画面底部 → 0.365 基本满幅摆正 → 之后缓慢 3D 漂移;
// 0.62-0.72 有一次明显镜头后拉(87% → 61.5% 宽),把画面让给弹窗。
const POSE = [
[0.3, 34.0, -13.0, -7.0, 47.0, 1.62],
[0.322, 24.0, -11.0, -5.5, 30.0, 1.44],
[0.345, 13.0, -8.0, -3.5, 14.0, 1.22],
[0.365, 5.5, -5.0, -1.6, 4.0, 1.055],
[0.42, 2.6, -3.0, -0.6, 0.4, 1.005],
[0.5, 2.2, -2.0, -0.3, 0.0, 0.966],
[0.62, 1.8, -0.6, 0.0, -0.5, 0.943],
[0.67, 1.6, 0.6, -0.4, -0.8, 0.852],
[0.72, 1.4, 1.4, -0.8, -1.1, 0.707],
[1.0, 1.2, 2.6, -0.9, -1.3, 0.7],
];
// 光斑巡游关键帧 [t, x%, y%, w, h]:右侧面板圆斑 → 左下移动拉长 → 停在弹窗底边偏左(=描边起笔点)
const BLOB = [
[0.4, 80.5, 42.0, 22, 22],
[0.44, 79.7, 46.5, 27, 30],
[0.5, 77.7, 56.3, 29, 36],
[0.56, 71.1, 63.9, 56, 25],
[0.61, 57.6, 65.2, 82, 21],
[0.65, 44.1, 67.4, 96, 16],
];
const FS = (n: number): React.CSSProperties => ({ fontSize: n });
const rowBetween: React.CSSProperties = { display: 'flex', justifyContent: 'space-between' };
// 订单簿一行(红/绿共用结构,量条右对齐渐变)
const ObRow: React.FC<{ w: string; price: string; amt: string; red?: boolean }> = ({ w, price, amt, red }) => (
<div style={{ position: 'relative', height: 8.5, margin: '1px 0' }}>
<div
style={{
position: 'absolute', right: 0, top: 0, bottom: 0, width: `${w}%`,
background: red
? 'linear-gradient(90deg,rgba(214,69,90,.08),rgba(214,69,90,.4))'
: 'linear-gradient(90deg,rgba(43,191,138,.08),rgba(43,191,138,.4))',
}}
/>
<span style={{ position: 'relative', fontSize: 5, color: red ? '#e05a70' : '#3ecf96', paddingLeft: 2 }}>{price}</span>
<span style={{ position: 'relative', float: 'right', fontSize: 5, color: '#7c828c', paddingRight: 2 }}>{amt}</span>
</div>
);
export const DashboardGlowHighlightPill: React.FC = () => {
const t = useT();
// 金字:满亮保持到 t≈0.30,0.30-0.355 淡出;辉光轻微呼吸
const yOut = seg(t, 0.3, 0.355, E.inQuad);
const br = 0.85 + 0.15 * Math.sin(t * Math.PI * 9);
// 仪表盘姿态(全部走 KF,不叠加二次 ease)
const [prx, pry, ptx, pty, ps] = KF(POSE, t);
const dashOp = t >= 0.298 ? seg(t, 0.298, 0.315) : 0;
// 背景虚化"先起后退":0.58-0.66 起,0.80-0.93 退掉约一半(原片终帧清晰度仍低于满清晰)
const blUp = seg(t, 0.58, 0.66, E.inOutQuad);
const blDown = seg(t, 0.8, 0.93, E.inOutQuad);
const bl = blUp * (1 - blDown * 0.52);
// 光斑 0.385-0.425 浮现 → 巡游拉长 → 0.655-0.678 交棒给描边
const gOn = seg(t, 0.385, 0.425, E.outCubic);
const gOff = seg(t, 0.655, 0.678, E.inQuad);
const [bx, by, bw, bh] = KF(BLOB, t);
// 描边 draw-on 0.655-0.775 走完一圈(outQuad 快起步),0.79-0.93 收敛成金色细框
const dr = seg(t, 0.655, 0.775, E.outQuad);
const settle = seg(t, 0.79, 0.93, E.inOutQuad);
// 弹窗内容淡入:底板比文字早一点到位
const mBase = seg(t, 0.665, 0.75, E.outCubic);
const mc = seg(t, 0.715, 0.84, E.outCubic);
// 弹窗/描边跟随半幅相机漂移,与背景仪表盘同向浮动
const mDrift =
` rotateX(${(prx * 0.45).toFixed(2)}deg) rotateY(${(pry * 0.45).toFixed(2)}deg)` +
` translate(${(ptx * 0.5).toFixed(2)}%,${(pty * 0.5).toFixed(2)}%)`;
const modalTransform = `translate(-50%,-50%) scale(${(0.985 + mc * 0.015).toFixed(4)})` + mDrift;
return (
<DesignStage bg="#050403" raster="zoom">
<div
style={{
position: 'absolute', inset: 0, background: '#050403', overflow: 'hidden',
perspective: 800, fontFamily: '-apple-system,system-ui,sans-serif',
}}
>
{/* 暖色底光(原片黑场并非纯黑,顶部偏暖褐) */}
<div
style={{
position: 'absolute', inset: 0, pointerEvents: 'none',
background: 'radial-gradient(130% 95% at 42% -8%,rgba(104,74,30,.32),rgba(44,32,14,.14) 42%,rgba(0,0,0,0) 74%)',
}}
/>
{/* 开场发光金字(上端浅暖白、下端沉到金褐;background-clip:text 辉光只能走 filter) */}
<div
style={{
position: 'absolute', left: '50%', top: '49%', transform: 'translate(-50%,-50%)',
fontSize: 27, fontWeight: 400, letterSpacing: 0.2, whiteSpace: 'nowrap',
background: 'linear-gradient(178deg,#fff8e2 6%,#f6dfa4 44%,#e0bd72 70%,#c99a45 100%)',
WebkitBackgroundClip: 'text', backgroundClip: 'text', color: 'transparent',
opacity: 1 - yOut,
filter:
`drop-shadow(0 0 7px rgba(255,232,168,${(0.8 * br).toFixed(2)}))` +
` drop-shadow(0 0 20px rgba(233,190,105,${(0.5 * br).toFixed(2)})) drop-shadow(0 0 44px rgba(200,158,78,.3))`,
}}
>
Ready.
</div>
{/* 交易所仪表盘(自底带透视升入;基准尺寸=落位后满幅,靠 scale 推拉) */}
<div
style={{
position: 'absolute', left: '50%', top: '50%', width: '87%', height: '91%',
opacity: dashOp,
transform:
`translate(-50%,-50%) translate(${ptx.toFixed(2)}%,${pty.toFixed(2)}%)` +
` rotateX(${prx.toFixed(2)}deg) rotateY(${pry.toFixed(2)}deg) scale(${ps.toFixed(4)})`,
}}
>
<div
style={{
position: 'absolute', inset: 0, borderRadius: 6, background: '#101114',
border: '1px solid #24272d', overflow: 'hidden', boxShadow: '0 20px 50px rgba(0,0,0,.6)',
filter: `blur(${(bl * 4.5).toFixed(2)}px) brightness(${(1.5 + bl * 0.05).toFixed(3)}) saturate(1.06)`,
}}
>
{/* 顶栏 */}
<div style={{ height: '9%', borderBottom: '1px solid #1a1c20', display: 'flex', alignItems: 'center', padding: '0 8px', gap: 10 }}>
<span style={{ fontSize: 8, fontWeight: 800, color: '#e8e6df', letterSpacing: 1 }}>◆ ACME</span>
<span style={{ ...FS(6), color: '#9aa0aa' }}>Trade</span>
<span style={{ ...FS(6), color: '#565c66' }}>Earn</span>
<span style={{ ...FS(6), color: '#565c66' }}>Vault</span>
<span style={{ marginLeft: 'auto', ...FS(6), color: '#565c66' }}>Support {' '} 0x8f...c2 {' '}</span>
<span style={{ ...FS(6), color: '#0b0c0e', background: '#e6c476', borderRadius: 3, padding: '1px 5px', fontWeight: 700 }}>Connect</span>
</div>
{/* 左:订单簿 */}
<div style={{ position: 'absolute', left: 0, top: '9%', bottom: '16%', width: '24%', borderRight: '1px solid #1a1c20', padding: '4px 5px', boxSizing: 'border-box' }}>
<div style={{ display: 'flex', gap: 8, marginBottom: 3 }}>
<span style={{ ...FS(6), color: '#d8dbe0', borderBottom: '1px solid #e6c476', paddingBottom: 1 }}>Orderbook</span>
<span style={{ ...FS(6), color: '#565c66' }}>Trades</span>
</div>
{OB_ROWS.map((r, i) => (
<ObRow key={`r${i}`} w={r.wRed} price={r.priceRed} amt={r.amtRed} red />
))}
<div style={{ fontSize: 7, fontWeight: 800, color: '#e05a70', padding: 2 }}>155.01 ▼</div>
{OB_ROWS.map((r, i) => (
<ObRow key={`g${i}`} w={r.wGreen} price={r.priceGreen} amt={r.amtGreen} />
))}
</div>
{/* 中:K 线 */}
<div style={{ position: 'absolute', left: '24%', top: '9%', bottom: '16%', right: '22%', padding: '4px 6px', boxSizing: 'border-box' }}>
<div style={{ display: 'flex', alignItems: 'baseline', gap: 6 }}>
<span style={{ fontSize: 7, fontWeight: 700, color: '#e8e6df' }}>
● TOKEN-USD <span style={{ color: '#565c66', fontSize: 5 }}>PERP</span>
</span>
<span style={{ fontSize: 8, fontWeight: 800, color: '#3ecf96' }}>155.01</span>
<span style={{ ...FS(5), color: '#7c828c' }}>24h Vol $1,891,145.10 {' '} Funding 0.0042% {' '} OI $9.4M</span>
</div>
<svg viewBox="0 0 290 130" style={{ width: '100%', height: '84%' }} preserveAspectRatio="none">
{CANDLES.map((c, i) => (
<React.Fragment key={i}>
<line x1={c.x + 2} y1={c.hi} x2={c.x + 2} y2={c.lo} stroke={c.col} strokeWidth={0.8} />
<rect x={c.x} y={c.yTop} width={4} height={c.h} fill={c.col} />
<rect x={c.x} y={c.volY} width={4} height={16} fill={c.col} opacity={0.45} />
</React.Fragment>
))}
</svg>
</div>
{/* 右:买卖面板 */}
<div style={{ position: 'absolute', right: 0, top: '9%', bottom: '16%', width: '22%', borderLeft: '1px solid #1a1c20', padding: '4px 6px', boxSizing: 'border-box' }}>
<div style={{ display: 'flex', gap: 3, marginBottom: 4 }}>
<span style={{ flex: 1, textAlign: 'center', fontSize: 5.5, color: '#d8dbe0', background: '#1d2026', borderRadius: 3, padding: '2px 0' }}>Cross</span>
<span style={{ flex: 1, textAlign: 'center', fontSize: 5.5, color: '#7c828c', background: '#14161a', borderRadius: 3, padding: '2px 0' }}>10x</span>
<span style={{ flex: 1, textAlign: 'center', fontSize: 5.5, color: '#7c828c', background: '#14161a', borderRadius: 3, padding: '2px 0' }}>One-Way</span>
</div>
<div style={{ ...rowBetween, ...FS(5), color: '#7c828c', marginBottom: 2 }}>
<span>Market</span><span>Limit</span><span>Pro</span>
</div>
<div style={{ height: 5, margin: '4px 0', background: 'linear-gradient(90deg,#e6c476,#e6c476 60%,#2a2d33 60%)', borderRadius: 2 }} />
<div style={{ ...FS(5), color: '#7c828c', marginBottom: 4 }}>▢ Reduce Only</div>
<div style={{ display: 'flex', gap: 4, marginBottom: 5 }}>
<div style={{ flex: 1, height: 14, borderRadius: 3, background: '#19a374', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 6, fontWeight: 700, color: '#04120c' }}>Buy</div>
<div style={{ flex: 1, height: 14, borderRadius: 3, background: '#d6455a', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 6, fontWeight: 700, color: '#1c0508' }}>Sell</div>
</div>
{(['Current Position|0.00 TOKEN', 'Liq. Price|--', 'Order Value|$0.00', 'Margin Required|$0.00', 'Fees|0.035% / 0.010%'] as const).map((s) => {
const p = s.split('|');
return (
<div key={p[0]} style={{ ...rowBetween, ...FS(5), color: '#7c828c', marginBottom: 2.5 }}>
<span>{p[0]}</span><span style={{ color: '#b9bec6' }}>{p[1]}</span>
</div>
);
})}
<div style={{ borderTop: '1px solid #1a1c20', marginTop: 4, paddingTop: 3, fontSize: 5.5, color: '#d8dbe0' }}>Account</div>
{(['Portfolio Margin|$20,182.49', 'Unrealized PNL|+$142.11', 'Available|$1,021.19'] as const).map((s) => {
const p = s.split('|');
return (
<div key={p[0]} style={{ ...rowBetween, ...FS(5), color: '#7c828c', marginTop: 2.5 }}>
<span>{p[0]}</span><span style={{ color: '#b9bec6' }}>{p[1]}</span>
</div>
);
})}
</div>
{/* 底:持仓表 */}
<div style={{ position: 'absolute', left: 0, right: 0, bottom: 0, height: '16%', borderTop: '1px solid #1a1c20', padding: '3px 8px', boxSizing: 'border-box' }}>
<div style={{ display: 'flex', gap: 9, marginBottom: 3 }}>
{['Positions (2)', 'Open Orders (0)', 'Balances', 'Order History', 'Trade History', 'Funding History', 'Position History'].map((s, i) => (
<span key={s} style={{ ...FS(5), color: i === 0 ? '#d8dbe0' : '#565c66' }}>{s}</span>
))}
</div>
{[0, 1].map((i) => (
<div key={i} style={{ display: 'flex', gap: 12, ...FS(5), color: '#7c828c', marginBottom: 2 }}>
<span style={{ color: '#d8dbe0' }}>{i === 0 ? 'TOKEN' : 'ALT'}-USD</span>
<span style={{ color: i === 0 ? '#3ecf96' : '#e05a70' }}>{i === 0 ? '+12.40' : '-3.61'}</span>
<span>152.30</span><span>$7,801.75</span><span>$1,775.00</span><span>74,212.07</span><span>$53,225.00</span>
<span style={{ color: '#e6c476' }}>Market | Limit</span>
<span style={{ color: '#7c828c' }}>Reverse</span>
</div>
))}
</div>
</div>
</div>
{/* 虚化压暗层 */}
<div style={{ position: 'absolute', inset: 0, background: 'rgba(6,5,4,.4)', opacity: blUp * 0.22, pointerEvents: 'none' }} />
{/* 金色巡游光斑:实心亮核 + box-shadow 外扩辉光(root 有 perspective,screen 混合会被隔离,见 effect.js) */}
<div
style={{
position: 'absolute',
left: `${bx.toFixed(2)}%`, top: `${by.toFixed(2)}%`,
width: Number(bw.toFixed(1)), height: Number(bh.toFixed(1)),
borderRadius: Number((bh / 2).toFixed(1)),
transform: 'translate(-50%,-50%)',
background: 'radial-gradient(60% 60% at 50% 50%,#fffefa 0%,#fffdf2 40%,#ffeec2 66%,rgba(255,206,110,.5) 85%,rgba(212,165,70,0))',
filter: 'blur(2px)',
opacity: gOn * (1 - gOff),
pointerEvents: 'none',
boxShadow: '0 0 18px rgba(255,235,175,.95),0 0 44px rgba(240,200,120,.6),0 0 90px rgba(212,175,90,.35)',
}}
/>
{/* Focus Mode 弹窗(描边与它共用同一个盒子与 transform 链,全程贴合轮廓) */}
<div
style={{
position: 'absolute', left: `${MCX}%`, top: `${MCY}%`, width: `${MW}%`, height: `${MH}%`,
transform: modalTransform,
opacity: Math.max(mBase * 0.72, mc),
borderRadius: MRAD,
background: 'linear-gradient(170deg,#141310,#0d0c0a)',
border: '1px solid rgba(230,196,118,.3)',
boxShadow: `0 0 ${(12 * mc).toFixed(1)}px rgba(212,175,90,${(0.3 * mc).toFixed(3)}),0 18px 44px rgba(0,0,0,.72)`,
padding: '6px 7px', boxSizing: 'border-box',
}}
>
<div style={{ fontSize: 5.5, fontWeight: 700, color: '#f2ead2', marginBottom: 4 }}>Focus Mode</div>
<div style={{ fontSize: 3.4, lineHeight: 1.62, color: '#8b8f98', marginBottom: 3 }}>
All panels share one unified workspace layout. Changes in one panel are reflected in the others,{' '}
<span style={{ color: '#cbb26a' }}>keeping context in one place</span>.
</div>
<div style={{ fontSize: 3.4, color: '#8b8f98', marginBottom: 4 }}>Choose how panels are arranged:</div>
<div style={{ border: '1px solid rgba(230,196,118,.42)', borderRadius: 3, background: 'rgba(230,196,118,.05)', padding: '4px 5px', marginBottom: 4 }}>
<div style={{ fontSize: 4, fontWeight: 700, color: '#eee6cc' }}>● Standard</div>
<div style={{ fontSize: 3.3, lineHeight: 1.55, color: '#8b8f98', marginTop: 1.5 }}>
Placeholder body copy for option one. The selected option directly determines the layout of each panel — simple and predictable.
</div>
</div>
<div style={{ border: '1px solid #23252a', borderRadius: 3, padding: '4px 5px' }}>
<div style={{ fontSize: 4, fontWeight: 700, color: '#b9bec6' }}>○ Pro</div>
<div style={{ fontSize: 3.3, lineHeight: 1.55, color: '#71757e', marginTop: 1.5 }}>
Placeholder body copy for option two, written a little longer so the block keeps its shape. Replace both with your own wording.
</div>
</div>
<div
style={{
position: 'absolute', left: 7, right: 7, bottom: 6, height: 9, borderRadius: 2.5,
background: 'linear-gradient(180deg,#e2bd63,#caa03e)',
display: 'flex', alignItems: 'center', justifyContent: 'center',
fontSize: 4, fontWeight: 700, color: '#241b06',
}}
>
Confirm
</div>
</div>
{/* 辉光描边(viewBox 1 unit = 1 CSS px;pathLength 归一化免运行时 getTotalLength) */}
<div
style={{
position: 'absolute', left: `${MCX}%`, top: `${MCY}%`, width: `${MW}%`, height: `${MH}%`,
transform: modalTransform,
pointerEvents: 'none',
opacity: dr > 0.001 ? 1 - settle * 0.42 : 0,
mixBlendMode: 'screen',
}}
>
<svg viewBox={`0 0 ${BW.toFixed(2)} ${BH.toFixed(2)}`} style={{ width: '100%', height: '100%', overflow: 'visible' }}>
<path
d={TRACE_D}
pathLength={P_L}
fill="none"
stroke={settle > 0.5 ? '#e6c887' : '#fff0c4'}
strokeWidth={Number((2.9 - settle * 1.9).toFixed(2))}
strokeLinecap="round"
strokeDasharray={`${P_L} ${P_L}`}
strokeDashoffset={Number((P_L * (1 - dr)).toFixed(1))}
style={{
filter:
settle > 0.001
? `drop-shadow(0 0 ${(3 - settle * 2.3).toFixed(2)}px rgba(255,232,160,${(0.95 - settle * 0.5).toFixed(2)}))` +
` drop-shadow(0 0 ${(10 - settle * 8).toFixed(1)}px rgba(240,200,110,${(0.75 - settle * 0.62).toFixed(2)}))`
: 'drop-shadow(0 0 3px rgba(255,232,160,.95)) drop-shadow(0 0 10px rgba(240,200,110,.75)) drop-shadow(0 0 26px rgba(212,175,90,.4))',
}}
/>
</svg>
</div>
</div>
</DesignStage>
);
};
- Read the recipePrompt = when to use, intention, parameter table with typical values and how each one feels, known pitfalls. Treat values as calibrated starting points.
- Get the codetool: git
git clone https://github.com/Vincentwei1021/video-shotcraft && cd video-shotcraft && npm install - Register + rendertool: remotion
npx remotion render <CompositionId> out.mp4 # component: demos/effects/dashboard-glow-highlight-pill/DashboardGlowHighlightPill.tsx - Or re-implementPort the timing/easing from the recipe to HTML + WAAPI (gimgs motion contract) or any engine; keep the same segment windows and easing.
| param | type | default | description |
|---|---|---|---|
| — | |||
More in this category

Dark-stage spotlight reveals
Three dark-stage spotlight reveals — A wake-sleep sweep (light in = bright, light out = dark), B edge-hugging glow pan (violet light seeps along UI edges + steady rightward sweep), C corner reveal (radial light expands steadily from a corner to light the whole screen); light itself narrates in the dark.

Slam entrance moves
Three high-energy slam entrances: a whip-in perspective stop, a heavy KPI drop, and a shockwave that hits neighboring cards.

Scanline assemble fly-in
A scan line sweeps down an empty grid, flying each region's component in from off-screen — finishing as the scan ends.

Scanline annotate focus
A bright scan line sweeps down the page, framing each element it passes and labeling it with monospace annotations.