Skip to main
Table of Contents

Password Interface

About

This demo shows a password input pattern with inline visual feedback for Shift and Caps Lock state, plus a press-and-hold show-password control. The implementation is plain JavaScript and uses page-scoped assets under content/example/password-interface.

It is useful when you want stronger login UX without framework dependencies.

Feedback Strategy

This page demonstrates two valid ways to convey password-state information: pure side icons and explanatory text below the field. Which one to use is a developer decision based on audience, accessibility goals, and UI density.

ApproachProsCons
Side Icons Only
  • Compact.
  • Fast to scan.
  • Minimal vertical space.
  • Can be less obvious for some users.
  • Relies more on icon meaning.
Explanatory Text Below
  • Explicit and clear.
  • Easier for accessibility.
  • No icon interpretation required.
  • Takes more space.
  • Can add visual noise in dense forms.

Examples

Password Input As Normal


Password Input With Feedback And Show Password Control

*iOS has its own Shift and CapsLock indicators in the keyboard so only the "Show Password" control is used on those devices. iOS also has it's own capslock indicator in the input field but it forgets the capslock state whenever the show password button is pressed/released, so that is hidden as well.

Simulating Autofill

This demo starts with a prefilled value to show first-interaction clearing behavior.

Security Hardening Notes

Client-Side (Demoed Here)

  • Password fields are armed as readonly and unlocked on trusted interaction.
  • First trusted focus clears prefilled value in the hardened password demo inputs.
  • Show-password is press-and-hold only, then automatically returns to masked state.
  • Password values are cleared on beforeunload and pagehide.
  • Autocomplete semantics use username + current-password for login context.

Server-Side (Required In Production)

  • Defend against XSS first: strict output escaping and strong CSP policy.
  • Use secure session cookies: HttpOnly, Secure, and SameSite.
  • Use CSRF protection for authenticated form posts.
  • Rate-limit login attempts and add lockout/step-up checks.
  • Never log raw passwords and never store reversible password data.

The Code

HTML

<div class="login login-feedback">
  <div class="login__input login__password clearfix">
    <label for="password-feedback">Password:</label>
    <div class="login__input__container login__password__icons-container">
      <input class="password__feedback" type="password" id="password-feedback">
      <div class="login__password__icons noselect">
        <div class="login__password__icon login__password__icons__shift login__password__icon--hidden" title="Shift key is active"></div>
        <div class="login__password__icon login__password__icons__capslock login__password__icon--hidden" title="Caps Lock is active"></div>
        <div class="login__password__icon login__password__icons__show login__password__icon--hidden" title="Press and hold to show password">
          <button class="login__password__icon" type="button" title="Press and hold to show password" aria-label="Press and hold to show password"></button>
        </div>
      </div>
    </div>
  </div>
</div>

CSS

.login__password__icons-container {
  position: relative;
}

.login__password__icons {
  position: absolute;
  right: 0;
  bottom: 0;
  height: 100%;
  pointer-events: none;
}

.login__password__icon {
  display: inline-block;
  height: 100%;
  background-position: center;
  background-repeat: no-repeat;
}

.login__password__icons__show button {
  width: 40px;
  height: 100%;
  pointer-events: auto;
  border: 0;
  background-color: transparent;
  cursor: pointer;
}

.login__password__icons .login__password__icons__shift::before {
  font-family: "font-icon--example-password-interface";
  color: #c59b48;
  content: "\\E003";
}

.login__password__icons .login__password__icons__capslock::before {
  font-family: "font-icon--example-password-interface";
  color: #c54a48;
  content: "\\E001";
}

.login__password__icons .login__password__icons__show button::before {
  font-family: "font-icon--example-password-interface";
  color: #3a87bd;
  content: "\\E002";
}

JavaScript

function armPasswordInput(input) {
  input.setAttribute("readonly", "readonly");

  function unlock(event) {
    if (!event || event.isTrusted !== false) input.removeAttribute("readonly");
  }

  input.addEventListener("focusin", unlock);
  input.addEventListener("pointerdown", unlock);
  input.addEventListener("keydown", unlock);
}

function clearPasswordValues() {
  document.querySelectorAll(".password__feedback").forEach(function (field) {
    field.value = "";
  });
}

window.addEventListener("beforeunload", clearPasswordValues);
window.addEventListener("pagehide", clearPasswordValues);

Full implementation files: in/asset/css/content/example/password-interface.scss and in/asset/js/content/example/password-interface.js.

Attribution

This page is comprised of my own additions and either partially or heavily modified elements from the following source(s):