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:
AR 15 M4
2026-03-22 20:41:27 +05:00
parent 0855892643
commit c1731615ab
3 changed files with 373 additions and 202 deletions
+333 -194
View File
@@ -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>
+39 -7
View File
@@ -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,
+1 -1
View File
@@ -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> {