Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using it's URL and the proper URL extention.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <header>
			<div class="title">
				<h2 class="better">Better</h2>
				<h1>Neumorphism</h1>
			</div>
		</header>
		<main>
			<div class="container">
				<div class="icon">i</div>
				<div class="info-container">
					<div class="info">
						<h2>
							Welcome to <span class="sub-title">Better</span> NEUMORPHISM!
						</h2>
						<p>
							This app was created to improve upon other neumorphism generators
							by implementing 2 unique tactics:
						</p>
						<ul>
							<li>HSL implementation</li>
							<li>Accommodation of rounded edges in UI elements</li>
						</ul>
						<p>
							Using HSL colors allows us to interact uniquely with the luminance
							of the UI and provides a much more cohesive feeling to the
							elements. Try luminance values between 50% and 90% for best
							results.
						</p>
						<p>
							Adding the ability to control rounded edges, or ramp, gives
							designers more flexibility into the finer details of UI elements.
							The effect can be subtle...or extreme.
						</p>
						<p>
							I hope you like this take on neumorphism generators and find this tool
							useful.
						</p>
						<div class="links">
							<a href="https://drucial.com" target="_blank" class="info-link">Drucial</a>
							<a href="https://codepen.io/drucial/pen/rNjJLWz" target="_blank" class="info-link">CodePen</a>
							<a href="https://github.com/Drucial/Neumorphism-Generator" target="_blank" class="info-link">GitHub</a>
						</div>
					</div>
				</div>
				<div class="slider-container">
					<div class="form-group">
						<label for="color">Color:</label>
						<div class="color">
							<p>hsl(&nbsp;</p>
							<input
								type="number"
								min="0"
								max="360"
								class="hsl-input"
								id="hue"
								placeholder="0-360"
							/>
							<input
								type="number"
								min="0"
								max="100"
								class="hsl-input"
								id="saturation"
								placeholder="%"
							/>
							<input
								type="number"
								min="20"
								max="90"
								class="hsl-input"
								id="luminance"
								placeholder="%"
							/>
							<p>&nbsp;)</p>
							<button id="color-btn" onclick="colorRandomizer()">Random</button>
						</div>
					</div>
					<div class="form-group">
						<label for="neu-depth">Depth</label>
						<input
							type="range"
							min="1"
							max="15"
							step="0.01"
							value="8"
							class="slider"
							id="neu-depth"
						/>
					</div>
					<div class="form-group">
						<label for="neu-strength">Strength</label>
						<input
							type="range"
							min="1.05"
							max="1.3"
							value="1.2"
							step="0.001"
							class="slider"
							id="neu-strength"
						/>
					</div>
					<div class="form-group">
						<label for="neu-edges">Edges</label>
						<input
							type="range"
							min="0"
							max="2"
							value=".2"
							step="0.01"
							class="slider"
							id="neu-edges"
						/>
					</div>
					<div class="form-group">
						<label for="neu-direction">Direction</label>
						<input
							type="range"
							class="slider"
							id="neu-direction"
							min="-1"
							max="1"
							step="0.01"
							value="-1"
						/>
					</div>
					<div class="form-group output">
						<label for="neuSize">Output</label>
						<div class="code">
							<code id="source">
								<p>box-shadow:</p>
								<p class="code-line">&nbsp;</p>
								<p class="code-line">&nbsp;</p>
								<p class="code-line">&nbsp;</p>
								<p class="code-line">&nbsp;</p>
							</code>
							<div class="tool-tip" id="toolTip">
								<p>Click to Copy</p>
							</div>
						</div>
					</div>
				</div>
			</div>
		</main>
              
            
!

CSS

              
                /* Global Site Elements */
