From c1731615ab06932c9fa8d8f5449eb3409bea11a7 Mon Sep 17 00:00:00 2001 From: AR 15 M4 Date: Sun, 22 Mar 2026 20:41:27 +0500 Subject: [PATCH] =?UTF-8?q?fix(buttons):=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B4?= =?UTF-8?q?=D0=B5=D0=BB=D0=B0=D0=BD=D1=8B=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA?= =?UTF-8?q?=D0=B8=20=D0=BF=D0=BE=D0=B4=20=D1=80=D0=B5=D0=B0=D0=BB=D1=8C?= =?UTF-8?q?=D0=BD=D1=8B=D0=B9=20=D1=81=D0=B0=D0=B9=D1=82=20oclinica.ru?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Анализ 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 --- apps/web/app/components/buttons/page.tsx | 533 ++++++++++++++--------- apps/web/app/globals.css | 48 +- apps/web/components/ui/Button.tsx | 2 +- 3 files changed, 377 insertions(+), 206 deletions(-) diff --git a/apps/web/app/components/buttons/page.tsx b/apps/web/app/components/buttons/page.tsx index 35b5d67..8490870 100644 --- a/apps/web/app/components/buttons/page.tsx +++ b/apps/web/app/components/buttons/page.tsx @@ -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 - -ВАРИАНТЫ -Вариант | 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 класс | 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-блоках - -СОСТОЯНИЯ -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 - -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; } +const LLM_BUTTONS_TEXT = `КНОПКИ — LLM-спецификация (с реального сайта oclinica.ru) +Версия: v2.0 · /components/buttons +Источник CSS: perm.oclinica.ru/.../style.css + +ВАРИАНТЫ (реальный сайт) +Вариант | 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 С САЙТА (точные значения) +/* форм-кнопка «Запишите меня!» */ +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); } + +/* appointment — «Записаться на прием» */ +.appointment { background:#FFF; border:#BF9975 solid 1px; color:#BF9975; + font-size:14px; line-height:38px; padding:3px 12px; border-radius:7px; } + +/* 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 = ` - - - - + const codeHtml = ` + - - - -`; + +Записаться на прием + + +Позвонить + + +Заказать звонок`; const codeReact = `import { Button } from "@/components/ui/Button"; -// Варианты - - - - +// Форм-кнопка (главный CTA) + + +// Запись из хедера / навигации + + +// Звонок + + +// Заказать звонок (открывает модал) + -// Размеры - - {/* по умолчанию */} - +// С loading-состоянием +`; -// Состояния - -`; + 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 (
@@ -158,46 +228,89 @@ export default function ButtonsPage() { Кнопки

- Все варианты кнопок, применяемых на сайте клиники. - Кнопки — основной элемент призыва к действию в интерфейсе. + Кнопки скопированы с реального сайта{" "} + + oclinica.ru + + . Цвета, размеры и тени взяты напрямую из CSS темы{" "} + + clinic_bootstrap_mobile/css/style.css + + .

+
+ Источник + + CSS сайта проанализирован 2026-03-22 — 4 типа кнопок с реальными значениями. + +
{/* 1. Варианты */}
-
- {VARIANT_INFO.map(({ variant, label, description, useCase }) => ( +
+ {VARIANTS.map(({ variant, name, label, where, example, note, bg, border, textColor, radius, shadow }) => (
+ {/* Превью */}
- +
+ + {/* Инфо */}
-

- {label} +

+ {name}

-

- {description} +

+ {note}

- + {[ + { k: "bg", v: bg }, + { k: "text", v: textColor }, + { k: "border", v: border }, + { k: "radius", v: radius }, + ...(shadow === "да" ? [{ k: "shadow", v: "да" }] : []), + ].map(({ k, v }) => ( + + {k}: {v} + + ))} +
+
- {useCase} - + + Где: + {" "} + {where} +
+ + Пример: + {" "} + {example} +
))} @@ -208,7 +321,7 @@ export default function ButtonsPage() {
( @@ -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 }} > -
+
@@ -251,12 +364,12 @@ export default function ButtonsPage() { {label}

- {hint} + padding: {hint.split("·")[0].trim()} · font-size: {hint.split("·")[1].trim()}

{use}

@@ -269,49 +382,53 @@ export default function ButtonsPage() {
-
+
{( [ { label: "Default", - node: , + node: , hint: "Стандартное состояние", }, { label: "Hover", node: ( - ), - hint: "filter: brightness(0.9) при наведении курсора", + hint: "filter: brightness(0.93)", }, { label: "Loading", - node: , - hint: "Спиннер + opacity: 0.5 + кнопка заблокирована", + node: , + hint: "Спиннер + blocked", }, { label: "Disabled", - node: , - hint: "opacity: 0.5, cursor: not-allowed", + node: , + hint: "opacity: 0.5", }, ] as const ).map(({ label, node, hint }) => (
{node}
-

+

{label}

@@ -322,51 +439,81 @@ export default function ButtonsPage() {

- {/* 4. Все варианты вместе */} + {/* 4. Контекст применения */}
-
- Вариант / Размер -
- {VARIANT_INFO.map(({ variant, label }, vi) => ( -
0 ? "1px solid var(--bb-border)" : undefined }} - > - - {label} - -
- - - -
-
- ))} + + + + {["Вариант", "Цвет фона", "Реальный класс/контекст", "Текст кнопки на сайте"].map((h) => ( + + ))} + + + + {[ + { + v: , + bg: "#FFA39C", + ctx: "button в entityform-блоках форм записи", + text: "«Запишите меня!»", + }, + { + v: , + bg: "#fff / рамка #BF9975", + ctx: ".appointment в хедере (block-block-15, 30, 32, 24)", + text: "«Записаться на прием»", + }, + { + v: , + bg: "#60959c", + ctx: ".show-phone (block-block-4, 15)", + text: "«Позвонить»", + }, + { + v: , + bg: "#e9e4d4", + ctx: "a.callback_url (modal trigger)", + text: "«Заказать звонок»", + }, + ].map(({ v, bg, ctx, text }, i) => ( + + + + + + + ))} + +
+ {h} +
{v} + {bg} + + {ctx} + + {text} +
@@ -374,58 +521,50 @@ export default function ButtonsPage() {
- + +
{/* LLM-блок */} - - + + [ + 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, ])} /> - - - + diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css index cf1c786..09d5e53 100644 --- a/apps/web/app/globals.css +++ b/apps/web/app/globals.css @@ -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; } - -.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; } +/* Размеры — только 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; } + +/* Варианты — цвета и радиус по реальному сайту 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, diff --git a/apps/web/components/ui/Button.tsx b/apps/web/components/ui/Button.tsx index 38058e4..a181970 100644 --- a/apps/web/components/ui/Button.tsx +++ b/apps/web/components/ui/Button.tsx @@ -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 {