Skip to content
Gx
GitHub

Toggle

A two-state button.

<toggle.Toggle name="bold">Bold</toggle.Toggle>

Installation

Run the command in the app module.

Terminal
gx add toggle

The command writes these files.

File Path in the app
Toggle.gx ui/toggle/Toggle.gx
Toggle.fixtures.go ui/toggle/Toggle.fixtures.go
styles.go ui/toggle/styles.go

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/toggle/Toggle.gx
package toggle

props {
  // Variant sets the visual style: Default or Outline.
  Variant  Variant = Default
  // Size sets the height and padding: Sm, Md or Lg.
  Size     Size = Md
  // Name is the form field name.
  Name     string = ""
  // Value is the value the form submits when the toggle is pressed.
  Value    string = "on"
  // Pressed sets the toggle to pressed for the first render.
  Pressed  bool = false
  // Disabled disables the toggle.
  Disabled bool = false
  // Invalid sets aria-invalid on the input and shows the error style.
  Invalid  bool = false
  // Class adds classes to the root element.
  Class    string = ""
  // Children is the toggle label.
  Children gx.Node
  // Attrs adds HTML attributes to the root element.
  Attrs    gx.Attrs = nil
}

<label class={p.class()} {...p.Attrs}>
  <input type="checkbox" name={p.Name} value={p.Value} checked={p.Pressed} disabled={p.Disabled} aria-invalid={p.invalid()} class="peer sr-only" />
  {p.Children}
</label>
ui/toggle/Toggle.fixtures.go
package toggle

import "github.com/alternayte/gx"

var ToggleFixtures = gx.Fixtures[ToggleProps]{
	"Off":       {Name: "bold", Children: gx.Text("Bold")},
	"On":        {Name: "bold", Pressed: true, Children: gx.Text("Bold")},
	"Disabled":  {Name: "bold", Disabled: true, Children: gx.Text("Bold")},
	"Outline":   {Name: "bold", Variant: Outline, Children: gx.Text("Bold")},
	"OutlineOn": {Name: "bold", Variant: Outline, Pressed: true, Children: gx.Text("Bold")},
	"Invalid":   {Name: "bold", Variant: Outline, Invalid: true, Children: gx.Text("Bold")},
	"Small":     {Name: "bold", Variant: Outline, Size: Sm, Children: gx.Text("Bold")},
	"Large":     {Name: "bold", Variant: Outline, Size: Lg, Children: gx.Text("Bold")},
}
ui/toggle/styles.go
package toggle

import "github.com/alternayte/gx"

// Variant is the visual style of a toggle.
type Variant string

// The variants of toggle.Toggle.
const (
	Default Variant = "default"
	Outline Variant = "outline"
)

var variantClass = gx.Enum[Variant]{
	Default: "bg-transparent",
	Outline: "border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground",
}

// Size is the height and padding of a toggle.
type Size string

// The sizes of toggle.Toggle.
const (
	Sm Size = "sm"
	Md Size = "md"
	Lg Size = "lg"
)

var sizeClass = gx.Enum[Size]{
	Sm: "h-8 min-w-8 px-1.5",
	Md: "h-9 min-w-9 px-2",
	Lg: "h-10 min-w-10 px-2.5",
}

// class returns the classes of one toggle. The label carries the recipe and
// reads the state of the checkbox inside it.
func (p ToggleProps) class() string {
	const base = "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none hover:bg-muted hover:text-muted-foreground has-[:focus-visible]:border-ring has-[:focus-visible]:ring-[3px] has-[:focus-visible]:ring-ring/50 has-[:disabled]:pointer-events-none has-[:disabled]:opacity-50 has-[[aria-invalid=true]]:border-destructive has-[[aria-invalid=true]]:ring-destructive/20 has-[:checked]:bg-accent has-[:checked]:text-accent-foreground dark:has-[[aria-invalid=true]]:ring-destructive/40 motion-reduce:transition-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
	variant := p.Variant
	if variant == "" {
		variant = Default
	}
	size := p.Size
	if size == "" {
		size = Md
	}
	return gx.Cx(base, variantClass[variant], sizeClass[size], p.Class)
}

// invalid returns the aria-invalid value of the input.
func (p ToggleProps) invalid() string {
	if p.Invalid {
		return "true"
	}
	return "false"
}

The theme must define these tokens: --muted, --muted-foreground, --accent, --accent-foreground, --input, --ring, --destructive.

Usage

<toggle.Toggle name="bold" pressed={p.Bold}>Bold</toggle.Toggle>
<toggle.Toggle name="italic" variant={toggle.Outline} size={toggle.Sm}>Italic</toggle.Toggle>

The toggle is a styled checkbox, so it works without JavaScript. The variants are toggle.Default and toggle.Outline. The sizes are toggle.Sm, toggle.Md and toggle.Lg. Disabled and Invalid set the state of the checkbox.

Examples

Off

<toggle.Toggle name="bold">Bold</toggle.Toggle>

On

<toggle.Toggle name="bold" pressed>Bold</toggle.Toggle>

Disabled

<toggle.Toggle name="bold" disabled>Bold</toggle.Toggle>

Outline

<toggle.Toggle name="bold" variant={toggle.Outline}>Bold</toggle.Toggle>

Outline on

<toggle.Toggle name="bold" variant={toggle.Outline} pressed>Bold</toggle.Toggle>

Invalid

<toggle.Toggle name="bold" variant={toggle.Outline} invalid>Bold</toggle.Toggle>

Small

<toggle.Toggle name="bold" variant={toggle.Outline} size={toggle.Sm}>Bold</toggle.Toggle>

Large

<toggle.Toggle name="bold" variant={toggle.Outline} size={toggle.Lg}>Bold</toggle.Toggle>

API reference

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

toggle.Toggle

Prop Type Default Description
Variant Variant Default Variant sets the visual style: Default or Outline.
Size Size Md Size sets the height and padding: Sm, Md or Lg.
Name string "" Name is the form field name.
Value string "on" Value is the value the form submits when the toggle is pressed.
Pressed bool false Pressed sets the toggle to pressed for the first render.
Disabled bool false Disabled disables the toggle.
Invalid bool false Invalid sets aria-invalid on the input and shows the error style.
Class string "" Class adds classes to the root element.
Children gx.Node Required Children is the toggle label.
Attrs gx.Attrs nil Attrs adds HTML attributes to the root element.

Do and do not

Do

  • Give the toggle a name that reads as a state.
  • Use it for a formatting choice.

Don't

  • Do not use a toggle for an immediate setting. Use a switch.
  • Do not use a toggle for a group of choices. Use a toggle group.

Keyboard

Key Action
Tab Moves focus to the toggle.
Space Toggles the state.