Adding Show/Hide Password to Custom Login Pages

If you have customized your PeopleFluent LMS login page to include a show/hide password toggle, review each section below and apply the changes that apply to your implementation.

1. HTML Markup

Your password field and toggle button must be wrapped in a container that establishes a positioning context for the button. The toggle button must immediately follow the password input as a sibling element so that CSS scoping and the :has() selector work correctly.

Standard login page (login.wm)

The password input and toggle button must be inside the existing .group div:

<div class="group">
    <input name="PWD" type="password" placeholder="Password" id="PWD"/>
    <button type="button" id="togglePassword" class="toggle-button" aria-label="Show password">
        <svg id="eye-icon-show" xmlns="http://www.w3.org/2000/svg" fill="none"
             viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="20" height="20">
            <!-- eye open path -->
        </svg>

        <svg id="eye-icon-hide" class="hidden" xmlns="http://www.w3.org/2000/svg" fill="none"
             viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="20" height="20">
            <!-- eye-slash path -->
        </svg>
    </button>
</div>

Key points:

  • The “show” icon (eye-icon-show) has no hidden class initially — it is visible when the password is masked.
  • The “hide” icon (eye-icon-hide) has the hidden class initially — it is hidden until the password is revealed.
  • Both id values on the SVGs (eye-icon-show, eye-icon-hide) are required by the JavaScript.

2. CSS

Standard login page — login.css

Add the following rules at the end of login.css. Do not remove the existing input[type="text"] and input[type="password"] rules already in the file

/* Positioning context for the toggle button */
#login-box .group {
    position: relative;
    width: 100%;
}

/* Reserve space for the toggle button - only on inputs that have one */
#login-box .group input:has(+ .toggle-button) {
    width: 100%;
    padding-right: 40px;
    box-sizing: border-box;
}

/* Toggle button placement */
#login-box .group .toggle-button {
    position: absolute;
    top: 50%;
    right: 10px;
    transform: translateY(-50%);
    background: transparent;
    border: none;
    padding: 0;
    cursor: pointer;
    display: flex;
    align-items: center;
    color: #6b7280;
}

/* Icon sizing */
#login-box .group .toggle-button svg {
    width: 20px;
    height: 20px;
    display: block;
}

/* Hide inactive icon - scoped to avoid affecting other elements */
#login-box .group .toggle-button svg.hidden {
    display: none;
}

/* General utility - restore if your stylesheet previously included this */
.hidden {
    display: none !important;
}

 

input:has(+ .toggle-button) note: This selector targets only the input immediately followed by the toggle button, so other inputs in .group (e.g., the User ID field) are not affected. This selector is supported in all modern browsers. If you must support IE11, replace it with a dedicated class on the password input (e.g., input.password-input) and style that instead.

 

3. JavaScript

Add the following script block, or update your existing toggle script to match. The logic below is ES5-compatible (uses var, not const/let) so it does not break in legacy browsers or interfere with other scripts sharing the same <script> tag.

Replace any previous toggle implementation with this:

// Show/Hide password toggle

document.addEventListener('DOMContentLoaded', function() {
    var passwordInput = document.getElementById('password'); // use 'PWD' for standard login
    var toggleButton  = document.getElementById('togglePassword');
    var iconShow      = document.getElementById('eye-icon-show');
    var iconHide      = document.getElementById('eye-icon-hide');

    // Guard: bail out cleanly if any element is missing
    if (!passwordInput || !toggleButton || !iconShow || !iconHide) {
        console.error("Error: Could not find password toggle elements.");
        return;
    }

    // Updates aria-label and aria-pressed to reflect current state
    function updatePasswordToggleState() {
        var passwordShown = passwordInput.getAttribute('type') === 'text';
        toggleButton.setAttribute('aria-label',  passwordShown ? 'Hide password' : 'Show password');
        toggleButton.setAttribute('aria-pressed', passwordShown ? 'true' : 'false');
    }

    // Associate button with input for screen readers
    toggleButton.setAttribute('aria-controls', 'PWD'); // use 'PWD' for standard login

    // Set correct initial state (matches the initial type="password" in the markup)
    updatePasswordToggleState();

    toggleButton.addEventListener('click', function() {
        var newType = passwordInput.getAttribute('type') === 'password' ? 'text' : 'password';
        passwordInput.setAttribute('type', newType);
        updatePasswordToggleState();

        // Set icon visibility deterministically - do not use classList.toggle()
        if (newType === 'text') {
            iconShow.classList.add('hidden');
            iconHide.classList.remove('hidden');
        } else {
            iconShow.classList.remove('hidden');
            iconHide.classList.add('hidden');
        }
    });
});

 

Standard login page note: The password input ID is PWD, not password. Update both getElementById calls and the aria-controls value accordingly.

4. Common Mistakes to Avoid

IssueSymptomFix
CSS scoped to input[type="password"]Toggle button overlaps text after revealing passwordUse input:has(+ .toggle-button) or a dedicated class on the input
Using classList.toggle()Icons desync if DOM initial state ever driftsUse explicit classList.add/remove driven by the computed new type
Using const/let in a shared <script> blockEntire script block fails to parse in IE11, breaking loginUse var throughout the toggle script
passwordInput not in the guard clauseRuntime error if the password field is renamed or removedInclude all four element references in the null check
Removing the global .hidden utility classOther elements using class="hidden" become visibleKeep .hidden { display: none !important; } in the stylesheet
Using top: 42% for the toggle buttonButton drifts off-center across font sizes or input heightsUse top: 50% with transform: translateY(-50%)

 

Was this article helpful?

0 out of 0 found this helpful