<div class="container">
<div class="placeholder">NAME</div>
<svg viewBox="0 0 320 100" width="320" height="100">
<path stroke="#000" stroke-width="2" fill="none" />
</svg>
<input type="text">
</div>
@import url('https://rsms.me/inter/inter-ui.css');
body {
align-items: center;
display: flex;
font-family: 'Inter UI', sans-serif;
font-size: 14px;
height: 100vh;
justify-content: center;
margin: 0;
}
.container {
display: flex;
flex-direction: column;
position: relative;
width: 320px;
}
.placeholder {
transform-origin: 0 50%;
transform: translateX(10px);
}
.placeholder.expand {
animation: MovePlaceholder 220ms both linear;
}
.placeholder.return {
animation: ReturnPlaceholder 220ms both linear;
}
input {
background: transparent;
border: 0;
font-size: 20px;
height: 30px;
outline: none !important;
position: absolute;
top: -12px;
}
svg {
position: relative;
top: -25px;
}
@keyframes MovePlaceholder {
0% { transform: translateX(10px) translateY(0) rotate(0) }
60% { transform: translateX(4px) translateY(-8px) rotate(-18deg) scale(0.92) }
100% { transform: translateX(0) translateY(-30px) rotate(0deg) scale(0.75) }
}
@keyframes ReturnPlaceholder {
0% { transform: translateX(0) translateY(-30px) scale(0.75) }
100% { transform: translateX(10px) translateY(0) }
}
let start = null;
const path = document.querySelector('path');
const bump = 0;
path.setAttribute('d', `m 0,30 c 16.920055,0 8.8823519,-${bump} 30,-${bump} 21.117648,0 13.001352,${bump} 30,${bump} h 260`);
const duration = 600;
const easing = BezierEasing(0.4, 0.0, 0.2, 1);
const ph = document.querySelector('.placeholder');
const input = document.querySelector('input');
input.addEventListener('focus', () => {
if (input.value.length === 0) {
ph.classList.remove('return');
ph.classList.add('expand');
window.requestAnimationFrame(step);
}
})
input.addEventListener('blur', () => {
if (input.value.length === 0) {
ph.classList.remove('expand');
ph.classList.add('return');
}
})
const animate = (inPct) => {
const pct = easing(inPct);
const newPct = 1 - 2 * Math.abs(0.5 - pct);
const newBump = newPct * 25;
path.setAttribute('d', `m 0,30 c 16.920055,0 8.8823519,-${newBump} 30,-${newBump} 21.117648,0 13.001352,${newBump} 30,${newBump} h 260`);
};
function step(timestamp) {
if (!start) start = timestamp;
const progress = (timestamp - start);
let pct = progress / duration;
if (pct > 1) pct = 1;
animate(pct);
if (progress < duration) {
window.requestAnimationFrame(step);
} else {
start = null;
}
}
This Pen doesn't use any external CSS resources.