Forms

Switch

An on/off toggle that is a real checkbox: it submits with a form and works with the keyboard.

#forms#input#no-js

Demo

Preview

Emails are on.

Get it

It needs theme.qwe, as every component does; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.

$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/switch.qwe
main.qwe
import ui/theme.qwe use theme_cssuse ui/switch.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>

Example

The code of the preview above.

demos/switch.qwe
component SwitchDemoLive() {    state on = true    <div.demo-stack>        <Switch label="Email me about replies" hint="At most one a day." checked={on}                on_change={(v) => { on = v }}/>        <p.demo-note>Emails are {on ? "on" : "off"}.</p>    </div>}view SwitchDemo() =    <div.demo-stack>        <SwitchDemoLive/>        <div.demo-row>            <Switch name="beta" label="Beta features"/>            <Switch size="sm" label="Small" checked={true}/>            <Switch label="Disabled" disabled={true}/>        </div>    </div>

Source

ui/switch.qwe
// Switch: an on/off toggle. It is a real checkbox, so it works in a plain <form>// (`name` is sent as "on", or `value`, when it is on) and with the keyboard.////   <Switch name="alerts" label="Email me about replies" checked={true}/>//   <Switch label="Dark mode" checked={dark} on_change={(on) => { dark = on }}/>//// on_change(on) is called when it is flipped; it works when the Switch is inside// one of your components (a page's own props are data, not functions).// size: "sm" or "md" (default). hint: a line of help under the label.import ui/theme.qwe use ui_clslet switch_css = css`.ui-switch { display: inline-flex; align-items: flex-start; gap: 10px; cursor: pointer; font: 14px/1.4 var(--ui-font); color: var(--ui-fg); }.ui-switch input { position: absolute; opacity: 0; width: 1px; height: 1px; }.ui-switch-track {  flex: none; position: relative; width: 38px; height: 22px; margin-top: -1px; border-radius: 999px;  background: var(--ui-strong); transition: background .15s;}.ui-switch-track::after {  content: ""; position: absolute; top: 3px; left: 3px; width: 16px; height: 16px; border-radius: 50%;  background: #fff; box-shadow: 0 1px 2px rgba(0, 0, 0, .25); transition: transform .15s;}.ui-switch input:checked + .ui-switch-track { background: var(--ui-accent); }.ui-switch input:checked + .ui-switch-track::after { transform: translateX(16px); }.ui-switch input:focus-visible + .ui-switch-track { box-shadow: var(--ui-focus); }.ui-switch input:disabled + .ui-switch-track { opacity: .5; }.ui-switch:has(input:disabled) { cursor: not-allowed; color: var(--ui-muted); }.ui-switch-sm .ui-switch-track { width: 30px; height: 18px; }.ui-switch-sm .ui-switch-track::after { width: 12px; height: 12px; }.ui-switch-sm input:checked + .ui-switch-track::after { transform: translateX(12px); }.ui-switch-text { display: grid; gap: 2px; }.ui-switch-hint { color: var(--ui-muted); font-size: 13px; }@media (prefers-reduced-motion: reduce) { .ui-switch-track, .ui-switch-track::after { transition: none; } }`view Switch(name: str? = null, label: str = "", hint: str? = null, checked: bool = false,            value: str? = null, disabled: bool = false, size: str = "md", on_change = null, class: str? = null) {    style global switch_css    <label class={ui_cls(["ui-switch", size == "sm" && "ui-switch-sm", class])}>        <input type="checkbox" role="switch" name={name} value={value} checked={checked} disabled={disabled}               @change={(e) => { on_change && on_change(e.checked) }}/>        <span.ui-switch-track aria-hidden="true"></span>        {(label != "" || hint != null) && <span.ui-switch-text>            {label}            {hint != null && <span.ui-switch-hint>{hint}</span>}        </span>}    </label>}
Thread
46Useful? Like it, or add to its thread below.

Thread 0 posts

On Switch: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.