Funky button
Button with randomized clipping, animated on hover/focus.
HTML
<button><span>Funky button!</span></button>
CSS
@import url('https://fonts.googleapis.com/css2?family=ZCOOL+KuaiLe&display=swap');
:root {
--color-page-background: #45b5f3;
--color-link-background: white;
--color-link-shadow: #0081c7;
--funky-button-width: 240px;
--funky-button-height: 70px;
}
body {
background-color: var(--color-page-background);
padding: 1em;
font-family: 'ZCOOL KuaiLe', Verdana, Geneva, Tahoma, sans-serif;
font-size: 20px;
font-weight: bold;
letter-spacing: 1px;
}
button {
--clip: polygon(
var(--clip-offset-1, 0px) var(--clip-offset-2, 0px),
calc(100% - var(--clip-offset-3, 0px)) var(--clip-offset-4, 0px),
calc(100% - var(--clip-offset-5, 0px)) calc(100% - var(--clip-offset-6, 0px)),
var(--clip-offset-7, 0px) calc(100% - var(--clip-offset-8, 0px))
);
position: relative;
background: unset;
border: none;
width: var(--funky-button-width);
height: var(--funky-button-height);
display: flex;
align-items: center;
justify-content: flex-start;
padding-left: 1.5em;
font: inherit;
outline: none;
cursor: pointer;
}
button::before,
button::after {
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: var(--color-link-background);
clip-path: var(--clip);
z-index: -1;
}
button::before {
background-color: var(--color-link-shadow);
transform: translate3d(7px, 5px, 0px);
}
button span {
display: inline-block;
}
button:is(:hover, :focus-visible)::after,
button:is(:hover, :focus-within) span {
transform: translate3d(-2px, -2px, 0px) scale(1.02);
}
button:is(:hover, :focus-visible)::before {
transform: translate3d(7px, 6px, 0px) scale(1.02);
}
JavaScript
const shuffle = (arr) => {
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}
const randomizeClipOffsets = (selector, offsets = [0, 0, 1, 3, 3, 4, 5, 6]) => {
for (const element of document.querySelectorAll(selector)) {
let index = 0;
for (const offset of shuffle(offsets.slice())) {
element.style.setProperty(`--clip-offset-${++index}`, `${offset}px`);
}
}
}
const init = () => {
randomizeClipOffsets('button');
setInterval(randomizeClipOffsets, 350, 'button:is(:hover, :focus-visible)');
}
init();