<!--
    A slider with track fill, configurable tick marks and min/max labels

    Design by Outcrowd https://dribbble.com/shots/9659984-Mobile-App-Mail-Service
-->
<div id="wrapper">
    <div id="sliderContainer">
        <div class="tick-slider">
            <div class="tick-slider-header">
                <h5><label for="weightSlider">Total weight</label></h5>
                <h5>kg</h5>
            </div>
            <div class="tick-slider-value-container">
                <div id="weightLabelMin" class="tick-slider-label">0</div>
                <div id="weightLabelMax" class="tick-slider-label">40</div>
                <div id="weightValue" class="tick-slider-value"></div>
            </div>
            <div class="tick-slider-background"></div>
            <div id="weightProgress" class="tick-slider-progress"></div>
            <div id="weightTicks" class="tick-slider-tick-container"></div>
            <input
                id="weightSlider"
                class="tick-slider-input"
                type="range"
                min="0"
                max="40"
                step="5"
                value="9"
                data-tick-step="5"
                data-tick-id="weightTicks"
                data-value-id="weightValue"
                data-progress-id="weightProgress"
                data-handle-size="18"
                data-min-label-id="weightLabelMin"
                data-max-label-id="weightLabelMax"
            />
        </div>
        <div class="tick-slider">
            <div class="tick-slider-header">
                <h5><label for="sizeSlider">Total size</label></h5>
                <h5>cm</h5>
            </div>
            <div class="tick-slider-value-container">
                <div id="sizeLabelMin" class="tick-slider-label">0</div>
                <div id="sizeLabelMax" class="tick-slider-label">300</div>
                <div id="sizeValue" class="tick-slider-value"></div>
            </div>
            <div class="tick-slider-background"></div>
            <div id="sizeProgress" class="tick-slider-progress"></div>
            <div id="sizeTicks" class="tick-slider-tick-container"></div>
            <input
                id="sizeSlider"
                class="tick-slider-input"
                type="range"
                min="0"
                max="300"
                step="25"
                value="150"
                data-tick-step="25"
                data-tick-id="sizeTicks"
                data-value-id="sizeValue"
                data-progress-id="sizeProgress"
                data-handle-size="18"
                data-min-label-id="sizeLabelMin"
                data-max-label-id="sizeLabelMax"
            />
        </div>
    </div>
</div>


<!-- Twitter link -->
<a class="twitter" style="position: fixed; left: 20px; bottom: 20px; opacity: 0.2;" href="https://twitter.com/park_narrative" target="_blank"><svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 72 72"><path fill="black" d="M67.812 16.141a26.246 26.246 0 0 1-7.519 2.06 13.134 13.134 0 0 0 5.756-7.244 26.127 26.127 0 0 1-8.313 3.176A13.075 13.075 0 0 0 48.182 10c-7.229 0-13.092 5.861-13.092 13.093 0 1.026.118 2.021.338 2.981-10.885-.548-20.528-5.757-26.987-13.679a13.048 13.048 0 0 0-1.771 6.581c0 4.542 2.312 8.551 5.824 10.898a13.048 13.048 0 0 1-5.93-1.638c-.002.055-.002.11-.002.162 0 6.345 4.513 11.638 10.504 12.84a13.177 13.177 0 0 1-3.449.457c-.846 0-1.667-.078-2.465-.231 1.667 5.2 6.499 8.986 12.23 9.09a26.276 26.276 0 0 1-16.26 5.606A26.21 26.21 0 0 1 4 55.976a37.036 37.036 0 0 0 20.067 5.882c24.083 0 37.251-19.949 37.251-37.249 0-.566-.014-1.134-.039-1.694a26.597 26.597 0 0 0 6.533-6.774z"></path></svg></a>
@import url("https://fonts.googleapis.com/css?family=Hind+Madurai:300,600|Poppins:300&display=swap");

