Select
Use CSelect when people choose one value and the collection should remain compact until opened. The component progressively enhances a native Select, so form submission and reset retain native behavior.
Select at a glance
Show code
import citry_ui
from citry import Component, citry
from citry_ui import CSelectOption
citry.register_library(citry_ui)
class SelectAtAGlance(Component):
template = """
<c-CField>
<c-fill name="label">Workspace</c-fill>
<c-fill name="description">Choose where new observations belong.</c-fill>
<c-fill name="default">
<c-CSelect c-options="options" placeholder="Choose a workspace" value="atlas" />
</c-fill>
</c-CField>
"""
def template_data(self, _kwargs: object, _slots: object) -> dict[str, object]:
return {
"options": [
CSelectOption("atlas", "Atlas research", "12 collaborators"),
CSelectOption("aurora", "Aurora field notes", "7 collaborators"),
CSelectOption("archive", "Archived studies", disabled=True),
]
}
preview = SelectAtAGlance()
preview # noqa: B018
Submit a value
Show code
import citry_ui
from citry import Component, citry
from citry_ui import CSelectOption
citry.register_library(citry_ui)
class SelectForm(Component):
template = """
<form x-data @submit.prevent="result = Array.from(new FormData($event.target).entries())">
<c-CField required>
<c-fill name="label">Review status</c-fill>
<c-fill name="default">
<c-CSelect c-options="options" placeholder="Choose a status" name="status" />
</c-fill>
</c-CField>
<c-CButton type="submit">Save</c-CButton>
<c-CButton type="reset" variant="ghost">Reset</c-CButton>
<output x-text="JSON.stringify(result)"></output>
</form>
"""
def template_data(self, _kwargs: object, _slots: object) -> dict[str, object]:
return {"options": [CSelectOption("draft", "Draft"), CSelectOption("review", "Ready for review")]}
preview = SelectForm()
preview # noqa: B018
Group related options
Show code
import citry_ui
from citry import Component, citry
from citry_ui import CSelectOption
citry.register_library(citry_ui)
class GroupedSelect(Component):
template = """
<c-CSelect
c-options="options"
placeholder="Choose a destination"
c-trigger_attrs="{'aria-label':'Destination'}"
/>
"""
def template_data(self, _kwargs: object, _slots: object) -> dict[str, object]:
return {
"options": [
CSelectOption("oslo", "Oslo", group="Europe"),
CSelectOption("prague", "Prague", group="Europe"),
CSelectOption("kyoto", "Kyoto", group="Asia"),
CSelectOption("seoul", "Seoul", group="Asia"),
]
}
preview = GroupedSelect()
preview # noqa: B018
Control selection
Show code
import citry_ui
from citry import Component, citry
from citry_ui import CSelectOption
citry.register_library(citry_ui)
class ControlledSelect(Component):
template = """
<div x-data>
<c-CSelect
c-options="options"
placeholder="Choose a status"
value="draft"
c-trigger_attrs="{'aria-label':'Status'}"
$c-props="{
value:$store.selectExample.value,
onValueChange:(next) => $store.selectExample.value = next,
}"
/>
<p>Current: <strong x-text="$store.selectExample.value"></strong></p>
</div>
"""
js = "Alpine.store('selectExample', {value:'draft'});"
def template_data(self, _kwargs: object, _slots: object) -> dict[str, object]:
return {"options": [CSelectOption("draft", "Draft"), CSelectOption("published", "Published")]}
preview = ControlledSelect()
preview # noqa: B018
Read-only and disabled states
Show code
import citry_ui
from citry import Component, citry
from citry_ui import CSelectOption
citry.register_library(citry_ui)
class SelectStates(Component):
template = """
<c-CStack>
<c-CSelect
c-options="options" placeholder="Choose" value="active" readonly
c-trigger_attrs="{'aria-label':'Read-only state'}"
/>
<c-CSelect
c-options="options" placeholder="Choose" disabled
c-trigger_attrs="{'aria-label':'Disabled state'}"
/>
<c-CSelect c-options="options" placeholder="Choose" invalid c-trigger_attrs="{'aria-label':'Invalid state'}" />
</c-CStack>
"""
def template_data(self, _kwargs: object, _slots: object) -> dict[str, object]:
return {"options": [CSelectOption("active", "Active"), CSelectOption("paused", "Paused")]}
preview = SelectStates()
preview # noqa: B018
Variants and sizes
Show code
import citry_ui
from citry import Component, citry
from citry_ui import CSelectOption
citry.register_library(citry_ui)
class SelectVariants(Component):
template = """
<c-CStack>
<c-CSelect
c-options="options" placeholder="Outline" variant="outline" size="sm"
c-trigger_attrs="{'aria-label':'Small outline'}"
/>
<c-CSelect
c-options="options" placeholder="Filled" variant="filled"
c-trigger_attrs="{'aria-label':'Medium filled'}"
/>
<c-CSelect
c-options="options" placeholder="Plain" variant="plain" size="lg"
c-trigger_attrs="{'aria-label':'Large plain'}"
/>
</c-CStack>
"""
def template_data(self, _kwargs: object, _slots: object) -> dict[str, object]:
return {"options": [CSelectOption("one", "One"), CSelectOption("two", "Two")]}
preview = SelectVariants()
preview # noqa: B018
Keyboard behavior
Enter, Space, Down, or Up opens the Listbox. Down and Up move the highlight; Home and End jump to its edges; printable text performs buffered typeahead; Enter or Space commits; Escape closes unchanged; and Tab closes while ordinary page navigation continues.
Show code
import citry_ui
from citry import Component, citry
from citry_ui import CSelectOption
citry.register_library(citry_ui)
class KeyboardSelect(Component):
template = """
<c-CSelect
c-options="options"
placeholder="Focus and use the keyboard"
loop
c-trigger_attrs="{'aria-label':'Planet keyboard example'}"
/>
"""
def template_data(self, _kwargs: object, _slots: object) -> dict[str, object]:
return {
"options": [
CSelectOption("earth", "Earth"),
CSelectOption("mars", "Mars"),
CSelectOption("jupiter", "Jupiter"),
]
}
preview = KeyboardSelect()
preview # noqa: B018
Customize Select
Show code
import citry_ui
from citry import Component, citry
from citry_ui import CSelectOption
citry.register_library(citry_ui)
class CustomizedSelect(Component):
css = """
.brand-select {
--cui-select-radius: 1rem;
--cui-select-selected-background: #53389e;
--cui-select-selected-foreground: white;
--cui-select-focus-color: #7f56d9;
inline-size: min(100%, 22rem);
}
"""
template = """
<c-CSelect
class_="brand-select"
c-options="options"
placeholder="Choose a collection"
c-trigger_attrs="{'aria-label':'Collection'}"
/>
"""
def template_data(self, _kwargs: object, _slots: object) -> dict[str, object]:
return {"options": [CSelectOption("botany", "Botany"), CSelectOption("astronomy", "Astronomy")]}
preview = CustomizedSelect()
preview # noqa: B018
Accessibility and forms
The visible Button uses the select-only combobox pattern and keeps DOM focus while aria-activedescendant identifies the highlighted Option. A native Select remains the form value, validity, and reset truth. Before client initialization, that native control is the visible fallback.
Use CListbox for a persistent collection, CMultiSelect for several compact values, and CCombobox when users need text filtering or custom input.
API reference
Inputs
CSelect server inputs
Server inputs are passed in a template through <c-CSelect ... /> or in Python through CSelect(...).
| Input | Type | Default | Effect |
|---|---|---|---|
options | Sequence[CSelectOption] | required | Supplies the nonempty ordered stable collection. |
placeholder | str | required | Supplies author-localized empty-value text. |
name | str | None | None | Sets the native form field name. |
form | str | None | None | Associates the native value proxy with a Form ID. |
id | str | None | None | Sets native proxy identity and generated relationships. |
value | str | None | None | Sets the initial selected stable value. |
open | bool | False | Sets initial popup visibility when eligible. |
required | bool | None | None | Enables native required validity outside Field. |
disabled | bool | None | None | Disables selection and form contribution. |
readonly | bool | None | None | Preserves submission while preventing changes. |
invalid | bool | None | None | Adds owner-supplied invalid presentation. |
loop | bool | False | Wraps open Listbox arrow navigation. |
placement | "bottom-start" | "bottom-end" | "top-start" | "top-end" (CSelectPlacement) | "bottom-start" | Sets preferred logical popup placement. |
match_width | bool | True | Matches the popup inline size to the control within viewport limits. |
variant | "outline" | "filled" | "plain" (CSelectVariant) | "outline" | Selects control treatment. |
size | "sm" | "md" | "lg" (CSelectSize) | "md" | Selects control and Option geometry. |
class_ | CClassValue | None (CClassValue) | None | Adds root classes. |
style | CStyleValue | None (CStyleValue) | None | Adds root inline styles. |
attrs | Mapping[str, object] | None | None | Adds trusted nonconflicting root attributes. |
trigger_attrs | Mapping[str, object] | None | None | Adds trusted relationships events and accessible naming to the combobox Button. |
listbox_attrs | Mapping[str, object] | None | None | Adds trusted nonconflicting Listbox attributes. |
CSelect client inputs
Client inputs are passed in the browser through the $c-props="{ ... }" attribute on <c-CSelect />.
| Input | Type | Omitted behavior | Effect |
|---|---|---|---|
value | string | null | Releases control to committed selection. | Controls selected value while supplied. |
open | boolean | null | Releases control to committed visibility. | Controls popup visibility while supplied. |
required | bool | Uses the server or Field fallback. | Reactively changes required validity. |
disabled | bool | Uses the server or Field fallback. | Reactively disables selection. |
readonly | bool | Uses the server or Field fallback. | Reactively prevents changes while preserving submission. |
invalid | bool | Uses the server or Field fallback. | Reactively changes invalid presentation. |
loop | bool | Uses the server value. | Reactively changes arrow wrapping. |
placement | CSelectPlacement | Uses the server value. | Reactively changes preferred placement. |
matchWidth | bool | Uses the server value. | Reactively changes popup sizing. |
variant | CSelectVariant | Uses the server value. | Reactively changes treatment. |
size | CSelectSize | Uses the server value. | Reactively changes geometry. |
onValueChange | ((value: string | null, detail: CSelectValueChangeDetail) => void) | undefined | No component callback runs. | Receives selection reset and structural requests. |
onOpenChange | ((open: boolean, detail: CSelectOpenChangeDetail) => void) | undefined | No component callback runs. | Receives visibility requests and forced-close notices. |
Slots
-
Events
Component events are callback inputs supplied through $c-props. Native browser events remain available through Alpine @... attributes.
CSelect events
| Event | Signature | Trigger and timing | Detail | Controlled and cancellation behavior |
|---|---|---|---|---|
onValueChange | (value: string | null, detail: CSelectValueChangeDetail) => void (CSelectValueChangeDetail) | Enabled selection reset or structural recovery. | {value, previousValue, option, controlled, source, sourceEvent} (CSelectValueChangeDetail) | Commits immediately when uncontrolled and waits for owner acceptance when controlled. |
onOpenChange | (open: boolean, detail: CSelectOpenChangeDetail) => void (CSelectOpenChangeDetail) | Visibility request or nonrejectable safety close. | {open, reason, controlled, forced, source} (CSelectOpenChangeDetail) | Controlled requests notify without changing visibility; forced safety closes always hide. |
Methods
-
CSS
CSS variables to theme the components. Set them on an ancestor or the component itself.
CSelect CSS variables
Apply these variables to CSelect or one of its ancestors.
| Variable | Type | Purpose | Default |
|---|---|---|---|
--cui-select-background | color | Control and popup surface. | Canvas |
--cui-select-foreground | color | Primary foreground. | CanvasText |
--cui-select-placeholder-color | color | Empty-value foreground. | scheme-aware muted |
--cui-select-muted-color | color | Description and disabled foreground. | scheme-aware muted |
--cui-select-border-color | color | Outline border. | scheme-aware subtle border |
--cui-select-hover-background | color | Highlighted Option surface. | CanvasText mix |
--cui-select-selected-background | color | Selected Option surface. | scheme-aware blue |
--cui-select-selected-foreground | color | Selected Option foreground. | scheme-aware blue text |
--cui-select-focus-color | color | Focus outline. | Highlight |
--cui-select-radius | length | Control and popup corners. | 0.625rem |
--cui-select-control-padding | length | Control padding. | size-derived |
--cui-select-option-padding | length | Option padding. | size-derived |
--cui-select-max-block-size | length | Popup scroll boundary. | 18rem |
--cui-select-offset | length | Anchor gap. | 0.25rem |
--cui-select-shadow | shadow | Popup elevation. | scheme-aware shadow |
--cui-select-duration | time | Popup and indicator motion. | 120ms |
Attributes
HTML attributes defined on the components that you can refer to for CSS, inspection, and testing. Read-only.
CSelect attributes
| Attribute | Element | Type | Meaning |
|---|---|---|---|
role | Control Button | combobox | Declares the select-only popup control. |
role | Listbox div | listbox | Declares the popup collection. |
role | Option div | option | Declares each value. |
aria-expanded | Control Button | true | false | Reflects popup visibility. |
aria-controls | Control Button | IDREF | Targets the Listbox. |
aria-activedescendant | Control Button | IDREF or absent | Identifies the highlighted open Option. |
aria-required | Control Button | true or absent | Mirrors effective required state. |
aria-disabled | Control Button | true or absent | Mirrors effective unavailability. |
aria-readonly | Control Button | true or absent | Mirrors read-only interaction. |
aria-invalid | Control Button | true or absent | Mirrors effective invalid presentation. |
aria-selected | Option div | true | false | Reflects effective selection. |
data-open | Root div | present-or-absent | Mirrors effective visibility. |
data-empty | Root div | present-or-absent | Mirrors no selected value. |
data-required | Root div | present-or-absent | Mirrors effective required state. |
data-readonly | Root div | present-or-absent | Mirrors read-only interaction. |
data-invalid | Root div | present-or-absent | Mirrors effective invalid presentation. |
data-match-width | Root div | present-or-absent | Mirrors popup width matching. |
data-variant | Root div | outline | filled | plain | Mirrors effective treatment. |
data-size | Root div | sm | md | lg | Mirrors effective geometry. |
data-value | Option div | string | Exposes stable identity. |
data-selected | Option div | present-or-absent | Mirrors selection. |
data-highlighted | Option div | present-or-absent | Mirrors active descendant. |
data-disabled | Root or Option div | present-or-absent | Mirrors effective unavailability. |
data-placement | Popup div | CSelectPlacement | Reflects preferred logical placement. |
Selectors
Selectors for the DOM nodes in the components that you can use for CSS, inspection, and testing.
CSelect selectors
| Selector | Element | Purpose |
|---|---|---|
[data-citry-ui-part="root"] | Root div | Stable root attrs and state surface. |
[data-citry-ui-part="control"] | Combobox Button | Visible control and focus owner. |
[data-citry-ui-part="value"] | Value span | Selected label or placeholder. |
[data-citry-ui-part="indicator"] | Indicator span | Decorative popup-state mark. |
[data-citry-ui-part="popup"] | Manual popover div | Top-layer scrolling surface. |
[data-citry-ui-part="listbox"] | Listbox div | Semantic collection. |
[data-citry-ui-part="group"] | Group div | Related Options. |
[data-citry-ui-part="group-label"] | Group label span | Visible group name. |
[data-citry-ui-part="option"] | Option div | Value semantics and state. |
[data-citry-ui-part="option-label"] | Option label span | Accessible Option name. |
[data-citry-ui-part="option-description"] | Option description span | Supporting description. |
Interfaces
Aliases and data shapes referenced above.
Input type aliases
| Interface | Definition |
|---|---|
CClassValue | str | Mapping[str, bool] | Sequence[CClassValue] |
CStyleValue | str | Mapping[str, object] | Sequence[CStyleValue] |
CSelectPlacement | Literal["bottom-start", "bottom-end", "top-start", "top-end"] |
CSelectVariant | Literal["outline", "filled", "plain"] |
CSelectSize | Literal["sm", "md", "lg"] |
CSelectChangeSource | Literal["pointer", "keyboard", "reset", "structure"] |
CSelectOpenReason | Literal["trigger", "keyboard", "selection", "escape", "tab", "outside", "focus-outside", "reset", "native", "ancestor"] |
CSelectOption
| Field | Type | Default | Meaning |
|---|---|---|---|
value | str | - | Stable unique form value. |
label | str | - | Visible accessible Option name. |
description | str | None | - | Optional separately described supporting text. |
disabled | bool | - | Prevents user selection. |
group | str | None | - | Optional contiguous visible group label. |
CSelectValueChangeDetail
| Field | Type | Default | Meaning |
|---|---|---|---|
value | str | None | - | Requested value. |
previousValue | str | None | - | Previous effective value. |
option | HTMLElement | None | - | Activated Option or None for reset and structure. |
controlled | bool | - | Whether client value owns selection. |
source | CSelectChangeSource | - | Request source. |
sourceEvent | Event | None | - | Native source event when present. |
CSelectOpenChangeDetail
| Field | Type | Default | Meaning |
|---|---|---|---|
open | bool | - | Requested or forced visibility. |
reason | CSelectOpenReason | - | Visibility reason. |
controlled | bool | - | Whether client open owns visibility. |
forced | bool | - | Whether safety made the close nonrejectable. |
source | EventTarget | None | - | Native source or safety owner. |
Translation keys
-