<div class="device" id="device">
  <div class="number" id="number">42</div>
  <div class="thermostat">
    <div class="bar"></div>
    <div class="glass-container">
      <div class="glass"></div>
      <div class="liquid">
        <div class="bg"></div>
        <div class="bubbles"></div>
      </div>
      <div class="glass-reflection"></div>
    </div>
    <div class="slider" id="slider"></div>
  </div>
</div>
// CSS Future Tip 🔮 https://developer.mozilla.org/en-US/docs/Web/CSS/@property
@supports (background: paint(houdini)) {
  @property --progress {
    syntax: "<percentage>";
    inherits: true;
    initial-value: 0%;
  }

  @property --x {
    syntax: "<percentage>";
    inherits: true;
    initial-value: 0%;
  }

  @property --y {
    syntax: "<percentage>";
    inherits: true;
    initial-value: 0%;
  }
}

:root {
  --c1: #00adff;
  --c2: #39d469;
  --c3: #ffeb00;
  --c4: #ff8100;
  --c5: #b50f0f;

  --surface: #fffeff;
  --on-surface: #636571;
  --outline: #000001;
  --background: #112;
  --v: 1;
}

.thermostat {
  height: 24vmin;
  width: 100%;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 1vmin;
  position: relative;
  place-items: center;
}

.liquid {
  position: absolute;
  inset: 0;
  border-radius: 10vmin;
  overflow: hidden;
  isolation: isolate; // fix for Safari's clip path + overflow issue
  
  .bg {
    position: absolute;
    inset: 0;
    
    &:before,
    &:after {
      content: "";
      position: absolute;
      inset: 0;
      --gp: calc(100% - var(--progress));
      background: linear-gradient(
        to bottom,
        var(--c5) calc(5% - var(--gp)),
        var(--c4) calc(25% - var(--gp)),
        var(--c3) calc(50% - var(--gp)),
        var(--c2) calc(75% - var(--gp)),
        var(--c1) calc(100% - var(--gp))
      );
      transform: translateY(calc(100% - var(--progress)));
      animation: wave 1s linear infinite;
      transition: all 0.3s linear;
      filter: saturate(0.65);
    }

    &:after {
      transform: scaleX(-1) translateY(calc(100% - var(--progress)));
      opacity: 0; // initial state before animation with delay
      --o: 0.6; // opacity during animation
      animation-delay: 0.55s;
    }
  }
}
// Amazing tool to generate different polygons: https://wave.novoselski.net/
@keyframes wave {
  from {
    opacity: var(--o, 1);
    clip-path: polygon(
      100% 100%,
      0% 100%,
      0% 27.83%,
      7.14% 28.61%,
      14.29% 28.98%,
      21.43% 28.9%,
      28.57% 28.4%,
      35.71% 27.49%,
      42.86% 26.22%,
      50% 24.65%,
      57.14% 22.86%,
      64.29% 20.95%,
      71.43% 19%,
      78.57% 17.12%,
      85.71% 15.4%,
      92.86% 13.92%,
      100% 12.77%
    );
  }

  25% {
    clip-path: polygon(
      100% 100%,
      0% 100%,
      0% 17.77%,
      7.14% 16.02%,
      14.29% 14%,
      21.43% 11.82%,
      28.57% 9.58%,
      35.71% 7.4%,
      42.86% 5.38%,
      50% 3.63%,
      57.14% 2.23%,
      64.29% 1.26%,
      71.43% 0.76%,
      78.57% 0.76%,
      85.71% 1.26%,
      92.86% 2.23%,
      100% 3.63%
    );
  }

  50% {
    clip-path: polygon(
      100% 100%,
      0% 100%,
      0% 2.21%,
      7.14% 1.34%,
      14.29% 0.93%,
      21.43% 1.01%,
      28.57% 1.58%,
      35.71% 2.6%,
      42.86% 4.03%,
      50% 5.8%,
      57.14% 7.81%,
      64.29% 9.97%,
      71.43% 12.16%,
      78.57% 14.28%,
      85.71% 16.22%,
      92.86% 17.88%,
      100% 19.19%
    );
  }

  to {
    opacity: var(--o, 1);
    clip-path: polygon(
      100% 100%,
      0% 100%,
      0% 31.33%,
      7.14% 32.11%,
      14.29% 32.48%,
      21.43% 32.4%,
      28.57% 31.9%,
      35.71% 30.99%,
      42.86% 29.72%,
      50% 28.15%,
      57.14% 26.36%,
      64.29% 24.45%,
      71.43% 22.5%,
      78.57% 20.62%,
      85.71% 18.9%,
      92.86% 17.42%,
      100% 16.27%
    );
  }
}

