<script setup lang="ts">
import { ref } from 'vue'
import { Mail } from '@lucide/vue'
import { Input } from '@hina-ui/vue'
const email = ref('shion@hoshimi.moe')
</script>
<template>
<Input v-model="email" type="email" clearable aria-label="Email" class="w-72">
<template #leading>
<Mail />
</template>
</Input>
</template>
Usage
import { Input } from '@hina-ui/vue'
The field renders a native input with v-model bound to its text. Attributes it does not declare land on the input, so type, placeholder, maxlength, autocomplete and the like work as usual.
<script setup lang="ts">
import { ref } from 'vue'
import { Input } from '@hina-ui/vue'
const name = ref('')
</script>
<template>
<Input v-model="name" aria-label="Nickname" placeholder="Nickname" class="w-72" />
</template>
Examples
Adornments
The leading and trailing slots sit on either side of the text area, for icons, units or shortcut hints. Each slot takes a square as tall as the field, with its content centred.
<script setup lang="ts">
import { Mail } from '@lucide/vue'
import { Input, Kbd, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack class="w-72">
<Input type="email" aria-label="Email" placeholder="Email">
<template #leading>
<Mail />
</template>
</Input>
<Input inputmode="decimal" aria-label="Weight" placeholder="Weight">
<template #trailing>kg</template>
</Input>
<Input aria-label="Quick jump" placeholder="Quick jump">
<template #trailing>
<Kbd>/</Kbd>
</template>
</Input>
</Stack>
</template>
Clearable
clearable shows a clear button once there is content; clicking it empties the field and emits clear.
<script setup lang="ts">
import { ref } from 'vue'
import { Input } from '@hina-ui/vue'
const keyword = ref('Spice and Wolf')
</script>
<template>
<Input v-model="keyword" clearable aria-label="Keyword" placeholder="Keyword" class="w-72" />
</template>
Loading
loading shows a spinner. It replaces the icon in the leading slot when there is one, and sits at the end otherwise.
<script setup lang="ts">
import { Mail } from '@lucide/vue'
import { Input, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack class="w-72">
<Input loading model-value="shion@hoshimi.moe" aria-label="Checking email">
<template #leading>
<Mail />
</template>
</Input>
<Input loading model-value="hoshimi" aria-label="Checking nickname" />
</Stack>
</template>
Sizes
sm suits tables and toolbars, md regular forms, and lg prominent places such as a sign-in page.
<script setup lang="ts">
import { Input, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack class="w-72">
<Input size="sm" aria-label="Small" placeholder="Small" />
<Input size="md" aria-label="Medium" placeholder="Medium" />
<Input size="lg" aria-label="Large" placeholder="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 { Input, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack class="w-full max-w-xs">
<Input variant="primary" aria-label="primary" placeholder="primary" />
<Input variant="secondary" aria-label="secondary" placeholder="secondary" />
<Input variant="bare" aria-label="bare" placeholder="bare" />
</Stack>
</template>
States
invalid marks a failed validation and disabled prevents editing.
<script setup lang="ts">
import { Input, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack class="w-72">
<Input invalid model-value="shion@" aria-label="Invalid" />
<Input disabled model-value="hoshimi" aria-label="Disabled" />
</Stack>
</template>
In a form
Inside a FormField the label points at the input through for, and the description and error message are rendered by the field and linked to it; validation rules, timing and submission belong to the Form.
<script setup lang="ts">
import { reactive, ref } from 'vue'
import * as v from 'valibot'
import { Button, Form, FormField, Input, Text } from '@hina-ui/vue'
const schema = v.object({
name: v.pipe(
v.string('Enter a nickname'),
v.nonEmpty('Enter a nickname'),
v.minLength(2, 'At least 2 characters'),
v.maxLength(12, 'At most 12 characters'),
),
})
const values = reactive({ name: '' })
const saved = ref('')
async function save(data: unknown) {
await new Promise(resolve => setTimeout(resolve, 600))
saved.value = JSON.stringify(data)
}
</script>
<template>
<Form v-slot="{ submitting }" :values="values" :rules="schema" class="w-80" @submit="save">
<FormField name="name" label="Nickname" description="2 to 12 characters" required>
<Input v-model="values.name" />
</FormField>
<Button type="submit" :loading="submitting" class="self-start">Save</Button>
<Text v-if="saved" tone="muted" size="sm">Saved: {{ saved }}</Text>
</Form>
</template>
Behaviour
- For
primaryandsecondary, the fill darkens on hover, an accent ring appears on focus, and the border and ring use the danger colour when invalid.bareomits these visual states. - The clear button only appears with content and when not disabled; clicking it does not blur the text area.
- Clicking an adornment focuses the text area, placing the caret at the start for a leading one and at the end for a trailing one.
- Adornments, the clear button and the spinner all transition in and out.
Accessibility
- Pair it with a
labelelement or anaria-labelfor its name. - Slot icons are decorative; give an icon an
aria-labelwhen its meaning matters. invalidalso setsaria-invalidandloadingsetsaria-busy. The clear button is named from the locale pack.
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
modelValue | string | — | The text |
clearable | boolean | false | Whether the clear button is shown |
loading | boolean | false | Whether the spinner is shown |
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 |
Slots
| Slot | Description |
|---|---|
leading | Adornment at the start of the field |
trailing | Adornment at the end of the field |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue | value: string | The text changed |
clear | — | The text was cleared |