Hover✨ Premium

Switch with Icons

Toggle switches with an SVG icon inside the knob: the icon changes on toggle (sun/moon, sound/mute, on/off). The knob slides with an elastic overshoot. Keyboard and screen-reader accessible.

CSSToggleIcons

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Hover has 48 effects, including 5 free. Explore the category →

3 usage examples

Dark/light mode toggle in a web app topbar — a single switch in the main navigation bar — Switch with Icons example 1

① Dark/light mode toggle in a web app topbar — a single switch in the main navigation bar

WhenA single switch in the top-right header toggles the interface theme. The moon icon (dark mode on) or sun icon (light mode on) identifies the state without text.
WhyAn isolated toggle in the navigation is the most minimal form of a theme switcher: it fits without cluttering; the icon in the knob removes the need for any label.
SettingsSwitch scaled to 52×28 px to fit the topbar height; moon icon in ON state, label hidden; page background in light mode (contrasted theme for the example).
Enabling automation rules — 'Status' column in a no-code tool's rules table — Switch with Icons example 2

② Enabling automation rules — 'Status' column in a no-code tool's rules table

WhenEach table row is a rule (name, description, trigger) ending with a mini-switch (50×26 px) that activates or deactivates the rule individually.
WhyIn a rules list, a subtle end-of-row switch enables toggling without opening a form; the icon (checkmark / bell) adds visual context.
SettingsSwitch at 50×26 px, label hidden (aria-label built from the Rule column); mixed initial states (on/off) to show both states in the same view.
Permission configuration during onboarding — step 2 of a startup wizard — Switch with Icons example 3

③ Permission configuration during onboarding — step 2 of a startup wizard

WhenIn a 3-step wizard, step 2 offers 3 toggles: email alerts, public profile, AI suggestions. The user enables or disables each option before continuing.
WhyThe icon-in-knob switch makes initial configuration intuitive: each option is understandable at a glance (envelope, silhouette, sun) without reading the label.
SettingsSwitches at standard size (72×36 px) inside permission cards; envelope, silhouette, sun/star icons; wizard background centred in the scene.

How it works

The .fx-0368-root root (250 px tall, rgb(26, 26, 36) background) centers three settings rows in .mi-switch-scene. Each row is made up of a span.mi-switch-label and a div.mi-switch toggle. The switch (80×40 px, rounded corners) holds a div.mi-switch-knob (32×32 px) containing an icon SVG. The knob moves from 4 px (off state) to 44 px (on state) via the CSS left property. The transition (cubic-bezier(0.34, 1.56, 0.64, 1), 0.3 s) gives a slight rightward overshoot before settling, creating a rubber-band feel.

Each switch has role="switch", aria-checked (true or false), and tabindex="0". The script reads the neighboring label’s text to dynamically build the switch’s aria-label: a switch preceded by the label Notifications receives aria-label="Notifications". This avoids static IDs that would duplicate when two copies of the component are on the same page. On click or Space/Enter keypress, the script toggles the .on class and updates aria-checked.

Each icon SVG has two CSS states: the off-state icon is visible when .on is absent, and vice versa. The icon change happens via a 0.2 s opacity transition. Both icons are in the DOM simultaneously; their swap is handled entirely in CSS via the .mi-switch:not(.on) and .mi-switch.on selectors. No DOM manipulation is needed for the icon change.

Accessibility

  • Switch role: each toggle has role="switch" and aria-checked. Screen readers announce it as a switch and report its state (on / off).
  • Keyboard: Tab reaches each switch (roving tabindex between rows), Space or Enter toggles it. The focus outline (:focus-visible) is visible on keyboard navigation.
  • Dynamic label: the aria-label is built from the neighboring span.mi-switch-label text by JavaScript. This approach works with two copies of the component on the same page without duplicate IDs.
  • Reduced motion: under prefers-reduced-motion: reduce, knob movement is instant (the left transition is removed); background color and icon transitions stay active over 0.15 s.

Browser compatibility

CSS (transitions on left, background-color, opacity) + 0.4 KB of JavaScript (classList, setAttribute, keydown), no dependency. Verified in Chromium 151: three independent switches, aria-checked updated, aria-label built from neighboring label, two independent component copies, reduced-motion without sliding, 0 errors.

Chrome 49+✓ Full (measured)
Firefox 55+✓ Full
Safari 9+✓ Full
Edge 79+✓ Full (Chromium engine)
Mobile iOS 9+✓ Full
Android Chrome 49+✓ Full (Chromium engine)

Without JavaScript, switches are visually correct but do not toggle on click. Without CSS transitions, the knob moves and the icon changes instantly.

The code

HTML structure to paste into your page (the CSS + JS come with your purchase):

index.html — structure
<div class="fx-0368-root">
  <div class="mi-switch-scene">
    <div class="mi-switch-row">
      <span class="mi-switch-label">Notifications</span>
      <div class="mi-switch on" role="switch" aria-checked="true" tabindex="0">
        <div class="mi-switch-knob">
          <!-- SVG icon for on state -->
        </div>
      </div>
    </div>
  </div>
</div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Icons in the knobs (HTML — SVG inside .mi-switch-knob) Sun/moon, sound/mute, check/cross Replace the SVGs with any pair of 16 px icons. Keep both SVGs in the knob (one per state); CSS handles their display via .mi-switch:not(.on) and .mi-switch.on.
Active background color (CSS — background on .mi-switch.on) Violet #6366f1 Switch to green (#10b981) for an activation toggle, or red (#ef4444) for a critical disableable function. The knob color at rest (.mi-switch-knob) can stay white regardless of the background color.
Knob bounce (CSS — cubic-bezier of the left transition) cubic-bezier(0.34, 1.56, 0.64, 1) ease-out gives a smooth slide with no bounce. Raise the second parameter above 1.5 for a more pronounced rebound.
Initial state (HTML — .on class on .mi-switch) Notifications ON, Dark mode OFF, Sound ON Add or remove the .on class in the HTML to set each switch’s initial state. The JS reads the initial state and sets aria-checked accordingly.

FAQ

In the click/keydown handler, after the class toggle, read the state via sw.classList.contains('on') and call your logic (e.g. document.body.classList.toggle('dark-theme')). The current script only toggles the visual and ARIA state.
Yes. Add a .disabled class to the .mi-switch div and check for it at the start of the handler to ignore the click. Also add aria-disabled="true" and remove tabindex so screen readers announce it as unavailable.
Yes. Remove the SVGs from .mi-switch-knob. Background and knob transitions work identically. Without an icon, the adjacent text label (span.mi-switch-label) becomes even more important to indicate what the switch controls.