Skip to content
Gx
GitHub

Field

Layout and labels for one form field.

<field.Field>
  <field.FieldLabel for="field-email">Email</field.FieldLabel>
  <input.Input id="field-email" type="email" placeholder="you@example.com" />
  <field.FieldDescription>We never share your email.</field.FieldDescription>
</field.Field>

Installation

Run the command in the app module.

Terminal
gx add field

The command also installs input.

The command writes these files.

File Path in the app
Field.gx ui/field/Field.gx
FieldLabel.gx ui/field/FieldLabel.gx
FieldDescription.gx ui/field/FieldDescription.gx
FieldError.gx ui/field/FieldError.gx
FieldGroup.gx ui/field/FieldGroup.gx
FieldSet.gx ui/field/FieldSet.gx
FieldLegend.gx ui/field/FieldLegend.gx
Field.fixtures.go ui/field/Field.fixtures.go
styles.go ui/field/styles.go
FieldDescription.fixtures.go ui/field/FieldDescription.fixtures.go
FieldError.fixtures.go ui/field/FieldError.fixtures.go
FieldGroup.fixtures.go ui/field/FieldGroup.fixtures.go
FieldLabel.fixtures.go ui/field/FieldLabel.fixtures.go
FieldLegend.fixtures.go ui/field/FieldLegend.fixtures.go
FieldSet.fixtures.go ui/field/FieldSet.fixtures.go
FieldContent.gx ui/field/FieldContent.gx
FieldContent.fixtures.go ui/field/FieldContent.fixtures.go
FieldTitle.gx ui/field/FieldTitle.gx
FieldTitle.fixtures.go ui/field/FieldTitle.fixtures.go
FieldSeparator.gx ui/field/FieldSeparator.gx
FieldSeparator.fixtures.go ui/field/FieldSeparator.fixtures.go

Install input first.

Copy each file to its path in the app. Change each import of a registry package to the path of that package in the app.

ui/field/Field.gx
package field

props {
  // Orientation sets the direction of the label and the control: Vertical, Horizontal or
  // Responsive. Responsive is vertical in a narrow field group and horizontal in a wide one.
  Orientation Orientation = Vertical
  // Invalid sets data-invalid on the field. The field text takes the destructive colour.
  Invalid     bool = false
  // Disabled sets data-disabled on the field. The label and the title fade.
  Disabled    bool = false
  // Class adds classes to the root element.
  Class       string = ""
  // Children is the content of the field: the label, the control and the description.
  Children    gx.Node
  // Attrs adds HTML attributes to the root element.
  Attrs       gx.Attrs = nil
}

<div role="group" data-slot="field" data-orientation={p.orientation()} class={gx.Cx("group/field flex w-full gap-3 data-[invalid=true]:text-destructive", orientationClass[Orientation(p.orientation())], p.Class)} {...p.attrs()}>{p.Children}</div>
ui/field/FieldLabel.gx
package field

props {
  // For is the id of the control that the label names. Empty omits the for attribute.
  For      string = ""
  // Class adds classes to the root element.
  Class    string = ""
  // Children is the label text.
  Children gx.Node
  // Attrs adds HTML attributes to the root element.
  Attrs    gx.Attrs = nil
}

<label data-slot="field-label" class={gx.Cx("flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50", "group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4 has-[:checked]:border-primary has-[:checked]:bg-primary/5 dark:has-[:checked]:bg-primary/10", p.Class)} {...p.attrs()}>{p.Children}</label>
ui/field/FieldDescription.gx
package field

props {
  // Class adds classes to the root element.
  Class    string = ""
  // Children is the description text.
  Children gx.Node
  // Attrs adds HTML attributes to the root element.
  Attrs    gx.Attrs = nil
}

<p data-slot="field-description" class={gx.Cx("text-sm leading-normal font-normal text-muted-foreground group-has-[[data-orientation=horizontal]]/field:text-balance last:mt-0 nth-last-2:-mt-1 [[data-variant=legend]+&]:-mt-1.5 [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary", p.Class)} {...p.Attrs}>{p.Children}</p>
ui/field/FieldError.gx
package field