:root {
	--box-h: 207;
	--box-s: 15%;
	--box-l: 80%;
	--direction: -32deg;
	--direction-reverse: -212deg;
	--box-source: calc(var(--box-l) + 10%);
	--box-dark: calc(var(--box-l) - 15%);
	--box-light: calc(var(--box-l) + 15%);
	--text-color: hsl(var(--box-h), var(--box-s), calc(var(--box-l) - 40%));
	--text-light: hsl(var(--box-h), var(--box-s), calc(var(--box-l) - 25%));
	--box-color: hsl(var(--box-h), var(--box-s), var(--box-l));
	--color-dark: hsl(var(--box-h), var(--box-s), var(--box-dark));
	--color-light: hsl(var(--box-h), var(--box-s), var(--box-light));
	--color-gradient: var(--direction), var(--color-light), var(--color-dark);
	--reverse-gradient: var(--direction-reverse), var(--color-light), var(--color-dark);

	--shadow-x: 3px;
	--shadow-distance: 3px;
	--shadow-ramp: 6px;
	--shadow-color: rgba(0 0 0 / .2);
	--actual-shadow: var(--shadow-x) var(--shadow-distance) var(--shadow-ramp) var(--shadow-color);

	--hl-strength: calc(var(--box-l) * 1.2);
	--shd-strength: calc(var(--box-l) * (1 - (1.2 - 1)));
	--neu-x: 7.5px;
	--neu-neg-x: -7.5px;
	--neu-distance: 7.5px;
	--neu-neg-dist: -7.5px;
	--neu-ramp: 18.75px;
	--highlight: hsl(var(--box-h), var(--box-s), var(--hl-strength));
	--shadow: hsl(var(--box-h), var(--box-s), var(--shd-strength));

	--neu-outer: 
		var(--neu-x) var(--neu-distance) var(--neu-ramp) var(--shadow),
		var(--neu-neg-x) var(--neu-neg-dist) var(--neu-ramp) var(--highlight);

	--neu-outer-small: 
		var(--neu-x-small) var(--neu-distance-small) var(--neu-ramp-small) var(--shadow),
		var(--neu-neg-x-small) var(--neu-neg-dist-small) var(--neu-ramp-small) var(--highlight);

	--neu-inner: 
		inset var(--neu-x) var(--neu-distance) var(--neu-ramp) var(--shadow),
		inset var(--neu-neg-x) var(--neu-neg-dist) var(--neu-ramp) var(--highlight);

	--neu-inner-small: 
		inset var(--neu-x-small) var(--neu-distance-small) var(--neu-ramp-small) var(--shadow),
		inset var(--neu-neg-x-small) var(--neu-neg-dist-small) var(--neu-ramp-small) var(--highlight);

	--edge-x: 0px;
	--edge-neg-x: 0px;
	--edge-dist: 0px;
	--edge-neg-dist: 0px;
	--edge-ramp: 0px;

	--neu-edges: 
    	inset var(--edge-neg-x) var(--edge-neg-dist) var(--edge-ramp) var(--shadow),
		inset var(--edge-x) var(--edge-dist) var(--edge-ramp) var(--highlight);
	--neu-edges-outer: 
    	var(--edge-neg-x) var(--edge-neg-dist) var(--edge-ramp) var(--shadow),
		var(--edge-x) var(--edge-dist) var(--edge-ramp) var(--highlight);

	--radius: 1rem;
	--neu-size: 50%;
	--neu-radius: 1rem;
}

* {
	margin: 0;
	padding: 0;
	box-sizing: border-box;
}

html {
	height: 100vh;
	height: calc(var(--vh, 1vh) * 100);
	font-family: 'Inter', sans-serif;
	font-size: 18px;
	font-weight: 400;
	color: var(--text-color);
	overflow-x: hidden;
}

body {
	height: 100%;
	display: flex;
	flex-direction: column;
	justify-content: center;
	align-items: center;
	background: var(--box-color);
	background: 
	linear-gradient(
		var(--direction), 
		var(--box-color)70%, 
		hsl(var(--box-h), var(--box-s), var(--box-source)))
}

header {
	padding: 1rem;
}

.title {
	display: flex;
	position: relative;
	font-size: 2rem;
}

.better {
	font-family: 'Satisfy', cursive;
	transform: rotate(-35deg);
	position: absolute;
	top: -.75em;
	left: -1.25em;

}

