- var val = 0;
- var d = 800;
- var o = -.9*d, r = .625*d;
- var fs = .1*d, sw = .1*fs;

.wrap(style=`--val: ${val}; --ang: 0deg` data-trf='')
	.sbox
		input#r(type='range' value=val)
		output(hidden)
		.axis(data-name='x')
		.axis(data-name='y')
	.orig transform-origin: 100% 0
	svg(viewBox=[o, o , d, d].join(' '))
		style text { font: #{fs}px comic sans ms }
			| [d] { stroke-width: #{sw} }
		path#a(d=`M${-r} 0A${r} ${r} 0 0 1 0${-r}
							M${-.15*r}${-1.1*r}
							q${.4*r} ${.1*r} 0 ${.2*r}`)
		text(x=1.5*fs dy=-.4*fs)
			textPath(xlink:href='#a') clockwise (+)
View Compiled
$k: .1;
$t: .5s;
$track-w: 25em;
$track-h: .02*$track-w;
$thumb-d: $k*$track-w;

$chart-d: (1 - 2*$k)*100%;

@mixin track() {
	border: none;
	width: $track-w; height: $track-h;
	border-radius: .5*$track-h;
	background: #333
}

@mixin thumb() {
	border: none;
	width: $thumb-d; height: $thumb-d;
	border-radius: 50%;
	transform: scale(.7);
	background: #f90;
	filter: saturate(.7);
	transition: transform $t linear, filter $t
}

@mixin thumb-focus() {
	transform: none;
	filter: none
}

* { margin: 0 }

@media (min-width: 500px), (min-height: 500px){
	html { overflow: hidden }
}

.wrap {
	position: relative;
	margin: 7.5em auto;
	width: $track-w; height: $track-w;
	background: #ccc;
	font: 2.5vmin consolas, monaco, monospace;
	
	@media (max-width: 500px), 
				 (max-height: 500px) { font-size: 10px }
  @media (min-width: 1600px), 
				 (min-height: 1600px) { font-size: 32px }
	
	&:before {
		display: flex;
		align-items: center;
		justify-content: center;
		position: absolute;
		top: 100%;
		width: 100%; height: 4em;
		content: attr(data-trf)
	}
	
	&:after {
		display: flex;
		align-items: center;
		justify-content: center;
		position: relative;
		width: $chart-d; height: $chart-d;
		background: #888;
		color: #555;
		font: 200 2em comic sans ms, cursive;
		text-shadow: -1px -1px #bbb;
		content: 'chart'
	}
}

.sbox {
	position: relative;
	z-index: 1;
	box-shadow: inset 0 0 0 2px currentcolor, 
		0 0 0 2px currentcolor;
	transform-origin: 100% 0;
	transform: rotate(var(--ang));
	color: #95a;
	
	&:before, &:after {
		display: flex;
		align-items: center;
		justify-content: center;
		position: absolute;
		width: $thumb-d; height: $thumb-d;
		transform: rotate(calc(-1*var(--ang)));
		color: #f90;
		text-shadow: 1px 1px #000;
		font-weight: 700
	}
	
	&:before {
		right: 100%;
		content: 'MIN'
	}
	
	&:after {
		top: 0; left: 100%;
		content: 'MAX'
	}
}

.axis {
	position: absolute;
	top: -1px; left: 100%;
	width: 25%; height: 2px;
	background: currentcolor;
	color: #b53;
	text-align: right;
	
	&:before, &:after {
		position: absolute;
		top: 50%; left: 100%;
	}
	
	&:before {
		border: solid 0 transparent;
		border-left-color: currentcolor;
		border-width: $track-h 2*$track-h;
		width: 0; height: 0;
		transform: translatey(-50%);
		content: ''
	}
	
	&:after {
		width: 1.5em; height: 1.5em;
		transform: rotate(calc(var(--ang)*-1));
		font: italic 700 1em/ 1.5 times new roman, serif;
		text-align: center;
		content: attr(data-name)
	}
}

[data-name='y'] {
	transform-origin: 0 50%;
	transform: rotate(90deg);
	
	&:after { transform: rotate(calc(-1*var(--ang) - 90deg)); }
}

.orig {
	position: absolute;
	z-index: 2;
	bottom: 100%; right: 0;
	padding-right: .25em;
	line-height: 3;
	
	&:after {
		position: absolute;
		right: 0; bottom: 0;
		width: 2*$track-h; height: 2*$track-h;
		border-radius: 50%;
		transform: translate(50%, 50%);
		background: #b53;
		content: ''
	}
}

[type='range'] {
	&, &::-webkit-slider-thumb { -webkit-appearance: none }
	
	display: block;
	padding: 0;
	width: $track-w; height: $thumb-d;
	background: transparent;
	font: inherit;
	cursor: pointer;

	&::-webkit-slider-runnable-track { @include track }
	&::-moz-range-track { @include track }
	&::-ms-track { @include track }

	&::-webkit-slider-thumb {
		margin-top: .5*($track-h - $thumb-d);
		@include thumb
	}
	&::-moz-range-thumb { @include thumb }
	&::-ms-thumb {
		margin-top: 0;
		@include thumb
	}
	
	&::-ms-tooltip { display: none }
	
	+ output {
		display: flex;
		align-items: center;
		justify-content: center;
		position: absolute;
		top: calc(100% + .5em); left: 0;
		width: $thumb-d; height: $thumb-d;
		border-radius: .5em;
		transform: 
			translate(calc(.01*var(--val)*#{$track-w*(1 - $k)})) 
			scale(.9);
		background: currentcolor;
		
		&:before {
			display: block;
			transform: rotate(calc(-1*var(--ang))) scale(.9);
			color: #fff;
			counter-reset: val var(--val);
			content: counter(val)'%'
		}
	}
	
	&:focus {
		outline: none;
		
		&::-webkit-slider-thumb { @include thumb-focus }
		&::-moz-range-thumb { @include thumb-focus }
		&::-ms-thumb { @include thumb-focus }
		
		
	}
}

svg {
	position: absolute;
	z-index: 3;
	right: 80%; bottom: 80%;
	width: .5*$track-w; height: .5*$track-w;
	//background: cyan;
}

path {
	fill: none;
	stroke: #b53
}

text { fill: #b53 }
View Compiled
const _R = document.getElementById('r'), 
			_W = _R.parentNode.parentNode, 
			_O = _W.querySelector('output'), 
			BT = 90, DT = 65, T = BT + 2*DT;

let val = null, t = 0, ang = 0;

function update() {
	let newval = +_R.value;

	if(val !== newval) {
		_W.style.setProperty('--val', val = newval);
	}
};

update();
_O.setAttribute('hidden', false);

function ani(t = 0) {
	if(t > T) t = 0;
	
	let trel = Math.min(Math.max(0, t - DT), 90);
	
	_W.style.setProperty('--ang', `${-trel}deg`);
	_W.dataset.trf = `transform: rotate(${-trel}deg)`;
	
	requestAnimationFrame(ani.bind(this, ++t));
};

ani();

_R.addEventListener('input', update, false);
_R.addEventListener('change', update, false);
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.