Si necesitas botones on off en CSS con ese aspecto de interruptor tipo iOS, aquà tienes el código listo para pegar. Sin librerÃas. Sin JavaScript. Solo un checkbox y unas reglas CSS.
Voy a mostrarte un toggle switch accesible de verdad: funciona con teclado, lo entienden los lectores de pantalla y respeta a quien prefiere menos animación. Copias, pegas y adaptas colores en un minuto.
Botones on off en CSS: el interruptor tipo iOS
Un botón on/off es un control de dos estados: encendido o apagado. Lo usas para activar el modo oscuro, silenciar notificaciones o guardar una preferencia. Visualmente imita el interruptor fÃsico de un móvil: una pista alargada y un cÃrculo que se desliza.
La clave está en no reinventar la rueda. Debajo de ese interruptor CSS hay un <input type="checkbox"> normal y corriente. El navegador ya sabe marcarlo, desmarcarlo y moverlo con el teclado. Tú solo lo maquillas con CSS.
Regla de oro: si el control activa o desactiva algo al instante, usa un checkbox estilado como switch. Si envÃa un formulario, sigue siendo un botón normal.
El toggle switch CSS accesible, paso a paso
Primero el HTML. Un <label> envuelve el checkbox, la pista y el texto. AsÃ, al hacer clic en cualquier parte, el estado cambia. El atributo role="switch" le dice al lector de pantalla que esto es un interruptor, no una casilla más.
<label class="switch">
<input class="switch__input" type="checkbox" role="switch">
<span class="switch__track" aria-hidden="true"></span>
<span class="switch__text">Activar notificaciones</span>
</label>
Ahora el CSS. Uso variables (--w, --on, --off…) para que después cambiar tamaño o color sea trivial. El checkbox real se oculta sin desaparecer del árbol de accesibilidad, y la pista se pinta con el hermano adyacente.
.switch {
--w: 3.25rem; /* ancho de la pista */
--h: 1.75rem; /* alto de la pista */
--pad: 3px; /* separación del cÃrculo */
--on: #22c55e; /* color encendido */
--off: #cbd5e1; /* color apagado */
--thumb: #ffffff; /* color del cÃrculo */
display: inline-flex;
align-items: center;
gap: .6rem;
cursor: pointer;
user-select: none;
font: 500 1rem/1.4 system-ui, sans-serif;
}
/* Oculta el checkbox sin quitarlo del foco ni del lector de pantalla */
.switch__input {
position: absolute;
width: 1px;
height: 1px;
opacity: 0;
}
.switch__track {
position: relative;
flex: none;
width: var(--w);
height: var(--h);
background: var(--off);
border-radius: 999px;
transition: background .2s ease;
}
/* El cÃrculo deslizante */
.switch__track::before {
content: "";
position: absolute;
top: var(--pad);
left: var(--pad);
width: calc(var(--h) - var(--pad) * 2);
height: calc(var(--h) - var(--pad) * 2);
background: var(--thumb);
border-radius: 50%;
box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
transition: transform .2s ease;
}
/* Estado encendido */
.switch__input:checked + .switch__track {
background: var(--on);
}
.switch__input:checked + .switch__track::before {
transform: translateX(calc(var(--w) - var(--h)));
}
/* Foco visible solo con teclado */
.switch__input:focus-visible + .switch__track {
outline: 2px solid #2563eb;
outline-offset: 2px;
}
/* Respeta a quien prefiere menos movimiento */
@media (prefers-reduced-motion: reduce) {
.switch__track,
.switch__track::before {
transition: none;
}
}
Eso es todo. Tienes un boton switch en HTML completo, funcional y accesible. Pruébalo: haz clic o pulsa Tab y luego Espacio. El cÃrculo se desliza y el color cambia de gris a verde.
3 variantes de estilo: tamaño, color e icono
Como todo cuelga de variables CSS, cada variante es una clase modificadora de dos lÃneas. No tocas la lógica: solo cambias valores.
1. Tamaños (pequeño y grande)
.switch--sm { --w: 2.5rem; --h: 1.35rem; }
.switch--lg { --w: 4.25rem; --h: 2.25rem; }
En el HTML solo añades la clase: <label class="switch switch--lg">. El cÃrculo y el recorrido se recalculan solos gracias a los calc().
2. Color de marca
.switch--brand { --on: #7c3aed; } /* violeta */
.switch--danger { --on: #ef4444; } /* rojo para acciones sensibles */
3. Con texto ON / OFF dentro
A veces el color no basta y quieres etiqueta dentro de la pista. Lo resuelvo con data-on y data-off más attr(), sin tocar el HTML del cÃrculo.
<label class="switch switch--text">
<input class="switch__input" type="checkbox" role="switch" aria-label="Wifi">
<span class="switch__track" data-on="ON" data-off="OFF" aria-hidden="true"></span>
</label>
.switch--text { --w: 4.5rem; --h: 2rem; }
.switch--text .switch__track::after {
content: attr(data-off);
position: absolute;
top: 0;
right: .55rem;
height: 100%;
display: flex;
align-items: center;
font: 700 .7rem/1 system-ui, sans-serif;
color: #fff;
}
.switch--text .switch__input:checked + .switch__track::after {
content: attr(data-on);
right: auto;
left: .55rem;
}
Si te gustan estos detalles de interacción, te va a interesar mi guÃa de microinteracciones web con ejemplos y código. Un toggle bien animado es justo eso: una microinteracción.
Accesibilidad: teclado, aria y prefers-reduced-motion
Aquà es donde la mayorÃa de tutoriales fallan. Un interruptor en CSS bonito pero inaccesible deja fuera a mucha gente. Estos son los tres puntos que ya cubre el código de arriba.
- Teclado nativo: al usar un checkbox real,
Tablo enfoca yEspaciolo activa. No añades ni una lÃnea de JavaScript. - Semántica correcta:
role="switch"hace que el lector de pantalla lo anuncie como «interruptor, activado/desactivado» en vez de «casilla». - Foco visible:
:focus-visiblemuestra el contorno solo a quien navega con teclado, sin molestar al clic de ratón.
Un apunte sobre las etiquetas. Si la pista lleva el texto visible (como en la variante ON/OFF), pon aria-label en el checkbox para que tenga nombre accesible. Si el texto va fuera, dentro del <label>, ya queda asociado y no necesitas nada más.
Por último, prefers-reduced-motion. Hay personas que sienten mareo con las animaciones. La media query desactiva la transición del deslizamiento para ellas y mantiene el cambio de estado. Puedes leer los detalles del selector :focus-visible en MDN.
Tip: ¿Montas la interfaz sobre WordPress y quieres que cargue rápido? Un hosting decente hace que estas microinteracciones se noten fluidas de verdad.
Más snippets CSS para tu interfaz
Si este toggle te ha resuelto la papeleta, en el blog tienes más recursos de la misma familia. Todos con código listo para copiar.
- Menús animados con CSS3 — la navegación de tu proyecto, con transiciones suaves.
- Personaliza el botón de subir archivos — otro control feo que CSS convierte en algo cuidado.
- Acordeón en HTML — para agrupar contenido sin saturar la pantalla.
- Microinteracciones web 2026 — la teorÃa detrás de por qué un toggle bien animado se siente mejor.
Preguntas frecuentes
¿Necesito JavaScript para un botón on/off en CSS?
No para el aspecto ni la interacción básica. El checkbox marca y desmarca solo. Necesitas JavaScript únicamente si quieres reaccionar al cambio (guardar la preferencia, aplicar el modo oscuro, llamar a una API).
¿Es accesible un toggle hecho solo con CSS?
SÃ, si lo construyes sobre un <input type="checkbox"> real con su <label>. Ahà heredas el foco, el teclado y la semántica gratis. Un <div> pintado como interruptor, en cambio, no es accesible sin mucho trabajo extra.
¿Qué diferencia hay entre un checkbox y un toggle switch?
Funcionalmente son lo mismo: dos estados. La diferencia es de expectativa. Un switch se usa cuando el cambio se aplica al instante; un checkbox, cuando confirmas después con un botón. El role="switch" comunica esa diferencia al lector de pantalla.
¿Cómo guardo el estado del interruptor?
Con JavaScript. Escuchas el evento change y guardas el valor en localStorage o en tu backend. Al recargar la página, lees ese valor y marcas el checkbox. El CSS se encarga del resto.
¿El atributo role=»switch» es obligatorio?
No es obligatorio, pero sà recomendable. Sin él, el control funciona igual, aunque el lector de pantalla lo anuncia como casilla. Con role="switch" queda más claro que es un interruptor de encendido y apagado.
Ya tienes tus botones on off en CSS listos para producción: accesibles, sin dependencias y fáciles de personalizar. Copia el snippet, ajusta las variables a tu marca y a otra cosa. Si te ha servido, compártelo con quien esté peleándose con un checkbox feo.


