<div class="rating-slider">
<div class="text">
<strong>Rate your experience</strong>
<ul>
<li>awful</li>
<li>bad</li>
<li>okay</li>
<li>good</li>
<li>great</li>
</ul>
</div>
<div class="smiley">
<svg class="eye left" viewBox="0 0 18 22">
<path d="M12.6744144,18.0128897 C17.3794842,15.6567898 19.3333811,9.83072065 17.0385652,5 C15.7595661,7.57089081 13.5517099,9.64170285 10.4149967,11.2124361 C7.27828344,12.7831694 3.80661788,13.5564215 0,13.5321925 C2.2948159,18.3629131 7.9693445,20.3689896 12.6744144,18.0128897 Z"></path>
</svg>
<svg class="eye right" viewBox="0 0 18 22">
<path d="M12.6744144,18.0128897 C17.3794842,15.6567898 19.3333811,9.83072065 17.0385652,5 C15.7595661,7.57089081 13.5517099,9.64170285 10.4149967,11.2124361 C7.27828344,12.7831694 3.80661788,13.5564215 0,13.5321925 C2.2948159,18.3629131 7.9693445,20.3689896 12.6744144,18.0128897 Z"></path>
</svg>
<svg class="mouth" viewBox="0 0 64 28">
<path d="M32,2 C41.5729357,2 58,10.8218206 58,21 C50.2396023,18.9643641 41.5729357,17.9465462 32,17.9465462 C22.4270643,17.9465462 13.7603977,18.9643641 6,21 C6,10.8218206 22.4270643,2 32,2 Z"></path>
</svg>
<svg class="teeth" viewBox="0 0 64 28">
<path d="M32,7.83261436 C41.5729357,7.83261436 52.5729357,7.05507624 63,1.5 C63,10.3056732 46.3594035,14.5 32,14.5 C17.6405965,14.5 1,10.3056732 1,1.5 C11.4270643,7.05507624 22.4270643,7.83261436 32,7.83261436 Z"></path>
</svg>
</div>
<div class="slide">
<svg viewBox="0 0 273 12" fill="currentColor"><path d="M266.934082,0.00110627889 C270.247182,-0.0357036686 272.96282,2.62025389 272.99963,5.93335351 C272.999877,5.95556942 273,5.97778653 273,6.00000382 C273,9.31330792 270.314036,11.9992716 267.000732,11.9992716 C266.978515,11.9992716 266.956298,11.9991482 266.934082,11.9989014 L2.96667078,9.06611103 C1.32291919,9.04784825 1.97412392e-14,7.71014921 0,6.06629617 L0,5.93371146 C9.7279267e-16,4.28985843 1.32291919,2.95215939 2.96667078,2.93389661 L266.934082,0.00110627889 Z M267,1.45028446 C264.514719,1.45028446 262.5,3.4872632 262.5,6.00000382 C262.5,8.51274443 264.514719,10.5497232 267,10.5497232 C269.485281,10.5497232 271.5,8.51274443 271.5,6.00000382 C271.5,3.4872632 269.485281,1.45028446 267,1.45028446 Z M210,1.95580884 C207.790861,1.95580884 206,3.7664566 206,6.00000382 C206,8.23355103 207.790861,10.0441988 210,10.0441988 C212.209139,10.0441988 214,8.23355103 214,6.00000382 C214,3.7664566 212.209139,1.95580884 210,1.95580884 Z M137,2.46133321 C135.067003,2.46133321 133.5,4.04565001 133.5,6.00000382 C133.5,7.95435763 135.067003,9.53867443 137,9.53867443 C138.932997,9.53867443 140.5,7.95435763 140.5,6.00000382 C140.5,4.04565001 138.932997,2.46133321 137,2.46133321 Z M64,2.96685758 C62.3431458,2.96685758 61,4.32484341 61,6.00000382 C61,7.67516423 62.3431458,9.03315005 64,9.03315005 C65.6568542,9.03315005 67,7.67516423 67,6.00000382 C67,4.32484341 65.6568542,2.96685758 64,2.96685758 Z M3,3.97790633 C1.8954305,3.97790633 1,4.88323021 1,6.00000382 C1,7.11677742 1.8954305,8.02210131 3,8.02210131 C4.1045695,8.02210131 5,7.11677742 5,6.00000382 C5,4.88323021 4.1045695,3.97790633 3,3.97790633 Z"></path></svg>
<span>
<svg viewBox="0 0 273 12" fill="currentColor"><path d="M266.934082,0.00110627889 C270.247182,-0.0357036686 272.96282,2.62025389 272.99963,5.93335351 C272.999877,5.95556942 273,5.97778653 273,6.00000382 C273,9.31330792 270.314036,11.9992716 267.000732,11.9992716 C266.978515,11.9992716 266.956298,11.9991482 266.934082,11.9989014 L2.96667078,9.06611103 C1.32291919,9.04784825 1.97412392e-14,7.71014921 0,6.06629617 L0,5.93371146 C9.7279267e-16,4.28985843 1.32291919,2.95215939 2.96667078,2.93389661 L266.934082,0.00110627889 Z M267,1.45028446 C264.514719,1.45028446 262.5,3.4872632 262.5,6.00000382 C262.5,8.51274443 264.514719,10.5497232 267,10.5497232 C269.485281,10.5497232 271.5,8.51274443 271.5,6.00000382 C271.5,3.4872632 269.485281,1.45028446 267,1.45028446 Z M210,1.95580884 C207.790861,1.95580884 206,3.7664566 206,6.00000382 C206,8.23355103 207.790861,10.0441988 210,10.0441988 C212.209139,10.0441988 214,8.23355103 214,6.00000382 C214,3.7664566 212.209139,1.95580884 210,1.95580884 Z M137,2.46133321 C135.067003,2.46133321 133.5,4.04565001 133.5,6.00000382 C133.5,7.95435763 135.067003,9.53867443 137,9.53867443 C138.932997,9.53867443 140.5,7.95435763 140.5,6.00000382 C140.5,4.04565001 138.932997,2.46133321 137,2.46133321 Z M64,2.96685758 C62.3431458,2.96685758 61,4.32484341 61,6.00000382 C61,7.67516423 62.3431458,9.03315005 64,9.03315005 C65.6568542,9.03315005 67,7.67516423 67,6.00000382 C67,4.32484341 65.6568542,2.96685758 64,2.96685758 Z M3,3.97790633 C1.8954305,3.97790633 1,4.88323021 1,6.00000382 C1,7.11677742 1.8954305,8.02210131 3,8.02210131 C4.1045695,8.02210131 5,7.11677742 5,6.00000382 C5,4.88323021 4.1045695,3.97790633 3,3.97790633 Z"></path></svg>
</span>
<div></div>
</div>
</div>
<svg style="position: absolute; visibility: hidden;">
<defs>
<filter id="inset-shadow">
<feOffset dx='0' dy='3' />
<feGaussianBlur stdDeviation='1' result='offset-blur' />
<feComposite operator='out' in='SourceGraphic' in2='offset-blur' result='inverse' />
<feFlood flood-color='black' flood-opacity='.5' result='color' />
<feComposite operator='in' in='color' in2='inverse' result='shadow' />
<feComposite operator='over' in='shadow' in2='SourceGraphic' />
</filter>
</defs>
</svg>
<!-- dribbble - twitter -->
<a class="dribbble" href="https://dribbble.com/aaroniker" target="_blank"><img src="https://cdn.dribbble.com/assets/dribbble-ball-mark-2bd45f09c2fb58dbbfb44766d5d1d07c5a12972d602ef8b32204d28fa3dda554.svg" alt=""></a>
<a class="twitter" target="_blank" href="https://twitter.com/aaroniker_me"><svg xmlns="http://www.w3.org/2000/svg" width="72" height="72" viewBox="0 0 72 72"><path 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>
.rating-slider {
--text: #2B3044;
--text-light: #BBC1E1;
--border-normal: #D1D6EE;
--shadow-normal: rgba(0, 9, 61, .16);
// Awful colors
--awful-fill: #FB8043;
--awful-radial: #FFCCB0;
--awful-border: #{rgba(#DE5B1A, .5)};
--awful-shadow: #{rgba(#90420E, .5)};
--awful-mouth-fill: #A34106;
--awful-mouth-shadow: #{rgba(#C45F28, .5)};
--awful-mouth-shine: #FEC6A7;
// Bad colors
--bad-fill: #FCA730;
--bad-radial: #FEE8C6;
--bad-border: #{rgba(#E07B13, .5)};
--bad-shadow: #{rgba(#BC600C, .5)};
--bad-mouth-fill: #AB6C09;
--bad-mouth-shadow: #{rgba(#945907, .3)};
--bad-mouth-shine: #FDE0B4;
// Okay colors
--okay-fill: #FBBB1F;
--okay-radial: #FEF3BD;
--okay-border: #{rgba(#E08F06, .5)};
--okay-shadow: #{rgba(#BE6D09, .5)};
--okay-mouth-fill: #AB7509;
--okay-mouth-shadow: #{rgba(#926807, .3)};
--okay-mouth-shine: #FFEECE;
// Good colors
--good-fill: #FBD51F;
--good-radial: #FEF3BD;
--good-border: #{rgba(#D5A50E, .5)};
--good-shadow: #{rgba(#BB840F, .5)};
--good-mouth-fill: #AB7509;
--good-mouth-shadow: #{rgba(#926807, .3)};
--good-mouth-shine: #FFEECE;
// Great colors
--great-fill: #F3E837;
--great-radial: #FFFFDC;
--great-border: #{rgba(#BDBD34, .5)};
--great-shadow: #{rgba(#D1CB30, .5)};
--great-mouth-fill: #A79627;
--great-mouth-shadow: #{rgba(#5D5003, .25)};
--great-mouth-shine: #FDF8B4;
display: inline-grid;
grid-template-columns: 40px auto;
align-items: center;
grid-gap: 16px 24px;
background: #fff;
border-radius: 9px;
padding: 24px;
border: 1px solid var(--border-normal);
box-shadow: 0 1px 6px -1px var(--shadow-normal);
.text {
grid-column: 1 / 3;
display: flex;
align-items: center;
justify-content: space-between;
strong {
display: block;
color: var(--text);
font-weight: 500;
font-size: 18px;
}
ul {
margin: 0;
padding: 0;
font-size: 14px;
font-weight: 500;
color: var(--text-light);
list-style: none;
position: relative;
text-align: right;
li {
transform: translateY(var(--y, 0));
opacity: var(--o, 0);
transition: opacity .2s;
&:not(:first-child) {
width: 100%;
top: 0;
position: absolute;
}
@for $i from 2 through 5 {
&:nth-child(#{$i}) {
top: 100% * ($i - 1);
}
}
}
}
}
&.awful {
.text {
ul {
li:nth-child(1) {
--o: 1;
}
}
}
}
&.bad {
.text {
ul {
li:nth-child(2) {
--o: 1;
}
}
}
}
&.okay {
.text {
ul {
li:nth-child(3) {
--o: 1;
}
}
}
}
&.good {
.text {
ul {
li:nth-child(4) {
--o: 1;
}
}
}
}
&.great {
.text {
ul {
li:nth-child(5) {
--o: 1;
}
}
}
.smiley {
svg {
&.teeth {
--teeth: 1;
}
}
}
}
&.scale {
.smiley {
animation: scale .6s ease forwards;
}
}
.slide {
grid-column: 2;
grid-row: 2;
height: 12px;
width: 264px;
position: relative;
div {
width: 24px;
height: 24px;
border-radius: 50%;
background: #fff;
top: -6px;
left: -12px;
border: 1px solid var(--border-normal);
box-shadow: 0 1px 3px var(--shadow-normal);
position: relative;
}
svg {
display: block;
width: 273px;
height: 12px;
}
span,
& > svg {
display: block;
position: absolute;
top: 0;
left: -3px;
}
span {
overflow: hidden;
width: var(--w, 0);
svg {
color: var(--fill, var(--awful-fill));
}
}
& > svg {
color: #E1E6F9;
}
}
.smiley {
grid-column: 1;
grid-row: 2;
width: 40px;
height: 40px;
border-radius: 50%;
z-index: 1;
position: relative;
background: radial-gradient(circle, var(--radial, var(--awful-radial)) 0%, var(--fill, var(--awful-fill)) 70%);
background-size: 100% 180%;
background-position: center bottom;
box-shadow: inset 0 0 0 2px var(--border, var(--awful-border)), inset 0 -4px 8px var(--shadow, var(--awful-shadow));
filter: drop-shadow(0 4px 12px rgba(#000, .08));
svg {
display: block;
position: absolute;
backface-visibility: hidden;
transform: translateZ(0);
fill: var(--mouth-fill, var(--awful-mouth-fill));
&.eye {
width: 7px;
height: 10px;
top: 10px;
filter: drop-shadow(0 .5px 1px var(--mouth-shine, var(--awful-mouth-shine))) url(#inset-shadow);
&.left {
left: 22px;
}
&.right {
right: 22px;
transform: scaleX(-1);
}
}
&.mouth,
&.teeth {
width: 26px;
height: 10px;
left: 7px;
bottom: 7px;
}
&.mouth {
filter: drop-shadow(0 .5px 1px var(--mouth-shine, var(--awful-mouth-shine))) drop-shadow(0 -.25px .25px var(--mouth-shadow, var(--awful-mouth-shadow))) url(#inset-shadow);
}
&.teeth {
fill: #fff;
opacity: var(--teeth, 0);
transition: opacity .2s;
}
}
}
}
@keyframes scale {
50% {
transform: scale(1.08) translateZ(0);
filter: drop-shadow(0 8px 24px rgba(#000, .12));
}
}
html {
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
}
* {
box-sizing: inherit;
&:before,
&:after {
box-sizing: inherit;
}
}
// Center & dribbble
body {
min-height: 100vh;
display: flex;
font-family: 'Inter UI', 'Inter', Arial;
justify-content: center;
align-items: center;
background: #ECEFFC;
.dribbble {
position: fixed;
display: block;
right: 20px;
bottom: 20px;
img {
display: block;
height: 28px;
}
}
.twitter {
position: fixed;
display: block;
right: 64px;
bottom: 14px;
svg {
width: 32px;
height: 32px;
fill: #1da1f2;
}
}
}
View Compiled
const $ = (s, o = document) => o.querySelector(s);
const $$ = (s, o = document) => o.querySelectorAll(s);
let emoji = $('.rating-slider'),
button = $('button', emoji),
slider = $('.slide', emoji),
drag = $('div', slider),
smiley = $('.smiley', emoji),
points = {
0: {
name: 'awful',
mouth: 'M32,2 C41.5729357,2 58,10.8218206 58,21 C50.2396023,18.9643641 41.5729357,17.9465462 32,17.9465462 C22.4270643,17.9465462 13.7603977,18.9643641 6,21 C6,10.8218206 22.4270643,2 32,2 Z',
eye: 'M12.6744144,18.0128897 C17.3794842,15.6567898 19.3333811,9.83072065 17.0385652,5 C15.7595661,7.57089081 13.5517099,9.64170285 10.4149967,11.2124361 C7.27828344,12.7831694 3.80661788,13.5564215 0,13.5321925 C2.2948159,18.3629131 7.9693445,20.3689896 12.6744144,18.0128897 Z'
},
61: {
name: 'bad',
mouth: 'M32,6 C41.5729357,6 62,10.80044 62,23 C52.2396023,17.5602347 42.2396023,14.840352 32,14.840352 C21.7603977,14.840352 11.7603977,17.5602347 2,23 C2,10.80044 22.4270643,6 32,6 Z',
eye: 'M9,19 C14.418278,19 17,15.418278 17,11 C17,6.581722 14.418278,3 9,3 C3.581722,3 1,6.581722 1,11 C1,15.418278 3.581722,19 9,19 Z'
},
134: {
name: 'okay',
mouth: 'M32,11.3326144 C41.5729357,11.3326144 51.5729357,9.55507624 62,4 C62,12.8056732 46.3594035,23 32,23 C17.6405965,23 2,12.8056732 2,4 C12.4270643,9.55507624 22.4270643,11.3326144 32,11.3326144 Z',
eye: 'M9,19 C14.418278,19 17,15.418278 17,11 C17,6.581722 14.418278,3 9,3 C3.581722,3 1,6.581722 1,11 C1,15.418278 3.581722,19 9,19 Z'
},
207: {
name: 'good',
mouth: 'M32,6.33261436 C41.5729357,6.33261436 51.5729357,5.55507624 62,0 C62,8.80567319 46.3594035,25 32,25 C17.6405965,25 2,8.80567319 2,0 C12.4270643,5.55507624 22.4270643,6.33261436 32,6.33261436 Z',
eye: 'M9,21 C13.418278,21 17,16.418278 17,11 C17,5.581722 13.418278,1 9,1 C4.581722,1 1,5.581722 1,11 C1,16.418278 4.581722,21 9,21 Z'
},
264: {
name: 'great',
mouth: 'M32,6.33261436 C41.5729357,6.33261436 53.5729357,5.55507624 64,0 C64,8.80567319 51.3594035,27 32,27 C12.6405965,27 0,8.80567319 0,0 C10.4270643,5.55507624 22.4270643,6.33261436 32,6.33261436 Z',
eye: 'M9,22 C13.418278,22 17,16.418278 17,11 C17,5.581722 13.418278,0 9,0 C4.581722,0 1,5.581722 1,11 C1,16.418278 4.581722,22 9,22 Z'
}
};
gsap.registerPlugin(MorphSVGPlugin);
gsap.registerPlugin(InertiaPlugin);
function setEmoji(value, duration = .4) {
let index = value == 0 ? value : Object.keys(points).indexOf(value),
name = points[value].name,
computed = getComputedStyle(emoji);
emoji.classList.remove('awful', 'bad', 'okay', 'good', 'great');
emoji.classList.add(name, 'scale');
gsap.to(emoji, {
'--y': index * -100 + '%',
'--fill': computed.getPropertyValue('--' + name + '-fill'),
'--radial': computed.getPropertyValue('--' + name + '-radial'),
'--border': computed.getPropertyValue('--' + name + '-border'),
'--shadow': computed.getPropertyValue('--' + name + '-shadow'),
'--mouth-fill': computed.getPropertyValue('--' + name + '-mouth-fill'),
'--mouth-shadow': computed.getPropertyValue('--' + name + '-mouth-shadow'),
'--mouth-shine': computed.getPropertyValue('--' + name + '-mouth-shine'),
duration: duration
});
gsap.to($$('.eye path', smiley), {
morphSVG: points[value].eye,
duration: .4
});
gsap.to($('.mouth path', smiley), {
morphSVG: points[value].mouth,
duration: .4
});
setTimeout(() => emoji.classList.remove('scale'), 600);
return value;
}
Draggable.create(drag, {
type: 'x',
bounds: {
left: -drag.offsetWidth / 2,
width: slider.offsetWidth + drag.offsetWidth
},
inertia: true,
snap(value) {
return setEmoji(Object.keys(points).reduce((p, c) => {
return (Math.abs(c - value) < Math.abs(p - value) ? c : p);
}));
},
onThrowUpdate() {
let x = this.x > 0 ? this.x : 0;
emoji.style.setProperty('--w', x + 'px');
},
onMove() {
let x = this.x > 0 ? this.x : 0;
emoji.style.setProperty('--w', x + 'px');
}
});
setEmoji(0, 0);