Next: , Previous: , Up: API  


1.11 (hygguile components field)

1.11.1 Procedures

Procedure: combobox KEY: #:label #:name #:id #:options #:value #:placeholder #:required #:help-text #:error #:multiple #:disabled #:classes

Render a free-text field with a list of suggestions.

The suggestion list is a native <datalist>: the browser draws it in its own way, filters it as the reader types, and leaves the field free to accept a value that is not on the list. That is the correct behaviour for a combobox, and none of it needs a script. A page that needs a fixed set of choices wants select instead; a page that needs several values wants multi-select.

label

The field’s visible name.

name

The form field name.

id

An explicit identifier; defaults to name.

options

A list of strings or (value . label) pairs.

value

The initial value.

placeholder

A hint shown while the field is empty.

required

Whether the field is required.

help-text

Optional helper copy.

error

Optional error message.

multiple

Whether several values may be entered (the datalist is unchanged; the field is a normal text input and this only sets multiple).

disabled

Whether the control is disabled.

classes

Classes for the field wrapper.

(combobox #:label "Colour" #:name "colour"
          #:options '("amber" "asparagus" "jeans-blue"))
Procedure: date-field KEY: #:label #:name #:id #:value #:min #:max #:required #:help-text #:error #:disabled #:classes

Render a date field backed by the browser’s own date control.

The value travels in ISO form (2026-10-11), which is the only form a <input type="date"> accepts and the only form a server should have to parse. The browser draws a calendar in the reader’s own locale, so the page never has to ship a date-picker script or guess at a format.

label

The field’s visible name.

name

The form field name.

id

An explicit identifier; defaults to name.

value

The initial value, in ISO form.

min

The earliest accepted date, in ISO form.

max

The latest accepted date, in ISO form.

required

Whether the field is required.

help-text

Optional helper copy.

error

Optional error message.

disabled

Whether the control is disabled.

classes

Classes for the field wrapper.

(date-field #:label "Starts" #:name "start" #:value "2026-10-11")
Procedure: date-range-field KEY: #:label #:name #:start #:end #:start-label #:end-label #:min #:max #:required #:help-text #:error #:disabled #:classes

Render a from/to date pair as one field.

The pair is a <fieldset> with a <legend>, because that is what a group of related controls is, and each half keeps its own label and its own id. The names are derived from name, so a server reads start=...&end=... without the page inventing a second convention.

Both halves are native date inputs, which means the range is usable with scripting off – a page that wants the second date to follow the first can add that behaviour on top, but it is not required for the field to work.

label

The group’s legend, e.g. "Stay".

name

The shared name; the halves become <name>-start and <name>-end.

start

The initial start date, in ISO form.

end

The initial end date, in ISO form.

start-label

The first half’s label. Defaults to "From".

end-label

The second half’s label. Defaults to "To".

min

The earliest accepted date for both halves.

max

The latest accepted date for both halves.

required

Whether both halves are required.

help-text

Optional helper copy for the group.

error

Optional error message for the group.

disabled

Whether both halves are disabled.

classes

Classes for the fieldset.

(date-range-field #:label "Stay" #:name "stay"
                  #:start "2026-10-11" #:end "2026-10-14")
Procedure: dropzone KEY: #:label #:name #:id #:accept #:multiple #:required #:help-text #:error #:disabled #:drop-label #:classes

Render a file field as a drop zone.

This is file-field with #:dropzone? #t and multi-file selection on by default. It is still a native file input under a label, so the picker, the accept filter and the upload work with scripting off; the drop-zone treatment and the list of chosen names are the enhancement.

label

The field’s visible name.

name

The form field name.

id

An explicit identifier; defaults to name.

accept

A native accept list.

multiple

Whether several files may be chosen. Defaults to true.

required

Whether a file is required.

help-text

Optional helper copy.

error

Optional error message.

disabled

Whether the control is disabled.

drop-label

The text inside the drop zone.

classes

Classes for the field wrapper.

(dropzone #:label "Attachments" #:name "files" #:accept "application/pdf")
Procedure: field-control-attributes wiring

Return the attribute pairs a control should carry for wiring.

Splice them into the control’s attribute list:

`((input (@ ,@(field-control-attributes wiring)
            (type "email"))))

The result carries the shared id, the name, the description and error links, required and aria-invalid when they apply, and nothing else – a control’s own type, value and classes are its own.

Procedure: field-described-by wiring

Return the aria-describedby value for the control, or #f.

Procedure: field-error-id wiring

Return the identifier of the field’s error message.

Procedure: field-help-id wiring

Return the identifier of the field’s helper copy.

Procedure: field-id wiring

Return the identifier the control and its label share.

Procedure: field-wiring KEY: #:name #:id #:help-text #:error #:required

Return the identifiers and attributes one control shares with its field shell.

The wiring is an association list rather than an opaque object, so it can be inspected, logged and passed between procedures. Read it through the accessors below, or hand it to field-control-attributes to get the attribute pairs a control should carry.

When name and id are both absent, a unique id is generated, so a field can be rendered without a form name (a filters bar, say) and still have a label bound to it.

name

The form field name.

id

An explicit identifier; defaults to name.

help-text

Optional helper copy, which gets an id and joins the description.

error

Optional error message, which gets an id and joins the description.

required

Whether the control is required.

(field-wiring #:name "email" #:help-text "We never share it.")
Procedure: file-field KEY: #:label #:name #:id #:accept #:multiple #:capture #:required #:help-text #:error #:disabled #:dropzone? #:drop-label #:classes

Render a file picker, optionally as a drop zone.

The control is a native <input type="file">, so the browser’s own picker, its filter for accept, its multi-file handling and its multipart/form-data upload all work without a script. The visible box is a <label> bound to the input, which means clicking it opens the picker and a keyboard reader reaches the input itself.

With dropzone? the box is bigger, says it accepts dropped files, and carries the attributes a small script uses to list the chosen names. Dropping is an enhancement: with scripting off the label still opens the picker, and the filename is still shown by the browser.

label

The field’s visible name.

name

The form field name.

id

An explicit identifier; defaults to name.

accept

A native accept list, e.g. "image/*,.pdf".

multiple

Whether several files may be chosen.

capture

An optional capture hint for a camera on a phone.

required

Whether a file is required.

help-text

Optional helper copy.

error

Optional error message.

disabled

Whether the control is disabled.

dropzone?

Render the larger drop-zone treatment. Defaults to false.

drop-label

The text inside the drop zone.

classes

Classes for the field wrapper.

(file-field #:label "Avatar" #:name "avatar" #:accept "image/*")
(dropzone #:label "Attachments" #:name "files" #:multiple #t)
Procedure: form-field KEY: #:label #:name #:id #:help-text #:error #:required #:label-hidden? #:control #:classes #:label-css #:help-classes #:error-css

Render one labelled, described, validated field shell around a control.

The shell owns the four things every field needs and every hand-written field eventually gets wrong: the label is bound to the control’s id, the helper copy and the error are linked through aria-describedby, the error is announced with role="alert", and the required state is both visible (a marker) and spoken (a visually hidden word plus aria-required on the control).

control is a procedure of one argument, the wiring, and returns the control’s SXML – a node or a list of nodes. A control that renders more than one element (a select and its tags, an input and a dropzone) returns them all and the shell places them in order.

label

The field’s visible name.

name

The form field name.

id

An explicit identifier; defaults to name.

help-text

Optional helper copy, rendered below the control.

error

Optional error message, rendered above the help text and announced.

required

Whether the field is required. Renders a visible marker and a spoken one.

label-hidden?

Render the label visually hidden but still available as the control’s name.

control

A procedure of one argument (the wiring) returning the control’s nodes.

classes

Classes for the field’s wrapper.

label-css

Classes for the label.

help-classes

Classes for the helper copy.

error-css

Classes for the error message.

(form-field #:label "Email"
            #:name "email"
            #:required #t
            #:help-text "We never share it."
            #:control
            (lambda (w)
              `((input (@ ,@(field-control-attributes w)
                          (type "email")
                          (class ,(mk-class '(p-3 rounded-xl))))))))
Procedure: multi-select KEY: #:label #:name #:id #:options #:selected #:size #:required #:help-text #:error #:disabled #:remove-href #:remove-label #:classes

Render a multi-value choice control with the chosen values shown as tags.

The control is a native <select multiple>, which is the only multi-select that works without a script and is announced correctly by assistive technology. The tags above it are a *second view* of the same selection, not a replacement: they show what is chosen without opening the menu, and, when remove-href is given, each tag is a link that removes that value – a plain navigation, so removal works with scripting off too.

label

The field’s visible name.

name

The form field name.

id

An explicit identifier; defaults to name.

options

A list of strings or (value . label) pairs.

selected

The values that start selected.

size

How many rows the menu shows. Defaults to the number of options, capped at 8.

required

Whether at least one value is required.

help-text

Optional helper copy.

error

Optional error message.

disabled

Whether the control is disabled.

remove-href

A procedure of one argument (a selected value) returning the URL that removes it, or #f for tags that are not removable.

remove-label

The accessible label template for a removal link. Defaults to "Remove".

classes

Classes for the field wrapper.

(multi-select #:label "Topics" #:name "topics"
              #:options '((a . "Accessibility") (s . "SSR"))
              #:selected '("ssr"))
Procedure: number-field KEY: #:label #:name #:id #:value #:min #:max #:step #:unit #:placeholder #:required #:help-text #:error #:disabled #:stepper? #:classes #:input-css

Render a numeric field with an optional unit and optional stepper buttons.

The control is a native <input type="number">: the browser’s own spinner, number keyboard on touch, and native validation all come for free. With stepper? the field also gets a pair of buttons that step the value by one step; those are a progressive enhancement (they carry js-only), so with scripting off the native input is still the whole control rather than a broken one.

label

The field’s visible name.

name

The form field name.

id

An explicit identifier; defaults to name.

value

The initial value.

min

The lowest accepted value.

max

The highest accepted value.

step

The increment between accepted values.

unit

An optional unit shown beside the field, e.g. "kg".

placeholder

A hint shown while the field is empty.

required

Whether the field is required.

help-text

Optional helper copy.

error

Optional error message.

disabled

Whether the control is disabled.

stepper?

Add the decrement/increment buttons. Defaults to false.

classes

Classes for the field wrapper.

input-css

Classes for the input itself.

(number-field #:label "Seats" #:name "seats" #:min 1 #:max 12 #:stepper? #t)
Procedure: search-field KEY: #:label #:name #:id #:value #:placeholder #:action #:method #:submit-label #:help-text #:error #:required #:label-hidden? #:classes

Render a search control as a real search landmark.

The form carries role="search", so assistive technology can jump straight to it, and the field is a native <input type="search">: pressing Enter submits, the browser offers its own clear control, and nothing depends on a script. The submit button is a real submit button, so the form still works when it is the only way to send the query.

label

The field’s name. Defaults to "Search".

name

The query parameter name.

id

An explicit identifier; defaults to name.

value

The current query, so a results page shows what was searched for.

placeholder

A hint shown while the field is empty.

action

The URL the search posts to.

method

'get (the default) or 'post.

submit-label

The submit button’s label, or #f for a field that submits on Enter only.

help-text

Optional helper copy.

error

Optional error message.

required

Whether a query is required.

label-hidden?

Hide the label visually while keeping it as the field’s name. Defaults to true, which is what a compact search bar wants.

classes

Classes for the search form.

(search-field #:name "q" #:action "/search" #:value "cozy")
Procedure: stepper KEY: #:label #:name #:id #:value #:min #:max #:step #:unit #:help-text #:error #:required #:disabled #:classes

Render a numeric field with the decrement/increment buttons already on.

This is number-field with #:stepper? #t, named for the thing a page asks for. The buttons are an enhancement over the native input, so the control keeps working with scripting off.

label

The field’s visible name.

name

The form field name.

id

An explicit identifier; defaults to name.

value

The initial value.

min

The lowest accepted value.

max

The highest accepted value.

step

The increment. Defaults to 1.

unit

An optional unit shown beside the field.

help-text

Optional helper copy.

error

Optional error message.

required

Whether the field is required.

disabled

Whether the control is disabled.

classes

Classes for the field wrapper.

(stepper #:label "Quantity" #:name "qty" #:value 1 #:min 1 #:max 9)
Procedure: text-field KEY: #:label #:name #:id #:value #:placeholder #:type #:required #:autocomplete #:input-mode #:pattern #:help-text #:error #:disabled #:readonly #:classes #:input-css

Render a single-line text field through the field shell.

‘input’ is still the right component when a page does not need the shell’s wiring (an inline search box, a bare numeric filter); this one is for a form, where the label, the hint, the error and the required state have to agree with every other field beside it.

label

The field’s visible name.

name

The form field name.

id

An explicit identifier; defaults to name.

value

The initial value.

placeholder

A hint shown while the field is empty.

type

The native input type. Defaults to 'text.

required

Whether the field is required.

autocomplete

An optional autocomplete hint.

input-mode

An optional virtual-keyboard hint.

pattern

An optional validation pattern.

help-text

Optional helper copy.

error

Optional error message.

disabled

Whether the control is disabled.

readonly

Whether the control is read-only.

classes

Classes for the field wrapper.

input-css

Classes for the input itself.

(text-field #:label "Email" #:name "email" #:type 'email #:required #t)
Procedure: time-field KEY: #:label #:name #:id #:value #:min #:max #:step #:required #:help-text #:error #:disabled #:classes

Render a time field backed by the browser’s own time control.

The value is the ISO time form (09:30, or 09:30:00 with seconds), and the browser draws it in the reader’s locale. step is in seconds, which is how a time input says whether it wants minutes or seconds.

label

The field’s visible name.

name

The form field name.

id

An explicit identifier; defaults to name.

value

The initial value, in ISO time form.

min

The earliest accepted time.

max

The latest accepted time.

step

The increment between accepted times, in seconds.

required

Whether the field is required.

help-text

Optional helper copy.

error

Optional error message.

disabled

Whether the control is disabled.

classes

Classes for the field wrapper.

(time-field #:label "Starts at" #:name "starts-at" #:step 900)

Next: (hygguile components fly-out), Previous: (hygguile components feedback), Up: API