Hacktron Design System

FormField

The label, hint and error message around a single form control.

We'll only use this to send you a receipt.

<template>  <FormField label="Email" name="email">    <Input v-model="state.email" />  </FormField></template>

Props

Name Type Default
labelstring-
namestring-
descriptionstring-
errorboolean | string-
requiredbooleanfalse
infostring-
...propsUFormField props (hint, help, size, orientation, errorPattern, ...)-

Slots

Name Description
defaultThe field's control (Input, Textarea, Select, InputNumber, or anything else), scoped with `error`.
labelOverrides the label text, scoped with `label`.
descriptionOverrides the description text, rendered above the control.
hintOverrides the hint text (right-aligned next to the label). With `info` set, it renders after the info icon, still right-aligned.
errorOverrides the error text, scoped with `error`. Rendered below the control.
helpOverrides the help text shown when there's no error. Rendered below the control.

Guidelines

Do

  • Give it a name matching the field's key in a Form's schema/state - that's how a schema validation error gets routed here automatically, no manual error prop needed.
  • Reach for it around any control - Input, Textarea, Select, Combobox, InputNumber, or a bespoke one - it makes no assumption about what it wraps.
  • Use required for a plain visual asterisk only; it doesn't add validation itself - the schema still needs its own non-optional rule for the field to actually be required.
  • Use description for context the user needs before they interact with the control - what to enter, an expected format, why the field is being asked at all. It renders above the control, so it's read first.
  • Use help for supplementary text that only matters after the control, and isn't essential - a reassurance, a privacy note, a format reminder. It renders below the control.
  • Use a tooltip beside the label (via the info prop - an info icon right after the label text, see the "All parts" example) for context that's genuinely optional to read - a definition, a "why we ask this" aside - that would clutter the field if it were always visible as description/help. It only appears on hover/focus, so never put anything there a keyboard-only or touch user must see to complete the field correctly.
  • Don't hand-place an InfoTooltip in the #label slot - that slot renders inside the <label> element, and the icon's trigger is a <button>, which is invalid there and leaks "More info" into the control's accessible name. info puts it beside the label instead.

Don't

  • Don't pass a literal error string for a validation failure a Form schema can already express - that duplicates the same message in two places and can drift out of sync.
  • Don't put anything essential in help - it's automatically replaced by the error message the moment validation fails on that field, so it's invisible exactly when the field is in its most important state.
  • Don't reach for a label tooltip when description or help would do - a tooltip hides its content behind a hover/focus interaction, so it's the wrong choice for anything the user needs to see without extra effort. Use it only for the genuinely-optional aside case above.

Examples

All parts

label, description (above the control), the control itself, help/error (below the control, mutually exclusive - error always wins when both are set), and an info tooltip beside the label via the info prop.

We'll only use this to send you a receipt.

We never share this with third parties.
Must be at least 8 characters
<template>  <FormField    label="Email"    description="We'll only use this to send you a receipt."    help="We never share this with third parties."  >    <Input placeholder="you@example.com" class="w-full" />  </FormField>    <FormField label="Password" error="Must be at least 8 characters">    <template #default="{ error }">      <Input type="password" :error="!!error" class="w-full" />    </template>  </FormField>    <FormField    name="webhookUrl"    label="Webhook URL"    info="Findings post here as soon as a scan finishes."  >    <Input placeholder="https://example.com/webhook" class="w-full" />  </FormField></template>