Ir al contenido
Evaluar mi proyecto
Componentes · FormulariosDisponible

Interruptor skeuomórfico de palanca

Palanca metálica en ranura hundida con luz piloto que se enciende y rotulado grabado en la placa.

Código completo

Es exactamente el archivo que se descarga. Cópialo a tu proyecto y úsalo.

interruptor-skeuo.tsx61 líneas
Descargar
"use client";

import { useState, type CSSProperties } from "react";

/** Interruptor skeuomórfico: palanca metálica en una ranura hundida, con luz piloto y rotulado grabado. React + TypeScript, sin dependencias. */
export type InterruptorSkeuoProps = {
  checked?: boolean;
  defaultChecked?: boolean;
  onChange?: (checked: boolean) => void;
  label?: string;
  disabled?: boolean;
  className?: string;
  style?: CSSProperties;
};

const CSS = `
.gcisk-root { box-sizing: border-box; display: inline-flex; align-items: center; gap: 16px; max-width: 100%; font: 700 12px/1 "Helvetica Neue", Arial, ui-sans-serif, system-ui, sans-serif; letter-spacing: .14em; text-transform: uppercase; color: #3d3f45; }
.gcisk-root *, .gcisk-root *::before, .gcisk-root *::after { box-sizing: border-box; }
.gcisk-root button { font: inherit; -webkit-tap-highlight-color: transparent; }
.gcisk-root :focus-visible { outline: 3px solid #2f7bff; outline-offset: 4px; border-radius: 12px; }
@media (prefers-reduced-motion: reduce) { .gcisk-root *, .gcisk-root *::before, .gcisk-root *::after { animation: none !important; transition-duration: .01ms !important; } }
.gcisk-plate { position: relative; padding: 8px; border-radius: 14px; background: linear-gradient(180deg, #e9ebef, #c9ccd3); box-shadow: inset 0 1px 0 #fff, inset 0 -1px 0 rgba(0, 0, 0, .18), 0 1px 0 rgba(255, 255, 255, .7), 0 3px 8px rgba(0, 0, 0, .25); }
.gcisk-sw { position: relative; display: block; width: 92px; height: 48px; padding: 0; border: 0; border-radius: 10px; cursor: pointer; background: linear-gradient(180deg, #202226, #3a3d44); box-shadow: inset 0 3px 7px rgba(0, 0, 0, .9), inset 0 -1px 0 rgba(255, 255, 255, .12), 0 1px 0 #fff; }
.gcisk-sw::before, .gcisk-sw::after { position: absolute; top: 50%; margin-top: -5px; font-size: 9px; font-weight: 800; letter-spacing: .08em; color: #7d818b; }
.gcisk-sw::before { content: "OFF"; right: 11px; }
.gcisk-sw::after { content: "ON"; left: 14px; color: #5e6068; }
.gcisk-sw[aria-checked="true"]::after { color: #8dffa0; text-shadow: 0 0 8px rgba(80, 255, 120, .9); }
.gcisk-sw[aria-checked="true"]::before { color: transparent; }
.gcisk-knob { position: absolute; top: 4px; left: 4px; width: 46px; height: 40px; border-radius: 8px; background: repeating-linear-gradient(90deg, rgba(255, 255, 255, 0) 0 2px, rgba(255, 255, 255, .25) 2px 3px), linear-gradient(180deg, #fbfbfd, #cfd2d9 55%, #aeb2bb); box-shadow: inset 0 1px 0 #fff, inset 0 -3px 4px rgba(0, 0, 0, .18), 0 3px 5px rgba(0, 0, 0, .6); transition: transform .28s cubic-bezier(.3, 1.4, .5, 1); }
.gcisk-knob::before { content: ""; position: absolute; top: 8px; bottom: 8px; left: 50%; width: 2px; margin-left: -1px; background: rgba(0, 0, 0, .25); box-shadow: -6px 0 0 rgba(0, 0, 0, .2), 6px 0 0 rgba(0, 0, 0, .2), 1px 0 0 rgba(255, 255, 255, .8); }
.gcisk-sw[aria-checked="true"] .gcisk-knob { transform: translateX(38px); }
.gcisk-sw:active:not(:disabled) .gcisk-knob { filter: brightness(.93); }
.gcisk-sw:disabled { opacity: .5; cursor: not-allowed; }
.gcisk-led { width: 12px; height: 12px; border-radius: 50%; background: radial-gradient(circle at 35% 30%, #7a3a3a, #3d1818); box-shadow: inset 0 1px 2px rgba(0, 0, 0, .6), 0 1px 0 rgba(255, 255, 255, .8); transition: background .25s, box-shadow .25s; }
.gcisk-led[data-on="true"] { background: radial-gradient(circle at 35% 30%, #d8ffd2, #32d34a 60%, #14912a); box-shadow: 0 0 10px 3px rgba(50, 211, 74, .65), 0 1px 0 rgba(255, 255, 255, .8); }
.gcisk-text { text-shadow: 0 1px 0 rgba(255, 255, 255, .85); }`;

export function InterruptorSkeuo({ checked, defaultChecked = false, onChange, label = "Alimentación", disabled, className = "", style }: InterruptorSkeuoProps) {
  const [inner, setInner] = useState(defaultChecked);
  const on = checked ?? inner;
  function toggle() {
    const next = !on;
    setInner(next);
    onChange?.(next);
  }
  return (
    <span className={`gcisk-root ${className}`} style={style}>
      <style>{CSS}</style>
      <span className="gcisk-plate">
        <button className="gcisk-sw" type="button" role="switch" aria-checked={on} aria-label={label} disabled={disabled} onClick={toggle}>
          <span className="gcisk-knob" />
        </button>
      </span>
      <span className="gcisk-led" data-on={on} aria-hidden="true" />
      <span className="gcisk-text" aria-hidden="true">
        {label}
      </span>
    </span>
  );
}

Úsalo en tu proyecto

Instálalo con la CLI de shadcn.

terminal.sh1 líneas
npx shadcn@latest add https://www.gassierconsulting.com/r/interruptor-skeuo

Ejemplo de uso de Interruptor skeuomórfico de palanca

Después de copiar interruptor-skeuo.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.

ejemplo.tsx5 líneas
import { InterruptorSkeuo } from "./interruptor-skeuo";

export default function Ejemplo() {
  return <InterruptorSkeuo label="Ejemplo" checked />;
}

Propiedades de InterruptorSkeuo

InterruptorSkeuo acepta 7 propiedades: todas son opcionales y tienen un valor por defecto, así que funciona sin configurar nada.

PropiedadTipoDescripción
checked?booleanOpcional.
defaultChecked?booleanOpcional.
onChange?(checked: boolean) => voidOpcional.
label?stringOpcional.
disabled?booleanOpcional.
className?stringOpcional.
style?CSSPropertiesOpcional.

Accesibilidad y rendimiento

El archivo tiene 61 líneas (4 KB) y no necesita @keyframes. Esto es lo que hace Interruptor skeuomórfico de palanca por quien lo usa:

  • Detecta prefers-reduced-motion y baja o quita el movimiento cuando la persona lo pide.
  • Muestra un anillo de foco visible al navegar con teclado (:focus-visible).
  • Declara roles ARIA para lectores de pantalla: switch.
  • Es un componente de cliente de Next.js: incluye la directiva "use client".
Licencia
MIT
Dependencias
React >=18
Compatibilidad
React 18 y 19 · Next.js (componente de cliente)