:root {
    --yellow: #ffd049;
    --light-yellow: #fdf2d2;
    --orange: #ffa929;
    --light-gray: #e3e4e8;
    --gray: #71738b;
    --light-blue: #7a7c93;
    --blue: #34385a;

    --slider-handle-size: 14px;
    --slider-handle-border-radius: 2px;
    --slider-handle-margin-top: -4px;
    --slider-track-height: 6px;
    --slider-track-border-radius: 4px;
}

* {
    box-sizing: border-box;
}

body {
    margin: 0 auto;
}

#wrapper {
    position: absolute;
    width: 100%;
    height: 100%;

    display: flex;
    justify-content: center;
    align-items: center;
}

#sliderContainer {
    width: 100%;
    max-width: 440px;

    padding: 56px 40px;

    border-radius: 40px;

    box-shadow: 0px 8px 40px rgba(128, 128, 128, 0.15);
}

#sliderContainer>div:first-child {
    margin-bottom: 48px;
}

.tick-slider-header {
    display: flex;
    justify-content: space-between;
    margin-bottom: 24px;
}

.tick-slider-header>h5 {
    margin: 0;

    font-family: "Poppins", sans-serif;
    font-size: 18px;
    font-weight: 300;
    color: var(--gray);
}

.tick-slider {
    position: relative;

    width: 100%;
}

.tick-slider-value-container {
    position: relative;
    width: 100%;

    display: flex;
    justify-content: space-between;
    align-items: center;

    margin-bottom: 12px;

    font-family: "Hind Madurai", sans-serif;
    font-size: 18px;
    color: var(--gray);
}

.tick-slider-value {
    position: absolute;
    top: 0;

    font-weight: bold;

    color: var(--blue);

    border-radius: var(--slider-handle-border-radius);
}

.tick-slider-value>div {
    animation: bulge 0.3s ease-out;
}

.tick-slider-background,
.tick-slider-progress,
.tick-slider-tick-container {
    position: absolute;
    bottom: 5px;
    left: 0;

    height: var(--slider-track-height);

    pointer-events: none;

    border-radius: var(--slider-track-border-radius);

    z-index: -1;
}

.tick-slider-background {
    width: 100%;
    background-color: var(--light-gray);
}

.tick-slider-progress {
    background-color: var(--yellow);
}

.tick-slider-tick-container {
    width: 100%;

    display: flex;
    justify-content: space-between;
    align-items: center;

    padding: 0 calc(var(--slider-handle-size) / 2);
}

.tick-slider-tick {
    width: 2px;
    height: 2px;

    border-radius: 50%;

    background-color: white;
}

.tick-slider-label {
    opacity: 0.85;
    transition: opacity 0.1s ease;
}

.tick-slider-label.hidden {
    opacity: 0;
}

@keyframes bulge {
    0% {
        transform: scale(1);
    }

    25% {
        transform: scale(1.1);
    }

    100% {
        transform: scale(1);
    }
}

/*

    REMOVE SLIDER STYLE DEFAULTS

*/
input[type="range"] {
    -webkit-appearance: none;

    width: 100%;
    height: 100%;

    background: transparent;
    outline: none;

    margin: 5px 0;
}

input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;

    border: none;
}

input[type="range"]:focus {
    outline: none;
}

input[type="range"]::-moz-focus-outer {
    border: 0;
}

/*

    HANDLE

*/
input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none;

    width: var(--slider-handle-size);
    height: var(--slider-handle-size);

    background: var(--orange);

    border-radius: var(--slider-handle-border-radius);

    cursor: pointer;

    margin-top: var(--slider-handle-margin-top);
  
    -webkit-transform: scale(1);
    transform: scale(1);

    transition: transform 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}

input[type="range"]:hover::-webkit-slider-thumb,
input[type="range"]:focus::-webkit-slider-thumb {
    transform: scale(1.2);
}

input[type="range"]::-moz-range-thumb {
    -webkit-appearance: none;

    width: var(--slider-handle-size);
    height: var(--slider-handle-size);

    background: var(--orange);

    border: none;
    border-radius: var(--slider-handle-border-radius);

    cursor: pointer;

    transition: transform 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}

