Skip to main content

Reset password form template

Overview

The reset password form template (lost_password_reset_form.php) is where users enter their new password after clicking the reset link in their email. This template includes real-time password validation, visual feedback for password requirements, and an optional password visibility toggle.

File location: templates/lost_password_reset_form.php

Stage: 2 (Reset)

When displayed: When users click the reset link in their email. The URL contains somresetpass=true, key, and uid parameters.

Template structure

<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}

// Get design settings for eye toggle
$design_options = get_option( 'somfrp_design_settings' );
$somfrp_enable_eye = ( ! isset( $design_options['somfrp_enable_eye_toggle'] )
|| $design_options['somfrp_enable_eye_toggle'] === 'on' );
?>

<div id="password-lost-form-wrap">
<!-- Error messages -->

<form id="resetpasswordform" method="post">
<fieldset>
<legend><?php echo $form_title; ?></legend>
<!-- Form text -->
<!-- New password input with eye toggle -->
<!-- Confirm password input with eye toggle -->
<!-- Password requirements list -->
<!-- Hidden fields (nonce, action, key, uid) -->
<!-- Submit button -->
</fieldset>
</form>
</div>

<!-- JavaScript for validation and eye toggle -->

Available variables

The template receives these variables from the plugin:

$form_title

Type: String

Source: General settings (somfrp_form_title)

Default: "Reset Password"

Purpose: Heading displayed at the top of the form

Usage: <legend><?php echo $form_title; ?></legend>

$reset_text_output

Type: String (HTML allowed)

Source: General settings (somfrp_reset_password_message)

Default: "Enter your new password below."

Purpose: Instruction text explaining what users should do

Usage: <?php echo $reset_text_output; ?>

$button_text

Type: String

Source: General settings (somfrp_button_text)

Default: "Reset Password"

Purpose: Label for the submit button

Usage: <button type="submit"><?php echo $button_text; ?></button>

$errors

Type: Array or null

Source: Form validation errors

Purpose: Error messages to display if validation fails

Usage:

<?php if ( ! empty( $errors ) && is_array( $errors ) ) : ?>
<?php foreach ( $errors as $error ) : ?>
<p class="som-password-error-message">
<?php echo esc_html( $error ); ?>
</p>
<?php endforeach; ?>
<?php endif; ?>

$sec_options

Type: Array

Source: Security settings (somfrp_security_settings)

Purpose: Contains password requirement settings

Keys:

  • somfrp_min_length - Minimum password length (default: 8)
  • somfrp_require_lowercase - Require lowercase letter (on/off)
  • somfrp_require_uppercase - Require uppercase letter (on/off)
  • somfrp_require_number - Require number (on/off)
  • somfrp_require_special - Require special character (on/off)

Usage: Used to display password requirements list

$somfrp_enable_eye

Type: Boolean

Source: Design settings (somfrp_enable_eye_toggle)

Default: true

Purpose: Controls whether password visibility toggle buttons are displayed

Usage: Conditional display of eye toggle buttons

Form fields

New Password Input

Field name: som_new_user_pass

Field ID: som_new_user_pass

Type: Password input

Required: Yes

Autocomplete: new-password

Purpose: User enters their new password

HTML:

<input 
type="password"
name="som_new_user_pass"
id="som_new_user_pass"
class="som-password-input"
required
autocomplete="new-password"
>

Confirm Password Input

Field name: som_new_user_pass_again

Field ID: som_new_user_pass_again

Type: Password input

Required: Yes

Autocomplete: new-password

Purpose: User confirms their new password

HTML:

<input 
type="password"
name="som_new_user_pass_again"
id="som_new_user_pass_again"
class="som-password-input"
required
autocomplete="new-password"
>

Hidden Fields

Nonce field:

<?php wp_nonce_field( 'somfrp_reset_pass', 'somfrp_nonce' ); ?>

Action identifier:

<input type="hidden" name="somfrp_action" value="somfrp_reset_pass">

Reset key (from URL):

<input type="hidden" name="somfrp_key" value="<?php echo esc_attr( $_GET['key'] ); ?>">

User ID (from URL):

<input type="hidden" name="somfrp_uid" value="<?php echo esc_attr( $_GET['uid'] ); ?>">
warning

Do not remove or modify hidden fields. They are required for form processing, security validation, and user identification.

Password requirements display

The template displays a dynamic list of password requirements based on security settings:

<ul class="password-requirements">
<?php if ( isset( $sec_options['somfrp_min_length'] ) ) : ?>
<li class="requirement" data-requirement="length">
At least <?php echo esc_html( $sec_options['somfrp_min_length'] ); ?> characters
</li>
<?php endif; ?>

<?php if ( isset( $sec_options['somfrp_require_lowercase'] ) && $sec_options['somfrp_require_lowercase'] === 'on' ) : ?>
<li class="requirement" data-requirement="lowercase">
One lowercase letter
</li>
<?php endif; ?>

