Introduction

This article covers several CSS techniques: using LESS, using display: flex and some CSS hacks.

You can download the complete source code on github.

Using LESS

LESS is CSS preprocessor which allows extending CSS with some useful features. You can learn more about it here. In this project, I use LESS variables and functions which as you can see later, allow me to work with colors in a cleaner way.

The stylesheet is attached to index.html file as usual CSS.

<link rel="stylesheet" href="stage-control.min.css"></link>
LESS file is compiled into stylesheet via:
$ lessc --clean-css stage-control.less stage-control.min.css

Making Same Width Stage Items

Consider a container having a random number of items that should have the same width and occupy all available container space.

The solution would be:

.stage-wrapper {
    display: flex;
}
.stage-item {
    flex-basis: 100%;
}
display: flex allows the container to alter items size and is commonly used for dynamic layouts. flex-basis defines the default size of an element before the remaining space is distributed. 100% makes the same width items suitable for our case.

Drawing and Arrow

Every item box is decorated with an arrow.

The idea behind drawing an arrow is zero width and zero height box, which border serves as an arrow itself. The size of the arrow is determined by the size of the border. So for example, if you want to create an arrow pointing right, you fill the left border with some color and other borders you leave transparent.

.arrow-right {
    width: 0;
    height: 0;
    border-top: 60px solid transparent;
    border-bottom: 60px solid transparent;
    border-left: 60px solid green;
}
To get arrow after the item box and some white space between two boxes, we combine two arrows: one smaller with the color of the item box and the other one quite bigger white using :before and :after pseudo elements.
&:before,
&:after {
    border: solid transparent;
    content: " ";
    left: 100%;
    position: absolute;
    top: 50%;
    z-index: 2
}

&:after {
    border-left-color: inherit;
    border-width: 12px;
    margin-top: -12px;
}

&:before {
    border-color: rgba(255, 255, 255, 0);
    border-left-color: #fff;
    border-width: 14px;
    margin-top: -14px;
}

Hovering Items

Consider our progressbar has already some items passed. Let us introduce the class to mark those items.

&.stage-active {
    background-color: @active-color;

    &:after {
        border-left-color: @active-color;
    }
}
Then how do we display intent to move to some next item and therefore mark this item and items before as passed? The idea is to mark all non-passed items that are before or hover with some lighter shade of @active-color.

As soon as in CSS, we can’t just select all items before hover, we achieve our goal in two steps: 1. Mark with lighter shade of @active-color all non-passed children of our control wrapper:

.stage-wrapper:hover div:not(.stage-active) {
    background-color: lighten(@active-color, 20%);

    &:after {
        border-left-color: lighten(@active-color, 20%);
    }
}
Please note how usage of LESS function lighten allows us not to hardcode lighter shade of active color. 2. Mark all items after hovered with the default color.
&:hover ~ div:not(.stage-active) {
    background-color: @default-color;

    &:after {
        border-left-color: @default-color;
    }
}

Hovering Items Backward

Consider a similar logic for pointing item that was already passed to return backward.

To achieve our goal, we just mark with accent of active color all items after hovered:

&:hover ~ div {
    background-color: lighten(@active-color, 20%);

    &:after {
        border-left-color: lighten(@active-color, 20%);
    }
}