ARIA

Accessible Rich Internet Applications specification.

Wi-Fi

Screen reader: “Wi-Fi, switch, true”

role="switch" aria-checked="true"

html

<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: &ldquo;Wi-Fi, switch, true&rdquo;</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>

tailwind

<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: &ldquo;Wi-Fi, switch, true&rdquo;</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>

react

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: &ldquo;{label}, {role}, {String(checked)}&rdquo;</p>}
      <code style={{ display: "block", marginTop: 8, fontSize: 12, color: "#241430aa" }}>role="{role}" aria-checked="{String(checked)}"</code>
    </div>
  );
}