<?php if ( isset( $sec_options['somfrp_require_uppercase'] ) && $sec_options['somfrp_require_uppercase'] === 'on' ) : ?>
<li class="requirement" data-requirement="uppercase">
One uppercase letter
</li>
<?php endif; ?>

<?php if ( isset( $sec_options['somfrp_require_number'] ) && $sec_options['somfrp_require_number'] === 'on' ) : ?>
<li class="requirement" data-requirement="number">
One number
</li>
<?php endif; ?>

<?php if ( isset( $sec_options['somfrp_require_special'] ) && $sec_options['somfrp_require_special'] === 'on' ) : ?>
<li class="requirement" data-requirement="special">
One special character
</li>
<?php endif; ?>

<li class="requirement" data-requirement="match">
Passwords match
</li>
</ul>

Each requirement has a data-requirement attribute used by JavaScript for validation feedback.

Real-time validation

The template includes JavaScript that validates passwords as users type:

document.addEventListener('DOMContentLoaded', function() {
const newPass = document.getElementById('som_new_user_pass');
const confirmPass = document.getElementById('som_new_user_pass_again');

function validatePassword() {
const password = newPass.value;
const confirmPassword = confirmPass.value;

// Check length
const lengthReq = document.querySelector('[data-requirement="length"]');
if (lengthReq) {
const minLength = parseInt(lengthReq.textContent.match(/\d+/)[0]);
if (password.length >= minLength) {
lengthReq.classList.add('valid');
lengthReq.classList.remove('invalid');
} else {
lengthReq.classList.add('invalid');
lengthReq.classList.remove('valid');
}
}

// Check lowercase
const lowercaseReq = document.querySelector('[data-requirement="lowercase"]');
if (lowercaseReq) {
if (/[a-z]/.test(password)) {
lowercaseReq.classList.add('valid');
lowercaseReq.classList.remove('invalid');
} else {
lowercaseReq.classList.add('invalid');
lowercaseReq.classList.remove('valid');
}
}

// Similar checks for uppercase, number, special character
// ...

// Check passwords match
const matchReq = document.querySelector('[data-requirement="match"]');
if (matchReq) {
if (password && confirmPassword && password === confirmPassword) {
matchReq.classList.add('valid');
matchReq.classList.remove('invalid');
} else if (confirmPassword) {
matchReq.classList.add('invalid');
matchReq.classList.remove('valid');
}
}
}

newPass.addEventListener('input', validatePassword);
confirmPass.addEventListener('input', validatePassword);
});

Password visibility toggle

If enabled in design settings, the template includes eye toggle buttons:

<?php if ( $somfrp_enable_eye ) : ?>
<button type="button" class="somfrp-eye-toggle" data-target="som_new_user_pass">
<span class="somfrp-eye">👁</span>
<span class="somfrp-eye-off" style="display:none;">👁‍🗨</span>
</button>
<?php endif; ?>

JavaScript handles the toggle functionality:

document.querySelectorAll('.somfrp-eye-toggle').forEach(function(button) {
button.addEventListener('click', function() {
const targetId = this.getAttribute('data-target');
const input = document.getElementById(targetId);
const eyeOn = this.querySelector('.somfrp-eye');
const eyeOff = this.querySelector('.somfrp-eye-off');

if (input.type === 'password') {
input.type = 'text';
eyeOn.style.display = 'none';
eyeOff.style.display = 'inline';
} else {
input.type = 'password';
eyeOn.style.display = 'inline';
eyeOff.style.display = 'none';
}
});
});

CSS classes and IDs

Main wrapper

ID: #password-lost-form-wrap

Purpose: Container for entire form and messages

Form

ID: #resetpasswordform

Purpose: Main form element

Password inputs

Class: .som-password-input

Purpose: Styling for password input fields

Requirements list

Class: .password-requirements

Purpose: Container for requirements list

Item class: .requirement

Valid state: .requirement.valid

Invalid state: .requirement.invalid

Eye toggle

Button class: .somfrp-eye-toggle

Eye icon: .somfrp-eye

Eye-off icon: .somfrp-eye-off

Messages

Error message: .som-password-error-message

Form processing flow

  1. User clicks email link: URL contains reset key and user ID
  2. Plugin validates key: Checks key is valid and not expired
  3. Template loads: Displays password entry form
  4. User enters passwords: Types in both fields
  5. JavaScript validates: Real-time feedback on requirements
  6. User submits form: Clicks submit button
  7. Plugin validates server-side:
    • Verifies nonce
    • Validates reset key again
    • Checks password meets requirements
    • Verifies passwords match
  8. On success:
    • Updates user password using reset_password()
    • Redirects to completion page with som_password_reset=true
  9. On error:
    • Reloads form with $errors array populated
    • Displays error messages

Customization examples

Change requirements display

<div class="password-strength-meter">
<ul class="requirements-list">
<!-- Custom requirement display -->
</ul>
</div>

Add password strength indicator

