Guide

# The field API

field(name, *modifiers, **options) renders label + input + error/hint in one call.

## The primary verb

`field` wraps a labeled control around the right input for the attribute, wiring `name`, `id`, `value`, `required`, and the error state from the bound model:

```ruby
f.field :email                          # type=email, label auto, required inferred
f.field :notify                         # boolean column → toggle
f.field :role                           # AR enum → select, humanized
f.field :bio, as: :textarea, rows: 6    # explicit as: always wins
f.field :name, :primary, :lg, label: "Full name", hint: "As on your ID"
```

Positional symbols are daisyUI variants (`:primary`, `:lg`, `:ghost`, …) stacked onto the inner input — see [Variants](https://phlex-forms.zoolutions.llc/docs/variants) — or a type override (`:number`, `:date`, …). Remaining keywords pass through to the input element (`rows:`, `placeholder:`, `data:`, …).

## Options

| Option | Effect |
| --- | --- |
| `label:` | Label text. Defaults to the model's humanized attribute name. label: false omits the label. |
| `hint:` | Help text shown beneath the field when there is no error. |
| `as:` | Override the control: :select, :textarea, :toggle, :checkbox, :file, :radio, :hidden, :rich_textarea, or any text-like type. |
| `required:` | Force the required flag. Otherwise inferred from the model's unconditional presence validators. |
| `choices:` | Choices for a select — implies as: :select when given. Pairs, flat values, or a Hash (optgroups). |
| `validate:` | Client-side validation override: false opts out, a Hash gives explicit rules. See Client-side validation. |

Everything not listed flows to the input. Caller options always beat inferred attributes — pass `maxlength: 10` and the validator-derived `maxlength` is replaced.

## Labels, errors, and hints

The label text comes from `human_attribute_name` when the model speaks ActiveModel, so your locale files apply. A field whose attribute has an unconditional presence validator renders a required marker and the `required` attribute.

When the model has an error on the attribute, the field renders the first full message **instead of** the hint, and the input picks up the error variant automatically:

```ruby
f.field :email, hint: "We never spam."
# valid:   … <p>We never spam.</p>
# invalid: … <p>Email is invalid</p>  (input gets the error variant)
```

> **Note:** An inferred association select is named by the foreign key `user[country_id]` but still shows errors Rails attached to `:country` — both names are checked.

## The submit button

`submit` defaults its label from the record's persistence state via i18n — *Create User* for a new record, *Update User* for a persisted one — and takes text and variants positionally:

```ruby
f.submit                    # "Create User" / "Update User"
f.submit :primary, :lg      # default text, variants
f.submit "Save", :primary   # custom text + variants
```