Button Library · Signature
Bakelite Switch
An old wall switch on a cream plate, with two screws and a real clunk.
States
Default
Hover
Active
Focus
Code
sig-switch.html
<button class="btn-sig-switch" type="button" aria-pressed="false">
<span class="btn-sig-switch__screw" aria-hidden="true"></span>
<span class="btn-sig-switch__well">
<span class="btn-sig-switch__rocker" aria-hidden="true"></span>
</span>
<span class="btn-sig-switch__word">Fan</span>
<span class="btn-sig-switch__screw" aria-hidden="true"></span>
</button>
sig-switch.css
.btn-sig-switch {
--btn-bg: #e8dcc4;
--btn-fg: #3a2a20;
--btn-font: ui-monospace, monospace;
--btn-size: 12px;
--btn-weight: 600;
--btn-w: 88px;
--btn-h: 132px;
--btn-bw: 0px;
--btn-line: #8b6cff;
--btn-radius: 10px;
--btn-speed: 90ms;
}
.btn-sig-switch {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
box-sizing: border-box;
width: var(--btn-w);
height: var(--btn-h);
padding: 8px 0;
font-family: var(--btn-font);
font-size: var(--btn-size);
font-weight: var(--btn-weight);
line-height: 1.2;
color: var(--btn-fg);
background: var(--btn-bg);
border: var(--btn-bw) solid var(--btn-line);
border-radius: var(--btn-radius);
box-shadow: inset 0 1px 0 #fff, 0 4px 10px rgba(0,0,0,.35);
cursor: pointer;
text-decoration: none;
white-space: nowrap;
transition: all var(--btn-speed) ease;
flex-direction: column;
}
.btn-sig-switch:focus-visible {
outline: 2px solid var(--btn-bg);
outline-offset: 2px;
}
.btn-sig-switch:disabled {
opacity: 0.5;
pointer-events: none;
}
.btn-sig-switch__icon {
width: 1.15em;
height: 1.15em;
flex-shrink: 0;
}
.btn-sig-switch:hover {
background: color-mix(in srgb, var(--btn-bg) 94%, #fff);
}
.btn-sig-switch[aria-pressed=true] {
--turn: -22deg;
--lamp: 1;
}
.btn-sig-switch::after {
content: "";
position: absolute;
top: 12px;
right: 12px;
width: 7px;
height: 7px;
background: #ffb300;
border-radius: 50%;
box-shadow: 0 0 8px #ffb300;
opacity: var(--lamp, 0);
}
.btn-sig-switch__screw {
width: 9px;
height: 9px;
background: linear-gradient(45deg, transparent 44%, #3a2a20 44% 56%, transparent 56%), #c4b393;
border-radius: 50%;
}
.btn-sig-switch__well {
display: grid;
place-items: center;
width: 38px;
height: 58px;
background: #2a1e17;
border-radius: 6px;
perspective: 120px;
}
.btn-sig-switch__rocker {
width: 30px;
height: 50px;
background: linear-gradient(180deg, #5a4436, var(--btn-fg) 50%, #1d140f);
border-radius: 4px;
transform: rotateX(var(--turn, 22deg));
transition: transform var(--btn-speed) steps(2);
}
@media (prefers-reduced-motion: reduce) {
.btn-sig-switch,
.btn-sig-switch__rocker {
animation: none !important;
transition: none !important;
}
}
sig-switch.js
document.querySelectorAll(".btn-sig-switch").forEach((el) => {
const flip = () => el.setAttribute("aria-pressed", String(el.getAttribute("aria-pressed") !== "true"));
el.addEventListener("click", flip);
return () => el.removeEventListener("click", flip);
});
sig-switch.tsx
import { useEffect, useRef } from "react";
import type React from "react";
import "./sig-switch.css"; // the CSS tab
function attach(el: HTMLElement) {
const flip = () => el.setAttribute("aria-pressed", String(el.getAttribute("aria-pressed") !== "true"));
el.addEventListener("click", flip);
return () => el.removeEventListener("click", flip);
}
export function BtnSigSwitch({ children = "Fan", ...props }: React.ComponentProps<"button">) {
const ref = useRef<HTMLButtonElement>(null);
useEffect(() => attach(ref.current!), []);
return (
<button className="btn-sig-switch" type="button" aria-pressed="false" ref={ref} {...props}>
<span className="btn-sig-switch__screw" aria-hidden="true" />
<span className="btn-sig-switch__well">
<span className="btn-sig-switch__rocker" aria-hidden="true" />
</span>
<span className="btn-sig-switch__word">{children}</span>
<span className="btn-sig-switch__screw" aria-hidden="true" />
</button>
);
}
sig-switch.html
<button class="btn-sig-switch relative inline-flex items-center justify-center gap-[6px] [box-sizing:border-box] w-[88px] h-[132px] [padding:8px_0] [font-family:ui-monospace,_monospace] text-[12px] font-[600] [line-height:1.2] text-[#3a2a20] bg-[#e8dcc4] [border:0px_solid_#8b6cff] rounded-[10px] [box-shadow:inset_0_1px_0_#fff,_0_4px_10px_rgba(0,0,0,.35)] cursor-pointer no-underline whitespace-nowrap [transition:all_90ms_ease] [flex-direction:column] focus-visible:[outline:2px_solid_#e8dcc4] focus-visible:[outline-offset:2px] disabled:[opacity:0.5] disabled:[pointer-events:none] hover:[background:color-mix(in_srgb,_#e8dcc4_94%,_#fff)] [&[aria-pressed=true]]:[--turn:-22deg] [&[aria-pressed=true]]:[--lamp:1] after:content-[''] after:absolute after:[top:12px] after:[right:12px] after:w-[7px] after:h-[7px] after:bg-[#ffb300] after:rounded-[50%] after:[box-shadow:0_0_8px_#ffb300] after:[opacity:var(--lamp,_0)] motion-reduce:[transition:none!important]" type="button" aria-pressed="false">
<span class="w-[9px] h-[9px] [background:linear-gradient(45deg,_transparent_44%,_#3a2a20_44%_56%,_transparent_56%),_#c4b393] rounded-[50%]" aria-hidden="true"></span>
<span class="grid [place-items:center] w-[38px] h-[58px] bg-[#2a1e17] rounded-[6px] [perspective:120px]">
<span class="w-[30px] h-[50px] [background:linear-gradient(180deg,_#5a4436,_#3a2a20_50%,_#1d140f)] rounded-[4px] [transform:rotateX(var(--turn,_22deg))] [transition:transform_90ms_steps(2)] motion-reduce:[transition:none!important]" aria-hidden="true"></span>
</span>
<span>Fan</span>
<span class="w-[9px] h-[9px] [background:linear-gradient(45deg,_transparent_44%,_#3a2a20_44%_56%,_transparent_56%),_#c4b393] rounded-[50%]" aria-hidden="true"></span>
</button>
<!-- Needs the script from the JS tab. -->