Miguel Moura
Miguel Moura

Reputation: 39454

Responsive CSS timeline with connecting lines

I am trying to create a responsive timeline that behaves as (https://codepen.io/anon/pen/KoGdqG):

  1. For width higher than 600px is horizontal. The width of each section is responsive;

  2. For width under than 600px becomes vertical;

So I have the following HTML:

<ul>
  <li>
    <span class="mark"></span>
    <h3>Step 1</h3>
    <p>Some text that describes text 1</p>
  </li><li>
    <span class="mark"></span>
    <h3>Step 2</h3>
    <p>Some text that describes text 2</p>
  </li><li>
    <span class="mark"></span>
    <h3>Step 3</h3>
    <p>Some text that describes text 3</p>    
  </li><li>
    <span class="mark"></span>
    <h3>Step 4</h3>
    <p>Some text that describes text 4</p>
  </li>      
</ul>

And the following CSS:

* {
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
} 

body {
  text-align: center;
}

ul {
  max-width: 1000px;  
  padding: 0;
  margin: 0 auto;
  width: 60%;
  list-style: none;
    list-style: none;
    list-style-type: none;  
}

li {
  padding: 20px;
  margin: 0;  
}

@media screen and (min-width: 600px) {  

  li {
    display: inline-block;
    width: 25%;
  }

  li span {
    margin: 0 auto;
  }  

} 

@media screen and (max-width: 599px) {  

  ul {
    text-align: left;
  }

  li h3 {
    margin-left: 40px;
  }

  li p {
    margin-left: 40px;
  }  

} 

li span {
  background-color: white;  
  border: 2px solid green;                
  display: inline-block;
  height: 24px;
  width: 24px;
  border-radius: 12px;   
}

li h3 {
  text-align: left;
}

li p {
  text-align: left;
}

PROBLEM

The problem is how to create lines that connects each circle.

The lines become vertical with the vertical version of the timeline.

How can I do this?

Upvotes: 2

Views: 2626

Answers (3)

Christian Vincenzo Traina
Christian Vincenzo Traina

Reputation: 10444

You can try adding a new element .line inside each li. The position of .line is calculated according to width (percentage) and margin (pixels):

@media screen and (min-width: 600px) {  
  .line {
    width: 100%;
    height: 0;
    border-top: solid 1px black;
    position: relative;
    display: block;
    transform: translateX(calc(50% + 20px)) translateY(-18px);
  } 

  li {
    display: inline-block;
    width: 25%;
  }

  li span {
    margin: 0 auto;
  }  

} 

@media screen and (max-width: 599px) {  
  .line {
    width: 0;
    height: 100%;
    border-left: solid 1px black;
    position: relative;
    display: table-cell;
    transform: translateX(-11px) translateY(calc(50% - 5px));
  } 

  li {
    display: table;
  }

  ul {
    text-align: left;
  }

  li h3 {
    margin-left: 40px;
  }

  li p {
    margin-left: 40px;
  }  

} 

li { display: table } and `.line { display: table-cell } is a workaround to make the inner element aware of the height of the parent element. The height is calculated dynamically according to the text content, so we must use this or flexbox.

Here is a working snippet:

* {
	-webkit-box-sizing: border-box;
	-moz-box-sizing: border-box;
	box-sizing: border-box;
}



body {
  text-align: center;
}

ul {
  max-width: 1000px;  
  padding: 0;
  margin: 0 auto;
  width: 60%;
  list-style: none;
	list-style: none;
	list-style-type: none;  
}

li {
  padding: 20px;
  margin: 0;  
}

@media screen and (min-width: 600px) {  
  .line {
  width: 100%;
  height: 0;
  border-top: solid 1px black;
  position: relative;
  display: block;
  transform: translateX(calc(50% + 20px)) translateY(-18px);
} 
  li {
    display: inline-block;
    width: 25%;
  }
  
  li span {
    margin: 0 auto;
  }  
  
} 

@media screen and (max-width: 599px) {  
  li {
    display: table;
  }
  .line {
    width: 0;
    height: 100%;
    border-left: solid 1px black;
    position: relative;
    display: table-cell;
    transform: translateX(-11px) translateY(calc(50% - 5px));
  } 
  ul {
    text-align: left;
  }
  
  li h3 {
    margin-left: 40px;
  }

  li p {
    margin-left: 40px;
  }  
  
} 

li span {
  background-color: white;  
  border: 2px solid green;                
  display: inline-block;
  height: 24px;
  width: 24px;
  border-radius: 12px;   
}

li h3 {
  text-align: left;
}

li p {
  text-align: left;
}
<ul>
  <li>
    <span class="mark"></span>
    <div class="line"></div>
    <h3>Step 1</h3>
    <p>Some text that describes text 1</p>
  </li><li>
    <span class="mark"></span>
    <div class="line"></div>
    <h3>Step 2</h3>
    <p>Some text that describes text 2</p>
  </li><li>
    <span class="mark"></span>
    <div class="line"></div>
    <h3>Step 3</h3>
    <p>Some text that describes text 3</p>    
  </li><li>
    <span class="mark"></span>
    <h3>Step 4</h3>
    <p>Some text that describes text 4</p>
  </li>      
</ul>

Upvotes: 0

Keith
Keith

Reputation: 4147

Another way to make this work is using pseudo classes as well. Add a div before the ul:

<div class="line"></div>
<ul>...</ul>

https://codepen.io/anon/pen/jzeqEM

Add these classes:

@media screen and (min-width: 600px) {  

  .line{
    width: 45%;
    margin: 0px auto;
    height: 3px;
    background: black;
    display: block;
    position: relative;
    z-index: 1;
    top: 33px;
   }  
}

@media screen and (max-width: 599px) {  

  li span::after{
    content: "";
    width: 3px;
    margin: 0px auto;
    height: 140px;
    background: black;
    display: block;
    position: relative;
    z-index: 1;
    top: 20px;
  }

  li:nth-child(4) span::after{
    content: "";   
    background: none;
  }

} 

Upvotes: 1

coreuter
coreuter

Reputation: 3572

A solution (CSS only) is to connect each circle by adding borders to the list elements depending on the viewport width and repositioning the circle:

/* vertical connections */
@media screen and (max-width: 599px) {
  li {
    border-width: 0 0 0 1px !important; /* change visible border to left */
    margin-top: 0 !important; 
  }
  li span.mark {
    left: -12px; /* half circle height */
  }
}

/* for horizontal connections */
li {
  position: relative;
  border-style: solid;
  border-width: 1px 0 0 0; /* show top border */
  margin-top: 15px;
}

li span.mark {
  position: absolute;
  top: -12px; /* half circle height */
}

Your complete CSS (I've reorganized a bit) could look like this:

* {
  -moz-box-sizing: border-box;
  -webkit-box-sizing: border-box;
  box-sizing: border-box
}

body {
  text-align: center
}

li {
  margin: 0;
  padding: 20px
}

ul {
  list-style: none;
  list-style: none;
  list-style-type: none;
  margin: 0 auto;
  max-width: 1000px;
  padding: 0;
  width: 60%
}

@media screen and (min-width: 600px) {
  li {
    display: inline-block;
    width: 25%
  }
  li span {
    margin: 0 auto
  }
}

@media screen and (max-width: 599px) {
  li {
    border-width: 0 0 0 1px !important;
    margin-left: 15px;
    margin-top: 0 !important
  }
  li h3 {
    margin-left: 40px
  }
  li p {
    margin-left: 40px
  }
  li span.mark {
    left: -12px
  }
  ul {
    text-align: left
  }
}

li {
  border-style: solid;
  border-width: 1px 0 0;
  margin-top: 15px;
  position: relative
}

li h3 {
  text-align: left
}

li p {
  text-align: left
}

li span {
  background-color: #fff;
  border: 2px solid green;
  border-radius: 12px;
  display: inline-block;
  height: 24px;
  width: 24px
}

li span.mark {
  position: absolute;
  top: -12px
}

NOTE: Tested only in codepen!

Upvotes: 1

Related Questions