h1 {
	font-weight: 900;
	text-transform: uppercase;
}

main {
	display: grid;
	place-items: center;
}

.container {
	display: flex;
	flex-direction: column;
	justify-content: center;
	min-height: 100%;
	padding: 1rem;
	position: relative;
	z-index: 1;
}

.slider-container {
	padding: 2rem 2rem;
	background: var(--box-color);
	border-radius: var(--neu-radius);
	box-shadow: var(--neu-outer), var(--neu-edges);
	position: relative;
}

.form-group {
	display: grid;
	align-items: center;
	grid-template-columns: 1fr 5fr;
	padding: 0.5rem 0;
}

.color {
	display: flex;
	align-items: center;
	gap: 1rem;
	color: var(--text-light);
}

.hsl-input {
	-webkit-appearance: none;
	height: 2rem;
	width: 4rem;
	/* margin: 0 0.5rem; */
	padding: 1rem;
	color: var(--text-light);
	text-align: center;
	border: none;
	border-radius: 1rem;
	background: var(--box-color);
	box-shadow: var(--neu-inner),
	var(--neu-edges-outer);
}

.hsl-input::placeholder {
	color: var(--text-light);
	text-align: center;
}

.hsl-input::-webkit-outer-spin-button,
.hsl-input::-webkit-inner-spin-button {
	-webkit-appearance: none;
	margin: 0;
}

#color-btn {
	font-family: 'Satisfy', cursive;
	font-size: 1rem;
	color: var(--text-color);
	background: var(--box-color);
	padding: .4rem 2rem;
	margin-left: .5rem;
	border: none;
	border-radius: var(--radius);
	box-shadow: 
		var(--neu-outer-small),
		var(--neu-edges);
	cursor: pointer;

	position: relative;
}

#color-btn::after {
	position: absolute;
	content: '';
	top: 0;
	bottom: 0;
	left: 0;
	right: 0;
	border-radius: var(--radius);
	box-shadow: 
		var(--neu-inner-small),
		var(--neu-edges-outer);
	opacity: 0;
	transition: opacity 300ms;
}

#color-btn:active {
	box-shadow: none;
	transform: scale(.98);
}

#color-btn:active::after {
	opacity: 1;
	transform: scale(1)
  }

.slider {
	-webkit-appearance: none;
	appearance: none;
	outline: none;
	height: 2rem;
	border-radius: 1rem;
	background: var(--box-color);
	box-shadow: var(--neu-inner),
				var(--neu-edges-outer);
	position: relative;
}

.slider::before {
	position: absolute;
	content: "";
	width: 92%;
	height: 5px;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	background: var(--text-color);
	border-radius: 1rem;
	z-index: 1;
}

.slider::-webkit-slider-thumb {
	-webkit-appearance: none;
	appearance: none;
	width: 2rem;
	height: 2rem;
	background: var(--box-color);
	background: linear-gradient(var(--color-gradient));
	box-shadow: 
		var(--neu-edges),
		var(--actual-shadow);
	border-radius: 1rem;
	border: none;
	cursor: pointer;
	position: relative;
	z-index: 2;
}

.slider::-moz-range-thumb {
	width: 2rem;
	height: 2rem;
	background: var(--box-color);
	box-shadow: 
		inset 1px 1px 3px var(--highlight),
		inset -1px -1px 3px var(--shadow), 
		3px 3px 5px var(--shadow);
	border-radius: 1rem;
	border: none;
	cursor: pointer;
	position: relative;
	z-index: 2;
}

.code {
	padding: 1rem;
	width: 50ch;
	color: var(--text-light);
	background: var(--box-color);
	box-shadow: var(--neu-inner),var(--neu-edges-outer);
	border-radius: 1rem;
	position: relative;

}

code p:not(:first-child){
	font-size: 14px;
	margin-left: 1rem;
}

#icon {
  display: none;
}

