JSFiddle - React, Tailwind, and code Playground

by secretgspot

HTML

<div class="inliner"></div>
<div class="inlined">
  <div class="progress-meter">
    <div class="track">
      <span class="progress"></span>
    </div>
    <ol class="progress-points" data-current="4">
      <li class="progress-point" data-point="1">
        <span class="label">Lorem ipsum</span>
      </li>
      <li class="progress-point" data-point="2">
        <span class="label">Aliquam tincidunt</span>
      </li>
      <li class="progress-point" data-point="3">
        <span class="label">Vestibulum auctor</span>
      </li>
      <li class="progress-point" data-point="4">
        <span class="label">Lorem ipsum</span></li>
      <li class="progress-point" data-point="5">
        <span class="label">Aliquam tincidunt</span>
      </li>
    </ol>
  </div>
  <div class="controls">
    <button class="trigger">Toggle progress</button>
    <p>Click any point to navigate to it directly</p>
  </div>
</div>

SCSS

$points        : 5;
$demo_w        : 90%;
$track_h       : 4px;
$track_h05     : $track_h / 2;
$gutter        : 100% / ($points - 1);
$duration      : 1s;

$_r    : 24px;
$_r05  : $_r / 2;
$_r025 : $_r05 / 2;
$_b    : $track_h;
$_mt   : ($track_h - $_r) * 0.5 - $track_h;

$tint-track      : #ddd;
$tint-progress   : red;
$tint-complete   : #777;
$tint-incomplete : lighten($tint-complete, 20%);

*, *:before, *:after {
  @include box-sizing(border-box);
}

html, body {
  height: 100%;
}

body {
  font-size: 14px;
  text-align: center;
}

.inliner {
  height: 100%;
}

.inliner,
.inliner + .inlined {
  display        : inline-block;
  vertical-align : middle;
}

.inlined {
  width: $demo_w;
}


%demo {
  margin  : 20px auto 40px;
  padding : 40px;
}

.progress-meter {
  @extend %demo;
  
  counter-reset : point;
  
  %check {
    @include transition;
    @include border-radius($_r);
    @include box-shadow(0 0 0 2px white);
    
    content          : "\2713";
    display          : block;
    width            : $_r;
    margin           : 0 auto $_r05;
    border           : $_b solid $tint-incomplete;
    text-align       : center;
    background-color : white;
    color            : white;
  }
  
  .track {
    position   : relative;
    height     : $track_h;
    background : $tint-track;
  }
  
  .progress {
    @include transition(width 1s);
    
    display    : block;
    position   : absolute;
    left       : 0;
    top        : 0;
    width      : 0;
    height     : $track_h;
    background : $tint-progress;
  }
  
  .progress-points {  
    position   : relative;
    margin     : $_mt 0 0;
    padding    : 0;
    list-style : none;
    
    @for $i from 1 through $points {
      [data-point='#{$i}'] {
        left: ($i - 1) * $gutter;
      }    
    }
  }
  
  .progress-point {
    $_w : 100px;
    $_ml: $_w / -2;
    
    @include transition(color $duration);
    
    position    : absolute;
    display     : block;
    width       : $_w;
   ...

CoffeeScript

$trigger   = $('.trigger').first()
$points    = $('.progress-points').first()
$point_arr = $('[data-point]')
$progress  = $('.progress').first()
  
# subtract 1 from values to zero index progress
val     = parseInt($points.data 'current') - 1
max     = $point_arr.length - 1
tracker = active = 0
  
# Helper method
activate = (index) ->
  if index isnt active 
    active = index
         
    $point_arr.removeClass('completed active')
    $point_arr.slice(0, index).addClass 'completed'
    $point_arr.eq(active).addClass 'active'
  
    $progress.css 'width', (index / max * 100) + "%"

# UI event handlers
$points.on 'click', 'li', (event) ->
  _index  = $point_arr.index @
  tracker = if _index is 0 then 1 else if _index is val then 0 else tracker
  activate _index

$trigger.on 'click', -> activate(if tracker++ % 2 is 0 then 0 else val)

# Start the demo
setTimeout (-> activate val), 1000