Лілейка — брендбук і дизайн-система
Візуальна система для ProjectK — системи управління пластунським куренем. Версія 1.2 · остання правка: 2026-09-22
0. Швидкий контекст (читати першим, особливо якщо ти AI-агент)
Section titled “0. Швидкий контекст (читати першим, особливо якщо ти AI-агент)”Продукт. Самохостована система для одного куреня НСОУ «Пласт»: реєстр учасників, планування сходин, точкування, вмілості, документи. Один розробник, малий проєкт, свідомо мінімальний обсяг UI.
Стек. Angular 22 (standalone components, signals + RxJS) · @openng/optimus-ui 2 — сумісний із PrimeNG форк, тож усе нижче про PrimeNG-компоненти, severity й Aura-токени чинне без змін · Tailwind 4 (@import "tailwindcss" + @openng/optimus-ui-tailwindcss) · .NET 10 backend. Фронтенд: Frontend/projectk-frontend.
Дві назви — не помилка:
| Назва | Де вживається |
|---|---|
| Лілейка | продукт: вітальна сторінка, шапка сайдбара, <title>, листи, розмова з користувачем |
| ProjectK | код: репозиторій, README, Docker, env-змінні, CI, назви пакетів |
Ніколи не перейменовуй технічні артефакти на «Лілейка» і ніколи не показуй «ProjectK» користувачу в UI (крім рядка версії).
Десять правил, які покривають 90% рішень:
- Один зелений (
#0E6E4E), один теплий (#D9762F), чорнило, поверхня. Більше кольорів не вводити. - Терракота — тільки статуси й акценти. Ніколи не кнопка, не посилання, не «успішно».
- Один шрифт в інтерфейсі: Manrope. Рукописний Neucha — тільки логотип і порожні стани.
- Рамка 1px, ніколи 2px. Кольорових лівих смужок у картках немає.
- Радіуси:
8pxінтерактивне,12pxкартки й панелі. - Тіні — тільки модалки, дропдауни й скриншоти в маркетингу. Картки живуть на рамці.
- Ховер = зміна фону на
surfaceабо рамки наprimary. Без зсувів, масштабу й анімації розміру. - Порожні стани обовʼязкові й мають голос. Один рукописний рядок, один рядок пояснення, одна дія.
- Іконки —
pi pi-*з PrimeIcons; коли потрібної там немає —icon-*з Lucide (lucide-static, той самий шрифтовий підхід, приклад:icon-bug). Понад ці два набори нічого не додавати, SVG не малювати. - Емодзі в UI немає. Ніде.
Що НЕ можна змінювати: червона табличка з числом куреня (kurin-number.css, #b30003 / #ff0005) — це пластова відзнака, а не елемент бренду. Див. §7.
1. Знак
Section titled “1. Знак”Скаутська лілея (fleur-de-lis) у лінійному виконанні, нахилена на 45° вправо. Складники: центральна пелюстка-вістря (терракотова), два бічні завитки, одинарна поперечка й нижнє вістря — зелені (або currentColor). Знак завжди fill="none", тонка лінія з круглими кінцями. Офіційна емблема НСОУ «Пласт» (лілея з тризубом) не використовується і не відтворюється — це власний узагальнений скаутський fleur-de-lis.
Геометрія (viewBox 0 0 100 100, усе всередині <g transform="rotate(45 50 50)">):
| Частина | Path |
|---|---|
| Правий завиток | M50 44 C54 33 62 28 71 33 C79 38 80 48 73 53 C68 56 62 55 60 51 |
| Лівий завиток | M50 44 C46 33 38 28 29 33 C21 38 20 48 27 53 C32 56 38 55 40 51 |
| Поперечка | M33 60 H67 |
| Нижнє вістря | M50 88 C46 80 46 71 50 65 C54 71 54 80 50 88 Z |
| Центральна пелюстка (терракота) | M50 7 C45 24 45 43 50 57 C55 43 55 24 50 7 Z |
| Файл | Призначення |
|---|---|
assets/lileyka-mark.svg |
основний, двоколірний (зелений + терракотова центральна пелюстка) |
assets/lileyka-mark-mono.svg |
currentColor, весь знак одним кольором — для друку/одноколірних контекстів |
assets/lileyka-mark-dark.svg |
для темного фону (#34C48D + #EE9A5A) |
assets/favicon.svg |
64px, білий знак на зеленому квадраті r=14, центр терракотою #D9762F |
assets/favicon-16.svg |
16px, той самий знак, товща лінія для читабельності |
Правила
Section titled “Правила”- Форма — скаутський fleur-de-lis, нахил 45°. Попередні варіанти (дво-/трипелюсткова лілея) не відтворювати.
- Вільне поле навколо знака — висота нижнього вістря (≈ 20% висоти знака).
- Товщина обведення: 5.5 для основного, 6–6.5 для дрібних фавіконів. Знак завжди
fill="none",stroke-linecap="round". - Центральна пелюстка терракотова на всіх поверхнях, і на зеленому теж: на фавіконі, банері, у шапці сайдбара й на аватарках знак білий, а центр — той самий
#D9762F(у темній темі#EE9A5A). Світлішого «клею» для зеленого немає: колишній#F5C99Bне був кольором палітри і розходився з тим, що малює застосунок. Виняток: одноколірна версія (lileyka-mark-mono.svg) лишається в один колір. Джерело правди кольору — клас.lil-mark-centerу застосунку (stroke: var(--lil-clay-500)). - Нахил 45° уже «вшитий» у файли (
rotate(45 50 50)) — додатково не обертати. Не заливати, не додавати тінь чи градієнт. Не ставити в коло, крім аватарки користувача.
Логотип (lockup)
Section titled “Логотип (lockup)”Знак + слово «Лілейка» у Neucha, горизонтально, відступ між ними = 0.4× висоти знака, вирівнювання по оптичному центру. Вертикального варіанта немає — не вигадувати.
2. Колір
Section titled “2. Колір”Світла тема
Section titled “Світла тема”| Роль | Hex | Де |
|---|---|---|
primary |
#0E6E4E |
кнопки, активний пункт меню, посилання, акцентні числа |
primary-hover |
#0B5A40 |
ховер кнопки |
primary-active |
#084733 |
натиснута кнопка |
primary-50 |
#EAF3EE |
фон активного пункту меню, фон іконки-плашки, тег «Admin» |
accent |
#D9762F |
терракота — див. обмеження нижче |
accent-50 |
#FBE9DC |
фон тега «Незабаром», банер cold start |
accent-700 |
#A8541B |
текст на accent-50 |
ink |
#101413 |
заголовки, основний текст |
ink-soft |
#2E3633 |
текст у пунктах меню |
muted |
#6E7873 |
підписи, другорядний текст |
faint |
#9AA39F |
мета-дані, підписи полів, іконки в спокої |
line |
#E4E8E6 |
усі рамки й розділювачі |
surface |
#F7F9F8 |
фон сторінки, ховер рядків |
paper |
#FFFFFF |
картки, панелі, модалки |
cream |
#FBF8F1 |
тільки маркетинг: OG-image, фон hero-ілюстрації |
Темна тема
Section titled “Темна тема”| Роль | Hex |
|---|---|
primary |
#34C48D |
primary-hover |
#4FD49E |
primary-bg |
#1D2E27 |
accent |
#EE9A5A |
accent-bg |
#2A1F16 |
ink |
#E9EDEA |
ink-soft |
#DDE2DF |
muted |
#9BA8A2 |
faint |
#6C7570 |
line |
#232A27 |
ground |
#0D1110 |
surface |
#161B19 |
hover-bg |
#1D2320 |
Темна тема вмикається document.documentElement.dataset.theme = 'dark' (darkModeSelector: '[data-theme="dark"]' у preset).
Терракота — вичерпний список місць
Section titled “Терракота — вичерпний список місць”Дозволено: тег «Незабаром» · крапка «є що модерувати» в сайдбарі · банер cold start · іконка pi-sparkles у «Зроблено пластунами для пластунів» · центральний листок знака · акцентне слово в маркетинговому заголовку («свисти алярми»).
Заборонено: кнопки, посилання, статус «успішно», графіки, фони секцій, hover-стани, рамки карток.
Правило одної плями: на будь-якому екрані терракота зʼявляється не більше ніж в одному місці одночасно. Якщо їх два — одне з них не потрібне.
Виняток — палітра категорій агенди. Категорія заходу (табір, сходини, вишкіл) має власний колір,
який Звʼязковий обирає з семи закритих значень у agenda-category-manager.ts (BRAND_COLORS) і
який зберігається в базі разом із категорією. Це єдиний hex-список у коді фронтенду: він є
даними, а не стилем, і саме тому не зводиться до токенів теми. Розширювати його — лише тут і лише
тонами, що тримають контраст із білим текстом бейджа.
Виняток — дошка задач (Задачі / Agenda). У палітрі немає жовтого, а статусна тріада «Зробити → В процесі → Зроблено» потребує теплого середнього тону. Тому на дошці задач warn-теракота закріплена за статусом «В процесі» (тег p-tag severity="warn") і може повторюватися на кількох картках одночасно — це свідомий виняток із правила одної плями, обмежений сторінкою дошки та статус-тегами календаря. Інші статуси: «Зробити» — secondary (сірий), «Зроблено» — info (зелений/primary). Джерело правди — agenda-status.config.ts.
Виняток — синій у календарі. Щоб на календарі події не зливалися із задачами (які фарбуються статусними кольорами сірий/теракота/зелений), події позначаються приглушеним синім тегом. Це єдине місце, де вводиться синій — токени --lil-blue-50 / --lil-blue-700 (світла/темна тема) у lileyka-theme.css, застосовані лише через токени --lil-agenda-event-* (клас .agenda-ev--event на сторінці календаря). Ніде інде синій не використовується.
Плашки на сітці календаря беруть кольори з токенів --lil-agenda-{todo|progress|done|event}-{bg|line|fg} у lileyka-theme.css — по набору на світлу й темну тему. Підкладка й рамка — ті самі сірий/теракота/зелений, що й у тегах статусів на дошці, плюс календарний синій для подій; текст на плашках — чорнило, як числа днів; ними ж фарбуються відповіді RSVP («йду» — зелений, «можливо» — теракота, «не йду» — сірий). Нових плашкових кольорів не вводити — розширювати лише ці токени. Колір категорії на плашці ніколи не є заливкою як є: він замішується в папір за коефіцієнтами --lil-agenda-cat-{bg|line|dot}-mix (світла тема — тінт 14 % під чорнильним текстом, темна — 70 %, майже суцільна смуга: рівно стільки, щоб золото «Таборів» тримало 4.5:1), а сама категорія впізнається по крапці. Без цього сім темних кольорів палітри, дібраних під білий текст бейджа, у світлій темі стояли б суцільними темними плитами поруч із пастельними плашками задач.
Статуси (PrimeNG severity)
Section titled “Статуси (PrimeNG severity)”| severity | Значення | Колір |
|---|---|---|
success |
підтверджено, вмілість зарахована | зелений з Aura |
info |
«Незабаром», нейтральна мітка | #EAF3EE / #0B5A40 — перевизначено на зелений, не синій |
warn |
очікує модерації, cold start | терракота |
danger |
ліміт, помилка, видалення | червоний з Aura |
secondary |
роль, службова мітка | faint |
3. Типографіка
Section titled “3. Типографіка”| Роль | Шрифт | Розмір | Вага | Tracking | Токен |
|---|---|---|---|---|---|
| h1 маркетинг | Manrope | 62px (clamp до 40px) | 800 | −4% | — (тільки сайт і вітальна) |
| h1 в системі | Manrope | 28px | 800 | −2.5% | --lil-text-h1 · --lil-tracking-h1 |
| h2 | Manrope | 24px | 800 | −2% | --lil-text-h2 · --lil-tracking-h2 |
| h3 / заголовок картки | Manrope | 17px | 800 | −1% | --lil-text-h3 · --lil-tracking-h3 |
| body | Manrope | 15px | 400 | 0 | --lil-text-body |
| UI (кнопки, меню) | Manrope | 15px | 600–700 | 0 | --lil-weight-ui |
| підпис / мета | Manrope | 13px | 400–600 | 0 | --lil-text-caption |
| мікропідпис | Manrope | 11px | 700, uppercase | +18% | --lil-text-micro · --lil-tracking-micro |
| логотип | Neucha | 29–50px | 400 | 0 | — |
| порожній стан — заголовок | Neucha | 24px | 400 | 0 | — |
| код, версія, ключі | ui-monospace | 11–12px | 400 | 0 | — |
Line-height: 1.02 для великих заголовків, 1.15 для h2–h3 (--lil-leading-heading), 1.5–1.55 для тексту (--lil-leading-body), 1.45 для дрібного. Вага заголовків — --lil-weight-heading (800).
Шкала живе в lileyka-theme.css як токени, і голі h1 / h2 / h3 в застосунку вже набрані нею. Tailwind-preflight скидає заголовки до розміру тексту, тому раніше кожна сторінка виставляла числа сама — і виставляла свої. Тепер правило одне: розмір і вага заголовка не пишуться в CSS сторінки і не вішаються Tailwind-класами (text-xl, font-bold на заголовку — помилка ревʼю). Сторінка обирає лише роль — заголовок сторінки (§5 «Сторінка») чи заголовок картки (§5 «Картка») — і роль приносить розмір. Виняток — маркетингові h1 на сайті й вітальній.
Чому не Karla і не Patrick Hand
Section titled “Чому не Karla і не Patrick Hand”Перша редакція брендбука називала Karla й Patrick Hand. Жоден із них не має кириличного набору — весь український текст падав на system-ui і виглядав по-різному на кожній ОС. Замінено на Manrope (той самий нейтральний гротеск, ваги 400–800) і Neucha (рукописний), обидва з повною кирилицею.
Neucha при однаковому кеглі дає ascent приблизно на 10% нижчий за Patrick Hand, тож розміри рукописного в таблиці підняті рівно настільки — оптично це той самий рядок.
Підключення
Section titled “Підключення”Шрифти локальні, не з CDN: самохост має підніматись без інтернету.
public/assets/fonts/├── manrope-var-cyrillic.woff2 ← варіативний, 400–800├── manrope-var-latin.woff2├── manrope-var-latin-ext.woff2├── neucha-400-cyrillic.woff2└── neucha-400-latin.woff2@font-face — у секції 0 lileyka-theme.css, з unicode-range від Google Fonts. Кирилиця Manrope додатково в <link rel="preload"> у index.html: нею набрано весь інтерфейс. Разом ~112 kB.
Neucha не використовується ніде, крім логотипу й заголовків порожніх станів. Не в кнопках, не в тегах, не в тултипах, не в заголовках сторінок.
4. Ритм, форма, рух
Section titled “4. Ритм, форма, рух”Відступи. Крок 4px. У реальному вжитку: 8 · 12 · 14 · 20 · 28 · 56. Внутрішній відступ картки — 20px (мобільний 16px, --lil-tile-pad), панелі — 24px, секції сторінки — 56px.
Ширина контенту. min(100% - 2rem, 72rem) — у темі це .lil-page з токенів --lil-page-width (72rem), --lil-page-gutter (1rem з кожного боку), --lil-page-block (2rem зверху й знизу, 1rem на мобільному) і --lil-page-gap (20px між плитками). Ширші сторінки — лише .lil-page--wide (84rem, --lil-page-width-wide): реєстр із десятьма колонками. Вужчі — перевизначенням --lil-page-width на сторінці (імпорт, 64rem). Власних width: min(...) у CSS сторінок більше не пишуть.
Радіуси. 8px — кнопки, поля, теги, пункти меню. 12px — картки (.kurin-tile), панелі, модалки. 14px — favicon, hero-ілюстрація. 50% — тільки аватарки й крапки-індикатори.
Рамки. Завжди 1px solid var(--lil-line). Ніяких 2px, ніяких кольорових лівих смужок, ніяких подвійних рамок. Пунктирна рамка (1px dashed) — тільки порожні стани й вимкнені елементи.
Тіні. Дозволено: модалка/дропдаун (0 12px 40px rgba(16,20,19,0.07)), скриншот продукту в маркетингу (0 20px 52px rgba(16,20,19,0.10)). Все інше — без тіні. Наявна тінь .kurin-tile (0 8px 26px -22px …primary…) прибирається.
Ховер. Фон → surface (світла) / hover-bg (темна), або рамка → primary. Перехід background-color 120ms ease, border-color 120ms ease. Без transform, без box-shadow, без зміни розміру.
Вимкнено. opacity: 0.4 + пунктирна іконка. Не сірий текст — прозорість, щоб було видно, що елемент є, але поки не працює.
Анімація. Максимум 200ms, ease. Плавний рух є лише в трьох місцях: пунктирна лінія в порожньому стані (stroke-dashoffset, 1.6s linear infinite), спінер cold start, поява дровера. Логотип на вітальній може мати легке коливання 6s — більше ніде. Прев’ю продукту на вітальній міняє кадр раз на 6 с із переходом 200ms; це зміна стану, а не рух, і за prefers-reduced-motion кадри не гортаються самі.
4.1 Рух у маркетингу
Section titled “4.1 Рух у маркетингу”Діє тільки на маркетингових поверхнях: сайт site/, OG-зображення, презентація, відео. У застосунку чинний §4 без жодних послаблень — усе нижче туди не переноситься.
Правило, з якого випливає решта: кадром керує прокрутка, а не таймер. Темп задає людина. Ніщо не рухається саме собою, доки вона стоїть.
- Один липкий кадр на секцію, до пʼяти станів. Показуємо один продукт, що змінюється, а не пʼять картинок поспіль. Шостий стан означає, що секцію треба різати надвоє.
- Перехід між станами — 200ms,
ease. Те саме число, що в застосунку; довших переходів немає й тут. - Дозволений рух — лише
opacityіtranslateпо одній осі, не більше 24px. Безscale, безrotate, без паралакса, без руху фону. Продукт не «прилітає» — він змінюється. - Техніка — нативні
animation-timeline: view()/scroll(). Бібліотек анімації не додавати: п’ять станів їх не потребують, а кожна з них — це ще один пакет у постачанні. - Стовпчик — стан за замовчуванням, а не запасний. За
prefers-reduced-motion: reduceі там, деanimation-timelineне підтримується, усі кадри показуються один під одним, без липкості. Спершу пишеться стовпчик, липкість накладається зверху. - Тінь на скриншоті — за §4:
0 20px 52px rgba(16,20,19,0.10). Рамка лишається 1px. - Відео — без звуку, без системного курсора, петля до 6 с. За
prefers-reduced-motionзамість відео показується постер.
Терракота й тут за §2: один акцент на екран, ніколи не кнопка й не посилання.
5. Компоненти
Section titled “5. Компоненти”Усі — PrimeNG. Нові компонентні бібліотеки не додавати.
Кнопки
Section titled “Кнопки”| Варіант | Коли | PrimeNG |
|---|---|---|
| primary заповнена | головна дія екрана, одна на екран | <p-button label="Зберегти" /> |
| secondary контурна | альтернативна дія | severity="secondary" |
| text | третинна, «Скасувати», «Розгорнути» | severity="secondary" variant="text" |
| з іконкою | завжди iconPos="left", крім «Відкрити →» |
icon="pi pi-plus" |
| icon-only | тулбар, дії в рядку таблиці | text severity="secondary" |
Висота 40px (size="small" 34px), горизонтальний відступ 20–28px. severity="info" для кнопок не використовувати — у поточному коді (kurin-panel) вона стоїть на «Створити» / «Додати учасника»; замінити на дефолтну primary.
Поля введення
Section titled “Поля введення”Мітка над полем: 13px, faint, вага 600. Поле: рамка line, радіус 8px, висота 40px, фокус — рамка primary + outline: 2px solid primary-50. Помилка — рамка danger + рядок 13px під полем. Форма — display: grid; gap: 1rem, три колонки на десктопі, одна на мобільному (як уже в .profile-form).
Сторінка
Section titled “Сторінка”Кожен екран застосунку складається з трьох речей, і всі три є в темі:
<div class="lil-page"> <!-- колонка: ширина, поля, відступ між плитками --> <header class="lil-page-head"> <div> <p class="lil-page-head__eyebrow">Налаштування куреня</p> <!-- необовʼязково --> <h1>Реєстр</h1> <p>12 курінь · УПЮ · 29 у складі</p> <!-- підпис 13px muted --> </div> <div class="lil-page-head__actions">…одна primary, решта secondary…</div> </header>
<section class="kurin-tile">…</section> <section class="kurin-tile">…</section></div>h1 у шапці — 28px/800 з токенів, без класів. Шапка над плитками, не всередині першої: заголовок належить сторінці, а не її першій таблиці. На ≤768px шапка стає стовпчиком, дії йдуть під заголовок. Виняток — сторінки-профілі (курінь, гурток, картка людини): там шапка сама є плиткою з фото чи числом куреня, і h1 живе в ній — але набирається тими самими токенами.
Картка / плитка
Section titled “Картка / плитка”.kurin-tile — базовий контейнер контенту:
.kurin-tile { background: var(--p-content-background); border: 1px solid var(--p-content-border-color); border-radius: 12px; box-shadow: none; padding: var(--lil-tile-pad); /* 20px, на ≤640px — 16px */}Шапка картки — .lil-tile-head, один клас на всі плитки й на <ng-template #caption> кожної p-table:
<div class="lil-tile-head"> <div> <h2>Гуртки</h2> <!-- 17px/800 незалежно від того, h2 це чи h3 --> <span class="lil-count">3</span> <!-- лічильник — плашка, не частина тексту --> <p>2 активних</p> <!-- підпис 13px muted --> </div> <div class="lil-tile-head__actions">…</div> <!-- праворуч, margin-left: auto --></div>Рівень заголовка — семантичний, не візуальний: плитка, що є розділом сторінки, бере h2; блок усередині плитки — h3. Розмір від цього не міняється, його дає роль. Копій .table-caption, .kv-caption, .leadership-caption і подібних у CSS сторінок немає — це все одна .lil-tile-head.
Таблиці (.project-table)
Section titled “Таблиці (.project-table)”Хедер: 11px, uppercase, tracking +18%, faint, фон surface, без рамок між колонками. Рядок: висота 52px, розділювач 1px line, ховер — фон surface. Головна колонка — назва 15px/650 + мета 13px muted під нею (як .group-name-cell). Колонка дій — праворуч, фіксована ширина, кнопки icon-only або splitButton. Мобільний — рядок стає flex, хедер display: none (уже реалізовано, лишається).
Порожня таблиця — не p-message, а порожній стан із §6.
Теги (p-tag)
Section titled “Теги (p-tag)”Радіус 6px, 11px/700, відступ 3px 9px. Роль користувача — secondary. «Незабаром» — info (зелений, не синій). «Очікує модерації» — warn (терракота). Ліміт CBT — danger при досягненні, інакше info.
Тулбар
Section titled “Тулбар”Висота 56px, фон paper, нижня рамка 1px line, без тіні. Ліворуч — бургер (pi pi-bars, text secondary). Праворуч — дзвіночок нотифікацій і вихід, gap: 0.35rem (уже в .toolbar-actions). Логотипа в тулбарі немає — він у сайдбарі.
Шлях (p-breadcrumb) — одразу за бургером, gap: 0.75rem (у .toolbar-start), без власного фону й рамки, підписи 13px; на ≤768px ховається. Домик веде в корінь ролі (курінь → /kurin, адмін поза куренем → /panel, інакше картка учасника) з підказкою-назвою місця. Сам корінь і все вище нього в ланцюжку не дублюється — з куреня в адміністрацію виходять кнопкою «До адміністрації».
Крихта, що позначає сутність (гурток, учасник, курінь), підписана її власною назвою у тому ж форматі, що й <title>: г. Соколи, Шевченко Тарас, к. ч. 12 (форматери в page-title.format.ts). Поки назва вантажиться або якщо не завантажилась — лишається загальний підпис маршруту («Гурток», «Картка учасника»).
Крихту, посилання якої нічим заповнити (ментор без гуртка), не показуємо взагалі — шлях будується за наявними даними. Порожній Guid не є даними: перевіряй ключі через isUsableKey.
Сайдбар (p-drawer headless)
Section titled “Сайдбар (p-drawer headless)”Шапка — зелений банер: фон primary, padding: 1.25rem, знак 32px білим + «Лілейка» Neucha 31px білим. Замінює <h2>Project K</h2>. Бічну рамку дровера прибрано — на зеленому вона світилась смужкою; відділяє тінь і підкладка.
Пункти: висота 42px, gap: 0.75rem, радіус 8px, 15px/600 ink-soft. Активний — фон primary-50, текст і іконка primary, вага 700. Вимкнений — opacity: 0.4 + пунктирна іконка. Індикатор «є що модерувати» — крапка 7px терракотою праворуч, без числа.
Низ: рядок версії 11px monospace faint, під ним рамка й блок користувача — іконка, e-mail 13px, тег ролі.
Порядок пунктів не змінювати — він залежить від ролей і будується в sidebar-menu.ts.
Модалки й дровери
Section titled “Модалки й дровери”Фон paper, радіус 12px, тінь 0 12px 40px rgba(16,20,19,0.07), заголовок 20px/800, підкладка rgba(16,20,19,0.32). Дії — праворуч знизу, gap: 0.5rem, порядок: text «Скасувати», потім primary.
Порожній стан
Section titled “Порожній стан”Обовʼязковий скрізь, де може не бути даних. Структура:
<div class="lil-empty"> <!-- проста лінія/фігура, stroke: line, 110×66 --> <div class="lil-empty__title">Тут ще нікого немає</div> <div class="lil-empty__body">Додай першого юнака — і реєстр почне жити.</div> <p-button label="Додати учасника" /></div>Рамка 1px dashed, радіус 12px, padding: 1.5rem, вирівнювання по центру, gap: 0.875rem. Ілюстрація — обведені прості фігури (кола, прямокутники, лінії) кольором line, одна деталь терракотою. Ніяких намальованих сцен і персонажів.
Cold start banner
Section titled “Cold start banner”Наявний компонент лишається за структурою. Кольори: фон accent-50, рамка 1px accent-100, текст accent-700, спінер accent. Заголовок 15px/700, повідомлення 13px.
6. Каталог whimsy-меседжів
Section titled “6. Каталог whimsy-меседжів”Формула: рукописний заголовок (без крапки) + рядок пояснення (з крапкою) + одна дія. Заголовок описує стан, а не наказує. Гумор — тихий, пластовий, без жартів про користувача.
| Місце | Заголовок | Пояснення | Дія |
|---|---|---|---|
| Реєстр порожній | Тут ще нікого немає | Додай першого юнака — і реєстр почне жити. | Додати учасника |
| Планування порожнє | Календар ще спить | Перша сходина в плані — і він прокинеться. | Створити планування |
| Точкування порожнє | Точкування чисте | Ще ніхто не заробив і не втратив пункта. | Поставити точки |
| Пошук без результатів | Нічого не знайшлось | Спробуй коротший запит — або перевір гурток. | Скинути фільтри |
| Гуртки порожні | Гуртків поки немає | Курінь без гуртків — як мандрівка без стежки. | Створити гурток |
| Вмілості порожні | Вмілостей ще немає | Заявки на вмілості зʼявляться тут. | — |
| Модерація порожня | Все розглянуто | Заявок на вмілості немає. Можна відпочити. | — |
| Документи (незабаром) | Документи в дорозі | Статути й накази житимуть тут. Скоро. | — |
| Cold start | Сервер ще прокидається | Самохост після сну. Дай йому пів хвилини. | Пробуємо знову… |
| 403 | Сюди тобі не можна | Ця сторінка для впорядників. Твоя роль — юнак. | На мій профіль |
| 404 | Такої стежки немає | Можливо, посилання застаріло. | На головну |
| 500 | Щось пішло не так | Ми вже знаємо. Спробуй за хвилину. | Оновити |
| Офлайн | Звʼязку немає | Перевір інтернет — дані збережені локально. | — |
| Успішне збереження (toast) | Збережено | — | — |
Toast-повідомлення не рукописні й не жартівливі: «Збережено», «Учасника додано», «Не вдалося зберегти». Whimsy живе тільки в порожніх станах і на екранах помилок.
7. Захищені елементи
Section titled “7. Захищені елементи”Число куреня (kurin-number.css) — біле число на червоному полі #b30003 з рамкою #ff0005, Arial. Це відтворення пластової курінної таблички, а не елемент бренду. Не перефарбовувати в зелений, не змінювати шрифт, не додавати радіус. Єдине, що дозволено — розміри для адаптиву (уже є).
Ілюстрація scouts-main.png — лінійна графіка пластунів, чорна. Лишається чорною на світлому фоні. Не тонувати в зелений, не інвертувати. У темній темі — на світлій підкладці #F7F9F8 з радіусом 16px.
8. Тон копірайтингу
Section titled “8. Тон копірайтингу”- Звертання на «ти», не на «Ви». Це система для своїх.
- Пластова термінологія без пояснень: курінь, гурток, юнак, впорядник, сходина, точкування, вмілість, алярм, станиця, край.
- Короткі речення. Одна думка — одне речення.
- Заголовки кнопок — дієслово в інфінітиві: «Додати учасника», «Створити планування», «Зберегти».
- Не писати «будь ласка», «на жаль», «упс», «ой». Не вигукувати.
- Не пояснювати очевидне. «Додати учасника» не потребує підпису «натисніть, щоб додати учасника».
- Технічні терміни в UI українською: не «submit», а «Надіслати». Виняток — назви ролей (
Admin) і технічні ключі.
9. Файли й підключення
Section titled “9. Файли й підключення”Frontend/projectk-frontend/├── public/assets/│ ├── README.md ← маніфест бренд-ассетів (джерело правди)│ ├── favicon.svg ← <link rel="icon">│ ├── favicon-16.svg│ ├── lileyka-mark.svg│ ├── lileyka-mark-mono.svg│ ├── lileyka-mark-dark.svg│ ├── lileyka-banner-1080x288.svg ← простий банер; README бере .github/assets/banners/readme-*.png│ ├── lileyka-og-1200x630.svg ← og:image│ └── fonts/ ← Manrope + Neucha, woff2 (див. §3)└── src/ ├── index.html ← favicon + preload кирилиці + <title>Лілейка</title> ├── styles.css ← @import "./lileyka-theme.css"; ├── lileyka-theme.css ← @font-face + токени + .lil-* класи └── app/ ├── lileyka-preset.ts ← preset для PrimeNG └── app.config.ts ← preset: LileykaPresetНазва системи — «Лілейка», стала: environment.appName у src/environments/*.ts, без runtime-перевизначення. Змінну PROJECTK_APP_NAME прибрано в 1.0 — ліцензія не дозволяє запускати систему під іншою назвою, тож і налаштування для цього бути не повинно.
Що йде в preset, а що в CSS
Section titled “Що йде в preset, а що в CSS”Не все перекривається через CSS-змінні. PrimeNG вставляє свою тему в <head> після styles.css, тож:
- Тільки в
lileyka-preset.ts: компонентні токени (tag,message— Aura береinfoз{sky.*}, аwarnз{orange.*}), шкалаprimitive.borderRadius,semantic.colorScheme.*.text. Змінні--p-info-*/--p-warn-*із теми до них не доходять. - Темна схема Aura читає
surfaceнавпаки:surface.0— це колір тексту,900— фон контенту,950— тло,700— рамка. Шкала йде від світлого до темного, інакше буде темний текст на темному тлі. - У CSS однокласові селектори (
.p-tag,.p-toolbar) потребують префікса:root— інакше програють темі PrimeNG за порядком у каскаді.
app.config.ts:
import { LileykaPreset } from './lileyka-preset';
providePrimeNG({ translation: { /* без змін */ }, theme: { preset: LileykaPreset, options: { darkModeSelector: '[data-theme="dark"]' } }})Окремі класи, які додає lileyka-theme.css: .lil-wordmark (логотип) · .lil-sidebar-brand (шапка сайдбара) · .lil-empty, .lil-empty__art, .lil-empty__title, .lil-empty__body, .lil-empty__accent, .lil-empty__dash (порожній стан). Плюс перевизначення .kurin-tile, .project-table, а також :root .p-toolbar, :root .p-tag, :root .p-panelmenu.lil-menu.
Порожній стан у коді
Section titled “Порожній стан у коді”Розмітку .lil-empty руками не писати — є app-empty-state (src/app/shared/empty-state/):
<app-empty-state art="list" title="Тут ще нікого немає" body="Додай першого юнака — і реєстр почне жити."> <p-button label="Додати учасника" icon="pi pi-plus" (onClick)="create()" /></app-empty-state>art — trail (типово), list, calendar. Дія проєктується через ng-content: якщо прав немає, просто не передавай нічого. Текст брати з каталогу §6.
Якщо та сама дія вже є в шапці списку — сховати її, поки список порожній. Інакше на екрані дві однакові primary-кнопки, що суперечить §5.
Кольори пунктів меню живуть у navigation.item.* у preset, а не в CSS: PrimeNG фарбує обгортку .p-panelmenu-*-content, тож правило на вкладене посилання дає два накладені прямокутники на ховері.
10. Чекліст ревʼю екрана
Section titled “10. Чекліст ревʼю екрана”- Терракота зʼявляється не більше ніж один раз
- Neucha тільки в логотипі або порожньому стані
- Усі рамки 1px
line, тіней немає (крім модалки) - Радіуси 8px / 12px, інших немає
- Одна primary-кнопка на екран
- Є порожній стан для кожного списку
- Немає емодзі й намальованих SVG-сцен
- Іконки — тільки
pi pi-* - Кольори взяті з токенів, hex у компонентах немає (крім числа куреня)
- Темна тема перевірена: контраст тексту ≥ 4.5:1
- «ProjectK» не показано користувачу (крім рядка версії)
- Тексти на «ти», без «будь ласка» й «упс»
11. Довідник для агента: типові задачі
Section titled “11. Довідник для агента: типові задачі”«Додай новий екран.» <div class="lil-page"> → <header class="lil-page-head"> з голим h1 → плитки <section class="kurin-tile">, кожна з .lil-tile-head (§5 «Сторінка», «Картка»). Жодних font-size на заголовках і жодних text-*/font-* Tailwind-класів на них — розмір дає роль. Дані — p-table styleClass="project-table" або dl.identity-grid. Обовʼязково порожній стан із каталогу §6.
«Додай кнопку.» Одна primary на екран. Решта — severity="secondary" або variant="text". severity="info" не використовувати.
«Зроби це помітнішим.» Спочатку вага шрифту й розмір, потім primary, і лише в крайньому разі терракота — і тільки якщо її на екрані більше немає.
«Додай іконку.» Знайти в PrimeIcons (pi pi-*). Якщо нема — обійтися без іконки. Не малювати SVG, не додавати бібліотеку.
«Додай нову сутність у меню.» Тільки через buildItems() у sidebar-menu.ts, з перевіркою прав через PermissionService. Порядок наявних пунктів не змінювати.
Чого не робити без запиту: не додавати градієнти, не вводити третій колір, не міняти шрифт, не додавати анімації понад §4, не перефарбовувати число куреня, не додавати емодзі, не переписувати scouts-main.png.