mirror of
https://github.com/go-vikunja/vikunja.git
synced 2026-07-21 17:37:54 -05:00
fix(a11y): show the password hint visually but hide it from screen readers
Restores the "e.g. ••••" hint that the previous commit removed, as an aria-hidden overlay instead of a native placeholder — screen readers read native placeholders character by character. FormField gets a decorativePlaceholder prop for the link-share fields, and the link-share auth field gains the aria-label it never had (its placeholder used to be its only hint). Re-using the translation key also fixes the dead-key CI failure. Claude-Session: https://claude.ai/code/session_018prq5WqRL38EGhPNQKqZUP
This commit is contained in:
@@ -9,6 +9,10 @@ interface Props {
|
||||
disabled?: boolean
|
||||
loading?: boolean
|
||||
layout?: 'stacked' | 'two-col'
|
||||
// Shown like a placeholder but hidden from assistive technology —
|
||||
// screen readers read native placeholders character by character
|
||||
// for hints like "e.g. ••••••••••••".
|
||||
decorativePlaceholder?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
@@ -129,6 +133,13 @@ defineExpose({
|
||||
:aria-describedby="errorId"
|
||||
@input="handleInput"
|
||||
>
|
||||
<span
|
||||
v-if="decorativePlaceholder && !modelValue"
|
||||
class="decorative-placeholder"
|
||||
aria-hidden="true"
|
||||
>
|
||||
{{ decorativePlaceholder }}
|
||||
</span>
|
||||
</slot>
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -14,6 +14,13 @@
|
||||
@keyup="() => {validateAfterFirst ? validate() : null}"
|
||||
@input="handleInput"
|
||||
>
|
||||
<span
|
||||
v-if="password === ''"
|
||||
class="decorative-placeholder"
|
||||
aria-hidden="true"
|
||||
>
|
||||
{{ $t('user.auth.passwordPlaceholder') }}
|
||||
</span>
|
||||
<BaseButton
|
||||
v-tooltip="passwordFieldType === 'password' ? $t('user.auth.showPassword') : $t('user.auth.hidePassword')"
|
||||
class="password-field-type-toggle"
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
v-model="password"
|
||||
v-tooltip="$t('project.share.links.passwordExplanation')"
|
||||
:label="$t('project.share.links.password')"
|
||||
:decorative-placeholder="$t('user.auth.passwordPlaceholder')"
|
||||
type="password"
|
||||
/>
|
||||
<XButton
|
||||
|
||||
@@ -105,3 +105,14 @@ h1, h2, h3 {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// Visual stand-in for a native placeholder that assistive technology must not
|
||||
// read (native placeholders are announced character by character).
|
||||
.decorative-placeholder {
|
||||
position: absolute;
|
||||
inset-block-start: 50%;
|
||||
transform: translateY(-50%);
|
||||
inset-inline-start: calc(0.75em + 1px);
|
||||
color: var(--input-placeholder-color);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
v-model="password"
|
||||
v-focus
|
||||
type="password"
|
||||
:aria-label="$t('user.auth.password')"
|
||||
:decorative-placeholder="$t('user.auth.passwordPlaceholder')"
|
||||
@keyup.enter.prevent="authenticate()"
|
||||
/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user