.tool-tip {
  opacity: 0;
  position: absolute;
  height: 100%;
  width: 100%;
  top: 0;
  left: 0;
  color: var(--text-color);
  background: var(--box-color);
  font-weight: bold;
  border-radius: 1rem;
  display: flex;
  justify-content: center;
  align-items: center;
  cursor: pointer;
  transition: all .25s ease-in-out;
}

.icon {
	position: absolute;
	right: 2rem;
	top: 2rem;
	width: 30px;
	height: 30px;
	border-radius: 100%;
	display: flex;
	align-items: center;
	justify-content: center;
	box-shadow: 
		var(--neu-outer-small);
	background: var(--box-color);
	z-index: 5;
	cursor: pointer;
	user-select: none;
	-webkit-touch-callout: none;
	-webkit-user-select: none;
}

.icon-close {
	box-shadow: 
		var(--neu-outer-small),
		var(--neu-edges);
	background: linear-gradient(var(--color-gradient));
}

.icon::after {
	position: absolute;
	content: '';
	top: 0;
	bottom: 0;
	left: 0;
	right: 0;
	border-radius: var(--radius);
	box-shadow: 
		var(--neu-inner-small),
		var(--neu-edges-outer);
	opacity: 0;
	transition: opacity 300ms;
}

.icon:active {
	box-shadow: none;
	transform: scale(.98);
}

.icon:active::after {
	opacity: 1;
	transform: scale(1)
  }

.info-container {
	display: none;
	position: absolute;
	top: 0;
	bottom: 0;
	right: 0;
	left: 0;
	padding: 1rem;
	z-index: 4;
}

.show {
	display: block
}

.info {
	z-index: 5;
	background: var(--box-color);
	width: 100%;
	height: 100%;
	border-radius: var(--neu-radius);
	box-shadow: var(--neu-edges);
	padding: 2rem;
	overflow-y: scroll;
	-ms-overflow-style: none;
	scrollbar-width: none;
}

.info::-webkit-scrollbar {
	display: none;
}

.sub-title, .info-link {
	font-family: 'Satisfy', cursive;
}

.info p, ul {
	margin-top: .5rem;
}

ul {
	margin-left: 1rem;
}

.links {
	width: 100%;
	display: flex;
	gap: 1rem;
}

.info-link {
	color: var(--text-color);
	font-size: 1.5rem;
	padding: 1rem 0;
}

.info-link:hover,
.info-link:focus {
	color: var(--text-light)
}

@media only screen and (max-width: 747px) {
	header {
		padding-bottom: 0;
	}
	.title {
		font-size: 1rem;
		width: 100%;
		margin-top: 2rem;
		align-items: flex-end;
	}

	.slider-container {
		padding: 1rem 1rem;
		width: 90vw;
	}

	.form-group {
		grid-template-columns: 1fr;
	}

	label {
		margin-bottom: 0.25rem;
	}

	.color *:not(button) {
		display: none;
	}

	#color-btn {
		margin: 0;
	}

	.code {
		width: 100%;
		margin-top: .5rem;
		box-shadow: var(--neu-outer);
	}

	code {
		display: none;
	}

	.tool-tip{
		opacity: 1 !important;
		background: none;
	}

	.links {
		flex-direction: column;
		margin-top: .5rem;
		gap: 0;
	}

	.info-link {
		padding: 0;
	}
}

              
            
!

JS

              
                // Mobile viewport Height Correction
let vh = window.innerHeight * 0.01;
document.documentElement.style.setProperty('--vh', `${vh}px`);

window.addEventListener('resize', () => {
  let vh = window.innerHeight * 0.01;
  document.documentElement.style.setProperty('--vh', `${vh}px`);
});

// Info Toggle
const icon = document.querySelector(".icon")
const info = document.querySelector(".info-container")

function toggleInfo() {
    if(info.classList.contains('show')){
        info.classList.remove('show');
        icon.classList.remove('icon-close')
        icon.innerText = "i"
    } else {
        info.classList.add('show');
        icon.classList.add('icon-close')
        icon.innerText = "X"
    }
}
icon.addEventListener("click", toggleInfo)

