32 CSS Floating Action Button Designs 16 / 32

Theme Toggle FAB

Floating light/dark mode toggle button with 4 style variants: circle, pill slider, square, and orbit ring — all synced to CSS variables.

CSS + JS MIT licensed
Live Demo Open in tab
Open in playground

The code

<div class="fb16">
<h1>Floating Theme Toggle</h1>
<p class="fb16-subtitle">Toggle light / dark mode — all 4 styles sync together</p>

<!-- Variant buttons -->
<div class="fb16-variants-row">

  <!-- 1. Circle sun/moon -->
  <div class="fb16-variant-col">
    <button class="fb16-toggle-fab fb16-fab-circle" data-theme-toggle aria-label="Toggle theme">
      <div class="fb16-fab-circle-inner">
        <svg class="fb16-icon-sun" viewBox="0 0 24 24">
          <circle cx="12" cy="12" r="5"/>
          <line x1="12" y1="1" x2="12" y2="3"/>
          <line x1="12" y1="21" x2="12" y2="23"/>
          <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/>
          <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/>
          <line x1="1" y1="12" x2="3" y2="12"/>
          <line x1="21" y1="12" x2="23" y2="12"/>
          <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/>
          <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>
        </svg>
        <svg class="fb16-icon-moon" viewBox="0 0 24 24">
          <path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/>
        </svg>
      </div>
    </button>
    <div class="fb16-variant-label">Circle</div>
  </div>

  <!-- 2. Pill -->
  <div class="fb16-variant-col">
    <button class="fb16-toggle-fab fb16-fab-pill-toggle" data-theme-toggle aria-label="Toggle theme">
      <div class="fb16-fab-pill-knob">
        <svg class="fb16-knob-sun" viewBox="0 0 24 24">
          <circle cx="12" cy="12" r="5"/>
          <line x1="12" y1="1" x2="12" y2="3"/>
          <line x1="12" y1="21" x2="12" y2="23"/>
          <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/>
          <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/>
          <line x1="1" y1="12" x2="3" y2="12"/>
          <line x1="21" y1="12" x2="23" y2="12"/>
          <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/>
          <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>
        </svg>
        <svg class="fb16-knob-moon" viewBox="0 0 24 24">
          <path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/>
        </svg>
      </div>
    </button>
    <div class="fb16-variant-label">Pill Slider</div>
  </div>

  <!-- 3. Square emoji -->
  <div class="fb16-variant-col">
    <button class="fb16-toggle-fab fb16-fab-square-toggle" data-theme-toggle aria-label="Toggle theme">
      <span class="fb16-fab-square-icon" id="fb16-squareIcon">☀️</span>
    </button>
    <div class="fb16-variant-label">Square</div>
  </div>

  <!-- 4. Orbit ring -->
  <div class="fb16-variant-col">
    <button class="fb16-toggle-fab fb16-fab-orbit" data-theme-toggle aria-label="Toggle theme">
      <div class="fb16-fab-orbit-ring"></div>
      <div class="fb16-fab-orbit-mask">
        <svg class="fb16-orbit-sun" viewBox="0 0 24 24">
          <circle cx="12" cy="12" r="5"/>
          <line x1="12" y1="1" x2="12" y2="3"/>
          <line x1="12" y1="21" x2="12" y2="23"/>
          <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/>
          <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/>
          <line x1="1" y1="12" x2="3" y2="12"/>
          <line x1="21" y1="12" x2="23" y2="12"/>
          <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/>
          <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>
        </svg>
        <svg class="fb16-orbit-moon" viewBox="0 0 24 24">
          <path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/>
        </svg>
      </div>
    </button>
    <div class="fb16-variant-label">Orbit Ring</div>
  </div>

</div>

<!-- Live preview card -->
<div class="fb16-preview-card">
  <div class="fb16-preview-card-header">
    <div class="fb16-avatar"></div>
    <div class="fb16-card-meta">
      <h3>Design System Preview</h3>
      <p>See how your UI adapts to the active theme</p>
    </div>
  </div>
  <div class="fb16-preview-body">
    This card updates its background, text, borders, and accent colors instantly as you toggle. Every token is a CSS variable — a single <code style="font-family:monospace;font-size:.8em;background:var(--surface-2);padding:1px 5px;border-radius:3px;color:var(--accent)">data-theme</code> attribute on the root element switches the entire design.
  </div>
  <div class="fb16-preview-tags">
    <span class="fb16-tag">Light Mode</span>
    <span class="fb16-tag">Dark Mode</span>
    <span class="fb16-tag">CSS Tokens</span>
    <span class="fb16-tag">FAB Toggle</span>
  </div>
  <div class="fb16-theme-indicator">
    <div class="fb16-mode-dot"></div>
    <span id="fb16-modeLabel">Light Mode Active</span>
  </div>
