<script setup lang="ts">
import { PasswordInput } from '@hina-ui/vue'
</script>
<template>
<PasswordInput
model-value="hoshimi-shion"
aria-label="Password"
autocomplete="current-password"
class="w-64"
/>
</template>
Usage
import { PasswordInput } from '@hina-ui/vue'
The field ends with a button that switches between masked and plain text, with v-model bound to the text. Attributes it does not declare land on the input; pass autocomplete, using current-password for sign-in and new-password for sign-up and password changes.
<script setup lang="ts">
import { ref } from 'vue'
import { PasswordInput } from '@hina-ui/vue'
const password = ref('')
</script>
<template>
<PasswordInput
v-model="password"
aria-label="Password"
placeholder="Password"
autocomplete="current-password"
class="w-64"
/>
</template>
Examples
Visibility
visible controls whether the text is shown in plain, and supports v-model:visible. Two fields sharing one value toggle together.
<script setup lang="ts">
import { ref } from 'vue'
import { PasswordInput, Stack } from '@hina-ui/vue'
const password = ref('')
const confirm = ref('')
const visible = ref(false)
</script>
<template>
<Stack class="w-64">
<PasswordInput
v-model="password"
v-model:visible="visible"
aria-label="New password"
placeholder="New password"
autocomplete="new-password"
/>
<PasswordInput
v-model="confirm"
v-model:visible="visible"
aria-label="Confirm password"
placeholder="Confirm password"
autocomplete="new-password"
/>
</Stack>
</template>
Sizes
Three sizes, matching the input.
<script setup lang="ts">
import { PasswordInput, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack class="w-64">
<PasswordInput size="sm" model-value="hoshimi" aria-label="Small" />
<PasswordInput size="md" model-value="hoshimi" aria-label="Medium" />
<PasswordInput size="lg" model-value="hoshimi" aria-label="Large" />
</Stack>
</template>
Variants
primary has a border, background and shadow; secondary uses a tinted background; bare is transparent, without a border, shadow, hover fill or container focus ring. Size and padding keep their existing settings.
bare preserves the disabled state and aria-invalid; FormField can display the error message. Action buttons keep their own hover and keyboard focus feedback.
<script setup lang="ts">
import { PasswordInput, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack class="w-full max-w-xs">
<PasswordInput variant="primary" aria-label="primary" model-value="hoshimi" />
<PasswordInput variant="secondary" aria-label="secondary" model-value="hoshimi" />
<PasswordInput variant="bare" aria-label="bare" model-value="hoshimi" />
</Stack>
</template>
States
invalid marks a failed validation and disabled prevents editing.
<script setup lang="ts">
import { PasswordInput, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack class="w-64">
<PasswordInput invalid model-value="123456" aria-label="Invalid" />
<PasswordInput disabled model-value="hoshimi" aria-label="Disabled" />
</Stack>
</template>
In a form
Inside a FormField the label points at the input, and the description and error message are rendered by the field; validation rules and submission belong to the Form. A rule that spans fields, such as the two entries matching, goes on the object and names the field the error lands on.
<script setup lang="ts">
import { reactive, ref } from 'vue'
import * as v from 'valibot'
import { Button, Form, FormField, PasswordInput, Text } from '@hina-ui/vue'
const schema = v.pipe(
v.object({
password: v.pipe(
v.string('Enter a password'),
v.minLength(8, 'At least 8 characters'),
v.regex(/[0-9]/, 'Include at least one digit'),
),
confirm: v.string('Enter the password again'),
}),
v.forward(
v.check(input => input.password === input.confirm, 'The passwords do not match'),
['confirm'],
),
)
const values = reactive({ password: '', confirm: '' })
const saved = ref(false)
async function save() {
await new Promise(resolve => setTimeout(resolve, 600))
saved.value = true
}
</script>
<template>
<Form v-slot="{ submitting }" :values="values" :rules="schema" class="w-80" @submit="save">
<FormField
name="password"
label="New password"
description="At least 8 characters with a digit"
required
>
<PasswordInput v-model="values.password" />
</FormField>
<FormField name="confirm" label="Confirm password" required>
<PasswordInput v-model="values.confirm" />
</FormField>
<Button type="submit" :loading="submitting" class="self-start">Change password</Button>
<Text v-if="saved" tone="muted" size="sm">Password changed.</Text>
</Form>
</template>
Behaviour
- Clicking the toggle does not blur the text area, so typing can continue after switching.
- The toggle only changes how the text is displayed, not its value.
- Hover, focus, invalid and disabled behave the same as Input.
Accessibility
- The toggle is reachable with Tab; its name switches between “Show password” and “Hide password” with the state.
- Pair it with a
labelelement or anaria-labelfor its name.invalidalso setsaria-invalid.
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
modelValue | string | — | The text |
visible | boolean | false | Whether the text is shown in plain; v-model:visible |
variant | 'primary' | 'secondary' | 'bare' | 'primary' | Variant |
size | 'sm' | 'md' | 'lg' | 'md' | Size |
invalid | boolean | false | Whether validation failed |
disabled | boolean | false | Whether the field is disabled |
class | string | — | Classes appended to the root element |
| Event | Payload | Description |
|---|---|---|
update:modelValue | value: string | The text changed |
update:visible | visible: boolean | The visibility changed |