Styling contract
Style listbox states through public attributes and option hooks instead of relying on internal DOM assumptions.
The focus ring belongs on [tngListbox]. Option rows expose active and selected state visually, but they stay out of the sequential tab order.
CSS contract table
| Selector | Applied on | Purpose |
|---|---|---|
[tngListbox] | Listbox host | Container layout, keyboard focus ring, and Tab-stop ownership. |
[tngOption] | Option host | Typography, spacing, and surface states. |
[tngOption][data-active] | Option host | Keyboard-active visual state. |
[tngOption][data-selected] | Option host | Selected item styling. |
[tngOption][data-disabled] | Option host | Disabled affordance and interaction cue. |
State selectors
listbox.states.css
css
[tngListbox]:focus {
border-color: var(--tng-semantic-accent-brand);
}
[tngOption][data-active] {
border-color: var(--tng-semantic-accent-brand);
}
[tngOption][data-selected] {
background: color-mix(in srgb, var(--tng-semantic-accent-brand) 15%, transparent);
}
[tngOption][data-disabled] {
cursor: not-allowed;
opacity: 0.56;
}
Example shells
listbox.contract.css
css
.project-listbox {
border: 1px solid var(--tng-semantic-border-subtle);
border-radius: 0.8rem;
display: grid;
gap: 0.45rem;
outline: none;
padding: 0.7rem;
}
.project-listbox:focus {
border-color: var(--tng-semantic-accent-brand);
}
.project-listbox [data-selected] {
background: color-mix(in srgb, var(--tng-semantic-accent-brand) 15%, transparent);
}
.project-listbox [data-active] {
border-color: var(--tng-semantic-accent-brand);
}
.project-listbox [data-disabled] {
cursor: not-allowed;
opacity: 0.56;
}