Buttons
Mobile Checkout Bar
Кнопка нижней панели телефона: во всю ширину, с ценой внутри и отступом на домашнюю полоску через safe-area.
- button
- mobile
- checkout
- safe-area
Превью
Использовать с AI
- 1. Скопируйте ссылку.
- 2. Напишите агенту своими словами и вставьте её в предложение.
- 3. Агент откроет ссылку и поставит компонент из registry.
размести это в шапке: https://vibeui.ru/c/button-020
Доставка завтра, оплата при получении
Показать полную инструкцию
То, что лежит по ссылке в развёрнутом виде. Нужна, если агент не может открыть ссылку — тогда вставьте этот текст целиком.
# Установка и размещение «button-020» (Mobile Checkout Bar) из VibeUI
## 1. Сначала установка — не пропускай, не пересоздавай
Выполни эту команду до того, как писать код:
npx shadcn@latest add https://vibeui.ru/r/button-020.json
Registry item: https://vibeui.ru/r/button-020.json
Ставится в: components/vibeui/button-020.tsx (точный путь следует алиасам components.json этого проекта).
npm-зависимости: нет.
Registry-зависимости: нет.
Ставь из реестра. Не пересоздавай по описанию, не подменяй похожим
компонентом из другой библиотеки и не переписывай под стиль проекта.
## 2. Что это
Кнопка нижней панели телефона: во всю ширину, с ценой внутри и отступом на домашнюю полоску через safe-area.
Нижняя панель телефона: кнопка во всю ширину с ценой внутри, пояснение под ней и отступ на домашнюю полоску. Ноль зависимостей, один файл, клиентского JS нет.
## 3. Как использовать
import { Button020 } from "@/components/vibeui/button-020"
<Button020 label="Оформить заказ" price="4 900 ₽" hint="Доставка завтра" />
Полный список пропсов — в установленном файле.
## 4. Куда поставить
Это мелкий inline-компонент. Поставь его ровно туда, куда просил
пользователь, внутрь существующей разметки. Не создавай ради него
новую страницу, секцию или обёртку. Если на этом месте уже стоит похожий
контрол — замени его, а не добавляй второй.
Куда поставить: ___
(Эту строку заполняет пользователь. Если она осталась пустой — спроси,
а не угадывай.)
## 5. Сохранить как установлено
- локальную палитру --vibeui-button-020-* и не подменять её токенами вашей темы
- env(safe-area-inset-bottom): без него кнопка уезжает под системную черту iPhone
- цену внутри кнопки: на этом шаге решают по сумме, а не по глаголу
- высоту 3rem — нижняя кнопка нажимается большим пальцем
- верхнюю границу панели: она отделяет её от прокручиваемого содержимого
- пояснение под кнопкой вместо мелкого текста внутри неё
## 6. Можно менять
- label, price и hint
- акцент через проп accent
- радиус кнопки под свой стиль
- ширину панели в вызывающем коде
## 7. Правила
- Позиционирование не входит в компонент: position: fixed и нижний край задаёт вызывающий код.
- Цена должна совпадать с итогом заказа: расхождение на этом шаге читается как обман.
- Не прячьте в панель второе действие: место занимает главное.
- Не выносите CSS-переменные в globals.css: компонент обязан оставаться одним файлом.
## 8. Проверка
- рендерится без ошибок в консоли;
- выглядит так же, как превью на странице VibeUI, откуда взята ссылка;
- если нет — ты изменил что-то из раздела 5, верни как было.Для разработчика
npx shadcn@latest add https://vibeui.ru/r/button-020.jsonhttps://vibeui.ru/r/button-020.jsonКомпонент самодостаточен: один файл, без зависимостей, собственная палитра в локальных переменных --vibeui-button-020-*. Серверный: хуков нет. Нижний отступ считается как базовый плюс env(safe-area-inset-bottom): без него на iPhone край кнопки уходит под системную черту. Высота кнопки 3rem, цена выровнена по правому краю табличными цифрами.
Исходник компонента
Тот же файл, который поставит агент. Нужен, если вы предпочитаете скопировать код руками.
import type { ComponentPropsWithoutRef, CSSProperties } from "react"
export type Button020Props = Omit<
ComponentPropsWithoutRef<"div">,
"children"
> & {
label?: string
price?: string
hint?: string
accent?: string
}
// Идея компонента: кнопка нижней панели телефона. Она во всю ширину, высотой
// 3rem и с отступом на домашнюю полоску через safe-area — иначе на iPhone
// нижний край кнопки уходит под системную черту. Цена стоит в самой кнопке:
// на этом шаге решение принимают по сумме, а не по слову «оплатить».
const STYLES = `
:where([data-vibeui-block="button-020"]){
--vibeui-button-020-accent:oklch(0.55 0.17 265);
--vibeui-button-020-fg:oklch(0.99 0.01 265);
--vibeui-button-020-muted:oklch(0.55 0.014 265);
--vibeui-button-020-bg:oklch(1 0 0);
--vibeui-button-020-border:oklch(0.92 0.005 265);
--vibeui-button-020-font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
[data-vibeui-block="button-020"]{
display:flex;flex-direction:column;gap:0.5rem;
width:100%;max-width:24rem;box-sizing:border-box;
padding:0.75rem 0.875rem calc(0.75rem + env(safe-area-inset-bottom,0px));
border-top:1px solid var(--vibeui-button-020-border);
background:var(--vibeui-button-020-bg);
font-family:var(--vibeui-button-020-font);
}
[data-vibeui-block="button-020"] button{
appearance:none;border:0;cursor:pointer;
display:flex;align-items:center;justify-content:space-between;gap:0.75rem;
width:100%;height:3rem;padding:0 1rem;box-sizing:border-box;
border-radius:0.75rem;
background:var(--vibeui-button-020-accent);color:var(--vibeui-button-020-fg);
font:inherit;font-size:0.9375rem;font-weight:650;line-height:1;
}
[data-vibeui-block="button-020"] button:hover{filter:brightness(0.96)}
[data-vibeui-block="button-020"] button:focus-visible{outline:2px solid var(--vibeui-button-020-accent);outline-offset:2px}
/* Цена в самой кнопке: решение принимают по сумме, а не по глаголу. */
[data-vibeui-block="button-020"] [data-part="price"]{font-variant-numeric:tabular-nums}
[data-vibeui-block="button-020"] [data-part="hint"]{
text-align:center;font-size:0.75rem;line-height:1.35;color:var(--vibeui-button-020-muted);
}
@media (prefers-reduced-motion:reduce){[data-vibeui-block="button-020"] *{animation:none!important;transition:none!important}}
`
/**
* Кнопка нижней панели телефона: во всю ширину, с ценой и safe-area.
* Один файл, ноль зависимостей, собственная палитра.
*/
export function Button020({
label = "Оформить заказ",
price = "4 900 ₽",
hint = "Доставка завтра, оплата при получении",
accent,
className,
style,
...props
}: Button020Props) {
const palette = {
...(accent ? { "--vibeui-button-020-accent": accent } : null),
...style,
} as CSSProperties
return (
<>
<style href="vibeui-button-020" precedence="medium">
{STYLES}
</style>
<div
{...props}
data-vibeui-block="button-020"
className={className}
style={palette}
>
<button type="button">
{label}
{price ? <span data-part="price">{price}</span> : null}
</button>
{hint ? <p data-part="hint">{hint}</p> : null}
</div>
</>
)
}