<div id="app">
<div class="container">
<div class="progress-bg"></div>
<div :style="barStyle" class="progress-bar"></div>
<div :style="ballStyle" class="progress-ball"></div>
<div v-for="(card, index) in cards"
:key="index"
class="card"
:style="getCardColor(index + 1)"
:class="getCardClass(index + 1)">
<h3>{{card.title}}</h3>
<p>{{card.description}}</p>
</div>
</div>
<div class="buttons">
<button @click="goPrev">Prev</button>
<button @click="goNext">Next</button>
<p>step: {{currentStep}}</p>
</div>
</div>
$bar-height: 60px;
$bar-color: #B52A1C;
body {
background-color: #B5AB9E;
}
.container {
position: relative;
display: grid;
overflow: hidden;
border-radius: 5px;
grid-template-columns: 1fr 1fr 1fr 1fr;
width: 700px;
box-shadow:0px 20px 26px -10px rgba(0, 0, 0, 0.5);
}
.progress-bg {
position: absolute;
top: $bar-height;
width: 100%;
height: 5px;
background-color: rgba(0,0,0,.1);
}
.progress-bar {
position: absolute;
top: $bar-height;
width: 0;
height: 5px;
background-color: $bar-color;
transition: width 0.3s;
}
.progress-ball {
position: absolute;
top: $bar-height - 3;
left: calc(35% - 6px);
border-radius: 50%;
background-color: $bar-color;
width: 12px;
height: 12px;
transition: left 0.3s, opacity 0.3s;
}
.card {
padding: 0 10px;
background-color: #FCF6EE;
color: rgba(0,0,0,.3);
font-family: Arial, Helvetica, sans-serif;
height: 200px;
transition: background-color 0.3s, color 0.3s;
p {
margin-top: 45px;
}
}
.card.done {
background-color: #C13326;
color: rgba(255, 255, 255, .6);
}
.card.active {
color: rgba(0, 0, 0, .6);
}
.buttons {
margin-top: 30px;
}
View Compiled
const hslColorRed = {
h: 5,
s: 67,
l: 45
}
const cards = [
{
title: 'Phase 01',
description: 'Project research and main component specifications.'
},
{
title: 'Phase 02',
description: 'Initial project requirements and initialization.'
},
{
title: 'Phase 03',
description: 'Design and basic bootstrap for all elements.'
},
{
title: 'Phase 04',
description: 'Final testing on various devices and platforms.'
}
]
new Vue({
el: '#app',
data: {
currentStep: 0,
cards: cards
},
computed: {
totalSteps () {
return this.cards.length
},
barWidth () {
const step = (100 / this.totalSteps)
const stepHalf = step / 2
// set width to match the center of the card
const target = (step * this.currentStep) - stepHalf
// keep width between 0 - 100
const width = Math.max(Math.min(target, 100), 0)
return `${width}%`
},
barStyle () {
return {
width: this.barWidth
}
},
ballStyle () {
const isVisible = this.currentStep && !this.allStepsDone
const opacity = isVisible ? 1 : 0
return {
left: `calc(${this.barWidth} - 6px)`,
opacity: opacity,
}
},
allStepsDone() {
return this.currentStep>this.totalSteps
}
},
methods: {
getCardClass(cardIndex) {
if(cardIndex===this.currentStep) {
return 'active'
}
if(cardIndex<this.currentStep) {
return 'done'
}
return ''
},
getCardColor(cardIndex) {
// step not done yet
if(cardIndex>=this.currentStep) {
return
}
const {h,s,l} = hslColorRed
if(this.allStepsDone){
return {
backgroundColor: `hsl(${h}, ${s}%, ${l}%)`
}
}
const lightnessRange = 15
const step = (lightnessRange / this.currentStep) * cardIndex
const newL = l + step
return {
backgroundColor: `hsl(${h}, ${s}%, ${newL}%)`
}
},
goPrev() {
if(this.currentStep>0){
this.currentStep--
}
},
goNext() {
if(this.currentStep<=this.totalSteps){
this.currentStep++
}
}
}
})
This Pen doesn't use any external CSS resources.