Combina semántica nativa, controles y agrupación visual.
Usa <form> para el envío y Form de Liquidify para la agrupación visual. Form no es un formulario HTML ni almacena valores.
import { useState } from "react"
import { Button, Form, Picker, TextField, Toggle } from "@liquidify/react"
import "@liquidify/react/styles.css"
export function AccountSettings() {
const [name, setName] = useState("")
const [plan, setPlan] = useState("free")
const [updates, setUpdates] = useState(true)
const save = () => console.log({ name, plan, updates })
return (
<form onSubmit={(event) => event.preventDefault()}>
<Form>
<Form.Section title="Profile">
<label htmlFor="account-name">Display name</label>
<TextField id="account-name" name="name" value={name} onChange={setName} />
</Form.Section>
<Form.Section title="Preferences" footer="You can change these later.">
<Picker
aria-label="Plan"
items={[
{ value: "free", label: "Free" },
{ value: "pro", label: "Pro" },
]}
value={plan}
onChange={setPlan}
/>
<Toggle aria-label="Product updates" checked={updates} onChange={setUpdates} />
</Form.Section>
</Form>
<Button variant="borderedProminent" onPress={save}>Guardar</Button>
</form>
)
}TextField participa en FormData mediante name. Mantén los valores de Picker, Toggle, Slider y otros controles compuestos en React y combínalos al enviar.
Usa un valor controlado con onChange, o defaultValue/defaultChecked para estado interno. No cambies de modo después del montaje.
Asocia los campos de texto a etiquetas nativas visibles. Usa aria-label o aria-labelledby para otros controles. Conecta los mensajes mediante aria-describedby, aria-invalid o aria-errormessage según la API, sin depender solo del color.
Consulta Formularios y el ejemplo controlado.