Input

A single-line text field.

<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>
vue

Usage

import { Input } from '@hina-ui/vue'
ts

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>
vue

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.

kg
/
<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>
vue

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>
vue

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>
vue

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>
vue

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>
vue

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>
vue

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.

2 to 12 characters

<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>
vue

Behaviour

  • For primary and secondary, the fill darkens on hover, an accent ring appears on focus, and the border and ring use the danger colour when invalid. bare omits 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 label element or an aria-label for its name.
  • Slot icons are decorative; give an icon an aria-label when its meaning matters.
  • invalid also sets aria-invalid and loading sets aria-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