Neumorphic Drag Dial Thermostat
See the Pen Neumorphic Drag Dial Thermostat.
Tech & Dependencies
Features
- ✓ Neumorphism
- ✓ Draggable Dial
- ✓ Dynamic Gradients
- ✓ Keyboard Accessible
Browser Support
Core
This is a Neumorphic Drag Dial Thermostat. It provides a tactile, skeuomorphic interface for setting numerical values (like temperature) through rotational dragging or keyboard input. The dial dynamically reveals cool-to-warm gradient colors based on the current value, enhancing the physical “feel” of a smart home control panel.
Specs
- Weight: ~130 KB (due to GSAP and Draggable dependencies).
- Performance: High. Rotation calculations are handled by GSAP, while the color transitions and scaling effects are offloaded to CSS opacity and transforms.
- Theming / Customization: Easily customizable via CSS variables (
--l1through--l6for the neumorphic shadows and highlights). Includes native light/dark mode support viaprefers-color-scheme. - Responsiveness: Uses viewport-relative font sizing (
calc(16px + ...)) so the entire UI scales fluidly on different devices. - Graceful Degradation: Without JavaScript, it remains a static UI element. With JS but without GSAP, the keyboard controls still function, but the dragging interaction breaks.
Anatomy
The structure mimics a physical control knob set into a soft, extruded surface.
- HTML (The Skeleton): The
.tempcontainer holds the main.temp__dialand a.temp__outdoorsinfo panel. The dial consists of overlapping layers:.temp__drag(the interaction area),.temp__dial-shades(the color gradients),.temp__dial-core(the inner shadow creating the “trench”), and the text value. - CSS (The Skin): Relies heavily on
box-shadow(combining inset and outset shadows with opposing light/dark colors) to create the soft, raised/depressed neumorphic look. Dynamic opacity swaps the cool and warm radial gradients. - JS (The Nervous System): An ES6 Class (
NeuThermostat) initializes GSAP’sDraggableon the knob. It translates the rotational angle into a temperature value (and vice-versa), updates CSS variables (--angle), and handles keyboard accessibility (Up/Right to increase, Down/Left to decrease).
Logic
The component intelligently extracts the rotation angle applied by GSAP and translates it back into a bounded temperature scale.
angleFromMatrix(transVal) {
let matrixVal = transVal.split('(')[1].split(')')[0].split(','),
[cos1,sin] = matrixVal.slice(0,2),
angle = Math.round(Math.atan2(sin,cos1) * (180 / Math.PI)) * -1;
// convert negative angles to positive
if (angle < 0) angle += 360;
if (angle > 0) angle = 360 - angle;
return angle;
}
When dragging, GSAP applies a transform: matrix(...) to the element. Instead of keeping a separate state variable for the angle, this function parses the raw CSS matrix string, uses Math.atan2 on the sine and cosine values, and converts the result back into standard degrees (0-360). This angle is then mapped against the defined minimum (15deg) and maximum (345deg) rotation bounds to calculate the exact temperature integer.
Feel
Soft, responsive, and highly tactile. The neumorphic design gives the dial a satisfying visual weight. When the user interacts (via drag or keypress), the inner core physically “depresses” into the screen via an animated drop shadow, while the outer ring simultaneously glows with a blue (cold) or orange (hot) gradient depending on the temperature setting. The numbers flip like a mechanical odometer, completing the sensory illusion of operating a high-end physical device.


