<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
<div data-js="filter" data-filter-file="#svgfilters">
    <form class="app" data-elm="app">
        <div class="app__header-holder">
            <strong class="app__header">CSS Filters</strong>
            <button type="reset" class="app__button app__button--reset" data-elm="resetPreset">↺ Reset</button>
        </div>

        <div class="app__edit">
            <div class="app__preview">
                <figure class="app__img-wrapper">
                    <img src="https://fruntend.com/storage/photos/1/raimond-klavins.jpg" class="app__img" data-elm="preview">
                </figure>
                <div class="app__thumbnails" data-elm="thumbnail">
                    <button type="button" data-index="0" style="background-image:url(https://fruntend.com/storage/photos/1/raimond-klavins.jpg);"
                            class="app__img-thumb"></button>
                    <button type="button" data-index="1" style="background-image:url(https://fruntend.com/storage/photos/1/alex-perri-m2D.jpg);"
                            class="app__img-thumb"></button>
                    <button type="button" data-index="2" style="background-image:url(https://fruntend.com/storage/photos/1/sonnie-hiles.jpg);"
                            class="app__img-thumb"></button>
                    <button type="button" data-index="3" style="background-image:url(https://fruntend.com/storage/photos/1/zach-miles-Y84.jpg);"
                            class="app__img-thumb"></button>
                    <button type="button" data-index="4" style="background-image:url(https://fruntend.com/storage/photos/1/turtle-in-ocean.jpg);"
                            class="app__img-thumb"></button>
                </div>
            </div>
            <div class="app__controls">

                <label class="app__label--range"><span>grayscale</span>
                    <div class="c-rng--output c-rng__wrapper" style="--rng-thumb-w: 1.5rem;">
                        <output class="c-rng__output" style="--rng-unit: 0;">0</output>
                        <input type="range" class="c-rng" min="0" max="1" value="0" step="0.01" name="grayscale" data-elm="grayscale"
                               data-range="output" id="c36a387f-a7ec-41c1-afcd-3310296d385d" style="--rng-percent: 0%; --rng-value: 0;">
                    </div>
                </label>
                <label class="app__label--range"><span>sepia</span>
                    <div class="c-rng--output c-rng__wrapper" style="--rng-thumb-w: 1.5rem;">
                        <output class="c-rng__output" style="--rng-unit: 0;">0</output>
                        <input type="range" class="c-rng" min="0" max="1" step="0.01" value="0" name="sepia" data-elm="sepia"
                               data-range="output" id="69d04bae-2ef2-42a8-bbbb-b4e28dc5eef4" style="--rng-percent: 0%; --rng-value: 0;">
                    </div>
                </label>
                <label class="app__label--range"><span>saturate</span>
                    <div class="c-rng--output c-rng__wrapper" style="--rng-thumb-w: 1.5rem;">
                        <output class="c-rng__output" style="--rng-unit: 33.333333333333336;">1</output>
                        <input type="range" class="c-rng" min="0" max="3" value="1" step="0.1" name="saturate" data-elm="saturate"
                               data-range="output" id="9d9c6b2a-165e-43bc-84d7-1a061b62bf86"
                               style="--rng-percent: 33.333333333333336%; --rng-value: 1;"></div>
                </label>
                <label class="app__label--range"><span>brightness</span>
                    <div class="c-rng--output c-rng__wrapper" style="--rng-thumb-w: 1.5rem;">
                        <output class="c-rng__output" style="--rng-unit: 33.333333333333336;">1</output>
                        <input type="range" class="c-rng" min="0" max="3" value="1" step="0.1" name="brightness" data-elm="brightness"
                               data-range="output" id="bdc00cfb-aaa7-4c8c-8c7d-aa56a9c640a5"
                               style="--rng-percent: 33.333333333333336%; --rng-value: 1;"></div>
                </label>
                <label class="app__label--range"><span>contrast</span>
                    <div class="c-rng--output c-rng__wrapper" style="--rng-thumb-w: 1.5rem;">
                        <output class="c-rng__output" style="--rng-unit: 33.333333333333336;">1</output>
                        <input type="range" class="c-rng" min="0" max="3" value="1" step="0.1" name="contrast" data-elm="contrast"
                               data-range="output" id="c8ebc330-8cca-44a1-82bd-842077b955ed"
                               style="--rng-percent: 33.333333333333336%; --rng-value: 1;"></div>
                </label>
                <label class="app__label--range"><span>hue-rotate</span>
                    <div class="c-rng--output c-rng__wrapper" style="--rng-thumb-w: 1.5rem;">
                        <output class="c-rng__output" style="--rng-unit: 0;">0</output>
                        <input type="range" class="c-rng" min="0" max="360" value="0" name="hue-rotate" data-elm="hue-rotate"
                               data-suffix="deg" data-range="output" id="52905cd8-de3d-488d-92e0-3f0e160501e8"
                               style="--rng-percent: 0%; --rng-value: 0;"></div>
                </label>
                <label class="app__label--range"><span>invert</span>
                    <div class="c-rng--output c-rng__wrapper" style="--rng-thumb-w: 1.5rem;">
                        <output class="c-rng__output" style="--rng-unit: 0;">0</output>
                        <input type="range" class="c-rng" min="0" max="1" value="0" step="0.01" name="invert" data-elm="invert"
                               data-range="output" id="333e0f67-1b97-44d0-917c-47d4802747c3" style="--rng-percent: 0%; --rng-value: 0;">
                    </div>
                </label>

                <label class="app__label--range"><span>blur</span>
                    <div class="c-rng--output c-rng__wrapper" style="--rng-thumb-w: 1.5rem;">
                        <output class="c-rng__output" style="--rng-unit: 0;">0</output>
                        <input type="range" class="c-rng" min="0" max="10" value="0" step="0.1" name="blur" data-elm="blur" data-suffix="px"
                               data-range="output" id="8812a488-9bb8-4468-89e3-c054f53c729c" style="--rng-percent: 0%; --rng-value: 0;">
                    </div>
                </label>
                <label class="app__label--range"><span>opacity</span>
                    <div class="c-rng--output c-rng__wrapper" style="--rng-thumb-w: 1.5rem;">
                        <output class="c-rng__output" style="--rng-unit: 100;">1</output>
                        <input type="range" class="c-rng" min="0" max="1" value="1" step="0.01" name="opacity" data-elm="opacity"
                               data-range="output" id="1626ff97-15cb-40b7-8e39-a2f605470c79" style="--rng-percent: 100%; --rng-value: 1;">
                    </div>
                </label>
            </div>
        </div>
    </form>
