<label class="heat-slider heat-slider--v">
<span class="heat-slider--label">How Spicy?</span>
<span class="heat-slider--input">
<input type="range" value="32" min="0" max="143" oninput="doThings(event)" />
</span>
</label>
<output>change the slider</output>
@mixin slider-track() {
background: transparent;
padding: calc(var(--slider-handle-size) / 1.6) 0;
margin: 0 calc(var(--slider-handle-size) / 2 * -1);
cursor: pointer;
}
@mixin slider-thumb($cursor: grab) {
position: relative;
appearance: none;
box-shadow: 0 0 0 .2em #444; // inset, 0 0 0 .2em #fff;
height: calc(var(--slider-handle-size) * 2);
width: var(--slider-handle-size);
margin: calc(var(--slider-handle-size) * -1) 0;
border-radius: var(--slider-radius, 1em);
background: transparent;
cursor: $cursor;
}
// LAYOUT
// - side-by-side
.heat-slider--h {
display: flex;
align-items: center;
.heat-slider--label + * {
margin-left: 1em;
flex: 1;
}
}
// - stacked
.heat-slider--v {
display: flex;
flex-direction: column;
justify-content: center;
.heat-slider--label + * {
margin-top: 1em;
flex: 1;
}
}
// MECHANICAL
.heat-slider {
--slider-base: #d8d8d8;
--slider-red: #fc3770;
--slider-orange: #ff7f36;
--slider-yellow: #F6D866;
--slider-green: #74C35A;
--slider-blue: #2C95DD;
--slider-radius: 1em;
--slider-handle-size: 1em;
--p: 22.9%; // magic, set by javascript funtion when input value updates
width: 30em; // cheap opinion
&--input {
position: relative;
line-height: 0;
border-radius: var(--slider-radius);
background-image: linear-gradient(to right,
var(--slider-blue),
var(--slider-green),
var(--slider-yellow),
var(--slider-orange),
var(--slider-red)
);
// the magic mask
&::before {
content: '';
position: absolute;
pointer-events: none;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin-right: -1px; // hack for border-radius anti-aliasing, ugh
border-radius: var(--slider-radius);
background: linear-gradient(to right,
transparent 0,
transparent calc(var(--p) + var(--slider-handle-size) / 2),
var(--slider-base) var(--p),
var(--slider-base) 100%
);
}
input {
position: realtive;
z-index: 1;
width: 100%;
appearance: none;
font: inherit;
background: transparent;
border: 0;
margin: 0;
padding: 0;
// slider track
&::slider-runnable-track { @include slider-track() }
&::range-track { @include slider-track() }
// slider thumb
&::slider-thumb { @include slider-thumb() }
&::range-thumb { @include slider-thumb() }
&:focus { outline: none }
&:active {
// slider thumb
&::slider-thumb { @include slider-thumb($cursor: grabbing); }
&::range-thumb { @include slider-thumb($cursor: grabbing); }
}
}
}
}
// boring
body {
margin: 0;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-family: system-ui, sans-serif;
// font-size: 2em; // uncomment to see full responsive sizing :)
}
*, *::before, *::after { box-sizing: border-box }
output {
margin: 1em;
opacity: .4;
}
View Compiled
const output = document.querySelector('output')
const doThings = (event) => {
const { value, min, max, step, parentElement: parent } = event.target
const decimals = step && step.includes('.') ? step.split('.')[1] : 1
const percent = `${((value - min)/(max - min) * 100).toFixed(decimals)}%`
parent.style.setProperty('--p', percent)
output.value = `value: ${value} (${percent})`
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.