From 0855892643dd75b9b411db782a6e9f163d4b13a6 Mon Sep 17 00:00:00 2001 From: AR 15 M4 Date: Sun, 22 Mar 2026 20:20:41 +0500 Subject: [PATCH] =?UTF-8?q?feat(sprint3):=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA?= =?UTF-8?q?=D0=B8,=20=D1=84=D0=BE=D1=80=D0=BC-=D0=BA=D0=BE=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BB=D1=8B,=20LLM-=D0=B1=D0=BB=D0=BE=D0=BA=D0=B8?= =?UTF-8?q?=20=E2=80=94=20Sprint=203=20v0.3.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - components/ui/Button.tsx — компонент Button (primary/secondary/ghost/danger, sm/md/lg, loading/disabled) - components/ui/CodeCopy.tsx — компонент копирования кода (clipboard API) - components/ui/Toggle.tsx — тумблер (client component, bb-toggle-track/thumb) - globals.css — CSS-классы: bb-btn, bb-input/textarea/select, bb-checkbox/radio, bb-toggle, @keyframes bb-spin - app/components/buttons/page.tsx — страница «Кнопки» (варианты, размеры, состояния, code copy, LLM-блок) - app/components/forms/page.tsx — страница «Форм-контролы» (Input/Textarea/Select/Checkbox/Radio/Toggle, LLM-блок) - foundation/logo/page.tsx — добавлен LLM-блок v1.0 - Sidebar: убраны «скоро» с Кнопок и Форм-контролов, версия Sprint 3 · v0.3.0 - docs/LLM_CONTEXT.md → версия 3.0, добавлена секция 9a с компонентами Co-Authored-By: Claude Sonnet 4.6 --- apps/web/app/components/buttons/page.tsx | 434 +++++++++++++++++ apps/web/app/components/forms/page.tsx | 589 +++++++++++++++++++++++ apps/web/app/foundation/logo/page.tsx | 88 ++++ apps/web/app/globals.css | 106 ++++ apps/web/components/layout/Sidebar.tsx | 6 +- apps/web/components/ui/Button.tsx | 45 ++ apps/web/components/ui/CodeCopy.tsx | 60 +++ apps/web/components/ui/Toggle.tsx | 59 +++ docs/LLM_CONTEXT.md | 47 +- 9 files changed, 1428 insertions(+), 6 deletions(-) create mode 100644 apps/web/app/components/buttons/page.tsx create mode 100644 apps/web/app/components/forms/page.tsx create mode 100644 apps/web/components/ui/Button.tsx create mode 100644 apps/web/components/ui/CodeCopy.tsx create mode 100644 apps/web/components/ui/Toggle.tsx diff --git a/apps/web/app/components/buttons/page.tsx b/apps/web/app/components/buttons/page.tsx new file mode 100644 index 0000000..35b5d67 --- /dev/null +++ b/apps/web/app/components/buttons/page.tsx @@ -0,0 +1,434 @@ +import type { Metadata } from "next"; +import { Button } from "@/components/ui/Button"; +import { CodeCopy } from "@/components/ui/CodeCopy"; +import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock"; + +export const metadata: Metadata = { + title: "Кнопки. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой", +}; + +function Section({ + id, + title, + subtitle, + children, +}: { + id?: string; + title: string; + subtitle?: string; + children: React.ReactNode; +}) { + return ( +
+
+

+ {title} +

