12 CSS Steppers 12 / 12
CSS Stepper With Connector Line Animation
Animated fill connector lines in both horizontal and vertical layouts — a shimmer sweep runs along the line as each step completes, with ripple ring bursts on the active node and a rose/orange/amber gradient palette.
This is a full-page demo — interact inside the frame above, or open it in the playground for the full-screen experience.
The code
<div class="stp-12">
<div class="stp-12__wrap">
<!-- Horizontal with animated fill -->
<div class="stp-12__horz">
<div class="stp-12__horz-label">Horizontal — Animated Connector Fill</div>
<div class="stp-12__h-track" id="stp-12-h">
<div class="stp-12__h-step done" data-hi="1">
<div class="stp-12__h-node">✓</div>
<span class="stp-12__h-label">Initiate</span>
</div>
<div class="stp-12__h-connector" data-ci="1"><div class="stp-12__h-connector-fill" style="width:100%"></div></div>
<div class="stp-12__h-step done" data-hi="2">
<div class="stp-12__h-node">✓</div>
<span class="stp-12__h-label">Configure</span>
</div>
<div class="stp-12__h-connector active" data-ci="2"><div class="stp-12__h-connector-fill" style="width:60%"></div></div>
<div class="stp-12__h-step active" data-hi="3">
<div class="stp-12__h-node">3</div>
<span class="stp-12__h-label">Build</span>
</div>
<div class="stp-12__h-connector" data-ci="3"><div class="stp-12__h-connector-fill" style="width:0%"></div></div>
<div class="stp-12__h-step" data-hi="4">
<div class="stp-12__h-node">4</div>
<span class="stp-12__h-label">Test</span>
</div>
<div class="stp-12__h-connector" data-ci="4"><div class="stp-12__h-connector-fill" style="width:0%"></div></div>
<div class="stp-12__h-step" data-hi="5">
<div class="stp-12__h-node">5</div>
<span class="stp-12__h-label">Deploy</span>
</div>
</div>
</div>
<!-- Vertical with animated fill lines -->
<div class="stp-12__vert" id="stp-12-v">
<div class="stp-12__v-entry done" data-vi="1">
<div class="stp-12__v-spine" style="grid-column:1;grid-row:1 / span 1">
<div class="stp-12__v-node">✓</div>
<div class="stp-12__v-line"><div class="stp-12__v-line-fill" style="height:100%"></div></div>
</div>
<div class="stp-12__v-content" style="grid-column:2;grid-row:1">
<div class="stp-12__v-step-tag">Step 01 · Completed</div>
<div class="stp-12__v-title">Project Kickoff</div>
<div class="stp-12__v-desc">Stakeholder alignment, scope definition, and milestone planning locked in.</div>
<div class="stp-12__v-meta">✓ Completed 3 Jan 2026</div>
</div>
</div>
<div class="stp-12__v-entry done" data-vi="2">
<div class="stp-12__v-spine" style="grid-column:1;grid-row:2 / span 1">
<div class="stp-12__v-node">✓</div>
<div class="stp-12__v-line"><div class="stp-12__v-line-fill" style="height:100%"></div></div>
</div>
<div class="stp-12__v-content" style="grid-column:2;grid-row:2">
<div class="stp-12__v-step-tag">Step 02 · Completed</div>
<div class="stp-12__v-title">Design Phase</div>
<div class="stp-12__v-desc">Wireframes approved, component library built, and brand tokens exported.</div>
<div class="stp-12__v-meta">✓ Completed 14 Feb 2026</div>
</div>
</div>
<div class="stp-12__v-entry active" data-vi="3">
<div class="stp-12__v-spine" style="grid-column:1;grid-row:3 / span 1">
<div class="stp-12__v-node">3</div>
<div class="stp-12__v-line"><div class="stp-12__v-line-fill" style="height:0%"></div></div>
</div>
<div class="stp-12__v-content" style="grid-column:2;grid-row:3">
<div class="stp-12__v-step-tag">Step 03 · In Progress</div>
<div class="stp-12__v-title">Development Sprint</div>
<div class="stp-12__v-desc">Feature development, API integration, and unit test coverage underway.</div>
<div class="stp-12__v-meta">● Started 1 Mar 2026</div>
</div>
</div>
<div class="stp-12__v-entry" data-vi="4">
<div class="stp-12__v-spine" style="grid-column:1;grid-row:4 / span 1">
<div class="stp-12__v-node">4</div>
<div class="stp-12__v-line"><div class="stp-12__v-line-fill" style="height:0%"></div></div>
</div>
<div class="stp-12__v-content" style="grid-column:2;grid-row:4">
<div class="stp-12__v-step-tag">Step 04</div>
<div class="stp-12__v-title">QA & Staging</div>
<div class="stp-12__v-desc">Full regression, performance audits, and stakeholder UAT on staging.</div>
<div class="stp-12__v-meta">— Scheduled Apr 2026</div>
</div>
</div>
<div class="stp-12__v-entry" data-vi="5">
<div class="stp-12__v-spine" style="grid-column:1;grid-row:5 / span 1">
<div class="stp-12__v-node">5</div>
<!-- no line after last -->
</div>
<div class="stp-12__v-content" style="grid-column:2;grid-row:5">
<div class="stp-12__v-step-tag">Step 05</div>
<div class="stp-12__v-title">Production Launch</div>
<div class="stp-12__v-desc">Canary rollout, monitoring alerts active, and public release.</div>
<div class="stp-12__v-meta">— Scheduled May 2026</div>
</div>
</div>
</div>
<!-- Nav -->
<div class="stp-12__nav">
<button class="stp-12__btn stp-12__btn--ghost" id="stp-12-back">← Previous</button>
<button class="stp-12__btn stp-12__btn--primary" id="stp-12-next">Mark Complete →</button>
</div>
</div>
</div> <div class="stp-12">
<div class="stp-12__wrap">
<!-- Horizontal with animated fill -->
<div class="stp-12__horz">
<div class="stp-12__horz-label">Horizontal — Animated Connector Fill</div>
<div class="stp-12__h-track" id="stp-12-h">
<div class="stp-12__h-step done" data-hi="1">
<div class="stp-12__h-node">✓</div>
<span class="stp-12__h-label">Initiate</span>
</div>
<div class="stp-12__h-connector" data-ci="1"><div class="stp-12__h-connector-fill" style="width:100%"></div></div>
<div class="stp-12__h-step done" data-hi="2">
<div class="stp-12__h-node">✓</div>
<span class="stp-12__h-label">Configure</span>
</div>
<div class="stp-12__h-connector active" data-ci="2"><div class="stp-12__h-connector-fill" style="width:60%"></div></div>
<div class="stp-12__h-step active" data-hi="3">
<div class="stp-12__h-node">3</div>
<span class="stp-12__h-label">Build</span>
</div>
<div class="stp-12__h-connector" data-ci="3"><div class="stp-12__h-connector-fill" style="width:0%"></div></div>
<div class="stp-12__h-step" data-hi="4">
<div class="stp-12__h-node">4</div>
<span class="stp-12__h-label">Test</span>
</div>
<div class="stp-12__h-connector" data-ci="4"><div class="stp-12__h-connector-fill" style="width:0%"></div></div>
<div class="stp-12__h-step" data-hi="5">
<div class="stp-12__h-node">5</div>
<span class="stp-12__h-label">Deploy</span>
</div>
</div>
</div>
<!-- Vertical with animated fill lines -->
<div class="stp-12__vert" id="stp-12-v">
<div class="stp-12__v-entry done" data-vi="1">
<div class="stp-12__v-spine" style="grid-column:1;grid-row:1 / span 1">
<div class="stp-12__v-node">✓</div>
<div class="stp-12__v-line"><div class="stp-12__v-line-fill" style="height:100%"></div></div>
</div>
<div class="stp-12__v-content" style="grid-column:2;grid-row:1">
<div class="stp-12__v-step-tag">Step 01 · Completed</div>
<div class="stp-12__v-title">Project Kickoff</div>
<div class="stp-12__v-desc">Stakeholder alignment, scope definition, and milestone planning locked in.</div>
<div class="stp-12__v-meta">✓ Completed 3 Jan 2026</div>
</div>
</div>
<div class="stp-12__v-entry done" data-vi="2">
<div class="stp-12__v-spine" style="grid-column:1;grid-row:2 / span 1">
<div class="stp-12__v-node">✓</div>
<div class="stp-12__v-line"><div class="stp-12__v-line-fill" style="height:100%"></div></div>
</div>
<div class="stp-12__v-content" style="grid-column:2;grid-row:2">
<div class="stp-12__v-step-tag">Step 02 · Completed</div>
<div class="stp-12__v-title">Design Phase</div>
<div class="stp-12__v-desc">Wireframes approved, component library built, and brand tokens exported.</div>
<div class="stp-12__v-meta">✓ Completed 14 Feb 2026</div>
</div>
</div>
<div class="stp-12__v-entry active" data-vi="3">
<div class="stp-12__v-spine" style="grid-column:1;grid-row:3 / span 1">
<div class="stp-12__v-node">3</div>
<div class="stp-12__v-line"><div class="stp-12__v-line-fill" style="height:0%"></div></div>
</div>
<div class="stp-12__v-content" style="grid-column:2;grid-row:3">
<div class="stp-12__v-step-tag">Step 03 · In Progress</div>
<div class="stp-12__v-title">Development Sprint</div>
<div class="stp-12__v-desc">Feature development, API integration, and unit test coverage underway.</div>
<div class="stp-12__v-meta">● Started 1 Mar 2026</div>
</div>
</div>
<div class="stp-12__v-entry" data-vi="4">
<div class="stp-12__v-spine" style="grid-column:1;grid-row:4 / span 1">
<div class="stp-12__v-node">4</div>
<div class="stp-12__v-line"><div class="stp-12__v-line-fill" style="height:0%"></div></div>
</div>
<div class="stp-12__v-content" style="grid-column:2;grid-row:4">
<div class="stp-12__v-step-tag">Step 04</div>
<div class="stp-12__v-title">QA & Staging</div>
<div class="stp-12__v-desc">Full regression, performance audits, and stakeholder UAT on staging.</div>
<div class="stp-12__v-meta">— Scheduled Apr 2026</div>
</div>
</div>
<div class="stp-12__v-entry" data-vi="5">
<div class="stp-12__v-spine" style="grid-column:1;grid-row:5 / span 1">
<div class="stp-12__v-node">5</div>
<!-- no line after last -->
</div>
<div class="stp-12__v-content" style="grid-column:2;grid-row:5">
<div class="stp-12__v-step-tag">Step 05</div>
<div class="stp-12__v-title">Production Launch</div>
<div class="stp-12__v-desc">Canary rollout, monitoring alerts active, and public release.</div>
<div class="stp-12__v-meta">— Scheduled May 2026</div>
</div>
</div>
</div>
<!-- Nav -->
<div class="stp-12__nav">
<button class="stp-12__btn stp-12__btn--ghost" id="stp-12-back">← Previous</button>
<button class="stp-12__btn stp-12__btn--primary" id="stp-12-next">Mark Complete →</button>
</div>
</div>
</div>.stp-12,.stp-12 *,.stp-12 *::before,.stp-12 *::after{box-sizing:border-box;margin:0;padding:0}
.stp-12 ::selection{background:#f43f5e;color:#fff}
.stp-12{
--bg:#fef2f2;
--white:#fff;
--rose:#f43f5e;
--orange:#fb923c;
--amber:#fbbf24;
--dark:#1c0a0a;
--mid:#7f1d1d;
--muted:#f87171;
--border:#fde8e8;
--grad:linear-gradient(90deg,#f43f5e,#fb923c,#fbbf24);
--gradv:linear-gradient(180deg,#f43f5e,#fb923c,#fbbf24);
font-family:'Segoe UI',system-ui,sans-serif;
background:var(--bg);
min-height:100vh;
display:flex;align-items:center;justify-content:center;
padding:60px 20px;
background-image:
radial-gradient(ellipse at 20% 80%,rgba(244,63,94,.06),transparent 50%),
radial-gradient(ellipse at 80% 20%,rgba(251,146,60,.06),transparent 50%);
}
.stp-12__wrap{max-width:740px;width:100%;display:flex;flex-direction:column;gap:64px}
/* ── Horizontal animated connector ── */
.stp-12__horz{width:100%}
.stp-12__horz-label{font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted);font-weight:700;text-align:center;margin-bottom:28px}
.stp-12__h-track{display:flex;align-items:center;position:relative}
.stp-12__h-step{display:flex;flex-direction:column;align-items:center;gap:12px;flex-shrink:0;position:relative;z-index:1}
.stp-12__h-connector{flex:1;height:4px;background:var(--border);border-radius:2px;position:relative;overflow:hidden;margin:0 -2px}
.stp-12__h-connector-fill{
height:100%;width:0%;border-radius:2px;
background:var(--grad);
transition:width .6s cubic-bezier(.4,0,.2,1);
position:relative;
}
/* shimmer on connector fill */
.stp-12__h-connector-fill::after{
content:'';position:absolute;inset:0;
background:linear-gradient(90deg,transparent,rgba(255,255,255,.5),transparent);
background-size:200% 100%;
animation:stp-12-shimmer 1.5s linear infinite;
opacity:0;
}
.stp-12__h-connector.active .stp-12__h-connector-fill::after{opacity:1}
@keyframes stp-12-shimmer{0%{background-position:200%}100%{background-position:-200%}}
.stp-12__h-node{
width:52px;height:52px;border-radius:50%;
display:flex;align-items:center;justify-content:center;
font-size:16px;font-weight:700;
background:var(--white);
border:3px solid var(--border);
color:#c4b0b0;
transition:all .4s cubic-bezier(.34,1.56,.64,1);
position:relative;
box-shadow:0 2px 12px rgba(244,63,94,.06);
}
/* ripple ring on active */
.stp-12__h-node::before{
content:'';position:absolute;inset:-6px;border-radius:50%;
border:2px solid var(--rose);opacity:0;
transition:all .4s;
}
.stp-12__h-step.done .stp-12__h-node{
background:var(--grad);border-color:transparent;color:#fff;
box-shadow:0 4px 20px rgba(244,63,94,.3);
}
.stp-12__h-step.active .stp-12__h-node{
border-color:var(--rose);color:var(--rose);
box-shadow:0 4px 24px rgba(244,63,94,.25);
animation:stp-12-ring 2s ease-in-out infinite;
}
.stp-12__h-step.active .stp-12__h-node::before{opacity:.4;animation:stp-12-ripple 1.5s ease-out infinite}
@keyframes stp-12-ring{0%,100%{box-shadow:0 4px 24px rgba(244,63,94,.25)}50%{box-shadow:0 4px 36px rgba(244,63,94,.45)}}
@keyframes stp-12-ripple{0%{opacity:.5;transform:scale(1)}100%{opacity:0;transform:scale(1.4)}}
.stp-12__h-label{font-size:11px;font-weight:600;color:#c4b0b0;text-align:center;text-transform:uppercase;letter-spacing:.06em;transition:color .3s;max-width:64px}
.stp-12__h-step.done .stp-12__h-label{color:var(--rose)}
.stp-12__h-step.active .stp-12__h-label{color:var(--dark)}
/* ── Vertical animated connector ── */
.stp-12__vert{display:grid;grid-template-columns:auto 1fr;gap:0 20px;align-items:stretch}
.stp-12__v-spine{display:flex;flex-direction:column;align-items:center;padding-top:6px}
.stp-12__v-node{
width:44px;height:44px;border-radius:14px;flex-shrink:0;
display:flex;align-items:center;justify-content:center;
font-size:14px;font-weight:700;
background:var(--white);border:2px solid var(--border);color:#c4b0b0;
transition:all .4s cubic-bezier(.34,1.56,.64,1);
box-shadow:0 2px 8px rgba(244,63,94,.06);
}
.stp-12__v-entry.done .stp-12__v-node{
background:var(--gradv);border-color:transparent;color:#fff;
box-shadow:0 4px 16px rgba(244,63,94,.3);
}
.stp-12__v-entry.active .stp-12__v-node{
border-color:var(--rose);color:var(--rose);
box-shadow:0 4px 20px rgba(244,63,94,.2),0 0 0 4px rgba(244,63,94,.1);
}
.stp-12__v-line{
width:4px;flex:1;min-height:24px;margin:6px 0;border-radius:2px;
background:var(--border);position:relative;overflow:hidden;
}
.stp-12__v-line-fill{
position:absolute;top:0;left:0;right:0;height:0%;
background:var(--gradv);border-radius:2px;
transition:height .6s cubic-bezier(.4,0,.2,1);
}
.stp-12__v-entry.done+.stp-12__v-gap .stp-12__v-line-fill{height:100%}
.stp-12__v-entry{display:contents}
.stp-12__v-content{padding:6px 0 28px}
.stp-12__v-entry:last-child .stp-12__v-content{padding-bottom:0}
.stp-12__v-step-tag{font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:#c4b0b0;font-weight:700;margin-bottom:4px}
.stp-12__v-entry.done .stp-12__v-step-tag,.stp-12__v-entry.active .stp-12__v-step-tag{color:var(--rose)}
.stp-12__v-title{font-size:17px;font-weight:700;color:#c4b0b0;margin-bottom:4px;transition:color .3s}
.stp-12__v-entry.done .stp-12__v-title,.stp-12__v-entry.active .stp-12__v-title{color:var(--dark)}
.stp-12__v-desc{font-size:13px;color:var(--muted);line-height:1.6}
.stp-12__v-entry.active .stp-12__v-desc{color:var(--mid)}
.stp-12__v-meta{font-size:11px;color:#c4b0b0;margin-top:6px;display:flex;align-items:center;gap:6px}
.stp-12__v-entry.done .stp-12__v-meta{color:var(--rose)}
.stp-12__v-entry.active .stp-12__v-meta{color:var(--rose)}
/* nav row */
.stp-12__nav{display:flex;justify-content:center;gap:12px}
.stp-12__btn{padding:12px 28px;border-radius:12px;border:none;font-size:14px;font-weight:700;cursor:pointer;transition:all .2s}
.stp-12__btn--ghost{background:var(--white);border:1.5px solid var(--border);color:var(--muted)}
.stp-12__btn--ghost:hover{border-color:var(--rose);color:var(--dark)}
.stp-12__btn--primary{background:var(--grad);color:#fff;box-shadow:0 4px 20px rgba(244,63,94,.3)}
.stp-12__btn--primary:hover{transform:translateY(-2px);box-shadow:0 10px 32px rgba(244,63,94,.4)}
@media (prefers-reduced-motion:reduce){
.stp-12__h-step.active .stp-12__h-node{animation:none}
.stp-12__h-step.active .stp-12__h-node::before{animation:none}
.stp-12__h-connector-fill::after{animation:none}
.stp-12__h-connector-fill,.stp-12__v-line-fill{transition:none}
} .stp-12,.stp-12 *,.stp-12 *::before,.stp-12 *::after{box-sizing:border-box;margin:0;padding:0}
.stp-12 ::selection{background:#f43f5e;color:#fff}
.stp-12{
--bg:#fef2f2;
--white:#fff;
--rose:#f43f5e;
--orange:#fb923c;
--amber:#fbbf24;
--dark:#1c0a0a;
--mid:#7f1d1d;
--muted:#f87171;
--border:#fde8e8;
--grad:linear-gradient(90deg,#f43f5e,#fb923c,#fbbf24);
--gradv:linear-gradient(180deg,#f43f5e,#fb923c,#fbbf24);
font-family:'Segoe UI',system-ui,sans-serif;
background:var(--bg);
min-height:100vh;
display:flex;align-items:center;justify-content:center;
padding:60px 20px;
background-image:
radial-gradient(ellipse at 20% 80%,rgba(244,63,94,.06),transparent 50%),
radial-gradient(ellipse at 80% 20%,rgba(251,146,60,.06),transparent 50%);
}
.stp-12__wrap{max-width:740px;width:100%;display:flex;flex-direction:column;gap:64px}
/* ── Horizontal animated connector ── */
.stp-12__horz{width:100%}
.stp-12__horz-label{font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted);font-weight:700;text-align:center;margin-bottom:28px}
.stp-12__h-track{display:flex;align-items:center;position:relative}
.stp-12__h-step{display:flex;flex-direction:column;align-items:center;gap:12px;flex-shrink:0;position:relative;z-index:1}
.stp-12__h-connector{flex:1;height:4px;background:var(--border);border-radius:2px;position:relative;overflow:hidden;margin:0 -2px}
.stp-12__h-connector-fill{
height:100%;width:0%;border-radius:2px;
background:var(--grad);
transition:width .6s cubic-bezier(.4,0,.2,1);
position:relative;
}
/* shimmer on connector fill */
.stp-12__h-connector-fill::after{
content:'';position:absolute;inset:0;
background:linear-gradient(90deg,transparent,rgba(255,255,255,.5),transparent);
background-size:200% 100%;
animation:stp-12-shimmer 1.5s linear infinite;
opacity:0;
}
.stp-12__h-connector.active .stp-12__h-connector-fill::after{opacity:1}
@keyframes stp-12-shimmer{0%{background-position:200%}100%{background-position:-200%}}
.stp-12__h-node{
width:52px;height:52px;border-radius:50%;
display:flex;align-items:center;justify-content:center;
font-size:16px;font-weight:700;
background:var(--white);
border:3px solid var(--border);
color:#c4b0b0;
transition:all .4s cubic-bezier(.34,1.56,.64,1);
position:relative;
box-shadow:0 2px 12px rgba(244,63,94,.06);
}
/* ripple ring on active */
.stp-12__h-node::before{
content:'';position:absolute;inset:-6px;border-radius:50%;
border:2px solid var(--rose);opacity:0;
transition:all .4s;
}
.stp-12__h-step.done .stp-12__h-node{
background:var(--grad);border-color:transparent;color:#fff;
box-shadow:0 4px 20px rgba(244,63,94,.3);
}
.stp-12__h-step.active .stp-12__h-node{
border-color:var(--rose);color:var(--rose);
box-shadow:0 4px 24px rgba(244,63,94,.25);
animation:stp-12-ring 2s ease-in-out infinite;
}
.stp-12__h-step.active .stp-12__h-node::before{opacity:.4;animation:stp-12-ripple 1.5s ease-out infinite}
@keyframes stp-12-ring{0%,100%{box-shadow:0 4px 24px rgba(244,63,94,.25)}50%{box-shadow:0 4px 36px rgba(244,63,94,.45)}}
@keyframes stp-12-ripple{0%{opacity:.5;transform:scale(1)}100%{opacity:0;transform:scale(1.4)}}
.stp-12__h-label{font-size:11px;font-weight:600;color:#c4b0b0;text-align:center;text-transform:uppercase;letter-spacing:.06em;transition:color .3s;max-width:64px}
.stp-12__h-step.done .stp-12__h-label{color:var(--rose)}
.stp-12__h-step.active .stp-12__h-label{color:var(--dark)}
/* ── Vertical animated connector ── */
.stp-12__vert{display:grid;grid-template-columns:auto 1fr;gap:0 20px;align-items:stretch}
.stp-12__v-spine{display:flex;flex-direction:column;align-items:center;padding-top:6px}
.stp-12__v-node{
width:44px;height:44px;border-radius:14px;flex-shrink:0;
display:flex;align-items:center;justify-content:center;
font-size:14px;font-weight:700;
background:var(--white);border:2px solid var(--border);color:#c4b0b0;
transition:all .4s cubic-bezier(.34,1.56,.64,1);
box-shadow:0 2px 8px rgba(244,63,94,.06);
}
.stp-12__v-entry.done .stp-12__v-node{
background:var(--gradv);border-color:transparent;color:#fff;
box-shadow:0 4px 16px rgba(244,63,94,.3);
}
.stp-12__v-entry.active .stp-12__v-node{
border-color:var(--rose);color:var(--rose);
box-shadow:0 4px 20px rgba(244,63,94,.2),0 0 0 4px rgba(244,63,94,.1);
}
.stp-12__v-line{
width:4px;flex:1;min-height:24px;margin:6px 0;border-radius:2px;
background:var(--border);position:relative;overflow:hidden;
}
.stp-12__v-line-fill{
position:absolute;top:0;left:0;right:0;height:0%;
background:var(--gradv);border-radius:2px;
transition:height .6s cubic-bezier(.4,0,.2,1);
}
.stp-12__v-entry.done+.stp-12__v-gap .stp-12__v-line-fill{height:100%}
.stp-12__v-entry{display:contents}
.stp-12__v-content{padding:6px 0 28px}
.stp-12__v-entry:last-child .stp-12__v-content{padding-bottom:0}
.stp-12__v-step-tag{font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:#c4b0b0;font-weight:700;margin-bottom:4px}
.stp-12__v-entry.done .stp-12__v-step-tag,.stp-12__v-entry.active .stp-12__v-step-tag{color:var(--rose)}
.stp-12__v-title{font-size:17px;font-weight:700;color:#c4b0b0;margin-bottom:4px;transition:color .3s}
.stp-12__v-entry.done .stp-12__v-title,.stp-12__v-entry.active .stp-12__v-title{color:var(--dark)}
.stp-12__v-desc{font-size:13px;color:var(--muted);line-height:1.6}
.stp-12__v-entry.active .stp-12__v-desc{color:var(--mid)}
.stp-12__v-meta{font-size:11px;color:#c4b0b0;margin-top:6px;display:flex;align-items:center;gap:6px}
.stp-12__v-entry.done .stp-12__v-meta{color:var(--rose)}
.stp-12__v-entry.active .stp-12__v-meta{color:var(--rose)}
/* nav row */
.stp-12__nav{display:flex;justify-content:center;gap:12px}
.stp-12__btn{padding:12px 28px;border-radius:12px;border:none;font-size:14px;font-weight:700;cursor:pointer;transition:all .2s}
.stp-12__btn--ghost{background:var(--white);border:1.5px solid var(--border);color:var(--muted)}
.stp-12__btn--ghost:hover{border-color:var(--rose);color:var(--dark)}
.stp-12__btn--primary{background:var(--grad);color:#fff;box-shadow:0 4px 20px rgba(244,63,94,.3)}
.stp-12__btn--primary:hover{transform:translateY(-2px);box-shadow:0 10px 32px rgba(244,63,94,.4)}
@media (prefers-reduced-motion:reduce){
.stp-12__h-step.active .stp-12__h-node{animation:none}
.stp-12__h-step.active .stp-12__h-node::before{animation:none}
.stp-12__h-connector-fill::after{animation:none}
.stp-12__h-connector-fill,.stp-12__v-line-fill{transition:none}
}(function(){
let cur=3;
const hSteps=document.querySelectorAll('.stp-12__h-step');
const hCons=document.querySelectorAll('.stp-12__h-connector');
const vEntries=document.querySelectorAll('.stp-12__v-entry');
const total=5;
const vMeta=[
'✓ Completed 3 Jan 2026','✓ Completed 14 Feb 2026',
'● Started 1 Mar 2026','— Scheduled Apr 2026','— Scheduled May 2026'
];
const vTags=['Step 01 · Completed','Step 02 · Completed','Step 03 · In Progress','Step 04','Step 05'];
function update(){
// horizontal
hSteps.forEach((s,i)=>{
s.classList.remove('done','active');
const node=s.querySelector('.stp-12__h-node');
if(i+1<cur){s.classList.add('done');node.textContent='✓';}
else if(i+1===cur){s.classList.add('active');node.textContent=cur;}
else node.textContent=i+1;
});
hCons.forEach((c,i)=>{
c.classList.remove('active');
const fill=c.querySelector('.stp-12__h-connector-fill');
if(i+1<cur){fill.style.width='100%';}
else if(i+1===cur){c.classList.add('active');fill.style.width='55%';}
else fill.style.width='0%';
});
// vertical
vEntries.forEach((e,i)=>{
e.classList.remove('done','active');
const node=e.querySelector('.stp-12__v-node');
const tag=e.querySelector('.stp-12__v-step-tag');
const meta=e.querySelector('.stp-12__v-meta');
const lineFill=e.querySelector('.stp-12__v-line-fill');
if(i+1<cur){
e.classList.add('done');node.textContent='✓';
if(tag) tag.textContent=vTags[i].replace('In Progress','Completed').replace('Step 0'+String(i+1),'Step 0'+String(i+1)+' · Completed');
if(lineFill) lineFill.style.height='100%';
} else if(i+1===cur){
e.classList.add('active');node.textContent=cur;
if(tag) tag.textContent='Step 0'+cur+' · In Progress';
if(lineFill) lineFill.style.height='0%';
} else {
node.textContent=i+1;
if(tag) tag.textContent='Step 0'+(i+1);
if(lineFill) lineFill.style.height='0%';
}
});
}
document.getElementById('stp-12-back').addEventListener('click',()=>{cur=Math.max(1,cur-1);update();});
document.getElementById('stp-12-next').addEventListener('click',()=>{cur=Math.min(total,cur+1);update();});
update();
})(); (function(){
let cur=3;
const hSteps=document.querySelectorAll('.stp-12__h-step');
const hCons=document.querySelectorAll('.stp-12__h-connector');
const vEntries=document.querySelectorAll('.stp-12__v-entry');
const total=5;
const vMeta=[
'✓ Completed 3 Jan 2026','✓ Completed 14 Feb 2026',
'● Started 1 Mar 2026','— Scheduled Apr 2026','— Scheduled May 2026'
];
const vTags=['Step 01 · Completed','Step 02 · Completed','Step 03 · In Progress','Step 04','Step 05'];
function update(){
// horizontal
hSteps.forEach((s,i)=>{
s.classList.remove('done','active');
const node=s.querySelector('.stp-12__h-node');
if(i+1<cur){s.classList.add('done');node.textContent='✓';}
else if(i+1===cur){s.classList.add('active');node.textContent=cur;}
else node.textContent=i+1;
});
hCons.forEach((c,i)=>{
c.classList.remove('active');
const fill=c.querySelector('.stp-12__h-connector-fill');
if(i+1<cur){fill.style.width='100%';}
else if(i+1===cur){c.classList.add('active');fill.style.width='55%';}
else fill.style.width='0%';
});
// vertical
vEntries.forEach((e,i)=>{
e.classList.remove('done','active');
const node=e.querySelector('.stp-12__v-node');
const tag=e.querySelector('.stp-12__v-step-tag');
const meta=e.querySelector('.stp-12__v-meta');
const lineFill=e.querySelector('.stp-12__v-line-fill');
if(i+1<cur){
e.classList.add('done');node.textContent='✓';
if(tag) tag.textContent=vTags[i].replace('In Progress','Completed').replace('Step 0'+String(i+1),'Step 0'+String(i+1)+' · Completed');
if(lineFill) lineFill.style.height='100%';
} else if(i+1===cur){
e.classList.add('active');node.textContent=cur;
if(tag) tag.textContent='Step 0'+cur+' · In Progress';
if(lineFill) lineFill.style.height='0%';
} else {
node.textContent=i+1;
if(tag) tag.textContent='Step 0'+(i+1);
if(lineFill) lineFill.style.height='0%';
}
});
}
document.getElementById('stp-12-back').addEventListener('click',()=>{cur=Math.max(1,cur-1);update();});
document.getElementById('stp-12-next').addEventListener('click',()=>{cur=Math.min(total,cur+1);update();});
update();
})();How this works
Connectors are div.stp-12__line elements between each node. Each line has a ::before pseudo that starts at width:0 (horizontal) or height:0 (vertical) and transitions to 100% when the preceding step receives .is-done. A second ::after pseudo runs a shimmer keyframe — a bright highlight sweeping from left to right — layered on top of the fill.
The active node shows a ripple ring: a ::before pseudo with border-radius:50% animates from scale(1) to scale(2.5) while fading out, creating a sonar-ping effect. JS advances the step, adds .is-done to completed steps and .is-active to the current, triggering all CSS transitions in sequence.
Customize
- Edit
--rose,--orange, and--amberat.stp-12to change the gradient palette across nodes and connector fills. - Adjust connector fill speed by editing
transition: width .6s easeon the.stp-12__line::before— slower values create a more dramatic reveal. - Disable the shimmer sweep by removing the
::afterpseudo block on.stp-12__linefor a cleaner minimal look. - Change ripple speed by editing
animation-duration: 1.2son the@keyframes stp-12-rippleblock. - Add a count-up number inside the active node by inserting a
spanand updating itstextContentin the JS update function.
Watch out for
- The shimmer
::afteron the connector usesoverflow:hiddenon the parent line — removing overflow:hidden causes the shimmer to escape the connector bounds. - Vertical and horizontal layouts share the same JS controller but use different transition properties (
widthvsheight) — both must be declared in CSS or only one layout animates. - The ripple ring uses
position:absolutewith negative offsets — the node parent must haveposition:relative; overflow:visibleor the ring is clipped.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 88+ | 14+ | 78+ | 88+ |
Uses width/height transitions and keyframe animations — no modern-only CSS features required.