</div>

<script src="scripts.js"></script>
</body>
</html>
.app {
    --blur: 0;
    --brightness: 1;
    --contrast: 1;
    --grayscale: 0;
    --hue-rotate: 0;
    --invert: 0;
    --opacity: 1;
    --saturate: 1;
    --sepia: 0;
}

[data-elm="preview"] {
    filter: blur(calc(var(--blur) * 1px)) brightness(var(--brightness)) contrast(var(--contrast)) grayscale(var(--grayscale)) hue-rotate(calc(var(--hue-rotate) * 1deg)) invert(var(--invert)) opacity(var(--opacity)) saturate(var(--saturate)) sepia(var(--sepia));
    max-width: 100%;
    will-change: filter;
}

.app__img-wrapper::after {
    content: '';
    display: block;
    padding-bottom: 100%;
}

.app__thumbnails {
    display: flex;

    width: 100%;

}
.app__img-thumb {
    background-size: 100%;
    border: 2px solid #FFF;
    flex: 1;
    height: 100px;
    background-position: 50% 50%;
    cursor: pointer;
    background-repeat: no-repeat;
    background-size: cover;
}

.app {
    --app-fz-large: 1.25rem;
    --app-fz-small: 0.75rem;
    --app-bdc: #DDD;
    --app-inp-brd: 1px solid #DDD;
    --app-inp-bdrs: 0.25rem;
    --app-inp-ff: monospace;
    --app-inp-fz: 1rem;
    --app-inp-h: 1.75rem;
    --app-inp-p: 0.25rem 0;
    --app-lbl-c: #606060;
    --app-lbl-m: 0.5rem 0.1rem 0 0.1rem;
    --app-m-unit: 1rem;
    --app-maw: 64rem;
    font-family: -apple-system,BlinkMacSystemFont,segoe ui,Roboto,Oxygen-Sans,Ubuntu,Cantarell,helvetica neue,sans-serif;
    margin: var(--app-m-unit) auto;
    max-width: var(--app-maw);
    padding: 0 calc(var(--app-m-unit)/2)
}

