# Field

A labelled form control with an optional hint, posted under the field's id without any script.

A field ties a label and an optional hint to one control; on the web the control's id and name are the field's id, so a plain form post carries it, and natively Pick hands back the control with the person's edit. Control::Input { value, placeholder } is a one-line text box, Select { options, value } a list of (value, label) choices, Textarea { value, rows } a box of several lines, and Switch(bool) and Checkbox(bool) an on or off box drawn on one row with its label. The default control is an empty Input.

## Use it

Web:

```rust
use ocean_ui_core::{Control, Field};
let control = Control::Select { options: vec![("p1".into(), "Urgent".into())], value: "p1".into() };
Field { id: "priority".into(), label: "Priority".into(), hint: Some("Urgent pages the on-call.".into()), control }
```

Native:

```rust
use ocean_ui_native::{core::{Control, Field}, widgets::Pick};
let field = Field { id: "wrap".into(), label: "Wrap lines".into(), hint: None, control: Control::Switch(true) };
let edited: Option<Control> = field.pick(ui).value;
```

Cargo feature `forms`; since 2026-10-29.
In App UI the `form` block draws it ([form](/ui/blocks/form.md)).

## Props

| Name | Type | Default | What |
|---|---|---|---|
| `id` | `String` | `—` | The control's id and name, and the start of the hint's id. |
| `label` | `String` | `—` | What the field asks for, in sentence case. |
| `hint` | `Option<String>` | `None` | One sentence under the control, tied to it by aria-describedby. |
| `control` | `Control` | `Input` | Input, Select, Textarea, Switch or Checkbox, with its value. |

On the Mac: ![Field, dark](/ui/native/field-dark.png) ![Field, light](/ui/native/field-light.png)

## Examples

### Text input

The label above, the help below, tied to the input.

```rust
Field {
    id: "f-name".into(),
    label: "Agent name".into(),
    hint: Some("People see this name in the queue.".into()),
    control: Control::Input {
        value: String::new(),
        placeholder: "Triage agent".into(),
    },
}
```

### Select

A closed list of choices; the current one selected.

```rust
Field {
    id: "f-tone".into(),
    label: "Priority".into(),
    hint: None,
    control: Control::Select {
        options: vec![
            ("low".into(), "Low".into()),
            ("normal".into(), "Normal".into()),
            ("high".into(), "High".into()),
        ],
        value: "normal".into(),
    },
}
```

### Textarea

Room for a few lines; rows set its height.

```rust
Field {
    id: "f-notes".into(),
    label: "Instructions".into(),
    hint: Some("Plain language; the agent follows it.".into()),
    control: Control::Textarea { value: String::new(), rows: 4 },
}
```

### Switch and checkbox

On and off controls put the label beside them.

```rust
Field {
    id: "f-live".into(),
    label: "Answer customers directly".into(),
    hint: None,
    control: Control::Switch(true),
}.html(out);
Field {
    id: "f-terms".into(),
    label: "Ask a person before refunds".into(),
    hint: None,
    control: Control::Checkbox(false),
}.html(out);
```

### Required

Required is said in the label, not by a colour.

```rust
Field {
    id: "f-email".into(),
    label: "Work email (required)".into(),
    hint: Some("Replies go to this address.".into()),
    control: Control::Input {
        value: "maya@".into(),
        placeholder: "name@company.com".into(),
    },
}
```

## Accessibility

- The label is a label element whose for names the control, so clicking it focuses or toggles the control.
- A hint is tied to the control by aria-describedby, so it is read after the label.
- A switch is a checkbox with role="switch", announced as on or off; a checkbox is announced as checked or not.
- Every control is a native element, so the keyboard works as the browser defines.

## Related

- [Segmented](/ui/components/segmented.md)
- [Dialog](/ui/components/dialog.md)
- [Button](/ui/components/button.md)
