Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 30 additions & 4 deletions static/css/settings.css
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,7 @@
position: relative;
display: flex;
align-items: center;
width: 100%;
}

.settings-password-wrapper .settings-input {
Expand All @@ -624,21 +625,46 @@
.settings-password-toggle-btn {
position: absolute;
right: var(--s-space-8);
top: 50%;
transform: translateY(-50%);
background: transparent;
border: none;
color: var(--s-color-muted);
cursor: pointer;
padding: var(--s-space-6);
display: flex;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: var(--s-radius-sm);
min-width: 32px;
min-height: 32px;
width: 32px;
height: 32px;
transition: color 0.15s ease, background-color 0.15s ease;
}

.settings-password-toggle-btn:hover {
color: var(--s-color-text);
background-color: var(--s-color-hover);
}

.settings-password-toggle-btn:focus-visible {
outline: 2px solid var(--s-color-primary);
outline-offset: 2px;
}

.settings-password-toggle-btn .icon-eye {
display: block;
}

.settings-password-toggle-btn .icon-eye-off {
display: none;
}

.settings-password-toggle-btn[aria-pressed="true"] .icon-eye {
display: none;
}

.settings-password-toggle-btn[aria-pressed="true"] .icon-eye-off {
display: block;
}

/* Password Strength Meter */
Expand Down
64 changes: 64 additions & 0 deletions static/css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -1056,6 +1056,70 @@ a {
color: var(--text-dim);
}

/* Password Toggle Wrapper & Action Button (Auth Pages) */
.password-input-wrapper {
position: relative;
display: flex;
align-items: center;
width: 100%;
}

.password-input-wrapper .form-input {
padding-right: 2.75rem;
}

.password-toggle-btn {
position: absolute;
right: 0.5rem;
top: 50%;
transform: translateY(-50%);
display: inline-flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
padding: 0;
background: transparent;
border: none;
border-radius: var(--radius-xs);
color: var(--text-dim);
cursor: pointer;
transition: color 0.15s ease, background-color 0.15s ease;
}

.password-toggle-btn:hover {
color: var(--text-primary);
background-color: rgba(15, 23, 42, 0.05);
}

.password-toggle-btn:focus-visible {
outline: 2px solid var(--brand-navy);
outline-offset: 1px;
}

.password-toggle-icon {
width: 1.125rem;
height: 1.125rem;
stroke-width: 1.6;
pointer-events: none;
}

.password-toggle-btn .icon-eye {
display: block;
}

.password-toggle-btn .icon-eye-off {
display: none;
}

.password-toggle-btn[aria-pressed="true"] .icon-eye {
display: none;
}

.password-toggle-btn[aria-pressed="true"] .icon-eye-off {
display: block;
}

.alert-danger {
padding: 0.75rem 1rem;
border-radius: var(--radius-sm);
Expand Down
8 changes: 8 additions & 0 deletions static/img/icons/settings-sprite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions static/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
document.addEventListener('DOMContentLoaded', () => {
initStitchSimulator();
initVaultSearchShortcut();
initPasswordToggles();
});

function initVaultSearchShortcut() {
Expand Down Expand Up @@ -113,3 +114,25 @@ function initStitchSimulator() {
});
}
}

/**
* Show/Hide Password Toggle for Auth Pages
*/
function initPasswordToggles() {
const buttons = document.querySelectorAll('.password-toggle-btn');
buttons.forEach((btn) => {
btn.addEventListener('click', (e) => {
e.preventDefault();
const wrapper = btn.closest('.password-input-wrapper');
if (!wrapper) return;
const input = wrapper.querySelector('input');
if (!input) return;

const isPassword = input.type === 'password';
input.type = isPassword ? 'text' : 'password';
btn.setAttribute('aria-pressed', isPassword ? 'true' : 'false');
btn.setAttribute('aria-label', isPassword ? 'Hide password' : 'Show password');
});
});
}