.app * {
    box-sizing: border-box
}

.app .c-rng {
    --rng-h: 0.25rem;
    --rng-m: 1rem 0;
    --rng-thumb-h: 1.5rem;
    --rng-thumb-w: 1.5rem
}

.app .c-rng__output {
    --rng-output-bgc: transparent;
    --rng-output-c: #444;
    float: right;
    left: auto;
    position: relative!important;
    text-align: right;
    top: 1em!important
}

.app .c-rng--output {
    margin-bottom: 0
}

.app__button {
    background: #ea1515;
    color: #fff;
    display: inline-flex;
    border: 1px solid #ea1515;
    border-radius: .25rem;
    margin-left: 2rem;
    padding: .35rem;
    font-size: 1rem;
    min-width: 100px;
    justify-content: center;
    cursor: pointer;
}

.app__controls {
    flex: 1 1 100%
}

.app__preview {
    display: flex;
    flex-direction: column;
    width: 100%;
}

@media screen and (min-width: 767px) {
    .app__controls {
        flex:0 1 calc(50% - (var(--app-m-unit)/2))
    }
    .app__preview {
        max-width: calc(50% - (var(--app-m-unit)/2));
    }
}

.app__edit {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between
}
.app__header-holder {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: .75rem;
}
.app__header {
    align-items: center;
    color: #222;
    display: flex;
    font-size: 2rem;
    flex-grow: 1;
    flex-shrink: 0;
}

.app__img {
    bottom: 0;
    height: 100%;
    left: 0;
    object-fit: cover;
    position: absolute;
    right: 0;
    top: 0;
    width: 100%
}

.app__img-wrapper {
    margin: 0;
    padding: 0;
    position: relative;
    flex-grow: 1;
}

.app__img-wrapper::after {
    content: '';
    display: block;
    padding-bottom: 56.25%
}

.app__label--range {
    display: block;
    position: relative
}

.app__label--range span {
    top: .5rem;
    color: gray;
    font-size: x-small;
    position: absolute
}

.c-rng {
    --rng-focus: #064374;
    --rng-lower: #6495ED;
    --rng-upper: #CFD8DC;
    --rng-percent: 0%;
    --rng-percent-upper: 80%;
    --rng-bdrs: .375rem;
    --rng-bgi: linear-gradient(to right, var(--rng-lower) var(--rng-percent), var(--rng-upper) var(--rng-percent));
    --rng-h: 0.75rem;
    --rng-m: 2rem 0;
    --rng-w: 100%;
    --rng-label-c: #333;
    --rng-label-fz: 0.75rem;
    --rng-label-off: 0.25rem;
    --rng-thumb-bdrs: 50%;
    --rng-thumb-bxsh: none;
    --rng-thumb-bxsh--focus: inset 0 0 0 0.25rem var(--rng-focus);
    --rng-thumb-bgc: #0960A5;
    --rng-thumb-h: 2rem;
    --rng-thumb-w: 2rem;
    --rng-thumb-z: 1;
    background-image: var(--rng-bgi);
    border-radius: var(--rng-bdrs);
    box-sizing: border-box;
    font-family: inherit;
    height: var(--rng-h);
    margin: var(--rng-m);
    outline: none;
    position: relative;
    width: var(--rng-w)
}

.c-rng::-moz-range-thumb {
    background-color: var(--rng-thumb-bgc);
    border-radius: var(--rng-thumb-bdrs);
    box-shadow: var(--rng-thumb-bxsh);
    color: #000;
    cursor: ew-resize;
    height: var(--rng-thumb-h);
    margin-top: calc(0px - ((var(--rng-thumb-h) - var(--rng-h))/2));
    position: relative;
    width: var(--rng-thumb-w);
    z-index: var(--rng-thumb-z)
}

.c-rng::-webkit-slider-thumb {
    background-color: var(--rng-thumb-bgc);
    border-radius: var(--rng-thumb-bdrs);
    box-shadow: var(--rng-thumb-bxsh);
    cursor: ew-resize;
    height: var(--rng-thumb-h);
    margin-top: calc(0px - ((var(--rng-thumb-h) - var(--rng-h))/2));
    position: relative;
    width: var(--rng-thumb-w);
    z-index: var(--rng-thumb-z)
}

