Inputs
Input Group
Поле с неизменяемой приставкой и окончанием внутри одной рамки: домен, протокол, единица измерения. Фокус подсвечивает группу целиком, а не только середину.
- input
- input group
- prefix
- form
Превью
Использовать с AI
- 1. Скопируйте ссылку.
- 2. Напишите агенту своими словами и вставьте её в предложение.
- 3. Агент откроет ссылку и поставит компонент из registry.
размести это в шапке: https://vibeui.ru/c/input-002
Показать полную инструкцию
То, что лежит по ссылке в развёрнутом виде. Нужна, если агент не может открыть ссылку — тогда вставьте этот текст целиком.
# Установка и размещение «input-002» (Input Group) из VibeUI
## 1. Сначала установка — не пропускай, не пересоздавай
Выполни эту команду до того, как писать код:
npx shadcn@latest add https://vibeui.ru/r/input-002.json
Registry item: https://vibeui.ru/r/input-002.json
Ставится в: components/vibeui/input-002.tsx (точный путь следует алиасам components.json этого проекта).
npm-зависимости: нет.
Registry-зависимости: нет.
Ставь из реестра. Не пересоздавай по описанию, не подменяй похожим
компонентом из другой библиотеки и не переписывай под стиль проекта.
## 2. Что это
Поле с неизменяемой приставкой и окончанием внутри одной рамки: домен, протокол, единица измерения. Фокус подсвечивает группу целиком, а не только середину.
Поле ввода с неизменяемыми частями слева и справа в общей рамке: протокол и домен, символ валюты, единица измерения, окончание файла. Приставка визуально отделена волосяной линией, фокус подсвечивает всю группу через :focus-within. Ноль зависимостей, один файл, своя палитра, клиентский JS не нужен.
## 3. Как использовать
import { Input002 } from "@/components/vibeui/input-002"
<Input002
id="slug"
name="slug"
label="Адрес страницы"
prefix="vibeui.ru/"
placeholder="portfolio"
/>
Полный список пропсов — в установленном файле.
## 4. Куда поставить
Это мелкий inline-компонент. Поставь его ровно туда, куда просил
пользователь, внутрь существующей разметки. Не создавай ради него
новую страницу, секцию или обёртку. Если на этом месте уже стоит похожий
контрол — замени его, а не добавляй второй.
Куда поставить: ___
(Эту строку заполняет пользователь. Если она осталась пустой — спроси,
а не угадывай.)
## 5. Сохранить как установлено
- локальную палитру --vibeui-input-002-* и не подменять её токенами вашей темы (bg-muted, border-input, text-muted-foreground и подобными)
- общую рамку: рамка живёт на группе, а у самого input её нет — иначе внутри рамки появится вторая
- подсветку группы через :focus-within вместе с кольцом box-shadow: без неё при фокусе загорается только середина
- разделители между фиксированной частью и полем (border-right у начала, border-left у конца) и их светлую подложку — по ним видно, что эту часть не редактируют
- overflow:hidden у группы: он обрезает подложку приставки по радиусу рамки
- user-select:none у фиксированных частей — их не выделяют и не копируют вместе со значением
- блок <style> внутри компонента — в нём вся палитра, геометрия и поведение фокуса
## 6. Можно менять
- содержимое prefix и suffix — это ReactNode, туда же можно положить свою иконку
- текст label и placeholder
- акцент через проп accent — цвет рамки и кольца при фокусе
- любые нативные пропсы input: type, name, value, onChange, inputMode, required, disabled
- ширину и внешние отступы через className — по умолчанию группа занимает всю ширину родителя
## 7. Правила
- Не кладите в prefix и suffix кнопки. Это неинтерактивные части рамки; кнопке рядом с полем нужен собственный компонент.
- Не дублируйте приставку в placeholder: пользователь увидит vibeui.ru дважды.
- id связывает подпись с полем. Без него клик по подписи не наводит фокус.
- Не выносите CSS-переменные в globals.css: компонент обязан оставаться одним файлом.
## 8. Проверка
- рендерится без ошибок в консоли;
- выглядит так же, как превью на странице VibeUI, откуда взята ссылка;
- если нет — ты изменил что-то из раздела 5, верни как было.Для разработчика
npx shadcn@latest add https://vibeui.ru/r/input-002.jsonhttps://vibeui.ru/r/input-002.jsonКомпонент самодостаточен: один файл, без зависимостей, собственная палитра в локальных переменных --vibeui-input-002-*. Приставка и окончание — обычные span'ы внутри рамки, поле растягивается между ними на flex. Подсветка группы держится на :focus-within, выключенное состояние — на :has(input:disabled), поэтому JS не нужен и компонент остаётся серверным. Внутри нативный <input>, принимающий любые его пропсы.
Исходник компонента
Тот же файл, который поставит агент. Нужен, если вы предпочитаете скопировать код руками.
import type { ComponentPropsWithoutRef, CSSProperties, ReactNode } from "react"
export type Input002Props = Omit<
ComponentPropsWithoutRef<"input">,
"size" | "prefix"
> & {
/** Неизменяемая часть слева: протокол, домен, символ валюты. */
prefix?: ReactNode
/** Неизменяемая часть справа: домен почты, единица измерения, расширение. */
suffix?: ReactNode
label?: string
accent?: string
}
// Идея компонента: приставка и окончание живут внутри той же рамки, что и
// поле, а фокус подсвечивает группу целиком. Пользователь видит будущее
// значение полностью и не набирает то, что и так известно.
const STYLES = `
:where([data-vibeui-block="input-002"]){
--vibeui-input-002-surface:oklch(1 0 0);
--vibeui-input-002-surface-border:oklch(0.91 0.006 265);
--vibeui-input-002-fg:oklch(0.24 0.016 265);
--vibeui-input-002-muted:oklch(0.54 0.014 265);
--vibeui-input-002-bg:oklch(1 0 0);
--vibeui-input-002-fixed:oklch(0.97 0.003 265);
--vibeui-input-002-border:oklch(0.87 0.008 265);
--vibeui-input-002-accent:oklch(0.55 0.2 262);
--vibeui-input-002-radius:0.625rem;
--vibeui-input-002-height:2.75rem;
--vibeui-input-002-font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
/* Собственная подложка: подпись поля — это текст, и на тёмной странице
он обязан читаться без правки палитры проекта. */
[data-vibeui-block="input-002"]{
box-sizing:border-box;padding:0.875rem;
background:var(--vibeui-input-002-surface);
border:1px solid var(--vibeui-input-002-surface-border);border-radius:0.875rem;
display:flex;flex-direction:column;gap:0.375rem;
font-family:var(--vibeui-input-002-font);color:var(--vibeui-input-002-fg);
}
[data-vibeui-block="input-002"] [data-part="label"]{
font-size:0.8125rem;font-weight:500;
}
[data-vibeui-block="input-002"] [data-part="group"]{
display:flex;align-items:stretch;overflow:hidden;
height:var(--vibeui-input-002-height);
border:1px solid var(--vibeui-input-002-border);
border-radius:var(--vibeui-input-002-radius);
background:var(--vibeui-input-002-bg);
transition:border-color .16s ease,box-shadow .16s ease;
}
[data-vibeui-block="input-002"] [data-part="group"]:focus-within{
border-color:var(--vibeui-input-002-accent);
box-shadow:0 0 0 3px color-mix(in oklab,var(--vibeui-input-002-accent) 22%,transparent);
}
[data-vibeui-block="input-002"] [data-part="fixed"]{
display:flex;align-items:center;flex:none;
padding:0 0.75rem;
background:var(--vibeui-input-002-fixed);
color:var(--vibeui-input-002-muted);
font-size:0.875rem;white-space:nowrap;user-select:none;
}
[data-vibeui-block="input-002"] [data-part="fixed"][data-side="start"]{border-right:1px solid var(--vibeui-input-002-border)}
[data-vibeui-block="input-002"] [data-part="fixed"][data-side="end"]{border-left:1px solid var(--vibeui-input-002-border)}
[data-vibeui-block="input-002"] input{
flex:1 1 auto;min-width:0;margin:0;border:0;outline:none;
padding:0 0.75rem;background:transparent;color:inherit;
font:inherit;font-size:0.9375rem;
}
[data-vibeui-block="input-002"] input::placeholder{color:color-mix(in oklab,var(--vibeui-input-002-muted) 70%,transparent)}
[data-vibeui-block="input-002"] input:disabled{cursor:not-allowed}
[data-vibeui-block="input-002"]:has(input:disabled) [data-part="group"]{opacity:.55}
@media (prefers-reduced-motion:reduce){[data-vibeui-block="input-002"] *{animation:none!important;transition:none!important}}
`
/**
* Поле с неизменяемой приставкой и окончанием в одной рамке.
* Один файл, ноль зависимостей, собственная палитра.
*/
export function Input002({
prefix = "vibeui.ru/",
suffix,
label = "Адрес страницы",
accent,
id,
className,
style,
...props
}: Input002Props) {
const palette = {
...(accent ? { "--vibeui-input-002-accent": accent } : null),
...style,
} as CSSProperties
return (
<>
<style href="vibeui-input-002" precedence="medium">
{STYLES}
</style>
<div data-vibeui-block="input-002" className={className} style={palette}>
{label ? (
<label data-part="label" htmlFor={id}>
{label}
</label>
) : null}
<div data-part="group">
{prefix ? (
<span data-part="fixed" data-side="start">
{prefix}
</span>
) : null}
<input {...props} id={id} />
{suffix ? (
<span data-part="fixed" data-side="end">
{suffix}
</span>
) : null}
</div>
</div>
</>
)
}