1 change: 1 addition & 0 deletions static/js/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,7 @@ function initPasswordToggles() {

const isPassword = input.type === 'password';
input.type = isPassword ? 'text' : 'password';
btn.setAttribute('aria-pressed', isPassword ? 'true' : 'false');
btn.setAttribute('aria-label', isPassword ? 'Hide password' : 'Show password');
});
});
Expand Down
14 changes: 13 additions & 1 deletion templates/accounts/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,19 @@ <h1 class="auth-title">Access Personal Vault</h1>
<label for="login-password" class="form-label" style="margin-bottom: 0;">Password</label>
<a href="{% url 'password_reset' %}" class="auth-link" style="font-size: 0.8rem;" data-testid="forgot-password-link">Forgot Password?</a>
</div>
{{ form.password }}
<div class="password-input-wrapper">
{{ form.password }}
<button type="button" class="password-toggle-btn" aria-label="Show password" aria-controls="login-password" aria-pressed="false" tabindex="0">
<svg class="password-toggle-icon icon-eye" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
<circle cx="12" cy="12" r="3"></circle>
</svg>
<svg class="password-toggle-icon icon-eye-off" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path>
<line x1="1" y1="1" x2="23" y2="23"></line>
</svg>
</button>
</div>
{% if form.password.errors %}
<span class="form-error">{{ form.password.errors.0 }}</span>
{% endif %}
Expand Down
28 changes: 26 additions & 2 deletions templates/accounts/password_reset_confirm.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,19 @@ <h1 class="auth-title">Set New Master Passphrase</h1>

<div class="form-group">
<label for="reset-password1" class="form-label">New Master Passphrase</label>
{{ form.password1 }}
<div class="password-input-wrapper">
{{ form.password1 }}
<button type="button" class="password-toggle-btn" aria-label="Show password" aria-controls="reset-password1" aria-pressed="false" tabindex="0">
<svg class="password-toggle-icon icon-eye" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
<circle cx="12" cy="12" r="3"></circle>
</svg>
<svg class="password-toggle-icon icon-eye-off" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path>
<line x1="1" y1="1" x2="23" y2="23"></line>
</svg>
</button>
</div>
{% if form.password1.errors %}
<span class="form-error">{{ form.password1.errors.0 }}</span>
{% else %}
Expand All @@ -48,7 +60,19 @@ <h1 class="auth-title">Set New Master Passphrase</h1>

<div class="form-group">
<label for="reset-password2" class="form-label">Confirm New Passphrase</label>
{{ form.password2 }}
<div class="password-input-wrapper">
{{ form.password2 }}
<button type="button" class="password-toggle-btn" aria-label="Show password" aria-controls="reset-password2" aria-pressed="false" tabindex="0">
<svg class="password-toggle-icon icon-eye" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
<circle cx="12" cy="12" r="3"></circle>
</svg>
<svg class="password-toggle-icon icon-eye-off" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path>
<line x1="1" y1="1" x2="23" y2="23"></line>
</svg>
</button>
</div>
{% if form.password2.errors %}
<span class="form-error">{{ form.password2.errors.0 }}</span>
{% endif %}
Expand Down
28 changes: 26 additions & 2 deletions templates/accounts/register.html
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,19 @@ <h1 class="auth-title">Create Personal Vault</h1>

<div class="form-group">
<label for="reg-password1" class="form-label">Master Passphrase</label>
{{ form.password1 }}
<div class="password-input-wrapper">
{{ form.password1 }}
<button type="button" class="password-toggle-btn" aria-label="Show password" aria-controls="reg-password1" aria-pressed="false" tabindex="0">
<svg class="password-toggle-icon icon-eye" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
<circle cx="12" cy="12" r="3"></circle>
</svg>
<svg class="password-toggle-icon icon-eye-off" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path>
<line x1="1" y1="1" x2="23" y2="23"></line>
</svg>
</button>
</div>
{% if form.password1.errors %}
<span class="form-error">{{ form.password1.errors.0 }}</span>
{% else %}
Expand All @@ -63,7 +75,19 @@ <h1 class="auth-title">Create Personal Vault</h1>

<div class="form-group">
<label for="reg-password2" class="form-label">Confirm Master Passphrase</label>
{{ form.password2 }}
<div class="password-input-wrapper">
{{ form.password2 }}
<button type="button" class="password-toggle-btn" aria-label="Show password" aria-controls="reg-password2" aria-pressed="false" tabindex="0">
<svg class="password-toggle-icon icon-eye" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
<circle cx="12" cy="12" r="3"></circle>
</svg>
<svg class="password-toggle-icon icon-eye-off" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path>
<line x1="1" y1="1" x2="23" y2="23"></line>
</svg>
</button>
</div>
{% if form.password2.errors %}
<span class="form-error">{{ form.password2.errors.0 }}</span>
{% endif %}
Expand Down
Loading
Loading