props {
  // Errors holds the error messages. Empty and repeated messages do not show; two or more
  // show as a list.
  Errors   []string = nil
  // Class adds classes to the root element.
  Class    string = ""
  // Children is the content of the error. It replaces Errors.
  Children gx.Node = nil
  // Attrs adds HTML attributes to the root element.
  Attrs    gx.Attrs = nil
}

if p.Children != nil || len(p.messages()) > 0 {
  <div role="alert" data-slot="field-error" class={gx.Cx("text-sm font-normal text-destructive", p.Class)} {...p.Attrs}>
    if p.Children != nil {
      {p.Children}
    } else if len(p.messages()) == 1 {
      {p.messages()[0]}
    } else {
      <ul class="ml-4 flex list-disc flex-col gap-1">
        for _, message := range p.messages() {
          <li>{message}</li>
        }
      </ul>
    }
  </div>
}
ui/field/FieldGroup.gx
package field

props {
  // Class adds classes to the root element.
  Class    string = ""
  // Children is the content of the group: the fields, the field sets and the separators.
  Children gx.Node
  // Attrs adds HTML attributes to the root element.
  Attrs    gx.Attrs = nil
}

<div data-slot="field-group" class={gx.Cx("group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4", p.Class)} {...p.Attrs}>{p.Children}</div>
ui/field/FieldSet.gx
package field

props {
  // Class adds classes to the root element.
  Class    string = ""
  // Children is the content of the field set.
  Children gx.Node
  // Attrs adds HTML attributes to the root element.
  Attrs    gx.Attrs = nil
}

<fieldset data-slot="field-set" class={gx.Cx("flex flex-col gap-6 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3", p.Class)} {...p.Attrs}>{p.Children}</fieldset>
ui/field/FieldLegend.gx
package field

props {
  // Variant sets the text size: Legend or Label. Label is the smaller size.
  Variant  Variant = Legend
  // Class adds classes to the root element.
  Class    string = ""
  // Children is the legend text.
  Children gx.Node
  // Attrs adds HTML attributes to the root element.
  Attrs    gx.Attrs = nil
}

<legend data-slot="field-legend" data-variant={p.variant()} class={gx.Cx("mb-3 font-medium data-[variant=legend]:text-base data-[variant=label]:text-sm", p.Class)} {...p.Attrs}>{p.Children}</legend>
ui/field/Field.fixtures.go
package field

import (
	"github.com/alternayte/gx"
	"github.com/alternayte/gx/registry/input"
)

var FieldFixtures = gx.Fixtures[FieldProps]{
	"LabelAndInput": {Children: gx.Frag(
		FieldLabel(FieldLabelProps{For: "field-email", Children: gx.Text("Email")}),
		input.Input(input.InputProps{Id: "field-email", Type: "email", Placeholder: "you@example.com"}),
		FieldDescription(FieldDescriptionProps{Children: gx.Text("We never share your email.")}),
	)},
	"Invalid": {Invalid: true, Children: gx.Frag(
		FieldLabel(FieldLabelProps{For: "field-invalid", Children: gx.Text("Email")}),
		input.Input(input.InputProps{Id: "field-invalid", Type: "email", Value: "ada@", Attrs: gx.Attrs{{Key: "aria-invalid", Value: "true"}}}),
		FieldError(FieldErrorProps{Children: gx.Text("Enter a valid email address.")}),
	)},
	"Disabled": {Disabled: true, Children: gx.Frag(
		FieldLabel(FieldLabelProps{For: "field-disabled", Children: gx.Text("Email")}),
		input.Input(input.InputProps{Id: "field-disabled", Type: "email", Attrs: gx.Attrs{gx.Bool("disabled", true)}}),
	)},
	"Horizontal": {Orientation: Horizontal, Children: gx.Frag(
		FieldContent(FieldContentProps{Children: gx.Frag(
			FieldTitle(FieldTitleProps{Children: gx.Text("Product news")}),
			FieldDescription(FieldDescriptionProps{Children: gx.Text("One message each month.")}),
		)}),
		gx.El("input", gx.Attrs{
			{Key: "type", Value: "checkbox"},
			{Key: "aria-label", Value: "Product news"},
			{Key: "class", Value: "size-4 accent-primary"},
		}),
	)},
	"Responsive": {Orientation: Responsive, Children: gx.Frag(
		FieldLabel(FieldLabelProps{For: "field-responsive", Children: gx.Text("Name")}),
		input.Input(input.InputProps{Id: "field-responsive"}),
	)},
}

