Input
The single-line input: three variants, seven sizes, composable everything else.
A single-line editor in themed chrome. Structure (labels, buttons, adornments) is
COMPOSITION — nil-able Start/End slots and the Field
wrapper; behavior (filters, transforms) is caller-owned functions. A file input is a native
picker on the web — out of scope for a canvas UI, use your platform's file dialog ("from the
web").
Installation
Use the module import (the default), or own the source: lotusui add
vendors the component into your app and lotusui update keeps the copy mergeable —
see Registry.
go get github.com/ikaito-com/lotusui
# or vendor the source into your app:
go run github.com/ikaito-com/lotusui/cmd/lotusui add input
Usage
var name lotusui.Input
name.LayoutField(th, gtx, "Email")
Disabled
Read-only, dimmed, no caret.
name.Disabled = true
With label
Wrap any control in Field for the label.
lotusui.Field(th, lotusui.FieldOpts{Label: "Email"}, func(gtx C) D {
return email.LayoutField(th, gtx, "you@example.com")
})
Variants
InputOutline (default), InputSubtle (filled, border on
focus), InputFlushed (underline only).
in.Variant = lotusui.InputSubtle
in.Variant = lotusui.InputFlushed
Sizes
All seven shared Size presets — text and padding move together.
in.Size = lotusui.Size2XS // … through lotusui.Size2XL
Start and End elements
Widgets INSIDE the field, beside the editor — an icon, a clear ✕, a visibility eye. nil costs nothing.
search.Start = lotusui.SVGIcon(lotusui.IconEye, 16, th.Palette.FgSubtle)
secret.End = lotusui.SVGIconButtonTint(th, &eyeBtn, lotusui.IconEyeOff, 16, false, th.Palette.FgSubtle)
Suffix addon
A fixed suffix rendered as part of the field's chrome.
sub.LayoutSuffix(th, gtx, "Subdomain", "yourname", ".example.com")
Invalid
Error switches the chrome to danger and renders the message.
in.Error = "this name is already taken"
Field — label, helper, required
The full wrapper is its own component — see Field for helper text, required markers and error slots around any control.
lotusui.Field(th, lotusui.FieldOpts{
Label: "Email", Helper: "We'll never share it.", Required: true,
}, control)
Filter and Transform
Generic mechanisms for the app's rules: Filter is an allow-list,
Transform a same-frame rewrite (here: fold to lowercase).
in.Filter = "abcdefghijklmnopqrstuvwxyz0123456789-"
in.Transform = strings.ToLower
Character counter — composed
Cap length with Transform; the counter is Field.Helper, recomputed each frame.
n := len(bio.Editor.Text())
lotusui.Field(th, lotusui.FieldOpts{Label: "Bio", Helper: fmt.Sprintf("%d / 80", n)}, control)
Grid
Inputs side by side are a Flex row — each field takes an equal share.
layout.Flex{}.Layout(gtx,
layout.Flexed(1, firstNameField),
layout.Rigid(lotusui.HSpacer(th.Space.MD)),
layout.Flexed(1, lastNameField),
)
With badge
The label row is yours to compose — here a Beta badge beside the label.
lotusui.HStack(th.Space.SM,
lotusui.SectionLabel(th, "API token"),
lotusui.Badge(th, "Beta", lotusui.BadgeOpts{Variant: lotusui.BadgeSecondary, Size: lotusui.SizeXS}),
)
Field group
Stacked Fields in a Card read as one form section.
lotusui.Card(th, lotusui.CardOpts{}, lotusui.VStack(th.Space.MD,
cityField, postalField,
))
Text addons
Muted text segments in the frame — a currency pair, a URL scheme and TLD — are Start/End widgets.
amount.Start = addonText(th, "$")
amount.End = addonText(th, "USD")
Kbd
Kbd renders the keyboard-cap hint — compose it as the End slot.
search.Start = lotusui.SVGIcon(lotusui.IconSearch, 16, th.Palette.FgSubtle)
search.End = lotusui.Kbd(th, "⌘K")
Spinner
field.End = lotusui.Spinner(th, 14)
Block addons
Top/Bottom render full-width rows INSIDE the frame, above/below the editor line.
name.Top = headerRow // a caption, a status row with actions
API
| Option | Type | Description |
|---|---|---|
Variant | InputVariant | InputOutline (default), InputSubtle, InputFlushed. |
Size | Size | The shared size presets (Size2XS–Size2XL). |
Filter | string | Allow-list of runes; empty accepts everything. |
Transform | func(string) string | Same-frame rewrite — folds, caps, masks. |
Error | string | Danger chrome + message below the field. |
Disabled | bool | Read-only, dimmed, no caret. |
Start / End | layout.Widget | Widgets inside the field, beside the editor; nil costs nothing. |
Top / Bottom | layout.Widget | Full-width rows inside the frame — the block addons. |
Kbd(th, key) | widget | The keyboard-cap hint, for End slots and prose. |