Switch
Turn a setting on or off with clear, theme-aware feedback.

use blinc_cn as cn;
use blinc_core::reactive::{Signal, computed};
use blinc_layout::{binding::IntoReactive, prelude::*};
pub fn switch_demo(confirmed: Signal<bool>, disabled: Signal<bool>) -> Div {
let checked = confirmed;
div()
.class("demo-stack")
.child(cn::switch(checked).label("Email notifications").disabled(disabled))
.child(
bound_text(computed(move |g| {
if g.get(checked).unwrap_or(false) {
"Notifications on"
} else {
"Notifications off"
}
.into()
}))
.class("demo-caption"),
)
}
fn bound_text(value: impl IntoReactive<String>) -> Text {
Text::bound(value.into_reactive())
}
@layer components {
.cn-switch {
font-family: var(--font-sans);
}
.cn-switch:focus-visible {
outline: 2px solid var(--focus-ring);
outline-offset: 2px;
}
.cn-switch {
background: var(--border-secondary);
border-radius: var(--radius-full);
cursor: pointer;
transition: background-color var(--duration-normal) var(--ease-state);
}
.cn-switch:hover:not(:disabled) {
background: color-mix(in srgb, var(--text-primary) 10%, var(--border-secondary));
}
.cn-switch:checked {
background: var(--primary) !important;
}
.cn-switch:checked:hover:not(:disabled) {
background: var(--primary-hover) !important;
}
.cn-switch:disabled {
background: var(--input-bg-disabled);
border: 1px solid var(--border-secondary);
opacity: 0.5;
cursor: not-allowed;
}
.cn-switch:checked:disabled {
background: var(--input-bg-disabled) !important;
}
.cn-switch:checked > .cn-switch-thumb {
background: var(--cn-switch-checked-thumb, var(--text-inverse)) !important;
}
.cn-switch-thumb {
background: var(--text-inverse);
border-radius: var(--radius-full);
box-shadow: var(--shadow-sm);
transition: transform var(--duration-normal) var(--ease-state);
}
.cn-switch:disabled > .cn-switch-thumb {
box-shadow: none;
border: 1px solid var(--border-secondary);
}
.cn-switch--sm:checked > .cn-switch-thumb {
transform: translateX(14px);
}
.cn-switch--md:checked > .cn-switch-thumb {
transform: translateX(20px);
}
.cn-switch--lg:checked > .cn-switch-thumb {
transform: translateX(24px);
}
.cn-switch:active:not(:disabled) > .cn-switch-thumb {
transform: scale(0.9);
}
.cn-switch--sm:checked:active:not(:disabled) > .cn-switch-thumb {
transform: translateX(14px) scale(0.9);
}
.cn-switch--md:checked:active:not(:disabled) > .cn-switch-thumb {
transform: translateX(20px) scale(0.9);
}
.cn-switch--lg:checked:active:not(:disabled) > .cn-switch-thumb {
transform: translateX(24px) scale(0.9);
}
.cn-switch-label {
color: var(--text-primary);
}
.cn-switch-row:disabled > .cn-switch-label {
color: var(--text-tertiary);
}
}
Usage and properties
Turn a setting on or off with clear, theme-aware feedback. Apply CSS after the component layer to restyle the documented parts.
Use the installation guide to add blinc_cn and its component stylesheet. Create the fixture bindings once, and return the demo from your UI builder. Values marked Bound accept Signal/Computed sources where the API supports them; structural options configure the component at construction.
| Property | Type | Update model | This example |
|---|---|---|---|
checked | Signal<bool> | Bound | values.confirmed |
label | string | Construction | Email notifications |
disabled | bool or IntoReactive<bool> | Bound | values.disabled |
Keyboard and interaction
Tab reaches the control; Enter or Space activates it. :focus-visible marks keyboard focus.
Customizing it
Add application CSS after the default component layer. These selectors are the restyling surface for this component:
.cn-switch.cn-switch-thumb.cn-switch-row.cn-switch-label
Theme and component variables: --cn-switch-checked-thumb, --primary, --text-primary, --text-secondary, --surface, --border, --radius-default, --duration-fast, --ease-state.
Built-in themes
The Live theme control installs Blinc's own theme bundles, including their colours, focus rings, corner shapes and motion tokens.
pub fn theme_bundle_for(name: &str) -> blinc_theme::ThemeBundle {
match name {
"restrained" => blinc_theme::RestrainedTheme::bundle(),
"expressive" => blinc_theme::ExpressiveTheme::bundle(),
_ => blinc_theme::HybridTheme::bundle(),
}
.with_css(blinc_cn::cn_styles::CN_STYLES)
}Download the default widget and component stylesheets. Sample assets and licenses are available in the asset guide.
Documentation
From the canonical component guide.
let dark = ctx.use_signal(false);
switch(dark)
.label("Dark mode")
.on_change(|on| println!("dark: {on}"))
switch(dark).size(SwitchSize::Small) // Small, Medium, Large
switch(dark).disabled(locked) // a bool, Signal or Computed
Styling
The switch is the track, with a thumb inside it. The track has a checked
attribute while on; the thumb slides by a transform and shrinks slightly while
pressed. All of it is the .cn-switch rules of CN_STYLES, per size
(.cn-switch--sm, --md, --lg):
@layer components {
.my-switch:checked {
background: #16a34a !important;
}
.my-switch:checked:hover:not(:disabled) {
background: #15803d !important;
}
}
switch(dark).class("my-switch")
Platforms and version
The component API is available in Blinc 0.6.0. The Live example runs in a dedicated worker and requires a compatible GPU surface. Preview and Code remain available when Live cannot start. The canvas has a descriptive label; a full screen-reader DOM mirror is not provided. The gallery integration is based on revision e2e55c24e078.
Recordings and live demos use the default Hybrid theme with bundled Noto Sans. The recording uses a 560 × 320 logical viewport at twice that resolution. Live follows its container and actual display density. Source base revision · Exact build source · Artifact versions and checksums · Font license.