// FieldWrap renders the field in a field group, as a form uses it. The
// group is the container that a responsive field measures.
func FieldWrap(n gx.Node) gx.Node {
	return FieldGroup(FieldGroupProps{Children: n})
}
ui/field/styles.go
package field

import "github.com/alternayte/gx"

// Variant is the size of a field legend.
type Variant string

// The variants of field.FieldLegend.
const (
	Legend Variant = "legend"
	Label  Variant = "label"
)

// variant returns the data-variant value; a zero value is Legend. The
// legend size and the gap below it follow this attribute.
func (p FieldLegendProps) variant() string {
	if p.Variant == "" {
		return string(Legend)
	}
	return string(p.Variant)
}

// Orientation is the direction of the label and the control of a field.
type Orientation string

// The orientations of field.Field. Responsive is vertical in a narrow
// field group and horizontal in a wide one.
const (
	Vertical   Orientation = "vertical"
	Horizontal Orientation = "horizontal"
	Responsive Orientation = "responsive"
)

var orientationClass = gx.Enum[Orientation]{
	Vertical:   "flex-col [&>*]:w-full [&>.sr-only]:w-auto",
	Horizontal: "flex-row items-center [&>[data-slot=field-label]]:flex-auto has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>:is([role=checkbox],[role=radio],input[type=checkbox],input[type=radio])]:mt-px",
	Responsive: "flex-col @md/field-group:flex-row @md/field-group:items-center [&>*]:w-full @md/field-group:[&>*]:w-auto [&>.sr-only]:w-auto @md/field-group:[&>[data-slot=field-label]]:flex-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>:is([role=checkbox],[role=radio],input[type=checkbox],input[type=radio])]:mt-px",
}

// orientation returns the data-orientation value; a zero value is Vertical.
func (p FieldProps) orientation() string {
	if p.Orientation == "" {
		return string(Vertical)
	}
	return string(p.Orientation)
}

// attrs returns the state attributes of one field, then the caller's. The
// field and its label read data-invalid and data-disabled.
func (p FieldProps) attrs() gx.Attrs {
	var a gx.Attrs
	if p.Invalid {
		a = append(a, gx.Attr{Key: "data-invalid", Value: "true"})
	}
	if p.Disabled {
		a = append(a, gx.Attr{Key: "data-disabled", Value: "true"})
	}
	return append(a, p.Attrs...)
}

// attrs returns the for attribute when the label names a control, then the
// caller's attributes.
func (p FieldLabelProps) attrs() gx.Attrs {
	if p.For == "" {
		return p.Attrs
	}
	return append(gx.Attrs{{Key: "for", Value: p.For}}, p.Attrs...)
}

// messages returns the distinct, non-empty errors in their first order.
func (p FieldErrorProps) messages() []string {
	seen := make(map[string]bool, len(p.Errors))
	out := make([]string, 0, len(p.Errors))
	for _, message := range p.Errors {
		if message == "" || seen[message] {
			continue
		}
		seen[message] = true
		out = append(out, message)
	}
	return out
}
ui/field/FieldDescription.fixtures.go
package field

import "github.com/alternayte/gx"

var FieldDescriptionFixtures = gx.Fixtures[FieldDescriptionProps]{"Text": {Children: gx.Text("A short hint.")}}
ui/field/FieldError.fixtures.go
package field

import "github.com/alternayte/gx"

var FieldErrorFixtures = gx.Fixtures[FieldErrorProps]{
	"Error": {Children: gx.Text("This field is required.")},
	"One":   {Errors: []string{"Enter a valid email address.", "Enter a valid email address."}},
	"List":  {Errors: []string{"Use 12 characters or more.", "Use one number."}},
}
ui/field/FieldGroup.fixtures.go
package field

import (
	"github.com/alternayte/gx"
	"github.com/alternayte/gx/registry/input"
)

