# FormField

> Wraps a control with a label, help/error text and id wiring, so a screen reader gets the full picture without manual aria plumbing.

- Package: `fancy-ui-svelte` (Svelte 5 + Tailwind CSS v4)
- Category: Forms
- Tags: form, field, label, validation, accessibility, compound
- Dependencies: label
- Docs: https://fancy-ui.rama.app/docs/components/form-field
- Source: https://github.com/RamaHerbin/fancy-ui/tree/main/src/lib/fancy-ui/form-field

## Import

```svelte
<script lang="ts">
  import { FormField } from 'fancy-ui-svelte';
</script>
```

Setup (once per project): `npm install fancy-ui-svelte`, then add `@import "fancy-ui-svelte/tailwind.css";` after `@import "tailwindcss";` in your main CSS file. Full guide: https://fancy-ui.rama.app/llms.txt

React: `import { FormField } from 'fancy-ui-react';` (same prop names; `class` becomes `className`, snippets become `ReactNode`).

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `label` | `string` | — | Label text — the common case. For custom label markup, render a Label in children instead |
| `description` | `string` | — | Help text under the control, replaced by error while the field is invalid |
| `error` | `string` | — | Error text. Setting it marks the field invalid and replaces the help text |
| `valid` | `boolean` | `false` | Decorative checkmark next to the help text; error always wins if both are set |
| `required` | `boolean` | `false` | Marks the field required: the label gets an asterisk, the control gets aria-required |
| `disabled` | `boolean` | `false` | Disables the field: reaches the wrapped control through context |
| `id` | `string` | — | Opts out of the generated id. description/error ids are suffixes of this same value |
| `class` | `string` | — | Additional CSS classes, merged onto the root |
| `ref` | `HTMLDivElement \| null` | `null` | Bindable reference to the root element |

## Snippets

| Snippet | Description |
| --- | --- |
| `children` | The control |

## Examples

### Basic Usage

A labelled field with help text underneath.

```svelte
<script lang="ts">
	import { FormField } from "fancy-ui-svelte";
</script>

<!--
  A context-aware control (Input and friends, elsewhere in this library)
  reads its id and aria-describedby off FormField's context automatically.
  This plain <input> stands in for one and mirrors that wiring by hand —
  see the FormField README for the full, no-manual-wiring picture.
-->
<FormField id="signup-email" label="Email" description="We'll only use this to send your receipt.">
	{#snippet children()}
		<input
			id="signup-email"
			type="email"
			placeholder="you@example.com"
			aria-describedby="signup-email-description"
			class="border-input bg-background placeholder:text-muted-foreground focus-visible:ring-ring/50 focus-visible:border-ring w-full rounded-lg border px-3 py-[9px] text-[13px] outline-none focus-visible:ring-2"
		/>
	{/snippet}
</FormField>
```

### With Error

error replaces the help text and marks the field invalid.

```svelte
<script lang="ts">
	import { FormField } from "fancy-ui-svelte";
</script>

<!--
  Setting `error` replaces the help text with the error message and marks
  the field invalid — the plain <input> below mirrors aria-invalid by hand
  the same way it mirrors the id; a context-aware control reads both off
  FormField's context automatically.
-->
<FormField id="signup-username" label="Username" required error="Minimum 3 characters.">
	{#snippet children()}
		<input
			id="signup-username"
			type="text"
			value="ab"
			aria-invalid="true"
			aria-describedby="signup-username-error"
			class="border-input bg-background placeholder:text-muted-foreground focus-visible:ring-ring/50 focus-visible:border-ring aria-invalid:border-destructive aria-invalid:focus-visible:ring-destructive/25 w-full rounded-lg border px-3 py-[9px] text-[13px] outline-none focus-visible:ring-2"
		/>
	{/snippet}
</FormField>
```

### Validated

valid's decorative checkmark, plus a control drawing its own success look.

```svelte
<script lang="ts">
	import { FormField } from "fancy-ui-svelte";
</script>

<!--
  `valid` puts a decorative checkmark next to the help text (aria-hidden —
  the description text itself is what actually says the value is fine) and
  publishes `valid` on the field context. A context-aware control can read
  that to draw its own success look; this plain <input> isn't one, so it
  mirrors the same treatment by hand, the same way it already mirrors the
  id and aria-describedby. Its own checkmark also gets an sr-only "(valid)",
  since a control-level glyph doesn't inherit FormField's own aria-hidden
  reasoning for free.
-->
<FormField
	id="signup-email-2"
	label="Email"
	description="Used for sign-in and notifications."
	valid
>
	{#snippet children()}
		<div class="relative">
			<input
				id="signup-email-2"
				type="email"
				value="rama@fancy.ui"
				aria-describedby="signup-email-2-description"
				class="bg-background w-full rounded-lg border border-emerald-500/45 px-3 py-[9px] pr-8 text-[13px] outline-none"
			/>
			<span
				class="pointer-events-none absolute inset-y-0 right-3 flex items-center text-emerald-500"
			>
				✓<span class="sr-only"> (valid)</span>
			</span>
		</div>
	{/snippet}
</FormField>
```

### Disabled

disabled reaches the control through context.

```svelte
<script lang="ts">
	import { FormField } from "fancy-ui-svelte";
</script>

<FormField
	id="plan-slug"
	label="Workspace slug"
	description="Set once, at workspace creation."
	disabled
>
	{#snippet children()}
		<input
			id="plan-slug"
			type="text"
			value="acme-inc"
			disabled
			aria-describedby="plan-slug-description"
			class="border-input bg-background w-full rounded-lg border px-3 py-[9px] text-[13px] outline-none disabled:cursor-not-allowed disabled:opacity-50"
		/>
	{/snippet}
</FormField>
```
