Inputs

Locked Switch

A locked switch that explains itself: a padlock beside the caption, the reason in words and a live link to lifting the limit.

  • switch
  • disabled
  • upgrade
  • state

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-008?lang=en

Available on the Team plan: on the current one export is manual only. Change plan

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-008" (Locked Switch) 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-008.json

Registry item: https://vibeui.ru/r/switch-008.json
Installs to: components/vibeui/switch-008.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 locked switch that explains itself: a padlock beside the caption, the reason in words and a live link to lifting the limit.

A disabled setting with the reason spelled out and a link that lifts it. One file, zero dependencies, a server component.

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

<Switch008
  label="Cloud export"
  reason="Available on the Team plan."
  actionText="Change plan"
  actionHref="/pricing"
/>

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-008-* palette — do not swap it for your theme tokens
- the reason in words next to the lock: a grey toggle with no text reads as a bug
- the live link inside the explanation — it is the only way out of the locked state and must not be disabled
- disabled on the input itself: a merely visual lock still submits a value with the form
- the dashed track and the padlock: the state has to read without colour too
- the component's own light surface — without it the dark text disappears on a dark page

## 6. You may change
- the setting caption through label
- the reason text through reason
- the link caption through actionText and its address through actionHref
- the accent colour through accent

## 7. Rules
- A disabled input takes no focus — so the keyboard path leads straight to the link in the explanation.
- The reason should name the condition, not the refusal: "available on plan X" beats "not available".
- The not-allowed cursor is on the row, not just the thumb: people hover the caption.
- 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-008.json
https://vibeui.ru/r/switch-008.json

Серверный компонент. Переключатель выключен через disabled, дорожка нарисована пунктиром — состояние читается и без цвета. Замок собран из прямоугольника корпуса и дужки на псевдоэлементе, без иконочного пакета. Ссылка намеренно не заблокирована и получает собственное кольцо фокуса: это единственный выход из тупика. Причина лежит в отдельном абзаце с приглушённым фоном. Палитра в --vibeui-switch-008-*.

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 Switch008Props = Omit<
  ComponentPropsWithoutRef<"input">,
  "type" | "size" | "disabled"
> & {
  label?: string
  /** Почему настройку нельзя включить: без этой строки блокировка — грубость. */
  reason?: string
  actionText?: string
  actionHref?: string
  accent?: string
}

// Идея компонента: выключенная настройка, которая объясняет себя. Серый
// тумблер без текста читается как поломка; здесь рядом стоит замок,
// причина написана словами, а ссылка ведёт туда, где ограничение снимается.
// Ссылка не заблокирована — иначе выход из тупика тоже был бы недоступен.
const STYLES = `
:where([data-vibeui-block="switch-008"]){
--vibeui-switch-008-bg:oklch(1 0 0);
--vibeui-switch-008-fg:oklch(0.22 0.014 265);
--vibeui-switch-008-muted:oklch(0.55 0.014 265);
--vibeui-switch-008-border:oklch(0.91 0.006 265);
--vibeui-switch-008-track:oklch(0.9 0.006 265);
--vibeui-switch-008-thumb:oklch(0.97 0.002 265);
--vibeui-switch-008-accent:oklch(0.55 0.19 262);
--vibeui-switch-008-lock:oklch(0.66 0.13 75);
--vibeui-switch-008-lock-tint:oklch(0.66 0.13 75 / 12%);
--vibeui-switch-008-font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
[data-vibeui-block="switch-008"]{
display:flex;flex-direction:column;gap:0.625rem;
width:100%;max-width:21rem;box-sizing:border-box;padding:0.875rem;
background:var(--vibeui-switch-008-bg);
border:1px solid var(--vibeui-switch-008-border);border-radius:0.875rem;
font-family:var(--vibeui-switch-008-font);color:var(--vibeui-switch-008-fg);
}
[data-vibeui-block="switch-008"] [data-part="row"]{
display:flex;align-items:center;gap:0.875rem;cursor:not-allowed;
}
[data-vibeui-block="switch-008"] [data-part="label"]{
display:flex;align-items:center;gap:0.375rem;flex:1 1 auto;min-width:0;
font-size:0.9375rem;font-weight:600;color:var(--vibeui-switch-008-muted);
}
/* Замок нарисован дужкой на псевдоэлементе поверх корпуса: значок
   блокировки без иконочного пакета. */
[data-vibeui-block="switch-008"] [data-part="lock"]{
position:relative;flex:none;width:0.75rem;height:0.625rem;margin-top:0.1875rem;
border-radius:0.125rem;background:var(--vibeui-switch-008-lock);
}
[data-vibeui-block="switch-008"] [data-part="lock"]::before{
content:"";position:absolute;left:50%;bottom:0.5rem;
width:0.5rem;height:0.4375rem;margin-left:-0.25rem;
border:1.5px solid var(--vibeui-switch-008-lock);border-bottom:0;
border-radius:0.25rem 0.25rem 0 0;
}
[data-vibeui-block="switch-008"] [data-part="track"]{position:relative;display:flex;flex:none;opacity:.6}
[data-vibeui-block="switch-008"] input{
appearance:none;-webkit-appearance:none;margin:0;
width:2.75rem;height:1.5rem;border-radius:9999px;
background:var(--vibeui-switch-008-track);cursor:not-allowed;
border:1px dashed color-mix(in oklab,var(--vibeui-switch-008-muted) 40%,transparent);
box-sizing:border-box;
}
[data-vibeui-block="switch-008"] [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-008-thumb);
box-shadow:0 1px 2px oklch(0.2 0.02 265 / 18%);
}
[data-vibeui-block="switch-008"] [data-part="reason"]{
display:flex;gap:0.5rem;margin:0;padding:0.5rem 0.625rem;
border-radius:0.5rem;background:var(--vibeui-switch-008-lock-tint);
font-size:0.75rem;line-height:1.45;color:var(--vibeui-switch-008-muted);
}
/* Ссылка остаётся живой и получает свой фокус: она — единственный выход
   из заблокированного состояния. */
[data-vibeui-block="switch-008"] a{
color:var(--vibeui-switch-008-accent);font-weight:600;
text-decoration:underline;text-underline-offset:2px;border-radius:0.25rem;
}
[data-vibeui-block="switch-008"] a:focus-visible{outline:2px solid var(--vibeui-switch-008-accent);outline-offset:2px}
`

/**
 * Заблокированный переключатель: причина словами и ссылка на выход.
 * Один файл, ноль зависимостей, собственная палитра.
 */
export function Switch008({
  label = "Экспорт в облако",
  reason = "Доступно на тарифе «Команда»: на текущем плане выгрузка идёт только вручную.",
  actionText = "Сменить тариф",
  actionHref = "#pricing",
  accent,
  className,
  style,
  ...props
}: Switch008Props) {
  const palette = {
    ...(accent ? { "--vibeui-switch-008-accent": accent } : null),
    ...style,
  } as CSSProperties

  return (
    <>
      <style href="vibeui-switch-008" precedence="medium">
        {STYLES}
      </style>
      <div data-vibeui-block="switch-008" className={className} style={palette}>
        <label data-part="row">
          <span data-part="label">
            <span data-part="lock" aria-hidden="true" />
            {label}
          </span>
          <span data-part="track">
            <input {...props} type="checkbox" role="switch" disabled />
            <span data-part="thumb" aria-hidden="true" />
          </span>
        </label>
        <p data-part="reason">
          <span>
            {reason} <a href={actionHref}>{actionText}</a>
          </span>
        </p>
      </div>
    </>
  )
}