var FieldGroupFixtures = gx.Fixtures[FieldGroupProps]{
	"TwoFields": {Children: gx.Frag(
		Field(FieldProps{Children: gx.Frag(
			FieldLabel(FieldLabelProps{For: "group-name", Children: gx.Text("Name")}),
			input.Input(input.InputProps{Id: "group-name"}),
		)}),
		FieldSeparator(FieldSeparatorProps{}),
		Field(FieldProps{Children: gx.Frag(
			FieldLabel(FieldLabelProps{For: "group-email", Children: gx.Text("Email")}),
			input.Input(input.InputProps{Id: "group-email", Type: "email"}),
		)}),
	)},
}
ui/field/FieldLabel.fixtures.go
package field

import "github.com/alternayte/gx"

var FieldLabelFixtures = gx.Fixtures[FieldLabelProps]{"Label": {For: "email", Children: gx.Text("Email")}}
ui/field/FieldLegend.fixtures.go
package field

import "github.com/alternayte/gx"

var FieldLegendFixtures = gx.Fixtures[FieldLegendProps]{
	"Legend": {Children: gx.Text("Address")},
	"Label":  {Variant: Label, Children: gx.Text("Address")},
}

// FieldLegendWrap renders the legend in a field set, where a legend
// belongs.
func FieldLegendWrap(n gx.Node) gx.Node {
	return FieldSet(FieldSetProps{Children: n})
}
ui/field/FieldSet.fixtures.go
package field

import (
	"github.com/alternayte/gx"
	"github.com/alternayte/gx/registry/input"
)

var FieldSetFixtures = gx.Fixtures[FieldSetProps]{
	"Address": {Children: gx.Frag(
		FieldLegend(FieldLegendProps{Children: gx.Text("Address")}),
		FieldDescription(FieldDescriptionProps{Children: gx.Text("We send the invoice to this address.")}),
		FieldGroup(FieldGroupProps{Children: gx.Frag(
			Field(FieldProps{Children: gx.Frag(
				FieldLabel(FieldLabelProps{For: "set-street", Children: gx.Text("Street")}),
				input.Input(input.InputProps{Id: "set-street"}),
			)}),
			Field(FieldProps{Children: gx.Frag(
				FieldLabel(FieldLabelProps{For: "set-city", Children: gx.Text("City")}),
				input.Input(input.InputProps{Id: "set-city"}),
			)}),
		)}),
	)},
}
ui/field/FieldContent.gx
package field

props {
  // Class adds classes to the root element.
  Class    string = ""
  // Children is the content of the column: the title and the description.
  Children gx.Node
  // Attrs adds HTML attributes to the root element.
  Attrs    gx.Attrs = nil
}

<div data-slot="field-content" class={gx.Cx("group/field-content flex flex-1 flex-col gap-1.5 leading-snug", p.Class)} {...p.Attrs}>{p.Children}</div>
ui/field/FieldContent.fixtures.go
package field

import "github.com/alternayte/gx"

var FieldContentFixtures = gx.Fixtures[FieldContentProps]{
	"TitleAndDescription": {Children: gx.Frag(
		FieldTitle(FieldTitleProps{Children: gx.Text("Product news")}),
		FieldDescription(FieldDescriptionProps{Children: gx.Text("One message each month.")}),
	)},
}
ui/field/FieldTitle.gx
package field

props {
  // Class adds classes to the root element.
  Class    string = ""
  // Children is the title text.
  Children gx.Node
  // Attrs adds HTML attributes to the root element.
  Attrs    gx.Attrs = nil
}

<div data-slot="field-label" class={gx.Cx("flex w-fit items-center gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50", p.Class)} {...p.Attrs}>{p.Children}</div>
ui/field/FieldTitle.fixtures.go
package field

import "github.com/alternayte/gx"

var FieldTitleFixtures = gx.Fixtures[FieldTitleProps]{"Title": {Children: gx.Text("Product news")}}
ui/field/FieldSeparator.gx
package field

props {
  // Class adds classes to the root element.
  Class    string = ""
  // Children is the text on the line. Nil shows the line only.
  Children gx.Node = nil
  // Attrs adds HTML attributes to the root element.
  Attrs    gx.Attrs = nil
}

<div data-slot="field-separator" data-content={p.Children != nil} class={gx.Cx("relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2", p.Class)} {...p.Attrs}>
  <div data-slot="separator" role="none" data-orientation="horizontal" class="absolute inset-0 top-1/2 h-px w-full shrink-0 bg-border"></div>
  if p.Children != nil {
    <span data-slot="field-separator-content" class="relative mx-auto block w-fit bg-background px-2 text-muted-foreground">{p.Children}</span>
  }
