Remocn
componentfomo-limit-orderssource: ok · hit
Order Flow
An independent 18-second trading-interface demo with fictional market data, graphite and amber styling, an original stepped mark, and editable camera choreography.
npx shadcn add "https://www.remocn.dev/r/fomo-limit-orders.json"free
Where it goes
data-table
Use cases
dashboard
Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.
Dependencies
Bundle weight · lightremotion@fontsource/manrope
Files10 · source cached 10/9/2026, 6:06:59 AM
registry/remocn-templates/fomo-limit-orders/content.ts
export interface FomoContent {
intro: string;
feature: string;
audience: string;
ticker: string;
assetName: string;
leverage: string;
position: string;
positionValue: string;
positionProfit: string;
positionSize: string;
positionQuantity: string;
positionReturn: string;
entryPrice: string;
liquidationPrice: string;
marketPrice: string;
change: string;
openInterest: string;
limitPrice: string;
margin: string;
orderSize: string;
action: string;
closing: string;
}
export const fomoContent: FomoContent = {
intro: "New in Order Flow",
feature: "Plan your next exit",
audience: "Before the market moves",
ticker: "DEMO",
assetName: "Demo Market",
leverage: "5x",
position: "2x Long",
positionValue: "$12,640.00",
positionProfit: "+$640.00",
positionSize: "$12.64K",
positionQuantity: "160 DEMO",
positionReturn: "10.67%",
entryPrice: "$75.00",
liquidationPrice: "$37.50",
marketPrice: "$79.00",
change: "1.28%",
openInterest: "$24M OI",
limitPrice: "$82.00",
margin: "$6,000.00",
orderSize: "$12.64K",
action: "Set an exit price",
closing: "Your price. Your next move.",
};
export interface FomoTheme {
ink: string;
panel: string;
paper: string;
accent: string;
positive: string;
}
export const fomoTheme: FomoTheme = {
ink: "#121310",
panel: "#20221c",
paper: "#f6f1e7",
accent: "#e8b45a",
positive: "#80bf97",
};
export interface FomoLimitOrdersProps {
brandName?: string;
accentColor?: string;
content?: Partial<FomoContent>;
theme?: Partial<FomoTheme>;
/** Optional, user-owned soundtrack. The source reference audio is not redistributed. */
audioSrc?: string;
volume?: number;
}
export interface SceneProps {
t: number;
content: FomoContent;
theme: FomoTheme;
brandName: string;
}
registry/remocn-templates/fomo-limit-orders/index.tsx
"use client";
import "@fontsource/manrope/latin-400.css";
import "@fontsource/manrope/latin-500.css";
import "@fontsource/manrope/latin-600.css";
import "@fontsource/manrope/latin-800.css";
import { AbsoluteFill, Audio, useCurrentFrame, useVideoConfig } from "remotion";
import {
type FomoLimitOrdersProps,
fomoContent,
fomoTheme,
type SceneProps,
} from "./content";
import {
FOMO_FPS,
FOMO_FRAMES,
FOMO_HEIGHT,
FOMO_WIDTH,
tween,
} from "./motion";
import { Closing } from "./scenes/closing";
import { Confirmation } from "./scenes/confirmation";
import { Opening } from "./scenes/opening";
import { Phone } from "./scenes/phone";
import { Price } from "./scenes/price";
import { Slider } from "./scenes/slider";
export type { FomoContent, FomoLimitOrdersProps, FomoTheme } from "./content";
export { fomoContent, fomoTheme } from "./content";
export {
FOMO_FPS,
FOMO_FRAMES,
FOMO_HEIGHT,
FOMO_WIDTH,
fomoTimeline,
} from "./motion";
export function FomoLimitOrders(props: FomoLimitOrdersProps) {
const frame = useCurrentFrame();
const { width, height, fps } = useVideoConfig();
const t = frame / fps;
const scale = Math.min(width / 480, height / 270);
const theme = {
...fomoTheme,
...props.theme,
...(props.accentColor ? { accent: props.accentColor } : {}),
};
const scene: SceneProps = {
t,
theme,
content: { ...fomoContent, ...props.content },
brandName: props.brandName ?? "Order Flow",
};
const volume = Number.isFinite(props.volume)
? Math.min(1, Math.max(0, props.volume ?? 1))
: 1;
return (
<AbsoluteFill
style={{
background: theme.ink,
color: "#fff",
overflow: "hidden",
fontFamily: "Manrope, sans-serif",
fontWeight: 400,
WebkitFontSmoothing: "antialiased",
}}
>
<div
style={{
position: "absolute",
left: (width - 480 * scale) / 2,
top: (height - 270 * scale) / 2,
width: 480,
height: 270,
transform: `scale(${scale})`,
transformOrigin: "0 0",
}}
>
{t < 2.7 ? (
<Opening {...scene} />
) : t < 8 ? (
<Phone {...scene} />
) : t < 10.9 ? (
<Slider {...scene} />
) : t < 12.6 ? (
<div
style={{
position: "absolute",
inset: 0,
opacity: 1 - tween(t, 12.3, 12.6),
}}
>
<Price {...scene} />
</div>
) : t < 14.15 ? (
<div
style={{
position: "absolute",
inset: 0,
opacity: tween(t, 12.6, 12.78),
}}
>
<Confirmation {...scene} t={t + 1.85} />
</div>
) : (
<Closing {...scene} t={t + 1.85} />
)}
</div>
{props.audioSrc?.trim() ? (
<Audio src={props.audioSrc} volume={volume} />
) : null}
</AbsoluteFill>
);
}
export const OrderFlow = FomoLimitOrders;
export type OrderFlowProps = FomoLimitOrdersProps;
export const fomoLimitOrdersConfig = {
componentName: "OrderFlow",
importPath: "@/components/remocn/templates/fomo-limit-orders",
controls: {
brandName: {
type: "text-content" as const,
default: "Order Flow",
description: "Closing wordmark",
},
accentColor: {
type: "color" as const,
default: "#e8b45a",
description: "Controls and progress color",
},
},
durationInFrames: FOMO_FRAMES,
fps: FOMO_FPS,
compositionWidth: FOMO_WIDTH,
compositionHeight: FOMO_HEIGHT,
};
registry/remocn-templates/fomo-limit-orders/motion.ts
import { Easing, interpolate } from "remotion";
export const FOMO_FPS = 60;
export const FOMO_FRAMES = 1108;
export const FOMO_WIDTH = 1920;
export const FOMO_HEIGHT = 1080;
export const fomoTimeline = [
{ id: "opening", from: 0, to: 162 },
{ id: "phone", from: 162, to: 480 },
{ id: "slider", from: 480, to: 654 },
{ id: "price", from: 654, to: 756 },
{ id: "confirmation", from: 756, to: 849 },
{ id: "closing", from: 849, to: FOMO_FRAMES },
] as const;
export const ease = Easing.bezier(0.22, 1, 0.36, 1);
export const travel = Easing.bezier(0.65, 0, 0.3, 1);
/** Fit to 25 source-frame measurements, 10.483–10.883 seconds. */
export const contraction = Easing.bezier(0.652, 0.343, 0.075, 0.909);
export const clamp = (n: number) => Math.min(1, Math.max(0, n));
export function ramp(t: number, start: number, end: number) {
return clamp((t - start) / (end - start));
}
export function tween(
t: number,
start: number,
end: number,
a = 0,
b = 1,
easing = ease,
) {
return a + (b - a) * easing(ramp(t, start, end));
}
export function key(t: number, times: number[], values: number[]) {
return interpolate(t, times, values, {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
}
export function money(n: number) {
return `$${n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
}
/** Centerline of the phone border, in the same local coordinates as its camera. */
export function phoneBorderPoint(progress: number) {
const inset = 0.85;
const r = 59 - inset;
const straight = 230 - 59;
const arc = (Math.PI * r) / 2;
const distance = clamp(progress) * (straight + arc + 66);
if (distance <= straight) return { x: 230 - distance, y: 680 - inset };
if (distance <= straight + arc) {
const angle = Math.PI / 2 + (distance - straight) / r;
return { x: 59 + r * Math.cos(angle), y: 621 + r * Math.sin(angle) };
}
return { x: inset, y: 621 - (distance - straight - arc) };
}
registry/remocn-templates/fomo-limit-orders/scenes/closing.tsx
import type { SceneProps } from "../content";
import { ramp, tween } from "../motion";
import { AssetIcon, Layer } from "../ui";
export function Closing({ t, content, brandName, theme }: SceneProps) {
const brand = tween(t, 18.15, 18.38);
return (
<>
{t < 18.3 && (
<Layer
x={15}
y={111}
w={450}
h={42}
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: 5,
fontSize: 22.5,
letterSpacing: -1,
whiteSpace: "nowrap",
opacity: 1 - ramp(t, 18.13, 18.24),
}}
>
{content.closing.split(" ").map((word, i) => {
const p = tween(t, 16 + i * 0.3, 16.18 + i * 0.3);
return (
<span
key={i}
style={{
opacity: p,
filter: `blur(${(1 - p) * 4}px)`,
transform: `translateY(${(1 - p) * 7}px)`,
}}
>
{word}
</span>
);
})}
</Layer>
)}
{t >= 18.15 && (
<Layer
x={15}
y={98}
w={450}
h={64}
style={{
display: "grid",
placeItems: "center",
fontSize: brandName.length > 9 ? 31 : 42,
fontWeight: 600,
letterSpacing: -1.2,
opacity: brand,
transform: `scale(${0.95 + 0.05 * brand})`,
filter: `blur(${(1 - brand) * 5}px)`,
}}
>
<span style={{ display: "flex", alignItems: "center", gap: 12 }}>
<AssetIcon size={42} color={theme.accent} />
{brandName}
</span>
</Layer>
)}
</>
);
}
registry/remocn-templates/fomo-limit-orders/scenes/confirmation.tsx
import type { SceneProps } from "../content";
import { key, tween } from "../motion";
import { AssetIcon, Badge, Layer, muted, row } from "../ui";
export function Confirmation({ t, content: c, theme }: SceneProps) {
const scale = key(
t,
[14.45, 14.65, 15.25, 15.6, 15.8, 16],
[0.67, 0.73, 1, 1.16, 1.25, 1.3],
);
const exit = tween(t, 15.77, 16, 0, 210, (v) => v * v);
const fill = Math.round(
key(t, [14.45, 14.7, 15, 15.25, 15.45], [20, 50, 83, 97, 100]),
);
return (
<Layer
x={240}
y={135 + exit}
w={300}
h={110}
style={{
transform: `translate(-50%, -50%) scale(${scale}) rotate(${key(t, [14.45, 15.4, 16], [-1.2, 0, 2])}deg)`,
background: theme.panel,
borderRadius: 12,
boxShadow: "0 0 10px #18131e65",
fontWeight: 500,
letterSpacing: -0.4,
}}
>
<Layer x={10} y={10}>
<Badge theme={theme}>{c.position}</Badge>
</Layer>
<Layer
x={235}
y={12}
w={55}
style={{ fontSize: 14, textAlign: "right", color: "#d0ced9" }}
>
Cancel
</Layer>
<Layer x={10} y={41} style={{ ...row, gap: 5, fontSize: 16 }}>
<AssetIcon size={18} color={theme.accent} />
<span>{c.ticker}</span>
<span style={{ color: muted, fontSize: 12 }}>⊙</span>
<span>{c.limitPrice}</span>
</Layer>
<Layer
x={214}
y={45}
style={{ ...row, gap: 8, fontSize: 12, color: muted }}
>
<svg width="14" height="14" viewBox="0 0 14 14" aria-hidden>
<circle
cx="7"
cy="7"
r="5"
fill="none"
stroke="#302946"
strokeWidth="1.8"
/>
<circle
cx="7"
cy="7"
r="5"
fill="none"
stroke={theme.accent}
strokeWidth="1.8"
strokeDasharray={`${(31.416 * fill) / 100} 31.416`}
transform="rotate(-90 7 7)"
/>
</svg>
<span>{fill}% filled</span>
</Layer>
<Layer
x={10}
y={86}
w={280}
style={{
...row,
justifyContent: "space-between",
fontSize: 12,
color: "#bbb8c6",
}}
>
<span>
Margin{" "}
<span style={{ color: "white", fontSize: 15, marginLeft: 4 }}>
{c.margin}
</span>
</span>
<span>
Lev. size{" "}
<span style={{ color: "white", fontSize: 15, marginLeft: 4 }}>
{c.orderSize}
</span>
</span>
</Layer>
</Layer>
);
}
registry/remocn-templates/fomo-limit-orders/scenes/opening.tsx
import type { SceneProps } from "../content";
import { ramp, tween } from "../motion";
import { Layer } from "../ui";
export function Opening({ t, content }: SceneProps) {
const text =
t < 0.7 ? content.intro : t < 1.9 ? content.feature : content.audience;
const start = t < 0.7 ? 0 : t < 1.9 ? 0.7 : 1.9;
const words = text.split(" ");
return (
<>
{Array.from({ length: 10 }, (_, i) => {
const upper = i < 5;
const column = i % 5;
const height = [18, 48, 85, 61, 30][column];
const drift = tween(t, 0.8 + column * 0.025, 1.25 + column * 0.025);
return (
<Layer
key={i}
x={upper ? 277 + column * 47 : -35 + column * 47}
y={upper ? -14 : 270 - height + 12}
w={47}
h={height}
style={{
opacity: ramp(t, 0.8, 1.05) * (1 - ramp(t, 2.45, 2.7)),
transform: `translateY(${(1 - drift) * (upper ? -75 : 75) + Math.sin(t * 2 + column) * 5}px)`,
borderRadius: 2,
background: `linear-gradient(${upper ? 150 : 30}deg, #c29b5452, #8770444a 40%, #4a412d30 76%, #12131000)`,
boxShadow: "inset 0.5px 0 1px #efd19626",
filter: "blur(0.7px)",
}}
/>
);
})}
<Layer
x={19}
y={110}
w={450}
h={42}
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
gap: 6,
fontSize: 24.5,
letterSpacing: -1.1,
whiteSpace: "nowrap",
opacity: 1 - ramp(t, 2.55, 2.7),
}}
>
{words.map((word, i) => {
const enter = tween(t, start + i * 0.1, start + i * 0.1 + 0.2);
return (
<span
key={`${start}-${i}`}
style={{
opacity: enter,
filter: `blur(${(1 - enter) * 5}px)`,
transform: `translateY(${(1 - enter) * 9}px)`,
}}
>
{word}
</span>
);
})}
</Layer>
</>
);
}
registry/remocn-templates/fomo-limit-orders/scenes/phone.tsx
import type { SceneProps } from "../content";
import { key, phoneBorderPoint, ramp, travel, tween } from "../motion";
import { AssetIcon, Badge, Layer, muted, row } from "../ui";
function PhoneUI({ t, content: c, theme }: SceneProps) {
return (
<>
<Layer
x={62}
y={32}
style={{ opacity: ramp(t, 3.85, 4.3), fontSize: 14, fontWeight: 600 }}
>
9:41
</Layer>
<Layer
x={280}
y={37}
style={{ opacity: ramp(t, 3.85, 4.3), fontSize: 9, letterSpacing: 3 }}
>
▮▮▮ ◔ ▰
</Layer>
<Layer x={54} y={73} style={{ ...row, gap: 14 }}>
<span style={{ position: "relative", display: "flex" }}>
<AssetIcon size={44} color={theme.accent} />
<span
style={{
position: "absolute",
inset: 0,
borderRadius: "50%",
background: "black",
opacity: 1 - ramp(t, 3.5, 4.1),
mixBlendMode: "darken",
}}
/>
<svg
width="44"
height="44"
viewBox="0 0 40 40"
style={{ position: "absolute", opacity: 1 - ramp(t, 3.5, 4.1) }}
>
<circle
cx="20"
cy="20"
r="19.8"
fill="none"
stroke="#ffffff10"
strokeWidth="0.4"
/>
<path d="M9 25h5v6H9Zm8-8h5v14h-5Zm8-8h6v22h-6Z" fill="white" />
</svg>
</span>
<div>
<div style={{ ...row, gap: 8, fontSize: 16.5, letterSpacing: -0.5 }}>
{c.ticker}
<span
style={{
fontSize: 12,
color: theme.accent,
borderRadius: 3,
padding: "1px 5px",
background: `${theme.accent}25`,
opacity: ramp(t, 3.35, 3.85),
}}
>
{c.leverage}
</span>
</div>
<div style={{ marginTop: 3, fontSize: 12, color: muted }}>
{c.assetName.slice(
0,
Math.floor(tween(t, 3.28, 4.1, 0, c.assetName.length)),
)}
</div>
</div>
</Layer>
<div style={{ opacity: ramp(t, 3.85, 4.3) }}>
<Layer x={23} y={135} style={{ fontSize: 24, letterSpacing: -0.9 }}>
{c.marketPrice}
</Layer>
<Layer x={24} y={169} style={{ fontSize: 12, color: theme.positive }}>
↑ $1.00 ({c.change})
</Layer>
<Layer x={22} y={215} w={355} h={110}>
<svg width="355" height="110" viewBox="0 0 355 110" aria-hidden>
{[20, 55, 90].map((y) => (
<path
key={y}
d={`M0 ${y}H355`}
stroke="#ffffff0a"
strokeWidth="0.5"
/>
))}
{Array.from({ length: 57 }, (_, i) => {
const y =
85 - i * 1.07 + Math.sin(i * 0.6) * 13 + Math.sin(i * 1.4) * 4;
const positive = i % 5 !== 1 && i % 7 !== 2;
return (
<g
key={i}
stroke={positive ? theme.positive : "#cf495e"}
fill={positive ? theme.positive : "#cf495e"}
>
<path d={`M${i * 6 + 2} ${y - 6}v20`} strokeWidth="0.6" />
<rect
x={i * 6}
y={y}
width="3.5"
height={5 + (i % 6)}
rx="0.3"
/>
</g>
);
})}
</svg>
</Layer>
<Layer
x={9}
y={334}
w={365}
style={{
...row,
justifyContent: "space-between",
color: "#b5b2be",
fontSize: 12,
fontWeight: 600,
}}
>
<span>
<span style={{ color: theme.positive }}>╂</span> 15m ⌃
</span>
{["1D", "1W", "3M", "6M", "1Y", "All"].map((s, i) => (
<span
key={s}
style={{
padding: 4,
background: i === 0 ? "#2b2c26" : undefined,
borderRadius: 4,
color: i === 0 ? "white" : undefined,
}}
>
{s}
</span>
))}
<span style={{ color: theme.positive, fontSize: 21 }}>ϟ</span>
</Layer>
<Layer
x={17}
y={379}
w={366}
h={138}
style={{
background: `linear-gradient(125deg, ${theme.ink}, ${theme.panel})`,
borderRadius: 11,
boxShadow: "inset 0 1px 8px #ffffff04",
}}
>
<Layer x={11} y={9}>
<Badge theme={theme}>{c.position}</Badge>
</Layer>
<Layer
x={11}
y={39}
w={344}
style={{
...row,
justifyContent: "space-between",
fontSize: 21,
fontWeight: 500,
letterSpacing: -1.1,
}}
>
<span>{c.positionValue}</span>
<span style={{ color: theme.positive }}>{c.positionProfit}</span>
</Layer>
<Layer x={11} y={70} style={{ fontSize: 13, color: muted }}>
Lev. size <span style={{ color: "#e7e6ed" }}>{c.positionSize}</span>{" "}
({c.positionQuantity})
</Layer>
<Layer x={265} y={70} style={{ fontSize: 13, color: "#bab7c4" }}>
<span style={{ color: theme.positive }}>▴</span> {c.positionReturn}
</Layer>
<Layer
x={16}
y={97}
w={334}
h={0.6}
style={{ background: "#ffffff0b" }}
/>
<Layer
x={11}
y={108}
w={344}
style={{
...row,
justifyContent: "space-between",
fontSize: 13,
color: muted,
}}
>
<span>
Avg. entry{" "}
<span style={{ color: "white", marginLeft: 6 }}>
{c.entryPrice}
</span>
</span>
<span>
Liq. price{" "}
<span style={{ color: "white", marginLeft: 6 }}>
{c.liquidationPrice}
</span>
</span>
</Layer>
</Layer>
<Layer
x={28}
y={541}
w={345}
style={{
...row,
justifyContent: "space-between",
color: theme.accent,
fontSize: 13,
fontWeight: 600,
}}
>
<span>
<span style={{ color: muted, marginRight: 10 }}>+</span>Add a note
</span>
<span>Add SL/TP</span>
</Layer>
<Layer x={51} y={575} style={{ fontSize: 11, color: muted }}>
Overview Activity Details
</Layer>
<Layer
x={24}
y={593}
w={350}
h={44}
style={{ ...row, gap: 15, fontSize: 13, fontWeight: 600 }}
>
<div
style={{
width: 43,
height: 42,
display: "grid",
placeItems: "center",
borderRadius: 11,
background: "#282c22",
border: "1px solid #7e806c",
}}
>
<svg width="19" height="19" viewBox="0 0 24 24">
<path d="m4 16 11-11 5 5-11 11-6 1Z" fill="white" />
<path d="m13 7 5 5" stroke="#282c22" />
</svg>
</div>
<div
style={{
background: theme.accent,
color: theme.ink,
borderRadius: 9,
padding: "11px 14px",
whiteSpace: "nowrap",
boxShadow: "inset 0 1px 1px #ffffff99",
}}
>
Close position
</div>
<div
style={{
background: theme.panel,
color: theme.positive,
borderRadius: 9,
padding: "11px 32px",
}}
>
Long
</div>
</Layer>
</div>
</>
);
}
export function Phone(props: SceneProps) {
const { t } = props;
const push = tween(t, 4.63, 5.18, 0, 1, travel);
const pull = tween(t, 6.04, 6.92, 0, 1, travel);
const orbit = tween(t, 6.92, 7.7, 0, 1, travel);
const openingScale = tween(t, 3.4, 4.3, 1.45, 1);
const s = openingScale + 0.18 * push - 0.28 * pull + 0.55 * orbit;
const x = tween(t, 3.4, 4.3, 62, 75) - 67 * push + 134 * pull + 95 * orbit;
const y = tween(t, 3.4, 4.3, 9, 45) - 435 * push - 13 * pull - 309 * orbit;
const dotTimes = [
6.55, 6.75, 7, 7.25, 7.5, 7.65, 7.75, 7.8, 7.85, 7.9, 7.95, 8,
];
const point = phoneBorderPoint(tween(t, 6.55, 7.75, 0, 1, travel));
const dotX = tween(t, 7.85, 8, x + point.x * s, 240);
const dotY = tween(t, 7.85, 8, y + point.y * s, 135);
const radius = key(
t,
dotTimes,
[4, 6, 6, 6.5, 7.5, 9.5, 12, 16, 24, 57, 160, 390],
);
return (
<>
<Layer
w={480}
h={270}
style={{ background: "black", opacity: ramp(t, 2.7, 2.9) }}
/>
<Layer
x={x}
y={y}
w={400}
h={680}
style={{
transform: `scale(${s})`,
transformOrigin: "0 0",
opacity: ramp(t, 2.7, 3),
filter: `blur(${Math.sin(push * Math.PI) * 1.1}px)`,
}}
>
<Layer
w={400}
h={680}
style={{
border: "1.7px solid #55584a",
boxSizing: "border-box",
borderRadius: 59,
opacity: ramp(t, 3.65, 4.35) * (1 - 0.8 * push * (1 - pull)),
boxShadow: "0 0 2px #ad926155, inset 0 0 4px #ad926122",
background: "#000",
}}
/>
<Layer
w={400}
h={680}
style={{
borderLeft: "1.2px solid #e8b45a",
boxSizing: "border-box",
borderBottom: "1.2px solid #e8b45a",
borderRadius: 59,
opacity: ramp(t, 6.55, 7.2),
maskImage: "linear-gradient(0deg, black, transparent 34%)",
boxShadow: "-1px 1px 3px #d59b4044",
}}
/>
<div style={{ opacity: 1 - ramp(t, 7.78, 7.96), fontWeight: 500 }}>
<PhoneUI {...props} />
</div>
</Layer>
<Layer
w={480}
h={270}
style={{
opacity: Math.sin(push * Math.PI) * 0.24,
background:
"linear-gradient(120deg, transparent 10%, #ebc47b 53%, transparent 75%)",
mixBlendMode: "screen",
}}
/>
<Layer
w={480}
h={270}
style={{
opacity: ramp(t, 6.1, 7),
background:
"linear-gradient(100deg, #000 0%, transparent 43%, #0004 66%, #000 96%)",
}}
/>
{t >= 6.55 && (
<>
<Layer
x={dotX - radius * 2}
y={dotY - radius * 2}
w={radius * 4}
h={radius * 4}
style={{
background: "#eed7a6",
borderRadius: "50%",
filter: `blur(${Math.min(10, radius)}px)`,
opacity: t < 7.88 ? 0.2 : 0,
}}
/>
<Layer
x={dotX - radius}
y={dotY - radius}
w={radius * 2}
h={radius * 2}
style={{
background: "#fff",
borderRadius: "50%",
boxShadow: "0 0 4px #f6e3bf",
}}
/>
</>
)}
</>
);
}
registry/remocn-templates/fomo-limit-orders/scenes/price.tsx
import type { SceneProps } from "../content";
import { ramp, travel, tween } from "../motion";
import { AssetIcon, Layer, lightMuted as muted } from "../ui";
export function Price({ t, content: c, theme }: SceneProps) {
const zoom = tween(t, 11.65, 12.25, 1, 2.7, travel);
const targetX = tween(t, 11.65, 12.25, 240, 277, travel);
const typed =
t < 11.84
? "$"
: c.limitPrice.slice(
0,
Math.floor(tween(t, 11.84, 12.12, 1, c.limitPrice.length, (v) => v)),
);
const alpha = ramp(t, 10.9, 11.18);
return (
<>
<Layer
w={480}
h={270}
style={{
background: `linear-gradient(145deg, ${theme.paper}, #ebe1ce 65%, #f7f0e3)`,
}}
/>
<Layer
x={240 - targetX * zoom}
y={135 - 135 * zoom}
w={480}
h={270}
style={{
transform: `scale(${zoom})`,
transformOrigin: "0 0",
color: "#28281f",
}}
>
<div style={{ opacity: alpha, fontWeight: 500 }}>
<Layer x={100} y={61}>
<AssetIcon size={30} color={theme.accent} />
</Layer>
<Layer x={140} y={63} style={{ fontSize: 12 }}>
{c.ticker}
<div style={{ marginTop: 2, color: muted, fontSize: 9 }}>
{c.openInterest}
</div>
</Layer>
<Layer
x={300}
y={65}
w={79}
style={{ textAlign: "right", fontSize: 12 }}
>
{c.marketPrice}
<div style={{ marginTop: 3, color: "#326846", fontSize: 9 }}>
{c.change}
</div>
</Layer>
<Layer
x={100}
y={110}
w={279}
h={0.6}
style={{ background: "#c5bba466" }}
/>
<Layer
x={100}
y={158}
w={279}
h={0.6}
style={{ background: "#c5bba444" }}
/>
<Layer x={117} y={120} style={{ fontSize: 11 }}>
Limit price
<div style={{ marginTop: 4, fontSize: 8, color: muted }}>
+3.80% above market
</div>
</Layer>
<Layer x={100} y={181} w={279} style={{ display: "flex", gap: 6 }}>
{["+1%", "+5%", "+10%", "+20%"].map((s) => (
<div
key={s}
style={{
width: 66,
height: 21,
borderRadius: 5,
border: "0.5px solid #cbbda282",
background: "#ffffff24",
display: "grid",
placeItems: "center",
fontSize: 9,
}}
>
{s}
</div>
))}
</Layer>
</div>
<Layer
x={225}
y={122}
w={96}
h={28}
style={{
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
transform: `translateX(${tween(t, 10.9, 11.18, -39, 0)}px)`,
gap: 5,
fontSize: 12,
fontWeight: 600,
letterSpacing: -0.35,
}}
>
<span style={{ opacity: alpha }}>{typed}</span>
<span
style={{
width: 1.5,
height: 19,
background: theme.accent,
opacity: 1,
}}
/>
<span style={{ color: "#865616", marginLeft: -1, opacity: alpha }}>
Mid
</span>
</Layer>
</Layer>
</>
);
}
registry/remocn-templates/fomo-limit-orders/scenes/slider.tsx
import type { SceneProps } from "../content";
import { contraction, key, money, ramp, tween } from "../motion";
import { Chevron, Layer, lightMuted as muted } from "../ui";
export function Slider({ t, theme, content }: SceneProps) {
const label = t < 8.65;
const barW =
t < 10.4833
? key(
t,
[8.65, 8.8, 9, 9.2, 9.3, 9.55, 9.8, 10.05, 10.3, 10.4833],
[3500, 2600, 1800, 820, 464, 322, 292, 276, 255, 220],
)
: tween(t, 10.4833, 10.8833, 220, 2, contraction);
const barH = key(
t,
[8.65, 8.8, 9, 9.2, 9.3, 9.55, 9.8, 10.3, 10.5],
[650, 400, 154, 66, 35, 24, 22, 20, 19],
);
const centerX = tween(t, 10.45, 10.8833, 240, 250, contraction);
const price = key(t, [9.3, 9.55, 9.8, 10.05, 10.3], [79, 79.5, 80, 81, 82]);
const quantity = key(
t,
[9.3, 9.55, 9.8, 10.05, 10.3],
[40, 72, 112, 144, 160],
);
const percent = Math.round((quantity / 160) * 100);
const detailAlpha = ramp(t, 9.15, 9.4) * (1 - ramp(t, 10.3, 10.56));
const uiScale = key(t, [9.3, 9.55, 9.8, 10.3], [1.6, 1.1, 1, 0.93]);
const roll = tween(t, 8.18, 8.55, 0, 1, (v) => v);
const knobSize = barH * 0.77;
return (
<>
<Layer
w={480}
h={270}
style={{
background: label
? "white"
: `linear-gradient(145deg, ${theme.paper}, #ebe1ce 65%, #f7f0e3)`,
}}
/>
{label ? (
<Layer
x={130}
y={116}
w={220}
h={38}
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: 9,
color: "#865616",
fontSize: 23,
letterSpacing: -0.6,
opacity: ramp(t, 8.01, 8.1),
}}
>
<span style={{ transform: "rotate(90deg)", display: "flex" }}>
<Chevron color={theme.accent} size={16} />
</span>
<div
style={{
width: 78,
height: 35,
position: "relative",
overflow: "visible",
}}
>
<Layer y={-roll * 35} style={{ opacity: 1 - roll }}>
Market
</Layer>
<Layer y={(1 - roll) * 35} style={{ opacity: roll }}>
Limit
</Layer>
</div>
</Layer>
) : (
<>
<Layer
x={centerX - barW / 2}
y={135 - barH / 2}
w={barW}
h={barH}
style={{
background: `linear-gradient(100deg, #c78a30, ${theme.accent})`,
borderRadius: Math.min(9, barH / 3),
boxShadow:
t < 10.7
? "inset 0 0.5px 1px #f3d8a3, 0 1px 2px #8d6a301f"
: undefined,
}}
>
<Layer
x={
barW *
key(t, [8.65, 9.2, 9.3, 10.3], [0.5, 0.57, 0.58, 0.955]) -
knobSize / 2
}
y={(barH - knobSize) / 2}
w={knobSize}
h={knobSize}
style={{
background: "#fff",
borderRadius: "50%",
boxShadow: "0 1px 3px #4d3b2126",
opacity: 1 - ramp(t, 10.4, 10.65),
}}
/>
</Layer>
<Layer
x={240}
y={135}
style={{
transform: `scale(${uiScale})`,
opacity: detailAlpha,
color: "#28281f",
fontSize: 9,
whiteSpace: "nowrap",
}}
>
<Layer
x={-146}
y={-65}
w={292}
style={{
display: "flex",
justifyContent: "center",
gap: 7,
fontSize: 11,
}}
>
<span>Limit price</span>
<span>
{money(price)}{" "}
<span style={{ color: muted }}>
(+{(((price - 79) / 79) * 100).toFixed(2)}%)
</span>
</span>
</Layer>
<Layer x={-142} y={-31} style={{ fontSize: 13, color: "#5b5548" }}>
{quantity.toFixed(2)} {content.ticker}
</Layer>
<Layer
x={113}
y={-31}
w={30}
style={{
textAlign: "right",
fontSize: 13,
color: "#865616",
fontWeight: 600,
}}
>
{percent}%
</Layer>
<Layer
x={-146}
y={25}
w={292}
style={{
display: "flex",
justifyContent: "space-between",
fontSize: 13,
}}
>
<span style={{ color: muted }}>Est. profit</span>
<span style={{ color: "#326846" }}>
{money(quantity * (price - 75))}
</span>
</Layer>
<Layer
x={-128}
y={53}
w={274}
style={{
display: "flex",
justifyContent: "space-between",
fontSize: 12,
}}
>
<span style={{ color: muted }}>Details</span>
<span><0.01% fee ﹀</span>
</Layer>
</Layer>
</>
)}
</>
);
}
registry/remocn-templates/fomo-limit-orders/ui.tsx
import type { CSSProperties, ReactNode } from "react";
import type { FomoTheme } from "./content";
export function Layer({
x = 0,
y = 0,
w,
h,
style,
children,
}: {
x?: number;
y?: number;
w?: number | string;
h?: number | string;
style?: CSSProperties;
children?: ReactNode;
}) {
return (
<div
style={{
position: "absolute",
left: x,
top: y,
width: w,
height: h,
...style,
}}
>
{children}
</div>
);
}
export function AssetIcon({
size = 40,
color = "#e8b45a",
}: {
size?: number;
color?: string;
}) {
return (
<svg width={size} height={size} viewBox="0 0 40 40" aria-hidden>
<circle
cx="20"
cy="20"
r="20"
fill="#242720"
stroke="#ffffff0b"
strokeWidth="0.5"
/>
<path d="M9 25h5v6H9Zm8-8h5v14h-5Zm8-8h6v22h-6Z" fill={color} />
</svg>
);
}
export function Chevron({
color = "white",
size = 16,
up = false,
}: {
color?: string;
size?: number;
up?: boolean;
}) {
return (
<svg
width={size}
height={size}
viewBox="0 0 24 24"
style={{ transform: up ? "rotate(-90deg)" : undefined }}
aria-hidden
>
<path
d="m5 5 7 7-7 7m8-14 7 7-7 7"
fill="none"
stroke={color}
strokeWidth="2.6"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
export function Badge({
children,
theme,
}: {
children: ReactNode;
theme: FomoTheme;
}) {
return (
<span
style={{
color: theme.positive,
background: `${theme.positive}20`,
padding: "2px 5px",
borderRadius: 3,
fontSize: 12,
fontWeight: 600,
}}
>
{children}
</span>
);
}
export const row: CSSProperties = { display: "flex", alignItems: "center" };
export const muted = "#aaa79d";
export const lightMuted = "#6a6357";