Input
Нативное поле с плавающей подписью, аффиксами и одним вспомогательным действием. Метрики и визуал задаёт productScope.
Статус: stable · в системе с 2026-05 · интерактивность: always (всегда)
Когда использовать
- Однострочный свободный ввод: имя, email, URL, поисковый запрос.
- Значение с единицей или валютой — аффиксы
prefix/suffix. - Пароль —
type="password"с переключателем видимости.
Не использовать:
- Ответ длиннее одной строки.
- Значение выбирается из известного набора — дай выбор, а не свободный ввод.
- Дата — у неё свой формат и календарь.
- Значение участвует в арифметике — используй Input Number.
Альтернативы:
textarea— многострочный свободный текстselect— выбор одного значения из известного спискаinput-date— одна дата с календарёмinput-number— арифметическое число с min, max и step
Конструктор
Меняйте свойства Input, сочетайте их и проверяйте результат в одном живом экземпляре.
Базовый
Подпись плавает: в покое — на месте плейсхолдера, в фокусе/со значением — наверх.
<script>
import { t } from "$lib/spec/demo-lang.svelte.js";
import Input from "$lib/ui/Input.svelte";
import LockSimple from "phosphor-svelte/lib/LockSimple";
let email = $state("");
let password = $state("");
</script>
<div style="display: grid; gap: var(--sp-3); width: var(--control-field-min);">
<Input placeholder={t("Без подписи")} aria-label={t("Поле без видимой подписи")} />
<Input label="Email" bind:value={email} />
<Input label={t("Пароль")} type="password" bind:value={password}>
{#snippet leading()}<LockSimple aria-hidden="true" />{/snippet}
</Input>
</div>
Размеры
Канонические s / m; точные метрики задаёт productScope.
<script>
import Input from "$lib/ui/Input.svelte";
</script>
<div style="display: grid; gap: var(--sp-3); width: var(--control-field-min);">
<Input size="s" label="Small" />
<Input size="m" label="Medium" />
</div>
Иконки и очистка
Ведущая/хвостовая иконки и кнопка очистки (clearable).
<script>
import { t } from "$lib/spec/demo-lang.svelte.js";
import Input from "$lib/ui/Input.svelte";
import EnvelopeSimple from "phosphor-svelte/lib/EnvelopeSimple";
import MagnifyingGlass from "phosphor-svelte/lib/MagnifyingGlass";
let search = $state("");
</script>
<div style="display: grid; gap: var(--sp-3); width: var(--control-field-min);">
<Input label={t("Поиск")} bind:value={search} clearable>
{#snippet leading()}<MagnifyingGlass aria-hidden="true" />{/snippet}
</Input>
<Input label="Email">
{#snippet trailing()}<EnvelopeSimple aria-hidden="true" />{/snippet}
</Input>
</div>
Аффиксы, счётчик и выравнивание
Prefix/suffix живут внутри поля; счётчик — в подвале рядом с описанием или ошибкой.
<script>
import { t } from "$lib/spec/demo-lang.svelte.js";
import Input from "$lib/ui/Input.svelte";
let budget = $state("1200");
let title = $state("Value");
</script>
<div style="display: grid; gap: var(--sp-3); width: var(--control-field-min);">
<Input label={t("Бюджет")} bind:value={budget} prefix="$" suffix="₽" />
<Input label={t("Заголовок")} bind:value={title} characterCounter={10} description={t("До 10 символов.")} />
<Input label={t("По центру")} value="Value" textAlign="center" />
<Input label={t("Справа")} value="Value" textAlign="right" />
</div>
Поле
Подсказка, описание, ошибка, обязательность, только чтение.
<script>
import { t } from "$lib/spec/demo-lang.svelte.js";
import Input from "$lib/ui/Input.svelte";
let invalid = $state(t("неверно"));
</script>
<div style="display: grid; gap: var(--sp-3); width: var(--control-field-min);">
<Input label={t("С подсказкой")} hint={t("Короткое пояснение к полю")} />
<Input label={t("Обязательное")} required />
<Input label={t("С описанием")} description={t("Поясняющий текст под полем.")} />
<Input label="Email" bind:value={invalid} error={t("Введите адрес в формате name@domain")} />
<Input label={t("Только чтение")} readonly clearable value={t("Значение")} />
</div>
Изменено
changed: точка у правого верхнего угла помечает поле с несохранённым изменением — как fieldIndicator легаси.
<!--
🟢
Точка «изменено» (эталон: Vue-легаси blocks/fieldIndicator): поле с
несохранённым значением получает красную точку у правого верхнего угла.
-->
<script>
import { t } from "$lib/spec/demo-lang.svelte.js";
import Input from "$lib/ui/Input.svelte";
const saved = t("Кампания: лето");
let value = $state(t("Кампания: лето 2026"));
</script>
<div style="width: 320px;">
<Input label={t("Название")} bind:value changed={value !== saved} description={`${t("Сохранено: «")}${saved}${t("» — точка гаснет, когда значение совпадёт.")}`} />
</div>
Макросы и копирование
macros — метки под полем вставляют значение в позицию курсора; copy — кнопка копирования; left — аддон слева.
<!--
🟢
Макросы, копирование и аддон слева (эталон: Vue-легаси text-input
macroses / copy / слот left): клик по метке вставляет макрос в позицию
курсора, кнопка справа копирует значение, слева — кнопка протокола.
-->
<script>
import { t } from "$lib/spec/demo-lang.svelte.js";
import Button from "$lib/ui/Button.svelte";
import Input from "$lib/ui/Input.svelte";
let url = $state("track.example/click?id={click_id}");
const macros = [
{ label: "{click_id}", value: "{click_id}" },
{ label: "{campaign_id}", value: "{campaign_id}" },
{ label: "{source}", value: "{source}" },
];
</script>
<div style="width: 480px;">
<Input label={t("Ссылка постбэка")} bind:value={url} {macros} copy>
{#snippet left()}
<Button variant="default">https://</Button>
{/snippet}
</Input>
</div>