</div>

<!-- Fixed FAB (actual page toggle) -->
<div class="fb16-fab-fixed-wrap">
  <button class="fb16-fab-fixed" id="fb16-fabFixed" data-theme-toggle aria-label="Toggle theme">
    <div class="fb16-fab-fixed-burst"></div>
    <svg class="fb16-icon-sun" viewBox="0 0 24 24">
      <circle cx="12" cy="12" r="5"/>
      <line x1="12" y1="1" x2="12" y2="3"/>
      <line x1="12" y1="21" x2="12" y2="23"/>
      <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/>
      <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/>
      <line x1="1" y1="12" x2="3" y2="12"/>
      <line x1="21" y1="12" x2="23" y2="12"/>
      <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/>
      <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>
    </svg>
    <svg class="fb16-icon-moon" viewBox="0 0 24 24">
      <path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/>
    </svg>
  </button>
</div>
</div>
.fb16, .fb16 *, .fb16 *::before, .fb16 *::after { box-sizing: border-box; margin: 0; padding: 0; }
/* ── Theme tokens ── */
:root {
  --bg:         #f5f7fa;
  --surface:    #ffffff;
  --surface-2:  #eef1f6;
  --text:       #1a202c;
  --text-2:     #718096;
  --border:     #e2e8f0;
  --shadow:     rgba(0,0,0,.08);
  --accent:     #4f46e5;
  --accent-2:   #a78bfa;

  --fab-bg:     #1a1a2e;
  --fab-icon:   #f6e05e;

  --toggle-dur: .5s;
}
[data-theme="dark"] {
  --bg:         #0d0d14;
  --surface:    #14141f;
  --surface-2:  #1e1e2e;
  --text:       #e2e8f0;
  --text-2:     #64748b;
  --border:     #1e293b;
  --shadow:     rgba(0,0,0,.35);
  --accent:     #a78bfa;
  --accent-2:   #4f46e5;

  --fab-bg:     #fef9c3;
  --fab-icon:   #1a1a2e;
}

.fb16 { transition: background var(--toggle-dur) ease; }

.fb16 {
  font-family: 'Plus Jakarta Sans', sans-serif;
  background: var(--bg);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 48px 24px 120px;
  gap: 32px;
  color: var(--text);
  transition: background var(--toggle-dur) ease, color var(--toggle-dur) ease;
}

h1 {
  font-size: 1.5rem;
  font-weight: 800;
  text-align: center;
  color: var(--text);
  transition: color var(--toggle-dur);
}
p.fb16-subtitle {
  font-size: 0.85rem;
  color: var(--text-2);
  text-align: center;
  margin-top: -24px;
  transition: color var(--toggle-dur);
}

/* ── Variants row ── */
.fb16-variants-row {
  display: flex;
  gap: 32px;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-end;
}
.fb16-variant-col { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.fb16-variant-label {
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--text-2);
  text-transform: uppercase;
  letter-spacing: .06em;
  transition: color var(--toggle-dur);
}

/* ── Toggle FAB shared ── */
.fb16-toggle-fab {
  position: relative;
  cursor: pointer;
  border: none;
  outline: none;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform .2s cubic-bezier(.34,1.56,.64,1), box-shadow .2s;
}
.fb16-toggle-fab:hover { transform: scale(1.1); }
.fb16-toggle-fab:active { transform: scale(.93); }

