<div class="col">
<div class="controls">
<h1>Zig-zag gradient lab</h1>
<h3>Options</h3>
<div class="controls__group">
<label for="#angle">Angle</label>
<input type="range" min="5" max="85" id="angle" data-input="range">
</div>
<div class="controls__group">
<label for="#thickness">Stripe thickness</label>
<input type="range" min="2" max="120" step="1" id="thickness" data-input="thickness" value="30">
</div>
<div class="controls__group">
<label for="#width">Segment width</label>
<input type="range" min="10" max="200" step="1" id="width" data-input="width" value="100">
</div>
<div class="controls__group">
<label for="#color1">Primary colour</label>
<input type="range" min="0" max="360" step="1" id="color1" data-input="color1" value="100">
</div>
<div class="controls__group">
<label for="#color1">Contrast</label>
<input type="range" min="50" max="90" step="1" id="contrast" data-input="contrast">
</div>
<h3>CSS output</h3>
<div class="css" data-output></div>
</div>
</div>
<div class="bg" data-bg></div>
@import url("https://fonts.googleapis.com/css?family=Open+Sans:400,700");
* {
box-sizing: border-box;
}
body {
font-family: "Open Sans", sans-serif;
margin: 0;
@media (min-width: 40em) {
display: flex;
}
}
.bg {
--h1: 100deg;
--h2: calc(var(--h1) + calc(180deg - 30deg));
--h3: calc(var(--h1) + calc(180deg + 30deg));
--l1: 50%;
--c1: hsl(var(--h1, 0), 90%, var(--l1));
--c2: hsl(var(--h2), 90%, var(--l, 70%));
--c3: hsl(var(--h3), 60%, var(--d, 40%));
--t: 10px;
--w: 100px;
--angle: 45deg;
--grad: var(--c1) var(--t), var(--c1) calc(var(--t) * 2), var(--c2) calc(var(--t) * 2), var(--c2) calc(var(--t) * 3), var(--c3) calc(var(--t) * 3), var(--c3) calc(var(--t) * 4);
--mask: repeating-linear-gradient(to right, black var(--w), black calc(var(--w) * 2), transparent calc(var(--w) * 2), transparent calc(var(--w) * 3));
background: repeating-linear-gradient(var(--angle), var(--grad)), var(--c1);
background-size: var(--w) 100%;
height: 100vh;
position: relative;
flex: 1 0 auto;
transition: --angle 200ms, --t 200ms, --w 200ms;
}
.bg::after {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
content: '';
background: repeating-linear-gradient(calc(var(--angle) * -1), var(--grad)), var(--c1);
background-size: var(--w) 100%;
-webkit-mask-image: var(--mask);
mask-image: var(--mask);
}
.controls {
padding: 1rem;
background-color: white;
max-width: 30rem;
z-index: 1;
box-shadow: 0 0 0.75rem rgba(0, 0, 0, 0.3);
@media (min-width: 40em) {
display: inline-block;
position: fixed;
overflow: scroll;
top: 0;
left: 0;
height: 100%;
}
}
pre {
max-width: 100%;
overflow: scroll;
background: rgba(240, 240, 240, 1);
padding: 1rem;
border-radius: 0.2rem;
}
.controls__group {
display: flex;
align-items: center;
max-width: 20rem;
label {
margin-right: 1rem;
}
input {
margin-left: auto;
}
}
View Compiled
const angleInput = document.querySelector('[data-input="range"]')
const thicknessInput = document.querySelector('[data-input="thickness"]')
const widthInput = document.querySelector('[data-input="width"]')
const colorInput = document.querySelector('[data-input="color1"]')
const contrastInput = document.querySelector('[data-input="contrast"]')
const outputContainer = document.querySelector('[data-output]')
const bg = document.querySelector('[data-bg]')
const inputs = [...document.querySelectorAll('[data-input]')]
const angleChange = (target) => {
const newAngle = `${target.value}deg`
setItem('--angle', newAngle, 'angle', target.value)
}
const thicknessChange = (target) => {
const newThickness = `${target.value}px`
setItem('--t', newThickness, 'thickness', target.value)
}
const setItem = (cssVar, newValue, property, unitlessValue) => {
bg.style.setProperty(cssVar, newValue)
getCSSOutput()
localStorage.setItem(property, unitlessValue)
}
const widthChange = (target) => {
const newWidth = `${target.value}px`
setItem('--w', newWidth, 'width', target.value)
}
const colorChange = (target) => {
const newColor = `${target.value}deg`
setItem('--h1', newColor, 'color', target.value)
}
const setContrast = (contrast) => {
const newL = `${contrast}%`
const newD = `${100 - contrast}%`
bg.style.setProperty('--l', newL)
bg.style.setProperty('--d', newD)
}
const contrastChange = (target) => {
setContrast(target.value)
getCSSOutput()
localStorage.setItem('contrast', target.value)
}
const getCSSOutput = () => {
const { backgroundImage, backgroundSize } = getComputedStyle(bg)
const backgroundImageAfter = getComputedStyle(bg, '::after').backgroundImage
const mask = getComputedStyle(bg, '::after').webkitMaskImage
const cssOutput = `
<pre>
.bg {
background-image: ${backgroundImage};
background-size: ${backgroundSize};
}
.bg::after {
background-image: ${backgroundImageAfter};
background-size: ${backgroundSize};
-webkit-mask-image: ${mask};
mask-image: ${mask};
}
</pre>`
outputContainer.innerHTML = cssOutput
}
const setStyle = (input, property, unitlessValue, value) => {
if (unitlessValue) {
input.value = unitlessValue
bg.style.setProperty(property, value)
}
}
const getInitialStateFromLocalStorage = () => {
const angle = localStorage.getItem('angle')
const thickness = localStorage.getItem('thickness')
const width = localStorage.getItem('width')
const color = localStorage.getItem('color')
const contrast = localStorage.getItem('contrast')
setStyle(angleInput, '--angle', angle, `${angle}deg`)
setStyle(thicknessInput, '--t', thickness, `${thickness}px`)
setStyle(widthInput, '--w', width, `${width}px`)
setStyle(colorInput, '--h1', color, `${color}deg`)
if (contrast) {
setContrast(contrast)
contrastInput.value = contrast
}
}
getInitialStateFromLocalStorage()
getCSSOutput()
let activeInput
const handleMouseDown = (e) => {
activeInput = e.target
}
const updateValues = (target) => {
const { id } = target
if (id === 'angle') {
angleChange(target)
}
if (id === 'thickness') {
thicknessChange(target)
}
if (id === 'color1') {
colorChange(target)
}
if (id === 'width') {
widthChange(target)
}
if (id === 'contrast') {
contrastChange(target)
}
}
const handleMouseMove = (e) => {
if (activeInput === e.target) {
updateValues(e.target)
}
}
const handleChange = (e) => {
updateValues(e.target)
}
inputs.forEach(el => {
el.addEventListener('change', handleChange)
el.addEventListener('mousedown', handleMouseDown)
el.addEventListener('mousemove', _.throttle(handleMouseMove, 100))
updateValues(el)
})
CSS.registerProperty({
name: '--angle',
syntax: '<angle>',
inherits: true,
initialValue: `${angleInput.value}deg`,
})
CSS.registerProperty({
name: '--t',
syntax: '<length>',
inherits: true,
initialValue: `${thicknessInput.value}px`,
})
CSS.registerProperty({
name: '--w',
syntax: '<length>',
inherits: true,
initialValue: `${widthInput.value}px`,
})
This Pen doesn't use any external CSS resources.