229 lines
12 KiB
Markdown
229 lines
12 KiB
Markdown
<!-- vibe42-design-version: v4-ds-scaffold-2026-09-11 -->
|
||
# Дизайн-система KT AI — по ней собирается весь UI
|
||
|
||
В папке проекта лежит `design-system/` — это **дизайн-система KT AI**
|
||
(источник: `github.com/erjan-A/kt-ai-design-system`, MIT, версия в `design-system/VERSION`).
|
||
Весь визуал берётся оттуда: цвета, шрифт, отступы, кнопки, карточки, секции лендинга.
|
||
|
||
**Не выдумывай свой стиль и не тащи чужие фреймворки.** Твоя работа — собрать страницу
|
||
из готовых классов ДС и наполнить её содержанием юзера.
|
||
|
||
Если папки `design-system/` в проекте нет (старый проект) — принеси её один раз:
|
||
|
||
```bash
|
||
cp -r /srv/opencode/design-system/kt-ai design-system
|
||
```
|
||
|
||
---
|
||
|
||
## 🔒 Три правила, которые нельзя нарушать
|
||
|
||
1. **Цвета и размеры — только токенами** `var(--kt-ai-…)`. Сырой hex (`#0F1218`, `#fff`) в вёрстке
|
||
запрещён: он ломает светлую/тёмную тему.
|
||
2. **Никакого Bootstrap / Tailwind / Material / Font Awesome / Google Fonts.** Шрифт (Inter) уже
|
||
внутри ДС и работает офлайн. Всё, что нужно лендингу, в ДС уже есть.
|
||
3. **Свой `<style>` — только на мелочи**, которых нет в ДС (позиционирование, картинка-фон).
|
||
Не переопределяй кнопки, карточки и типографику.
|
||
|
||
---
|
||
|
||
## Старт: `index.html`
|
||
|
||
Это рабочий каркас лендинга. Копируй его целиком и меняй содержание.
|
||
|
||
**В новом проекте этот каркас уже лежит как `index.html`** — не создавай файл заново: `read index.html` → `edit`.
|
||
Если проект старый и файла нет — скопируй каркас ниже целиком. Если проект не про веб-страницу — стартовый `index.html` можно удалить.
|
||
|
||
```html
|
||
<!DOCTYPE html>
|
||
<html lang="ru" data-theme="auto">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>Название проекта</title>
|
||
<link rel="stylesheet" href="design-system/kt-ai-fonts.css">
|
||
<link rel="stylesheet" href="design-system/kt-ai-tokens.css">
|
||
<link rel="stylesheet" href="design-system/kt-ai-components.css">
|
||
<link rel="stylesheet" href="design-system/kt-ai-page.css">
|
||
<style>
|
||
/* ДС рисует кнопки для <button>; у ссылки-кнопки браузер добавляет подчёркивание */
|
||
a.kt-ai-btn { text-decoration: none; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<header class="kt-ai-pagehead">
|
||
<div class="kt-ai-wrap kt-ai-pagehead-in">
|
||
<strong>Название</strong>
|
||
<span class="kt-ai-spacer"></span>
|
||
<a class="kt-ai-btn" data-variant="ghost" href="#about">О нас</a>
|
||
<a class="kt-ai-btn" data-variant="primary" href="#contacts">Оставить заявку</a>
|
||
</div>
|
||
</header>
|
||
|
||
<section class="kt-ai-hero" data-align="center">
|
||
<div class="kt-ai-wrap">
|
||
<h1>Главное обещание в одну строку</h1>
|
||
<p class="kt-ai-hero-sub">Подзаголовок: кому это и почему стоит остаться.</p>
|
||
<div class="kt-ai-hero-actions">
|
||
<a class="kt-ai-btn" data-variant="primary" data-size="lg" href="#contacts">Оставить заявку</a>
|
||
<a class="kt-ai-btn" data-size="lg" href="#about">Подробнее</a>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="about" class="kt-ai-section" data-divider="true">
|
||
<div class="kt-ai-wrap">
|
||
<div class="kt-ai-section-head">
|
||
<h2>Что вы получите</h2>
|
||
<p>Одно-два предложения пояснения к секции.</p>
|
||
</div>
|
||
<div class="kt-ai-grid" style="margin-top:var(--kt-ai-space-9)">
|
||
<article class="kt-ai-tile"><h3>Пункт первый</h3><p>Коротко и по делу.</p></article>
|
||
<article class="kt-ai-tile"><h3>Пункт второй</h3><p>Коротко и по делу.</p></article>
|
||
<article class="kt-ai-tile"><h3>Пункт третий</h3><p>Коротко и по делу.</p></article>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="contacts" class="kt-ai-cta">
|
||
<div class="kt-ai-wrap">
|
||
<h2>Призыв к действию</h2>
|
||
<p>Город, адрес, часы работы — то, что нужно, чтобы дойти.</p>
|
||
<a class="kt-ai-btn" data-variant="primary" data-size="lg" href="#">Написать</a>
|
||
</div>
|
||
</section>
|
||
|
||
<footer class="kt-ai-wrap kt-ai-pagefoot">
|
||
<span>© 2026 Название</span>
|
||
<span class="kt-ai-spacer"></span>
|
||
<span>Контакты</span>
|
||
</footer>
|
||
|
||
</body>
|
||
</html>
|
||
```
|
||
|
||
---
|
||
|
||
## Блоки страницы (`kt-ai-page.css`)
|
||
|
||
| Класс | Что это |
|
||
|-------|---------|
|
||
| `.kt-ai-wrap` | контейнер по центру (1080px). `data-width="narrow"` — 720px, `"wide"` — 1200px |
|
||
| `.kt-ai-pagehead` + `.kt-ai-pagehead-in` | липкая шапка страницы |
|
||
| `.kt-ai-hero`, `.kt-ai-hero-sub`, `.kt-ai-hero-actions` | первый экран. `data-align="center"` — по центру |
|
||
| `.kt-ai-section` + `.kt-ai-section-head` (h2 + p) | обычная секция. `data-divider="true"` — линия сверху |
|
||
| `.kt-ai-grid` + `.kt-ai-tile` (h3 + p) | сетка плиток «возможности». `data-cols="2"/"4"`, у плитки `data-span="2"/"full"` |
|
||
| `.kt-ai-tile-icon` | иконка над заголовком плитки (спрайт — `design-system/icons/`) |
|
||
| `.kt-ai-cta` | финальный блок с призывом |
|
||
| `.kt-ai-pagefoot` | подвал |
|
||
| `.kt-ai-spacer` | распорка внутри шапки/подвала (толкает остальное вправо) |
|
||
| `.kt-ai-centered` | одна карточка по центру экрана — для страницы входа и 404 |
|
||
|
||
Отступы между секциями ДС ставит сама. Адаптив (3→2→1 колонка) — тоже, `@media` писать не нужно.
|
||
|
||
## Элементы (`kt-ai-components.css`)
|
||
|
||
| Класс | Как применять |
|
||
|-------|---------------|
|
||
| `.kt-ai-btn` | кнопка / ссылка-кнопка. `data-variant="primary"` — главная, `"ghost"` — прозрачная, `"danger"` — опасная. `data-size="sm"/"lg"` |
|
||
| `.kt-ai-card` | карточка контента |
|
||
| `.kt-ai-chip` | статус-пилюля / тег |
|
||
| `.kt-ai-link` | текстовая ссылка |
|
||
| `.kt-ai-field` + `.kt-ai-input` / `.kt-ai-textarea` / `.kt-ai-select` | поле формы с подписью |
|
||
|
||
**Один `primary` на экран.** Остальные кнопки — обычные или `ghost`.
|
||
|
||
## Токены (`kt-ai-tokens.css`)
|
||
|
||
Цвет: `--kt-ai-bg`, `--kt-ai-bg-soft`, `--kt-ai-fg`, `--kt-ai-fg-muted`, `--kt-ai-fg-faint`,
|
||
`--kt-ai-border`, `--kt-ai-divider`, `--kt-ai-card-bg`, `--kt-ai-card-border`,
|
||
`--kt-ai-primary`, `--kt-ai-link`, `--kt-ai-danger`.
|
||
|
||
Отступы: `--kt-ai-space-1…12` (2px → 64px). Скругления: `--kt-ai-radius-md/lg/xl/2xl/3xl/full`.
|
||
Текст: `--kt-ai-text-xs…3xl`. Вес: `--kt-ai-weight-regular/medium/semibold/bold`.
|
||
Шрифты: `--kt-ai-font-sans` (Inter), `--kt-ai-font-mono`.
|
||
|
||
**Тема** — атрибут на `<html>`: `data-theme="auto"` (по системной, ставь его по умолчанию),
|
||
`"light"`, `"dark"`. Проверь страницу в обеих темах — с токенами это бесплатно.
|
||
|
||
---
|
||
|
||
## Не лендинг, а приложение (дашборд, таблица, очередь заявок, чат)
|
||
|
||
Тогда экран собирается **не вёрсткой руками, а конфигом**:
|
||
|
||
1. Возьми `design-system/templates/kt-ai-app-shell.html` — самодостаточный HTML-каркас приложения
|
||
(без React и npm).
|
||
2. Опиши экран JSON-конфигом по `design-system/docs/PRODUCT_CONTRACT.md` и положи его в тег
|
||
`<script id="kt-app-config" type="application/json">…</script>` внутри каркаса — он сам отрисует
|
||
таблицу, KPI, фильтры, drawer и темы.
|
||
3. Проверь конфиг: `python3 design-system/scripts/validate_product.py config.json --strict` → нужен `0/0`.
|
||
|
||
Полные правила для сложных экранов — `design-system/AGENTS.md`, список компонентов —
|
||
`design-system/COMPONENTS.md`, приёмка — `design-system/CHECKLIST.md`. Читай их, когда лендинга
|
||
уже мало.
|
||
|
||
---
|
||
|
||
## 📦 Готовые рецепты
|
||
|
||
### Фото без ключей
|
||
- Одно и то же слово → одна и та же картинка: `https://picsum.photos/seed/coffee/800/600`
|
||
- По теме: `https://loremflickr.com/800/600/coffee` (тег прямо в URL)
|
||
|
||
Вставляй с рамкой ДС: `<img src="…" alt="…" style="width:100%;border-radius:var(--kt-ai-radius-3xl)">`
|
||
|
||
### Кнопка WhatsApp
|
||
```html
|
||
<a class="kt-ai-btn" data-variant="primary" data-size="lg"
|
||
href="https://wa.me/7XXXXXXXXXX?text=Здравствуйте!">Написать в WhatsApp</a>
|
||
```
|
||
`7XXXXXXXXXX` — номер в международном формате без `+`.
|
||
|
||
### Форма заявки без бэкенда
|
||
```html
|
||
<form action="https://formsubmit.co/EMAIL" method="POST" style="max-width:420px">
|
||
<div class="kt-ai-field"><label>Имя</label><input class="kt-ai-input" name="name" required></div>
|
||
<div class="kt-ai-field"><label>Телефон</label><input class="kt-ai-input" name="phone" required></div>
|
||
<button class="kt-ai-btn" data-variant="primary" type="submit">Отправить</button>
|
||
</form>
|
||
```
|
||
Скажи юзеру: замени `EMAIL` на свою почту — заявки будут приходить туда (первая заявка активирует форму).
|
||
|
||
### Favicon-эмодзи (без файла)
|
||
```html
|
||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>☕</text></svg>">
|
||
```
|
||
|
||
### Карта
|
||
На maps.google.com: место → «Поделиться» → «Встроить карту» → вставь iframe как есть:
|
||
```html
|
||
<iframe src="https://www.google.com/maps/embed?pb=..." width="100%" height="320"
|
||
style="border:0;border-radius:var(--kt-ai-radius-3xl)" loading="lazy"></iframe>
|
||
```
|
||
|
||
### Плавные появления при скролле
|
||
```html
|
||
<style>
|
||
.reveal{opacity:0;transform:translateY(20px);transition:opacity .6s ease,transform .6s ease}
|
||
.reveal.in{opacity:1;transform:none}
|
||
</style>
|
||
<script>
|
||
var io=new IntersectionObserver(function(entries){
|
||
entries.forEach(function(e){ if(e.isIntersecting) e.target.classList.add('in'); });
|
||
},{threshold:.15});
|
||
document.querySelectorAll('.reveal').forEach(function(el){ io.observe(el); });
|
||
</script>
|
||
```
|
||
|
||
---
|
||
|
||
## Перед тем как сказать «готово»
|
||
|
||
- [ ] Подключены все четыре CSS из `design-system/`, папка лежит в проекте (не забудь закоммитить её).
|
||
- [ ] В вёрстке нет сырых hex-цветов и чужих шрифтов.
|
||
- [ ] Страница нормально выглядит в светлой и тёмной теме и на телефоне (узкое окно).
|
||
- [ ] Один `data-variant="primary"` на экран, тексты — про юзера, а не «Lorem ipsum».
|