Skip to content

@vibe-labs/design-components-toggles

Toggle/switch component tokens, styles, and TypeScript types for the Vibe Design System.

Installation

css
@import "@vibe-labs/design-components-toggles";
ts
import { ToggleSizes } from "@vibe-labs/design-components-toggles/types";
import type { ToggleSize, ToggleStyleProps } from "@vibe-labs/design-components-toggles/types";

Contents

Tokens (toggle.css)

CSS custom properties defined in @layer vibe.tokens:

Track

Tokensmmdlg
--toggle-width-{size}2rem2.5rem3rem
--toggle-height-{size}1.125rem1.375rem1.625rem
TokenDefaultDescription
--toggle-radius--radius-fullTrack border radius
--toggle-bg--surface-overlayUnchecked background
--toggle-bg-checked--color-accentChecked background
--toggle-border-width--border-width-1Border width
--toggle-border-color--border-defaultUnchecked border
--toggle-border-color-checked--color-accentChecked border

Thumb

Tokensmmdlg
--toggle-thumb-size-{size}0.875rem1.125rem1.375rem
TokenDefaultDescription
--toggle-thumb-color--color-neutral-0Thumb color (white)
--toggle-thumb-shadow--shadow-xsThumb elevation
--toggle-thumb-inset2pxGap between thumb and track edge

Generated Styles (toggle.g.css)

Component classes generated into @layer vibe.components.

Elements

  • toggle — inline-flex track with hidden native input, transition on background/border
  • toggle-thumb — circular knob with shadow, slides via translateX on checked
  • toggle-field — inline-flex wrapper for toggle + label text

Sizes

sm · md · lg (default: md) — thumb travel distance auto-calculated per size from width - thumb - inset - border*2

States

  • Checked — accent background/border, thumb slides to end position (via :has(input:checked))
  • Disabled — reduced opacity, not-allowed cursor
  • Focus visible — ring via box-shadow

TypeScript Types (types/)

ts
ToggleSizes // ["sm", "md", "lg"]
type ToggleSize

interface ToggleStyleProps { size?: ToggleSize; disabled?: boolean }
interface ToggleThumbStyleProps {}
interface ToggleFieldStyleProps {}

Dist Structure

FileDescription
index.cssBarrel — imports tokens + generated styles
toggle.cssToken definitions
toggle.g.cssGenerated component styles
index.js / index.d.tsTypeScript types + runtime constants

Dependencies

Requires tokens from @vibe-labs/design (colors, surfaces, borders, spacing, transitions, elevation).

Build

bash
npm run build

Usage Guide

Import

css
@import "@vibe-labs/design-components-toggles";
ts
import { ToggleSizes } from "@vibe-labs/design-components-toggles/types";
import type { ToggleStyleProps } from "@vibe-labs/design-components-toggles/types";

Variants

Attribute / FlagValuesNotes
data-sizesm md lgDefault: md
data-disabledboolean flagReduced opacity, not-allowed cursor

Checked state is driven by input:checked inside .toggle (uses :has()). No separate data attribute needed.

Examples

Basic toggle

html
<label class="toggle">
  <input type="checkbox" />
  <div class="toggle-thumb"></div>
</label>

Pre-checked toggle

html
<label class="toggle">
  <input type="checkbox" checked />
  <div class="toggle-thumb"></div>
</label>

Sizes

html
<label class="toggle" data-size="sm">
  <input type="checkbox" />
  <div class="toggle-thumb"></div>
</label>

<label class="toggle" data-size="md">
  <input type="checkbox" checked />
  <div class="toggle-thumb"></div>
</label>

<label class="toggle" data-size="lg">
  <input type="checkbox" />
  <div class="toggle-thumb"></div>
</label>

Disabled toggle

html
<label class="toggle" data-disabled>
  <input type="checkbox" disabled />
  <div class="toggle-thumb"></div>
</label>

<label class="toggle" data-disabled>
  <input type="checkbox" checked disabled />
  <div class="toggle-thumb"></div>
</label>

Toggle with label text (toggle-field)

html
<label class="toggle-field">
  <span>Enable notifications</span>
  <span class="toggle">
    <input type="checkbox" />
    <div class="toggle-thumb"></div>
  </span>
</label>

Toggle field with description

html
<div style="display: flex; flex-direction: column; gap: 4px;">
  <label class="toggle-field">
    <div>
      <div>Dark mode</div>
      <div style="font-size: var(--text-sm); color: var(--text-muted);">Switch to dark theme</div>
    </div>
    <span class="toggle">
      <input type="checkbox" />
      <div class="toggle-thumb"></div>
    </span>
  </label>
</div>

With Vue

vue
<template>
  <label class="toggle-field">
    <span>{{ label }}</span>
    <span class="toggle" :data-size="size" :data-disabled="disabled || undefined">
      <input
        type="checkbox"
        :checked="modelValue"
        :disabled="disabled"
        @change="$emit('update:modelValue', ($event.target as HTMLInputElement).checked)"
      />
      <div class="toggle-thumb"></div>
    </span>
  </label>
</template>

<script setup lang="ts">
import type { ToggleSize } from "@vibe-labs/design-components-toggles/types";
defineProps<{ modelValue: boolean; label?: string; size?: ToggleSize; disabled?: boolean }>();
defineEmits<{ 'update:modelValue': [value: boolean] }>();
</script>

Vibe