Add password visibility toggle to login form

- Implement show/hide password functionality with eye icon toggle button
- Add dynamic input type switching between password and text
- Position toggle button inside password input field with proper styling
- Include hover, focus states and accessibility features (tabindex, title)
- Use FontAwesome eye/eye-slash icons for visual feedback
- Maintain secure default (password hidden) with optional visibility
- Integrate proper tab order with existing form elements

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Connor Johnstone
2025-09-13 18:21:56 -04:00
parent 9d84c380d1
commit b307be7eb1
2 changed files with 66 additions and 10 deletions

View File

@@ -23,6 +23,9 @@ pub fn Login(props: &LoginProps) -> Html {
// Remember checkboxes state - default to checked
let remember_server = use_state(|| true);
let remember_username = use_state(|| true);
// Password visibility toggle
let show_password = use_state(|| false);
let server_url_ref = use_node_ref();
let username_ref = use_node_ref();
@@ -97,6 +100,13 @@ pub fn Login(props: &LoginProps) -> Html {
}
})
};
let on_toggle_password_visibility = {
let show_password = show_password.clone();
Callback::from(move |_| {
show_password.set(!*show_password);
})
};
let on_submit = {
let server_url = server_url.clone();
@@ -242,16 +252,27 @@ pub fn Login(props: &LoginProps) -> Html {
<div class="form-group">
<label for="password">{"Password"}</label>
<input
ref={password_ref}
type="password"
id="password"
placeholder="Enter your password"
value={(*password).clone()}
onchange={on_password_change}
disabled={*is_loading}
tabindex="3"
/>
<div class="password-input-container">
<input
ref={password_ref}
type={if *show_password { "text" } else { "password" }}
id="password"
placeholder="Enter your password"
value={(*password).clone()}
onchange={on_password_change}
disabled={*is_loading}
tabindex="3"
/>
<button
type="button"
class="password-toggle-btn"
onclick={on_toggle_password_visibility}
tabindex="6"
title={if *show_password { "Hide password" } else { "Show password" }}
>
<i class={if *show_password { "fas fa-eye-slash" } else { "fas fa-eye" }}></i>
</button>
</div>
</div>
{

View File

@@ -534,6 +534,41 @@ body {
font-weight: 400;
}
.password-input-container {
position: relative;
display: flex;
align-items: center;
}
.password-input-container input {
padding-right: 3rem !important;
}
.password-toggle-btn {
position: absolute;
right: 0.75rem;
background: transparent;
border: none;
color: #888;
cursor: pointer;
padding: 0.25rem;
font-size: 1rem;
display: flex;
align-items: center;
justify-content: center;
transition: color 0.2s ease;
z-index: 1;
}
.password-toggle-btn:hover {
color: #555;
}
.password-toggle-btn:focus {
outline: none;
color: #667eea;
}
.login-button, .register-button {
width: 100%;
padding: var(--control-padding);