+ {subtitle && ( +

+ {subtitle} +

+ )} +
+ {children} +
+ ); +} + +const VARIANT_INFO = [ + { + variant: "primary" as const, + label: "Primary", + cssClass: ".bb-btn-primary", + bg: "#5b7b87", + text: "#fff", + description: "Основная кнопка призыва к действию. Тёмный бирюзово-серый фон, белый текст.", + useCase: "Записаться · Подтвердить", + }, + { + variant: "secondary" as const, + label: "Secondary", + cssClass: ".bb-btn-secondary", + bg: "прозрачный", + text: "#5b7b87", + description: "Второстепенное действие. Контурная кнопка с фирменным цветом.", + useCase: "Узнать подробнее · Редактировать", + }, + { + variant: "ghost" as const, + label: "Ghost", + cssClass: ".bb-btn-ghost", + bg: "прозрачный", + text: "#5b7b87", + description: "Третичное действие. Без фона и видимой рамки, текстовый акцент.", + useCase: "Отмена · Назад · Ещё...", + }, + { + variant: "danger" as const, + label: "Danger", + cssClass: ".bb-btn-danger", + bg: "#dc2626", + text: "#fff", + description: "Деструктивные и необратимые действия. Красный фон.", + useCase: "Удалить · Отменить запись", + }, +]; + +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; } + +ПРАВИЛА ПРИМЕНЕНИЯ +✓ Не более одной primary-кнопки на видимый экран в контексте одной задачи +✓ Текст кнопки — глагол или чёткий призыв: «Записаться», «Узнать цену» +✓ Primary → главное действие (форма записи, подтверждение) +✓ Secondary → второстепенное (подробнее, редактировать) +✓ Ghost → отмена, навигационная ссылка без акцента +✓ Danger → только деструктивные действия (удалить, отменить запись) +✕ Не менять цвета произвольно — только варианты из фирменной палитры +✕ Не добавлять тени к кнопкам +✕ Не использовать Danger для нейтральных действий`.trim(); + +export default function ButtonsPage() { + const codeHtml = ` + + + + + + + + +`; + + const codeReact = `import { Button } from "@/components/ui/Button"; + +// Варианты + + + + + +// Размеры + + {/* по умолчанию */} + + +// Состояния + +`; + + return ( +
+ {/* Заголовок */} +
+

+ Компоненты → 3.1 +

+

+ Кнопки +

+

+ Все варианты кнопок, применяемых на сайте клиники. + Кнопки — основной элемент призыва к действию в интерфейсе. +

+
+ + {/* 1. Варианты */} +
+
+ {VARIANT_INFO.map(({ variant, label, description, useCase }) => ( +
+
+ +
+
+

+ {label} +

+

+ {description} +

+ + {useCase} + +
+
+ ))} +
+
+ + {/* 2. Размеры */} +
+
+ {( + [ + { + size: "sm" as const, + label: "Small", + hint: "padding: 5px 12px · font: 13px · radius: 6px", + use: "Компактные интерфейсы, действия в таблицах", + }, + { + size: "md" as const, + label: "Medium", + hint: "padding: 8px 18px · font: 14px · radius: 8px", + use: "Стандартный размер (по умолчанию)", + }, + { + size: "lg" as const, + label: "Large", + hint: "padding: 12px 26px · font: 16px · radius: 10px", + use: "Главные CTA на Hero-блоках", + }, + ] as const + ).map(({ size, label, hint, use }, i) => ( +
0 ? "1px solid var(--bb-border)" : undefined }} + > +
+ +
+
+

+ {label} +

+

+ {hint} +

+
+

+ {use} +

+
+ ))} +
+
+ + {/* 3. Состояния */} +
+
+ {( + [ + { + label: "Default", + node: , + hint: "Стандартное состояние", + }, + { + label: "Hover", + node: ( + + ), + hint: "filter: brightness(0.9) при наведении курсора", + }, + { + label: "Loading", + node: , + hint: "Спиннер + opacity: 0.5 + кнопка заблокирована", + }, + { + label: "Disabled", + node: , + hint: "opacity: 0.5, cursor: not-allowed", + }, + ] as const + ).map(({ label, node, hint }) => ( +
+
+ {node} +
+

+ {label} +

+

+ {hint} +

+
+ ))} +
+
+ + {/* 4. Все варианты вместе */} +
+
+
+ Вариант / Размер +
+ {VARIANT_INFO.map(({ variant, label }, vi) => ( +
0 ? "1px solid var(--bb-border)" : undefined }} + > + + {label} + +
+ + + +
+
+ ))} +
+
+ + {/* 5. Примеры кода */} +
+
+ + +
+
+ + {/* LLM-блок */} + + + [ + v.variant, + v.cssClass, + v.bg, + v.text, + v.useCase, + ])} + /> + + + + + + + +
+ ); +} diff --git a/apps/web/app/components/forms/page.tsx b/apps/web/app/components/forms/page.tsx new file mode 100644 index 0000000..b1882eb --- /dev/null +++ b/apps/web/app/components/forms/page.tsx @@ -0,0 +1,589 @@ +import type { Metadata } from "next"; +import { Toggle } from "@/components/ui/Toggle"; +import { CodeCopy } from "@/components/ui/CodeCopy"; +import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock"; + +export const metadata: Metadata = { + title: "Форм-контролы. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой", +}; + +function Section({ + id, + title, + subtitle, + children, +}: { + id?: string; + title: string; + subtitle?: string; + children: React.ReactNode; +}) { + return ( +
+
+