input[type="range"]:hover::-moz-range-thumb,
input[type="range"]:focus::-moz-range-thumb {
    transform: scale(1.2);
}

/*

    TRACK

*/

input[type="range"]::-webkit-slider-runnable-track {
    width: 100%;
    height: var(--slider-track-height);

    cursor: pointer;

    background: none;

    border-radius: var(--slider-track-border-radius);
}

input[type="range"]::-moz-range-track {
    width: 100%;
    height: var(--slider-track-height);

    cursor: pointer;

    background: none;

    border-radius: var(--slider-track-border-radius);
}

input[type="range"]:focus::-webkit-slider-runnable-track {
    background: none;
}
input[type="range"]:active::-webkit-slider-runnable-track {
    background: none;
}
function init() {
    const sliders = document.getElementsByClassName("tick-slider-input");

    for (let slider of sliders) {
        slider.oninput = onSliderInput;

        updateValue(slider);
        updateValuePosition(slider);
        updateLabels(slider);
        updateProgress(slider);

        setTicks(slider);
    }
}

function onSliderInput(event) {
    updateValue(event.target);
    updateValuePosition(event.target);
    updateLabels(event.target);
    updateProgress(event.target);
}

function updateValue(slider) {
    let value = document.getElementById(slider.dataset.valueId);

    value.innerHTML = "<div>" + slider.value + "</div>";
}

function updateValuePosition(slider) {
    let value = document.getElementById(slider.dataset.valueId);

    const percent = getSliderPercent(slider);

    const sliderWidth = slider.getBoundingClientRect().width;
    const valueWidth = value.getBoundingClientRect().width;
    const handleSize = slider.dataset.handleSize;

    let left = percent * (sliderWidth - handleSize) + handleSize / 2 - valueWidth / 2;

    left = Math.min(left, sliderWidth - valueWidth);
    left = slider.value === slider.min ? 0 : left;

    value.style.left = left + "px";
}

function updateLabels(slider) {
    const value = document.getElementById(slider.dataset.valueId);
    const minLabel = document.getElementById(slider.dataset.minLabelId);
    const maxLabel = document.getElementById(slider.dataset.maxLabelId);

    const valueRect = value.getBoundingClientRect();
    const minLabelRect = minLabel.getBoundingClientRect();
    const maxLabelRect = maxLabel.getBoundingClientRect();

    const minLabelDelta = valueRect.left - (minLabelRect.left);
    const maxLabelDelta = maxLabelRect.left - valueRect.left;

    const deltaThreshold = 32;

    if (minLabelDelta < deltaThreshold) minLabel.classList.add("hidden");
    else minLabel.classList.remove("hidden");

    if (maxLabelDelta < deltaThreshold) maxLabel.classList.add("hidden");
    else maxLabel.classList.remove("hidden");
}

function updateProgress(slider) {
    let progress = document.getElementById(slider.dataset.progressId);
    const percent = getSliderPercent(slider);

    progress.style.width = percent * 100 + "%";
}

function getSliderPercent(slider) {
    const range = slider.max - slider.min;
    const absValue = slider.value - slider.min;

    return absValue / range;
}

function setTicks(slider) {
    let container = document.getElementById(slider.dataset.tickId);
    const spacing = parseFloat(slider.dataset.tickStep);
    const sliderRange = slider.max - slider.min;
    const tickCount = sliderRange / spacing + 1; // +1 to account for 0

    for (let ii = 0; ii < tickCount; ii++) {
        let tick = document.createElement("span");

        tick.className = "tick-slider-tick";

        container.appendChild(tick);
    }
}

function onResize() {
    const sliders = document.getElementsByClassName("tick-slider-input");

    for (let slider of sliders) {
        updateValuePosition(slider);
    }
}

window.onload = init;
window.addEventListener("resize", onResize);

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.