Repository Public
1<!--2 ============================================================3 SVGarden — https://www.svgarden.dev4 Author : Simon-Pierre Boucher5 Contact: contact@spboucher.ai6 File : snippets/interactive/slider-emoji-mood.html7 Desc : Range slider morphing a face from sad to happy via one custom property8 ============================================================9-->10<!--svgarden11title: Emoji mood slider12slug: slider-emoji-mood13category: interactive14tags: [interactive, slider, emoji, custom-properties, js]15difficulty: advanced16techniques: ["calc()", "clamp()", custom-property-data-flow, scaleY-mouth-flip, "hsl()"]17how_it_works: >18 The script is a courier, not an animator: on input it copies the slider's19 value into one --sg-mood custom property on the root, and every visual20 response is derived from that number in CSS. The face tint is21 hsl(calc(mood × 1.2) …), sweeping the classic mood scale from cross red22 through neutral yellow to content green; the23 brows tilt through calc() wrapped in clamp() so they can't over-rotate. The24 mouth is the clever bit — one smile path scaled by25 scaleY(calc((mood − 50) / 50)), so 100 is a full smile, 0 is the same curve26 flipped into a frown, and 50 collapses it into a perfectly flat line —27 vector-effect="non-scaling-stroke" keeps that line visible, since transforms28 would otherwise squash the stroke to nothing along with the geometry.29 Transitions on transform and fill smooth every step.30customizable:31 - { var: "--sg-size", label: "Size", type: range, min: 90, max: 220, default: 140, unit: px }32 - { var: "--sg-duration", label: "Smoothness", type: range, min: 0.05, max: 0.8, step: 0.05, default: 0.25, unit: s }33created: 2026-08-1034-->35<div class="sg-slider-emoji-mood" style="--sg-size:140px; --sg-duration:0.25s; --sg-mood:50;">36 <svg viewBox="0 0 64 64" role="img" aria-label="Mood face">37 <title>Mood face</title>38 <circle class="sg-slider-emoji-mood-face" cx="32" cy="32" r="26" />39 <circle class="sg-slider-emoji-mood-pupil" cx="23" cy="27" r="2.8" />40 <circle class="sg-slider-emoji-mood-pupil" cx="41" cy="27" r="2.8" />41 <line class="sg-slider-emoji-mood-brow" x1="19" y1="19.5" x2="27" y2="19.5" />42 <line class="sg-slider-emoji-mood-brow" x1="37" y1="19.5" x2="45" y2="19.5" />43 <g class="sg-slider-emoji-mood-mouth">44 <path d="M22 41 Q 32 51 42 41" vector-effect="non-scaling-stroke" />45 </g>46 </svg>47 <input class="sg-slider-emoji-mood-slider" type="range" min="0" max="100" value="50" aria-label="Mood, 0 sad to 100 happy" />48</div>49<script>50 document.querySelectorAll('.sg-slider-emoji-mood:not([data-sg-init])').forEach(function (root) {51 root.setAttribute('data-sg-init', '');52 var slider = root.querySelector('.sg-slider-emoji-mood-slider');53 slider.addEventListener('input', function () {54 root.style.setProperty('--sg-mood', slider.value);55 });56 });57</script>58<style>59 .sg-slider-emoji-mood {60 width: var(--sg-size);61 display: flex;62 flex-direction: column;63 align-items: center;64 gap: 0.7rem;65 }66 .sg-slider-emoji-mood svg {67 display: block;68 width: 100%;69 }70 .sg-slider-emoji-mood-face {71 fill: hsl(calc(var(--sg-mood) * 1.2) 72% 62%);72 transition: fill var(--sg-duration) ease;73 }74 .sg-slider-emoji-mood-pupil {75 fill: rgba(25, 25, 35, 0.85);76 }77 .sg-slider-emoji-mood-brow {78 stroke: rgba(25, 25, 35, 0.85);79 stroke-width: 2.2;80 stroke-linecap: round;81 transform-box: fill-box;82 transform-origin: center;83 transition: transform var(--sg-duration) ease;84 transform: rotate(clamp(-14deg, calc((50 - var(--sg-mood)) * 0.28deg), 14deg));85 }86 .sg-slider-emoji-mood-brow:nth-of-type(2) {87 transform: rotate(clamp(-14deg, calc((var(--sg-mood) - 50) * 0.28deg), 14deg));88 }89 .sg-slider-emoji-mood-mouth {90 transform-box: fill-box;91 transform-origin: center;92 transition: transform var(--sg-duration) ease;93 /* +0.04 epsilon: a true scaleY(0) is a singular matrix and vanishes */94 transform: scaleY(calc((var(--sg-mood) - 50) / 50 + 0.04));95 }96 .sg-slider-emoji-mood-mouth path {97 fill: none;98 stroke: rgba(25, 25, 35, 0.85);99 stroke-width: 2.6;100 stroke-linecap: round;101 }102 .sg-slider-emoji-mood-slider {103 width: 90%;104 accent-color: hsl(calc(var(--sg-mood) * 1.2) 65% 48%);105 }106</style>107