🔥 Oxide UI v0.1.0 — Dark mode only, copy-paste ready. Get started →
Skip to content

Toggle / Switch

<button role="switch">

A binary control for enabling or disabling a setting. More visually expressive than a checkbox.

Overview

Preview

Basic toggle

Preview
Enabled
html
<!-- On -->
<button
  role="switch"
  aria-checked="true"
  class="relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent bg-[#D40C37] transition-colors duration-200 ease-in-out focus:outline-none focus-visible:ring-2 focus-visible:ring-[#D40C37]/50 focus-visible:ring-offset-2 focus-visible:ring-offset-zinc-950"
>
  <span class="pointer-events-none inline-block h-5 w-5 translate-x-5 transform rounded-full bg-white shadow-lg ring-0 transition duration-200 ease-in-out" />
</button>

<!-- Off -->
<button
  role="switch"
  aria-checked="false"
  class="... bg-zinc-700"
>
  <span class="... translate-x-0" />
</button>

With Vue reactivity

vue
<script setup>
import { ref } from 'vue'
const enabled = ref(false)
</script>

<template>
  <button
    role="switch"
    :aria-checked="enabled"
    @click="enabled = !enabled"
    :class="['relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#D40C37]/50 focus-visible:ring-offset-2 focus-visible:ring-offset-zinc-950',
      enabled ? 'bg-[#D40C37]' : 'bg-zinc-700']"
  >
    <span :class="['pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow-lg ring-0 transition duration-200 ease-in-out',
      enabled ? 'translate-x-5' : 'translate-x-0']"
    />
  </button>
</template>
vue
<script setup>
import { Switch } from '@headlessui/vue'
import { ref } from 'vue'
const enabled = ref(false)
</script>

<template>
  <Switch
    v-model="enabled"
    :class="['relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-[#D40C37]/50 focus-visible:ring-offset-2 focus-visible:ring-offset-zinc-950',
      enabled ? 'bg-[#D40C37]' : 'bg-zinc-700']"
  >
    <span :class="['pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow-lg transition duration-200',
      enabled ? 'translate-x-5' : 'translate-x-0']"
    />
  </Switch>
</template>

Accessibility notes

  • Use role="switch" with aria-checked for state
  • Always pair with a visible label or aria-label
  • The disabled attribute should also add aria-disabled="true"
  • Headless UI's <Switch> handles all ARIA and keyboard interaction automatically

Released under the MIT License.