Styling contract
Headless switch exposes a small, predictable contract on the button host. Build the visual shell around that contract rather than relying on invisible wrapper markup.
| Selector | Applied on | Purpose |
|---|---|---|
[data-slot="switch"] | Primitive button | Stable selector anchor for track styling. |
[data-state="checked"], [data-state="unchecked"] | Primitive button | Switch between active and inactive visuals. |
[data-disabled] | Primitive button | Dim the control and block hover affordances. |
:focus-visible | Primitive button | Keyboard-only focus ring around the switch itself. |
State selectors
switch.states.css
css
button[tngSwitch][data-slot="switch"] {
background: var(--tng-semantic-border-subtle, #cbd5e1);
}
button[tngSwitch][data-state="checked"] {
background: var(--tng-semantic-accent-brand, #2563eb);
}
button[tngSwitch][data-disabled] {
cursor: not-allowed;
opacity: 0.55;
}
button[tngSwitch]:focus-visible {
outline: 2px solid color-mix(
in srgb,
var(--tng-semantic-accent-brand, #2563eb) 25%,
white
);
outline-offset: 2px;
}
switch.shell.css
css
.headless-switch-shell {
display: grid;
gap: 0.85rem;
inline-size: min(100%, 30rem);
margin-inline: auto;
padding: 1rem;
border: 1px solid #cbd5e1;
border-radius: 1rem;
background: #ffffff;
}
.headless-switch-shell__row {
display: inline-flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}
Example shells
The same switch primitive can live inside a hand-written CSS shell or a Tailwind utility shell without changing the behavior layer.
Status shell (Plain-CSS)
Require review
Status shell (Tailwind CSS)
Require reviewHold publishing until an approver signs off.