timeline


component variations

Standard timeline

/
<div class="a-timeline">
  <div class="a-timeline__time">
    <label for="timeline1" class="a-timeline__current"></label>
    <span>/</span>
    <label for="timeline1" class="a-timeline__duration"></label>
  </div>
  <div class="a-timeline__range">
    <input
      tabindex="0"
      id="timeline1"
      type="range"
      min="0"
      max="325"
      aria-valuemin="0"
      aria-valuemax="325"
      aria-valuenow="0"
      aria-label="Standard timeline"
      value="0"
    />
  </div>
</div>

timeline with progress

/
<div class="a-timeline">
  <div class="a-timeline__time">
    <label for="timeline2" class="a-timeline__current"></label>
    <span>/</span>
    <label for="timeline2" class="a-timeline__duration"></label>
  </div>
  <div class="a-timeline__range">
    <input
      tabindex="0"
      id="timeline2"
      type="range"
      min="0"
      max="266"
      aria-valuemin="0"
      aria-valuemax="266"
      aria-valuenow="133"
      aria-label="timeline with progress"
      value="133"
    />
  </div>
</div>

timeline playing with progress

/
<div class="a-timeline -playing">
  <div class="a-timeline__time">
    <label for="timeline3" class="a-timeline__current"></label>
    <span>/</span>
    <label for="timeline3" class="a-timeline__duration"></label>
  </div>
  <div class="a-timeline__range">
    <input
      tabindex="0"
      id="timeline3"
      type="range"
      min="0"
      max="324"
      aria-valuemin="0"
      aria-valuemax="324"
      aria-valuenow="245"
      aria-label="timeline playing with progress"
      value="245"
    />
  </div>
</div>

timeline disabled

/
<div class="a-timeline -disabled">
  <div class="a-timeline__time">
    <label for="timeline4" class="a-timeline__current"></label>
    <span>/</span>
    <label for="timeline4" class="a-timeline__duration"></label>
  </div>
  <div class="a-timeline__range">
    <input
      tabindex="0"
      id="timeline4"
      type="range"
      min="0"
      max="324"
      disabled=""
      aria-valuemin="0"
      aria-valuemax="324"
      aria-valuenow="162"
      aria-label="timeline disabled"
      value="162"
    />
  </div>
</div>

additional content

styles SCSS

/* stylelint-disable no-descending-specificity */
/* stylelint-disable a11y/content-property-no-static-value */

@mixin thumb-base {
  border-radius: 0;
  height: 1rem;
  margin-top: -0.4375rem;
  position: relative;
  width: 0.25rem;
  z-index: 999;
}

.a-timeline {
  display: flex;

  &__time {
    align-items: center;
    display: flex;
    padding-inline-end: 1rem;

    label {
      font-size: 1rem;
    }

    span {
      padding-inline: 0.25rem;
    }
  }

  &.-playing {
    .a-timeline__current {
      color: var(--major-accent__enabled__fill__default);
    }
  }

  &.-disabled {
    .a-timeline__time {
      color: var(--major-accent__disabled__fill__default);
    }
  }

  &__range {
    --timeline-range-percentage: 50; // This initial value will be overwritten by JS.

    height: 3rem;
    width: auto;
    display: flex;
    align-items: center;
    flex: 1;

    input {
      appearance: none;
      background: transparent;
      height: 1.5rem;
      width: 100%;
      -webkit-appearance: none;
      outline: 0;
      position: relative;
      cursor: pointer;

      // Chrome
      // ::before = Styling for the part not in progress of the timeline range
      // ::after = Styling for the part in progress of the timeline range
      // ::-webkit-slider-thumb = Styling for the timeline range thumb (vertical bar)
      &::before,
      &::after {
        position: absolute;
        content: ' ';
        left: 0;
        right: 0;
        height: 0.5rem;
        top: 0.5rem; // (thumbHeight - trackHeight) / 2 --> (1.5 - 0.5) / 2
        border: 0.0625rem solid var(--plain__enabled__front__default);
      }

      // Styling for the part in progress of the timeline range
      &::after {
        background: var(--integrated__enabled__front__default);
        width: calc(var(--timeline-range-percentage) * 1%);

        .-playing & {
          background: var(--minor-accent__enabled__front__default);
        }
      }

      // Styling for the timeline range thumb (vertical bar)
      &::-webkit-slider-thumb {
        background: var(--plain__enabled__front__default);
      }

      // Firefox
      // ::-moz-range-track = Styling for the part not in progress of the timeline range
      // ::-moz-range-progress = Styling for the part in progress of the timeline range
      // ::-moz-range-thumb = Styling for the timeline range thumb (vertical bar)
      &::-moz-range-track,
      &::-moz-range-progress {
        height: 0.375rem;
        border: 0.0625rem solid var(--plain__enabled__front__default);
      }

      // Styling for the part in progress of the timeline range
      &::-moz-range-progress {
        background: var(--integrated__enabled__front__default);
        border-right: none;

        .-playing & {
          background: var(--minor-accent__enabled__front__default);
        }
      }

      // Styling for the timeline range thumb (vertical bar)
      &::-moz-range-thumb {
        background: var(--plain__enabled__front__default);
      }

      &:hover {
        // Chrome
        &::after {
          background: var(--integrated__enabled__front__hovered);

          .-playing & {
            background: var(--minor-accent__enabled__front__hovered);
          }
        }

        // Firefox
        &::-moz-range-progress {
          background: var(--integrated__enabled__front__hovered);

          .-playing & {
            background: var(--minor-accent__enabled__front__hovered);
          }
        }
      }

      &:active {
        // Chrome
        &::after {
          background: var(--integrated__enabled__front__pressed);

          .-playing & {
            background: var(--minor-accent__enabled__front__pressed);
          }
        }

        // Firefox
        &::-moz-range-progress {
          background: var(--integrated__enabled__front__pressed);

          .-playing & {
            background: var(--minor-accent__enabled__front__pressed);
          }
        }
      }

      &:disabled {
        // Chrome
        &::before,
        &::after {
          border-color: var(--plain__disabled__front__default);
        }

        &::after {
          background: var(--integrated__disabled__front__default);
        }

        &::-webkit-slider-thumb {
          background: var(--plain__disabled__front__default);
        }

        // Firefox
        &::-moz-range-progress,
        &::-moz-range-track {
          border-color: var(--plain__disabled__front__default);
        }

        &::-moz-range-progress {
          background: var(--integrated__disabled__front__default);
        }

        &::-moz-range-thumb {
          background: var(--plain__disabled__front__default);
        }
      }

      // Chrome
      &::-webkit-slider-thumb {
        @include thumb-base;

        -webkit-appearance: none;
      }

      &::-webkit-slider-runnable-track {
        height: 0.125rem;
      }

      // Firefox
      &::-moz-range-thumb {
        @include thumb-base;

        border: 0;
      }

      &:focus-visible {
        // Chrome
        &::-webkit-slider-thumb {
          @include focus-outside;
        }

        // Firefox
        &::-moz-range-thumb {
          @include focus-outside;
        }
      }
    }
  }
}