// Global Variables
const root = document.documentElement;
const neuDir = document.getElementById('neu-direction');
const neuDepth = document.getElementById('neu-depth');
const neuStrength = document.getElementById('neu-strength')
const neuEdges = document.getElementById('neu-edges');
const neuRadius = document.getElementById('neu-radius');

// Color Input
const hslInput = document.querySelectorAll('.hsl-input')
const hue = document.getElementById('hue')
const sat = document.getElementById('saturation')
const lum = document.getElementById('luminance')
// Validation

function hueVal(){
    if(hue.value === ''){
        return 
    } else if(parseInt(hue.value) > 360 || parseInt(hue.value) < 0 ){
        alert('Please enter a hue value between 0 and 360')
        hue.value = ""
        return
    } else {
        return true
    }
}
function satVal(){
    if(sat.value === ''){
        return 
    } else if(parseInt(sat.value) > 100 || parseInt(sat.value) < 0 ){
        alert('Please enter a saturation value between 0 and 100')
        sat.value = ""
        return 
    } else {
        return true
    }
}
function lumVal(){
    if(lum.value === ''){
        return 
    } else if(parseInt(lum.value) > 100 || parseInt(lum.value) < 0 ){
        alert('Please enter a luminance value between 20 and 90')
        lum.value = ""
        return
    } else {
        return true
    }
}
for(let i =0; i < hslInput.length; i++){
    hslInput[i].addEventListener("input", () => {
        hueVal();
        satVal();
        lumVal();
        if(hueVal() === true && satVal() === true && lumVal() === true){
            colorUpdate()
        } else {
            return
        }
    })
}

function colorUpdate() {
    const h = hue.value;
    const s = sat.value;
    const l = lum.value;
    document.documentElement.style.setProperty('--box-h', `${h}`);
    document.documentElement.style.setProperty('--box-s', `${s}%`);
    document.documentElement.style.setProperty('--box-l', `${l}%`);
    setDepth();
    setStrength();
    setEdges();
    setCss();
}

// Depth Slider
function setDepth() {
    neuDistance = neuDepth.value;
    neuDistanceSmall = neuDepth.value / 2;
    neuNeg = 1 - neuDistance
    neuNegSmall = 1 - neuDistanceSmall
    rampCalc = neuDistance * 2.5
    rampCalcSmall = (neuDistance * 2.5) / 2
    root.style.setProperty("--neu-distance", `${neuDistance}px`);
    root.style.setProperty("--neu-neg-dist", `${neuNeg}px`);
    root.style.setProperty("--neu-distance-small", `${neuDistanceSmall}px`);
    root.style.setProperty("--neu-neg-dist-small", `${neuNegSmall}px`);
    root.style.setProperty("--neu-ramp", `${rampCalc}px`);
    root.style.setProperty("--neu-ramp-small", `${rampCalcSmall}px`);
    setStrength()
    setDirection()
}
neuDepth.addEventListener("input", () => {
    setDepth();
    setCss()
});

// Strength Slider
function setStrength() {
    const multiplier = neuStrength.value;
    const staticLum = getComputedStyle(root).getPropertyValue('--box-l').split("%")[0]
    const lightCalc = (staticLum * multiplier)
    const darkCalc = (staticLum * (1 - (multiplier - 1)))
    root.style.setProperty("--hl-strength", `${lightCalc}%`);
    root.style.setProperty("--shd-strength", `${darkCalc}%`);
}
neuStrength.addEventListener("input", () => {
    setStrength();
    setCss()
});

// Edges Slider
function setEdges() {
    edgeDistance = neuEdges.value;
    edgeNeg = -1 * edgeDistance
    rampCalc = edgeDistance * 4
    root.style.setProperty("--edge-dist", `${edgeDistance}px`);
    root.style.setProperty("--edge-neg-dist", `${edgeNeg}px`);
    root.style.setProperty("--edge-ramp", `${rampCalc}px`);
    setDirection()
}
neuEdges.addEventListener("input", () => {
    setEdges();
    setCss()
});

