Skip to content
Velocity X Docs
← All components

Toggle Switch Row

Free · MIT

Stacked list of labeled on/off switches for notification preferences, built on native checkboxes with zero JS.

Category
Forms
Framework
astro
Version
1.0.0
Peer deps
none

Install

bash
velocityx add toggle-switch-row

Free and MIT licensed — no account needed. The file lands under src/components/velocity/toggle-switch-row/ by default (override with --dest), and it's yours to edit from the moment it's written.

Demo

View the live demo on the showcase site ↗

Source

Pulled live from the registry — this is the exact file velocityx add toggle-switch-row writes into your project right now.

components/velocity/toggle-switch-row/ToggleSwitchRow.astro

astro
---
//    VELOCITY COMPONENT · theming
// 
//    Colours here are Tailwind `primary-*` / `accent-*`, which resolve through
//    the Velocity preset. Out of the box they are a neutral indigo — NOT the
//    Aidxn brand — so this drops into your project without dragging our look in.
// 
//    ⚡ NOT USING VELOCITY? Run this once, in your project root:
// 
//          velocityx theme
// 
//    It writes velocity-preset.cjs + velocity-theme.css. Add the preset to your
//    tailwind.config, import the css, then set your whole brand in ONE place:
// 
//          :root {
//            --vx-brand: custom;
//            --vx-primary-600: 220 38 38;   <- your action colour
//            --vx-primary-700: 185 28 28;
//          }
// 
//    ⚠️  Raw RGB channels, space separated — NOT hex. `#DC2626` here silently
//        breaks every /opacity variant in the library (bg-primary-600/20 etc).
// 
//    Prefer not to install anything? Find-and-replace `primary-` with your own
//    scale name. Nothing else in this file depends on Velocity.
/**
 * ToggleSwitchRow — a stacked list of labeled on/off switches (notification
 * preferences, feature flags). Native checkbox + `peer-checked` styling, no
 * JS. Each row is its own <label> so the whole row is clickable.
 */
export interface ToggleRow {
  name: string;
  label: string;
  description?: string;
  defaultChecked?: boolean;
  disabled?: boolean;
}
export interface Props {
  rows?: ToggleRow[];
  heading?: string;
  className?: string;
}

const {
  rows = [
    { name: 'email_updates', label: 'Product updates', description: 'New features and improvements', defaultChecked: true },
    { name: 'email_marketing', label: 'Marketing emails', description: 'Tips, offers, and promotions' },
    { name: 'email_security', label: 'Security alerts', description: 'Sign-ins and account changes', defaultChecked: true, disabled: true },
  ],
  heading = 'Email notifications',
  className = '',
} = Astro.props;
---

<div class:list={['rounded-2xl border border-primary-500/15 bg-white/95 dark:bg-primary-950/60 overflow-hidden', className]}>
  {heading && (
    <div class="px-5 py-4 border-b border-primary-500/10">
      <h3 class="font-bold text-primary-950 dark:text-white">{heading}</h3>
    </div>
  )}
  <div class="divide-y divide-primary-500/10">
    {rows.map((row) => {
      const id = `tsw-${row.name}`;
      return (
        <label for={id} class:list={['flex items-center justify-between gap-4 px-5 py-4', row.disabled ? 'opacity-60 cursor-not-allowed' : 'cursor-pointer']}>
          <span class="min-w-0">
            <span class="block text-sm font-semibold text-primary-950 dark:text-white">{row.label}</span>
            {row.description && <span class="block body-sm text-primary-950/55 dark:text-primary-200/55 mt-0.5">{row.description}</span>}
          </span>
          <span class="relative shrink-0 inline-flex h-6 w-11 items-center">
            <input
              id={id}
              type="checkbox"
              name={row.name}
              checked={row.defaultChecked}
              disabled={row.disabled}
              class="peer sr-only"
            />
            <span class="absolute inset-0 rounded-full bg-primary-200 dark:bg-primary-800 transition-colors peer-checked:bg-primary-500 peer-focus-visible:ring-4 peer-focus-visible:ring-primary-500/25"></span>
            <span class="absolute left-0.5 top-0.5 h-5 w-5 rounded-full bg-white shadow transition-transform peer-checked:translate-x-5 motion-reduce:transition-none"></span>
          </span>
        </label>
      );
    })}
  </div>
</div>

Theming

Uses the shared primary-* / accent-* scale — see Theming if colours look unstyled after install.