Iso Growth
IllustrationsIsometric illustration — ascending iso bars with a rising curve threading through their tops, looping to suggest compounding growth. Themeable accent via `brand`; reduced-motion aware.
components/ui/iso-growth.tsx
"use client";
import { motion, useReducedMotion } from "motion/react";
/**
* Isometric illustration — ascending iso bars with a rising curve threading
* through their tops, looping to suggest compounding growth. Themeable accent
* via `brand`; animation stills under reduced-motion.
*/
export function IsoGrowth({
brand = "#FF4701",
className,
}: {
brand?: string;
className?: string;
}) {
const reduce = useReducedMotion();
const bars = [
{ x: 64, h: 18 },
{ x: 88, h: 30 },
{ x: 112, h: 46 },
{ x: 136, h: 64 },
];
return (
<svg viewBox="0 0 200 210" className={className} style={{ ["--brand" as string]: brand }}>
<IsoPlatform />
{bars.map((b, i) => {
const baseY = 168 - (b.x - 100) * 0.44;
return (
<motion.g
key={i}
initial={{ opacity: 0.35, y: 6 }}
animate={
reduce
? { opacity: 1, y: 0 }
: { opacity: [0.35, 1, 1, 0.55, 0.35], y: [6, 0, 0, 0, 6] }
}
transition={{
duration: 4.2,
repeat: Infinity,
delay: i * 0.3,
times: [0, 0.15, 0.7, 0.9, 1],
ease: "easeOut",
}}
>
<path
d={`M${b.x} ${baseY - b.h} L${b.x + 12} ${baseY - b.h + 5} L${b.x} ${baseY - b.h + 10} L${b.x - 12} ${baseY - b.h + 5} Z`}
fill="var(--brand)"
/>
<path
d={`M${b.x} ${baseY - b.h + 10} L${b.x + 12} ${baseY - b.h + 5} L${b.x + 12} ${baseY + 5} L${b.x} ${baseY + 10} Z`}
fill="rgba(217,119,6,0.7)"
/>
<path
d={`M${b.x} ${baseY - b.h + 10} L${b.x - 12} ${baseY - b.h + 5} L${b.x - 12} ${baseY + 5} L${b.x} ${baseY + 10} Z`}
fill="rgba(217,119,6,0.45)"
/>
</motion.g>
);
})}
<motion.path
d="M64 150 Q90 138 112 122 T160 84"
fill="none"
stroke="var(--brand)"
strokeWidth="1.6"
strokeDasharray="220 220"
initial={{ strokeDashoffset: 220 }}
animate={reduce ? { strokeDashoffset: 0 } : { strokeDashoffset: [220, 0, 0, -220] }}
transition={{ duration: 4.2, repeat: Infinity, times: [0, 0.45, 0.75, 1], ease: "easeInOut" }}
/>
<motion.circle
cx="160"
cy="84"
r="3"
fill="var(--brand)"
animate={reduce ? { opacity: 1 } : { opacity: [0, 1, 1, 0], scale: [0.8, 1.2, 1, 0.8] }}
transition={{ duration: 4.2, repeat: Infinity, times: [0.4, 0.5, 0.8, 0.95], ease: "easeOut" }}
/>
</svg>
);
}
/** Shared isometric floor tile — keeps the diorama on a consistent plane. */
function IsoPlatform() {
return (
<g>
<path
d="M100 138 L168 168 L100 198 L32 168 Z"
fill="rgba(217,119,6,0.05)"
stroke="rgba(217,119,6,0.18)"
strokeWidth="0.8"
/>
<path
d="M100 138 L168 168 M100 138 L32 168 M100 198 L32 168 M100 198 L168 168"
stroke="rgba(0,0,0,0.05)"
strokeWidth="0.6"
/>
{[0.25, 0.5, 0.75].map((t) => (
<g key={t} stroke="rgba(0,0,0,0.05)" strokeWidth="0.5">
<line x1={100 - 68 * t} y1={138 + 30 * t} x2={168 - 68 * t} y2={168 + 30 * t} />
<line x1={100 + 68 * t} y1={138 + 30 * t} x2={32 + 68 * t} y2={168 + 30 * t} />
</g>
))}
</g>
);
}Installation
Terminal
npx shadcn@latest add https://ui.saumyarex.xyz/r/iso-growth.json1. Install dependencies
Terminal
npm install motion2. Copy the source into your project
components/ui/iso-growth.tsx
"use client";
import { motion, useReducedMotion } from "motion/react";
/**
* Isometric illustration — ascending iso bars with a rising curve threading
* through their tops, looping to suggest compounding growth. Themeable accent
* via `brand`; animation stills under reduced-motion.
*/
export function IsoGrowth({
brand = "#FF4701",
className,
}: {
brand?: string;
className?: string;
}) {
const reduce = useReducedMotion();
const bars = [
{ x: 64, h: 18 },
{ x: 88, h: 30 },
{ x: 112, h: 46 },
{ x: 136, h: 64 },
];
return (
<svg viewBox="0 0 200 210" className={className} style={{ ["--brand" as string]: brand }}>
<IsoPlatform />
{bars.map((b, i) => {
const baseY = 168 - (b.x - 100) * 0.44;
return (
<motion.g
key={i}
initial={{ opacity: 0.35, y: 6 }}
animate={
reduce
? { opacity: 1, y: 0 }
: { opacity: [0.35, 1, 1, 0.55, 0.35], y: [6, 0, 0, 0, 6] }
}
transition={{
duration: 4.2,
repeat: Infinity,
delay: i * 0.3,
times: [0, 0.15, 0.7, 0.9, 1],
ease: "easeOut",
}}
>
<path
d={`M${b.x} ${baseY - b.h} L${b.x + 12} ${baseY - b.h + 5} L${b.x} ${baseY - b.h + 10} L${b.x - 12} ${baseY - b.h + 5} Z`}
fill="var(--brand)"
/>
<path
d={`M${b.x} ${baseY - b.h + 10} L${b.x + 12} ${baseY - b.h + 5} L${b.x + 12} ${baseY + 5} L${b.x} ${baseY + 10} Z`}
fill="rgba(217,119,6,0.7)"
/>
<path
d={`M${b.x} ${baseY - b.h + 10} L${b.x - 12} ${baseY - b.h + 5} L${b.x - 12} ${baseY + 5} L${b.x} ${baseY + 10} Z`}
fill="rgba(217,119,6,0.45)"
/>
</motion.g>
);
})}
<motion.path
d="M64 150 Q90 138 112 122 T160 84"
fill="none"
stroke="var(--brand)"
strokeWidth="1.6"
strokeDasharray="220 220"
initial={{ strokeDashoffset: 220 }}
animate={reduce ? { strokeDashoffset: 0 } : { strokeDashoffset: [220, 0, 0, -220] }}
transition={{ duration: 4.2, repeat: Infinity, times: [0, 0.45, 0.75, 1], ease: "easeInOut" }}
/>
<motion.circle
cx="160"
cy="84"
r="3"
fill="var(--brand)"
animate={reduce ? { opacity: 1 } : { opacity: [0, 1, 1, 0], scale: [0.8, 1.2, 1, 0.8] }}
transition={{ duration: 4.2, repeat: Infinity, times: [0.4, 0.5, 0.8, 0.95], ease: "easeOut" }}
/>
</svg>
);
}
/** Shared isometric floor tile — keeps the diorama on a consistent plane. */
function IsoPlatform() {
return (
<g>
<path
d="M100 138 L168 168 L100 198 L32 168 Z"
fill="rgba(217,119,6,0.05)"
stroke="rgba(217,119,6,0.18)"
strokeWidth="0.8"
/>
<path
d="M100 138 L168 168 M100 138 L32 168 M100 198 L32 168 M100 198 L168 168"
stroke="rgba(0,0,0,0.05)"
strokeWidth="0.6"
/>
{[0.25, 0.5, 0.75].map((t) => (
<g key={t} stroke="rgba(0,0,0,0.05)" strokeWidth="0.5">
<line x1={100 - 68 * t} y1={138 + 30 * t} x2={168 - 68 * t} y2={168 + 30 * t} />
<line x1={100 + 68 * t} y1={138 + 30 * t} x2={32 + 68 * t} y2={168 + 30 * t} />
</g>
))}
</g>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
brand | string | "#FF4701" | Accent color; drives `--brand` inside the SVG. |
className | string | — | Classes for the <svg> (size it here). |
Dependencies
motion