@keyframes b1 {
  from {
    --x: 50%;
    --y: 60%;
    opacity: 0;
  }

  50% {
    --x: 60%;
    --y: 45%;
    opacity: 1;
    transform: scale(1.1);
  }

  to {
    --x: 50%;
    --y: 30%;
    opacity: 0;
  }
}

@keyframes b2 {
  from {
    --x: 50%;
    --y: 80%;
    opacity: 0;
  }

  50% {
    --x: 10%;
    --y: 55%;
    opacity: 1;
    transform: scale(1.1);
  }

  to {
    --x: 60%;
    --y: 30%;
    opacity: 0;
  }
}
@supports (background: paint(houdini)) {
  .bubbles {
    position: absolute;
    inset: 0;

    &:before {
      content: "";
      position: absolute;
      inset: 0;
      --x: 50%;
      --y: 80%;
      background: radial-gradient(
            0.02vmin at var(--x) var(--y),
            hsla(0, 0, 100%, 0.8) .2vmin,
            transparent .4vmin,
            transparent
          )
          center center no-repeat,
        radial-gradient(
            0.03vmin at calc(var(--x) * 1.9) calc(var(--y) * 1.9),
            hsla(0, 0, 100%, 0.8) .2vmin,
            transparent .4vmin,
            transparent
          )
          center center no-repeat;
      mix-blend-mode: soft-light;
      animation: b2 linear 3.5s infinite;
    }

    &:after {
      content: "";
      position: absolute;
      inset: 0;
      --x: 30%;
      --y: 60%;
      background: radial-gradient(
            0.02vmin at var(--x) var(--y),
            hsla(0, 0, 100%, 0.1) 0.1vmin,
            transparent .4vmin,
            transparent
          )
          center center no-repeat,
        radial-gradient(
            0.03vmin at calc(var(--x) * 1.2) calc(var(--y) * 1.5),
            hsla(0, 0, 100%, 0.1) .1vmin,
            transparent .4vmin,
            transparent
          )
          center center no-repeat;
      mix-blend-mode: soft-light;
      animation: b1 linear 6s infinite;
    }
  }
}
.glass {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.1);
  border-radius: 10vmin;
}

.glass-container {
  height: 24vmin;
  width: 6vmin;
  border-radius: 10vmin;
  position: relative;
  border: 0.1vmin solid hsla(0, 0, 100%, 0.2);
  box-shadow: -1px 0.1vmin 0 1px hsla(0, 0, 100%, 0.5),
    1px 0.1vmin 0 1px hsla(0, 0, 100%, 0.2);

  &:before {
    border-radius: 10vmin;
    content: "";
    position: absolute;
    --size: 2vmin;
    inset: calc(var(--size) * -1);
    border-left: var(--size) solid hsla(0, 0, 100%, 0.5);
    border-right: var(--size) solid hsla(0, 0, 50%, 0.2);
    filter: blur(0.5vmin);
  }
}

.glass-reflection {
  pointer-events: none;
  mix-blend-mode: overlay;
  position: absolute;
  border-radius: 10vmin;
  overflow: hidden;
  inset: 0;
  // blurred soft lights
  &:before {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(
          to right,
          hsla(0, 0, 100%, 0.5),
          hsla(0, 0, 100%, 0.5)
        )
        no-repeat,
      linear-gradient(to right, hsla(0, 0, 100%, 0.5), hsla(0, 0, 100%, 0.1))
        no-repeat;
    background-size: 0.5vmin 19vmin, 1.8vmin 21vmin;
    background-position: 1vmin 3vmin, 3vmin 2vmin;
    filter: blur(0.4vmin);
  }

  // hard light
  &:after {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(
        4vmin 14vmin at 1vmin 20%,
        hsla(0, 0, 100%, 0.3) 10%,
        transparent 50%,
        transparent
      ),
      radial-gradient(
        2vmin 2vmin at 2vmin 5%,
        hsla(0, 0, 100%, 0.9),
        transparent,
        transparent
      ),
      radial-gradient(
        7vmin 7vmin at 2vmin 5%,
        hsla(0, 0, 100%, 0.9),
        transparent,
        transparent
      );
  }
}

