ListBox
HeroUIA list of options to select or act on, HeroUI's on Kobalte's.
Solidtype_heroui.List_box is Kobalte’s Listbox, with the classes HeroUI’s
own listboxVariants, listboxItemVariants and listboxSectionVariants
give. HeroUI’s Label, Description, Header and Separator go inside its
options and sections, as in React.
module Avatar = Solidtype_heroui.Avatarmodule Description = Solidtype_heroui.Descriptionmodule Label = Solidtype_heroui.Labelmodule List_box = Solidtype_heroui.List_box
let make () = <List_box aria_label="Users" class_="w-[220px]" selection_mode=`single> <List_box.Item id="1" text_value="Bob"> <Avatar size=`sm> <Avatar.Image alt="Bob" src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg" /> <Avatar.Fallback>"B"</Avatar.Fallback> </Avatar> <div class_="flex flex-col"> <Label>"Bob"</Label> <Description>"bob@heroui.com"</Description> </div> <List_box.Item_indicator /> </List_box.Item> <List_box.Item id="2" text_value="Fred"> <Avatar size=`sm> <Avatar.Image alt="Fred" src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg" /> <Avatar.Fallback>"F"</Avatar.Fallback> </Avatar> <div class_="flex flex-col"> <Label>"Fred"</Label> <Description>"fred@heroui.com"</Description> </div> <List_box.Item_indicator /> </List_box.Item> <List_box.Item id="3" text_value="Martha"> <Avatar size=`sm> <Avatar.Image alt="Martha" src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg" /> <Avatar.Fallback>"M"</Avatar.Fallback> </Avatar> <div class_="flex flex-col"> <Label>"Martha"</Label> <Description>"martha@heroui.com"</Description> </div> <List_box.Item_indicator /> </List_box.Item> </List_box>Anatomy
Section titled “Anatomy”<List_box aria_label="..."> <List_box.Item id="..."> <Label>...</Label> <Description>...</Description> <List_box.Item_indicator /> </List_box.Item> <Separator /> <List_box.Section> <Header>...</Header> <List_box.Item id="..." /> </List_box.Section></List_box>Examples
Section titled “Examples”With Disabled Items
Section titled “With Disabled Items”module Description = Solidtype_heroui.Descriptionmodule Header = Solidtype_heroui.Headermodule Kbd = Solidtype_heroui.Kbdmodule Label = Solidtype_heroui.Labelmodule List_box = Solidtype_heroui.List_boxmodule Separator = Solidtype_heroui.Separatormodule Surface = Solidtype_heroui.Surface
let make () = <Surface class_="w-[256px] rounded-3xl shadow-surface"> <List_box aria_label="File actions" class_="w-full p-2" disabled_keys=(fun () -> [| "delete-file" |]) selection_mode=`none on_action=(fun (_ : string) -> ())> <List_box.Section> <Header>"Actions"</Header> <List_box.Item id="new-file" text_value="New file"> <div class_="flex h-8 items-start justify-center pt-px"> <Gravity.Square_plus class_="size-4 shrink-0 text-muted" /> </div> <div class_="flex flex-col"> <Label>"New file"</Label> <Description>"Create a new file"</Description> </div> <Kbd class_="ms-auto" variant=`light> <Kbd.Abbr key_value=`command /> <Kbd.Content>"N"</Kbd.Content> </Kbd> </List_box.Item> <List_box.Item id="edit-file" text_value="Edit file"> <div class_="flex h-8 items-start justify-center pt-px"> <Gravity.Pencil class_="size-4 shrink-0 text-muted" /> </div> <div class_="flex flex-col"> <Label>"Edit file"</Label> <Description>"Make changes"</Description> </div> <Kbd class_="ms-auto" variant=`light> <Kbd.Abbr key_value=`command /> <Kbd.Content>"E"</Kbd.Content> </Kbd> </List_box.Item> </List_box.Section> <Separator /> <List_box.Section> <Header>"Danger zone"</Header> <List_box.Item id="delete-file" text_value="Delete file" variant=`danger> <div class_="flex h-8 items-start justify-center pt-px"> <Gravity.Trash_bin class_="size-4 shrink-0 text-danger" /> </div> <div class_="flex flex-col"> <Label>"Delete file"</Label> <Description>"Move to trash"</Description> </div> <Kbd class_="ms-auto" variant=`light> <Kbd.Abbr key_value=`command /> <Kbd.Abbr key_value=`shift /> <Kbd.Content>"D"</Kbd.Content> </Kbd> </List_box.Item> </List_box.Section> </List_box> </Surface>With Sections
Section titled “With Sections”module Description = Solidtype_heroui.Descriptionmodule Header = Solidtype_heroui.Headermodule Kbd = Solidtype_heroui.Kbdmodule Label = Solidtype_heroui.Labelmodule List_box = Solidtype_heroui.List_boxmodule Separator = Solidtype_heroui.Separatormodule Surface = Solidtype_heroui.Surface
let make () = <Surface class_="w-[256px] rounded-3xl shadow-surface"> <List_box aria_label="File actions" class_="w-full p-2" selection_mode=`none on_action=(fun (_ : string) -> ())> <List_box.Section> <Header>"Actions"</Header> <List_box.Item id="new-file" text_value="New file"> <div class_="flex h-8 items-start justify-center pt-px"> <Gravity.Square_plus class_="size-4 shrink-0 text-muted" /> </div> <div class_="flex flex-col"> <Label>"New file"</Label> <Description>"Create a new file"</Description> </div> <Kbd class_="ms-auto" variant=`light> <Kbd.Abbr key_value=`command /> <Kbd.Content>"N"</Kbd.Content> </Kbd> </List_box.Item> <List_box.Item id="edit-file" text_value="Edit file"> <div class_="flex h-8 items-start justify-center pt-px"> <Gravity.Pencil class_="size-4 shrink-0 text-muted" /> </div> <div class_="flex flex-col"> <Label>"Edit file"</Label> <Description>"Make changes"</Description> </div> <Kbd class_="ms-auto" variant=`light> <Kbd.Abbr key_value=`command /> <Kbd.Content>"E"</Kbd.Content> </Kbd> </List_box.Item> </List_box.Section> <Separator /> <List_box.Section> <Header>"Danger zone"</Header> <List_box.Item id="delete-file" text_value="Delete file" variant=`danger> <div class_="flex h-8 items-start justify-center pt-px"> <Gravity.Trash_bin class_="size-4 shrink-0 text-danger" /> </div> <div class_="flex flex-col"> <Label>"Delete file"</Label> <Description>"Move to trash"</Description> </div> <Kbd class_="ms-auto" variant=`light> <Kbd.Abbr key_value=`command /> <Kbd.Abbr key_value=`shift /> <Kbd.Content>"D"</Kbd.Content> </Kbd> </List_box.Item> </List_box.Section> </List_box> </Surface>Multi Select
Section titled “Multi Select”module Avatar = Solidtype_heroui.Avatarmodule Description = Solidtype_heroui.Descriptionmodule Label = Solidtype_heroui.Labelmodule List_box = Solidtype_heroui.List_boxmodule Surface = Solidtype_heroui.Surface
let make () = <Surface class_="w-[256px] rounded-3xl shadow-surface"> <List_box aria_label="Users" selection_mode=`multiple> <List_box.Item id="1" text_value="Bob"> <Avatar size=`sm> <Avatar.Image alt="Bob" src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg" /> <Avatar.Fallback>"B"</Avatar.Fallback> </Avatar> <div class_="flex flex-col"> <Label>"Bob"</Label> <Description>"bob@heroui.com"</Description> </div> <List_box.Item_indicator /> </List_box.Item> <List_box.Item id="2" text_value="Fred"> <Avatar size=`sm> <Avatar.Image alt="Fred" src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg" /> <Avatar.Fallback>"F"</Avatar.Fallback> </Avatar> <div class_="flex flex-col"> <Label>"Fred"</Label> <Description>"fred@heroui.com"</Description> </div> <List_box.Item_indicator /> </List_box.Item> <List_box.Item id="3" text_value="Martha"> <Avatar size=`sm> <Avatar.Image alt="Martha" src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg" /> <Avatar.Fallback>"M"</Avatar.Fallback> </Avatar> <div class_="flex flex-col"> <Label>"Martha"</Label> <Description>"martha@heroui.com"</Description> </div> <List_box.Item_indicator /> </List_box.Item> </List_box> </Surface>Controlled
Section titled “Controlled”Selected: 1
module S = Solidtype.Solidmodule Jsx = Solidtype.Jsxmodule Avatar = Solidtype_heroui.Avatarmodule Description = Solidtype_heroui.Descriptionmodule Label = Solidtype_heroui.Labelmodule List_box = Solidtype_heroui.List_boxmodule Surface = Solidtype_heroui.Surface
let make () = let selected, set_selected = S.create_signal [| "1" |] in <div class_="space-y-4"> <Surface class_="w-[256px] rounded-3xl shadow-surface"> <List_box aria_label="Users" selected_keys=(fun () -> S.get selected) selection_mode=`multiple on_selection_change=(S.set set_selected)> <List_box.Item id="1" text_value="Bob"> <Avatar size=`sm> <Avatar.Image alt="Bob" src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg" /> <Avatar.Fallback>"B"</Avatar.Fallback> </Avatar> <div class_="flex flex-col"> <Label>"Bob"</Label> <Description>"bob@heroui.com"</Description> </div> <List_box.Item_indicator> (fun selected -> if S.get selected then <Gravity.Check class_="size-4 text-accent-soft-foreground" /> else Jsx.nothing) </List_box.Item_indicator> </List_box.Item> <List_box.Item id="2" text_value="Fred"> <Avatar size=`sm> <Avatar.Image alt="Fred" src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg" /> <Avatar.Fallback>"F"</Avatar.Fallback> </Avatar> <div class_="flex flex-col"> <Label>"Fred"</Label> <Description>"fred@heroui.com"</Description> </div> <List_box.Item_indicator> (fun selected -> if S.get selected then <Gravity.Check class_="size-4 text-accent-soft-foreground" /> else Jsx.nothing) </List_box.Item_indicator> </List_box.Item> <List_box.Item id="3" text_value="Martha"> <Avatar size=`sm> <Avatar.Image alt="Martha" src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg" /> <Avatar.Fallback>"M"</Avatar.Fallback> </Avatar> <div class_="flex flex-col"> <Label>"Martha"</Label> <Description>"martha@heroui.com"</Description> </div> <List_box.Item_indicator> (fun selected -> if S.get selected then <Gravity.Check class_="size-4 text-accent-soft-foreground" /> else Jsx.nothing) </List_box.Item_indicator> </List_box.Item> </List_box> </Surface> <p class_="text-sm text-muted"> "Selected: " (fun () -> Jsx.string (match S.get selected with | [||] -> "None" | keys -> String.concat ", " (Array.to_list keys))) </p> </div>Virtualization
Section titled “Virtualization”HeroUI’s list is drawn by React Aria’s virtualizer, which draws only the options in view. The port draws every option, here a thousand: Kobalte’s list is told it is virtualized only so that the port may draw it.
module Description = Solidtype_heroui.Descriptionmodule Label = Solidtype_heroui.Labelmodule List_box = Solidtype_heroui.List_box
type user = { id : string; name : string; email : string }
let first_names = [| "Emma"; "Liam"; "Olivia"; "Noah"; "Ava"; "James"; "Sophia"; "Oliver"; "Isabella"; "Lucas"; "Mia"; "Ethan"; "Charlotte"; "Mason"; "Amelia"; "Logan"; "Harper"; "Alexander"; "Ella"; "Benjamin"; |]
let last_names = [| "Smith"; "Johnson"; "Williams"; "Brown"; "Jones"; "Garcia"; "Miller"; "Davis"; "Rodriguez"; "Martinez"; "Anderson"; "Taylor"; "Thomas"; "Jackson"; "White"; "Harris"; "Clark"; "Lewis"; "Robinson"; "Walker"; |]
let generate_users n = Array.init n (fun i -> let first_name = first_names.(i mod Array.length first_names) in let last_name = last_names.(i / Array.length first_names mod Array.length last_names) in { id = string_of_int (i + 1); name = first_name ^ " " ^ last_name; email = String.lowercase_ascii first_name ^ "." ^ String.lowercase_ascii last_name ^ "@acme.com"; })
let make () = let users = generate_users 1000 in <List_box aria_label="Virtualized list with 1000 items" class_="h-[400px] w-[300px] overflow-y-auto"> <Solidtype.For each=(fun () -> users)> (fun (user : user) _ -> <List_box.Item id=(fun () -> user.id) text_value=(fun () -> user.name)> <div class_="flex flex-col"> <Label>(fun () -> Solidtype.Jsx.string user.name)</Label> <Description> (fun () -> Solidtype.Jsx.string user.email) </Description> </div> <List_box.Item_indicator /> </List_box.Item>) </Solidtype.For> </List_box>Custom Check Icon
Section titled “Custom Check Icon”module S = Solidtype.Solidmodule Avatar = Solidtype_heroui.Avatarmodule Description = Solidtype_heroui.Descriptionmodule Label = Solidtype_heroui.Labelmodule List_box = Solidtype_heroui.List_boxmodule Surface = Solidtype_heroui.Surface
let make () = <Surface class_="w-[256px] rounded-3xl shadow-surface"> <List_box aria_label="Users" selection_mode=`multiple> <List_box.Item id="1" text_value="Bob"> <Avatar size=`sm> <Avatar.Image alt="Bob" src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg" /> <Avatar.Fallback>"B"</Avatar.Fallback> </Avatar> <div class_="flex flex-col"> <Label>"Bob"</Label> <Description>"bob@heroui.com"</Description> </div> <List_box.Item_indicator> (fun selected -> if S.get selected then <Gravity.Check class_="size-4 text-accent-soft-foreground" /> else Solidtype.Jsx.nothing) </List_box.Item_indicator> </List_box.Item> <List_box.Item id="2" text_value="Fred"> <Avatar size=`sm> <Avatar.Image alt="Fred" src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg" /> <Avatar.Fallback>"F"</Avatar.Fallback> </Avatar> <div class_="flex flex-col"> <Label>"Fred"</Label> <Description>"fred@heroui.com"</Description> </div> <List_box.Item_indicator> (fun selected -> if S.get selected then <Gravity.Check class_="size-4 text-accent-soft-foreground" /> else Solidtype.Jsx.nothing) </List_box.Item_indicator> </List_box.Item> <List_box.Item id="3" text_value="Martha"> <Avatar size=`sm> <Avatar.Image alt="Martha" src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg" /> <Avatar.Fallback>"M"</Avatar.Fallback> </Avatar> <div class_="flex flex-col"> <Label>"Martha"</Label> <Description>"martha@heroui.com"</Description> </div> <List_box.Item_indicator> (fun selected -> if S.get selected then <Gravity.Check class_="size-4 text-accent-soft-foreground" /> else Solidtype.Jsx.nothing) </List_box.Item_indicator> </List_box.Item> </List_box> </Surface>Render Function
Section titled “Render Function”HeroUI’s Render Function example gives the list render, React’s way to
swap the element it renders, which a Solid component has no need of.
Customization
Section titled “Customization”Tailwind CSS
Section titled “Tailwind CSS”class_ is added after HeroUI’s classes.
module Avatar = Solidtype_heroui.Avatarmodule Description = Solidtype_heroui.Descriptionmodule Label = Solidtype_heroui.Labelmodule List_box = Solidtype_heroui.List_box
let make () = <List_box aria_label="Assignee" class_="w-56 rounded-xl border border-border/80 bg-surface p-1 shadow-sm" selection_mode=`single> <List_box.Item class_="rounded-lg data-[focused=true]:bg-accent/10 \ data-[selected=true]:bg-accent/5" id="1" text_value="Bob"> <Avatar size=`sm> <Avatar.Image alt="Bob" src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg" /> <Avatar.Fallback>"B"</Avatar.Fallback> </Avatar> <div class_="flex flex-col"> <Label>"Bob"</Label> <Description>"bob@heroui.com"</Description> </div> <List_box.Item_indicator /> </List_box.Item> <List_box.Item class_="rounded-lg data-[focused=true]:bg-accent/10 \ data-[selected=true]:bg-accent/5" id="2" text_value="Fred"> <Avatar size=`sm> <Avatar.Image alt="Fred" src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/green.jpg" /> <Avatar.Fallback>"F"</Avatar.Fallback> </Avatar> <div class_="flex flex-col"> <Label>"Fred"</Label> <Description>"fred@heroui.com"</Description> </div> <List_box.Item_indicator /> </List_box.Item> </List_box>Global CSS
Section titled “Global CSS”HeroUI’s classes are global, so a stylesheet restyles every one, as with HeroUI in React:
@layer components { .list-box { @apply rounded-lg border border-border bg-surface p-2; }
.list-box-item { @apply rounded px-2 py-1 cursor-pointer; }
.list-box-item--danger { @apply text-danger; }}Styling Reference
Section titled “Styling Reference”CSS Classes
Section titled “CSS Classes”.list-box,.list-box--default,.list-box--danger.list-box-item,.list-box-item--default,.list-box-item--danger,.list-box-item__indicator, and the indicator’s[data-slot="list-box-item-indicator--checkmark"].list-box-section, and.headerfor its title[data-slot="separator"]within the list
Interactive States
Section titled “Interactive States”- Hover:
[data-hovered="true"]on an option - Focus:
:focus-visibleor[data-focus-visible="true"], and[data-focused="true"]on the option with the focus - Pressed:
[data-pressed="true"]on an option - Selected:
[data-selected="true"]and[aria-selected="true"]on an option - Disabled:
[data-disabled="true"]on an option - Selection:
[data-selection-mode="single"]or"multiple"on the options
How it is made
Section titled “How it is made”Kobalte’s list moves the focus between its options by the arrows and by
what is typed, and selects one or many, as React Aria’s does. Kobalte’s list
is made from its options as data, where HeroUI’s is written as elements: so
each option, section and separator joins the list as it is made, in its
order, and the list hands Kobalte its options alone and draws them in its
sections, between its separators, as React Aria’s does. A section is a
<section> with the role group, named by its header.
Kobalte counts each option’s place for a reader with aria-posinset and
aria-setsize, which React Aria leaves to its list.
API Reference
Section titled “API Reference”Each prop beside the name HeroUI’s React component gives it.
List_box
Section titled “List_box”| React | solidtype | Type | Default | Description |
|---|---|---|---|---|
aria-label |
aria_label |
string |
Its name | |
selectionMode |
selection_mode |
`none | `single | `multiple |
`single |
How it selects |
selectedKeys |
selected_keys |
string array |
The selected options’ ids, controlled | |
defaultSelectedKeys |
default_selected_keys |
string array |
The ids selected at first | |
onSelectionChange |
on_selection_change |
string array -> unit |
Told the ids selected as they change | |
disabledKeys |
disabled_keys |
string array |
The disabled options’ ids | |
onAction |
on_action |
string -> unit |
Told the id of an option pressed, with no selection | |
variant |
variant |
`default | `danger |
`default |
Its look |
className |
class_ |
string |
Added after HeroUI’s classes | |
children |
children |
elements | Its options, sections and separators |
List_box.Item
Section titled “List_box.Item”| React | solidtype | Type | Default | Description |
|---|---|---|---|---|
id |
id |
string |
Its id, which its list is told | |
textValue |
text_value |
string |
Its text, typed to reach it | |
isDisabled |
is_disabled |
bool |
false |
Whether it is disabled |
variant |
variant |
`default | `danger |
`default |
Its look |
className |
class_ |
string |
Added after HeroUI’s classes | |
children |
children |
elements | What it shows |
List_box.Item_indicator
Section titled “List_box.Item_indicator”| React | solidtype | Type | Description |
|---|---|---|---|
className |
class_ |
string |
Added after HeroUI’s classes |
children |
children |
bool accessor -> element |
An indicator of its own, given whether its option is selected |
List_box.Section
Section titled “List_box.Section”| React | solidtype | Type | Description |
|---|---|---|---|
className |
class_ |
string |
Added after HeroUI’s classes |
children |
children |
elements | Its header and options |
Beside React
Section titled “Beside React”Original HeroUI documentation: ListBox.
The same HeroUI ListBox twice, under the same props: in solidtype on the left, in React on the right. Below them, every attribute of each list: a marked row is one they disagree on, and a dimmed row is a framework’s own, which no style reads.
| attribute | solidtype | React |
|---|
