Components

Control

Associates a label with and provides necessary attributes for a form control.

In the context of a form, a control refers to any interactive element such as an input field, a select dropdown, or a button. This includes custom components like select dropdowns or checkboxes that function as buttons but still serve as form inputs, typically activated by clicking on a label or pressing a key.

Each control and its label should be wrapped in its own Control component. This is important for accessibility, as it ensures that the label is associated with the control, and that the label is announced to screen readers when the control receives focus.

Why a separate component?

Props

The Control component doesn't render an element itself, it strictly provides context and attributes for the control via a slot prop and state for the Label.

	export type ControlProps = {
	/**
	 * Optionally provide a unique id for the form item/control.
	 * If not provided, a unique ID will be generated for you.
	 *
	 * This is useful when another library automatically generates
	 * IDs for form items. You can pass that ID to the `id` prop and
	 * the label will be associated with that control.
	 */
	id?: string;
};	

Slot Props

The only slot prop provided by the Control is the attrs prop which should be spread onto the control element/component.

	type SlotProps = {
	attrs: ControlAttrs;
};	

Attributes

	export type ControlAttrs = {
	/** The name of the control used for form submission. */
	name: string;
 
	/** The ID of the control, used for label association. */
	id: string;
 
	/** Present when a validation error exists on the field. */
	"data-fs-error": string | undefined;
 
	/** Present when description or validation exists. */
	"aria-describedby": string | undefined;
 
	/** Present when a validation error exists on the field. */
	"aria-invalid": "true" | undefined;
 
	/** Present when the field is required. */
	"aria-required": "true" | undefined;
 
	/** Used for selection during styling or otherwise */
	"data-fs-control": string;
};	

Composition

Since the Control component doesn't render any HTML elements, it's a common practice to create a wrapper component around it to have consistent styling and behavior across your forms.

For example, you may want to automatically include the Label for each item, and you want the label and slot content to be wrapped in a <div>.

Here's how you might do just that:

CustomControl.svelte
	<script lang="ts">
	import { Control, Label, type ControlProps } from "formsnap";
 
	type $$Props = ControlProps & {
		label: string;
	};
 
	export let label: string;
</script>
 
<Control let:attrs {...$$restProps}>
	<div class="flex flex-col gap-2">
		<Label>{label}</Label>
		<slot {attrs} />
	</div>
</Control>	
MIT

© 2024 Svecosystem Team