Skip to content

Instantly share code, notes, and snippets.

@hackur
Created July 2, 2025 19:26
Show Gist options
  • Save hackur/61a2e51a74445984d2d5f6815f7c4187 to your computer and use it in GitHub Desktop.
Save hackur/61a2e51a74445984d2d5f6815f7c4187 to your computer and use it in GitHub Desktop.

Revisions

  1. hackur created this gist Jul 2, 2025.
    7 changes: 7 additions & 0 deletions animated-process-steps.markdown
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    Animated Process Steps
    ----------------------
    I wanted to add animation to a css/pug process steps component with minimal javascript. Changing classes fires the animation, the only problem I came across was when going backwards in steps the transition delays started to break up so I had to add a bit more js.

    A [Pen](https://codepen.io/webdevian/pen/MpxRBZ) by [Ian Egner](https://codepen.io/webdevian) on [CodePen](https://codepen.io).

    [License](https://codepen.io/license/pen/MpxRBZ).
    25 changes: 25 additions & 0 deletions index.pug
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,25 @@

    -
    var steps = [{
    name: 'My First Step',
    status: 'done', // done, active or null
    },
    {
    name: 'My Next Step',
    status: 'done'
    },
    {
    name: 'Another Step',
    status: 'active'
    },
    {
    name: 'My Final Step'
    }]

    .progress-steps-wrap
    - var count = steps.length || 0
    .progress-steps(data-count=count)
    if steps
    each step, i in steps
    a.step(href=step.link, class=step.status, data-index=i + 1)= step.name

    22 changes: 22 additions & 0 deletions script.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,22 @@
    $(document).on('click', 'a.step', function () {
    var $this = $(this)
    var $parent = $this.closest('.progress-steps')

    // If the bar is moving from right to left, the css transitions
    // must be in reverse order, done via parent class
    var activeIndex = $parent.find('.active').index()
    var thisIndex = $this.index()

    if (thisIndex < activeIndex) {
    $parent.addClass('reverse')
    } else {
    $parent.removeClass('reverse')
    }

    // Remove status classes of all steps
    $parent.find('a').removeClass('active done')

    // Add done to all steps before, add active to this step
    $this.prevAll().addClass('done')
    $this.addClass('active')
    })
    1 change: 1 addition & 0 deletions scripts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    242 changes: 242 additions & 0 deletions style.scss
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,242 @@
    $top-spacing: 12px;
    $horizontal-spacing: 72px;
    $edge-spacing: $horizontal-spacing / 4;
    $line-thickness: 3px;

    $default-line-colour: #bfbfbf;
    $active-line-colour: #4d7298;

    $default-text-colour: #666;
    $active-text-colour: #0a0a0a;
    $done-text-colour: $default-line-colour;

    $icon-size: 24px;

    $line-transition: .2s;


    .progress-steps-wrap {
    width: 100%;
    margin-bottom: 18px;
    margin-top: 72px;
    text-align: center;
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    }

    .progress-steps {
    display: inline-block;
    margin-right: auto;
    margin-left: auto;

    &::before {
    display: block;
    position: relative;
    top: $line-thickness;
    left: 0;
    width: 100%;
    height: $line-thickness;
    background: $default-line-colour;
    content: '';
    }

    a {
    display: inline-block;
    position: relative;
    padding-top: $top-spacing;
    padding-right: $horizontal-spacing / 2;
    padding-left: $horizontal-spacing / 2;
    color: $default-text-colour;
    font-size: .875rem;
    font-weight: bold;
    letter-spacing: -.0125rem;
    cursor: pointer;

    &::before {
    display: block;
    position: relative;
    top: 0 - $top-spacing;
    left: 0 - ($horizontal-spacing / 2);
    width: 0;
    height: $line-thickness;
    background: $active-line-colour;
    content: '';
    z-index: 2;
    }

    &:first-child {
    padding-left: $edge-spacing;

    &::before {
    left: 0 - $edge-spacing;
    }

    &::after {
    left: calc(50% - #{$icon-size});
    }
    }

    &:last-child {
    padding-right: $edge-spacing;

    &::after {
    left: 50%;
    }
    }

    &::after {
    display: block;
    position: absolute;
    top: 0 - (($icon-size / 2 - $top-spacing) + $top-spacing);
    left: calc(50% - #{$icon-size / 2});
    width: $icon-size;
    height: $icon-size * .75;
    opacity: 0;
    z-index: 3;
    padding-top: $icon-size * .125;
    padding-bottom: $icon-size * .125;
    border-radius: 50%;
    background: $active-line-colour;
    color: #fefefe;
    font-size: .875rem;
    font-weight: bold;
    line-height: $icon-size * .75;
    text-align: center;
    content: attr(data-index);
    }

    &.active {
    color: $active-text-colour;

    &::before {
    width: calc(50% + #{$horizontal-spacing / 2});
    }

    &::after {
    opacity: 1;
    }

    &:first-child {
    &::before {
    width: calc(50% + #{$edge-spacing});
    }
    }

    &:last-child {
    &::before {
    width: calc(50% + #{$horizontal-spacing / 2});
    }
    }
    }

    &.done {
    color: $done-text-colour;

    &::before {
    width: calc(100% + #{$horizontal-spacing});
    }

    &:first-child,
    &:last-child {
    &::before {
    width: calc(100% + #{$horizontal-spacing / 2 + $edge-spacing});
    }
    }
    }
    }

    a::before {
    transition-delay: $line-transition;
    transition-duration: $line-transition;
    transition-property: width;
    transition-timing-function: linear;
    }

    .active::before {
    transition-timing-function: ease-out;
    }

    @for $i from 1 through 8 {
    &[data-count='#{$i}'] {
    @for $j from 1 through 8 {
    a:nth-child(#{$j})::before,
    &.reverse a:nth-child(#{$j})::before {
    transition-delay: $line-transition * ($i - $j);
    }
    }
    }
    }

    @for $i from 1 through 8 {
    &[data-count] {
    .done:nth-child(#{$i})::before,
    .active:nth-child(#{$i})::before {
    transition-delay: $line-transition * ($i - 1);
    }
    }
    }

    a::after{
    transition-delay: $line-transition;
    transition-duration: $line-transition / 2;
    transition-property: opacity;
    transition-timing-function: ease-in;
    }

    .active::after {
    transition-duration: $line-transition;
    transition-timing-function: ease-out;
    }

    @for $i from 1 through 8 {
    &[data-count='#{$i}'] {
    @for $j from 1 through 8 {
    a:nth-child(#{$j})::after,
    &.reverse a:nth-child(#{$j})::after {
    transition-delay: ($line-transition * ($i - $j)) + $line-transition / 2;
    }
    }
    }
    }

    @for $i from 1 through 8 {
    &[data-count] {
    .done:nth-child(#{$i})::after,
    .active:nth-child(#{$i})::after {
    transition-delay: ($line-transition * ($i - 1)) + $line-transition / 2;
    }
    }
    }


    a {
    transition-delay: $line-transition;
    transition-duration: $line-transition / 2;
    transition-property: color;
    transition-timing-function: ease-in;
    }

    .active {
    transition-duration: $line-transition;
    transition-timing-function: ease-out;
    }

    @for $i from 1 through 8 {
    &[data-count='#{$i}'] {
    @for $j from 1 through 8 {
    a:nth-child(#{$j}),
    &.reverse a:nth-child(#{$j}) {
    transition-delay: ($line-transition * ($i - $j)) + $line-transition / 2;
    }
    }
    }
    }

    @for $i from 1 through 8 {
    &[data-count] {
    .done:nth-child(#{$i}),
    .active:nth-child(#{$i}) {
    transition-delay: ($line-transition * ($i - 1)) + $line-transition / 2;
    }
    }
    }
    }