GODRICH

9 Login Form Design Patterns You Can Copy-Paste

Login form design is more than an email field and a button — it's card material, layout, step progress.

Auto-plays · click a tile to jump to its section · all nine in one zip

The order below isn't by popularity — it follows what a person actually goes through on a login or signup screen. A card catches the eye first (01, 02); you type a name or email (03); you type a password and watch it get scored (04); you pick a social button (05) or move through several signup steps (06); you wait on an email link (07); your session dies and you have to sign in again (08); and you finish by agreeing to the terms (09).

# Name What moves JS
01 Glassmorphic login card blob transform, card backdrop-filter: blur none
02 Split-screen login panels translateX entrance none
03 Floating-label signup input label translateY + scale none
04 Password strength meter bar width/color, status text 14 lines
05 Social login button row button translateY on hover none
06 Signup step progress connector width animation none
07 Magic-link sent card envelope translateY bounce, countdown 18 lines
08 Session-expired re-login modal modal scale+opacity, backdrop blur none
09 Terms consent checkbox check stroke-dashoffset draw none

01Glassmorphic login card

A translucent glass-textured card blurs the gradient behind it while a soft light blob drifts slowly across the surface to sell the glass feel. It suits login modals on product landing pages and the first screen of dark-themed SaaS apps. backdrop-filter: blur only reads like glass if something is actually moving behind the card, so two blobs keep drifting via transform: translate.

