components

FormField

A generic wrapper for form inputs providing consistent label, helper text, and error message styling.

FormField

The FormField component is a generic wrapper designed to bring consistency to form inputs by providing a unified label, optional helper text, and error messaging. It acts as a structural molecule that automatically wires up necessary accessibility attributes (like aria-describedby and aria-invalid) to its child components through context.

Curva & Concreto

FormField aligns perfectly with the “Curva & Concreto” doctrine. The label and validation text represent the static, structural “Concreto” geometry, while the nested interactive input elements (like switches or text inputs) use the organic “Curva” properties to respond to user interactions.

Usage

Form fields use a Svelte snippet (children) to receive the target input component. The wrapper provides an id and an optional aria-describedby to the inner component via snippet variables, ensuring correct linking.

<script>
import FormField from 'cobogo/components/FormField';
import TextInput from 'cobogo/components/TextInput';
let email = $state('');
</script>
<FormField label="Email Address" htmlFor="email-input" required>
{#snippet children(props)}
<TextInput type="email" bind:value={email} {...props} />
{/snippet}
</FormField>

Basic Example

<FormField label="Full Name" htmlFor="demo-name" helper="Enter your full legal name.">
{#snippet children(props)}
<TextInput placeholder="e.g. Maria da Silva" {...props} />
{/snippet}
</FormField>

With Error State

When the error prop is provided, FormField overrides the helper text, displays the error message, and turns the label to var(--vermelho). The invalid context is also automatically passed to the child element so it can update its visual state (like turning its borders red).

<FormField label="Username" htmlFor="demo-username" error="This username is already taken.">
{#snippet children(props)}
<TextInput value="brad_frost_1929" {...props} />
{/snippet}
</FormField>
<FormField label="Accept Terms" htmlFor="demo-terms" error="You must accept the terms to continue.">
{#snippet children(props)}
<Switch checked={false} {...props} />
{/snippet}
</FormField>

Wrapping Other Inputs

The FormField works seamlessly with different types of inputs, including Select and Switch. The child input simply needs to consume the props passed through the snippet or the context.

<FormField label="Notification Preference" htmlFor="demo-pref" helper="We'll use this to reach you.">
{#snippet children(props)}
<Select
options={[
{ value: 'email', label: 'Email' },
{ value: 'sms', label: 'SMS' },
{ value: 'push', label: 'Push Notification' }
]}
value="email"
{...props}
/>
{/snippet}
</FormField>
<FormField label="Enable Tracking" htmlFor="demo-switch" helper="Help us improve the product by sharing anonymous usage data.">
{#snippet children(props)}
<Switch checked={true} {...props} />
{/snippet}
</FormField>
<FormField label="Subscribe" htmlFor="demo-subscribe" helper="Receive weekly updates.">
{#snippet children(props)}
<Checkbox checked={true} {...props} />
{/snippet}
</FormField>

Props

NameTypeDefaultDescription
label *stringThe text displayed as the label for the form field.
htmlFor *stringThe ID of the child input element to associate the label with.
required booleanfalseWhether the field is required. Appends a red asterisk to the label.
helper stringOptional helper text displayed below the field to provide additional context.
error stringOptional error text displayed below the field. Overrides the helper text and triggers the invalid state.
children *Snippet<[{ id: string; 'aria-describedby'?: string }]>The snippet for the child input control.