<div class="KickInput">
<input type="text" class="KickInput-input" />
<label class="KickInput-label">
First Name
</label>
<svg width="300px" height="42px" viewBox="0 0 80 42" class="KickInput-svg">
<path
class="KickInput-kicker"
d="M0,40 C16,40 50,40 80,40 L300,40"
stroke="#C9CBCE"
stroke-width="4"
stroke-linecap="round"
fill="none"
/>
</svg>
</div>
<div class="Info">
Read more about front end development on <a target="_blank" href="https://stanko.github.io">my blog</a>.
<br/><br/>
Inspired by <a target="_blank" href="https://uimovement.com/ui/5963/input/">this mockup</a>.
</div>
// Inspired by this mockup
// https://uimovement.com/ui/5963/input/
body {
padding: 100px 10px;
font-family: Helvetica, Arial, sans-serif;
font-size: 20px;
}
input {
font-family: Helvetica, Arial, sans-serif;
font-size: 20px;
}
svg {
overflow: visible;
}
.KickInput {
height: 60px;
width: 300px;
position: relative;
margin: 0 auto;
}
.KickInput-svg {
height: 42px;
width: 80px;
display: block;
position: absolute;
bottom: 0;
left: 0;
z-index: 0;
}
.KickInput-kicker {
transition: stroke 500ms;
.KickInput-input:focus + label + svg & {
stroke: #3978F7;
}
}
.KickInput-input {
height: 60px;
width: 300px;
display: block;
position: relative;
z-index: 10;
border: 0;
background: none;
outline: 0;
Color: #232428;
}
.KickInput-label {
position: absolute;
bottom: 14px;
left: 0;
transition: transform 250ms cubic-bezier(0.145, 1.625, 0.715, 1.515), color 250ms;
transform-origin: top left;
color: #C9CBCE;
.KickInput-input:focus + &,
.KickInput-input--hasValue + & {
transform: translateY(-35px) scale(0.7);
}
.KickInput-input:focus + & {
color: #3978F7;
}
}
svg {
display: block;
}
// Info
.Info {
text-align: center;
margin-top: 100px;
color: rgba(black, 0.2);
font-size: 14px;
line-height: 22px;
a {
color: rgba(black, 0.2);
text-decoration: none;
border-bottom: thin solid rgba(black, 0.2);
&:hover {
color: rgba(black, 0.3);
border-bottom-color: rgba(black, 0.3);
}
}
}
View Compiled
// M0,40 C16,40
// M5,20 C16,35
// M10,0 C16,30
// a b c
// a 0 - 10
// b 40 - 0
// c 40 - 30
const endOfPath = '50,40 80,40 L300,40';
const aStart = 0;
const aEnd = 10;
const bStart = 40;
const bEnd = 0;
const cStart = 40;
const cEnd = 30;
const aStep = 1.1;
const bStep = -4 * aStep;
const cStep = -aStep;
let a = aStart;
let b = bStart;
let c = cStart;
const kicker = document.querySelector('.KickInput-kicker');
const input = document.querySelector('.KickInput-input');
const FRAME_DURATION = 16.66; // 60fps
// If available we are using native "performance" API instead of "Date"
// Read more about it on MDN:
// https://developer.mozilla.org/en-US/docs/Web/API/Performance
const getTime = typeof performance === 'function' ? performance.now : Date.now;
// Initial time
let lastUpdate = null;
let direction = 1;
let stop = false;
function animate() {
const now = getTime();
// This is the main part
// We are checking how much time has passed since the last update
// and translating that to frames
const delta = (now - lastUpdate) / FRAME_DURATION;
// Updating scene logic
a += aStep * delta * direction;
b += bStep * delta * direction;
c += cStep * delta * direction;
if (a > aEnd) {
// Reverse direction
direction = -1;
a = aEnd;
b = bEnd;
c = cEnd;
} else if (a < aStart && direction === -1) {
// Stop animation
stop = true;
direction = 1;
a = aStart;
b = bStart;
c = cStart;
}
// Render updated scene
kicker.setAttribute('d', `M${ a },${ b } C16,${ c } ${ endOfPath }`);
// Update last updated time
lastUpdate = now;
if (!stop) {
requestAnimationFrame(animate);
}
}
input.addEventListener('focus', () => {
lastUpdate = getTime();
stop = false;
if (input.value.trim() === '') {
animate();
}
});
input.addEventListener('blur', () => {
if (input.value.trim() === '') {
input.setAttribute('class', 'KickInput-input');
} else {
input.setAttribute('class', 'KickInput-input KickInput-input--hasValue');
}
});
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.