+ {title} +

+ {subtitle && ( +

+ {subtitle} +

+ )} +
+ {children} +
+ ); +} + +function FieldLabel({ text, required }: { text: string; required?: boolean }) { + return ( + + ); +} + +function FieldHint({ text }: { text: string }) { + return ( +

+ {text} +

+ ); +} + +function FieldError({ text }: { text: string }) { + return ( +

+ {text} +

+ ); +} + +function StateCard({ + label, + hint, + children, +}: { + label: string; + hint: string; + children: React.ReactNode; +}) { + return ( +
+
{children}
+

+ {label} +

+

+ {hint} +

+
+ ); +} + +const LLM_FORMS_TEXT = `ФОРМ-КОНТРОЛЫ — LLM-спецификация +Версия: v1.0 · /components/forms + +ТЕКСТОВОЕ ПОЛЕ (Input) +CSS класс: .bb-input +Высота: ~40px (padding: 9px 12px) +border: 1.5px solid #e5e7eb · border-radius: 8px · font: Fira Sans 14px +Состояния: + default: border #e5e7eb + focus: border #7ecfca + box-shadow 0 0 0 3px rgba(126,207,202,0.2) + error: border #dc2626 + класс .bb-error + disabled: opacity 0.5 + cursor not-allowed + bg #f8f9fa + +МНОГОСТРОЧНЫЙ ТЕКСТ (Textarea) +CSS класс: .bb-textarea +Те же состояния что у Input +min-height: 100px · resize: vertical + +ВЫПАДАЮЩИЙ СПИСОК (Select) +CSS класс: .bb-select +Стрелка: SVG background-image (data URI) · padding-right: 36px +Те же состояния что у Input + +ФЛАЖОК (Checkbox) +CSS класс: .bb-checkbox +size: 16×16px · accent-color: #5b7b87 +HTML: +Состояния: unchecked / checked / disabled / checked+disabled + +ПЕРЕКЛЮЧАТЕЛЬ ВАРИАНТА (Radio) +CSS класс: .bb-radio +size: 16×16px · accent-color: #5b7b87 +HTML: +Всегда в группе — один выбранный из нескольких + +ТУМБЛЕР (Toggle/Switch) +Компонент: @/components/ui/Toggle (React, "use client") +Ширина трека: 44px · Высота: 24px · Бегунок: 20×20px +Выкл: track #d1d5db · Вкл: track #5b7b87 +CSS: .bb-toggle-track / .bb-toggle-thumb +HTML-аналог: + +ОБЩИЕ ПРАВИЛА +✓ Метка (label) всегда над полем, font-weight: 500 +✓ Обязательные поля помечены * красным цветом (#dc2626) +✓ Подсказка (hint) серым текстом под полем — font-size: 12px +✓ Сообщение об ошибке красным (#dc2626) под полем вместо hint +✓ Focus outline — тёмный бирюзовый #7ecfca (--brand-053m) +✓ Группы checkbox/radio — вертикальный список с gap: 10px +✓ Toggle — для булевых настроек включить/выключить +✕ Не использовать placeholder вместо label +✕ Не скрывать обязательность поля +✕ Не делать поля шире контейнера`.trim(); + +export default function FormsPage() { + const codeInput = ` + + +

Укажите имя как в паспорте

+ + + +

Минимум 3 символа

+ + +`; + + const codeTextarea = ` +`; + + const codeSelect = ` +`; + + const codeCheckbox = ` + + + +
+ + +
`; + + const codeRadio = `
+ + + +
`; + + const codeToggle = `import { Toggle } from "@/components/ui/Toggle"; + +// Базовый тумблер + + +// С меткой + + +// По умолчанию включён + + +// Заблокирован +`; + + return ( +
+ {/* Заголовок */} +
+

+ Компоненты → 3.2 +

+

+ Форм-контролы +

+

+ Элементы ввода данных: текстовые поля, выпадающие списки, флажки, переключатели. + Применяются в формах записи, фильтрах и настройках. +

+
+ + {/* 1. Input */} +
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + {/* Password */} +
+

+ Тип password +

+
+ + +
+
+
+ + {/* 2. Textarea */} +
+
+ + +