.c-rng:focus::-webkit-slider-thumb {
    box-shadow: var(--rng-thumb-bxsh--focus)
}

.c-rng::-moz-range-track {
    background: 0 0;
    background-size: 100%;
    border-radius: var(--rng-bdrs);
    box-sizing: border-box;
    height: var(--rng-h)
}

.c-rng::-webkit-slider-runnable-track {
    background: 0 0;
    background-size: 100%;
    border-radius: var(--rng-bdrs);
    box-sizing: border-box;
    height: var(--rng-h)
}

.c-rng,.c-rng::-webkit-slider-runnable-track,.c-rng::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none
}

.c-rng::after,.c-rng::before {
    color: var(--rng-label-c);
    font-size: var(--rng-label-fz);
    line-height: 1;
    position: absolute;
    top: calc(var(--rng-h) + var(--rng-label-off));
    z-index: -1
}

.c-rng[data-range*=labels]::after {
    content: attr(max);
    right: 0
}

.c-rng[data-range*=labels]::before {
    content: attr(min)
}

.c-rng__output {
    --rng-output-bgc: #444;
    --rng-output-c: #EEE;
    --rng-output-p: 0.25rem 0 0 0;
    --rng-output-t: 0.25rem;
    --rng-output-w: 2rem;
    background-color: var(--rng-output-bgc);
    color: var(--rng-output-c);
    display: inline-block;
    font-family: sans-serif;
    font-size: .675rem;
    left: calc(1% * var(--rng-unit) - ((var(--rng-thumb-w)/100) * var(--rng-unit)));
    padding: var(--rng-output-p);
    position: relative;
    text-align: center;
    top: var(--rng-output-t);
    width: var(--rng-output-w)
}

.c-rng__output::after {
    border-left: calc(var(--rng-output-w)/2) solid transparent;
    border-right: calc(var(--rng-output-w)/2) solid transparent;
    border-top: calc(var(--rng-output-w)/3) solid var(--rng-output-bgc);
    content: "";
    height: 0;
    left: 0;
    position: absolute;
    top: 100%;
    width: 0
}

.c-rng__wrapper .c-rng {
    bottom: 1rem;
    left: 0;
    margin: 0;
    position: absolute
}

.c-rng__wrapper .c-rng__output {
    position: absolute;
    top: 0
}

.c-rng__wrapper {
    --rng-ticks-fill: #B0B0B0;
    flex: 1;
    height: 4rem;
    margin-bottom: 1rem;
    position: relative
}
const rangeSliders = document.querySelectorAll('.app__label--range');

rangeSliders.forEach(slider => {
    const input = slider.querySelector('input');
    const output = slider.querySelector('output');
    input.addEventListener('input', () => {
        const value = input.value;
        const variableName = input.getAttribute('data-elm');
        const appElement = document.querySelector('.app');
        appElement.style.setProperty(`--${variableName}`, value);

        const percentValue = ((value - input.min) / (input.max - input.min)) * 100;
        input.style.setProperty('--rng-percent', `${percentValue}%`);

        output.textContent = value;
    });
});

const resetButton = document.querySelector('.app__button--reset');

resetButton.addEventListener('click', () => {
    setTimeout(() => {
        rangeSliders.forEach(slider => {
            const input = slider.querySelector('input');
            const output = slider.querySelector('output');
            const value = input.value;

            const variableName = input.getAttribute('data-elm');

            const appElement = document.querySelector('.app');
            appElement.style.setProperty(`--${variableName}`, value);

            const percentValue = ((value - input.min) / (input.max - input.min)) * 100;
            input.style.setProperty('--rng-percent', `${percentValue}%`);

            output.textContent = value;
        })
    }, 0);
});

const imageThumbnails = document.querySelectorAll('.app__img-thumb');

imageThumbnails.forEach(thumbnail => {
    thumbnail.addEventListener('click', () => {
        const backgroundImage = thumbnail.style.backgroundImage;

        const imageIndex = thumbnail.getAttribute('data-index');
        const imageSrc = thumbnail.getAttribute('style').match(/url\((.*?)\)/)[1];

        const imagePreview = document.querySelector('.app__img');
        imagePreview.setAttribute('src', imageSrc);

    });
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.