.number {
  font-size: 7vmin;
  font-weight: bold;
  color: #e8eaec;
  position: relative;
  text-shadow: -0.5vmin -0.5vmin 1vmin hsla(0, 0, 100%, 0.4),
    0.5vmin 0.5vmin 0.8vmin hsla(0, 0, 0, 0.2);
  font-variant-numeric: tabular-nums;
  &:after {
    content: "°";
    position: absolute;
  }
}

.slider {
  place-self: center;
  --gp: calc(100% - var(--progress));
  --pos: 0px;
  background: linear-gradient(to bottom, #333 var(--gp), white var(--gp));
  box-shadow: -0.1vmin 0 0 0.1vmin hsla(0, 0, 0, 0.08);
  width: 0.1vmin;
  border-radius: 1vmin;
  height: 20vmin;
  position: relative;

  // click area
  &:before {
    content: "";
    position: absolute;
    inset: -1vmin -2vmin;
  }

  &:after {
    content: "";
    position: absolute;
    top: calc(10vmin * var(--gp) / 100);
    width: 3vmin;
    height: 3vmin;
    background: radial-gradient(30% 30% at 50% 50%, white, transparent),
      radial-gradient(70% 70% at 50% 50%, #f6f0f0, #bfc1cd), white;
    border-radius: 50%;
    transform: translate(-50%, var(--pos, 0));
    border-left: 0.1vmin solid white;
    box-shadow: -0.5vmin -0.5vmin 1vmin hsla(0, 0, 100%, 0.8),
      0.5vmin 0.5vmin 1vmin hsla(0, 0, 10%, 0.2);
  }
}

.device {
  background: transparent;
  width: 25svmin;
  aspect-ratio: 3/ 5.5;
  border-radius: 4vmin;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2.5vmin;
  --progress: 100%;
  transform: scale(1.5);
}

body {
  height: 100vh;
  width: 100vw;
  background: linear-gradient(135deg, #f7fbfc, #bcc7d3);
  display: grid;
  place-items: center;
  position: relative;
  font-family: "Inter", "Helvetica Neue", "Helvetica", sans-serif;

  &:after {
    content: "";
    position: absolute;
    inset: 0;
    background: url(https://assets.codepen.io/907471/noise.svg);
    mix-blend-mode: overlay;
    pointer-events: none;
  }
}

* {
  box-sizing: border-box;
}
View Compiled
const number = document.getElementById("number");
const device = document.getElementById("device");
const slider = document.getElementById("slider");
const random = (min, max) => {
  return Math.random() * (max - min) + min;
};

const interval = setInterval(() => {
  const offsetY = random(0, slider.offsetHeight - 20);
  requestAnimationFrame(() => update(offsetY));
}, 2000);

const update = (offsetY) => {
  const pos = Math.max(0, Math.min(slider.offsetHeight - 20, offsetY));
  slider.style.setProperty("--pos", `${pos}px`);
  const progress = Math.max(
    0,
    Math.min(100, 100 - (offsetY / slider.offsetHeight) * 100)
  );
  number.innerText = Math.round(progress * 0.42);
  device.style.setProperty("--progress", `${progress}%`);
};

let pointerdown = false;
slider.addEventListener("pointerdown", () => {
  pointerdown = true;
  clearInterval(interval);
});
slider.addEventListener("pointermove", ({ offsetY }) => {
  if (pointerdown) {
    update(offsetY);
  }
});
slider.addEventListener("pointerup", () => {
  pointerdown = false;
});

slider.addEventListener("pointerleave", () => {
  pointerdown = false;
  clearInterval(interval);
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://codepen.io/konstantindenerz/pen/KKxvdPq.js