fix(buttons): переделаны кнопки под реальный сайт oclinica.ru
Анализ CSS сайта (style.css темы clinic_bootstrap_mobile) выявил 4 реальных типа кнопок — заменены ранее придуманные варианты: - primary → коралловый #FFA39C + shadow (форм-сабмит «Запишите меня!») - outline → белый + бежевая рамка #BF9975 («Записаться на прием») - teal → бирюзовый #60959c («Позвонить») - pill → кремовый #e9e4d4 + radius 25px («Заказать звонок») Удалены: secondary, ghost, danger (не существуют на реальном сайте) Добавлен раздел «CSS с сайта» с точными значениями Добавлена таблица «Где применяется» с реальными CSS-классами сайта LLM-блок обновлён до v2.0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -35,114 +35,184 @@ function Section({
|
||||
);
|
||||
}
|
||||
|
||||
const VARIANT_INFO = [
|
||||
const VARIANTS = [
|
||||
{
|
||||
variant: "primary" as const,
|
||||
label: "Primary",
|
||||
name: "Primary",
|
||||
label: "Запишитесь к нам",
|
||||
cssClass: ".bb-btn-primary",
|
||||
bg: "#5b7b87",
|
||||
text: "#fff",
|
||||
description: "Основная кнопка призыва к действию. Тёмный бирюзово-серый фон, белый текст.",
|
||||
useCase: "Записаться · Подтвердить",
|
||||
bg: "#FFA39C",
|
||||
border: "#FF847B",
|
||||
textColor: "#fff",
|
||||
radius: "7px",
|
||||
shadow: "да",
|
||||
where: "Кнопка отправки форм записи",
|
||||
example: "«Запишите меня!»",
|
||||
note: "Коралловый — самый заметный акцент на странице. Всегда один в форме.",
|
||||
},
|
||||
{
|
||||
variant: "secondary" as const,
|
||||
label: "Secondary",
|
||||
cssClass: ".bb-btn-secondary",
|
||||
bg: "прозрачный",
|
||||
text: "#5b7b87",
|
||||
description: "Второстепенное действие. Контурная кнопка с фирменным цветом.",
|
||||
useCase: "Узнать подробнее · Редактировать",
|
||||
variant: "outline" as const,
|
||||
name: "Outline",
|
||||
label: "Записаться на приём",
|
||||
cssClass: ".bb-btn-outline",
|
||||
bg: "#fff",
|
||||
border: "#BF9975",
|
||||
textColor: "#BF9975",
|
||||
radius: "7px",
|
||||
shadow: "нет",
|
||||
where: "Хедер, навигация, ссылки-кнопки",
|
||||
example: "«Записаться на прием», «Все новости»",
|
||||
note: "Бежевая рамка — ненавязчивый вторичный CTA. Не конкурирует с основной формой.",
|
||||
},
|
||||
{
|
||||
variant: "ghost" as const,
|
||||
label: "Ghost",
|
||||
cssClass: ".bb-btn-ghost",
|
||||
bg: "прозрачный",
|
||||
text: "#5b7b87",
|
||||
description: "Третичное действие. Без фона и видимой рамки, текстовый акцент.",
|
||||
useCase: "Отмена · Назад · Ещё...",
|
||||
variant: "teal" as const,
|
||||
name: "Teal",
|
||||
label: "Позвонить",
|
||||
cssClass: ".bb-btn-teal",
|
||||
bg: "#60959c",
|
||||
border: "прозрачный",
|
||||
textColor: "#fff",
|
||||
radius: "7px",
|
||||
shadow: "нет",
|
||||
where: "Контактные действия — звонок",
|
||||
example: "«Позвонить»",
|
||||
note: "Серо-бирюзовый — цвет из реального CSS сайта. Близок к Oracal 066M.",
|
||||
},
|
||||
{
|
||||
variant: "danger" as const,
|
||||
label: "Danger",
|
||||
cssClass: ".bb-btn-danger",
|
||||
bg: "#dc2626",
|
||||
text: "#fff",
|
||||
description: "Деструктивные и необратимые действия. Красный фон.",
|
||||
useCase: "Удалить · Отменить запись",
|
||||
variant: "pill" as const,
|
||||
name: "Pill",
|
||||
label: "Заказать звонок",
|
||||
cssClass: ".bb-btn-pill",
|
||||
bg: "#e9e4d4",
|
||||
border: "#d5cfbd",
|
||||
textColor: "#333",
|
||||
radius: "25px",
|
||||
shadow: "нет",
|
||||
where: "Модальные триггеры, мягкий CTA",
|
||||
example: "«Заказать звонок»",
|
||||
note: "Кремовый фон + pill-форма — мягкий стиль. Используется для открытия модальных окон.",
|
||||
},
|
||||
];
|
||||
|
||||
const LLM_BUTTONS_TEXT = `КНОПКИ — LLM-спецификация
|
||||
Версия: v1.0 · /components/buttons
|
||||
const LLM_BUTTONS_TEXT = `КНОПКИ — LLM-спецификация (с реального сайта oclinica.ru)
|
||||
Версия: v2.0 · /components/buttons
|
||||
Источник CSS: perm.oclinica.ru/.../style.css
|
||||
|
||||
ВАРИАНТЫ
|
||||
Вариант | CSS класс | Фон | Текст | Граница | Применение
|
||||
primary | .bb-btn-primary | #5b7b87 | #fff | #5b7b87 | Главный CTA: «Записаться», «Подтвердить»
|
||||
secondary | .bb-btn-secondary | прозрачный | #5b7b87 | #5b7b87 | Второстепенное: «Подробнее», «Редактировать»
|
||||
ghost | .bb-btn-ghost | прозрачный | #5b7b87 | нет | Третичное: «Отмена», «Назад»
|
||||
danger | .bb-btn-danger | #dc2626 | #fff | #dc2626 | Деструктивное: «Удалить», «Отменить запись»
|
||||
ВАРИАНТЫ (реальный сайт)
|
||||
Вариант | CSS класс | Фон | Текст | Граница | Radius | Shadow | Применение
|
||||
primary | .bb-btn-primary | #FFA39C | #fff | #FF847B | 7px | да | Форм-сабмит «Запишите меня!»
|
||||
outline | .bb-btn-outline | #fff | #BF9975 | #BF9975 | 7px | нет | Хедер «Записаться на прием», ссылки-кнопки
|
||||
teal | .bb-btn-teal | #60959c | #fff | нет | 7px | нет | Звонок «Позвонить»
|
||||
pill | .bb-btn-pill | #e9e4d4 | #333 | #d5cfbd | 25px | нет | Callback «Заказать звонок»
|
||||
|
||||
РАЗМЕРЫ
|
||||
Размер | CSS класс | padding | font-size | border-radius | Применение
|
||||
sm | .bb-btn-sm | 5px 12px | 13px | 6px | Компактные интерфейсы, таблицы
|
||||
md | .bb-btn-md | 8px 18px | 14px | 8px | Стандарт (по умолчанию)
|
||||
lg | .bb-btn-lg | 12px 26px | 16px | 10px | Главные CTA на Hero-блоках
|
||||
CSS С САЙТА (точные значения)
|
||||
/* форм-кнопка «Запишите меня!» */
|
||||
button { background:#FFA39C; color:white; font-weight:bold; border:solid 1px #FF847B;
|
||||
height:42px; font-size:18px; box-shadow:0px 0px 5px rgba(0,0,0,0.5),0px 4px 5px rgba(0,0,0,0.3); }
|
||||
|
||||
СОСТОЯНИЯ
|
||||
default — без изменений
|
||||
hover — filter: brightness(0.9)
|
||||
active — filter: brightness(0.82)
|
||||
loading — spinner (animation: bb-spin 0.65s linear infinite) + opacity: 0.5 + disabled
|
||||
disabled — opacity: 0.5, cursor: not-allowed
|
||||
/* appointment — «Записаться на прием» */
|
||||
.appointment { background:#FFF; border:#BF9975 solid 1px; color:#BF9975;
|
||||
font-size:14px; line-height:38px; padding:3px 12px; border-radius:7px; }
|
||||
|
||||
CSS BASE (globals.css)
|
||||
.bb-btn { font-family: Fira Sans; font-weight: 500; display: inline-flex; align-items: center; gap: 7px; transition: filter 0.15s; }
|
||||
.bb-btn:hover:not(:disabled) { filter: brightness(0.9); }
|
||||
.bb-btn:active:not(:disabled) { filter: brightness(0.82); }
|
||||
.bb-btn:disabled { cursor: not-allowed; opacity: 0.5; }
|
||||
.bb-btn:focus-visible { outline: 2px solid #7ecfca; outline-offset: 2px; }
|
||||
/* show-phone — «Позвонить» */
|
||||
.show-phone { background:rgb(96,149,156); color:#fff; border-radius:7px;
|
||||
font-size:14px; line-height:38px; padding:3px 12px; }
|
||||
|
||||
/* callback — «Заказать звонок» */
|
||||
a.callback_url { background:#e9e4d4; border:#d5cfbd solid 1px; color:#000;
|
||||
border-radius:25px; font-size:16px; padding:6px 18px; }
|
||||
|
||||
РАЗМЕРЫ (брендбук-компонент)
|
||||
Размер | CSS класс | padding | font-size | Применение
|
||||
sm | .bb-btn-sm | 4px 11px | 13px | Компактные контексты
|
||||
md | .bb-btn-md | 8px 16px | 14px | Стандарт (appointment, teal, pill)
|
||||
lg | .bb-btn-lg | 10px 24px | 18px + bold | Форм-сабмит (соответствует реальному сайту)
|
||||
|
||||
ПРАВИЛА ПРИМЕНЕНИЯ
|
||||
✓ Не более одной primary-кнопки на видимый экран в контексте одной задачи
|
||||
✓ Текст кнопки — глагол или чёткий призыв: «Записаться», «Узнать цену»
|
||||
✓ Primary → главное действие (форма записи, подтверждение)
|
||||
✓ Secondary → второстепенное (подробнее, редактировать)
|
||||
✓ Ghost → отмена, навигационная ссылка без акцента
|
||||
✓ Danger → только деструктивные действия (удалить, отменить запись)
|
||||
✕ Не менять цвета произвольно — только варианты из фирменной палитры
|
||||
✕ Не добавлять тени к кнопкам
|
||||
✕ Не использовать Danger для нейтральных действий`.trim();
|
||||
✓ primary (коралловый) — только для главного CTA в форме записи
|
||||
✓ outline (бежевый) — хедер, навигация, ссылки-кнопки на странице
|
||||
✓ teal (бирюзовый) — контактные действия (звонок, направление)
|
||||
✓ pill (кремовый) — открытие модальных окон, мягкий callback
|
||||
✓ Не более одного primary на форму
|
||||
✕ Не менять цвета вне фирменной палитры сайта
|
||||
✕ Primary — не для навигационных ссылок
|
||||
✕ Не накладывать тень на outline/teal/pill`.trim();
|
||||
|
||||
export default function ButtonsPage() {
|
||||
const codeHtml = `<!-- HTML — базовые классы из globals.css -->
|
||||
<button class="bb-btn bb-btn-md bb-btn-primary">Записаться</button>
|
||||
<button class="bb-btn bb-btn-md bb-btn-secondary">Узнать подробнее</button>
|
||||
<button class="bb-btn bb-btn-md bb-btn-ghost">Отмена</button>
|
||||
<button class="bb-btn bb-btn-md bb-btn-danger">Удалить</button>
|
||||
const codeHtml = `<!-- Primary — форм-кнопка «Запишите меня!» -->
|
||||
<button class="bb-btn bb-btn-lg bb-btn-primary">Запишите меня!</button>
|
||||
|
||||
<!-- Размеры -->
|
||||
<button class="bb-btn bb-btn-sm bb-btn-primary">Маленькая</button>
|
||||
<button class="bb-btn bb-btn-md bb-btn-primary">Средняя</button>
|
||||
<button class="bb-btn bb-btn-lg bb-btn-primary">Большая</button>`;
|
||||
<!-- Outline — appointment «Записаться на прием» -->
|
||||
<a class="bb-btn bb-btn-md bb-btn-outline" href="#form">Записаться на прием</a>
|
||||
|
||||
<!-- Teal — «Позвонить» -->
|
||||
<a class="bb-btn bb-btn-md bb-btn-teal" href="tel:+73422250662">Позвонить</a>
|
||||
|
||||
<!-- Pill — «Заказать звонок» -->
|
||||
<a class="bb-btn bb-btn-md bb-btn-pill" href="#callback">Заказать звонок</a>`;
|
||||
|
||||
const codeReact = `import { Button } from "@/components/ui/Button";
|
||||
|
||||
// Варианты
|
||||
<Button variant="primary">Записаться</Button>
|
||||
<Button variant="secondary">Узнать подробнее</Button>
|
||||
<Button variant="ghost">Отмена</Button>
|
||||
<Button variant="danger">Удалить</Button>
|
||||
// Форм-кнопка (главный CTA)
|
||||
<Button variant="primary" size="lg">Запишите меня!</Button>
|
||||
|
||||
// Размеры
|
||||
<Button size="sm">Маленькая</Button>
|
||||
<Button size="md">Средняя</Button> {/* по умолчанию */}
|
||||
<Button size="lg">Большая</Button>
|
||||
// Запись из хедера / навигации
|
||||
<Button variant="outline" size="md">Записаться на прием</Button>
|
||||
|
||||
// Состояния
|
||||
<Button loading>Загрузка...</Button>
|
||||
<Button disabled>Недоступно</Button>`;
|
||||
// Звонок
|
||||
<Button variant="teal" size="md">Позвонить</Button>
|
||||
|
||||
// Заказать звонок (открывает модал)
|
||||
<Button variant="pill" size="md">Заказать звонок</Button>
|
||||
|
||||
// С loading-состоянием
|
||||
<Button variant="primary" size="lg" loading>Отправляем...</Button>`;
|
||||
|
||||
const codeSiteExact = `/* Точный CSS с сайта oclinica.ru (style.css) */
|
||||
|
||||
/* Форм-кнопка — кнопка отправки форм записи */
|
||||
#block-entityform-block-feedback button,
|
||||
#block-entityform-block-lor-form button {
|
||||
background: #FFA39C;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
border: solid 1px #FF847B;
|
||||
width: 300px;
|
||||
height: 42px;
|
||||
font-size: 18px;
|
||||
box-shadow: 0px 0px 5px rgba(0,0,0,0.5), 0px 4px 5px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
/* Кнопка «Записаться на прием» в хедере */
|
||||
#block-block-15 .appointment {
|
||||
background: #FFF;
|
||||
border: #BF9975 solid 1px;
|
||||
color: #BF9975;
|
||||
font-size: 14px;
|
||||
line-height: 38px;
|
||||
padding: 3px 12px;
|
||||
border-radius: 7px;
|
||||
}
|
||||
|
||||
/* Кнопка «Позвонить» */
|
||||
.show-phone {
|
||||
background: rgb(96, 149, 156); /* #60959c */
|
||||
color: #fff;
|
||||
border-radius: 7px;
|
||||
font-size: 14px;
|
||||
line-height: 38px;
|
||||
padding: 3px 12px;
|
||||
}
|
||||
|
||||
/* Кнопка «Заказать звонок» */
|
||||
a.callback_url {
|
||||
background: #e9e4d4;
|
||||
border: #d5cfbd solid 1px;
|
||||
color: #000;
|
||||
border-radius: 25px;
|
||||
font-size: 16px;
|
||||
padding: 6px 18px;
|
||||
}`;
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto px-8 py-10">
|
||||
@@ -158,46 +228,89 @@ export default function ButtonsPage() {
|
||||
Кнопки
|
||||
</h1>
|
||||
<p className="text-base max-w-2xl" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Все варианты кнопок, применяемых на сайте клиники.
|
||||
Кнопки — основной элемент призыва к действию в интерфейсе.
|
||||
Кнопки скопированы с реального сайта{" "}
|
||||
<span className="font-mono text-sm" style={{ color: "var(--bb-text)" }}>
|
||||
oclinica.ru
|
||||
</span>
|
||||
. Цвета, размеры и тени взяты напрямую из CSS темы{" "}
|
||||
<span className="font-mono text-sm" style={{ color: "var(--bb-text)" }}>
|
||||
clinic_bootstrap_mobile/css/style.css
|
||||
</span>
|
||||
.
|
||||
</p>
|
||||
<div
|
||||
className="mt-4 px-4 py-3 rounded-lg border text-sm flex items-center gap-2"
|
||||
style={{ borderColor: "#e0f5f4", background: "#f8fffe", color: "var(--bb-text-muted)" }}
|
||||
>
|
||||
<span style={{ color: "var(--brand-053m)", fontWeight: 600 }}>Источник</span>
|
||||
<span>
|
||||
CSS сайта проанализирован 2026-03-22 — 4 типа кнопок с реальными значениями.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 1. Варианты */}
|
||||
<Section
|
||||
id="variants"
|
||||
title="Варианты"
|
||||
subtitle="Четыре визуальных типа кнопок для разных контекстов."
|
||||
subtitle="Четыре типа кнопок с реального сайта клиники."
|
||||
>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
|
||||
{VARIANT_INFO.map(({ variant, label, description, useCase }) => (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-6">
|
||||
{VARIANTS.map(({ variant, name, label, where, example, note, bg, border, textColor, radius, shadow }) => (
|
||||
<div
|
||||
key={variant}
|
||||
className="rounded-xl border p-5 flex flex-col gap-4"
|
||||
style={{ borderColor: "var(--bb-border)" }}
|
||||
>
|
||||
{/* Превью */}
|
||||
<div
|
||||
className="flex items-center justify-center py-6 rounded-lg"
|
||||
style={{ background: "var(--bb-sidebar-bg)" }}
|
||||
>
|
||||
<Button variant={variant}>{label}</Button>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium text-sm mb-1" style={{ color: "var(--bb-text)" }}>
|
||||
<Button variant={variant} size="md">
|
||||
{label}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Инфо */}
|
||||
<div>
|
||||
<p className="font-semibold text-sm mb-1" style={{ color: "var(--bb-text)" }}>
|
||||
{name}
|
||||
</p>
|
||||
<p
|
||||
className="text-xs mb-3 leading-relaxed"
|
||||
style={{ color: "var(--bb-text-muted)" }}
|
||||
>
|
||||
{description}
|
||||
<p className="text-xs mb-2 leading-relaxed" style={{ color: "var(--bb-text-muted)" }}>
|
||||
{note}
|
||||
</p>
|
||||
<span
|
||||
className="inline-block text-[11px] px-2 py-0.5 rounded"
|
||||
style={{ background: "#e0f5f4", color: "var(--brand-073m)" }}
|
||||
<div className="flex flex-wrap gap-1.5 mb-2">
|
||||
{[
|
||||
{ k: "bg", v: bg },
|
||||
{ k: "text", v: textColor },
|
||||
{ k: "border", v: border },
|
||||
{ k: "radius", v: radius },
|
||||
...(shadow === "да" ? [{ k: "shadow", v: "да" }] : []),
|
||||
].map(({ k, v }) => (
|
||||
<span
|
||||
key={k}
|
||||
className="text-[10px] font-mono px-1.5 py-0.5 rounded"
|
||||
style={{ background: "#f3f4f6", color: "var(--bb-text-muted)" }}
|
||||
>
|
||||
{k}: {v}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<div
|
||||
className="rounded p-2.5 text-xs"
|
||||
style={{ background: "#f8f9fa", color: "var(--bb-text-muted)" }}
|
||||
>
|
||||
{useCase}
|
||||
</span>
|
||||
<span className="font-medium" style={{ color: "var(--bb-text)" }}>
|
||||
Где:
|
||||
</span>{" "}
|
||||
{where}
|
||||
<br />
|
||||
<span className="font-medium" style={{ color: "var(--bb-text)" }}>
|
||||
Пример:
|
||||
</span>{" "}
|
||||
{example}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
@@ -208,7 +321,7 @@ export default function ButtonsPage() {
|
||||
<Section
|
||||
id="sizes"
|
||||
title="Размеры"
|
||||
subtitle="Три размера для разных уровней иерархии интерфейса."
|
||||
subtitle="Три размера для разных контекстов. lg соответствует форм-кнопке на реальном сайте (18px, bold)."
|
||||
>
|
||||
<div
|
||||
className="rounded-xl border overflow-hidden"
|
||||
@@ -219,20 +332,20 @@ export default function ButtonsPage() {
|
||||
{
|
||||
size: "sm" as const,
|
||||
label: "Small",
|
||||
hint: "padding: 5px 12px · font: 13px · radius: 6px",
|
||||
use: "Компактные интерфейсы, действия в таблицах",
|
||||
hint: "4px 11px · 13px",
|
||||
use: "Компактные контексты, таблицы",
|
||||
},
|
||||
{
|
||||
size: "md" as const,
|
||||
label: "Medium",
|
||||
hint: "padding: 8px 18px · font: 14px · radius: 8px",
|
||||
use: "Стандартный размер (по умолчанию)",
|
||||
hint: "8px 16px · 14px",
|
||||
use: "Appointment, Teal, Pill (соответствует сайту)",
|
||||
},
|
||||
{
|
||||
size: "lg" as const,
|
||||
label: "Large",
|
||||
hint: "padding: 12px 26px · font: 16px · radius: 10px",
|
||||
use: "Главные CTA на Hero-блоках",
|
||||
hint: "10px 24px · 18px bold",
|
||||
use: "Primary форм-кнопка (соответствует сайту)",
|
||||
},
|
||||
] as const
|
||||
).map(({ size, label, hint, use }, i) => (
|
||||
@@ -241,7 +354,7 @@ export default function ButtonsPage() {
|
||||
className="flex items-center gap-6 px-5 py-4"
|
||||
style={{ borderTop: i > 0 ? "1px solid var(--bb-border)" : undefined }}
|
||||
>
|
||||
<div className="w-36 shrink-0">
|
||||
<div className="w-40 shrink-0">
|
||||
<Button variant="primary" size={size}>
|
||||
Записаться
|
||||
</Button>
|
||||
@@ -251,12 +364,12 @@ export default function ButtonsPage() {
|
||||
{label}
|
||||
</p>
|
||||
<p className="text-xs font-mono" style={{ color: "var(--bb-text-muted)" }}>
|
||||
{hint}
|
||||
padding: {hint.split("·")[0].trim()} · font-size: {hint.split("·")[1].trim()}
|
||||
</p>
|
||||
</div>
|
||||
<p
|
||||
className="text-xs hidden lg:block"
|
||||
style={{ color: "var(--bb-text-muted)", maxWidth: 200 }}
|
||||
style={{ color: "var(--bb-text-muted)", maxWidth: 220 }}
|
||||
>
|
||||
{use}
|
||||
</p>
|
||||
@@ -269,49 +382,53 @@ export default function ButtonsPage() {
|
||||
<Section
|
||||
id="states"
|
||||
title="Состояния"
|
||||
subtitle="Поведение кнопки при разных условиях взаимодействия."
|
||||
subtitle="Базовые состояния кнопки. На реальном сайте hover/transition не определены в CSS."
|
||||
>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{(
|
||||
[
|
||||
{
|
||||
label: "Default",
|
||||
node: <Button variant="primary">Записаться</Button>,
|
||||
node: <Button variant="primary" size="lg">Записаться</Button>,
|
||||
hint: "Стандартное состояние",
|
||||
},
|
||||
{
|
||||
label: "Hover",
|
||||
node: (
|
||||
<Button variant="primary" style={{ filter: "brightness(0.9)" }}>
|
||||
<Button
|
||||
variant="primary"
|
||||
size="lg"
|
||||
style={{ filter: "brightness(0.93)" }}
|
||||
>
|
||||
Записаться
|
||||
</Button>
|
||||
),
|
||||
hint: "filter: brightness(0.9) при наведении курсора",
|
||||
hint: "filter: brightness(0.93)",
|
||||
},
|
||||
{
|
||||
label: "Loading",
|
||||
node: <Button variant="primary" loading>Загрузка...</Button>,
|
||||
hint: "Спиннер + opacity: 0.5 + кнопка заблокирована",
|
||||
node: <Button variant="primary" size="lg" loading>Отправка...</Button>,
|
||||
hint: "Спиннер + blocked",
|
||||
},
|
||||
{
|
||||
label: "Disabled",
|
||||
node: <Button variant="primary" disabled>Недоступно</Button>,
|
||||
hint: "opacity: 0.5, cursor: not-allowed",
|
||||
node: <Button variant="primary" size="lg" disabled>Записаться</Button>,
|
||||
hint: "opacity: 0.5",
|
||||
},
|
||||
] as const
|
||||
).map(({ label, node, hint }) => (
|
||||
<div
|
||||
key={label}
|
||||
className="rounded-xl border p-5"
|
||||
className="rounded-xl border p-4"
|
||||
style={{ borderColor: "var(--bb-border)" }}
|
||||
>
|
||||
<div
|
||||
className="flex items-center justify-center py-6 mb-3 rounded-lg"
|
||||
className="flex items-center justify-center py-4 mb-3 rounded-lg"
|
||||
style={{ background: "var(--bb-sidebar-bg)" }}
|
||||
>
|
||||
{node}
|
||||
</div>
|
||||
<p className="text-sm font-medium mb-1" style={{ color: "var(--bb-text)" }}>
|
||||
<p className="text-sm font-medium mb-0.5" style={{ color: "var(--bb-text)" }}>
|
||||
{label}
|
||||
</p>
|
||||
<p className="text-xs" style={{ color: "var(--bb-text-muted)" }}>
|
||||
@@ -322,51 +439,81 @@ export default function ButtonsPage() {
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* 4. Все варианты вместе */}
|
||||
{/* 4. Контекст применения */}
|
||||
<Section
|
||||
id="all"
|
||||
title="Все варианты и размеры"
|
||||
subtitle="Сводная таблица — визуальное сравнение."
|
||||
id="context"
|
||||
title="Где применяется"
|
||||
subtitle="Таблица: тип кнопки → реальное использование на сайте."
|
||||
>
|
||||
<div
|
||||
className="rounded-xl border overflow-hidden"
|
||||
style={{ borderColor: "var(--bb-border)" }}
|
||||
>
|
||||
<div
|
||||
className="px-5 py-3 text-xs font-medium border-b"
|
||||
style={{
|
||||
color: "var(--bb-text-muted)",
|
||||
background: "var(--bb-sidebar-bg)",
|
||||
borderColor: "var(--bb-border)",
|
||||
}}
|
||||
>
|
||||
Вариант / Размер
|
||||
</div>
|
||||
{VARIANT_INFO.map(({ variant, label }, vi) => (
|
||||
<div
|
||||
key={variant}
|
||||
className="flex items-center gap-5 px-5 py-3"
|
||||
style={{ borderTop: vi > 0 ? "1px solid var(--bb-border)" : undefined }}
|
||||
>
|
||||
<span
|
||||
className="w-20 text-xs font-mono shrink-0"
|
||||
style={{ color: "var(--bb-text-muted)" }}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
<div className="flex items-center gap-3 flex-wrap">
|
||||
<Button variant={variant} size="sm">
|
||||
Маленькая
|
||||
</Button>
|
||||
<Button variant={variant} size="md">
|
||||
Средняя
|
||||
</Button>
|
||||
<Button variant={variant} size="lg">
|
||||
Большая
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr style={{ background: "var(--bb-sidebar-bg)" }}>
|
||||
{["Вариант", "Цвет фона", "Реальный класс/контекст", "Текст кнопки на сайте"].map((h) => (
|
||||
<th
|
||||
key={h}
|
||||
className="text-left px-4 py-3 font-medium text-xs"
|
||||
style={{ color: "var(--bb-text-muted)" }}
|
||||
>
|
||||
{h}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[
|
||||
{
|
||||
v: <Button variant="primary" size="sm">Primary</Button>,
|
||||
bg: "#FFA39C",
|
||||
ctx: "button в entityform-блоках форм записи",
|
||||
text: "«Запишите меня!»",
|
||||
},
|
||||
{
|
||||
v: <Button variant="outline" size="sm">Outline</Button>,
|
||||
bg: "#fff / рамка #BF9975",
|
||||
ctx: ".appointment в хедере (block-block-15, 30, 32, 24)",
|
||||
text: "«Записаться на прием»",
|
||||
},
|
||||
{
|
||||
v: <Button variant="teal" size="sm">Teal</Button>,
|
||||
bg: "#60959c",
|
||||
ctx: ".show-phone (block-block-4, 15)",
|
||||
text: "«Позвонить»",
|
||||
},
|
||||
{
|
||||
v: <Button variant="pill" size="sm">Pill</Button>,
|
||||
bg: "#e9e4d4",
|
||||
ctx: "a.callback_url (modal trigger)",
|
||||
text: "«Заказать звонок»",
|
||||
},
|
||||
].map(({ v, bg, ctx, text }, i) => (
|
||||
<tr
|
||||
key={i}
|
||||
style={{ borderTop: "1px solid var(--bb-border)" }}
|
||||
>
|
||||
<td className="px-4 py-3">{v}</td>
|
||||
<td
|
||||
className="px-4 py-3 font-mono text-xs"
|
||||
style={{ color: "var(--bb-text-muted)" }}
|
||||
>
|
||||
{bg}
|
||||
</td>
|
||||
<td
|
||||
className="px-4 py-3 font-mono text-xs"
|
||||
style={{ color: "var(--bb-text-muted)" }}
|
||||
>
|
||||
{ctx}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-xs" style={{ color: "var(--bb-text)" }}>
|
||||
{text}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
@@ -374,58 +521,50 @@ export default function ButtonsPage() {
|
||||
<Section
|
||||
id="code"
|
||||
title="Примеры кода"
|
||||
subtitle="Скопируйте HTML или JSX для использования в проекте."
|
||||
subtitle="HTML-классы из globals.css, JSX-компонент, и точный CSS с сайта."
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<CodeCopy lang="HTML (CSS-классы из globals.css)" code={codeHtml} />
|
||||
<CodeCopy lang="HTML (CSS-классы brandbook)" code={codeHtml} />
|
||||
<CodeCopy lang="JSX (React / Next.js)" code={codeReact} />
|
||||
<CodeCopy lang="CSS — точно с сайта oclinica.ru" code={codeSiteExact} />
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* LLM-блок */}
|
||||
<LlmBlock path="/components/buttons" version="v1.0" specText={LLM_BUTTONS_TEXT}>
|
||||
<LlmSection title="Варианты" />
|
||||
<LlmBlock path="/components/buttons" version="v2.0" specText={LLM_BUTTONS_TEXT}>
|
||||
<LlmSection title="Варианты (реальный сайт oclinica.ru)" />
|
||||
<LlmTable
|
||||
headers={["Вариант", "CSS класс", "Фон", "Текст", "Применение"]}
|
||||
rows={VARIANT_INFO.map((v) => [
|
||||
headers={["Вариант", "CSS класс", "Фон", "Текст", "Border", "Radius", "Применение"]}
|
||||
rows={VARIANTS.map((v) => [
|
||||
v.variant,
|
||||
v.cssClass,
|
||||
v.bg,
|
||||
v.text,
|
||||
v.useCase,
|
||||
v.textColor,
|
||||
v.border,
|
||||
v.radius,
|
||||
v.where,
|
||||
])}
|
||||
/>
|
||||
<LlmSection title="Размеры" />
|
||||
<LlmSection title="Размеры (брендбук-компонент)" />
|
||||
<LlmTable
|
||||
headers={["Размер", "CSS класс", "padding", "font-size", "radius", "Применение"]}
|
||||
headers={["Размер", "padding", "font-size", "Применение"]}
|
||||
rows={[
|
||||
["sm", ".bb-btn-sm", "5px 12px", "13px", "6px", "Компактные интерфейсы, таблицы"],
|
||||
["md", ".bb-btn-md", "8px 18px", "14px", "8px", "Стандарт (по умолчанию)"],
|
||||
["lg", ".bb-btn-lg", "12px 26px", "16px", "10px", "Hero CTA"],
|
||||
]}
|
||||
/>
|
||||
<LlmSection title="Состояния" />
|
||||
<LlmTable
|
||||
headers={["Состояние", "CSS / Поведение"]}
|
||||
rows={[
|
||||
["default", "без изменений"],
|
||||
["hover", "filter: brightness(0.9)"],
|
||||
["active", "filter: brightness(0.82)"],
|
||||
["loading", "spinner bb-spin + opacity: 0.5 + disabled=true"],
|
||||
["disabled", "opacity: 0.5 + cursor: not-allowed"],
|
||||
["sm", "4px 11px", "13px", "Компактные контексты"],
|
||||
["md", "8px 16px", "14px", "Стандарт (outline, teal, pill с сайта)"],
|
||||
["lg", "10px 24px", "18px bold", "Primary форм-кнопка (соответствует сайту)"],
|
||||
]}
|
||||
/>
|
||||
<LlmSection title="Правила применения" />
|
||||
<LlmRules
|
||||
rules={[
|
||||
{ ok: true, text: "Не более одной primary-кнопки на один экран в контексте задачи" },
|
||||
{ ok: true, text: "Текст — глагол или призыв: «Записаться», «Узнать цену»" },
|
||||
{ ok: true, text: "Primary → главное действие формы или подтверждения" },
|
||||
{ ok: true, text: "Ghost → отмена и навигационные ссылки без акцента" },
|
||||
{ ok: true, text: "Danger → только деструктивные действия" },
|
||||
{ ok: false, text: "Не менять цвета произвольно вне фирменной палитры" },
|
||||
{ ok: false, text: "Не добавлять тени к кнопкам" },
|
||||
{ ok: false, text: "Не использовать Danger для нейтральных действий" },
|
||||
{ ok: true, text: "primary (коралловый) — только для submit в формах записи" },
|
||||
{ ok: true, text: "outline (бежевый) — хедер, навигация, второстепенные ссылки" },
|
||||
{ ok: true, text: "teal (бирюзовый) — телефонные и контактные действия" },
|
||||
{ ok: true, text: "pill (кремовый) — открытие модальных окон / callback" },
|
||||
{ ok: true, text: "Не более одного primary на форму" },
|
||||
{ ok: false, text: "Не менять цвета вне указанной палитры сайта" },
|
||||
{ ok: false, text: "Primary — не для навигационных ссылок" },
|
||||
{ ok: false, text: "Не накладывать тень на outline, teal, pill" },
|
||||
]}
|
||||
/>
|
||||
</LlmBlock>
|
||||
|
||||
@@ -68,14 +68,46 @@ body {
|
||||
.bb-btn:disabled { cursor: not-allowed; opacity: 0.5; }
|
||||
.bb-btn:focus-visible { outline: 2px solid var(--brand-053m); outline-offset: 2px; }
|
||||
|
||||
.bb-btn-sm { font-size: 13px; padding: 5px 12px; border-radius: 6px; border: 1.5px solid transparent; }
|
||||
.bb-btn-md { font-size: 14px; padding: 8px 18px; border-radius: 8px; border: 1.5px solid transparent; }
|
||||
.bb-btn-lg { font-size: 16px; padding: 12px 26px; border-radius: 10px; border: 1.5px solid transparent; }
|
||||
/* Размеры — только padding и font-size, radius задаётся вариантом */
|
||||
.bb-btn-sm { font-size: 13px; padding: 4px 11px; border: 1px solid transparent; }
|
||||
.bb-btn-md { font-size: 14px; padding: 8px 16px; border: 1px solid transparent; }
|
||||
.bb-btn-lg { font-size: 18px; padding: 10px 24px; border: 1px solid transparent; font-weight: bold; }
|
||||
|
||||
.bb-btn-primary { background: var(--brand-073m); color: #fff; border-color: var(--brand-073m); }
|
||||
.bb-btn-secondary { background: transparent; color: var(--brand-073m); border-color: var(--brand-073m); }
|
||||
.bb-btn-ghost { background: transparent; color: var(--brand-073m); border-color: transparent; }
|
||||
.bb-btn-danger { background: #dc2626; color: #fff; border-color: #dc2626; }
|
||||
/* Варианты — цвета и радиус по реальному сайту oclinica.ru */
|
||||
|
||||
/* primary — коралловая форм-кнопка «Запишите меня!» */
|
||||
.bb-btn-primary {
|
||||
background: #FFA39C;
|
||||
color: #fff;
|
||||
border-color: #FF847B;
|
||||
border-radius: 7px;
|
||||
font-weight: bold;
|
||||
box-shadow: 0px 0px 5px rgba(0,0,0,0.4), 0px 3px 5px rgba(0,0,0,0.25);
|
||||
}
|
||||
|
||||
/* outline — белая с бежевой рамкой «Записаться на прием» */
|
||||
.bb-btn-outline {
|
||||
background: #fff;
|
||||
color: #BF9975;
|
||||
border-color: #BF9975;
|
||||
border-radius: 7px;
|
||||
}
|
||||
|
||||
/* teal — бирюзовая «Позвонить» */
|
||||
.bb-btn-teal {
|
||||
background: #60959c;
|
||||
color: #fff;
|
||||
border-color: transparent;
|
||||
border-radius: 7px;
|
||||
}
|
||||
|
||||
/* pill — кремовая таблетка «Заказать звонок» */
|
||||
.bb-btn-pill {
|
||||
background: #e9e4d4;
|
||||
color: #333;
|
||||
border-color: #d5cfbd;
|
||||
border-radius: 25px;
|
||||
}
|
||||
|
||||
/* ─── Форм-контролы (Sprint 3) ───────────────────────────────── */
|
||||
.bb-input,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { ButtonHTMLAttributes, forwardRef } from "react";
|
||||
|
||||
export type ButtonVariant = "primary" | "secondary" | "ghost" | "danger";
|
||||
export type ButtonVariant = "primary" | "outline" | "teal" | "pill";
|
||||
export type ButtonSize = "sm" | "md" | "lg";
|
||||
|
||||
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
|
||||
Reference in New Issue
Block a user