</div>
ui/field/FieldSeparator.fixtures.go
package field

import "github.com/alternayte/gx"

var FieldSeparatorFixtures = gx.Fixtures[FieldSeparatorProps]{
	"Line": {},
	"Text": {Children: gx.Text("Or continue with")},
}

The theme must define these tokens: --destructive, --muted-foreground, --border, --background, --primary.

Usage

<field.FieldGroup>
  <field.Field>
    <field.FieldLabel for="email">Email</field.FieldLabel>
    <input.Input id="email" name="email" type="email" />
    <field.FieldDescription>We never share your email.</field.FieldDescription>
  </field.Field>
  <field.FieldSeparator>Or</field.FieldSeparator>
  <field.Field orientation={field.Horizontal}>
    <field.FieldContent>
      <field.FieldTitle>Product news</field.FieldTitle>
      <field.FieldDescription>One message each month.</field.FieldDescription>
    </field.FieldContent>
    <switches.Switch name="news" label="Product news" />
  </field.Field>
</field.FieldGroup>

Field has three orientations: Vertical, Horizontal and Responsive. A responsive field is horizontal when its FieldGroup is wide.

FieldSet and FieldLegend group related fields. FieldGroup sets the space between fields. FieldError shows its children, or the distinct messages of Errors. It renders nothing when it has no message.

For a generated form field with rules and errors, use the gx.FormField control.

Examples

Field: Label and input

<field.Field>
  <field.FieldLabel for="field-email">Email</field.FieldLabel>
  <input.Input id="field-email" type="email" placeholder="you@example.com" />
  <field.FieldDescription>We never share your email.</field.FieldDescription>
</field.Field>

Field: Invalid

<field.Field invalid>
  <field.FieldLabel for="field-invalid">Email</field.FieldLabel>
  <input.Input id="field-invalid" type="email" value="ada@" attrs={gx.Attrs{{Key: "aria-invalid", Value: "true"}}} />
  <field.FieldError>Enter a valid email address.</field.FieldError>
</field.Field>

Field: Disabled

<field.Field disabled>
  <field.FieldLabel for="field-disabled">Email</field.FieldLabel>
  <input.Input id="field-disabled" type="email" attrs={gx.Attrs{gx.Bool("disabled", true)}} />
</field.Field>

Field: Horizontal

<field.Field orientation={field.Horizontal}>
  <field.FieldContent>
    <field.FieldTitle>Product news</field.FieldTitle>
    <field.FieldDescription>One message each month.</field.FieldDescription>
  </field.FieldContent>
  <input type="checkbox" aria-label="Product news" class="size-4 accent-primary" />
</field.Field>

Field: Responsive

<field.Field orientation={field.Responsive}>
  <field.FieldLabel for="field-responsive">Name</field.FieldLabel>
  <input.Input id="field-responsive" />
</field.Field>

FieldContent: Title and description

<field.FieldContent>
  <field.FieldTitle>Product news</field.FieldTitle>
  <field.FieldDescription>One message each month.</field.FieldDescription>
</field.FieldContent>

FieldDescription: Text

<field.FieldDescription>A short hint.</field.FieldDescription>

FieldError: Error

<field.FieldError>This field is required.</field.FieldError>

FieldError: One

<field.FieldError errors={[]string{"Enter a valid email address.", "Enter a valid email address."}} />

FieldError: List

<field.FieldError errors={[]string{"Use 12 characters or more.", "Use one number."}} />

FieldGroup: Two fields

<field.FieldGroup>
  <field.Field>
    <field.FieldLabel for="group-name">Name</field.FieldLabel>
    <input.Input id="group-name" />
  </field.Field>
  <field.FieldSeparator />
  <field.Field>
    <field.FieldLabel for="group-email">Email</field.FieldLabel>
    <input.Input id="group-email" type="email" />
  </field.Field>
</field.FieldGroup>

FieldLabel: Label

<field.FieldLabel for="email">Email</field.FieldLabel>

FieldLegend: Legend

