5

I'm trying to build an horizontal timeline. There can be many events on any given day of a month.

So, when the events are more, the list items are unable to accommodate the available height(from: min-height) and a vertical scroll is appearing.

If I try removing the min-height whole content is distorted. I want the container to occupy the list with any number of items without vertical scroll.

Also, there is one more issue, when the window is small(can be seen on codepen), the horizontal scroll appears(expected and needed). But, the connector is not occupying the whole scroll width.

*,
*::before,
*::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.timeline__container {
  background: #c0ffee;
  overflow-x: auto;
  display: flex;
  position: relative;
}
.timeline__connector {
  position: absolute;
  width: 100%;
  left: 0;
  top: calc(50% - 4px);
  height: 8px;
  background: #ccc;
}
.timeline__item {
  background: gold;
  min-width: 85px;
  min-height: 200px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.timeline__item:nth-child(2n) {
  flex-direction: column-reverse;
}
.timeline__up {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}
.timeline__down {
  height: 100%;
  display: flex;
}
.timeline__month {
  -webkit-transform: rotate(-90deg);
          transform: rotate(-90deg);
  display: inline-block;
}
.timeline__month-up {
  margin-bottom: 20px;
  align-self: flex-end;
}
.timeline__month-down {
  align-self: flex-start;
  margin-top: 20px;
}
.timeline__count {
  margin: auto;
}
.timeline__count-up {
  margin-bottom: 20px;
}
.timeline__count-down {
  margin-top: 20px;
}
.timeline__item-event {
  min-width: 180px;
}
.timeline__event__list {
  height: 100%;
}
.timeline__event__list ul {
  margin-left: 20px;
}
<div class="timeline__container">
  <div class="timeline__connector"></div>
  <div class="timeline__item">
    <div class="timeline__up">
      <div class="timeline__month timeline__month-down">JAN</div>
    </div>
    <div class="timeline__down">
      <div class="timeline__count timeline__count-up">5</div>
    </div>
  </div>
  <div class="timeline__item">
    <div class="timeline__up">
      <div class="timeline__month timeline__month-up">FEB</div>
    </div>
    <div class="timeline__down">
      <div class="timeline__count timeline__count-down">15</div>
    </div>
  </div>
  <div class="timeline__item timeline__item-event">
    <div class="timeline__up">
      <div class="timeline__event__list">
        <div class="timeline__date">5th</div>
        <ul>
          <li>Lorem ipsum</li>
          <li>Lorem ipsum</li>
          <li>inventore nihil sint est.</li>
          <li>Lorem ipsum dolor sit</li>
          <li>dolor sit</li>
        </ul>
      </div>
    </div>
    <div class="timeline__down">
<!--       <div class="timeline__count timeline__count-up">5</div> -->
    </div>
  </div>
  <div class="timeline__item timeline__item-event">
    <div class="timeline__up">
      <div class="timeline__event__list">
        <div class="timeline__date">15th</div>
        <ul>
          <li>Lorem ipsum</li>
          <li>Lorem ipsum</li>
          <li>inventore nihil sint est.</li>
          <li>Lorem ipsum dolor sit</li>
          <li>dolor sit</li>
        </ul>
      </div>
    </div>
    <div class="timeline__down">
      
    </div>
  </div>
  <div class="timeline__item timeline__item-event">
    <div class="timeline__up">
      <div class="timeline__event__list">
        <div class="timeline__date">25th</div>
        <ul>
          <li>Lorem ipsum</li>
          <li>Lorem ipsum</li>
          <li>dolor sit</li>
        </ul>
      </div>
    </div>
    <div class="timeline__down">      
    </div>
  </div>
</div>

Codepen for the same

Towkir
  • 3,889
  • 2
  • 22
  • 41
Raviteja
  • 3,399
  • 23
  • 42
  • 69
  • I am looking through your code and i needed some basic clarity. What does your upper and down timeline signify? – logeekal Apr 19 '19 at 11:05
  • @logeekal I want the content to be on alternative sides of the progress bar (grey line in the center). – Raviteja Apr 19 '19 at 11:07
  • Sorry you have to be a little more detailed. I find your HTML code be a little bit messy and i needed to know what are you trying to achieve. Is it possible to quickly create a mockup in 2 rows of excel quickly? – logeekal Apr 19 '19 at 11:22
  • I don't think `flexbox` is your friend here, since you have empty `.timeline__up` or `.timeline__down` above or below one another, also you want the `.timeline__connector` to be always in the middle of them, never overflowing and crossing one another, also the list items to be scalable, that means no matter how much list items are there, they should be occupied, right ? also are you open to other implementation such as css `grid` ? might need to tweak your `DOM`s a little. let me know what you think. – Towkir Apr 19 '19 at 11:25
  • @Towkir You absolutely understood my problem. I'm open to `grid` but, I think there are some issues with safari browser. Also, I'm open to any other approach which has better control and browser compatibility. – Raviteja Apr 19 '19 at 11:28
  • @logeekal It's hard to update in excel now. – Raviteja Apr 19 '19 at 11:32
  • No problem.. Checkout codepen https://codepen.io/anon/pen/yrjoMJ I have updated css so that you timeline takeup full width. You just need to add flex:1 1 0 to all the divs in container. Let me see if i can come around other problems you are facing.. – logeekal Apr 19 '19 at 11:36
  • @Raviteja, did my answer solve your problem ? If so, consider upvoting and accepting the answer. thanks !! – Towkir Apr 20 '19 at 16:18
  • @Raviteja , Sure. Many thanks – Towkir Apr 20 '19 at 16:49

3 Answers3

6

I have come up with an idea to combine flex and grid to achieve what you are looking for.

There is a snipped added and I tried to keep the code as clean as possible, added comments on some of them so the code can talk for itself. Didn't change the DOM structure at all. But wait, look at this:

Also, there is one more issue, when the window is small(can be seen on codepen), the horizontal scroll appears(expected and needed). But, the connector is not occupying the whole scroll width.

To solve the above, I wrapped the .timeline__container into a .timeline__wrapper. removed position: relative; from .timeline__container. And added position: relative; to .timeline__wrapper.

There are enough comments on the code, so when you go through this, you should be able to understand. You can research the grid properties used here to understand actually how this was accomplished.

Also I added a little padding on the timeline up/down classes to ignore the visual conflict with the connector (or the grey line)

Feel free to add/remove list items so that the height of the timeline items increase/decrease, and you can check if this is what was expected.

Almost forgot to mention, this works fine on safari too.

Snippet finally:

* {
  /* you can ignore the pseudo elements here */
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.timeline__wrapper {
  position: relative;
  /* to fix the timeline connector */
}

.timeline__container {
  background: #c0ffee;
  overflow-x: auto;
  /* element with an overflow can't display an absolute positioned element, that's why the wrapper up there */
  display: flex;
}

.timeline__connector {
  position: absolute;
  width: 100%;
  left: 0;
  top: calc(50% - 4px);
  height: 8px;
  background: #ccc;
}

.timeline__item {
  background: gold;
  min-width: 85px;
  justify-content: center;
  display: grid;
  grid-auto-flow: row;
  grid-auto-rows: 1fr;
  /* this keeps the upper and lower portion same height */
}

.timeline__item:nth-child(2n) .timeline__down {
  /* these styles reverses the expected rows */
  grid-row-start: 2;
  grid-row-end: 1;
}

.timeline__up {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 10px;
}

.timeline__item:nth-child(2n) .timeline__up {
  align-items: flex-start;
}

.timeline__down {
  display: flex;
  align-items: flex-start;
  padding: 10px;
}

.timeline__month {
  -webkit-transform: rotate(-90deg);
  transform: rotate(-90deg);
  display: inline-block;
}

.timeline__month-up {
  margin-bottom: 20px;
  align-self: flex-end;
}

.timeline__month-down {
  align-self: flex-start;
  margin-top: 20px;
}

.timeline__count {
  margin: auto;
}

.timeline__count-up {
  margin-bottom: 20px;
}

.timeline__count-down {
  margin-top: 20px;
}

.timeline__item-event {
  min-width: 180px;
}

.timeline__event__list ul {
  margin-left: 20px;
}
<div class="timeline__wrapper">
  <div class="timeline__container">
    <div class="timeline__connector"></div>
    <div class="timeline__item">
      <div class="timeline__up">
        <div class="timeline__month timeline__month-down">JAN</div>
      </div>
      <div class="timeline__down">
        <div class="timeline__count timeline__count-up">5</div>
      </div>
    </div>
    <div class="timeline__item">
      <div class="timeline__up">
        <div class="timeline__month timeline__month-up">FEB</div>
      </div>
      <div class="timeline__down">
        <div class="timeline__count timeline__count-down">15</div>
      </div>
    </div>
    <div class="timeline__item timeline__item-event">
      <div class="timeline__up">
        <div class="timeline__event__list">
          <div class="timeline__date">5th</div>
          <ul>
            <li>Lorem ipsum</li>
            <li>Lorem ipsum</li>
            <li>inventore nihil sint est.</li>
            <li>Lorem ipsum dolor sit</li>
            <li>dolor sit</li>
          </ul>
        </div>
      </div>
      <div class="timeline__down">
        <!--       <div class="timeline__count timeline__count-up">5</div> -->
      </div>
    </div>
    <div class="timeline__item timeline__item-event">
      <div class="timeline__up">
        <div class="timeline__event__list">
          <div class="timeline__date">15th</div>
          <ul>
            <li>Lorem ipsum</li>
            <li>Lorem ipsum</li>
            <li>inventore nihil sint est.</li>
            <li>Lorem ipsum dolor sit</li>
            <li>dolor sit</li>
          </ul>
        </div>
      </div>
      <div class="timeline__down">

      </div>
    </div>
    <div class="timeline__item timeline__item-event">
      <div class="timeline__up">
        <div class="timeline__event__list">
          <div class="timeline__date">25th</div>
          <ul>
            <li>Lorem ipsum</li>
            <li>Lorem ipsum</li>
            <li>dolor sit</li>
          </ul>
        </div>
      </div>
      <div class="timeline__down">
      </div>
    </div>
    <div class="timeline__item timeline__item-event">
      <div class="timeline__up">
        <div class="timeline__event__list">
          <div class="timeline__date">25th</div>
          <ul>
            <li>Lorem ipsum</li>
            <li>Lorem ipsum</li>
            <li>dolor sit</li>
          </ul>
        </div>
      </div>
      <div class="timeline__down">
      </div>
    </div>
    <div class="timeline__item timeline__item-event">
      <div class="timeline__up">
        <div class="timeline__event__list">
          <div class="timeline__date">25th</div>
          <ul>
            <li>Lorem ipsum</li>
            <li>Lorem ipsum</li>
            <li>dolor sit</li>
          </ul>
        </div>
      </div>
      <div class="timeline__down">
      </div>
    </div>
    <div class="timeline__item timeline__item-event">
      <div class="timeline__up">
        <div class="timeline__event__list">
          <div class="timeline__date">25th</div>
          <ul>
            <li>Lorem ipsum</li>
            <li>Lorem ipsum</li>
            <li>dolor sit</li>
          </ul>
        </div>
      </div>
      <div class="timeline__down">
      </div>
    </div>
    <div class="timeline__item timeline__item-event">
      <div class="timeline__up">
        <div class="timeline__event__list">
          <div class="timeline__date">25th</div>
          <ul>
            <li>Lorem ipsum</li>
            <li>Lorem ipsum</li>
            <li>dolor sit</li>
          </ul>
        </div>
      </div>
      <div class="timeline__down">
      </div>
    </div>
    <div class="timeline__item timeline__item-event">
      <div class="timeline__up">
        <div class="timeline__event__list">
          <div class="timeline__date">25th</div>
          <ul>
            <li>Lorem ipsum</li>
            <li>Lorem ipsum</li>
            <li>dolor sit</li>
          </ul>
        </div>
      </div>
      <div class="timeline__down">
      </div>
    </div>
    <div class="timeline__item timeline__item-event">
      <div class="timeline__up">
        <div class="timeline__event__list">
          <div class="timeline__date">25th</div>
          <ul>
            <li>Lorem ipsum</li>
            <li>Lorem ipsum</li>
            <li>dolor sit</li>
          </ul>
        </div>
      </div>
      <div class="timeline__down">
      </div>
    </div>
    <div class="timeline__item timeline__item-event">
      <div class="timeline__up">
        <div class="timeline__event__list">
          <div class="timeline__date">25th</div>
          <ul>
            <li>Lorem ipsum</li>
            <li>Lorem ipsum</li>
            <li>dolor sit</li>
          </ul>
        </div>
      </div>
      <div class="timeline__down">
      </div>
    </div>
    <div class="timeline__item timeline__item-event">
      <div class="timeline__up">
        <div class="timeline__event__list">
          <div class="timeline__date">25th</div>
          <ul>
            <li>Lorem ipsum</li>
            <li>Lorem ipsum</li>
            <li>dolor sit</li>
          </ul>
        </div>
      </div>
      <div class="timeline__down">
      </div>
    </div>
  </div>
</div>

Update: Added more items to the view according to comments.

Towkir
  • 3,889
  • 2
  • 22
  • 41
0

One of the problem i found that breaks your layout is the ul. its container (which is the .timeline__up) should have its own scrollbar.

.timeline__up {
  /* your existing styles... */
  overflow: auto;
}

I hope that will help.

0

min-height doesn't factor in the height of the horizontal scroll track.

Therefore min-height + height of horizontal scroll = vertical overflow.

I'm not sure how you want to handle this, but here are two ideas:

  1. Consider moving your min-height rule from the items to the container, and setting a high enough value to clear the scroll track.

    .timeline__container {
        background: #c0ffee;
        overflow-x: auto;
        display: flex;
        position: relative;
        min-height: 250px; /* new */
    }
    

    revised demo

OR

  1. Consider adding some bottom padding to resolve the problem.

    .timeline__container {
        background: #c0ffee;
        overflow-x: auto;
        display: flex;
        position: relative;
        padding-bottom: 10px; <--- add this
    }
    

    revised demo

    However, this method causes the background color of the larger container to appear. If you can match them switch to background-color: gold in the code above.

    revised demo


In terms of the second issue, where the dividing line gets truncated at the viewport edge, that's also a tricky problem. You may need to use JavaScript for a solution.

Here's an explanation: Make background color extend into overflow area

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • This does not solve the problem, enough list item on the upper portion of the grey line crosses the line and comes below that – Towkir Apr 19 '19 at 12:26
  • I'm not saying this post solves the problem (read the third line in my answer). I attempted to explain the problems and provide guidance toward a solution. @Towkir – Michael Benjamin Apr 19 '19 at 12:31
  • *In terms of the second issue, where the dividing line gets truncated at the viewport edge, that's also a tricky problem. You may need to use JavaScript for a solution.* No, a simple wrapper on the `.timeline__container`, removing it's `position: relative` and adding `position: relative` to that wrapper would solve this. No need of JS here – Towkir Apr 19 '19 at 12:34