Persistent label, bound to the field via htmlFor / id. Never a placeholder.
value *
string
n/a
Current value (controlled).
onChange
(value: string) => void
n/a
Change handler. Ignored while disabled or read-only.
placeholder
string
n/a
Ghost prompt INSIDE the well. It is supplementary, never the label.
helperText
string
n/a
Helper text under the well. Replaced by errorText / statusText when set.
errorText
string
n/a
When set, the field renders its error treatment (red border + this message).
status
FieldStatus
n/a
The validation ladder: warning / success (or error) border + a matching, icon-carrying help line showing statusText. errorText still wins if both are set (back-compat). Color is never the only signal (a glyph always rides along).
statusText
string
n/a
The message shown for status (ignored when errorText is set).
required
boolean
false
Marks the field required: renders the status-colored * after the label.
maxLength
number
n/a
Optional max length: adds a footer row with a right-aligned character counter (help on the left, "{len} / {max}" on the right); over the cap = error treatment.
disabled
boolean
false
Inert + clearly-seen (never invisible).
readOnly
boolean
false
Value shown, selectable, not editable.
leadingIcon
string
n/a
Optional leading glyph rendered inside the well, before the text.
type
string
'text'
input type (text, email, password, …).
Also accepts the native <input> attributes (e.g. onClick, disabled, id, aria-*, data-*), forwarded straight through, plus a forwarded ref.
NockerlTextField is the published recessed-well field. The 12dp control shape and the
canvasAlt well / hairline-border / accent-focus / status-error color mapping are baked
in, so the call site only supplies content. Label + error are bundled: a non-null
errorText puts the field in the error state and replaces helperText on the supporting
line (error is text, never color alone, per law §14).
Parameter
Type
Default
Description
value *
String
n/a
Current text value.
onValueChange *
(String) -> Unit
n/a
Invoked on every edit.
modifier
Modifier
Modifier
External modifier. Fields are typically fillMaxWidth().
label
String?
null
Persistent floating label, the canonical persistent-label pattern (never placeholder-as-label).
placeholder
String?
null
Hint shown while empty: an addition to the label, never a replacement.
helperText
String?
null
Persistent supporting line under the field.
errorText
String?
null
When non-null: error state + this text on the supporting line (replaces helperText).
leadingIcon / trailingIcon
(@Composable -> Unit)?
null
Glyph slots inside the field (e.g. a lock, a show-password toggle).
enabled
Boolean
true
When false, the field is dimmed and non-interactive.
singleLine
Boolean
true
Single-line entry (the default); set false for a short text area.
maxLines
Int
if (singleLine) 1 else 5
Line cap for multi-line fields.
visualTransformation
VisualTransformation
None
e.g. PasswordVisualTransformation() for masked secrets.
keyboardOptions / keyboardActions
KeyboardOptions / KeyboardActions
Default
IME type/action configuration and the matching action callbacks.
SwiftUI applies the field chrome as a .nockerlFieldWell() view modifier on a
plain TextField / SecureField. State (focus, editing) comes from the SwiftUI
environment, so the modifier takes no explicit state parameters. Label and error are
sibling views in this idiom: the title is a sibling Text, and an error is shown as one
too.
Style
Type
Default
Description
.nockerlFieldWell()
ViewModifier
n/a
The published recessed input chrome: the canvasAlt plane, an inner top shade, the control radius, and the cardHairline edge. Wrap a .textFieldStyle(.plain) field with it.
.textFieldStyle(.plain)
TextFieldStyle
n/a
Strips the native bezel so the Nockerl well chrome is the only container.
.foregroundStyle(NockerlTheme.onSurface)
modifier
n/a
Binds the typed-text color to the on-surface token.
TextField / SecureField
View
n/a
The host control. SecureField is the masked variant for secrets; both take the same modifier.
Cross-platform drift (tracked for reconciliation)
The recessed-well spec is the target; these are the real divergences between the shipped
platforms, tracked in the Review queue:
Shared component: the Kotlin tab documents NockerlTextField
(com.dizyx.nockerl.design.components), the extracted recessed-well field. The Swift tab
documents the chrome as a reusable modifier (.nockerlFieldWell()) on a plain field
rather than a bundled component, and web still ships the shadcn input.tsx. The target is
one extracted field on every surface, with the app screens that recolor OutlinedTextField
inline moving onto it.
Recessed vs outlined: Voice + the published Kotlin NockerlTextField + the spec are
recessed wells; the app’s older inline OutlinedTextField call-sites are still
outlined boxes (no inset) until they adopt the shipped component.
Control radius: Voice hardcodes 8pt; Android uses the 12dp token; web’s
shadcn input is a full pill (rounded-full). The well must be the 12px control
radius everywhere (the pill is reserved for chips + the input bar).
Focus treatment: web shadcn uses a 1px ring (a shadow); the law wants a 2px
outline. Voice/Android rely on a border-color change.
Label + error slots: web + Compose bundle label / error / helper; the Swift idiom
documented here keeps them as siblings. One bundled API should win.