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
Use it with AI
- 1. Copy the link.
- 2. Write to your agent in your own words and drop the link into the sentence.
- 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.jsonhttps://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>
</>
)
}