I have created this design, feel free to use it as a reference.
Customize the style and calculations based on your requirements.
CurvedStepper
const CurvedStepper = ({ steps }) => {
return (
<div className="curved-stepper">
<svg width="100%" height="200" viewBox="0 0 400 200" className="curve">
<path
d="M10,150 Q100,50 200,150 T390,150"
stroke="#00f"
strokeWidth="2"
fill="none"
/>
{steps.map((step, index) => {
const x = 10 + index * 150;
const y = index % 2 === 0 ? 150 : 110;
return (
<g key={index} transform={`translate(${x},${y})`}>
<circle cx="0" cy="0" r="15" fill="#fff" stroke="#00f" strokeWidth="2" />
<text x="0" y="0" textAnchor="middle" dy=".3em" fill="#00f">
{index + 1}
</text>
<foreignObject x="-50" y="20" width="100" height="100">
<div className="step-content">
<h3>{step.title}</h3>
<p>{step.description}</p>
</div>
</foreignObject>
</g>
);
})}
</svg>
</div>
);
};
App
function App() {
const steps = [
{ title: 'Step 1', description: 'This is the description of step 1' },
{ title: 'Step 2', description: 'This is the description of step 2' },
{ title: 'Step 3', description: 'This is the description of step 3' },
];
return <div className="App">
<CurvedStepper steps={steps} />
</div>
}
css
.curved-stepper {
position: relative;
width: 100%;
height: 200px;
}
.curve {
position: absolute;
top: 0;
left: 0;
}
.step-content {
text-align: center;
font-size: 10px;
}
.step-content h3 {
margin: 0;
font-size: 1em;
color: #fff;
}
.step-content p {
margin: 0;
font-size: 0.8em;
color: #fff;
}