Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0198947c4e | |||
| 6648f16bcb | |||
| 761347ed85 | |||
| 52acdc98e2 | |||
| 0570b50d9f | |||
| 789f436be1 | |||
| 5298f8c1cd | |||
| ff3f6acc03 | |||
| 39ecd72fde | |||
| a715503ca5 | |||
| 657cc26d3e | |||
| f03599abcf |
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useCallback } from "react";
|
||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
/* ─── Утилиты конвертации ──────────────────────────────────────────── */
|
||||
@@ -102,6 +103,87 @@ const BRAND_COLORS = [
|
||||
},
|
||||
];
|
||||
|
||||
/* ─── Соответствие цветов ──────────────────────────────────────────── */
|
||||
const COLOR_MAPPING = [
|
||||
{
|
||||
brand: { oracal: "053M", name: "Основной бирюзовый", hex: "#7ecfca" },
|
||||
web: { name: "Бирюзовый", hex: "#63bac3", count: 4 },
|
||||
note: "Чуть темнее и насыщеннее на сайте",
|
||||
},
|
||||
{
|
||||
brand: { oracal: "073M", name: "Тёмный серо-голубой", hex: "#5b7b87" },
|
||||
web: { name: "Серо-бирюзовый", hex: "#60959c", count: 7 },
|
||||
note: "Светлее на сайте",
|
||||
},
|
||||
{
|
||||
brand: { oracal: "066M", name: "Средний бирюзовый", hex: "#5bb5ad" },
|
||||
web: { name: "Бирюзовый средний", hex: "#52b4bd", count: 4 },
|
||||
note: "Смещён в синеву",
|
||||
},
|
||||
{
|
||||
brand: { oracal: "081M", name: "Бежевый", hex: "#c4a882" },
|
||||
web: { name: "Бежевый", hex: "#bf9975", count: 12 },
|
||||
note: "Темнее и насыщеннее на сайте",
|
||||
},
|
||||
{
|
||||
brand: { oracal: "050M", name: "Тёмно-синий", hex: "#1b4c72" },
|
||||
web: null,
|
||||
note: "Не найден в CSS сайта",
|
||||
},
|
||||
{
|
||||
brand: { oracal: "080M", name: "Тёмно-коричневый", hex: "#5c2e0e" },
|
||||
web: null,
|
||||
note: "Не найден в CSS сайта",
|
||||
},
|
||||
{
|
||||
brand: null,
|
||||
web: { name: "Основной текст", hex: "#464646", count: 3 },
|
||||
note: "Только на сайте — нет Oracal-аналога",
|
||||
},
|
||||
{
|
||||
brand: null,
|
||||
web: { name: "Второстепенный текст", hex: "#949290", count: 4 },
|
||||
note: "Только на сайте — нет Oracal-аналога",
|
||||
},
|
||||
{
|
||||
brand: null,
|
||||
web: { name: "Кремовый фон", hex: "#e9e4d4", count: 1 },
|
||||
note: "Только на сайте — нет Oracal-аналога",
|
||||
},
|
||||
{
|
||||
brand: null,
|
||||
web: { name: "Коралловый", hex: "#ffa39c", count: 2 },
|
||||
note: "Только на сайте — CTA-кнопки, нет в оффлайн-палитре",
|
||||
},
|
||||
{
|
||||
brand: null,
|
||||
web: { name: "Светло-жёлтый фон", hex: "#eef4d1", count: 1 },
|
||||
note: "Только на сайте — фон карточек отзывов",
|
||||
},
|
||||
{
|
||||
brand: null,
|
||||
web: { name: "Светло-зелёный фон", hex: "#f2fee6", count: 1 },
|
||||
note: "Только на сайте — фон секции новостей",
|
||||
},
|
||||
];
|
||||
|
||||
/* ─── Цвета с сайта ────────────────────────────────────────────────── */
|
||||
// Источник: https://perm.oclinica.ru/sites/all/themes/clinic_bootstrap_mobile/css/style.css
|
||||
// Извлечены парсингом CSS: grep + python Counter по property/value, 2026-03-22
|
||||
const WEB_COLORS = [
|
||||
{ name: "Бежевый", hex: "#bf9975", usage: "Основной тёплый акцент, фоны, рамки, текст", count: 12, group: "Акценты" },
|
||||
{ name: "Серо-бирюзовый", hex: "#60959c", usage: "Основной холодный акцент, ссылки", count: 7, group: "Акценты" },
|
||||
{ name: "Бирюзовый", hex: "#63bac3", usage: "Фоны акцентных блоков, иконки", count: 4, group: "Акценты" },
|
||||
{ name: "Бирюзовый средний", hex: "#52b4bd", usage: "Вторичные цветовые акценты", count: 4, group: "Акценты" },
|
||||
{ name: "Основной текст", hex: "#464646", usage: "Цвет основного текста на сайте", count: 3, group: "Текст" },
|
||||
{ name: "Второстепенный текст", hex: "#949290", usage: "Подписи, второстепенный контент", count: 4, group: "Текст" },
|
||||
{ name: "Светло-бирюзовый фон", hex: "#b8e6ed", usage: "Фоны светлых секций с акцентом", count: 1, group: "Фоны" },
|
||||
{ name: "Кремовый фон", hex: "#e9e4d4", usage: "Тёплые фоны секций", count: 1, group: "Фоны" },
|
||||
{ name: "Коралловый", hex: "#ffa39c", usage: "CTA-кнопки («Запишите меня!»), акцентные призывы к действию", count: 2, group: "Акценты" },
|
||||
{ name: "Светло-жёлтый фон", hex: "#eef4d1", usage: "Фон карточек отзывов (секция «Отзывы о нас»)", count: 1, group: "Фоны" },
|
||||
{ name: "Светло-зелёный фон", hex: "#f2fee6", usage: "Фон секции новостей", count: 1, group: "Фоны" },
|
||||
];
|
||||
|
||||
const CONTRAST_PAIRS = [
|
||||
{ fg: "#ffffff", bg: "#5b7b87", label: "Белый на тёмном серо-голубом" },
|
||||
{ fg: "#ffffff", bg: "#1b4c72", label: "Белый на тёмно-синем" },
|
||||
@@ -245,9 +327,59 @@ function ContrastRow({ pair }: { pair: typeof CONTRAST_PAIRS[0] }) {
|
||||
);
|
||||
}
|
||||
|
||||
function WebColorCard({ color }: { color: typeof WEB_COLORS[0] }) {
|
||||
const { r, g, b } = hexToRgb(color.hex);
|
||||
const { h, s, l } = rgbToHsl(r, g, b);
|
||||
const isLight = l > 60;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="rounded-xl overflow-hidden border"
|
||||
style={{ borderColor: "var(--bb-border)" }}
|
||||
>
|
||||
<div
|
||||
className="h-24 flex items-end justify-between px-4 pb-3"
|
||||
style={{ background: color.hex }}
|
||||
>
|
||||
<span
|
||||
className="text-xs font-semibold px-2 py-0.5 rounded"
|
||||
style={{
|
||||
background: isLight ? "rgba(0,0,0,0.12)" : "rgba(255,255,255,0.18)",
|
||||
color: isLight ? "rgba(0,0,0,0.75)" : "rgba(255,255,255,0.9)",
|
||||
}}
|
||||
>
|
||||
{color.group}
|
||||
</span>
|
||||
<span
|
||||
className="text-xs px-2 py-0.5 rounded font-mono"
|
||||
style={{
|
||||
background: isLight ? "rgba(0,0,0,0.12)" : "rgba(255,255,255,0.18)",
|
||||
color: isLight ? "rgba(0,0,0,0.75)" : "rgba(255,255,255,0.9)",
|
||||
}}
|
||||
>
|
||||
×{color.count}
|
||||
</span>
|
||||
</div>
|
||||
<div className="p-4" style={{ background: "var(--bb-content-bg)" }}>
|
||||
<p className="font-medium text-sm mb-1" style={{ color: "var(--bb-text)" }}>
|
||||
{color.name}
|
||||
</p>
|
||||
<p className="text-xs mb-3" style={{ color: "var(--bb-text-muted)" }}>
|
||||
{color.usage}
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
<CopyBadge value={color.hex.toUpperCase()} label="HEX" />
|
||||
<CopyBadge value={`rgb(${r}, ${g}, ${b})`} label="RGB" />
|
||||
<CopyBadge value={`hsl(${h}, ${s}%, ${l}%)`} label="HSL" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/* ─── Экспорт токенов ──────────────────────────────────────────────── */
|
||||
function exportTokens() {
|
||||
const tokens: Record<string, Record<string, string>> = { colors: {} };
|
||||
const tokens: Record<string, Record<string, unknown>> = { colors: {} };
|
||||
BRAND_COLORS.forEach(c => {
|
||||
const key = c.oracal !== "—" ? `brand-${c.oracal.toLowerCase()}` : "brand-white";
|
||||
const { r, g, b } = hexToRgb(c.hex);
|
||||
@@ -268,6 +400,62 @@ function exportTokens() {
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
/* ─── LLM spec text ────────────────────────────────────────────────── */
|
||||
const LLM_COLORS_TEXT = `# ЦВЕТА — LLM-СПЕЦИФИКАЦИЯ
|
||||
# Клиника ухо, горло, нос им. проф. Е.Н.Оленевой
|
||||
# docs/LLM_CONTEXT.md · /foundation/colors · v2.1 · 2026-03-22
|
||||
|
||||
ФИРМЕННЫЕ ЦВЕТА (Oracal)
|
||||
Oracal | Название | HEX | RGB | CSS-переменная | Применение
|
||||
053M | Основной бирюзовый | #7ECFCA | rgb(126,207,202) | --brand-053m | Акцент, CTA-кнопки, иконки, активные состояния
|
||||
073M | Тёмный серо-голубой | #5B7B87 | rgb(91,123,135) | --brand-073m | Тёмный фон, хедер, заголовки навигации
|
||||
066M | Средний бирюзовый | #5BB5AD | rgb(91,181,173) | --brand-066m | Вторичные акценты, фоны секций
|
||||
050M | Тёмно-синий | #1B4C72 | rgb(27,76,114) | --brand-050m | Наружная реклама, полиграфия, заголовки
|
||||
081M | Бежевый | #C4A882 | rgb(196,168,130) | --brand-081m | Форма сотрудников, тёплые акценты
|
||||
080M | Тёмно-коричневый | #5C2E0E | rgb(92,46,14) | --brand-080m | Текст на бежевом, логотип на форме
|
||||
— | Белый | #FFFFFF | rgb(255,255,255) | --brand-white | Фон, инвертированный текст, логотип на тёмных
|
||||
|
||||
ЦВЕТА САЙТА oclinica.ru (CSS: clinic_bootstrap_mobile/css/style.css)
|
||||
Название | HEX | Группа | × | Применение
|
||||
Бежевый | #BF9975 | Акценты | 12| Тёплый акцент, фоны, рамки, текст
|
||||
Серо-бирюзовый | #60959C | Акценты | 7 | Холодный акцент, ссылки
|
||||
Бирюзовый | #63BAC3 | Акценты | 4 | Фоны акцентных блоков, иконки
|
||||
Бирюзовый средний | #52B4BD | Акценты | 4 | Вторичные цветовые акценты
|
||||
Коралловый | #FFA39C | Акценты | 2 | CTA-кнопки («Запишите меня!»)
|
||||
Основной текст | #464646 | Текст | 3 | Цвет основного текста сайта
|
||||
Второстепенный текст | #949290 | Текст | 4 | Подписи, второстепенный контент
|
||||
Светло-бирюзовый фон | #B8E6ED | Фоны | 1 | Фоны светлых секций
|
||||
Кремовый фон | #E9E4D4 | Фоны | 1 | Тёплые фоны секций
|
||||
Светло-жёлтый фон | #EEF4D1 | Фоны | 1 | Фон карточек отзывов
|
||||
Светло-зелёный фон | #F2FEE6 | Фоны | 1 | Фон секции новостей
|
||||
|
||||
СООТВЕТСТВИЕ ORACAL → САЙТ
|
||||
053M #7ECFCA → #63BAC3 (темнее, насыщеннее)
|
||||
073M #5B7B87 → #60959C (светлее)
|
||||
066M #5BB5AD → #52B4BD (смещён в синеву)
|
||||
081M #C4A882 → #BF9975 (темнее, насыщеннее)
|
||||
050M #1B4C72 → не найден в CSS сайта
|
||||
080M #5C2E0E → не найден в CSS сайта
|
||||
|
||||
КОНТРАСТНОСТЬ WCAG 2.1
|
||||
#FFFFFF / #5B7B87 | 4.6:1 | AA PASS | AAA FAIL
|
||||
#FFFFFF / #1B4C72 | 9.3:1 | AA PASS | AAA PASS
|
||||
#FFFFFF / #5C2E0E | 11.2:1 | AA PASS | AAA PASS
|
||||
#FFFFFF / #5BB5AD | 3.0:1 | AA FAIL | AAA FAIL | только крупный текст ≥18pt
|
||||
#111827 / #7ECFCA | 5.8:1 | AA PASS | AAA FAIL
|
||||
#111827 / #C4A882 | 4.8:1 | AA PASS | AAA FAIL
|
||||
#5C2E0E / #C4A882 | 6.7:1 | AA PASS | AAA FAIL
|
||||
|
||||
ПРАВИЛА
|
||||
✓ Только цвета из фирменной палитры
|
||||
✓ Digital → цвета сайта; оффлайн → коды Oracal
|
||||
✓ Текст на цветном фоне: минимум WCAG AA (4.5:1)
|
||||
✓ Белый текст на: 073M (#5B7B87), 050M (#1B4C72), 080M (#5C2E0E)
|
||||
✓ Тёмный текст на: 053M (#7ECFCA), 081M (#C4A882)
|
||||
✕ Произвольные цвета вне фирменной палитры
|
||||
✕ Изменение насыщенности / оттенка фирменных цветов
|
||||
✕ Тёплые и холодные акценты рядом без нейтрального разделителя`.trim();
|
||||
|
||||
/* ─── Страница ─────────────────────────────────────────────────────── */
|
||||
export default function ColorsPage() {
|
||||
return (
|
||||
@@ -348,8 +536,120 @@ export default function ColorsPage() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* 3. Применение */}
|
||||
{/* 3. Цвета с сайта */}
|
||||
<section className="mb-12">
|
||||
<div className="mb-6">
|
||||
<h2 className="text-xl font-semibold" style={{ color: "var(--bb-text)" }}>
|
||||
Цвета с сайта
|
||||
</h2>
|
||||
<p className="mt-1 text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Реальные цвета, используемые на сайте oclinica.ru. Извлечены из CSS темы сайта.
|
||||
Число применений показано в правом углу карточки.
|
||||
</p>
|
||||
<div
|
||||
className="mt-3 flex items-center gap-2 px-3 py-2 rounded-lg text-xs w-fit font-mono"
|
||||
style={{ background: "var(--bb-sidebar-bg)", color: "var(--bb-text-muted)", border: "1px solid var(--bb-border)" }}
|
||||
>
|
||||
<span style={{ color: "var(--brand-053m)" }}>CSS</span>
|
||||
perm.oclinica.ru/sites/all/themes/clinic_bootstrap_mobile/css/style.css
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
{WEB_COLORS.map(c => (
|
||||
<WebColorCard key={c.hex} color={c} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* 4. Соответствие */}
|
||||
<section className="mb-12">
|
||||
<div className="mb-6">
|
||||
<h2 className="text-xl font-semibold" style={{ color: "var(--bb-text)" }}>
|
||||
Соответствие: Oracal → Сайт
|
||||
</h2>
|
||||
<p className="mt-1 text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Сравнение фирменной палитры (Oracal, брендбук) с реальными цветами, применёнными на сайте.
|
||||
Расхождения ожидаемы — цифровая адаптация плёночных цветов под экран.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3">
|
||||
{COLOR_MAPPING.map((row, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex items-center gap-3 p-4 rounded-xl border"
|
||||
style={{ borderColor: "var(--bb-border)", background: "var(--bb-content-bg)" }}
|
||||
>
|
||||
{/* Левая сторона — Oracal */}
|
||||
<div className="flex items-center gap-3 w-56 shrink-0">
|
||||
{row.brand ? (
|
||||
<>
|
||||
<div
|
||||
className="w-10 h-10 rounded-lg shrink-0 border"
|
||||
style={{ background: row.brand.hex, borderColor: "var(--bb-border)" }}
|
||||
/>
|
||||
<div>
|
||||
<p className="text-xs font-semibold font-mono" style={{ color: "var(--bb-text)" }}>
|
||||
{row.brand.hex.toUpperCase()}
|
||||
</p>
|
||||
<p className="text-xs" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Oracal {row.brand.oracal}
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div
|
||||
className="w-10 h-10 rounded-lg shrink-0 flex items-center justify-center text-lg border"
|
||||
style={{ borderColor: "var(--bb-border)", background: "var(--bb-sidebar-bg)" }}
|
||||
>
|
||||
—
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Стрелка */}
|
||||
<div className="shrink-0 text-sm font-bold" style={{ color: "var(--bb-text-muted)" }}>→</div>
|
||||
|
||||
{/* Правая сторона — Сайт */}
|
||||
<div className="flex items-center gap-3 w-56 shrink-0">
|
||||
{row.web ? (
|
||||
<>
|
||||
<div
|
||||
className="w-10 h-10 rounded-lg shrink-0 border"
|
||||
style={{ background: row.web.hex, borderColor: "var(--bb-border)" }}
|
||||
/>
|
||||
<div>
|
||||
<p className="text-xs font-semibold font-mono" style={{ color: "var(--bb-text)" }}>
|
||||
{row.web.hex.toUpperCase()}
|
||||
</p>
|
||||
<p className="text-xs" style={{ color: "var(--bb-text-muted)" }}>
|
||||
{row.web.name} · ×{row.web.count}
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="w-10 h-10 rounded-lg shrink-0 flex items-center justify-center text-lg border"
|
||||
style={{ borderColor: "var(--bb-border)", background: "var(--bb-sidebar-bg)" }}
|
||||
>
|
||||
—
|
||||
</div>
|
||||
<p className="text-xs" style={{ color: "var(--bb-text-muted)" }}>Нет на сайте</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Примечание */}
|
||||
<p className="flex-1 text-xs" style={{ color: "var(--bb-text-muted)" }}>
|
||||
{row.note}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* 5. Применение */}
|
||||
<section className="mb-10">
|
||||
<div className="mb-6">
|
||||
<h2 className="text-xl font-semibold" style={{ color: "var(--bb-text)" }}>
|
||||
Правила применения
|
||||
@@ -410,6 +710,105 @@ export default function ColorsPage() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* 6. LLM-спецификация */}
|
||||
<section className="mb-8">
|
||||
<div className="mb-6">
|
||||
<h2 className="text-xl font-semibold" style={{ color: "var(--bb-text)" }}>
|
||||
LLM-спецификация
|
||||
</h2>
|
||||
<p className="mt-1 text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Машиночитаемые данные раздела для использования AI-ассистентами при разработке
|
||||
дизайна, макетов и кода. Нажмите «Скопировать» чтобы получить полный текст.
|
||||
</p>
|
||||
</div>
|
||||
<LlmBlock
|
||||
path="/foundation/colors"
|
||||
version="v2.1"
|
||||
specText={LLM_COLORS_TEXT}
|
||||
>
|
||||
{/* Фирменные цвета */}
|
||||
<div className="space-y-2">
|
||||
<LlmSection title="Фирменные цвета (Oracal)" />
|
||||
<LlmTable
|
||||
headers={["Oracal", "HEX", "RGB", "CSS-переменная", "Применение"]}
|
||||
rows={BRAND_COLORS.map(c => {
|
||||
const { r, g, b } = hexToRgb(c.hex);
|
||||
return [
|
||||
<span key="o" className="flex items-center gap-1.5">
|
||||
<span className="w-3 h-3 rounded-sm shrink-0 border inline-block" style={{ background: c.hex, borderColor: "var(--bb-border)" }} />
|
||||
{c.oracal}
|
||||
</span>,
|
||||
<span key="h" style={{ color: "var(--bb-text)" }}>{c.hex.toUpperCase()}</span>,
|
||||
`rgb(${r},${g},${b})`,
|
||||
<span key="v" style={{ color: "var(--brand-073m)" }}>{c.cssVar}</span>,
|
||||
c.usage,
|
||||
];
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Цвета сайта */}
|
||||
<div className="space-y-2">
|
||||
<LlmSection title="Цвета сайта oclinica.ru" />
|
||||
<LlmTable
|
||||
headers={["Название", "HEX", "Группа", "×", "Применение"]}
|
||||
rows={WEB_COLORS.map(c => [
|
||||
<span key="n" className="flex items-center gap-1.5">
|
||||
<span className="w-3 h-3 rounded-sm shrink-0 border inline-block" style={{ background: c.hex, borderColor: "var(--bb-border)" }} />
|
||||
{c.name}
|
||||
</span>,
|
||||
<span key="h" style={{ color: "var(--bb-text)" }}>{c.hex.toUpperCase()}</span>,
|
||||
c.group,
|
||||
String(c.count),
|
||||
c.usage,
|
||||
])}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Контрастность */}
|
||||
<div className="space-y-2">
|
||||
<LlmSection title="Контрастность WCAG 2.1" />
|
||||
<LlmTable
|
||||
headers={["Пара (fg / bg)", "Ratio", "AA (4.5:1)", "AAA (7:1)", "AA large (3:1)"]}
|
||||
rows={CONTRAST_PAIRS.map(p => {
|
||||
const ratio = contrastRatio(p.fg, p.bg);
|
||||
const aa = ratio >= 4.5, aaa = ratio >= 7, aal = ratio >= 3;
|
||||
const badge = (pass: boolean) => (
|
||||
<span key={String(pass)} style={{ color: pass ? "#059669" : "#dc2626", fontWeight: 700 }}>
|
||||
{pass ? "PASS" : "FAIL"}
|
||||
</span>
|
||||
);
|
||||
return [
|
||||
<span key="pair" className="flex items-center gap-1.5">
|
||||
<span className="w-3 h-3 rounded-sm border inline-block shrink-0" style={{ background: p.bg, borderColor: "var(--bb-border)" }} />
|
||||
{p.fg} / {p.bg}
|
||||
</span>,
|
||||
`${ratio}:1`,
|
||||
badge(aa),
|
||||
badge(aaa),
|
||||
badge(aal),
|
||||
];
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Правила */}
|
||||
<div className="space-y-2">
|
||||
<LlmSection title="Правила применения" />
|
||||
<LlmRules rules={[
|
||||
{ ok: true, text: "Только цвета из фирменной палитры" },
|
||||
{ ok: true, text: "Digital → цвета сайта; оффлайн → коды Oracal" },
|
||||
{ ok: true, text: "Текст на цветном фоне: минимум WCAG AA (4.5:1)" },
|
||||
{ ok: true, text: "Белый текст на: 073M, 050M, 080M" },
|
||||
{ ok: true, text: "Тёмный текст на: 053M, 081M" },
|
||||
{ ok: false, text: "Произвольные цвета вне фирменной палитры" },
|
||||
{ ok: false, text: "Изменение насыщенности / оттенка фирменных цветов" },
|
||||
{ ok: false, text: "Тёплые + холодные акценты рядом без разделителя" },
|
||||
]} />
|
||||
</div>
|
||||
</LlmBlock>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Metadata } from "next";
|
||||
import Image from "next/image";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Логотип | Брендбук О!Клиника",
|
||||
title: "Логотип. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
||||
};
|
||||
|
||||
function RuleTag({ children }: { children: React.ReactNode }) {
|
||||
@@ -132,31 +132,119 @@ export default function LogoPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 1. Иерархия */}
|
||||
{/* 1. Логотип */}
|
||||
<Section
|
||||
id="hierarchy"
|
||||
title="Иерархия и версии"
|
||||
subtitle="Клиника использует два варианта логотипа в зависимости от контекста применения."
|
||||
title="Логотип"
|
||||
subtitle="Единый логотип клиники. Применяется на всех носителях."
|
||||
>
|
||||
<div className="grid grid-cols-1 gap-8 md:grid-cols-2">
|
||||
<LogoCard
|
||||
src="/logo/logo-transparent.png"
|
||||
alt="Основной логотип Клиника УХО ГОРЛО НОС им. проф. Е.Н. Оленевой"
|
||||
label="Основной логотип"
|
||||
description="Локальные версии по направлениям (ЛОР, аллергология и др.). Применяется в точках контакта с клиентами, на лендингах и сайтах направлений."
|
||||
tag="Точки контакта с клиентом"
|
||||
/>
|
||||
<LogoCard
|
||||
src="/logo/logo-transparent.png"
|
||||
alt="Общий логотип сети клиник"
|
||||
label="Общий логотип"
|
||||
description="Версия сети клиник. Применяется для онлайн и оффлайн коммуникаций с клиентами, во внутренней документации. Допустимо на общем сайте."
|
||||
tag="Сеть клиник · Документация · Сайт"
|
||||
/>
|
||||
<LogoCard
|
||||
src="/logo/logo-transparent.png"
|
||||
alt="Логотип Клиника ухо, горло, нос им. проф. Е.Н. Оленевой"
|
||||
label="Логотип клиники"
|
||||
description="Применяется в точках контакта с клиентами, на лендингах и сайтах направлений. Применяется для онлайн и оффлайн коммуникаций с клиентами, во внутренней документации."
|
||||
tag="Онлайн · Оффлайн · Документация"
|
||||
/>
|
||||
</Section>
|
||||
|
||||
{/* 2. Символика знака */}
|
||||
<Section
|
||||
id="symbol"
|
||||
title="Символика знака"
|
||||
subtitle="О том, что стоит за формой логотипа."
|
||||
>
|
||||
{/* Главный тезис */}
|
||||
<div
|
||||
className="rounded-xl border p-6 mb-6"
|
||||
style={{ borderColor: "var(--bb-border)", background: "var(--bb-sidebar-bg)" }}
|
||||
>
|
||||
<p className="text-sm leading-relaxed" style={{ color: "var(--bb-text)" }}>
|
||||
В знаке клиники — три округлых элемента, расположенных с равной дистанцией от центра.
|
||||
Это создаёт ощущение симметрии, порядка и движения, но без завершённой формы (звезды или круга).
|
||||
</p>
|
||||
<div className="mt-4 flex flex-col gap-2">
|
||||
{[
|
||||
"Сохраняет баланс и лёгкость",
|
||||
"Намекает на естественность и органику",
|
||||
"Не замыкает символ — оставляет «дыхание», открытость",
|
||||
].map(item => (
|
||||
<div key={item} className="flex items-start gap-2.5">
|
||||
<div
|
||||
className="w-1.5 h-1.5 rounded-full mt-1.5 shrink-0"
|
||||
style={{ background: "var(--brand-053m)" }}
|
||||
/>
|
||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>{item}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Три значения */}
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-3 mb-6">
|
||||
{[
|
||||
{
|
||||
num: "1",
|
||||
title: "Незамкнутая симметрия",
|
||||
text: "Это процесс, а не результат. Знак говорит: «мы развиваемся, мы живые, мы не идеальны, но гармоничны».",
|
||||
},
|
||||
{
|
||||
num: "2",
|
||||
title: "Три элемента",
|
||||
text: "Классическая структура уха–горла–носа. Триада равновесия, ритм дыхания, символ доверия и открытости.",
|
||||
},
|
||||
{
|
||||
num: "3",
|
||||
title: "Отсутствие замкнутости",
|
||||
text: "Нет барьера — есть приглашение. Открытая форма передаёт заботу, доступность и человечность.",
|
||||
},
|
||||
].map(item => (
|
||||
<div
|
||||
key={item.num}
|
||||
className="rounded-xl border p-5"
|
||||
style={{ borderColor: "var(--bb-border)" }}
|
||||
>
|
||||
<div
|
||||
className="w-7 h-7 rounded-full flex items-center justify-center text-xs font-bold mb-3"
|
||||
style={{ background: "#e0f5f4", color: "var(--brand-073m)" }}
|
||||
>
|
||||
{item.num}
|
||||
</div>
|
||||
<p className="font-medium text-sm mb-2" style={{ color: "var(--bb-text)" }}>
|
||||
{item.title}
|
||||
</p>
|
||||
<p className="text-sm leading-relaxed" style={{ color: "var(--bb-text-muted)" }}>
|
||||
{item.text}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Почему не звезда */}
|
||||
<div
|
||||
className="rounded-xl border p-5 flex gap-4"
|
||||
style={{ borderColor: "#e0f5f4", background: "#f8fffe" }}
|
||||
>
|
||||
<div
|
||||
className="w-8 h-8 rounded-full flex items-center justify-center shrink-0 mt-0.5"
|
||||
style={{ background: "#e0f5f4" }}
|
||||
>
|
||||
<span className="text-base leading-none">○</span>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium text-sm mb-1.5" style={{ color: "var(--bb-text)" }}>
|
||||
Почему нет законченной звезды
|
||||
</p>
|
||||
<p className="text-sm leading-relaxed" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Звезда — символ завершённости и сакрального порядка. Знак клиники — символ жизни
|
||||
и взаимодействия. Он ближе по духу к живой биоморфной форме (капли, клетки, лепестки),
|
||||
чем к идеальной математической фигуре. Такой дизайн передаёт не «власть формы»,
|
||||
а заботу, мягкость и человечность.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* 2. Цветовые варианты */}
|
||||
{/* 3. Цветовые варианты */}
|
||||
<Section
|
||||
id="variants"
|
||||
title="Цветовые варианты"
|
||||
@@ -237,8 +325,7 @@ export default function LogoPage() {
|
||||
{/* 4. Минимальные размеры */}
|
||||
<Section
|
||||
id="sizes"
|
||||
title="Минимальные размеры"
|
||||
subtitle="Размеры логотипа для размещения на форме сотрудников."
|
||||
title="Размеры логотипа для размещения на форме сотрудников"
|
||||
>
|
||||
<div className="overflow-hidden rounded-xl border" style={{ borderColor: "var(--bb-border)" }}>
|
||||
<table className="w-full text-sm">
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type { Metadata } from "next";
|
||||
import { LlmBlock, LlmSection, LlmTable, LlmRules } from "@/components/llm/LlmBlock";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Типографика | Брендбук О!Клиника",
|
||||
title: "Типографика. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
||||
};
|
||||
|
||||
/* ─── Шкала типографики ────────────────────────────────────────────── */
|
||||
@@ -28,6 +29,60 @@ const FIRA_SCALE = [
|
||||
{ token: "overline", size: "10px / 0.625rem",weight: "600", lh: "1.4", sample: "ФУНДАМЕНТ → 1.4" },
|
||||
];
|
||||
|
||||
/* ─── LLM spec text ────────────────────────────────────────────────── */
|
||||
const LLM_TYPOGRAPHY_TEXT = `# ТИПОГРАФИКА — LLM-СПЕЦИФИКАЦИЯ
|
||||
# Клиника ухо, горло, нос им. проф. Е.Н.Оленевой
|
||||
# docs/LLM_CONTEXT.md · /foundation/typography · v2.0 · 2026-03-22
|
||||
|
||||
ШРИФТЫ БРЕНДА
|
||||
Шрифт | Тип | Применение | CSS
|
||||
DINPro | Бренд | Оффлайн, физические носители (бейджи, таблички, транспорт, форма) | font-family: 'DINPro', Arial, sans-serif
|
||||
Fira Sans| Веб | Сайт, цифровые материалы, брендбук | font-family: 'Fira Sans', sans-serif; Google Fonts; weights: 300/400/500/600
|
||||
|
||||
ПРАВИЛО ВЫБОРА: носитель цифровой → Fira Sans; физический/печатный → DINPro
|
||||
|
||||
DINPro — ШКАЛА (оффлайн)
|
||||
Стиль | font-size | rem | weight | line-height
|
||||
h1 | 40px | 2.5rem | 700 | 1.20
|
||||
h2 | 32px | 2rem | 700 | 1.25
|
||||
h3 | 24px | 1.5rem | 700 | 1.30
|
||||
h4 | 20px | 1.25rem | 700 | 1.35
|
||||
h5 | 16px | 1rem | 700 | 1.40
|
||||
h6 | 14px | 0.875rem | 700 | 1.40
|
||||
|
||||
Fira Sans — ШКАЛА (веб)
|
||||
Стиль | font-size | rem | weight | line-height | letter-spacing
|
||||
h1 | 40px | 2.5rem | 600 | 1.20 | -0.025em
|
||||
h2 | 32px | 2rem | 600 | 1.25 | -0.020em
|
||||
h3 | 24px | 1.5rem | 600 | 1.30 | -0.010em
|
||||
h4 | 20px | 1.25rem | 500 | 1.35 | 0em
|
||||
h5 | 16px | 1rem | 500 | 1.40 | 0em
|
||||
h6 | 14px | 0.875rem | 500 | 1.40 | +0.010em
|
||||
body | 16px | 1rem | 400 | 1.60 | 0em
|
||||
body-sm | 14px | 0.875rem | 400 | 1.60 | 0em
|
||||
caption | 12px | 0.75rem | 400 | 1.50 | +0.020em
|
||||
label | 12px | 0.75rem | 500 | 1.40 | +0.030em
|
||||
overline | 10px | 0.625rem | 600 | 1.40 | +0.100em (uppercase)
|
||||
|
||||
ПРИМЕНЕНИЕ ПО НОСИТЕЛЮ
|
||||
Носитель | Шрифт
|
||||
Сайт, цифровые материалы, брендбук | Fira Sans
|
||||
Форма сотрудников, бейджи | DINPro
|
||||
Вывески, таблички, навигация | DINPro
|
||||
Брендирование транспорта | DINPro
|
||||
Визитки, листовки, полиграфия | DINPro
|
||||
Telegram-бот, пуш-уведомления | Fira Sans (системный)
|
||||
|
||||
ПРАВИЛА
|
||||
✓ H1 — только один на странице
|
||||
✓ Не пропускать уровни заголовков (h1 → h2 → h3)
|
||||
✓ Минимальный размер текста на экране: 12px
|
||||
✓ Кириллица → Fira Sans (не DINPro)
|
||||
✓ Fira Sans: доступные веса 300 / 400 / 500 / 600
|
||||
✕ DINPro на сайте без явного согласования дизайнера
|
||||
✕ Light (300) для текста < 14px
|
||||
✕ Смешивать DINPro и Fira Sans на одном носителе`.trim();
|
||||
|
||||
/* ─── Компоненты ───────────────────────────────────────────────────── */
|
||||
function Section({
|
||||
title,
|
||||
@@ -344,6 +399,110 @@ export default function TypographyPage() {
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* LLM-спецификация */}
|
||||
<section className="mb-8">
|
||||
<div className="mb-6">
|
||||
<h2 className="text-xl font-semibold" style={{ color: "var(--bb-text)" }}>
|
||||
LLM-спецификация
|
||||
</h2>
|
||||
<p className="mt-1 text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Машиночитаемые данные раздела для использования AI-ассистентами при разработке
|
||||
дизайна, макетов и кода. Нажмите «Скопировать» чтобы получить полный текст.
|
||||
</p>
|
||||
</div>
|
||||
<LlmBlock
|
||||
path="/foundation/typography"
|
||||
version="v2.0"
|
||||
specText={LLM_TYPOGRAPHY_TEXT}
|
||||
>
|
||||
{/* Шрифты */}
|
||||
<div className="space-y-2">
|
||||
<LlmSection title="Шрифты бренда" />
|
||||
<LlmTable
|
||||
headers={["Шрифт", "Тип", "Применение", "CSS font-family"]}
|
||||
rows={[
|
||||
[
|
||||
<span key="d" style={{ color: "var(--bb-text)", fontWeight: 600 }}>DINPro</span>,
|
||||
"Бренд",
|
||||
"Оффлайн, физические носители (бейджи, таблички, транспорт, форма)",
|
||||
"'DINPro', Arial, sans-serif",
|
||||
],
|
||||
[
|
||||
<span key="f" style={{ color: "var(--bb-text)", fontWeight: 600 }}>Fira Sans</span>,
|
||||
"Веб",
|
||||
"Сайт, цифровые материалы, брендбук",
|
||||
"'Fira Sans', sans-serif · Google Fonts · 300/400/500/600",
|
||||
],
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* DINPro шкала */}
|
||||
<div className="space-y-2">
|
||||
<LlmSection title="DINPro — шкала (оффлайн)" />
|
||||
<LlmTable
|
||||
headers={["Стиль", "font-size", "rem", "weight", "line-height"]}
|
||||
rows={DIN_SCALE.map(r => {
|
||||
const [px, rem] = r.size.split(" / ");
|
||||
return [r.token, px, rem, r.weight, r.lh];
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Fira Sans шкала */}
|
||||
<div className="space-y-2">
|
||||
<LlmSection title="Fira Sans — шкала (веб)" />
|
||||
<LlmTable
|
||||
headers={["Стиль", "font-size", "rem", "weight", "line-height", "letter-spacing"]}
|
||||
rows={[
|
||||
["h1", "40px", "2.5rem", "600", "1.20", "-0.025em"],
|
||||
["h2", "32px", "2rem", "600", "1.25", "-0.020em"],
|
||||
["h3", "24px", "1.5rem", "600", "1.30", "-0.010em"],
|
||||
["h4", "20px", "1.25rem", "500", "1.35", "0em"],
|
||||
["h5", "16px", "1rem", "500", "1.40", "0em"],
|
||||
["h6", "14px", "0.875rem", "500", "1.40", "+0.010em"],
|
||||
["body", "16px", "1rem", "400", "1.60", "0em"],
|
||||
["body-sm", "14px", "0.875rem", "400", "1.60", "0em"],
|
||||
["caption", "12px", "0.75rem", "400", "1.50", "+0.020em"],
|
||||
["label", "12px", "0.75rem", "500", "1.40", "+0.030em"],
|
||||
["overline", "10px", "0.625rem", "600", "1.40", "+0.100em"],
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Применение */}
|
||||
<div className="space-y-2">
|
||||
<LlmSection title="Применение по носителю" />
|
||||
<LlmTable
|
||||
headers={["Носитель", "Шрифт"]}
|
||||
rows={[
|
||||
["Сайт, цифровые материалы, брендбук", <span key="fs" style={{ color: "var(--brand-073m)" }}>Fira Sans</span>],
|
||||
["Форма сотрудников, бейджи", <span key="d1" style={{ color: "var(--brand-073m)" }}>DINPro</span>],
|
||||
["Вывески, таблички, навигация", <span key="d2" style={{ color: "var(--brand-073m)" }}>DINPro</span>],
|
||||
["Брендирование транспорта", <span key="d3" style={{ color: "var(--brand-073m)" }}>DINPro</span>],
|
||||
["Визитки, листовки, полиграфия", <span key="d4" style={{ color: "var(--brand-073m)" }}>DINPro</span>],
|
||||
["Telegram-бот, пуш-уведомления", <span key="fs2" style={{ color: "var(--brand-073m)" }}>Fira Sans (системный)</span>],
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Правила */}
|
||||
<div className="space-y-2">
|
||||
<LlmSection title="Правила применения" />
|
||||
<LlmRules rules={[
|
||||
{ ok: true, text: "H1 — только один на странице" },
|
||||
{ ok: true, text: "Не пропускать уровни заголовков (h1 → h2 → h3)" },
|
||||
{ ok: true, text: "Минимальный размер текста на экране: 12px" },
|
||||
{ ok: true, text: "Кириллица → Fira Sans (не DINPro)" },
|
||||
{ ok: true, text: "Fira Sans: доступные веса 300 / 400 / 500 / 600" },
|
||||
{ ok: false, text: "DINPro на сайте без явного согласования дизайнера" },
|
||||
{ ok: false, text: "Light (300) для текста < 14px" },
|
||||
{ ok: false, text: "Смешивать DINPro и Fira Sans на одном носителе" },
|
||||
]} />
|
||||
</div>
|
||||
</LlmBlock>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,79 +31,6 @@ function Section({
|
||||
);
|
||||
}
|
||||
|
||||
/* Компонент бейджа (масштабированный макет) */
|
||||
function BadgeMockup({
|
||||
variant,
|
||||
name,
|
||||
role,
|
||||
}: {
|
||||
variant: "light" | "dark";
|
||||
name: string;
|
||||
role: string;
|
||||
}) {
|
||||
const isDark = variant === "dark";
|
||||
/* 70×30 мм → пропорция 7:3. Отображаем в 280×120px */
|
||||
return (
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<div
|
||||
className="rounded-lg flex items-center px-5 gap-4"
|
||||
style={{
|
||||
width: 280,
|
||||
height: 120,
|
||||
background: isDark ? "var(--brand-073m)" : "var(--brand-081m)",
|
||||
border: isDark ? "none" : "1px solid #d1d5db",
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
{/* Логотип */}
|
||||
<Image
|
||||
src="/logo/logo-transparent.png"
|
||||
alt="Логотип"
|
||||
width={72}
|
||||
height={26}
|
||||
className="object-contain shrink-0"
|
||||
style={{
|
||||
filter: isDark
|
||||
? "brightness(0) invert(1)"
|
||||
: "brightness(0) sepia(1) saturate(2) hue-rotate(330deg) brightness(0.45)",
|
||||
}}
|
||||
/>
|
||||
{/* Разделитель */}
|
||||
<div
|
||||
className="self-stretch w-px mx-1"
|
||||
style={{ background: isDark ? "rgba(255,255,255,0.25)" : "rgba(92,46,14,0.2)" }}
|
||||
/>
|
||||
{/* Текст */}
|
||||
<div>
|
||||
<p
|
||||
className="font-bold leading-tight"
|
||||
style={{
|
||||
fontFamily: "'DINPro', Arial, sans-serif",
|
||||
fontSize: 13,
|
||||
color: isDark ? "#ffffff" : "#5c2e0e",
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
</p>
|
||||
<p
|
||||
className="leading-tight mt-0.5"
|
||||
style={{
|
||||
fontFamily: "'DINPro', Arial, sans-serif",
|
||||
fontSize: 10,
|
||||
color: isDark ? "rgba(255,255,255,0.7)" : "rgba(92,46,14,0.7)",
|
||||
}}
|
||||
>
|
||||
{role}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs" style={{ color: "var(--bb-text-muted)" }}>
|
||||
{isDark ? "Тёмный вариант (серо-голубой)" : "Светлый вариант (бежевый)"}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function BadgesPage() {
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto px-8 py-10">
|
||||
@@ -120,24 +47,76 @@ export default function BadgesPage() {
|
||||
Бейджи сотрудников
|
||||
</h1>
|
||||
<p className="text-base max-w-2xl" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Именные бейджи для идентификации сотрудников клиники. Размер 70×30 мм.
|
||||
Два цветовых варианта в зависимости от должности.
|
||||
Именные бейджи для идентификации сотрудников клиники. Размер 70×30 мм,
|
||||
магнитное крепление. Белый фон, чёрный текст.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Размеры и технические требования */}
|
||||
{/* Фотографии */}
|
||||
<Section
|
||||
title="Образцы бейджей"
|
||||
subtitle="Фотографии реальных бейджей из брендбука клиники."
|
||||
>
|
||||
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2">
|
||||
{/* Лицевая сторона */}
|
||||
<div>
|
||||
<div
|
||||
className="rounded-xl overflow-hidden border mb-4"
|
||||
style={{ borderColor: "var(--bb-border)" }}
|
||||
>
|
||||
<Image
|
||||
src="/offline/badges/badge-2.jpeg"
|
||||
alt="Лицевая сторона бейджа: Лебединская Елена Александровна, врач оториноларинголог"
|
||||
width={690}
|
||||
height={347}
|
||||
className="w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<p className="font-medium text-sm mb-1" style={{ color: "var(--bb-text)" }}>
|
||||
Лицевая сторона
|
||||
</p>
|
||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Белый фон, скруглённые углы. ФИО — крупный шрифт,
|
||||
должности — мелкий. Металлическая рамка.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Оборотная сторона */}
|
||||
<div>
|
||||
<div
|
||||
className="rounded-xl overflow-hidden border mb-4"
|
||||
style={{ borderColor: "var(--bb-border)" }}
|
||||
>
|
||||
<Image
|
||||
src="/offline/badges/badge-1.jpeg"
|
||||
alt="Оборотная сторона бейджа с магнитным креплением"
|
||||
width={657}
|
||||
height={369}
|
||||
className="w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<p className="font-medium text-sm mb-1" style={{ color: "var(--bb-text)" }}>
|
||||
Оборотная сторона
|
||||
</p>
|
||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Чёрный пластик, магнитное крепление (CAUTION MAGNETIC).
|
||||
Не требует проколов в одежде.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* Технические параметры */}
|
||||
<Section
|
||||
title="Технические параметры"
|
||||
subtitle="Единый стандарт для всех сотрудников клиники."
|
||||
>
|
||||
<div
|
||||
className="grid grid-cols-2 gap-4 sm:grid-cols-4 mb-6"
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-4 sm:grid-cols-4 mb-6">
|
||||
{[
|
||||
{ label: "Ширина", value: "70 мм" },
|
||||
{ label: "Высота", value: "30 мм" },
|
||||
{ label: "Материал", value: "ПВХ / металл" },
|
||||
{ label: "Крепление", value: "Булавка / клипса" },
|
||||
{ label: "Материал", value: "Металл / ПВХ" },
|
||||
{ label: "Крепление", value: "Магнитное" },
|
||||
].map(({ label, value }) => (
|
||||
<div
|
||||
key={label}
|
||||
@@ -155,29 +134,10 @@ export default function BadgesPage() {
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* Варианты */}
|
||||
<Section
|
||||
title="Варианты бейджей"
|
||||
subtitle="Макет бейджа (масштаб: 4× от реального размера 70×30 мм)."
|
||||
>
|
||||
<div className="flex flex-wrap gap-8 items-start">
|
||||
<BadgeMockup
|
||||
variant="light"
|
||||
name="Иванова А.В."
|
||||
role="Врач-оториноларинголог"
|
||||
/>
|
||||
<BadgeMockup
|
||||
variant="dark"
|
||||
name="Петров К.С."
|
||||
role="Главный врач"
|
||||
/>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* Состав текста */}
|
||||
<Section
|
||||
title="Состав текста на бейдже"
|
||||
subtitle="Строгий порядок элементов. Шрифт — DINPro."
|
||||
subtitle="Строгий порядок элементов."
|
||||
>
|
||||
<div
|
||||
className="overflow-hidden rounded-xl border"
|
||||
@@ -186,7 +146,7 @@ export default function BadgesPage() {
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr style={{ background: "var(--bb-sidebar-bg)" }}>
|
||||
{["Элемент", "Содержание", "Шрифт / Размер", "Позиция"].map(h => (
|
||||
{["Элемент", "Содержание", "Оформление"].map(h => (
|
||||
<th
|
||||
key={h}
|
||||
className="text-left px-5 py-3 font-medium"
|
||||
@@ -199,11 +159,10 @@ export default function BadgesPage() {
|
||||
</thead>
|
||||
<tbody>
|
||||
{[
|
||||
["Логотип", "Логотип клиники", "PNG / SVG", "Левая часть, по центру по высоте"],
|
||||
["Разделитель", "Вертикальная линия", "1px, 40% прозрачность", "Между логотипом и текстом"],
|
||||
["ФИО", "Фамилия И.О.", "DINPro Bold 13px", "Правая часть, верхняя строка"],
|
||||
["Должность", "Полное название должности", "DINPro Regular 10px", "Правая часть, нижняя строка"],
|
||||
].map(([el, content, font, pos]) => (
|
||||
["ФИО", "Фамилия Имя Отчество", "Крупный шрифт, первая строка"],
|
||||
["Должность", "Основная должность", "Мелкий шрифт, вторая строка"],
|
||||
["Специализация", "Специализация / учёная степень (если есть)", "Мелкий шрифт, третья строка"],
|
||||
].map(([el, content, style]) => (
|
||||
<tr
|
||||
key={el}
|
||||
className="border-t"
|
||||
@@ -211,60 +170,33 @@ export default function BadgesPage() {
|
||||
>
|
||||
<td className="px-5 py-3 font-medium" style={{ color: "var(--bb-text)" }}>{el}</td>
|
||||
<td className="px-5 py-3" style={{ color: "var(--bb-text-muted)" }}>{content}</td>
|
||||
<td className="px-5 py-3 font-mono text-xs" style={{ color: "var(--bb-text-muted)" }}>{font}</td>
|
||||
<td className="px-5 py-3" style={{ color: "var(--bb-text-muted)" }}>{pos}</td>
|
||||
<td className="px-5 py-3" style={{ color: "var(--bb-text-muted)" }}>{style}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* Цветовые варианты */}
|
||||
<Section
|
||||
title="Применение вариантов"
|
||||
subtitle="Выбор варианта зависит от должности сотрудника."
|
||||
>
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
||||
{[
|
||||
{
|
||||
variant: "Светлый (бежевый)",
|
||||
color: "#c4a882",
|
||||
usage: "Медицинский персонал, санитарки, технический персонал",
|
||||
oracal: "081M",
|
||||
},
|
||||
{
|
||||
variant: "Тёмный (серо-голубой)",
|
||||
color: "#5b7b87",
|
||||
usage: "Административный персонал, менеджеры, главный врач",
|
||||
oracal: "073M",
|
||||
},
|
||||
].map(item => (
|
||||
<div
|
||||
key={item.variant}
|
||||
className="flex gap-4 p-4 rounded-xl border"
|
||||
style={{ borderColor: "var(--bb-border)" }}
|
||||
>
|
||||
<div
|
||||
className="w-8 h-8 rounded-lg shrink-0 mt-0.5"
|
||||
style={{ background: item.color }}
|
||||
/>
|
||||
<div>
|
||||
<p className="font-medium text-sm mb-1" style={{ color: "var(--bb-text)" }}>
|
||||
{item.variant}
|
||||
</p>
|
||||
<p className="text-xs mb-1.5" style={{ color: "var(--bb-text-muted)" }}>
|
||||
{item.usage}
|
||||
</p>
|
||||
<span
|
||||
className="text-[10px] font-mono px-1.5 py-0.5 rounded"
|
||||
style={{ background: "#f3f4f6", color: "#374151" }}
|
||||
>
|
||||
Oracal {item.oracal}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{/* Пример из фото */}
|
||||
<div
|
||||
className="mt-4 rounded-xl border p-4 flex items-start gap-3"
|
||||
style={{ borderColor: "var(--bb-border)", background: "var(--bb-sidebar-bg)" }}
|
||||
>
|
||||
<div
|
||||
className="w-1.5 self-stretch rounded-full shrink-0"
|
||||
style={{ background: "var(--brand-053m)" }}
|
||||
/>
|
||||
<div>
|
||||
<p className="text-xs font-medium mb-1" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Пример из брендбука
|
||||
</p>
|
||||
<p className="text-sm font-semibold" style={{ color: "var(--bb-text)" }}>
|
||||
Лебединская Елена Александровна
|
||||
</p>
|
||||
<p className="text-xs mt-0.5" style={{ color: "var(--bb-text-muted)" }}>
|
||||
врач оториноларинголог · ведущий хирург · кандидат медицинских наук
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type { Metadata } from "next";
|
||||
import Image from "next/image";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Внутренняя навигация | Брендбук О!Клиника",
|
||||
title: "Внутренняя навигация. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
||||
};
|
||||
|
||||
function Section({
|
||||
@@ -30,74 +31,6 @@ function Section({
|
||||
);
|
||||
}
|
||||
|
||||
/* Макет настенной таблички */
|
||||
function SignMockup({
|
||||
type,
|
||||
text,
|
||||
subtext,
|
||||
bgColor,
|
||||
textColor,
|
||||
accentColor,
|
||||
size,
|
||||
}: {
|
||||
type: string;
|
||||
text: string;
|
||||
subtext?: string;
|
||||
bgColor: string;
|
||||
textColor: string;
|
||||
accentColor: string;
|
||||
size: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex flex-col items-start gap-2">
|
||||
<div
|
||||
className="rounded-lg px-6 py-4 flex items-center gap-4"
|
||||
style={{
|
||||
background: bgColor,
|
||||
width: 260,
|
||||
border: bgColor === "#ffffff" ? "1px solid #e5e7eb" : "none",
|
||||
}}
|
||||
>
|
||||
{/* Цветовая полоса */}
|
||||
<div
|
||||
className="w-1.5 self-stretch rounded-full"
|
||||
style={{ background: accentColor, minHeight: 32 }}
|
||||
/>
|
||||
<div>
|
||||
<p
|
||||
style={{
|
||||
fontFamily: "'DINPro', Arial, sans-serif",
|
||||
fontSize: 14,
|
||||
fontWeight: 700,
|
||||
color: textColor,
|
||||
lineHeight: 1.3,
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</p>
|
||||
{subtext && (
|
||||
<p
|
||||
style={{
|
||||
fontFamily: "'DINPro', Arial, sans-serif",
|
||||
fontSize: 10,
|
||||
color: textColor,
|
||||
opacity: 0.65,
|
||||
marginTop: 3,
|
||||
}}
|
||||
>
|
||||
{subtext}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="pl-1">
|
||||
<p className="text-xs font-medium" style={{ color: "var(--bb-text)" }}>{type}</p>
|
||||
<p className="text-xs" style={{ color: "var(--bb-text-muted)" }}>{size}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function NavigationPage() {
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto px-8 py-10">
|
||||
@@ -115,52 +48,111 @@ export default function NavigationPage() {
|
||||
</h1>
|
||||
<p className="text-base max-w-2xl" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Система навигационных табличек и указателей внутри клиники.
|
||||
Единый стиль с фирменными цветами и шрифтом DINPro.
|
||||
На оргстекле, наклейки из плёнок Оракл 053M и 073M.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Шаблоны табличек */}
|
||||
{/* Макеты таблечек */}
|
||||
<Section
|
||||
title="Типы табличек"
|
||||
subtitle="Четыре базовых шаблона для разных зон клиники."
|
||||
title="Макеты навигационных табличек"
|
||||
subtitle="Два типа: табличка кабинета с бирюзовым заголовком и карточка врача с логотипом и QR-кодом."
|
||||
>
|
||||
<div className="flex flex-wrap gap-6">
|
||||
<SignMockup
|
||||
type="Кабинет врача"
|
||||
text="Кабинет № 101"
|
||||
subtext="Оториноларингология"
|
||||
bgColor="#ffffff"
|
||||
textColor="#1b4c72"
|
||||
accentColor="#7ecfca"
|
||||
size="200 × 80 мм"
|
||||
/>
|
||||
<SignMockup
|
||||
type="Направляющий указатель"
|
||||
text="→ Регистратура"
|
||||
subtext="2-й этаж"
|
||||
bgColor="#5b7b87"
|
||||
textColor="#ffffff"
|
||||
accentColor="#7ecfca"
|
||||
size="300 × 80 мм"
|
||||
/>
|
||||
<SignMockup
|
||||
type="Зона ожидания"
|
||||
text="Зона ожидания"
|
||||
subtext="Пожалуйста, соблюдайте тишину"
|
||||
bgColor="#e0f5f4"
|
||||
textColor="#1b4c72"
|
||||
accentColor="#5bb5ad"
|
||||
size="250 × 80 мм"
|
||||
/>
|
||||
<SignMockup
|
||||
type="Запрещающий"
|
||||
text="Вход только для персонала"
|
||||
bgColor="#1b4c72"
|
||||
textColor="#ffffff"
|
||||
accentColor="#c4a882"
|
||||
size="250 × 60 мм"
|
||||
<div className="max-w-lg">
|
||||
<div
|
||||
className="rounded-xl overflow-hidden border mb-4"
|
||||
style={{ borderColor: "var(--bb-border)" }}
|
||||
>
|
||||
<Image
|
||||
src="/offline/navigation/nav-mockup-signs.jpeg"
|
||||
alt="Макеты навигационных табличек: Кабинет 04 с бирюзовым заголовком и карточка врача Лебединской"
|
||||
width={620}
|
||||
height={570}
|
||||
className="w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<p className="font-medium text-sm mb-1" style={{ color: "var(--bb-text)" }}>
|
||||
Макет из брендбука
|
||||
</p>
|
||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Табличка «Кабинет 04»: бирюзовый заголовок (Oracal 053M), специализация и ФИО врачей.
|
||||
Карточка врача: логотип клиники, имя, должности, QR-код на страницу врача.
|
||||
</p>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* Нумерация дверей */}
|
||||
<Section
|
||||
title="Нумерация кабинетов на дверях"
|
||||
subtitle="Номер кабинета размещается непосредственно на двери — крупный шрифт, плёнка Oracal."
|
||||
>
|
||||
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2">
|
||||
<div>
|
||||
<div
|
||||
className="rounded-xl overflow-hidden border mb-4"
|
||||
style={{ borderColor: "var(--bb-border)" }}
|
||||
>
|
||||
<Image
|
||||
src="/offline/navigation/nav-door-31.png"
|
||||
alt="Белая дверь кабинета 31 с крупным номером из плёнки"
|
||||
width={770}
|
||||
height={963}
|
||||
className="w-full object-cover"
|
||||
style={{ maxHeight: 420, objectPosition: "top" }}
|
||||
/>
|
||||
</div>
|
||||
<p className="font-medium text-sm mb-1" style={{ color: "var(--bb-text)" }}>
|
||||
Кабинет 31 — белая дверь
|
||||
</p>
|
||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Крупный номер в верхней части двери. Тёмная плёнка на белом фоне.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div
|
||||
className="rounded-xl overflow-hidden border mb-4"
|
||||
style={{ borderColor: "var(--bb-border)" }}
|
||||
>
|
||||
<Image
|
||||
src="/offline/navigation/nav-3.jpeg"
|
||||
alt="Серая дверь кабинета 13 с крупным номером"
|
||||
width={800}
|
||||
height={463}
|
||||
className="w-full object-cover"
|
||||
style={{ maxHeight: 420, objectPosition: "left" }}
|
||||
/>
|
||||
</div>
|
||||
<p className="font-medium text-sm mb-1" style={{ color: "var(--bb-text)" }}>
|
||||
Кабинет 13 — серая дверь
|
||||
</p>
|
||||
<p className="text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Тёмная дверь с крупным номером. Рядом — табличка кабинета на стене.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* Указатели по этажам */}
|
||||
<Section
|
||||
title="Указатели по этажам"
|
||||
subtitle="Навигационные панели в холлах — показывают расположение кабинетов и специализации на каждом этаже."
|
||||
>
|
||||
<div
|
||||
className="rounded-xl overflow-hidden border"
|
||||
style={{ borderColor: "var(--bb-border)" }}
|
||||
>
|
||||
<Image
|
||||
src="/offline/navigation/nav-render-p13.jpeg"
|
||||
alt="Навигационные панели по этажам: кабинеты 01-06, 21-25, 31-37, 41-45"
|
||||
width={2105}
|
||||
height={1489}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
<p className="mt-3 text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Слева — указатель с этажом и направлением, справа — панель с полным перечнем кабинетов.
|
||||
Активный этаж выделяется бирюзовым фоном (Oracal 053M).
|
||||
</p>
|
||||
</Section>
|
||||
|
||||
{/* Технические требования */}
|
||||
@@ -188,12 +180,10 @@ export default function NavigationPage() {
|
||||
</thead>
|
||||
<tbody>
|
||||
{[
|
||||
["Основной материал", "ПВХ 3мм / ПС зеркальный / акрил"],
|
||||
["Покрытие фона", "Oracal плёнка (053M / 073M / 050M)"],
|
||||
["Шрифт", "DINPro Bold / Regular (DXF для фрезеровки)"],
|
||||
["Крепление", "Двусторонний скотч / шурупы с дистанционным держателем"],
|
||||
["Толщина букв (фрезеровка)", "3 мм от основы"],
|
||||
["Минимальный размер текста", "10 мм по высоте"],
|
||||
["Основной материал", "Оргстекло"],
|
||||
["Покрытие", "Наклейка из плёнок Oracal"],
|
||||
["Шрифт", "DINPro Bold / Regular"],
|
||||
["Крепление", "Дистанционные держатели"],
|
||||
].map(([param, value]) => (
|
||||
<tr
|
||||
key={param}
|
||||
@@ -218,24 +208,25 @@ export default function NavigationPage() {
|
||||
title="Цвета Oracal для навигации"
|
||||
subtitle="Допустимые цвета плёнки по коду Oracal."
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-4 sm:grid-cols-4">
|
||||
<div className="flex gap-6">
|
||||
{[
|
||||
{ code: "053M", hex: "#7ecfca", name: "Акцент / полоса" },
|
||||
{ code: "073M", hex: "#5b7b87", name: "Фон указателей" },
|
||||
{ code: "050M", hex: "#1b4c72", name: "Фон запрещающих" },
|
||||
{ code: "081M", hex: "#c4a882", name: "Акцент на тёмном" },
|
||||
{ code: "053M", hex: "#7ecfca", name: "Заголовок таблички / активный этаж" },
|
||||
{ code: "073M", hex: "#5b7b87", name: "Дополнительный акцент" },
|
||||
].map(c => (
|
||||
<div
|
||||
key={c.code}
|
||||
className="rounded-xl overflow-hidden border"
|
||||
style={{ borderColor: "var(--bb-border)" }}
|
||||
className="flex items-center gap-4 rounded-xl border p-4"
|
||||
style={{ borderColor: "var(--bb-border)", background: "var(--bb-sidebar-bg)" }}
|
||||
>
|
||||
<div className="h-16" style={{ background: c.hex }} />
|
||||
<div className="p-3">
|
||||
<p className="font-medium text-xs" style={{ color: "var(--bb-text)" }}>
|
||||
<div
|
||||
className="w-12 h-12 rounded-lg shrink-0"
|
||||
style={{ background: c.hex }}
|
||||
/>
|
||||
<div>
|
||||
<p className="font-medium text-sm" style={{ color: "var(--bb-text)" }}>
|
||||
Oracal {c.code}
|
||||
</p>
|
||||
<p className="text-xs" style={{ color: "var(--bb-text-muted)" }}>
|
||||
<p className="text-xs mt-0.5" style={{ color: "var(--bb-text-muted)" }}>
|
||||
{c.name}
|
||||
</p>
|
||||
<p className="font-mono text-xs mt-1" style={{ color: "var(--bb-text-muted)" }}>
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Metadata } from "next";
|
||||
import Image from "next/image";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Печатные материалы | Брендбук О!Клиника",
|
||||
title: "Печатные материалы. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
||||
};
|
||||
|
||||
function Section({
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Metadata } from "next";
|
||||
import Image from "next/image";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Брендирование транспорта | Брендбук О!Клиника",
|
||||
title: "Брендирование транспорта. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой",
|
||||
};
|
||||
|
||||
function Section({
|
||||
@@ -31,101 +31,6 @@ function Section({
|
||||
);
|
||||
}
|
||||
|
||||
/* Макет трамвая (упрощённый силуэт) */
|
||||
function TramMockup() {
|
||||
return (
|
||||
<div
|
||||
className="relative rounded-2xl overflow-hidden"
|
||||
style={{
|
||||
background: "#f8f9fa",
|
||||
border: "1px solid var(--bb-border)",
|
||||
padding: "32px 24px",
|
||||
}}
|
||||
>
|
||||
{/* Кузов трамвая */}
|
||||
<div
|
||||
className="relative mx-auto rounded-xl overflow-hidden"
|
||||
style={{
|
||||
width: "100%",
|
||||
maxWidth: 560,
|
||||
height: 160,
|
||||
background: "#ffffff",
|
||||
border: "2px solid #e5e7eb",
|
||||
}}
|
||||
>
|
||||
{/* Верхняя полоса — бирюзовая */}
|
||||
<div
|
||||
className="absolute top-0 left-0 right-0"
|
||||
style={{ height: 28, background: "#7ecfca" }}
|
||||
/>
|
||||
|
||||
{/* Основная бежевая полоса */}
|
||||
<div
|
||||
className="absolute left-0 right-0"
|
||||
style={{ top: 28, height: 76, background: "#c4a882" }}
|
||||
/>
|
||||
|
||||
{/* Нижняя полоса — серо-голубая */}
|
||||
<div
|
||||
className="absolute left-0 right-0"
|
||||
style={{ top: 104, height: 56, background: "#5b7b87" }}
|
||||
/>
|
||||
|
||||
{/* Логотип по центру бежевой полосы */}
|
||||
<div
|
||||
className="absolute flex items-center justify-center"
|
||||
style={{ top: 28, left: 0, right: 0, height: 76 }}
|
||||
>
|
||||
<Image
|
||||
src="/logo/logo-transparent.png"
|
||||
alt="Логотип на трамвае"
|
||||
width={180}
|
||||
height={64}
|
||||
className="object-contain"
|
||||
style={{
|
||||
filter: "brightness(0) sepia(1) saturate(2) hue-rotate(330deg) brightness(0.45)",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Окна (декоративные) */}
|
||||
{[80, 180, 280, 380, 460].map(x => (
|
||||
<div
|
||||
key={x}
|
||||
className="absolute rounded"
|
||||
style={{
|
||||
left: x,
|
||||
top: 40,
|
||||
width: 50,
|
||||
height: 44,
|
||||
background: "rgba(255,255,255,0.6)",
|
||||
border: "1px solid rgba(255,255,255,0.8)",
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* Колёса (декоративные) */}
|
||||
{[60, 200, 360, 500].map(x => (
|
||||
<div
|
||||
key={x}
|
||||
className="absolute rounded-full"
|
||||
style={{
|
||||
left: x,
|
||||
bottom: -8,
|
||||
width: 24,
|
||||
height: 24,
|
||||
background: "#374151",
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<p className="mt-4 text-center text-xs" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Схема цветового решения (превью, не финальный макет)
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function TransportPage() {
|
||||
return (
|
||||
@@ -151,9 +56,20 @@ export default function TransportPage() {
|
||||
{/* Макет */}
|
||||
<Section
|
||||
title="Макет трамвая"
|
||||
subtitle="Трёхполосная схема брендирования: верхняя бирюзовая, центральная бежевая с логотипом, нижняя серо-голубая."
|
||||
subtitle="Вид с обеих сторон: бирюзовая полоса, логотип клиники, фотографии пациентов и врачей, контактная информация."
|
||||
>
|
||||
<TramMockup />
|
||||
<div
|
||||
className="rounded-xl overflow-hidden border"
|
||||
style={{ borderColor: "var(--bb-border)" }}
|
||||
>
|
||||
<Image
|
||||
src="/offline/transport/tram-mockup.jpeg"
|
||||
alt="Макет брендирования трамвая: вид спереди и сзади с логотипом Клиники ухо горло нос им. проф. Е.Н.Оленевой"
|
||||
width={1884}
|
||||
height={977}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* Цветовая схема */}
|
||||
|
||||
@@ -47,92 +47,113 @@ export default function UniformPage() {
|
||||
Форма сотрудников
|
||||
</h1>
|
||||
<p className="text-base max-w-2xl" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Фирменная медицинская одежда сотрудников клиники. Бежевый костюм
|
||||
с логотипом клиники на левой стороне груди.
|
||||
Фирменная медицинская одежда сотрудников клиники. Два цветовых варианта:
|
||||
бежевый с коричневым логотипом и синий с белым логотипом.
|
||||
Логотип размещается на левой стороне груди.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Описание костюма */}
|
||||
{/* Фотографии вариантов */}
|
||||
<Section
|
||||
title="Описание комплекта"
|
||||
subtitle="Стандартная форма для всех сотрудников клиники."
|
||||
title="Варианты формы"
|
||||
subtitle="Фотографии реальной формы сотрудников с логотипом клиники."
|
||||
>
|
||||
<div
|
||||
className="rounded-xl border p-6"
|
||||
style={{ borderColor: "var(--bb-border)", background: "var(--bb-sidebar-bg)" }}
|
||||
>
|
||||
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2">
|
||||
<div>
|
||||
<p className="font-medium text-sm mb-3" style={{ color: "var(--bb-text)" }}>
|
||||
Состав формы
|
||||
</p>
|
||||
<ul className="space-y-2">
|
||||
{["Медицинский костюм (куртка + брюки)", "Цвет: бежевый (Oracal 081M)", "Материал: медицинская ткань", "Логотип вышит или нанесён термопечатью"].map(item => (
|
||||
<li key={item} className="flex items-start gap-2 text-sm" style={{ color: "var(--bb-text-muted)" }}>
|
||||
<span style={{ color: "var(--brand-053m)" }}>•</span> {item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2">
|
||||
{/* Бежевый вариант */}
|
||||
<div>
|
||||
<div
|
||||
className="rounded-xl overflow-hidden border mb-4"
|
||||
style={{ borderColor: "var(--bb-border)" }}
|
||||
>
|
||||
<Image
|
||||
src="/offline/uniform/uniform-1.jpeg"
|
||||
alt="Бежевая форма сотрудника клиники с коричневым логотипом"
|
||||
width={742}
|
||||
height={990}
|
||||
className="w-full object-cover"
|
||||
style={{ maxHeight: 480, objectPosition: "top" }}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium text-sm mb-3" style={{ color: "var(--bb-text)" }}>
|
||||
Цветовая схема
|
||||
</p>
|
||||
<div className="flex gap-3">
|
||||
<div className="text-center">
|
||||
<div className="w-12 h-12 rounded-lg border mb-1"
|
||||
style={{ background: "#c4a882", borderColor: "var(--bb-border)" }} />
|
||||
<p className="text-xs" style={{ color: "var(--bb-text-muted)" }}>081M</p>
|
||||
<p className="text-xs font-mono" style={{ color: "var(--bb-text-muted)" }}>#c4a882</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="w-12 h-12 rounded-lg border mb-1"
|
||||
style={{ background: "#5c2e0e", borderColor: "var(--bb-border)" }} />
|
||||
<p className="text-xs" style={{ color: "var(--bb-text-muted)" }}>080M</p>
|
||||
<p className="text-xs font-mono" style={{ color: "var(--bb-text-muted)" }}>#5c2e0e</p>
|
||||
</div>
|
||||
<p className="font-medium text-sm mb-1" style={{ color: "var(--bb-text)" }}>
|
||||
Бежевый вариант
|
||||
</p>
|
||||
<p className="text-sm mb-3" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Основная форма для медицинского персонала. Логотип — тёмно-коричневый.
|
||||
</p>
|
||||
<div className="flex gap-2">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<div
|
||||
className="w-4 h-4 rounded"
|
||||
style={{ background: "#c4a882", border: "1px solid #e5e7eb" }}
|
||||
/>
|
||||
<span className="text-xs font-mono" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Oracal 081M
|
||||
</span>
|
||||
</div>
|
||||
<span style={{ color: "var(--bb-text-muted)" }}>·</span>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<div
|
||||
className="w-4 h-4 rounded"
|
||||
style={{ background: "#5c2e0e", border: "1px solid #e5e7eb" }}
|
||||
/>
|
||||
<span className="text-xs font-mono" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Логотип 080M
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Синий вариант */}
|
||||
<div>
|
||||
<div
|
||||
className="rounded-xl overflow-hidden border mb-4"
|
||||
style={{ borderColor: "var(--bb-border)" }}
|
||||
>
|
||||
<Image
|
||||
src="/offline/uniform/uniform-2.jpeg"
|
||||
alt="Синяя форма сотрудника клиники с белым логотипом"
|
||||
width={580}
|
||||
height={773}
|
||||
className="w-full object-cover"
|
||||
style={{ maxHeight: 480, objectPosition: "top" }}
|
||||
/>
|
||||
</div>
|
||||
<p className="font-medium text-sm mb-1" style={{ color: "var(--bb-text)" }}>
|
||||
Синий вариант
|
||||
</p>
|
||||
<p className="text-sm mb-3" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Альтернативный вариант. Логотип — белый инвертированный.
|
||||
</p>
|
||||
<div className="flex gap-2">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<div
|
||||
className="w-4 h-4 rounded"
|
||||
style={{ background: "#4a90c4", border: "1px solid #e5e7eb" }}
|
||||
/>
|
||||
<span className="text-xs font-mono" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Синий медицинский
|
||||
</span>
|
||||
</div>
|
||||
<span style={{ color: "var(--bb-text-muted)" }}>·</span>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<div
|
||||
className="w-4 h-4 rounded"
|
||||
style={{ background: "#ffffff", border: "1px solid #e5e7eb" }}
|
||||
/>
|
||||
<span className="text-xs font-mono" style={{ color: "var(--bb-text-muted)" }}>
|
||||
Логотип белый
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* Логотип на форме */}
|
||||
{/* Размеры логотипа */}
|
||||
<Section
|
||||
title="Размещение логотипа"
|
||||
title="Размеры логотипа для размещения на форме сотрудников"
|
||||
subtitle="Логотип располагается на левой стороне груди. Размер зависит от размера одежды."
|
||||
>
|
||||
{/* Визуализация размещения */}
|
||||
<div
|
||||
className="rounded-xl border p-8 mb-6 flex flex-col items-center justify-center"
|
||||
style={{ background: "#c4a882", borderColor: "transparent", minHeight: 200 }}
|
||||
>
|
||||
<div className="relative w-64 h-48 flex items-center justify-center">
|
||||
{/* Силуэт куртки (упрощённая схема) */}
|
||||
<div className="absolute inset-0 rounded-xl opacity-20"
|
||||
style={{ border: "2px dashed #5c2e0e" }} />
|
||||
{/* Зона логотипа — левая грудь */}
|
||||
<div className="absolute top-6 left-10 flex flex-col items-center gap-2">
|
||||
<Image
|
||||
src="/logo/logo-transparent.png"
|
||||
alt="Логотип на форме"
|
||||
width={100}
|
||||
height={36}
|
||||
className="object-contain"
|
||||
style={{ filter: "brightness(0) sepia(1) saturate(2) hue-rotate(330deg) brightness(0.45)" }}
|
||||
/>
|
||||
<div className="border border-dashed rounded px-1"
|
||||
style={{ borderColor: "#5c2e0e50" }}>
|
||||
<p className="text-xs" style={{ color: "#5c2e0e80" }}>← Левая грудь</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p className="mt-4 text-sm" style={{ color: "rgba(92,46,14,0.7)" }}>
|
||||
Схема размещения логотипа (превью)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Таблица размеров */}
|
||||
<div
|
||||
className="overflow-hidden rounded-xl border"
|
||||
style={{ borderColor: "var(--bb-border)" }}
|
||||
@@ -178,7 +199,7 @@ export default function UniformPage() {
|
||||
{[
|
||||
{ ok: true, text: "Носить комплект в полном составе" },
|
||||
{ ok: true, text: "Поддерживать чистоту и опрятность формы" },
|
||||
{ ok: true, text: "Логотип — только тёмно-коричневый на бежевом" },
|
||||
{ ok: true, text: "Логотип только в утверждённых цветовых вариантах" },
|
||||
{ ok: false, text: "Носить форму без логотипа" },
|
||||
{ ok: false, text: "Изменять цвет или материал формы" },
|
||||
{ ok: false, text: "Добавлять сторонние нашивки и знаки" },
|
||||
|
||||
@@ -68,7 +68,6 @@ const NAV: NavSection[] = [
|
||||
{ label: "Бейджи", href: "/offline/badges" },
|
||||
{ label: "Навигация", href: "/offline/navigation" },
|
||||
{ label: "Транспорт", href: "/offline/transport" },
|
||||
{ label: "Печать", href: "/offline/print" },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
|
||||
interface LlmBlockProps {
|
||||
/** Путь страницы, например "/foundation/colors" */
|
||||
path: string;
|
||||
/** Версия данных, например "v2.1" */
|
||||
version: string;
|
||||
/** Плоский текст для копирования */
|
||||
specText: string;
|
||||
/** Содержимое блока — таблицы, правила */
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* LlmBlock — переиспользуемый блок LLM-спецификации.
|
||||
* Добавляется в конец каждой страницы брендбука, содержащей дизайн-стандарты.
|
||||
* Требование: ФТ-03-LLM (TZ.md) · docs/LLM_CONTEXT.md
|
||||
*/
|
||||
export function LlmBlock({ path, version, specText, children }: LlmBlockProps) {
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
function handleCopy(e: React.MouseEvent) {
|
||||
e.preventDefault();
|
||||
navigator.clipboard.writeText(specText);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
}
|
||||
|
||||
return (
|
||||
<details
|
||||
open
|
||||
className="rounded-xl overflow-hidden"
|
||||
style={{
|
||||
border: "2px dashed var(--brand-053m)",
|
||||
}}
|
||||
>
|
||||
{/* Заголовок */}
|
||||
<summary
|
||||
className="flex items-center justify-between px-5 py-3 cursor-pointer select-none list-none"
|
||||
style={{ background: "rgba(126,207,202,0.07)" }}
|
||||
>
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
<span
|
||||
className="shrink-0 text-[10px] font-bold px-1.5 py-0.5 rounded tracking-wider"
|
||||
style={{ background: "var(--brand-053m)", color: "#fff" }}
|
||||
>
|
||||
LLM
|
||||
</span>
|
||||
<span className="font-semibold text-sm" style={{ color: "var(--bb-text)" }}>
|
||||
LLM-спецификация
|
||||
</span>
|
||||
<span
|
||||
className="text-xs truncate hidden sm:inline"
|
||||
style={{ color: "var(--bb-text-muted)" }}
|
||||
>
|
||||
· машиночитаемые данные · docs/LLM_CONTEXT.md
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3 shrink-0 ml-3">
|
||||
<span
|
||||
className="text-[10px] font-mono hidden md:inline"
|
||||
style={{ color: "var(--bb-text-muted)" }}
|
||||
>
|
||||
{path} · {version}
|
||||
</span>
|
||||
<button
|
||||
onClick={handleCopy}
|
||||
className="text-xs px-3 py-1 rounded font-medium transition-colors shrink-0"
|
||||
style={{
|
||||
background: copied ? "#d1fae5" : "var(--brand-053m)",
|
||||
color: copied ? "#065f46" : "#fff",
|
||||
}}
|
||||
>
|
||||
{copied ? "✓ Скопировано" : "Скопировать"}
|
||||
</button>
|
||||
</div>
|
||||
</summary>
|
||||
|
||||
{/* Содержимое */}
|
||||
<div
|
||||
className="px-5 py-5 space-y-6 border-t"
|
||||
style={{
|
||||
borderColor: "var(--brand-053m)",
|
||||
borderStyle: "dashed",
|
||||
background: "var(--bb-content-bg)",
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</details>
|
||||
);
|
||||
}
|
||||
|
||||
/* ─── Утилиты для содержимого блока ──────────────────────────── */
|
||||
|
||||
/** Заголовок подсекции внутри LLM-блока */
|
||||
export function LlmSection({ title }: { title: string }) {
|
||||
return (
|
||||
<p
|
||||
className="text-[10px] font-semibold uppercase tracking-widest pb-1 border-b"
|
||||
style={{ color: "var(--bb-text-muted)", borderColor: "var(--bb-border)" }}
|
||||
>
|
||||
{title}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
/** Компактная таблица для LLM-блока */
|
||||
export function LlmTable({
|
||||
headers,
|
||||
rows,
|
||||
}: {
|
||||
headers: string[];
|
||||
rows: (string | React.ReactNode)[][];
|
||||
}) {
|
||||
return (
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-xs border-collapse font-mono">
|
||||
<thead>
|
||||
<tr style={{ background: "var(--bb-sidebar-bg)" }}>
|
||||
{headers.map((h) => (
|
||||
<th
|
||||
key={h}
|
||||
className="text-left px-2 py-1.5 font-medium border whitespace-nowrap"
|
||||
style={{ color: "var(--bb-text-muted)", borderColor: "var(--bb-border)" }}
|
||||
>
|
||||
{h}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rows.map((row, ri) => (
|
||||
<tr key={ri} style={{ borderTop: `1px solid var(--bb-border)` }}>
|
||||
{row.map((cell, ci) => (
|
||||
<td
|
||||
key={ci}
|
||||
className="px-2 py-1 border"
|
||||
style={{
|
||||
borderColor: "var(--bb-border)",
|
||||
color: "var(--bb-text-muted)",
|
||||
maxWidth: "240px",
|
||||
}}
|
||||
>
|
||||
{cell}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/** Список правил ✓ / ✕ */
|
||||
export function LlmRules({ rules }: { rules: { ok: boolean; text: string }[] }) {
|
||||
return (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-x-8 gap-y-0.5">
|
||||
{rules.map((r) => (
|
||||
<div key={r.text} className="flex items-start gap-1.5 py-0.5 text-xs font-mono">
|
||||
<span
|
||||
style={{ color: r.ok ? "#059669" : "#dc2626", fontWeight: 700, flexShrink: 0 }}
|
||||
>
|
||||
{r.ok ? "✓" : "✕"}
|
||||
</span>
|
||||
<span style={{ color: "var(--bb-text-muted)" }}>{r.text}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
import type { NextConfig } from "next";
|
||||
import path from "path";
|
||||
|
||||
const isDev = process.env.NODE_ENV === "development";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
turbopack: {
|
||||
root: path.resolve(__dirname, "../.."),
|
||||
},
|
||||
...(isDev && {
|
||||
turbopack: {
|
||||
root: path.resolve(__dirname, "../.."),
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
||||
|
After Width: | Height: | Size: 103 KiB |
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 871 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 217 KiB |
|
After Width: | Height: | Size: 249 KiB |
|
After Width: | Height: | Size: 303 KiB |
|
After Width: | Height: | Size: 224 KiB |
|
After Width: | Height: | Size: 2.3 MiB |
@@ -0,0 +1,156 @@
|
||||
# Деплой — oclinica-brandbook
|
||||
|
||||
## Текущее состояние
|
||||
|
||||
| Сервис | Статус | URL | Платформа |
|
||||
|------------|-------------|----------------------------------------|---------------|
|
||||
| Фронтенд | ✅ Активен | https://web-oclinica.vercel.app | Vercel Hobby |
|
||||
| Бэкенд | локально | http://localhost:3001 | Docker Compose |
|
||||
| База данных | локально | localhost:5433 | PostgreSQL 16 |
|
||||
|
||||
---
|
||||
|
||||
## Фронтенд — Vercel
|
||||
|
||||
### Первоначальная настройка (уже выполнена)
|
||||
|
||||
```bash
|
||||
# 1. Установить Vercel CLI
|
||||
npm install -g vercel
|
||||
|
||||
# 2. Войти в аккаунт (однократно, открывает браузер)
|
||||
vercel login
|
||||
|
||||
# 3. Первый деплой из директории apps/web
|
||||
cd apps/web
|
||||
vercel --yes
|
||||
```
|
||||
|
||||
### Деплой обновлений
|
||||
|
||||
```bash
|
||||
cd apps/web
|
||||
vercel --prod --yes
|
||||
```
|
||||
|
||||
Деплой занимает ~30 секунд. После завершения изменения сразу доступны по адресу:
|
||||
**https://web-oclinica.vercel.app**
|
||||
|
||||
### Как это работает
|
||||
|
||||
- Vercel автоматически определяет Next.js и использует pnpm для сборки
|
||||
- Каждый `vercel --prod` создаёт новый immutable deployment и привязывает его к production URL
|
||||
- Предыдущие деплои остаются доступны по уникальным preview URL
|
||||
- Логи билда: https://vercel.com/oclinica/web
|
||||
|
||||
### Ограничения Vercel Hobby (бесплатный план)
|
||||
|
||||
| Параметр | Лимит |
|
||||
|-----------------------|-------------------------|
|
||||
| Bandwidth | 100 GB / месяц |
|
||||
| Builds | 6000 минут / месяц |
|
||||
| Serverless Functions | 100 GB-hours / месяц |
|
||||
| Тип использования | Только некоммерческие |
|
||||
|
||||
Для брендбука (внутренний инструмент) лимитов более чем достаточно.
|
||||
|
||||
---
|
||||
|
||||
## Локальная разработка
|
||||
|
||||
### Запуск фронтенда
|
||||
|
||||
```bash
|
||||
# Из корня monorepo
|
||||
pnpm dev
|
||||
|
||||
# Или только фронтенд
|
||||
cd apps/web && pnpm dev
|
||||
```
|
||||
|
||||
Доступен на: http://localhost:3001
|
||||
|
||||
### Запуск бэкенда + БД
|
||||
|
||||
```bash
|
||||
# Запустить PostgreSQL
|
||||
docker compose up -d
|
||||
|
||||
# Запустить NestJS
|
||||
cd apps/api && pnpm dev
|
||||
```
|
||||
|
||||
### .env файлы
|
||||
|
||||
```bash
|
||||
# Скопировать и заполнить
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Содержимое `.env.example`:
|
||||
```
|
||||
DATABASE_URL="postgresql://brandbook:brandbook@localhost:5433/brandbook"
|
||||
API_PORT=3001
|
||||
NEXT_PUBLIC_API_URL=http://localhost:3001
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Автодеплой через Gitea Actions (планируется в Sprint 12)
|
||||
|
||||
Для автоматического деплоя при пуше в ветку `main` создать файл
|
||||
`.gitea/workflows/deploy-frontend.yml`:
|
||||
|
||||
```yaml
|
||||
name: Deploy Frontend to Vercel
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'apps/web/**'
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
cache-dependency-path: apps/web/pnpm-lock.yaml
|
||||
|
||||
- name: Install Vercel CLI
|
||||
run: npm install -g vercel
|
||||
|
||||
- name: Deploy to Vercel
|
||||
run: cd apps/web && vercel --prod --yes --token ${{ secrets.VERCEL_TOKEN }}
|
||||
```
|
||||
|
||||
**Настройка:**
|
||||
1. Получить Vercel Token: https://vercel.com/account/tokens
|
||||
2. Добавить в Gitea: Settings → Secrets → `VERCEL_TOKEN`
|
||||
|
||||
---
|
||||
|
||||
## Хостинг бэкенда (планируется к Sprint 11)
|
||||
|
||||
Бэкенд (NestJS + PostgreSQL) потребуется для экспериментальной секции (Sprint 11).
|
||||
Варианты для рассмотрения:
|
||||
|
||||
| Платформа | PostgreSQL | Бесплатно | Карта |
|
||||
|-----------|-----------|-----------|-------|
|
||||
| Railway | ✅ | $5 кредит / месяц | нужна |
|
||||
| Supabase | ✅ | ✅ (PostgreSQL managed) | нет |
|
||||
| Fly.io | ✅ | ✅ ограниченно | нет |
|
||||
| VPS клиники | ✅ | ✅ (если есть) | нет |
|
||||
|
||||
Рекомендация: **Supabase** для БД (бесплатно, managed PostgreSQL) + **Railway** или VPS для NestJS.
|
||||
@@ -0,0 +1,376 @@
|
||||
# LLM Context — Цифровой брендбук Клиники
|
||||
|
||||
## Клиника ухо, горло, нос им. проф. Е.Н.Оленевой
|
||||
|
||||
**Версия контекста:** 2.1
|
||||
**Дата обновления:** 2026-03-22
|
||||
**Актуальный спринт:** Sprint 3
|
||||
**Сайт клиники:** https://oclinica.ru
|
||||
**Брендбук (локально):** http://localhost:3001
|
||||
**Брендбук (production):** https://web-oclinica.vercel.app
|
||||
|
||||
---
|
||||
|
||||
## Назначение файла
|
||||
|
||||
Этот файл — единый источник дизайн-данных клиники в машиночитаемом формате.
|
||||
Используется как контекст для LLM при:
|
||||
- разработке страниц и компонентов сайта
|
||||
- создании макетов и прототипов
|
||||
- разработке мобильных приложений
|
||||
- проектировании новых носителей бренда
|
||||
- генерации CSS / Tailwind / Figma Tokens
|
||||
|
||||
При работе с любым материалом клиники **всегда загружай этот файл первым**.
|
||||
|
||||
---
|
||||
|
||||
## 1. О клинике
|
||||
|
||||
**Полное название:** Клиника ухо, горло, нос им. проф. Е.Н.Оленевой
|
||||
**Тип:** Медицинская клиника, отоларингология (ЛОР)
|
||||
**Город:** Пермь
|
||||
**Платформа сайта:** Drupal (тема `clinic_bootstrap_mobile`)
|
||||
**Аудитория:** Пациенты, врачи клиники, подрядчики
|
||||
|
||||
---
|
||||
|
||||
## 2. Цветовая палитра
|
||||
|
||||
### 2.1 Фирменные цвета (Oracal — базовая палитра)
|
||||
|
||||
Основаны на кодах самоклеящейся плёнки Oracal. HEX-значения — приближённые
|
||||
цифровые эквиваленты. **Для печати и оффлайн-носителей используй коды Oracal,
|
||||
не HEX.**
|
||||
|
||||
| Oracal | Название | HEX | RGB | HSL | CSS-переменная | Применение |
|
||||
|--------|-----------------------|-----------|-------------------|------------------|------------------|------------|
|
||||
| 053M | Основной бирюзовый | `#7ecfca` | rgb(126,207,202) | hsl(177,47%,65%) | `--brand-053m` | Акцент, CTA-кнопки, иконки, активные состояния |
|
||||
| 073M | Тёмный серо-голубой | `#5b7b87` | rgb(91,123,135) | hsl(197,20%,44%) | `--brand-073m` | Тёмный фон, хедер, заголовки навигации |
|
||||
| 066M | Средний бирюзовый | `#5bb5ad` | rgb(91,181,173) | hsl(174,33%,53%) | `--brand-066m` | Вторичные акценты, фоны секций, иллюстрации |
|
||||
| 050M | Тёмно-синий | `#1b4c72` | rgb(27,76,114) | hsl(208,61%,28%) | `--brand-050m` | Наружная реклама, полиграфия, заголовки на светлом |
|
||||
| 081M | Бежевый | `#c4a882` | rgb(196,168,130) | hsl(33,35%,64%) | `--brand-081m` | Форма сотрудников, оффлайн носители, тёплые акценты |
|
||||
| 080M | Тёмно-коричневый | `#5c2e0e` | rgb(92,46,14) | hsl(23,73%,21%) | `--brand-080m` | Текст на бежевых поверхностях, логотип на форме |
|
||||
| — | Белый | `#ffffff` | rgb(255,255,255) | hsl(0,0%,100%) | `--brand-white` | Фон, инвертированный текст, логотип на тёмных фонах |
|
||||
|
||||
### 2.2 Цвета сайта oclinica.ru (реальный CSS)
|
||||
|
||||
Извлечены из CSS темы Drupal:
|
||||
`https://perm.oclinica.ru/sites/all/themes/clinic_bootstrap_mobile/css/style.css`
|
||||
Метод: python + regex + Counter, 2026-03-22
|
||||
|
||||
| Название | HEX | RGB | Группа | Вхождений | Применение на сайте |
|
||||
|-----------------------|-----------|-------------------|----------|-----------|---------------------|
|
||||
| Бежевый | `#bf9975` | rgb(191,153,117) | Акценты | 12 | Основной тёплый акцент, фоны, рамки, текст |
|
||||
| Серо-бирюзовый | `#60959c` | rgb(96,149,156) | Акценты | 7 | Основной холодный акцент, ссылки |
|
||||
| Бирюзовый | `#63bac3` | rgb(99,186,195) | Акценты | 4 | Фоны акцентных блоков, иконки |
|
||||
| Бирюзовый средний | `#52b4bd` | rgb(82,180,189) | Акценты | 4 | Вторичные цветовые акценты |
|
||||
| Коралловый | `#ffa39c` | rgb(255,163,156) | Акценты | 2 | CTA-кнопки («Запишите меня!») |
|
||||
| Основной текст | `#464646` | rgb(70,70,70) | Текст | 3 | Цвет основного текста на сайте |
|
||||
| Второстепенный текст | `#949290` | rgb(148,146,144) | Текст | 4 | Подписи, второстепенный контент |
|
||||
| Светло-бирюзовый фон | `#b8e6ed` | rgb(184,230,237) | Фоны | 1 | Фоны светлых секций с акцентом |
|
||||
| Кремовый фон | `#e9e4d4` | rgb(233,228,212) | Фоны | 1 | Тёплые фоны секций |
|
||||
| Светло-жёлтый фон | `#eef4d1` | rgb(238,244,209) | Фоны | 1 | Фон карточек отзывов |
|
||||
| Светло-зелёный фон | `#f2fee6` | rgb(242,254,230) | Фоны | 1 | Фон секции новостей |
|
||||
|
||||
### 2.3 Соответствие Oracal → Сайт
|
||||
|
||||
| Oracal (бренд) | HEX бренда | Сайт (цифровой) | HEX сайта | Отклонение |
|
||||
|--------------------------|------------|-----------------------|-----------|------------|
|
||||
| 053M Основной бирюзовый | `#7ecfca` | Бирюзовый | `#63bac3` | Темнее, насыщеннее |
|
||||
| 073M Тёмный серо-голубой | `#5b7b87` | Серо-бирюзовый | `#60959c` | Светлее на сайте |
|
||||
| 066M Средний бирюзовый | `#5bb5ad` | Бирюзовый средний | `#52b4bd` | Смещён в синеву |
|
||||
| 081M Бежевый | `#c4a882` | Бежевый | `#bf9975` | Темнее, насыщеннее |
|
||||
| 050M Тёмно-синий | `#1b4c72` | — | — | Не найден в CSS сайта |
|
||||
| 080M Тёмно-коричневый | `#5c2e0e` | — | — | Не найден в CSS сайта |
|
||||
|
||||
**Важно:** расхождения ожидаемы — это цифровая адаптация плёночных цветов под экран.
|
||||
При разработке digital-материалов используй цвета сайта (раздел 2.2), не Oracal.
|
||||
|
||||
### 2.4 Контрастность пар (WCAG 2.1)
|
||||
|
||||
| Пара | Коэффициент | AA (4.5:1) | AAA (7:1) | AA large (3:1) |
|
||||
|-------------------------------------------|-------------|------------|-----------|----------------|
|
||||
| Белый на тёмном серо-голубом (#5b7b87) | 4.6:1 | ✓ | ✕ | ✓ |
|
||||
| Белый на тёмно-синем (#1b4c72) | 9.3:1 | ✓ | ✓ | ✓ |
|
||||
| Белый на тёмно-коричневом (#5c2e0e) | 11.2:1 | ✓ | ✓ | ✓ |
|
||||
| Белый на среднем бирюзовом (#5bb5ad) | 3.0:1 | ✕ | ✕ | ✓ |
|
||||
| Тёмный текст (#111827) на основном бирюзовом (#7ecfca) | 5.8:1 | ✓ | ✕ | ✓ |
|
||||
| Тёмный текст (#111827) на бежевом (#c4a882) | 4.8:1 | ✓ | ✕ | ✓ |
|
||||
| Тёмно-коричневый (#5c2e0e) на бежевом (#c4a882) | 6.7:1 | ✓ | ✕ | ✓ |
|
||||
|
||||
---
|
||||
|
||||
## 3. Типографика
|
||||
|
||||
### 3.1 DINPro — фирменный шрифт (оффлайн)
|
||||
|
||||
**Применение:** логотип, оффлайн-носители (бейджи, навигационные таблички, транспорт,
|
||||
форма сотрудников, полиграфия).
|
||||
**Не использовать** на веб-сайте и в digital-материалах (лицензионный шрифт,
|
||||
нет легального встраивания в веб).
|
||||
**Веса:** Regular, Medium, Bold
|
||||
|
||||
| Стиль | font-weight | font-size | line-height | letter-spacing | Применение |
|
||||
|----------|-------------|-----------|-------------|----------------|------------|
|
||||
| Display | 700 | 48px | 1.15 | -0.5px | Крупные заголовки носителей |
|
||||
| H1 | 700 | 36px | 1.2 | -0.3px | Основной заголовок печатных материалов |
|
||||
| H2 | 700 | 28px | 1.25 | -0.2px | Подзаголовки носителей |
|
||||
| H3 | 500 | 22px | 1.3 | 0px | Подзаголовки третьего уровня |
|
||||
| Body | 400 | 16px | 1.5 | 0px | Основной текст оффлайн |
|
||||
| Small | 400 | 12px | 1.4 | 0.2px | Подписи, бейджи, таблички |
|
||||
| Caption | 400 | 10px | 1.3 | 0.4px | Сноски, технические пометки |
|
||||
|
||||
### 3.2 Fira Sans — веб-шрифт (digital)
|
||||
|
||||
**Применение:** сайт oclinica.ru, цифровые коммуникации, digital-баннеры, email.
|
||||
**Источник:** Google Fonts (`https://fonts.google.com/specimen/Fira+Sans`)
|
||||
**Поддержка кириллицы:** да
|
||||
**Используемые веса:** 300 (Light), 400 (Regular), 500 (Medium), 600 (SemiBold)
|
||||
**CSS:** `font-family: 'Fira Sans', sans-serif;`
|
||||
|
||||
| Стиль | font-weight | font-size | line-height | letter-spacing | Применение |
|
||||
|----------------|-------------|---------------|-------------|----------------|------------|
|
||||
| h1 | 600 | 2.25rem (36px)| 1.25 | -0.025em | Заголовок страницы |
|
||||
| h2 | 600 | 1.875rem (30px)| 1.3 | -0.02em | Заголовок секции |
|
||||
| h3 | 500 | 1.5rem (24px) | 1.375 | -0.01em | Подзаголовок |
|
||||
| h4 | 500 | 1.25rem (20px)| 1.4 | 0em | Заголовок компонента |
|
||||
| h5 | 500 | 1.125rem (18px)| 1.45 | 0em | Подзаголовок компонента |
|
||||
| h6 | 500 | 1rem (16px) | 1.5 | 0.01em | Метка секции |
|
||||
| body-large | 400 | 1.125rem (18px)| 1.6 | 0em | Лид-текст, вводный абзац |
|
||||
| body | 400 | 1rem (16px) | 1.625 | 0em | Основной текст |
|
||||
| body-small | 400 | 0.875rem (14px)| 1.5 | 0em | Дополнительный текст, подписи |
|
||||
| caption | 300 | 0.75rem (12px)| 1.4 | 0.02em | Подписи к изображениям, сноски |
|
||||
| label | 500 | 0.875rem (14px)| 1.2 | 0.03em | Метки форм |
|
||||
| overline | 600 | 0.6875rem (11px)| 1.2 | 0.1em | Надписи над заголовками (uppercase) |
|
||||
|
||||
**Принцип выбора шрифта:**
|
||||
- Носитель цифровой → Fira Sans
|
||||
- Носитель печатный / физический → DINPro
|
||||
|
||||
---
|
||||
|
||||
## 4. Логотип
|
||||
|
||||
### 4.1 Версии логотипа
|
||||
|
||||
**Основной логотип** — включает название специализации («ухо, горло, нос»).
|
||||
Используется на всех основных носителях: сайт, навигация, транспорт, форма.
|
||||
|
||||
**Общий логотип** — без специализации, только название клиники или сеть.
|
||||
Используется для обозначения сети клиник, в корпоративных материалах.
|
||||
|
||||
### 4.2 Файлы
|
||||
|
||||
| Файл | Описание | Путь в проекте |
|
||||
|------|----------|----------------|
|
||||
| `logo-transparent.png` | Логотип с прозрачным фоном | `apps/web/public/logo/logo-transparent.png` |
|
||||
|
||||
SVG-версия ожидается (не получена от клиники).
|
||||
|
||||
### 4.3 Цветовые варианты
|
||||
|
||||
| Вариант | Фон | Логотип | Применение |
|
||||
|---------|-----|---------|------------|
|
||||
| Основной | Белый / светлый | Полноцветный | Сайт, полиграфия на белом |
|
||||
| Инвертированный | Тёмный (#5b7b87, #1b4c72) | Белый (`filter: brightness(0) invert(1)`) | Хедер, тёмные секции |
|
||||
| На форме (беж) | Бежевый (#c4a882 / Oracal 081M) | Коричневый (#5c2e0e / Oracal 080M) | Одежда персонала |
|
||||
| На форме (синий) | Тёмно-синий (#1b4c72 / Oracal 050M) | Белый | Одежда персонала |
|
||||
|
||||
### 4.4 Охранная зона
|
||||
|
||||
Минимальный отступ вокруг логотипа = **высота буквы «К»** в названии.
|
||||
Запрещено размещать другие элементы в охранной зоне.
|
||||
|
||||
### 4.5 Минимальные размеры
|
||||
|
||||
| Носитель | Размер логотипа |
|
||||
|----------|----------------|
|
||||
| Одежда до р.46 | 70 × 25,5 мм |
|
||||
| Одежда от р.48 | 90 × 32,8 мм |
|
||||
|
||||
### 4.6 Запрещено
|
||||
|
||||
- Искажать пропорции
|
||||
- Менять цвета на нефирменные
|
||||
- Добавлять тени, обводки, эффекты
|
||||
- Размещать на пёстром или плохо контрастном фоне
|
||||
- Использовать низкое разрешение (< 150 dpi для печати)
|
||||
- Переворачивать или отражать
|
||||
|
||||
---
|
||||
|
||||
## 5. CSS-переменные брендбука
|
||||
|
||||
Определены в `apps/web/app/globals.css`:
|
||||
|
||||
```css
|
||||
/* Цвета бренда */
|
||||
--brand-053m: #7ecfca; /* Основной бирюзовый */
|
||||
--brand-073m: #5b7b87; /* Тёмный серо-голубой */
|
||||
--brand-066m: #5bb5ad; /* Средний бирюзовый */
|
||||
--brand-050m: #1b4c72; /* Тёмно-синий */
|
||||
--brand-081m: #c4a882; /* Бежевый */
|
||||
--brand-080m: #5c2e0e; /* Тёмно-коричневый */
|
||||
--brand-white: #ffffff; /* Белый */
|
||||
|
||||
/* UI брендбука */
|
||||
--bb-sidebar-bg: ... /* Фон сайдбара */
|
||||
--bb-sidebar-border: ... /* Граница сайдбара */
|
||||
--bb-sidebar-text: ... /* Текст сайдбара */
|
||||
--bb-sidebar-text-muted: ...
|
||||
--bb-sidebar-section: .../* Заголовки секций сайдбара */
|
||||
--bb-sidebar-active-bg: ...
|
||||
--bb-text: ... /* Основной текст контента */
|
||||
--bb-text-muted: ... /* Приглушённый текст */
|
||||
--bb-border: ... /* Границы */
|
||||
--bb-content-bg: ... /* Фон карточек */
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Оффлайн носители
|
||||
|
||||
### 6.1 Форма сотрудников
|
||||
|
||||
**Варианты:**
|
||||
- Бежевый: ткань цвета Oracal 081M, логотип Oracal 080M (коричневый), расположение — левая сторона груди
|
||||
- Синий: ткань цвета Oracal 050M (тёмно-синий), логотип белый (Oracal 010), расположение — левая сторона груди
|
||||
|
||||
**Размеры логотипа на форме:**
|
||||
- Размеры до 46: 70 × 25,5 мм
|
||||
- Размеры от 48: 90 × 32,8 мм
|
||||
|
||||
### 6.2 Бейджи
|
||||
|
||||
**Размер:** 70 × 30 мм
|
||||
**Варианты:** серый (светлый) и белый
|
||||
**Состав текста:** имя, должность, учёная степень (при наличии)
|
||||
**Шрифт:** DINPro
|
||||
**Логотип:** левый верхний угол или левая часть
|
||||
|
||||
### 6.3 Внутренняя навигация
|
||||
|
||||
**Материал:** оргстекло
|
||||
**Плёнки:** Oracal 053M (бирюзовый) и 073M (тёмный серо-голубой)
|
||||
**Типы табличек:**
|
||||
- Таблички на дверях кабинетов: номер кабинета, профиль врача с фото и QR-кодом
|
||||
- Указатели по этажам: стрелки направлений + номера кабинетов
|
||||
|
||||
**Нумерация кабинетов:**
|
||||
- Двузначное число, крупный шрифт (DINPro Bold)
|
||||
- Фон: бирюзовый (053M), номер: белый
|
||||
- Пример реализации: кабинеты 13, 31, «Кабинет 04»
|
||||
|
||||
### 6.4 Брендирование транспорта (трамвай)
|
||||
|
||||
**Зоны оклейки:**
|
||||
- Борта: Oracal 053M (#7ecfca) + 073M (#5b7b87)
|
||||
- Передняя часть: Oracal 066M (#5bb5ad) + 050M (#1b4c72)
|
||||
- Акценты: Oracal 081M (#c4a882) + 080M (#5c2e0e)
|
||||
|
||||
**Все 6 фирменных цветов присутствуют на транспорте.**
|
||||
|
||||
---
|
||||
|
||||
## 7. Структура брендбука (страницы)
|
||||
|
||||
| URL | Статус | Описание |
|
||||
|-----|--------|----------|
|
||||
| `/foundation/logo` | ✅ Готова | Логотип, варианты, охранная зона, правила |
|
||||
| `/foundation/colors` | ✅ Готова | Палитра, контраст WCAG, цвета сайта, соответствие |
|
||||
| `/foundation/typography` | ✅ Готова | DINPro + Fira Sans, шкала стилей |
|
||||
| `/foundation/icons` | 🔜 Скоро | Иконография |
|
||||
| `/offline/uniform` | ✅ Готова | Форма сотрудников |
|
||||
| `/offline/badges` | ✅ Готова | Бейджи |
|
||||
| `/offline/navigation` | ✅ Готова | Внутренняя навигация |
|
||||
| `/offline/transport` | ✅ Готова | Брендирование транспорта |
|
||||
| `/components/buttons` | 🔜 Sprint 3 | Кнопки |
|
||||
| `/components/forms` | 🔜 Sprint 3 | Форм-контролы |
|
||||
| `/components/*` | 🔜 Sprint 3–4 | Карточки, бейджи, алерты, модалки, таблицы |
|
||||
| `/blocks/*` | 🔜 Sprint 5–8 | Hero, врачи, отзывы, новости, формы |
|
||||
| `/pages/*` | 🔜 Sprint 9–11 | Главная, заболевание, врачи, цены, контакты |
|
||||
|
||||
---
|
||||
|
||||
## 8. Правила применения цветов
|
||||
|
||||
### Разрешено
|
||||
- Использовать только цвета из фирменной палитры (раздел 2.1)
|
||||
- Для digital: адаптировать к цветам сайта (раздел 2.2)
|
||||
- Текст на цветном фоне — минимум WCAG AA (4.5:1)
|
||||
- Белый логотип на тёмных фонах (073M, 050M, 080M)
|
||||
- Коричневый логотип (080M) на бежевом фоне (081M)
|
||||
|
||||
### Запрещено
|
||||
- Использовать произвольные цвета вне палитры
|
||||
- Осветлять, затемнять или менять оттенок фирменных цветов
|
||||
- Текст с контрастом ниже 3:1 (даже для крупного текста)
|
||||
- Размещать тёплые и холодные акценты рядом без разделителя
|
||||
|
||||
### Иерархия цветов
|
||||
|
||||
```
|
||||
Основной бирюзовый (053M / #7ecfca) ← главный идентификатор бренда
|
||||
└── Средний бирюзовый (066M / #5bb5ad) ← вторичный акцент
|
||||
└── Тёмный серо-голубой (073M / #5b7b87) ← фоны, хедер
|
||||
|
||||
Тёмно-синий (050M / #1b4c72) ← авторитет, полиграфия
|
||||
Бежевый (081M / #c4a882) ← тепло, форма
|
||||
└── Тёмно-коричневый (080M / #5c2e0e) ← текст на бежевом
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Правила применения типографики
|
||||
|
||||
### Веб (Fira Sans)
|
||||
- H1 только один на странице
|
||||
- Заголовки не пропускать по уровням (h1→h2→h3)
|
||||
- Основной текст: 16px / 400 / 1.625
|
||||
- Минимальный размер текста на экране: 12px
|
||||
- Кириллица: только Fira Sans, не DINPro
|
||||
|
||||
### Оффлайн (DINPro)
|
||||
- Все физические носители: DINPro
|
||||
- Логотип: DINPro Bold
|
||||
- Таблички: DINPro Medium / Bold
|
||||
- Не использовать light (300) для текста < 14pt
|
||||
|
||||
---
|
||||
|
||||
## 10. Технический стек проекта
|
||||
|
||||
| Слой | Технология | Версия |
|
||||
|------|-----------|--------|
|
||||
| Фронтенд | Next.js App Router | 16.x |
|
||||
| Стилизация | Tailwind CSS | 4.x |
|
||||
| Язык | TypeScript | 5.x |
|
||||
| Шрифты | next/font/google | — |
|
||||
| Бэкенд | NestJS | 11.x |
|
||||
| БД | PostgreSQL + Prisma | 16.x / 7.x |
|
||||
| Деплой | Vercel Hobby | — |
|
||||
| Пакетный менеджер | pnpm | 10.x |
|
||||
|
||||
---
|
||||
|
||||
## 11. История изменений контекста
|
||||
|
||||
| Версия | Дата | Что добавлено |
|
||||
|--------|------|---------------|
|
||||
| 1.0 | 2026-03-22 | Sprint 1: логотип, базовые цвета |
|
||||
| 2.0 | 2026-03-22 | Sprint 2: типографика, оффлайн носители, цвета сайта (8 цветов) |
|
||||
| 2.1 | 2026-03-22 | Sprint 2 доп.: +3 цвета сайта (коралловый, светло-жёлтый, светло-зелёный) |
|
||||
|
||||
---
|
||||
|
||||
## 12. Что обновлять в этом файле
|
||||
|
||||
При каждом спринте добавляй:
|
||||
- Новые компоненты и их спецификации (цвета, размеры, состояния)
|
||||
- Новые правила применения, выявленные при разработке
|
||||
- Изменения в палитре или типографике
|
||||
- Новые паттерны и примеры кода
|
||||
|
||||
**Соответствующий раздел в TZ.md:** ФТ-03-LLM
|
||||
**Соответствующий раздел в SPRINTS.md:** задача «LLM-блок» в каждом спринте
|
||||
@@ -81,40 +81,55 @@
|
||||
### Фактические результаты
|
||||
- Страница `/foundation/colors` — 7 цветов с HEX/RGB/HSL/CSS-var и копированием, WCAG-контраст 7 пар, экспорт JSON
|
||||
- Страница `/foundation/typography` — DINPro (оффлайн) + Fira Sans (веб), таблица применения, полные шкалы, живой пример
|
||||
- Страница `/offline/uniform` — схема формы, таблица размеров логотипа, правила использования
|
||||
- Страница `/offline/badges` — макеты бейджей 70×30 мм (светлый/тёмный), состав текста, применение
|
||||
- Страница `/offline/navigation` — 4 шаблона табличек, технические требования, цвета Oracal
|
||||
- Страница `/offline/transport` — CSS-макет трамвая с трёхполосной схемой, таблица зон
|
||||
- Страница `/offline/print` — макеты визитки (лицо/оборот) и листовки А5, Telegram-бот
|
||||
- Sidebar: убраны «скоро» для Цветов, Типографики и всех 5 страниц Оффлайн
|
||||
- Страница `/offline/uniform` — реальные фото из PDF (беж + синий вариант), таблица размеров, правила
|
||||
- Страница `/offline/badges` — реальные фото из PDF (лицевая + оборотная), состав текста, пример
|
||||
- Страница `/offline/navigation` — макеты из PDF (Кабинет 04, карточка врача), фото дверей с номерами (13, 31), указатели по этажам
|
||||
- Страница `/offline/transport` — макет трамвая из PDF (оба вида, реальный рендер), таблица зон, цвета Oracal
|
||||
- Страница `/offline/print` — убрана из навигации (нет данных из брендбука)
|
||||
- Sidebar: убраны «скоро» для Цветов, Типографики и всех страниц Оффлайн кроме Печати
|
||||
- Версия обновлена до **Sprint 2 · v0.2.0**
|
||||
- **Деплой на Vercel:** https://web-oclinica.vercel.app (production, бесплатно)
|
||||
- **Тайтлы страниц:** единый формат «Раздел. Цифровой брендбук Клиники ухо, горло, нос им. проф. Е.Н.Оленевой»
|
||||
|
||||
### Технические решения Sprint 2
|
||||
- Страница «Цвета» — `"use client"` для clipboard API и экспорта JSON
|
||||
- WCAG relative luminance вычисляется на клиенте, без зависимостей
|
||||
- DINPro отображается с фоллбэком Arial (лицензионный шрифт)
|
||||
- Макеты (бейджи, трамвай, визитки) — чистый CSS/Tailwind без внешних зависимостей
|
||||
- Реальные фото и макеты из PDF: PyMuPDF (fitz) — извлечение растровых изображений и рендер векторных страниц
|
||||
- Рендер PDF страниц: 2.5–3.0x масштаб → JPEG, кроп до нужной области через Pillow
|
||||
|
||||
**Результат спринта:** Разделы «Цвета», «Типографика» и «Оффлайн элементы» полностью готовы.
|
||||
### Ретроспектива Sprint 2 — LLM-контекст
|
||||
- [x] Docs: Создан `docs/LLM_CONTEXT.md` — сводный файл дизайн-данных для LLM (v2.1)
|
||||
- [x] TZ: Добавлено требование ФТ-03-LLM — LLM-блок на каждой странице брендбука
|
||||
- [ ] FE: Добавить LLM-блоки на страницы Sprint 1–2 (логотип, цвета, типографика, оффлайн) — перенесено в Sprint 3
|
||||
|
||||
**Результат спринта:** Разделы «Цвета», «Типографика» и «Оффлайн элементы» полностью готовы с реальными материалами из брендбука.
|
||||
|
||||
---
|
||||
|
||||
## Sprint 3 — Базовые компоненты: кнопки и поля ввода
|
||||
|
||||
**Цель:** Все варианты кнопок и форм-контролов в брендбуке.
|
||||
**Цель:** Все варианты кнопок и форм-контролов в брендбуке. LLM-блоки на страницах.
|
||||
|
||||
### Задачи
|
||||
### Задачи — LLM-контекст
|
||||
- [ ] FE: Добавить LLM-блок на страницу «Логотип» (`/foundation/logo`)
|
||||
- [x] FE: Добавить LLM-блок на страницу «Цвета» (`/foundation/colors`) — v2.1
|
||||
- [x] FE: Добавить LLM-блок на страницу «Типографика» (`/foundation/typography`) — v2.0
|
||||
- [x] FE: Создать переиспользуемый компонент `components/llm/LlmBlock.tsx` (LlmBlock, LlmSection, LlmTable, LlmRules)
|
||||
- [ ] Docs: Обновить `docs/LLM_CONTEXT.md` по итогам спринта (версия 3.x)
|
||||
|
||||
### Задачи — компоненты
|
||||
- [ ] FE: Компонент Button (все варианты: primary/secondary/ghost/danger, размеры, состояния)
|
||||
- [ ] FE: Компонент Input (text, password, focus/error/disabled)
|
||||
- [ ] FE: Компонент Textarea
|
||||
- [ ] FE: Компонент Select
|
||||
- [ ] FE: Компонент Checkbox и Radio
|
||||
- [ ] FE: Компонент Toggle/Switch
|
||||
- [ ] FE: Страница брендбука «Компоненты → Кнопки» с документацией
|
||||
- [ ] FE: Страница брендбука «Компоненты → Форм-контролы»
|
||||
- [ ] FE: Страница брендбука «Компоненты → Кнопки» с документацией + LLM-блок
|
||||
- [ ] FE: Страница брендбука «Компоненты → Форм-контролы» + LLM-блок
|
||||
- [ ] FE: Копирование HTML/CSS кода компонента в один клик
|
||||
|
||||
**Результат спринта:** Раздел «Базовые компоненты — кнопки и ввод» готов.
|
||||
**Результат спринта:** Раздел «Базовые компоненты — кнопки и ввод» готов. LLM-блоки добавлены на страницы Фундамента.
|
||||
|
||||
---
|
||||
|
||||
@@ -123,6 +138,8 @@
|
||||
**Цель:** Все типы карточек, используемых на сайте.
|
||||
|
||||
### Задачи
|
||||
- [ ] Docs: Обновить `docs/LLM_CONTEXT.md` — добавить спецификации карточек
|
||||
- [ ] FE: Добавить LLM-блок на страницу «Карточки»
|
||||
- [ ] FE: Карточка врача (DoctorCard) — фото, имя, специализация, кнопка записи
|
||||
- [ ] FE: Карточка услуги / заболевания
|
||||
- [ ] FE: Карточка новости — превью, дата, заголовок, анонс, читать далее
|
||||
@@ -140,6 +157,8 @@
|
||||
**Цель:** Ключевые верхние блоки страниц.
|
||||
|
||||
### Задачи
|
||||
- [ ] Docs: Обновить `docs/LLM_CONTEXT.md` — добавить спецификации Hero и CEO-блоков
|
||||
- [ ] FE: Добавить LLM-блоки на страницы Hero и CEO-текст
|
||||
- [ ] FE: Hero-блок вариант 1 — фон + заголовок + CTA
|
||||
- [ ] FE: Hero-блок вариант 2 — иллюстрация сбоку
|
||||
- [ ] FE: Hero-блок вариант 3 — с встроенной формой записи
|
||||
@@ -157,6 +176,8 @@
|
||||
**Цель:** Блоки и компоненты, связанные с врачами.
|
||||
|
||||
### Задачи
|
||||
- [ ] Docs: Обновить `docs/LLM_CONTEXT.md` — добавить спецификации блоков врачей
|
||||
- [ ] FE: Добавить LLM-блок на страницу «Блоки → Врачи»
|
||||
- [ ] FE: Блок «Наши врачи» — сетка карточек с фильтром по специализации
|
||||
- [ ] FE: Блок «Врач — профиль» (полная страница): фото, биография, специализации, расписание
|
||||
- [ ] FE: Компонент «Расписание / слоты записи»
|
||||
@@ -173,6 +194,8 @@
|
||||
**Цель:** Контентные блоки сайта.
|
||||
|
||||
### Задачи
|
||||
- [ ] Docs: Обновить `docs/LLM_CONTEXT.md` — добавить спецификации блоков отзывов и новостей
|
||||
- [ ] FE: Добавить LLM-блок на страницу «Блоки → Отзывы и новости»
|
||||
- [ ] FE: Блок отзывов — карусель
|
||||
- [ ] FE: Блок отзывов — статичная сетка
|
||||
- [ ] FE: Блок рейтинга (звёзды + количество отзывов)
|
||||
@@ -191,6 +214,8 @@
|
||||
**Цель:** Все формы и диалоги взаимодействия с пациентом.
|
||||
|
||||
### Задачи
|
||||
- [ ] Docs: Обновить `docs/LLM_CONTEXT.md` — добавить спецификации форм и модальных окон
|
||||
- [ ] FE: Добавить LLM-блок на страницу «Блоки → Формы и контакты»
|
||||
- [ ] FE: Форма записи — короткая (имя, телефон)
|
||||
- [ ] FE: Форма записи — расширенная (имя, телефон, специализация, врач, дата)
|
||||
- [ ] FE: Форма записи в модальном окне
|
||||
@@ -252,20 +277,25 @@
|
||||
|
||||
---
|
||||
|
||||
## Sprint 12 — Деплой, полировка, документация
|
||||
## Sprint 12 — Полировка, финальный деплой и документация
|
||||
|
||||
**Цель:** Финальный релиз и публикация.
|
||||
**Цель:** Финальный релиз. Фронтенд уже живёт на Vercel с Sprint 2, Sprint 12 — финальная полировка и production-готовность бэкенда.
|
||||
|
||||
### Задачи
|
||||
- [ ] BE + FE: Полный smoke-тест всего брендбука
|
||||
- [ ] FE: Мобильная адаптация — финальная проверка всех страниц
|
||||
- [ ] FE: Accessibility-аудит (WCAG AA)
|
||||
- [ ] Деплой: настройка CI/CD, публикация на сервере
|
||||
- [ ] Docs: Создание `docs/deployment.md`
|
||||
- [ ] Docs: Обновление README.md финальными инструкциями
|
||||
- [ ] Деплой BE: выбрать и настроить хостинг для NestJS + PostgreSQL
|
||||
- [ ] Деплой: настроить автоматический деплой через Gitea Actions → Vercel (при пуше в `main`)
|
||||
- [ ] Docs: Обновление `docs/DEPLOY.md` финальными инструкциями
|
||||
- [ ] Design: Финальный ревью брендбука
|
||||
|
||||
**Результат спринта:** Брендбук задеплоен и доступен по URL.
|
||||
### Текущий статус деплоя
|
||||
- **Фронтенд:** https://web-oclinica.vercel.app (Vercel Hobby, задеплоен в Sprint 2)
|
||||
- **Команда деплоя:** `cd apps/web && vercel --prod --yes`
|
||||
- **Бэкенд:** локально (Docker Compose), хостинг выбирается в Sprint 12
|
||||
|
||||
**Результат спринта:** Брендбук полностью готов, оба сервиса задеплоены, автодеплой настроен.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -37,9 +37,10 @@
|
||||
| Краткое название | oclinica-brandbook |
|
||||
| Сайт клиники | https://oclinica.ru |
|
||||
| Тип системы | Веб-приложение (Living Styleguide) |
|
||||
| Режим работы | Локальная разработка + деплой на сервер |
|
||||
| Режим работы | Локальная разработка + Vercel (preview + production) |
|
||||
| Аудитория | Внутренние дизайнеры клиники, внешние подрядчики |
|
||||
| Хостинг | TBD — будет прописан отдельно |
|
||||
| Хостинг (фронтенд) | Vercel Hobby (бесплатно) — https://web-oclinica.vercel.app |
|
||||
| Хостинг (бэкенд + БД) | TBD — уточняется при переходе к Sprint 11 (экспериментальная секция) |
|
||||
|
||||
---
|
||||
|
||||
@@ -152,6 +153,17 @@
|
||||
- HTML-код компонента с кнопкой «Скопировать»
|
||||
- Краткое описание и правила применения
|
||||
|
||||
**ФТ-03-LLM.** На каждой странице брендбука, содержащей дизайн-стандарты (цвета, типографика,
|
||||
компоненты, блоки, страницы), должен присутствовать **LLM-блок** — свёрнутая или отдельная
|
||||
секция с машиночитаемым описанием стандарта для использования языковыми моделями при
|
||||
разработке дизайна, макетов, сайта и приложений. LLM-блок содержит:
|
||||
- Все токены и значения в табличном формате
|
||||
- Правила применения в формате «разрешено / запрещено»
|
||||
- Взаимосвязи с другими стандартами (ссылки на разделы)
|
||||
- Примеры корректного и некорректного использования
|
||||
|
||||
Сводный LLM-контекст всего брендбука хранится в файле: **`docs/LLM_CONTEXT.md`**
|
||||
|
||||
---
|
||||
|
||||
### 5.2 Раздел «Цвета»
|
||||
@@ -393,17 +405,18 @@
|
||||
|
||||
| Слой | Технология | Версия | Обоснование |
|
||||
|-------------------|-----------------------|----------|--------------------------------------------------|
|
||||
| Фронтенд | Next.js (App Router) | 15.x | SSR/SSG, оптимизация, экосистема React |
|
||||
| Бэкенд | NestJS | 10.x | Типизированный Node.js фреймворк, DI, модули |
|
||||
| Фронтенд | Next.js (App Router) | 16.x | SSR/SSG, оптимизация, экосистема React |
|
||||
| Бэкенд | NestJS | 11.x | Типизированный Node.js фреймворк, DI, модули |
|
||||
| База данных | PostgreSQL | 16.x | Надёжная реляционная БД, JSON-поля для атрибутов |
|
||||
| ORM | Prisma | 5.x | Type-safe запросы, миграции, seed |
|
||||
| Стилизация | CSS Modules | — | Изоляция стилей, нет рантайм-зависимостей |
|
||||
| ORM | Prisma | 7.x | Type-safe запросы, миграции, seed |
|
||||
| Стилизация | Tailwind CSS | 4.x | Utility-first, CSS-переменные, нет рантайм-overhead |
|
||||
| Дизайн-токены | CSS Custom Properties | — | Нативно поддерживается всеми браузерами |
|
||||
| Шрифт (бренд) | DINPro | — | Фирменный шрифт бренда, оффлайн-носители |
|
||||
| Шрифт (веб) | Fira Sans | — | Google Fonts, кириллица, веса 300 и 400, сайт |
|
||||
| Шрифт (веб) | Fira Sans | — | Google Fonts, кириллица, веса 300/400/500/600 |
|
||||
| Авторизация | JWT + httpOnly cookie | — | Безопасное хранение токена |
|
||||
| Пакетный менеджер | pnpm | 9.x | Monorepo workspaces, скорость |
|
||||
| Контейнеризация | Docker + Compose | — | Единообразное окружение dev/prod |
|
||||
| Пакетный менеджер | pnpm | 10.x | Monorepo workspaces, скорость |
|
||||
| Контейнеризация | Docker + Compose | — | Единообразное окружение локальной разработки |
|
||||
| Хостинг фронтенда | Vercel | — | Нативная поддержка Next.js, бесплатный Hobby-план |
|
||||
|
||||
---
|
||||
|
||||
@@ -480,16 +493,29 @@ oclinica-brandbook/
|
||||
| Docker | >= 24 |
|
||||
| Docker Compose | >= 2 |
|
||||
|
||||
### 12.2 Production-сервер
|
||||
### 12.2 Деплой фронтенда (Vercel)
|
||||
|
||||
> TBD — параметры хостинга будут прописаны отдельно.
|
||||
Фронтенд (`apps/web`) деплоится на Vercel Hobby (бесплатно).
|
||||
|
||||
Минимальные ожидаемые требования:
|
||||
- ОС: Ubuntu 22.04+
|
||||
- RAM: 2 GB
|
||||
- Disk: 20 GB
|
||||
- PostgreSQL 16 (или managed database)
|
||||
- Node.js 20 LTS
|
||||
**Production URL:** https://web-oclinica.vercel.app
|
||||
|
||||
**Команда деплоя** (из директории `apps/web`):
|
||||
```bash
|
||||
vercel --prod --yes
|
||||
```
|
||||
|
||||
**Требования:**
|
||||
- Vercel CLI установлен глобально: `npm install -g vercel`
|
||||
- Выполнен `vercel login` (однократно)
|
||||
|
||||
**Деплой занимает ~30 секунд.** После команды изменения сразу доступны по production URL.
|
||||
|
||||
### 12.3 Бэкенд и база данных
|
||||
|
||||
> TBD — параметры хостинга бэкенда (NestJS + PostgreSQL) будут определены к Sprint 11,
|
||||
> когда потребуется работающий API для экспериментальной секции.
|
||||
|
||||
Варианты для рассмотрения: Railway, Render, VPS клиники.
|
||||
|
||||
---
|
||||
|
||||
@@ -511,11 +537,12 @@ oclinica-brandbook/
|
||||
| № | Вопрос | Ответственный | Срок |
|
||||
|----|--------------------------------------------------------------------------------------|---------------|----------|
|
||||
| ОВ-01 | Доступен ли JSON API или REST API на oclinica.ru? Каков формат ответов? | Клиника | Sprint 1 |
|
||||
| ОВ-02 | Параметры хостинга для production-деплоя | Клиника | TBD |
|
||||
| ОВ-02 | ~~Параметры хостинга для production-деплоя~~ **Закрыт:** фронтенд — Vercel Hobby (https://web-oclinica.vercel.app); бэкенд — TBD к Sprint 11 | — | Частично закрыт |
|
||||
| ОВ-03 | Нужна ли страница «Заболевание» как отдельный тип, или это подвид страницы «Услуга»? | Клиника | Sprint 9 |
|
||||
| ОВ-04 | Список иконок — какую стороннюю библиотеку утвердить? (Lucide, Heroicons, и др.) | Совместно | Sprint 2 |
|
||||
| ОВ-05 | ~~Нужен ли раздел «Логотип» в v1.0 или ждём вектор?~~ **Закрыт:** страница логотипа реализуется в Sprint 1 с PNG-версией; вектор будет добавлен позже | — | Закрыт |
|
||||
| ОВ-06 | HEX-эквиваленты цветов Oracal (053M, 073M, 066M, 050M, 081M, 080M) для использования в токенах | Совместно | Sprint 2 |
|
||||
| ОВ-06 | ~~HEX-эквиваленты цветов Oracal~~ **Закрыт:** приблизительные HEX зафиксированы в Sprint 2 (053M=#7ecfca, 073M=#5b7b87, 066M=#5bb5ad, 050M=#1b4c72, 081M=#c4a882, 080M=#5c2e0e). Дополнительно — реальные цвета сайта извлечены из CSS (см. ОВ-07). Точная калибровка Oracal — при получении физических образцов. | — | Закрыт |
|
||||
| ОВ-07 | **Цвета сайта oclinica.ru** — CSS тема Drupal доступна по адресу: `https://perm.oclinica.ru/sites/all/themes/clinic_bootstrap_mobile/css/style.css`. Тема: `clinic_bootstrap_mobile`. Ключевые цвета извлечены парсингом (python + regex + Counter), 2026-03-22. Добавлены в раздел «Цвета с сайта» в брендбуке (`/foundation/colors`). Расхождение с Oracal-палитрой ожидаемо — цифровые адаптации под экран. | — | Закрыт |
|
||||
|
||||
---
|
||||
|
||||
|
||||