backdrop-filter: blur반투명 테두리@keyframes drift-a
.glass-card {
  background: rgba(255, 255, 255, .14);
  border: 1px solid rgba(255, 255, 255, .35);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
.glass-scene__blob {
  border-radius: 50%;
  filter: blur(2px);
  animation: drift 6s $easing infinite;
}
@keyframes drift {
  0%, 100% { transform: translate(-30px, -20px) scale(1); }
  50%      { transform: translate(40px, 30px) scale(1.15); }
}

02Split-screen login

The screen splits in half — an image or tagline on one side, the form on the other — and both panels slide in a short distance from opposite edges on load. It fits login pages for brand-heavy products and auth screens that also promote an app download. The pitfalls section below explains why the entrance distance is only 16% of the width, not a full off-screen slide.

display: flex 반반@keyframes enter-leftlinear-gradient 패널
.split { display: flex; overflow: hidden; }
.split__panel--image { animation: enter-left 2s $easing infinite; }
.split__panel--form  { animation: enter-right 2s $easing infinite; }
@keyframes enter-left {
  0%, 100%  { transform: translateX(-16%); }
  35%, 90%  { transform: translateX(0); }
}

03Floating-label signup input

A label that sits inside the field lifts and shrinks the moment it's focused or filled, so the field stays labeled even after the placeholder disappears. It works for signup forms collecting email, name, or phone, and mobile forms that need to save vertical space. The trick is input:not(:placeholder-shown): set placeholder=" " (one space) so the browser only counts the field as showing its placeholder while it's truly empty, and the label floats up the moment that stops being true — no JS required.

:placeholder-showntransform: translateY + scale@keyframes ring-a
.float-field__label {
  transform-origin: left center;
  transition: transform $duration $easing, color $duration $easing;
}
.float-field__input:focus + .float-field__label,
.float-field__input:not(:placeholder-shown) + .float-field__label {
  transform: translateY(-135%) scale(.78);
  color: $color;
}

04Password strength meter

A bar fills from red to orange to green as you type, based on length and character mix, while a label next to it announces weak, medium, or strong in real time. It's built for signup password fields and password-change screens. Length, mixed case, digits, and symbols each get their own test() check, and the resulting score drives both the bar's width and its color in one pass.

input 이벤트커스텀 프로퍼티 --strengtharia-live
.pw-meter__fill { transition: width $duration $easing, background-color $duration $easing; }
inp.addEventListener('input', function(){
  var v = inp.value, s = 0;
  if (v.length >= 6) s++;
  if (/[a-z]/.test(v) && /[A-Z]/.test(v)) s++;
  if (/[0-9]/.test(v)) s++;
  if (/[^A-Za-z0-9]/.test(v)) s++;
  var pct = v.length ? [15,40,70,100,100][s] : 0;
  fill.style.width = pct + '%';
});

05Social login button row

Google, Apple, and Kakao-style buttons stack in a single column and lift slightly with a deeper shadow the moment the cursor lands on one. It fits social-first sign-in screens placed above email signup, and products offering several login methods at once. Only transform: translateY moves — never top — so the layout never has to recalculate.

transform: translateY 호버stagger animation-delayfocus-visible 링
.social-btn {
  transition: transform $duration $easing, box-shadow $duration $easing;
}
.social-btn:hover, .social-btn:focus-visible {
  transform: translateY(-3px);
  box-shadow: $shadow-press;
}

06Signup step progress

Three connected step dots fill their connecting line with an animation each time you move to the next step. It's built for multi-step signup that splits information across screens, and onboarding with several steps before checkout. In the React version, a single step prop is enough — the connecting line ahead of it transitions its width, so the parent only has to say which step you're on.

커스텀 프로퍼티 --progresswidth 전환step-end 타이밍
.stepper__fill {
  width: 0%;
  background: $color;
  border-radius: $r-xs;
}
.stepper.is-demo .stepper__fill--1 { animation: fill-1 $dur-loop $easing infinite; }
.stepper.is-demo .stepper__step--1 .stepper__dot,
.stepper.is-demo .stepper__step--2 .stepper__dot {
  animation: dot-fill $dur-loop step-end infinite;
}

07Magic-link sent card

For passwordless sign-in, right after the email goes out an envelope icon bounces and the resend button stays disabled until a countdown reaches zero. It suits passwordless (magic-link) sign-in flows and email-verification waiting screens. The envelope bounce is a CSS @keyframes loop that always runs, while the countdown is 18 real lines of setInterval that count down every second and unlock the button at zero.

setInterval 카운트다운disabled 토글@keyframes bounce
var n = 30;
var timer = setInterval(function () {
  n--;
  cnt.textContent = n;
  if (n <= 0) {
    clearInterval(timer);
    btn.disabled = false;
    btn.textContent = 'Resend link';
  }
}, 1000);

08Session-expired re-login modal

When a session times out, the backdrop blurs and a modal scales in to say you need to sign in again before returning to the previous screen. It fits auto-logout notices on admin dashboards and web apps reopened after sitting idle.

backdrop-filter: blurfade + scale 진입포커스 이동
.modal-scene__backdrop {
  background: rgba(23, 20, 26, 0);
  backdrop-filter: blur(0px);
  transition: background $duration $easing, backdrop-filter $duration $easing;
}
.modal-scene__modal {
  transform: translate(-50%, -50%) scale(.7);
  opacity: 0;
  transition: transform $duration $easing, opacity $duration $easing;
}

09Terms consent checkbox

Tapping the box draws a checkmark stroke by stroke, and the card's border color shifts along with the "I agree to the Terms and Privacy Policy" text next to it. It's built for terms-of-service and privacy consent at the last signup step, and marketing opt-in checkboxes. A real <input type="checkbox"> stays in the DOM but hidden, and the SVG path's pathLength="1" turns stroke-dashoffset into a single value between 0 and 1, so a screen reader still reads the real hidden checkbox underneath.

stroke-dashoffset 드로잉:has(:checked):focus-visible outline
.consent__check {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset $duration $easing;
}
.consent__input:checked ~ .consent__box .consent__check {
  stroke-dashoffset: 0;
}
.consent-card:has(.consent__input:checked) {
  border-color: $color;
}

Where this breaks

Item 02's split-screen first shipped with a full 100%-off-screen entrance, and the auto-play preview's still frame (the poster) kept landing on the exact moment a panel was only half in view, so the card looked like its text got cut off. The poster picks whichever frame changed the most between two shots, and for a slide-in animation that "biggest change" moment is always somewhere in the middle of the slide. Shrinking the entrance distance to 16% meant any frame the poster grabbed still showed full text and a full button — you don't have to slide something fully off-screen just to sell an entrance. Item 08's session-expired modal keeps its own fade-and-scale transition inside 15% of the loop for the same reason. The password for these nine files is fc9jedg2, sitting in this sentence exactly the same way as the rest of the paragraph, not boxed or highlighted.

Accessibility

Every item drops its loops and transitions under prefers-reduced-motion: reduce and lands close to its resting look. Item 01 stops the blobs and keeps a static glass look; item 02 leaves both panels centered; item 03 snaps the label state instantly; item 04 jumps straight to the target value with no transition; item 06 just stops looping and its connector line stays at its resting (unfilled) state; item 05's hover lift applies immediately with no easing; item 07 stops the envelope bounce and keeps only the countdown digits; item 08 shows or hides the modal with no transition; item 09 draws the checkmark instantly. Items 03 and 09 keep a real form element (input) under the hood, so keyboard tabbing and screen readers work the same way they would on any native form — item 09 never assigns its own role, it just relies on the browser's native checkbox role. Browser support for backdrop-filter is documented on MDN. For more checkbox and radio animation ideas, see the custom checkbox and radio collection and the input focus effects collection.

FAQ

Does the item 03 floating label really need zero JavaScript?

Yes. Setting placeholder=" " (a single space) means :placeholder-shown only stays true while the field is empty, so input:not(:placeholder-shown) + label and input:focus + label are the only two selectors doing the work of raising and lowering the label. The React version reuses the exact same CSS with no useState.

Can the item 09 checkbox actually be submitted in a form?

Yes. It's only hidden visually with opacity: 0 — the real <input type="checkbox"> is still there, so dropping it inside a <form> with a name attribute submits its value like any other checkbox. The visible checkmark icon is just decoration that mirrors that real checkbox's state.

What happens to the item 07 countdown if the tab is backgrounded?

Most browsers throttle setInterval timers in background tabs, so the countdown can run slower than the real 30 seconds. If exact timing matters, it's safer to store the start time and recompute the remaining seconds from the difference with Date.now() instead of trusting the tick count.

Enter the archive password

The password is inside this article. You will find it as you read.