Shown on your profile page
<script setup lang="ts">
import { ref } from 'vue'
import { FormField, Input } from '@hina-ui/vue'
const name = ref('')
</script>
<template>
<FormField label="Nickname" description="Shown on your profile page" required class="w-80">
<Input v-model="name" placeholder="Hoshimi Shion" />
</FormField>
</template>
Usage
import { FormField } from '@hina-ui/vue'
A form field lays out one control together with its label, description and error message. Inside a Form it reads the error of its field through name; on its own, error sets the message directly. Controls from the library connect automatically once placed in a field: the label points at the control, the description and the error are linked with aria-describedby, the control turns invalid while an error is present, and the field’s disabled is passed down.
<script setup lang="ts">
import { ref } from 'vue'
import { FormField, Input } from '@hina-ui/vue'
const name = ref('')
</script>
<template>
<FormField label="Nickname" class="w-80">
<Input v-model="name" />
</FormField>
</template>
Examples
Horizontal layout
orientation="horizontal" puts the label on the leading side and the control on the trailing side. label-width accepts a CSS length or a number of pixels; it defaults to 10rem. Use FormLayout to share a label width across fields. The horizontal layout always keeps two columns.
Shown on your public profile.
<script setup lang="ts">
import { ref } from 'vue'
import { FormField, Input } from '@hina-ui/vue'
const name = ref('Hina')
</script>
<template>
<FormField
label="Display name"
orientation="horizontal"
label-width="8rem"
description="Shown on your public profile."
class="w-full"
>
<Input v-model="name" />
</FormField>
</template>
Description placement
description-placement="label" places the description below the label and before the control in reading order. The default, control, places it below the control. Errors always stay in the control area. Both placements support the description slot and retain the same accessible association.
Shown on your public profile.
Use an address that can receive notifications.
Enter an email address.
<script setup lang="ts">
import { ref } from 'vue'
import { FormField, Input, Stack } from '@hina-ui/vue'
const name = ref('Hina')
const email = ref('')
</script>
<template>
<Stack class="w-full">
<FormField
label="Display name"
orientation="responsive"
description-placement="label"
description="Shown on your public profile."
>
<Input v-model="name" />
</FormField>
<FormField
label="Email address"
orientation="responsive"
description-placement="control"
description="Use an address that can receive notifications."
error="Enter an email address."
>
<Input v-model="email" type="email" />
</FormField>
</Stack>
</template>
Responsive layout
orientation="responsive" stacks content while the field is narrower than 32rem, and uses two columns at 32rem and above. This follows the field’s own available width, including inside a multi-column form, rather than the viewport. Resize the example to see the switch.
Resize this panel: fields stack below 32rem.
<script setup lang="ts">
import { ref } from 'vue'
import { FormField, Input, Stack } from '@hina-ui/vue'
const name = ref('Hina')
</script>
<template>
<Stack class="w-full max-w-2xl resize-x overflow-auto p-2">
<FormField
label="Display name"
orientation="responsive"
description="Resize this panel: fields stack below 32rem."
>
<Input v-model="name" />
</FormField>
</Stack>
</template>
Description
description shows a line of help under the control; the slot of the same name takes richer content.
Shown on your profile page and editable any time
Up to 80 characters, line breaks allowed.
<script setup lang="ts">
import { ref } from 'vue'
import { FormField, Input, Stack, Textarea } from '@hina-ui/vue'
const name = ref('')
const bio = ref('')
</script>
<template>
<Stack gap="md" align="stretch" class="w-80">
<FormField label="Nickname" description="Shown on your profile page and editable any time">
<Input v-model="name" />
</FormField>
<FormField label="Bio">
<Textarea v-model="bio" />
<template #description>Up to 80 characters, line breaks allowed.</template>
</FormField>
</Stack>
</template>
Required mark
required shows a mark after the label and gives assistive technology the matching text. The mark is only a hint; whether the field is required is up to the validation rules.
<script setup lang="ts">
import { ref } from 'vue'
import { FormField, Input } from '@hina-ui/vue'
const email = ref('')
</script>
<template>
<FormField label="Email" required class="w-80">
<Input v-model="email" type="email" />
</FormField>
</template>
Standalone
Outside a form, error decides the message directly, which suits places that run their own validation.
The email is not valid
<script setup lang="ts">
import { ref } from 'vue'
import { FormField, Input, Stack, Switch } from '@hina-ui/vue'
const email = ref('shion@example')
const failed = ref(true)
</script>
<template>
<Stack gap="md" align="stretch" class="w-80">
<Switch v-model="failed">Show the error</Switch>
<FormField label="Email" :error="failed ? 'The email is not valid' : undefined">
<Input v-model="email" type="email" />
</FormField>
</Stack>
</template>
Kinds of controls
A single control is linked through the label’s for; grouped controls such as radio groups, checkbox groups, sliders, ratings and date fields are linked to the label through aria-labelledby.
<script setup lang="ts">
import { ref } from 'vue'
import {
CheckboxGroup,
DateField,
FormField,
RadioGroup,
Rating,
Slider,
Stack,
} from '@hina-ui/vue'
const kinds = [
{ label: 'Game', value: 'game' },
{ label: 'Novel', value: 'novel' },
{ label: 'Manga', value: 'manga' },
]
const kind = ref<string | number | null>('game')
const tags = ref<Array<string | number>>([])
const score = ref(4)
const volume = ref(30)
const released = ref<string | null>(null)
</script>
<template>
<Stack gap="md" align="stretch" class="w-80">
<FormField label="Kind">
<RadioGroup v-model="kind" :options="kinds" orientation="horizontal" />
</FormField>
<FormField label="Tags">
<CheckboxGroup v-model="tags" :options="kinds" orientation="horizontal" />
</FormField>
<FormField label="Score">
<Rating v-model="score" />
</FormField>
<FormField label="Volume">
<Slider v-model="volume" />
</FormField>
<FormField label="Release date">
<DateField v-model="released" />
</FormField>
</Stack>
</template>
Behavior
- A field holds one control; an
idoraria-describedbyset on the control wins over the values the field generates. - The error message pushes the content below it aside as it appears and pulls back as it leaves; height and spacing change continuously.
- When the field loses focus it notifies its form, which the
blurvalidation timing relies on.
Accessibility
- The label points at the control through
for; grouped controls link back to the label througharia-labelledby. - The required mark is hidden from assistive technology, and hidden text says the field is required.
- The error message carries
aria-live="polite", so it is announced when it appears.
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | — | Field name used to read the error from the form |
label | string | — | Label text |
description | string | — | Description text |
error | string | — | Error text set directly |
required | boolean | false | Whether the required mark shows |
orientation | 'vertical' | 'horizontal' | 'responsive' | 'vertical' | Label/control layout; inherits from FormLayout |
descriptionPlacement | 'label' | 'control' | 'control' | Description below the label or control; inherits from FormLayout |
labelWidth | string | number | '10rem' | Label column width: CSS length or pixels; inherits from FormLayout |
disabled | boolean | false | Whether the control is disabled |
class | string | — | Classes appended to the root element |
Slots
| Slot | Description |
|---|---|
| default | The control |
label | Label content |
description | Description content |