Skip to content
Gx
GitHub

Badge

A small label for a status or a count.

<badge.Badge>Badge</badge.Badge>

Installation

Run the command in the app module.

Terminal
gx add badge

The command also installs icons.

The command writes these files.

File Path in the app
Badge.gx ui/badge/Badge.gx
Badge.fixtures.go ui/badge/Badge.fixtures.go
styles.go ui/badge/styles.go

Install icons 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/badge/Badge.gx
package badge

props {
  // Variant sets the visual style: Default, Secondary, Destructive, Outline, Ghost or Link.
  Variant  Variant = Default
  // Href is the URL of the link. A non-empty value renders the badge as an a element, not a span.
  Href     gx.URL = ""
  // Class adds classes to the root element.
  Class    string = ""
  // Children is the badge label.
  Children gx.Node
  // Attrs adds HTML attributes to the root element.
  Attrs    gx.Attrs = nil
}

if p.Href != "" {
  <a href={p.Href} data-slot="badge" data-variant={p.variant()} class={p.class()} {...p.Attrs}>{p.Children}</a>
} else {
  <span data-slot="badge" data-variant={p.variant()} class={p.class()} {...p.Attrs}>{p.Children}</span>
}
ui/badge/Badge.fixtures.go
package badge

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

var BadgeFixtures = gx.Fixtures[BadgeProps]{
	"Default":     {Children: gx.Text("Badge")},
	"Secondary":   {Variant: Secondary, Children: gx.Text("Secondary")},
	"Destructive": {Variant: Destructive, Children: gx.Text("Destructive")},
	"Outline":     {Variant: Outline, Children: gx.Text("Outline")},
	"Ghost":       {Variant: Ghost, Children: gx.Text("Ghost")},
	"Link":        {Variant: Link, Href: gx.URL("/docs"), Children: gx.Text("Link")},
	"WithIcon":    {Variant: Secondary, Children: gx.Frag(icons.CircleCheck(icons.CircleCheckProps{}), gx.Text("Verified"))},
	"Anchor":      {Href: gx.URL("/docs"), Children: gx.Text("Docs")},
}
ui/badge/styles.go
package badge

import "github.com/alternayte/gx"

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

// The variants of badge.Badge.
const (
	Default     Variant = "default"
	Secondary   Variant = "secondary"
	Destructive Variant = "destructive"
	Outline     Variant = "outline"
	Ghost       Variant = "ghost"
	Link        Variant = "link"
)

var variantClass = gx.Enum[Variant]{
	Default:     "bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
	Secondary:   "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
	Destructive: "bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90",
	Outline:     "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
	Ghost:       "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
	Link:        "text-primary underline-offset-4 [a&]:hover:underline",
}

// variant returns the data-variant value; a zero value is Default.
func (p BadgeProps) variant() string {
	if p.Variant == "" {
		return string(Default)
	}
	return string(p.Variant)
}

// class returns the classes of one badge.
func (p BadgeProps) class() string {
	const base = "inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3"
	return gx.Cx(base, variantClass[Variant(p.variant())], p.Class)
}

The theme must define these tokens: --primary, --primary-foreground, --secondary, --secondary-foreground, --destructive, --accent, --accent-foreground, --border, --foreground, --ring.

Usage

<badge.Badge>New</badge.Badge>
<badge.Badge variant={badge.Outline}>Draft</badge.Badge>
<badge.Badge variant={badge.Secondary}><icons.CircleCheck />Verified</badge.Badge>
<badge.Badge variant={badge.Link} href={docsRoute}>Docs</badge.Badge>

The variants are Default, Secondary, Destructive, Outline, Ghost and Link. Set Href to render the badge as a link. A link badge has a hover state.

Examples

Default

<badge.Badge>Badge</badge.Badge>

Secondary

<badge.Badge variant={badge.Secondary}>Secondary</badge.Badge>

Destructive

<badge.Badge variant={badge.Destructive}>Destructive</badge.Badge>

Outline

<badge.Badge variant={badge.Outline}>Outline</badge.Badge>

Ghost

<badge.Badge variant={badge.Ghost}>Ghost</badge.Badge>
<badge.Badge variant={badge.Link} href={gx.URL("/docs")}>Link</badge.Badge>

With icon

<badge.Badge variant={badge.Secondary}>
  <icons.CircleCheck />
  Verified
</badge.Badge>

Anchor

<badge.Badge href={gx.URL("/docs")}>Docs</badge.Badge>

API reference

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

badge.Badge

Prop Type Default Description
Variant Variant Default Variant sets the visual style: Default, Secondary, Destructive, Outline, Ghost or Link.
Href gx.URL "" Href is the URL of the link. A non-empty value renders the badge as an a element, not a span.
Class string "" Class adds classes to the root element.
Children gx.Node Required Children is the badge label.
Attrs gx.Attrs nil Attrs adds HTML attributes to the root element.

Do and do not

Do

  • Keep the text to one or two words.
  • Use Destructive only for an error or a removal.

Don't

  • Do not use a badge as a button. Use a button.
  • Do not use colour as the only signal. Write the status in the text.

Keyboard

Key Action
Tab Moves focus to a link badge.
Enter Follows a link badge.