Accessible Rich Internet Applications specification.
Screen reader: “Wi-Fi, switch, true”
role="switch" aria-checked="true"
<div class="aria-demo" style="--accent:#A8E6C1;">
<div class="aria-demo__row">
<button type="button" class="aria-demo__ctrl aria-demo__ctrl--switch is-on" role="switch" aria-checked="true" aria-label="Wi-Fi">
<span class="aria-demo__thumb"></span>
</button>
<span class="aria-demo__label">Wi-Fi</span>
</div>
<p class="aria-demo__sr">Screen reader: “Wi-Fi, switch, true”</p>
<code class="aria-demo__code">role="switch" aria-checked="true"</code>
</div>
<style>
.aria-demo { width: 320px; font: 15px/1.5 system-ui, sans-serif; color: #241430; }
.aria-demo__row { display: flex; align-items: center; gap: 12px; }
.aria-demo__ctrl { position: relative; border: 2px solid var(--accent); background: #FAF7F2; cursor: pointer; padding: 0; }
.aria-demo__ctrl--switch { width: 48px; height: 26px; border-radius: 999px; }
.aria-demo__ctrl--checkbox { width: 26px; height: 26px; border-radius: 6px; }
.aria-demo__ctrl--radio { width: 26px; height: 26px; border-radius: 50%; }
.aria-demo__thumb { position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 50%; background: var(--accent); opacity: 0; }
@media (prefers-reduced-motion: no-preference) {
.aria-demo__thumb { transition: transform .15s, opacity .15s; }
}
.aria-demo__ctrl--switch .aria-demo__thumb { opacity: 1; }
.aria-demo__ctrl--switch.is-on .aria-demo__thumb { transform: translateX(22px); }
.aria-demo__ctrl--checkbox.is-on .aria-demo__thumb { opacity: 1; border-radius: 4px; width: 18px; height: 18px; }
.aria-demo__ctrl--radio.is-on .aria-demo__thumb { opacity: 1; }
.aria-demo__ctrl:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.aria-demo__label { font-weight: 600; }
.aria-demo__sr { margin: 10px 0 0; font-size: 13px; background: #A8E6C133; border-left: 3px solid var(--accent); padding: 8px 10px; }
.aria-demo__code { display: block; margin-top: 8px; font: 12px/1.4 ui-monospace, monospace; color: #241430aa; }
</style><div class="flex flex-col gap-2.5" style="--accent:#A8E6C1;width:320px;font-family:system-ui,sans-serif">
<div class="flex items-center gap-3">
<button type="button" role="switch" aria-checked="true" aria-label="Wi-Fi" class="aria-tw aria-tw--switch is-on relative border-2 bg-[#FAF7F2] cursor-pointer p-0" style="border-color:#A8E6C1">
<span class="aria-tw__thumb absolute top-0.5 left-0.5 w-[18px] h-[18px] rounded-full motion-safe:transition-transform" style="background:#A8E6C1"></span>
</button>
<span class="font-semibold text-[#241430] text-[15px]">Wi-Fi</span>
</div>
<p class="text-[13px] m-0 p-2 border-l-[3px]" style="background:#A8E6C133;border-color:#A8E6C1">Screen reader: “Wi-Fi, switch, true”</p>
<code class="text-[12px] text-[#241430aa]">role="switch" aria-checked="true"</code>
</div>
<style>
.aria-tw--switch { width: 48px; height: 26px; border-radius: 999px; }
.aria-tw--checkbox { width: 26px; height: 26px; border-radius: 6px; }
.aria-tw--radio { width: 26px; height: 26px; border-radius: 50%; }
.aria-tw--checkbox .aria-tw__thumb, .aria-tw--radio .aria-tw__thumb { opacity: 0; }
.aria-tw--switch.is-on .aria-tw__thumb { transform: translateX(22px); }
.aria-tw--checkbox.is-on .aria-tw__thumb, .aria-tw--radio.is-on .aria-tw__thumb { opacity: 1; }
</style>export function AriaDemo({
label = "Wi-Fi",
checked = true,
role = "switch",
accent = "#A8E6C1",
announce = true,
}) {
const shape = {
switch: { box: { width: 48, height: 26, borderRadius: 999 }, on: { transform: "translateX(22px)", opacity: 1 }, off: { opacity: 1 } },
checkbox: { box: { width: 26, height: 26, borderRadius: 6 }, on: { opacity: 1 }, off: { opacity: 0 } },
radio: { box: { width: 26, height: 26, borderRadius: "50%" }, on: { opacity: 1 }, off: { opacity: 0 } },
}[role];
return (
<div style={{ width: 320, fontFamily: "system-ui, sans-serif", fontSize: 15, color: "#241430" }}>
<div style={{ display: "flex", alignItems: "center", gap: 12 }}>
<button type="button" role={role} aria-checked={checked} aria-label={label}
style={{ position: "relative", border: "2px solid " + accent, background: "#FAF7F2", cursor: "pointer", padding: 0, ...shape.box }}>
<span className="aria-thumb" style={{ position: "absolute", top: 2, left: 2, width: 18, height: 18, borderRadius: "50%", background: accent, ...(checked ? shape.on : shape.off) }} />
</button>
<span style={{ fontWeight: 600 }}>{label}</span>
</div>
<style>{`
@media (prefers-reduced-motion: no-preference) {
.aria-thumb { transition: transform .15s, opacity .15s; }
}
`}</style>
{announce && <p style={{ margin: "10px 0 0", fontSize: 13, background: "#A8E6C133", borderLeft: "3px solid " + accent, padding: "8px 10px" }}>Screen reader: “{label}, {role}, {String(checked)}”</p>}
<code style={{ display: "block", marginTop: 8, fontSize: 12, color: "#241430aa" }}>role="{role}" aria-checked="{String(checked)}"</code>
</div>
);
}