function calculateStrength(password) {
let strength = 0;
if (password.length >= 8) strength++;
if (password.length >= 12) strength++;
if (/[a-z]/.test(password)) strength++;
if (/[A-Z]/.test(password)) strength++;
if (/[0-9]/.test(password)) strength++;
if (/[^a-zA-Z0-9]/.test(password)) strength++;

return strength;
}

// Display strength meter
const strength = calculateStrength(password);
strengthMeter.style.width = (strength / 6 * 100) + '%';

Customize eye toggle icons

Replace emoji icons with Font Awesome or custom SVG:

<button type="button" class="somfrp-eye-toggle">
<i class="fa fa-eye somfrp-eye"></i>
<i class="fa fa-eye-slash somfrp-eye-off" style="display:none;"></i>
</button>

Add help tooltips

<div class="password-field-wrapper">
<input type="password" name="som_new_user_pass" id="som_new_user_pass">
<span class="help-tooltip" title="Choose a strong, unique password">
<i class="fa fa-question-circle"></i>
</span>
</div>

Common issues

Validation doesn't work

Problem: Requirements list doesn't update as user types.

Causes:

  • JavaScript error in console
  • Changed input IDs or requirement data attributes
  • JavaScript not loaded

Solution: Check browser console for errors. Verify input IDs match JavaScript selectors. Ensure JavaScript is at bottom of template.

Eye toggle doesn't work

Problem: Clicking eye icon doesn't show/hide password.

Causes:

  • JavaScript error
  • Changed button class or data-target attribute
  • CSS hiding the button

Solution: Check console for errors. Verify button has somfrp-eye-toggle class and correct data-target. Check CSS isn't hiding button.

Form submits with invalid password

Problem: Form submits even when requirements aren't met.

Causes:

  • Client-side validation is only visual feedback
  • Server-side validation is the actual enforcement
  • JavaScript disabled

Solution: This is expected behavior. Server-side validation will catch invalid passwords. JavaScript validation is for user experience only.

Reset key expired

Problem: Form displays "Invalid or expired reset key" error.

Causes:

  • User waited too long to click email link
  • Reset key already used
  • Key parameter corrupted in URL

Solution: User must request a new password reset. Keys expire after 24 hours by default (WordPress core behavior).

Accessibility considerations

Labels

Always include labels for password fields:

<label for="som_new_user_pass">
<?php _e( 'New Password', 'frontend-reset-password' ); ?>
</label>

ARIA attributes

Add ARIA attributes for screen readers:

<input 
type="password"
id="som_new_user_pass"
aria-label="<?php esc_attr_e( 'New Password', 'frontend-reset-password' ); ?>"
aria-describedby="password-requirements"
aria-required="true"
>

<ul id="password-requirements" class="password-requirements">
<!-- Requirements list -->
</ul>

Live regions for validation

Use ARIA live regions for validation feedback:

<div role="status" aria-live="polite" aria-atomic="true">
<span class="sr-only" id="validation-status"></span>
</div>

<script>
// Update validation status for screen readers
function updateValidationStatus() {
const validCount = document.querySelectorAll('.requirement.valid').length;
const totalCount = document.querySelectorAll('.requirement').length;
document.getElementById('validation-status').textContent =
validCount + ' of ' + totalCount + ' requirements met';
}
</script>

Keyboard navigation

Ensure eye toggle buttons are keyboard accessible:

<button 
type="button"
class="somfrp-eye-toggle"
aria-label="<?php esc_attr_e( 'Toggle password visibility', 'frontend-reset-password' ); ?>"
tabindex="0"
>

Security best practices

Always escape output

// Good
<?php echo esc_html( $sec_options['somfrp_min_length'] ); ?>
<?php echo esc_attr( $_GET['key'] ); ?>

// Bad
<?php echo $sec_options['somfrp_min_length']; ?>
<?php echo $_GET['key']; ?>

Preserve nonce and key validation

Never remove or modify security fields:

<?php wp_nonce_field( 'somfrp_reset_pass', 'somfrp_nonce' ); ?>
<input type="hidden" name="somfrp_key" value="<?php echo esc_attr( $_GET['key'] ); ?>">
<input type="hidden" name="somfrp_uid" value="<?php echo esc_attr( $_GET['uid'] ); ?>">

Use autocomplete attributes

Help password managers:

<input autocomplete="new-password">

Don't weaken validation

Don't modify JavaScript to allow weak passwords. Server-side validation will reject them anyway.

Testing checklist

After customizing this template:

  • Form displays correctly on desktop
  • Form displays correctly on mobile
  • Password requirements list displays correctly
  • Requirements update in real-time as user types
  • Eye toggle shows/hides password (if enabled)
  • Both password fields work correctly
  • Form submits with valid password
  • Form rejects invalid password with error message
  • Form rejects mismatched passwords
  • Form rejects expired reset keys
  • Form works with JavaScript disabled (server-side validation)
  • Screen readers can navigate the form
  • Keyboard navigation works (Tab, Enter, Space for toggle)

What's next