Item
A row for one entry in a list.
<item.Item variant={item.Outline}>
<item.ItemMedia variant={item.MediaIcon}><icons.Info /></item.ItemMedia>
<item.ItemContent>
<item.ItemTitle>Item title</item.ItemTitle>
<item.ItemDescription>A short description of the item.</item.ItemDescription>
</item.ItemContent>
<item.ItemActions><button.Button variant={button.Outline} size={button.Sm}>Open</button.Button></item.ItemActions>
</item.Item>Installation
Run the command in the app module.
gx add itemThe command also installs button and icons.
The command writes these files.
| File | Path in the app |
|---|---|
Item.gx |
ui/item/Item.gx |
ItemMedia.gx |
ui/item/ItemMedia.gx |
ItemContent.gx |
ui/item/ItemContent.gx |
ItemTitle.gx |
ui/item/ItemTitle.gx |
ItemDescription.gx |
ui/item/ItemDescription.gx |
ItemActions.gx |
ui/item/ItemActions.gx |
ItemHeader.gx |
ui/item/ItemHeader.gx |
ItemFooter.gx |
ui/item/ItemFooter.gx |
Item.fixtures.go |
ui/item/Item.fixtures.go |
ItemActions.fixtures.go |
ui/item/ItemActions.fixtures.go |
ItemContent.fixtures.go |
ui/item/ItemContent.fixtures.go |
ItemDescription.fixtures.go |
ui/item/ItemDescription.fixtures.go |
ItemFooter.fixtures.go |
ui/item/ItemFooter.fixtures.go |
ItemHeader.fixtures.go |
ui/item/ItemHeader.fixtures.go |
ItemMedia.fixtures.go |
ui/item/ItemMedia.fixtures.go |
ItemTitle.fixtures.go |
ui/item/ItemTitle.fixtures.go |
ItemGroup.gx |
ui/item/ItemGroup.gx |
ItemGroup.fixtures.go |
ui/item/ItemGroup.fixtures.go |
ItemSeparator.gx |
ui/item/ItemSeparator.gx |
ItemSeparator.fixtures.go |
ui/item/ItemSeparator.fixtures.go |
styles.go |
ui/item/styles.go |
Install button and 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.
package item
props {
// Variant sets the surface: Default, Outline or Muted.
Variant Variant = Default
// Size sets the padding and the gap: Md or Sm.
Size Size = Md
// Href is the link target. With a value the root is an a element; empty gives a div.
Href gx.URL = ""
// Class adds classes to the root element.
Class string = ""
// Children is the content of the item.
Children gx.Node
// Attrs adds HTML attributes to the root element.
Attrs gx.Attrs = nil
}
if p.Href != "" {
<a href={p.Href} data-slot="item" data-variant={p.variant()} data-size={p.size()} class={p.class()} {...p.Attrs}>{p.Children}</a>
} else {
<div data-slot="item" data-variant={p.variant()} data-size={p.size()} class={p.class()} {...p.Attrs}>{p.Children}</div>
}package item
props {
// Variant sets the look of the media: MediaDefault, MediaIcon or MediaImage.
Variant Media = MediaDefault
// Class adds classes to the root element.
Class string = ""
// Children is the media, for example an icon or an image.
Children gx.Node
// Attrs adds HTML attributes to the root element.
Attrs gx.Attrs = nil
}
<div data-slot="item-media" data-variant={p.variant()} class={gx.Cx("flex shrink-0 items-center justify-center gap-2 group-has-[[data-slot=item-description]]/item:translate-y-0.5 group-has-[[data-slot=item-description]]/item:self-start [&_svg]:pointer-events-none", mediaClass[Media(p.variant())], p.Class)} {...p.Attrs}>{p.Children}</div>package item
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="item-content" class={gx.Cx("flex flex-1 flex-col gap-1 [&+[data-slot=item-content]]:flex-none", p.Class)} {...p.Attrs}>{p.Children}</div>package item
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="item-title" class={gx.Cx("flex w-fit items-center gap-2 text-sm leading-snug font-medium", p.Class)} {...p.Attrs}>{p.Children}</div>package item
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="item-description" class={gx.Cx("line-clamp-2 text-sm leading-normal font-normal text-balance text-muted-foreground [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary", p.Class)} {...p.Attrs}>{p.Children}</p>package item
props {
// Class adds classes to the root element.
Class string = ""
// Children is the actions of the item, for example buttons.
Children gx.Node
// Attrs adds HTML attributes to the root element.
Attrs gx.Attrs = nil
}
<div data-slot="item-actions" class={gx.Cx("flex items-center gap-2", p.Class)} {...p.Attrs}>{p.Children}</div>package item
props {
// Class adds classes to the root element.
Class string = ""
// Children is the content of the header.
Children gx.Node
// Attrs adds HTML attributes to the root element.
Attrs gx.Attrs = nil
}
<div data-slot="item-header" class={gx.Cx("flex basis-full items-center justify-between gap-2", p.Class)} {...p.Attrs}>{p.Children}</div>package item
props {
// Class adds classes to the root element.
Class string = ""
// Children is the content of the footer.
Children gx.Node
// Attrs adds HTML attributes to the root element.
Attrs gx.Attrs = nil
}
<div data-slot="item-footer" class={gx.Cx("flex basis-full items-center justify-between gap-2", p.Class)} {...p.Attrs}>{p.Children}</div>package item
import (
"github.com/alternayte/gx"
"github.com/alternayte/gx/registry/button"
"github.com/alternayte/gx/registry/icons"
)
// body is the content of the item fixtures: an icon, a title with a
// description, and one action.
func body() gx.Node {
return gx.Frag(
ItemMedia(ItemMediaProps{Variant: MediaIcon, Children: icons.Info(icons.InfoProps{})}),
ItemContent(ItemContentProps{Children: gx.Frag(
ItemTitle(ItemTitleProps{Children: gx.Text("Item title")}),
ItemDescription(ItemDescriptionProps{Children: gx.Text("A short description of the item.")}),
)}),
ItemActions(ItemActionsProps{Children: button.Button(button.ButtonProps{Variant: button.Outline, Size: button.Sm, Children: gx.Text("Open")})}),
)
}
var ItemFixtures = gx.Fixtures[ItemProps]{
"Full": {Variant: Outline, Children: body()},
"Default": {Children: body()},
"Muted": {Variant: Muted, Children: body()},
"Small": {Variant: Outline, Size: Sm, Children: gx.Frag(
ItemMedia(ItemMediaProps{Children: icons.CircleCheck(icons.CircleCheckProps{Class: "size-5"})}),
ItemContent(ItemContentProps{Children: ItemTitle(ItemTitleProps{Children: gx.Text("Your profile is verified.")})}),
ItemActions(ItemActionsProps{Children: icons.ChevronRight(icons.ChevronRightProps{Class: "size-4"})}),
)},
"Link": {Variant: Outline, Size: Sm, Href: gx.URL("/docs"), Children: gx.Frag(
ItemContent(ItemContentProps{Children: gx.Frag(
ItemTitle(ItemTitleProps{Children: gx.Text("Read the docs")}),
ItemDescription(ItemDescriptionProps{Children: gx.Text("The item is one link.")}),
)}),
ItemActions(ItemActionsProps{Children: icons.ChevronRight(icons.ChevronRightProps{Class: "size-4"})}),
)},
"HeaderAndFooter": {Variant: Outline, Children: gx.Frag(
ItemHeader(ItemHeaderProps{Children: gx.Text("Header")}),
ItemContent(ItemContentProps{Children: gx.Frag(
ItemTitle(ItemTitleProps{Children: gx.Text("Item title")}),
ItemDescription(ItemDescriptionProps{Children: gx.Text("A short description of the item.")}),
)}),
ItemFooter(ItemFooterProps{Children: gx.Text("Footer")}),
)},
}package item
import "github.com/alternayte/gx"
var ItemActionsFixtures = gx.Fixtures[ItemActionsProps]{"Empty": {}}package item
import "github.com/alternayte/gx"
var ItemContentFixtures = gx.Fixtures[ItemContentProps]{"Empty": {}}package item
import "github.com/alternayte/gx"
var ItemDescriptionFixtures = gx.Fixtures[ItemDescriptionProps]{"Text": {Children: gx.Text("Description")}}package item
import "github.com/alternayte/gx"
var ItemFooterFixtures = gx.Fixtures[ItemFooterProps]{"Empty": {}}package item
import "github.com/alternayte/gx"
var ItemHeaderFixtures = gx.Fixtures[ItemHeaderProps]{"Empty": {}}package item
import (
"github.com/alternayte/gx"
"github.com/alternayte/gx/registry/icons"
)
var ItemMediaFixtures = gx.Fixtures[ItemMediaProps]{
"Icon": {Variant: MediaIcon, Children: icons.Info(icons.InfoProps{})},
"Default": {Children: icons.Info(icons.InfoProps{Class: "size-5"})},
"Image": {Variant: MediaImage, Children: gx.El("div", gx.Attrs{
{Key: "class", Value: "size-full bg-muted"},
})},
}package item
import "github.com/alternayte/gx"
var ItemTitleFixtures = gx.Fixtures[ItemTitleProps]{"Title": {Children: gx.Text("Item title")}}package item
props {
// Class adds classes to the root element.
Class string = ""
// Children is the items of the group.
Children gx.Node
// Attrs adds HTML attributes to the root element.
Attrs gx.Attrs = nil
}
<div data-slot="item-group" class={gx.Cx("group/item-group flex flex-col", p.Class)} {...p.Attrs}>{p.Children}</div>package item
import "github.com/alternayte/gx"
var ItemGroupFixtures = gx.Fixtures[ItemGroupProps]{
"Separated": {Children: gx.Frag(
Item(ItemProps{Children: ItemContent(ItemContentProps{Children: gx.Frag(
ItemTitle(ItemTitleProps{Children: gx.Text("Ada Lovelace")}),
ItemDescription(ItemDescriptionProps{Children: gx.Text("ada@example.com")}),
)})}),
ItemSeparator(ItemSeparatorProps{}),
Item(ItemProps{Children: ItemContent(ItemContentProps{Children: gx.Frag(
ItemTitle(ItemTitleProps{Children: gx.Text("Grace Hopper")}),
ItemDescription(ItemDescriptionProps{Children: gx.Text("grace@example.com")}),
)})}),
)},
}package item
props {
// Class adds classes to the root element.
Class string = ""
// Attrs adds HTML attributes to the root element.
Attrs gx.Attrs = nil
}
<div data-slot="item-separator" role="none" data-orientation="horizontal" class={gx.Cx("h-px w-full shrink-0 bg-border my-0", p.Class)} {...p.Attrs}></div>package item
import "github.com/alternayte/gx"
var ItemSeparatorFixtures = gx.Fixtures[ItemSeparatorProps]{"Default": {}}package item
import "github.com/alternayte/gx"
// Variant is the surface of an item.
type Variant string
// The variants of item.Item.
const (
Default Variant = "default"
Outline Variant = "outline"
Muted Variant = "muted"
)
var variantClass = gx.Enum[Variant]{
Default: "bg-transparent",
Outline: "border-border",
Muted: "bg-muted/50",
}
// Size is the padding and the gap of an item.
type Size string
// The sizes of item.Item.
const (
Md Size = "default"
Sm Size = "sm"
)
var sizeClass = gx.Enum[Size]{
Md: "gap-4 p-4",
Sm: "gap-2.5 px-4 py-3",
}
// Media is the look of the media slot of an item.
type Media string
// The variants of item.ItemMedia.
const (
MediaDefault Media = "default"
MediaIcon Media = "icon"
MediaImage Media = "image"
)
var mediaClass = gx.Enum[Media]{
MediaDefault: "bg-transparent",
MediaIcon: "size-8 rounded-sm border border-border bg-muted [&_svg:not([class*='size-'])]:size-4",
MediaImage: "size-10 overflow-hidden rounded-sm [&_img]:size-full [&_img]:object-cover",
}
// variant returns the data-variant value; a zero value is Default.
func (p ItemProps) variant() string {
if p.Variant == "" {
return string(Default)
}
return string(p.Variant)
}
// size returns the data-size value; a zero value is Md.
func (p ItemProps) size() string {
if p.Size == "" {
return string(Md)
}
return string(p.Size)
}
// class returns the classes of one item.
func (p ItemProps) class() string {
const base = "group/item flex flex-wrap items-center rounded-md border border-transparent text-sm transition-colors duration-100 outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 [a]:transition-colors [a]:hover:bg-accent/50"
return gx.Cx(base, variantClass[Variant(p.variant())], sizeClass[Size(p.size())], p.Class)
}
// variant returns the data-variant value; a zero value is MediaDefault.
func (p ItemMediaProps) variant() string {
if p.Variant == "" {
return string(MediaDefault)
}
return string(p.Variant)
}The theme must define these tokens: --border, --muted, --muted-foreground, --accent, --ring, --primary.
Usage
A row with media, a title, a description and actions.
<item.Item variant={item.Outline}>
<item.ItemMedia variant={item.MediaIcon}><icons.Info /></item.ItemMedia>
<item.ItemContent>
<item.ItemTitle>Item title</item.ItemTitle>
<item.ItemDescription>A short description of the item.</item.ItemDescription>
</item.ItemContent>
<item.ItemActions>
<button.Button variant={button.Outline} size={button.Sm}>Open</button.Button>
</item.ItemActions>
</item.Item>
<item.ItemGroup>
<item.Item>...</item.Item>
<item.ItemSeparator />
<item.Item>...</item.Item>
</item.ItemGroup>The variants are Default, Outline and Muted. The sizes are Md and Sm.
ItemMedia has the variants MediaDefault, MediaIcon and MediaImage. The media aligns with the title when the item has a description.
Set Href to make the whole item one link. A link item has a hover state.
ItemHeader and ItemFooter take a full row above and below the content.
Examples
Item: Full
<item.Item variant={item.Outline}>
<item.ItemMedia variant={item.MediaIcon}><icons.Info /></item.ItemMedia>
<item.ItemContent>
<item.ItemTitle>Item title</item.ItemTitle>
<item.ItemDescription>A short description of the item.</item.ItemDescription>
</item.ItemContent>
<item.ItemActions><button.Button variant={button.Outline} size={button.Sm}>Open</button.Button></item.ItemActions>
</item.Item>Item: Default
<item.Item>
<item.ItemMedia variant={item.MediaIcon}><icons.Info /></item.ItemMedia>
<item.ItemContent>
<item.ItemTitle>Item title</item.ItemTitle>
<item.ItemDescription>A short description of the item.</item.ItemDescription>
</item.ItemContent>
<item.ItemActions><button.Button variant={button.Outline} size={button.Sm}>Open</button.Button></item.ItemActions>
</item.Item>Item: Muted
<item.Item variant={item.Muted}>
<item.ItemMedia variant={item.MediaIcon}><icons.Info /></item.ItemMedia>
<item.ItemContent>
<item.ItemTitle>Item title</item.ItemTitle>
<item.ItemDescription>A short description of the item.</item.ItemDescription>
</item.ItemContent>
<item.ItemActions><button.Button variant={button.Outline} size={button.Sm}>Open</button.Button></item.ItemActions>
</item.Item>Item: Small
<item.Item variant={item.Outline} size={item.Sm}>
<item.ItemMedia><icons.CircleCheck class="size-5" /></item.ItemMedia>
<item.ItemContent><item.ItemTitle>Your profile is verified.</item.ItemTitle></item.ItemContent>
<item.ItemActions><icons.ChevronRight class="size-4" /></item.ItemActions>
</item.Item>Item: Link
<item.Item variant={item.Outline} size={item.Sm} href={gx.URL("/docs")}>
<item.ItemContent>
<item.ItemTitle>Read the docs</item.ItemTitle>
<item.ItemDescription>The item is one link.</item.ItemDescription>
</item.ItemContent>
<item.ItemActions><icons.ChevronRight class="size-4" /></item.ItemActions>
</item.Item>Item: Header and footer
<item.Item variant={item.Outline}>
<item.ItemHeader>Header</item.ItemHeader>
<item.ItemContent>
<item.ItemTitle>Item title</item.ItemTitle>
<item.ItemDescription>A short description of the item.</item.ItemDescription>
</item.ItemContent>
<item.ItemFooter>Footer</item.ItemFooter>
</item.Item>ItemActions: Empty
<item.ItemActions children={gx.Frag()} />ItemContent: Empty
<item.ItemContent children={gx.Frag()} />ItemDescription: Text
<item.ItemDescription>Description</item.ItemDescription>ItemFooter: Empty
<item.ItemFooter children={gx.Frag()} />ItemGroup: Separated
<item.ItemGroup>
<item.Item>
<item.ItemContent>
<item.ItemTitle>Ada Lovelace</item.ItemTitle>
<item.ItemDescription>ada@example.com</item.ItemDescription>
</item.ItemContent>
</item.Item>
<item.ItemSeparator />
<item.Item>
<item.ItemContent>
<item.ItemTitle>Grace Hopper</item.ItemTitle>
<item.ItemDescription>grace@example.com</item.ItemDescription>
</item.ItemContent>
</item.Item>
</item.ItemGroup>ItemHeader: Empty
<item.ItemHeader children={gx.Frag()} />ItemMedia: Icon
<item.ItemMedia variant={item.MediaIcon}><icons.Info /></item.ItemMedia>ItemMedia: Default
<item.ItemMedia><icons.Info class="size-5" /></item.ItemMedia>ItemMedia: Image
<item.ItemMedia variant={item.MediaImage}><div class="size-full bg-muted" /></item.ItemMedia>ItemSeparator: Default
<item.ItemSeparator />ItemTitle: Title
<item.ItemTitle>Item title</item.ItemTitle>API reference
A tag sets a prop by its name with a lower-case first letter: Class is class.
item.Item
| Prop | Type | Default | Description |
|---|---|---|---|
Variant |
Variant |
Default |
Variant sets the surface: Default, Outline or Muted. |
Size |
Size |
Md |
Size sets the padding and the gap: Md or Sm. |
Href |
gx.URL |
"" |
Href is the link target. With a value the root is an a element; empty gives a div. |
Class |
string |
"" |
Class adds classes to the root element. |
Children |
gx.Node |
Required | Children is the content of the item. |
Attrs |
gx.Attrs |
nil |
Attrs adds HTML attributes to the root element. |
item.ItemActions
| Prop | Type | Default | Description |
|---|---|---|---|
Class |
string |
"" |
Class adds classes to the root element. |
Children |
gx.Node |
Required | Children is the actions of the item, for example buttons. |
Attrs |
gx.Attrs |
nil |
Attrs adds HTML attributes to the root element. |
item.ItemContent
| 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. |
item.ItemDescription
| 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. |
item.ItemFooter
| Prop | Type | Default | Description |
|---|---|---|---|
Class |
string |
"" |
Class adds classes to the root element. |
Children |
gx.Node |
Required | Children is the content of the footer. |
Attrs |
gx.Attrs |
nil |
Attrs adds HTML attributes to the root element. |
item.ItemGroup
| Prop | Type | Default | Description |
|---|---|---|---|
Class |
string |
"" |
Class adds classes to the root element. |
Children |
gx.Node |
Required | Children is the items of the group. |
Attrs |
gx.Attrs |
nil |
Attrs adds HTML attributes to the root element. |
item.ItemHeader
| Prop | Type | Default | Description |
|---|---|---|---|
Class |
string |
"" |
Class adds classes to the root element. |
Children |
gx.Node |
Required | Children is the content of the header. |
Attrs |
gx.Attrs |
nil |
Attrs adds HTML attributes to the root element. |
item.ItemMedia
| Prop | Type | Default | Description |
|---|---|---|---|
Variant |
Media |
MediaDefault |
Variant sets the look of the media: MediaDefault, MediaIcon or MediaImage. |
Class |
string |
"" |
Class adds classes to the root element. |
Children |
gx.Node |
Required | Children is the media, for example an icon or an image. |
Attrs |
gx.Attrs |
nil |
Attrs adds HTML attributes to the root element. |
item.ItemSeparator
| Prop | Type | Default | Description |
|---|---|---|---|
Class |
string |
"" |
Class adds classes to the root element. |
Attrs |
gx.Attrs |
nil |
Attrs adds HTML attributes to the root element. |
item.ItemTitle
| 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
- Keep the title to one line.
- Use
ItemGroupandItemSeparatorfor a list of items.
Don't
- Do not put a button in an item that has
Href. A link does not hold a button. - Do not use an item for tabular data. Use a table.
Keyboard
| Key | Action |
|---|---|
| Tab | Moves focus to a link item, then to the controls in ItemActions. |
| Enter | Follows a link item. |