javascripthtmlcssreactjs

Logic to add vertical stepper lines


I have a created a dummy stepper application, functionality wise the app works fine . I am trying to add a progress line right below the circle and between all circles. The circles indicating the steps have three states

Similar to this progress line

  1. Green, when the step is completed
  2. Blue, when on the current step
  3. White, when its untouched.

In the similar lines, I want green line when the current step is completed and the line directed towards the next step should be blue, else case it should be grey in color.

https://codesandbox.io/p/sandbox/stepper-ppnqgc

interface StepsProps {
  currentStep: number;
  isSubmitted: boolean;
  stepsData: { title: string }[];
}

const Steps: React.FC<StepsProps> = ({
  currentStep,
  isSubmitted,
  stepsData,
}) => (
  <nav className="steps">
    <ul>
      {stepsData.map((step, index) => (
        <li
          key={index}
          className={`${
            index < currentStep ||
            (index === stepsData.length - 1 && isSubmitted)
              ? "complete"
              : ""
          } ${currentStep === index ? "active" : ""}`}
        >
          <span className="circle"></span>
          <span className="step-label">{step.title}</span>
        </li>
      ))}
    </ul>
  </nav>
);

interface BodyProps {
  currentStep: number;
  isSubmitted: boolean;
  stepsData: { title: string; content: string }[];
}

const Body: React.FC<BodyProps> = ({ currentStep, isSubmitted, stepsData }) => (
  <div className="body">
    <div className="left">
      <Steps
        currentStep={currentStep}
        isSubmitted={isSubmitted}
        stepsData={stepsData}
      />
    </div>
    <div className="right">
      <StepContent currentStep={currentStep} stepsData={stepsData} />
    </div>
  </div>
);

const StepContent = () => { return null }

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
  <Body
    currentStep={1}
    isSubmitted={false}
    stepsData={[
      { title: "Example 1", content: "Hello, world!" },
      { title: "Example 2", content: "Hello, world!" },
    ]}
  />
)
body {
  margin: 0;
  font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
  background-color: #f9f9f9;
  color: #333;
}

h1,
h2,
h3,
p {
  margin: 0;
}

.header {
  text-align: center;
  padding: 20px;
  background-color: #007bff;
  color: white;
}

.footer {
  display: flex;
  justify-content: center;
  gap: 15px;
  padding: 15px;
  background-color: #007bff;
}

.footer-button {
  padding: 10px 20px;
  font-size: 16px;
  color: white;
  background-color: #0056b3;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s ease;
}

.footer-button:disabled {
  background-color: #ccc;
  cursor: not-allowed;
}

.footer-button:hover:not(:disabled) {
  background-color: #004085;
}

.footer-button.submit {
  background-color: #28a745;
}

.footer-button.submit:hover:not(:disabled) {
  background-color: #218838;
}

.body {
  display: flex;
  flex-direction: row;
}

.left {
  width: 30%;
  padding: 20px;
  background-color: #f4f4f4;
  border-right: 1px solid #ddd;
}

.steps {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  position: relative;
}

.steps ul {
  list-style: none;
  padding: 0;
  margin: 0;
}

.steps li {
  display: flex;
  align-items: center;
  margin-bottom: 40px;
  cursor: pointer;
}

.steps li .circle {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 2px solid #ccc;
  background-color: #fff;
  position: relative;
  z-index: 1;
  transition: background-color 0.3s, border-color 0.3s;
}

.steps li.complete .circle {
  background-color: #28a745;
  border-color: #28a745;
}

.steps li.active .circle {
  background-color: #007bff;
  border-color: #007bff;
}

.steps li .step-label {
  margin-left: 15px;
  font-size: 16px;
  color: #333;
}

.right {
  flex: 1;
  padding: 20px;
}

.step-content {
  padding: 10px;
  background-color: #fff;
  border-radius: 4px;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

.step-content p {
  font-size: 16px;
  line-height: 1.6;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.production.min.js"></script>

<div id="root"></div>


Solution

  • I checked the sandbox, I think the missing part are the lines. Please use this CSS:

    .steps li:not(:last-child) .circle::after {
      content: "";
      width: 1.25px;
      height: 35px;
      position: absolute;
      top: 110%;
      left: 40%;
      background-color: grey;
    }
    
    .steps li.complete:not(:last-child) .circle::after {
      content: "";
      width: 1.5px;
      height: 35px;
      position: absolute;
      top: 110%;
      left: 40%;
      background-color: blue;
    }
    

    Hope it will be helpful. Sandbox Link