// Direction Slider
// Yeah I know it looks like a bunch of giberish...but I was sleepy so...
function setDirection() {
    const neuDirection = parseFloat(neuDir.value);
    const gradientDirection = -32
    const gradDirOffset = - (gradientDirection * neuDirection);
    const x = parseFloat(getComputedStyle(root).getPropertyValue('--neu-distance').split('px')[0])
    const dirOffsetX = - (x * neuDirection)
    const dirNegOffsetX = (x * neuDirection)
    const dirOffsetXSmall = - (x * neuDirection) / 2
    const dirNegOffsetXSmall = (x * neuDirection) / 2
    const edgeX = parseFloat(getComputedStyle(root).getPropertyValue('--edge-dist').split('px')[0])
    const dirEdgeOffsetX = - (edgeX * neuDirection)
    const dirEdgeNegOffsetX = (edgeX * neuDirection)
    const shdX = parseFloat(getComputedStyle(root).getPropertyValue('--shadow-distance').split('px')[0])
    const shdOffsetX = - (shdX * neuDirection)
    root.style.setProperty("--neu-x", `${dirOffsetX}px`);
    root.style.setProperty("--neu-neg-x", `${dirNegOffsetX}px`);
    root.style.setProperty("--neu-x-small", `${dirOffsetXSmall}px`);
    root.style.setProperty("--neu-neg-x-small", `${dirNegOffsetXSmall}px`);
    root.style.setProperty("--direction", `${gradDirOffset}deg`);
    root.style.setProperty("--edge-x", `${dirEdgeOffsetX}px`);
    root.style.setProperty("--edge-neg-x", `${dirEdgeNegOffsetX}px`);
    root.style.setProperty("--shadow-x", `${shdOffsetX}px`);
    setStrength()
}
neuDir.addEventListener("input", () => {
    setDirection();
    setCss()
});

// Code Output
function setCss() {
    const neuContainer = document.querySelector('.slider-container')
    const codeLine = document.querySelectorAll('.code-line')
    styleOuter = window.getComputedStyle(neuContainer).getPropertyValue('--neu-outer');
    styleArrOne = styleOuter.split("), ")
    styleInner= window.getComputedStyle(neuContainer).getPropertyValue('--neu-edges');
    styleArrTwo = styleInner.split("), ")

    lineOne = `${styleArrOne[0]}),`
    lineTwo = `${styleArrOne[1]},`
    lineThree = `${styleArrTwo[0]}),`
    lineFour = `${styleArrTwo[1]};`

    for(let i = 0; i < codeLine.length; i++){
        codeLine[0].innerText = lineOne
        codeLine[1].innerText = lineTwo
        codeLine[2].innerText = lineThree
        codeLine[3].innerText = lineFour
    }
}

// Random Colors on Load
function colorRandomizer(){
    hStartup = Math.round(Math.random() * (360 - 0) + 0);
    sStartup = Math.round(Math.random() * (30 - 0) + 0);
    lStartup = Math.round(Math.random() * (80 - 60) + 60);
    root.style.setProperty("--box-h", `${hStartup}`);
    root.style.setProperty("--box-s", `${sStartup}%`);
    root.style.setProperty("--box-l", `${lStartup}%`)
    setDepth();
    setStrength();
    setEdges();
    setCss();
}
colorRandomizer()

//   Source Code Copy & Tool Tips 
const toolTip = document.getElementById("toolTip");
function alertCopy () {
    toolTip.style.setProperty("opacity", .85);
}
function copyToClipboard() {
    const source = document.getElementById('source').innerText;
    const text = source.split(/\r?\n|\r/g).join(' ')
    navigator.clipboard.writeText(text);
    toolTip.innerHTML = "Copied!";
}
function resetCopy() {
    toolTip.style.setProperty("opacity", 0);
    toolTip.innerHTML = "Click to Copy";
}
toolTip.addEventListener("mouseover", alertCopy)
toolTip.addEventListener("click", copyToClipboard)
toolTip.addEventListener("mouseout", resetCopy)
              
            
!
999px

Console