/* Style 1: Circle with animated sun/moon swap */
.fb16-fab-circle {
  width: 60px;
  height: 60px;
  border-radius: 50%;
  background: var(--fab-bg);
  box-shadow: 0 4px 18px var(--shadow);
  transition: background var(--toggle-dur), box-shadow .2s, transform .2s;
  overflow: hidden;
}
.fb16-fab-circle-inner {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}
.fb16-icon-sun, .fb16-icon-moon {
  position: absolute;
  width: 24px;
  height: 24px;
  stroke: var(--fab-icon);
  fill: none;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: opacity var(--toggle-dur), transform var(--toggle-dur), stroke var(--toggle-dur);
}
/* Light mode: show sun, hide moon */
.fb16-icon-sun  { opacity: 1; transform: rotate(0deg) scale(1); }
.fb16-icon-moon { opacity: 0; transform: rotate(90deg) scale(.6); }
[data-theme="dark"] .fb16-icon-sun  { opacity: 0; transform: rotate(-90deg) scale(.6); }
[data-theme="dark"] .fb16-icon-moon { opacity: 1; transform: rotate(0deg) scale(1); }

/* Style 2: Pill toggle (iOS-style slider) */
.fb16-fab-pill-toggle {
  width: 64px;
  height: 34px;
  border-radius: 999px;
  background: #e2e8f0;
  position: relative;
  padding: 0;
  transition: background var(--toggle-dur);
  box-shadow: inset 0 1px 3px rgba(0,0,0,.15);
}
[data-theme="dark"] .fb16-fab-pill-toggle { background: #7c3aed; }

.fb16-fab-pill-knob {
  position: absolute;
  top: 4px;
  left: 4px;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 2px 6px rgba(0,0,0,.2);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform var(--toggle-dur) cubic-bezier(.34,1.3,.64,1);
}
[data-theme="dark"] .fb16-fab-pill-knob { transform: translateX(30px); }

.fb16-fab-pill-knob svg {
  width: 14px; height: 14px;
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: stroke var(--toggle-dur), opacity var(--toggle-dur);
}
.fb16-knob-sun  { stroke: #f59e0b; }
.fb16-knob-moon { stroke: #7c3aed; position: absolute; opacity: 0; }
[data-theme="dark"] .fb16-knob-sun  { opacity: 0; }
[data-theme="dark"] .fb16-knob-moon { opacity: 1; }

/* Style 3: Square with morph icon */
.fb16-fab-square-toggle {
  width: 56px;
  height: 56px;
  border-radius: 16px;
  background: var(--surface);
  border: 1.5px solid var(--border);
  box-shadow: 0 2px 12px var(--shadow);
  transition: background var(--toggle-dur), border-color var(--toggle-dur), box-shadow .2s, transform .2s;
}
.fb16-fab-square-icon {
  font-size: 1.4rem;
  line-height: 1;
  display: block;
  transition: transform var(--toggle-dur), filter var(--toggle-dur);
}
[data-theme="dark"] .fb16-fab-square-icon { transform: rotate(360deg); }

/* Style 4: Gradient orbit ring */
.fb16-fab-orbit {
  width: 68px;
  height: 68px;
  border-radius: 50%;
  background: var(--surface);
  position: relative;
  box-shadow: 0 4px 18px var(--shadow);
  transition: background var(--toggle-dur), box-shadow .2s, transform .2s;
}
.fb16-fab-orbit-ring {
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  background: conic-gradient(from 0deg, #f59e0b, #a78bfa, #38bdf8, #f59e0b);
  z-index: -1;
  transition: opacity var(--toggle-dur);
  opacity: .7;
  animation: fb16-orbit-spin 4s linear infinite;
}
[data-theme="dark"] .fb16-fab-orbit-ring { opacity: 1; }
@keyframes fb16-orbit-spin { to { transform: rotate(360deg); } }

.fb16-fab-orbit-mask {
  position: absolute;
  inset: 2px;
  border-radius: 50%;
  background: var(--surface);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background var(--toggle-dur);
}
.fb16-orbit-sun, .fb16-orbit-moon {
  position: absolute;
  width: 26px; height: 26px;
  stroke: var(--text);
  fill: none;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: opacity var(--toggle-dur), transform var(--toggle-dur), stroke var(--toggle-dur);
}
.fb16-orbit-sun  { opacity: 1; transform: scale(1); }
.fb16-orbit-moon { opacity: 0; transform: scale(.5) rotate(45deg); }
[data-theme="dark"] .fb16-orbit-sun  { opacity: 0; transform: scale(.5) rotate(-45deg); }
[data-theme="dark"] .fb16-orbit-moon { opacity: 1; transform: scale(1) rotate(0deg); }

/* ── Live preview card ── */
.fb16-preview-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 24px;
  padding: 28px 32px;
  max-width: 500px;
  width: 100%;
  box-shadow: 0 4px 24px var(--shadow);
  transition: background var(--toggle-dur), border-color var(--toggle-dur), box-shadow var(--toggle-dur);
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.fb16-preview-card-header {
  display: flex;
  align-items: center;
  gap: 12px;
}
.fb16-avatar {
  width: 44px; height: 44px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  transition: background var(--toggle-dur);
}
.fb16-card-meta h3 {
  font-size: 0.92rem;
  font-weight: 700;
  color: var(--text);
  transition: color var(--toggle-dur);
}
.fb16-card-meta p {
  font-size: 0.75rem;
  color: var(--text-2);
  transition: color var(--toggle-dur);
}
.fb16-preview-body {
  font-size: 0.82rem;
  color: var(--text-2);
  line-height: 1.75;
  transition: color var(--toggle-dur);
}
.fb16-preview-tags {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.fb16-tag {
  padding: 4px 12px;
  background: var(--surface-2);
  border-radius: 999px;
  font-size: 0.73rem;
  font-weight: 600;
  color: var(--accent);
  transition: background var(--toggle-dur), color var(--toggle-dur);
}
.fb16-theme-indicator {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--text-2);
  transition: color var(--toggle-dur);
}
.fb16-mode-dot {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--accent);
  transition: background var(--toggle-dur);
}

/* ── Fixed FAB (bottom-right) ── */
.fb16-fab-fixed-wrap {
  position: fixed;
  bottom: 28px;
  right: 28px;
  z-index: 200;
}
.fb16-fab-fixed {
  width: 60px;
  height: 60px;
  border-radius: 50%;
  background: var(--fab-bg);
  border: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 6px 24px var(--shadow);
  transition: background var(--toggle-dur), transform .2s cubic-bezier(.34,1.56,.64,1), box-shadow .2s;
  outline: none;
  overflow: hidden;
}
.fb16-fab-fixed:hover { transform: scale(1.1); }
.fb16-fab-fixed:active { transform: scale(.93); }

.fb16-fab-fixed .fb16-icon-sun  { stroke: var(--fab-icon); }
.fb16-fab-fixed .fb16-icon-moon { stroke: var(--fab-icon); }

/* Burst overlay on toggle */
.fb16-fab-fixed-burst {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--fab-bg);
  transform: scale(1);
  opacity: 0;
  pointer-events: none;
}
.fb16-fab-fixed.fb16-bursting .fb16-fab-fixed-burst {
  animation: fb16-burst-out .5s ease-out forwards;
}
@keyframes fb16-burst-out {
  0%   { transform: scale(1);   opacity: .5; }
  100% { transform: scale(4);   opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  }
const root      = document.documentElement;
const squareIcon = document.getElementById('fb16-squareIcon');
const modeLabel  = document.getElementById('fb16-modeLabel');
const fabFixed   = document.getElementById('fb16-fabFixed');

function applyTheme(theme) {
  root.setAttribute('data-theme', theme);
  squareIcon.textContent = theme === 'dark' ? '🌙' : '☀️';
  modeLabel.textContent  = theme === 'dark' ? 'Dark Mode Active' : 'Light Mode Active';
  try { localStorage.setItem('fab-theme', theme); } catch(_) {}
}

function toggle() {
  const next = root.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
  applyTheme(next);
  // Burst on the fixed FAB
  fabFixed.classList.remove('fb16-bursting');
  requestAnimationFrame(() => fabFixed.classList.add('fb16-bursting'));
}

// All toggle buttons trigger the same theme change
document.querySelectorAll('[data-theme-toggle]').forEach(btn => {
  btn.addEventListener('click', toggle);
});

// Restore saved preference
try {
  const saved = localStorage.getItem('fab-theme');
  if (saved) applyTheme(saved);
} catch(_) {}

// Respect OS preference if no saved value
const osDark = window.matchMedia('(prefers-color-scheme: dark)');
try {
  if (!localStorage.getItem('fab-theme') && osDark.matches) applyTheme('dark');
} catch(_) {
  if (osDark.matches) applyTheme('dark');
}

Search CodeFronts

Loading…