Inputs

Switch Tile

A switch tile: heading, explanation and a toggle in the corner, with the whole card recoloured when the option is on.

  • switch
  • card
  • settings
  • toggle

Preview

1440pxHost theme

Use it with AI

  1. 1. Copy the link.
  2. 2. Write to your agent in your own words and drop the link into the sentence.
  3. 3. The agent opens the link and installs the component from the registry.

put this in the header: https://vibeui.ru/c/switch-002?lang=en

Show the full instructions

What the link says, spelled out. Use it when your agent cannot open links — paste this text instead.

# Install and place "switch-002" (Switch Tile) from VibeUI

## 1. Install first — do not skip, do not recreate
Run this exact command before writing any code:
npx shadcn@latest add https://vibeui.ru/r/switch-002.json

Registry item: https://vibeui.ru/r/switch-002.json
Installs to: components/vibeui/switch-002.tsx (the exact path follows this project's components.json aliases).
npm dependencies: none.
Registry dependencies: none.

Install it from the registry. Do not recreate it from the description,
do not substitute a similar component from another library, and do not
rewrite it to match the project's existing style.

## 2. What it is
A switch tile: heading, explanation and a toggle in the corner, with the whole card recoloured when the option is on.

A tile switch with heading, description and a full-card tint when enabled. One file, zero dependencies, a server component.

## 3. How to use it
import { Switch002 } from "@/components/vibeui/switch-002"

<Switch002
  title="Smart reminders"
  description="We ping you an hour before the deadline."
  badge="On"
/>

Read the installed file for the full prop list.

## 4. Where to place it
This is a small inline component. Put it exactly where the user asked,
inside the existing markup. Do not create a new page, section or
wrapper for it. If a similar control already sits in that spot,
replace it instead of adding a second one.

Placement: ___
(The user fills this line in. If it is still blank, ask where to put it
instead of guessing.)

## 5. Keep exactly as installed
- the local --vibeui-switch-002-* palette — do not swap it for your theme tokens
- the native checkbox with role="switch": it brings keyboard, form support and state announcement for free
- the root <label>: the tap target is the whole tile, not a thumb the width of a finger
- the background and border driven by :has(input:checked) — across several tiles colour reads better than thumb position
- the focus ring on the tile via :has(input:focus-visible): the input itself is visually tiny
- the component's own light surface — without it the dark text disappears on a dark page

## 6. You may change
- the tile heading through title
- the explanation through description
- the badge caption through badge
- the accent colour through accent

## 7. Rules
- The badge only shows on an enabled tile: an empty badge removes it entirely.
- The tile stretches to its max-width — in a grid of cards the parent decides the width.
- The description is short on purpose: a long paragraph turns the tile into an article and breaks the grid rhythm.
- Do not lift the CSS variables into globals.css: the component has to stay a single file.

## 8. Verify
- it renders with no console errors;
- it looks like the preview on the VibeUI page you copied this from;
- if it does not, you changed something listed in section 5 — put it back.

For developers

npx shadcn@latest add https://vibeui.ru/r/switch-002.json
https://vibeui.ru/r/switch-002.json

Серверный компонент: состояние держит нативный checkbox с role="switch", JS не нужен. Корень — <label>, поэтому нажатие в любой точке плитки переключает опцию. Рамка, фон и бейдж «Включено» переключаются правилами :has(input:checked) на корне; фокус ловится через :has(input:focus-visible), потому что сам input визуально мал. Бегунок — псевдоузел рядом с input'ом, сдвиг задан transform. Палитра в --vibeui-switch-002-*.

Component source

The same file your agent installs. Here in case you would rather copy it by hand.

import type { ComponentPropsWithoutRef, CSSProperties } from "react"

export type Switch002Props = Omit<
  ComponentPropsWithoutRef<"input">,
  "type" | "size"
> & {
  title?: string
  description?: string
  /** Текст в углу, когда опция включена. */
  badge?: string
  accent?: string
}

// Идея компонента: не строка настройки, а плитка-выбор. Заголовок, абзац
// объяснения и переключатель в углу; вся карточка — цель нажатия, а
// включённое состояние меняет рамку и фон, поэтому среди нескольких плиток
// выбранные видно с расстояния, а не по положению маленького бегунка.
const STYLES = `
:where([data-vibeui-block="switch-002"]){
--vibeui-switch-002-bg:oklch(1 0 0);
--vibeui-switch-002-fg:oklch(0.22 0.014 265);
--vibeui-switch-002-muted:oklch(0.54 0.014 265);
--vibeui-switch-002-border:oklch(0.9 0.006 265);
--vibeui-switch-002-track:oklch(0.88 0.008 265);
--vibeui-switch-002-thumb:oklch(1 0 0);
--vibeui-switch-002-accent:oklch(0.56 0.16 155);
--vibeui-switch-002-tint:oklch(0.56 0.16 155 / 8%);
--vibeui-switch-002-font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
[data-vibeui-block="switch-002"]{
display:flex;flex-direction:column;gap:0.5rem;
width:100%;max-width:20rem;box-sizing:border-box;padding:1rem;
background:var(--vibeui-switch-002-bg);
border:1.5px solid var(--vibeui-switch-002-border);border-radius:1rem;
font-family:var(--vibeui-switch-002-font);color:var(--vibeui-switch-002-fg);
cursor:pointer;transition:border-color .18s ease,background-color .18s ease;
}
/* Включённая плитка окрашивается целиком: положение бегунка в списке
   из четырёх карточек читается хуже, чем цвет фона. */
[data-vibeui-block="switch-002"]:has(input:checked){
border-color:var(--vibeui-switch-002-accent);
background:var(--vibeui-switch-002-tint);
}
[data-vibeui-block="switch-002"]:has(input:focus-visible){
outline:2px solid var(--vibeui-switch-002-accent);outline-offset:2px;
}
[data-vibeui-block="switch-002"] [data-part="head"]{
display:flex;align-items:center;justify-content:space-between;gap:0.75rem;
}
[data-vibeui-block="switch-002"] [data-part="title"]{font-size:0.9375rem;font-weight:650;line-height:1.3}
[data-vibeui-block="switch-002"] [data-part="description"]{
font-size:0.8125rem;line-height:1.5;color:var(--vibeui-switch-002-muted);
}
[data-vibeui-block="switch-002"] [data-part="track"]{position:relative;display:flex;flex:none}
[data-vibeui-block="switch-002"] input{
appearance:none;-webkit-appearance:none;margin:0;
width:2.625rem;height:1.5rem;border-radius:9999px;
background:var(--vibeui-switch-002-track);cursor:inherit;
transition:background-color .18s ease;
}
[data-vibeui-block="switch-002"] input:checked{background:var(--vibeui-switch-002-accent)}
[data-vibeui-block="switch-002"] [data-part="thumb"]{
position:absolute;left:0.1875rem;top:0.1875rem;
width:1.125rem;height:1.125rem;border-radius:9999px;pointer-events:none;
background:var(--vibeui-switch-002-thumb);
box-shadow:0 1px 2px oklch(0.2 0.02 265 / 28%);
transition:transform .18s cubic-bezier(.32,.72,0,1);
}
[data-vibeui-block="switch-002"] input:checked + [data-part="thumb"]{transform:translateX(1.125rem)}
/* Бейдж появляется только у включённой плитки — через :has(), без JS. */
[data-vibeui-block="switch-002"] [data-part="badge"]{
display:none;align-self:flex-start;
padding:0.125rem 0.5rem;border-radius:9999px;
background:var(--vibeui-switch-002-accent);color:oklch(1 0 0);
font-size:0.6875rem;font-weight:650;letter-spacing:0.02em;
}
[data-vibeui-block="switch-002"]:has(input:checked) [data-part="badge"]{display:inline-block}
@media (prefers-reduced-motion:reduce){[data-vibeui-block="switch-002"] *{animation:none!important;transition:none!important}}
`

/**
 * Плитка-переключатель: заголовок, объяснение и цвет всей карточки.
 * Один файл, ноль зависимостей, собственная палитра.
 */
export function Switch002({
  title = "Умные напоминания",
  description = "Присылаем задачу за час до дедлайна и молчим по выходным.",
  badge = "Включено",
  accent,
  defaultChecked = true,
  className,
  style,
  ...props
}: Switch002Props) {
  const palette = {
    ...(accent ? { "--vibeui-switch-002-accent": accent } : null),
    ...style,
  } as CSSProperties

  return (
    <>
      <style href="vibeui-switch-002" precedence="medium">
        {STYLES}
      </style>
      <label
        data-vibeui-block="switch-002"
        className={className}
        style={palette}
      >
        <span data-part="head">
          <span data-part="title">{title}</span>
          <span data-part="track">
            <input
              {...props}
              type="checkbox"
              role="switch"
              defaultChecked={defaultChecked}
            />
            <span data-part="thumb" aria-hidden="true" />
          </span>
        </span>
        <span data-part="description">{description}</span>
        {badge ? <span data-part="badge">{badge}</span> : null}
      </label>
    </>
  )
}