<field.FieldLegend>Address</field.FieldLegend>

FieldLegend: Label

<field.FieldLegend variant={field.Label}>Address</field.FieldLegend>

FieldSeparator: Line

<field.FieldSeparator />

FieldSeparator: Text

<field.FieldSeparator>Or continue with</field.FieldSeparator>

FieldSet: Address

<field.FieldSet>
  <field.FieldLegend>Address</field.FieldLegend>
  <field.FieldDescription>We send the invoice to this address.</field.FieldDescription>
  <field.FieldGroup>
    <field.Field>
      <field.FieldLabel for="set-street">Street</field.FieldLabel>
      <input.Input id="set-street" />
    </field.Field>
    <field.Field>
      <field.FieldLabel for="set-city">City</field.FieldLabel>
      <input.Input id="set-city" />
    </field.Field>
  </field.FieldGroup>
</field.FieldSet>

FieldTitle: Title

<field.FieldTitle>Product news</field.FieldTitle>

API reference

A tag sets a prop by its name with a lower-case first letter: Class is class.

field.Field

Prop Type Default Description
Orientation Orientation Vertical Orientation sets the direction of the label and the control: Vertical, Horizontal or Responsive. Responsive is vertical in a narrow field group and horizontal in a wide one.
Invalid bool false Invalid sets data-invalid on the field. The field text takes the destructive colour.
Disabled bool false Disabled sets data-disabled on the field. The label and the title fade.
Class string "" Class adds classes to the root element.
Children gx.Node Required Children is the content of the field: the label, the control and the description.
Attrs gx.Attrs nil Attrs adds HTML attributes to the root element.

field.FieldContent

Prop Type Default Description
Class string "" Class adds classes to the root element.
Children gx.Node Required Children is the content of the column: the title and the description.
Attrs gx.Attrs nil Attrs adds HTML attributes to the root element.

field.FieldDescription

Prop Type Default Description
Class string "" Class adds classes to the root element.
Children gx.Node Required Children is the description text.
Attrs gx.Attrs nil Attrs adds HTML attributes to the root element.

field.FieldError

Prop Type Default Description
Errors []string nil Errors holds the error messages. Empty and repeated messages do not show; two or more show as a list.
Class string "" Class adds classes to the root element.
Children gx.Node nil Children is the content of the error. It replaces Errors.
Attrs gx.Attrs nil Attrs adds HTML attributes to the root element.

field.FieldGroup

Prop Type Default Description
Class string "" Class adds classes to the root element.
Children gx.Node Required Children is the content of the group: the fields, the field sets and the separators.
Attrs gx.Attrs nil Attrs adds HTML attributes to the root element.

field.FieldLabel

Prop Type Default Description
For string "" For is the id of the control that the label names. Empty omits the for attribute.
Class string "" Class adds classes to the root element.
Children gx.Node Required Children is the label text.
Attrs gx.Attrs nil Attrs adds HTML attributes to the root element.

field.FieldLegend

Prop Type Default Description
Variant Variant Legend Variant sets the text size: Legend or Label. Label is the smaller size.
Class string "" Class adds classes to the root element.
Children gx.Node Required Children is the legend text.
Attrs gx.Attrs nil Attrs adds HTML attributes to the root element.

field.FieldSeparator

Prop Type Default Description
Class string "" Class adds classes to the root element.
Children gx.Node nil Children is the text on the line. Nil shows the line only.
Attrs gx.Attrs nil Attrs adds HTML attributes to the root element.

field.FieldSet

Prop Type Default Description
Class string "" Class adds classes to the root element.
Children gx.Node Required Children is the content of the field set.
Attrs gx.Attrs nil Attrs adds HTML attributes to the root element.

field.FieldTitle

Prop Type Default Description
Class string "" Class adds classes to the root element.
Children gx.Node Required Children is the title text.
Attrs gx.Attrs nil Attrs adds HTML attributes to the root element.

Do and do not

Do

  • Set Invalid on the field when a rule fails. Set aria-invalid on the control too.
  • Put the error in FieldError below the control.
  • Set Disabled on the field when its control is disabled.

Don't

  • Do not use a field for a layout grid. Use CSS.
  • Do not set For to an id that does not exist.

Keyboard

Key Action
None The component is static.