<form id="login-form">
<svg class="logo">
<use xlink:href="#logo">
</svg>
<h1>Sign In</h1>
<div class="input email">
<input type="text" value="email@gmail.com" placeholder=" ">
<label>Email</label>
</div>
<div class="input password">
<div class="dots"></div>
<input type="password" placeholder=" ">
<label>Password</label>
<div class="cursor"></div>
<div class="line">
<svg>
<use xlink:href="#line">
</svg>
</div>
<div class="tick">
<svg>
<use xlink:href="#tick">
</svg>
</div>
</div>
<button type="submit" disabled>
<svg viewBox="0 0 16 16">
<circle stroke-opacity=".1" cx="8" cy="8" r="6"></circle>
<circle class="load" cx="8" cy="8" r="6"></circle>
</svg>
<span>Submit</span>
</button>
</form>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 44 44" id="logo">
<path d="M33.0457936,22 L44,22 C44,34.1502645 34.1912695,44 22.0915872,44 C16.0417461,44 10.5646429,41.5375661 6.6,37.5563492 L14.3462931,29.7786761 C16.3285751,31.7689899 19.0669207,33 22.0915872,33 C25.1013453,33 27.827598,31.7810952 29.8075146,29.8080513 L22,22 L33.0457936,22.001001 C33.0457936,22.0006673 33.0457936,22.0003337 33.0457936,22 Z M21.9084128,0 C27.958756,0 33.4362661,2.4628426 37.400987,6.44464202 L29.6552,14.2228233 C27.6728001,12.2316284 24.9338388,11 21.9084128,11 C15.8585716,11 10.9542064,15.9248678 10.9542064,22 L10.954,22 L0,22 C0,9.8497355 9.8087305,0 21.9084128,0 Z"></path>
</symbol>
<symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 900 22" id="line">
<path d="M0,11 L180,11 C240,11.00344 300,13.6718267 360,19.00516 C450,27.00516 450,-4.99483997 540,3.00516003 C600,8.33849336 660,11.00344 720,11 L900,11"></path>
</symbol>
<symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 28" id="tick">
<path d="M3,12.5026479 L7,16.5026479 L13,9.50264792 C29.6216402,-12.0066881 40.3541164,26.00516 19,26.0026479 L-3.37507799e-13,26.0026479"></path>
</symbol>
</svg>
<!-- dribbble - twitter -->
<a class="dribbble" href="https://dribbble.com/ai" target="_blank"><img src="https://cdn.dribbble.com/assets/dribbble-ball-mark-2bd45f09c2fb58dbbfb44766d5d1d07c5a12972d602ef8b32204d28fa3dda554.svg" alt=""></a>
<a class="twitter" target="_blank" href="https://twitter.com/aaroniker_me"><svg xmlns="http://www.w3.org/2000/svg" width="72" height="72" viewBox="0 0 72 72"><path d="M67.812 16.141a26.246 26.246 0 0 1-7.519 2.06 13.134 13.134 0 0 0 5.756-7.244 26.127 26.127 0 0 1-8.313 3.176A13.075 13.075 0 0 0 48.182 10c-7.229 0-13.092 5.861-13.092 13.093 0 1.026.118 2.021.338 2.981-10.885-.548-20.528-5.757-26.987-13.679a13.048 13.048 0 0 0-1.771 6.581c0 4.542 2.312 8.551 5.824 10.898a13.048 13.048 0 0 1-5.93-1.638c-.002.055-.002.11-.002.162 0 6.345 4.513 11.638 10.504 12.84a13.177 13.177 0 0 1-3.449.457c-.846 0-1.667-.078-2.465-.231 1.667 5.2 6.499 8.986 12.23 9.09a26.276 26.276 0 0 1-16.26 5.606A26.21 26.21 0 0 1 4 55.976a37.036 37.036 0 0 0 20.067 5.882c24.083 0 37.251-19.949 37.251-37.249 0-.566-.014-1.134-.039-1.694a26.597 26.597 0 0 0 6.533-6.774z"></path></svg></a>
#login-form {
--primary: #6D58FF;
--primary-dark: #362A89;
--dark: #2B3044;
--grey-dark: #404660;
--grey: #8A91B4;
--grey-light: #A6ACCD;
--light: #BBC1E1;
--pale: #ECEFFC;
--white: #FFFFFF;
--red: #F04949;
width: 100%;
max-width: 180px;
.logo {
width: 44px;
height: 44px;
display: block;
margin: 0 auto;
fill: var(--primary);
}
h1 {
margin: 24px 0;
font-family: inherit;
font-size: 20px;
font-weight: bold;
text-align: center;
color: var(--dark);
}
.input {
position: relative;
label {
pointer-events: none;
font-size: 12px;
font-weight: 500;
line-height: 1.5;
position: absolute;
top: 8px;
left: 0;
transform-origin: 0 50%;
transform: translateY(var(--label-y, 0)) scale(var(--label-scale, 1)) translateZ(0);
transition: transform .3s, color .3s;
color: var(--label-color, var(--grey-light));
}
input {
width: 100%;
border-radius: 0;
-webkit-appearance: none;
&:not(:placeholder-shown),
&:focus {
& + label {
--label-y: -17px;
--label-scale: .8;
--label-color: var(--grey);
}
}
}
&.email {
margin-bottom: 16px;
input {
background: none;
outline: none;
border: none;
color: var(--grey-dark);
font-weight: 500;
font-family: inherit;
font-size: 13px;
letter-spacing: .025em;
line-height: 22px;
caret-color: var(--primary);
padding: 7px 0 5px 0;
box-shadow: inset 0 -1px 0 0 var(--grey);
}
}
&.password {
height: 34px;
.dots {
position: absolute;
display: flex;
left: 0;
top: 50%;
transform: translateY(-2px);
i {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--grey-dark);
display: block;
margin-right: 4px;
animation: var(--name, scale-in) .05s linear forwards;
&.remove {
--name: scale-out;
}
$i: 1;
@while $i <= 15 {
$delay: 400 - 6.5 * $i;
&:nth-child(#{$i}) {
--delay: #{$delay}ms;
}
$i: $i + 1;
}
}
}
.cursor {
position: absolute;
height: 15px;
width: 1px;
left: 0;
top: 10px;
background: var(--primary);
opacity: 0;
transform: translateX(var(--cursor-x, 0));
transition: transform var(--cursor-duration, .1s);
}
input {
opacity: 0;
padding: 0;
position: absolute;
left: 0;
top: 0;
bottom: 0;
&:focus {
& + label + .cursor {
animation: cursor 1s ease infinite;
}
}
}
}
.line,
.tick {
pointer-events: none;
position: absolute;
left: var(--left, 0);
bottom: var(--bottom, -10px);
opacity: var(--opacity, 1);
transition: opacity 0s linear var(--tick-opacity-delay, .5s);
svg {
stroke: var(--line-stroke, var(--grey));
stroke-width: 1;
fill: none;
display: block;
width: var(--width, 900px);
height: var(--height, 22px);
transform: translateX(var(--x, var(--line-x, 0)));
transition: transform var(--line-x-duration, 0s) ease, stroke .3s;
}
}
.line {
overflow: hidden;
width: 100%;
transform-origin: 0 50%;
}
.tick {
--left: 100%;
--bottom: -1px;
--width: 32px;
--height: 28px;
--x: -16px;
--opacity: 0;
svg {
stroke: var(--tick-stroke, var(--grey));
stroke-dasharray: 17 81;
stroke-dashoffset: var(--tick-offset, 34px);
transition: stroke-dashoffset .5s ease var(--tick-delay, 0s), stroke .5s ease var(--tick-delay, 0s);
}
}
}
button {
border: none;
outline: none;
padding: 10px 0;
margin: 24px 0 0 0;
border-radius: 7px;
color: var(--c, #fff);
width: 100%;
font-size: 14px;
line-height: 16px;
position: relative;
font-family: inherit;
font-weight: 500;
background: var(--b, var(--primary-dark));
transition: color .3s, background .3s;
-webkit-appearance: none;
-webkit-tap-highlight-color: transparent;
cursor: pointer;
&:disabled {
--b: var(--pale);
--c: var(--grey-light);
cursor: not-allowed;
}
svg {
width: 16px;
height: 16px;
display: block;
position: absolute;
left: 50%;
top: 50%;
margin: -8px 0 0 -8px;
fill: none;
stroke: currentColor;
stroke-linecap: round;
stroke-width: 1.5;
opacity: var(--svg-o, 0);
transform: translateY(var(--svg-y, -16px)) rotate(-90deg) scale(var(--svg-s, .7)) translateZ(0);
transition: transform .3s, opacity .3s;
circle {
&:first-child {
stroke-opacity: .1;
}
&:last-child {
stroke-dasharray: 38px;
stroke-dashoffset: 114px;
}
}
}
span {
display: inline-block;
vertical-align: top;
line-height: 16px;
opacity: var(--span-o, 1);
transform: translateY(var(--span-y, 0)) scale(var(--span-s, 1)) translateZ(0);
transition: transform .3s, opacity .3s;
}
}
&.processing {
&:not(.error):not(.success) {
button {
--svg-y: 0;
--svg-s: 1;
--svg-o: 1;
--span-y: 16px;
--span-s: .7;
--span-o: 0;
svg {
circle {
&:last-child {
animation: load 1.2s linear .3s;
}
}
}
}
}
&.error {
--line-x: -80%;
--line-x-duration: 2s;
--line-stroke: var(--red);
--cursor-duration: 1s;
.dots {
i {
animation: flip .4s linear var(--delay) forwards;
}
}
}
&.success {
.input {
.tick {
--opacity: 1;
--tick-offset: 98px;
--tick-delay: .45s;
--tick-opacity-delay: 0s;
--tick-stroke: var(--primary);
animation: tick .4s linear forwards;
}
.line {
animation: line .5s linear forwards;
}
}
}
}
}
@keyframes tick {
70%,
80% {
transform: translateX(-10px);
}
}
@keyframes line {
70% {
transform: scaleX(.8);
}
}
@keyframes flip {
25% {
transform: translateY(8px);
}
35% {
transform: translateY(12px);
}
65% {
transform: translateY(-4px);
}
100% {
transform: translateY(-12px) scale(0);
}
}
@keyframes load {
from {
stroke-dashoffset: 114px;
}
to {
stroke-dashoffset: 38px;
}
}
@keyframes scale-in {
from {
transform: scale(0);
}
to {
transform: scale(1);
}
}
@keyframes scale-out {
from {
transform: scale(1);
}
to {
transform: scale(0);
}
}
@keyframes cursor {
50% {
opacity: 1;
}
}
html {
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
}
* {
box-sizing: inherit;
&:before,
&:after {
box-sizing: inherit;
}
}
// Center & dribbble
body {
min-height: 100vh;
display: flex;
font-family: 'Inter', 'Inter UI', Arial;
justify-content: center;
align-items: center;
background: #F6F8FF;
.dribbble {
position: fixed;
display: block;
right: 20px;
bottom: 20px;
img {
display: block;
height: 28px;
}
}
.twitter {
position: fixed;
display: block;
right: 64px;
bottom: 14px;
svg {
width: 32px;
height: 32px;
fill: #1da1f2;
}
}
}
View Compiled
// correct password is `password` lol
const $ = (s, o = document) => o.querySelector(s);
const $$ = (s, o = document) => o.querySelectorAll(s);
const login = $('#login-form');
const passwordContainer = $('.password', login);
const password = $('input', passwordContainer);
const passwordList = $('.dots', passwordContainer);
const submit = $('button', login);
password.addEventListener('input', e => {
if(password.value.length > $$('i', passwordList).length) {
passwordList.appendChild(document.createElement('i'));
}
submit.disabled = !password.value.length;
passwordContainer.style.setProperty('--cursor-x', password.value.length * 10 + 'px');
});
let pressed = false;
password.addEventListener('keydown', e => {
if(pressed || login.classList.contains('processing') || (password.value.length > 14 && e.keyCode != 8 && e.keyCode != 13)) {
e.preventDefault();
}
pressed = true;
setTimeout(() => pressed = false, 50);
if(e.keyCode == 8) {
let last = $('i:last-child', passwordList);
if(last !== undefined && last) {
last.classList.add('remove');
setTimeout(() => last.remove(), 50);
}
}
});
password.addEventListener('select', function() {
this.selectionStart = this.selectionEnd;
});
login.addEventListener('submit', e => {
e.preventDefault();
if(!login.classList.contains('processing')) {
login.classList.add('processing');
setTimeout(() => {
let cls = password.value == 'password' ? 'success' : 'error';
console.log(password.value);
login.classList.add(cls);
setTimeout(() => {
login.classList.remove('processing', cls);
if(cls == 'error') {
password.value = '';
passwordList.innerHTML = '';
submit.disabled = true;
}
}, 2000);
setTimeout(() => {
if(cls == 'error') {
passwordContainer.style.setProperty('--cursor-x', 0 + 'px');
}
}, 600);
}, 1500);
}
});
This Pen doesn't use any external JavaScript resources.