Design system / source of truth

Предгорье UI Kit

Спецификация общего визуального слоя для портала и самостоятельных модулей. Здесь зафиксированы токены, размеры, отступы, состояния, адаптивное поведение, готовая разметка и правила использования компонентов.

v0.2 · prototype baselinedeveloper-ready
CSS ordertokens → components → page
Prefixpg-
Container1240 px max
Breakpoints960 / 720 px
Control44 px default
Ruleникаких локальных «почти таких же» значений
00 / CONTRACT

Что является источником истины

UI Kit не описывает дизайн «примерно». Значения ниже должны использоваться в коде напрямую. Если компоненту нужна новая величина, сначала решаем, это новый системный token или локальное исключение, и только потом добавляем CSS.

Import contract

Порядок подключения

assets/css/*.css
1predgorie-tokens.css — шрифты, цвета, spacing, radii, type scale, z-index.
2predgorie-components.css — reusable UI primitives и составные компоненты.
3CSS конкретной страницы — только layout страницы и уникальная композиция, не переопределение базовых компонентов.
JS hooksИспользовать data-*. Классы pg-* принадлежат визуальному слою.
Inline stylesНе использовать в production-разметке. Они допустимы только в этой документации для визуализации токенов.
<link rel="stylesheet" href="assets/css/predgorie-tokens.css">
<link rel="stylesheet" href="assets/css/predgorie-components.css">
<link rel="stylesheet" href="assets/css/page-name.css">
Основное правило: если на двух страницах должны выглядеть одинаково «заголовок + текст», кнопка, поле, карточка или состояние — это один компонент/recipe. Не копируем визуально похожие значения вручную.
01 / COLORS

Цветовая система

Пять брендовых/базовых цветов плюс нейтрали и семантические состояния. Карточки кликабельны: копируется CSS variable.

Usage

Семантика цветов

Blueглавный brand/action color; основные заголовки, primary actions, эфирные поверхности.
Air Blueвторичный информационный акцент; kicker, auxiliary surfaces, alternate routes.
Greenживое / актуальное / позитивный акцент; не использовать как универсальную «зелёную кнопку подтверждения».
Paper / WhitePaper — фон сайта; White — активные поверхности и контентные блоки.
Warmобщественные/архивные/вторичные акцентные поверхности.
Danger / Warning / Successтолько семантические состояния, не декоративные темы.
02 / SPACING

Единая шкала отступов

Шкала 4–128 px. Значения 28/30/34 px встречаются как конкретные композиционные правила, но не являются свободными spacing tokens.

Tokens

Spacing scale

--pg-space-1 … --pg-space-12
space-1
4
space-2
8
space-3
12
space-4
16
space-5
20
space-6
24
space-7
32
space-8
40
space-9
56
space-10
72
space-11
96
space-12
128
4–12внутри маленьких controls, иконка ↔ текст, micro metadata.
16–24типовой вертикальный ритм карточек и полей.
32–56между смысловыми группами и в section headers.
72–128section rhythm / крупные page-level интервалы.
03 / GEOMETRY

Радиусы, обводки, тени

Радиусы не выбираются «на глаз». У controls, panels, hero/media и pill-элементов разные уровни геометрии.

Radius tokens

6 уровней скругления

XS · 8
S · 12
M · 20
L · 28
XL · 42
Round
XS 8docs/nav micro containers, локальные служебные элементы.
S 12inputs, alerts, spec cards.
M 20ride card, consent banner, album media, secondary surfaces.
L 28panels, module/news/event/service/goods cards, modal dialog.
XL 42крупный broadcast container.
Roundbuttons, tags, status, segment, circular controls.
Borders & shadows

Глубина

Border only
1px · --pg-line
Shadow S
hover / lifted card
Shadow M
hero / broadcast
Default border1px solid var(--pg-line)
Strong border1px solid var(--pg-line-strong) — controls / structural divider.
Shadow S0 10px 28px rgba(0,54,167,.08)
Shadow M0 22px 58px rgba(0,54,167,.12)
Overlay0 26px 90px rgba(0,0,0,.24)
04 / LAYOUT

Контейнеры, сетки, stack / row

Базовые layout primitives предназначены для простых повторяемых раскладок. Page-specific layout можно писать отдельно, если он не претендует на повторное использование.

Container

Shell

.pg-shell
viewport
.pg-shell · max 1240px · desktop side gap 20px / mobile 14px
Desktopwidth:min(calc(100% - 40px), 1240px)
≤720 pxwidth:min(calc(100% - 28px), 1240px)
Alignmentmargin-inline:auto
Grid primitives

2 / 3 / 4 columns

.pg-grid
1
2
3
4
Gap24 px / --pg-space-6
≤960 px3/4-column grids → 2 columns.
≤720 px2/3/4-column grids → 1 column.
Columnsminmax(0,1fr) обязательно, чтобы длинный контент не раздвигал viewport.
<div class="pg-grid pg-grid--3">
  <article>…</article>
  <article>…</article>
  <article>…</article>
</div>
Flow primitives

Stack / row

.pg-stack · .pg-row
stack item24 px gapstack item
row12 px gapwrap
StackGrid, gap 24 px. .pg-stack--tight → 12 px.
RowFlex, align center, gap 12 px, wrap enabled.
RuleДля вертикальной композиции группы предпочитать stack/recipe вместо цепочки случайных margins.
05 / TYPOGRAPHY

Типографическая шкала

Victor Mono — голос бренда и большие смысловые акценты. IBM Plex Mono — всё информационное: body, данные, navigation, controls, формы и metadata.

Font families

Два шрифта, две роли

Victor Mono / 600 italic
Голос территории
Заголовки, крупные цифры, названия маршрутов, даты событий.
IBM Plex Mono / 400 & 600
Сегодня, 12:40 · Элекмонар · +21°
Основной текст, навигация, данные, подписи, формы и служебная информация.
Type scale

Display → micro

Display XL.pg-title--xlclamp(48px, 7vw, 108.8px)LH .98 · tracking -.055em
Предгорье
Display L.pg-title--lclamp(36px, 4.8vw, 76.8px)LH .98 · tracking -.055em
Новости района
Display M.pg-title--mclamp(25.6px, 3vw, 44.8px)
История территории
Display S.pg-title--sclamp(20.8px, 2vw, 28.8px)
Карточка раздела
Lead.pg-leadclamp(15.04px, 1.4vw, 18.88px)
Крупный вводный текст первого экрана. Не используется как обычный абзац внутри карточек.
Body.pg-text16px inheritedLH 1.55
Основной текст материалов, пояснений и описаний. Цвет muted по умолчанию.
Note.pg-note13.12px / .82rem
Второстепенное пояснение, заметка, небольшая инструкция.
Meta.pg-meta11.84px / .74rem
12 августа · Элекмонар · источник: музей
Kicker.pg-kicker11.52px / .72rem600 · uppercase · .12em
06 / Фотоархив
06 / TEXT RECIPES

Канонические пары «заголовок + текст»

Это специальный слой против разъезжающихся отступов. Не ставим вручную margin-top:14/16/18/24px, а выбираем смысловой recipe.

Recipe 01

Hero copy

.pg-copy.pg-copy--hero
Предгорье FM

Радио — точка входа

Крупное вводное сообщение страницы. Здесь допустим самый сильный контраст масштаба.

Kicker → title24 px
Title.pg-title--xl
Title → lead28 px
Lead.pg-lead, max-width 700 px
Lead → actions30 px
Useтолько главный hero страницы/модуля.
<div class="pg-copy pg-copy--hero">
  <span class="pg-kicker">Предгорье FM</span>
  <h1 class="pg-title pg-title--xl">Радио — точка входа</h1>
  <p class="pg-lead">…</p>
  <div class="pg-row">…actions…</div>
</div>
Recipe 02

Section intro

.pg-section-head + .pg-copy--section
02 / Карта модулей

Один портал, самостоятельные сервисы

Стандартный вводный блок секции. Заголовок и пояснение всегда имеют одинаковый вертикальный ритм.

Desktop columns.55fr / 1.45fr
Column gap40 px
Title.pg-title--l
Title → body16 px
Intro max-width780 px
Block → next content56 px
≤720 px1 column, gap 18 px, bottom 34 px
Recipe 03

Feature copy

.pg-copy.pg-copy--feature
архив

Село до больших дорог

Применяется внутри крупного контентного feature: фотоархив, материал, тематический блок.

Tag/kicker → title15 px
Title.pg-title--m
Title → body14 px
Useкрупная карточка/feature, но не section header.
Recipe 04

Card copy

.pg-copy.pg-copy--card

Ремонт техники

Короткое описание услуги или компактного блока.

Title.pg-title--s
Title → body12 px
Title → meta8 px, если body отсутствует.
Usepanel/card body.
Recipe 05

Modal heading

.pg-copy.pg-copy--modal
Privacy layer

Настройки согласий

Kicker → title10 px
Title.pg-title--m
Usemodal/drawer/dialog headers.
Да

Выбрать recipe по роли блока и использовать его целиком.

Нет

Собирать каждый новый «заголовок + текст» через inline margin и новый font-size.

07 / LOGO

Оригинальные логотипы

Используются только предоставленные SVG. Текстовая имитация логотипа не допускается.

Полный логотип
Вордмарк
Иконка
Инверсный логотип
Assets

Доступные варианты

full.svgосновной полный знак на светлом фоне.
full-inverse.svgполный знак на синем/тёмном фоне.
wordmark*.svgтекстовый знак без иконки.
icon*.svgквадратные/малые контексты, favicon/PWA candidate.
full-compact.svgкомпактная композиция, использовать только если размер контейнера это оправдывает.
08 / BUTTONS

Кнопки и actions

Variant отвечает за смысл, size — за плотность интерфейса. Форма всегда pill; произвольные радиусы не вводим.

Variants

Button family

.pg-btn
Default height44 px
Horizontal padding17 px
Gap icon/text9 px
Font12.48 px / .78rem · 600
Radius999 px
HovertranslateY(-1px); primary darkens.
Focus3 px Air Blue ring + 2 px offset.
Disabledopacity .45, no pointer events, no shadow/transform.
Sizes

SM / default / LG / icon

SM36 px, padding 14 px, 11.52 px type. Dense toolbars only.
Default44 px. Основной размер сайта.
LG52 px, padding 22 px. Hero/critical primary CTA.
Icon44×44 px circle. Обязателен aria-label.
<button class="pg-btn pg-btn--primary" type="button">Слушать эфир</button>
<a class="pg-btn pg-btn--secondary" href="/news">Все новости</a>
Primary

Главное действие текущего блока. Обычно одно.

Accent

Живой/особый брендовый акцент, а не второй primary рядом.

Secondary / outline / ghost

Иерархически более слабые действия.

Danger

Не использовать просто потому, что хочется красную кнопку. Только destructive action.

09 / LABELS

Tags и status

Tag классифицирует объект. Status сообщает динамическое состояние системы. Они визуально родственны, но семантически разные.

Tag

Категории и признаки

.pg-tag
DefaultНовостиСейчасАрхивСлужебное
Heightmin 28 px
Padding5 × 9 px
Font10.88 px / .68rem · 600
Radiusround
Useкатегория, тип материала, временной признак, scope.
Status

Live / reconnect / offline

.pg-status
unknownonlinereconnectoffline
Indicator7×7 px dot + 7 px right gap
Live halo4 px translucent green ring
Useтекущее техническое/операционное состояние, которое может измениться без перезагрузки сущности.
10 / FORMS

Поля ввода

Форма строится из .pg-field: label → control → hint/error. Ошибка задаётся атрибутом aria-invalid="true", а не отдельной копией input-компонента.

Input / select / textarea

Field family

Field gap8 px
Control height44 px minimum
Textarea120 px minimum, vertical resize
Padding10 × 12 px
Border1 px --pg-line-strong
Radius12 px / S
Hoverborder #8E9AAD
Focus3 px Air Blue ring, offset 2 px
Errordanger border + error text 10.88 px
Disabled#F0F2F5 bg, #8B95A4 text
<label class="pg-field">
  <span class="pg-label">Телефон</span>
  <input class="pg-input" type="tel" autocomplete="tel">
  <span class="pg-hint">Виден только после действия пользователя.</span>
</label>
11 / TOGGLES

Switch и segmented control

Switch меняет бинарный параметр. Segment выбирает один mutually exclusive режим или dataset.

Switch

Boolean toggle

.pg-switch
Track42×24 px
Thumb18×18 px, internal padding 3 px
CheckedBlue track, thumb translateX(18px)
Gap10 px
Keyboardnative checkbox remains focusable; visual track receives focus ring.
Segment

Mode switch

.pg-segment
Outer padding4 px
Inner gap4 px
Button padding8 × 12 px
Activewhite surface, blue text, micro shadow
JS state.is-active; для real tabs добавить корректные ARIA roles.
12 / SURFACES

Panel surfaces

Panel — нейтральный контейнер для группировки информации, не универсальная «карточка для всего». Контентные сущности ниже имеют собственные компоненты.

Panel

White / blue / green / warm / dark

.pg-panel

White

Базовая поверхность.

Blue

Brand emphasis.

Green

Live / accent.

Warm
Dark
Flat / radius 0
Paddingclamp(20px, 3vw, 34px)
Radius28 px default
Border1 px line or surface color for solid variants
RulePanel не должен автоматически становиться link/hover-card. Для кликабельной сущности используйте dedicated component.
13 / MEDIA

Базовый media container

Фотография получает container, crop и caption. Отдельные продуктовые компоненты могут задавать собственный aspect ratio.

Media

Image with caption

.pg-media
Пейзаж
Элекмонар · август · демонстрационный снимок
Min-height240 px
Radius28 px
Object fitcover
Caption insetleft/right 16 px, bottom 14 px
Overlaybottom gradient to 35% black; caption remains readable.
Altобязателен для content images; decorative image получает empty alt.
14 / BROADCAST

Эфирный модуль

Составной компонент: visual stream + overlay + weather + now playing + schedule. Реальные streaming/reconnect algorithms остаются вне UI слоя.

Composite

Broadcast shell

.pg-broadcast
Демо камеры
КАМЕРА / СЕЙЧАС
Катунь в Элекмонаре
online
ПОГОДА / СЕЙЧАС
+21.4°
54%
742 мм
2.1 м/с
Обложка
Предгорье FM
Прямой эфир
Утро в Предгорье
Музыкальный эфир
Desktop columns1.45fr / minmax(300px,.55fr)
Min height600 px
Outer radius42 px
Visual overlay inset28 px
Side panel padding/gap28 / 24 px
≤960 pxvisual on top, panel becomes 2-column internal layout.
≤720 pxsingle column, visual min 430px, overlay 18px, panel 20px.
State contractstream status должен использовать .pg-status; reconnect/offline не маскируются под normal.
Liveonline
Recoveringreconnect
Unavailableoffline
15 / MODULES

Карточка модуля

Навигационный entry point в самостоятельный сервис. Вся карточка — ссылка. Не использовать для обычной новости или товара.

16 / NEWS

Новости: feature + compact row

Два уровня иерархии: один выделенный материал и плотная лента. Не превращаем каждую новость в отдельную крупную карточку.

Featured news

Lead material

.pg-news-feature
Демо новости
Районсегодня · 11:20

Крупный материал недели

Короткий лид материала. Источник должен быть явно виден для агрегированного контента.

Источник: местная газетаОткрыть
Desktop grid1.08fr / .92fr
Min height470 px
Body paddingclamp(24px,4vw,46px)
Body gap32 px
Feature titleclamp(28.8px,3.3vw,54.4px), LH 1.02
≤960single column; media min-height 360 px.
17 / EVENTS

Афиша

Event card должна ощущаться как календарная сущность: дата доминирует, затем название и metadata.

Event card

Blue / green

.pg-event-card
17августа · 18:00

Вечер на берегу

Элекмонар · площадка у клуба
24августа · 12:00

Сельский праздник

Чемальский район
Min height430 px
Padding / gap26 / 28 px
Radius28 px
Date56 px / 3.5rem, LH .82
Date → small11 px
Title32 px, LH 1, tracking -.04em
Title margins14 px top / 12 px bottom
18 / BOARD

Три разных типа объявлений

Услуга, попутка и товар имеют разные информационные центры, поэтому это три разных компонента, а не variant одной generic card.

Service

Исполнитель + работа + цена

.pg-service-card
АК

Ремонт техники

Элекмонар · выезд
от 800 ₽

Стиральные машины, холодильники, мелкая техника.

Padding24 px
Radius28 px
Card gap22 px
Header gap20 px
Avatar54×54 px circle
Title → meta6 px
Ride

Route + time + seats

.pg-ride-card
откудаЭлекмонар
кудаГорно-Алтайск
Сегодня · 16:30
2 места
Outer radius20 px
Route padding24 px
Route gridauto / 1fr / auto, gap 14 px
Route point gap5 px
Body padding18×24 px
Route marker7 px green dots on line.
Goods

Фото + название + цена

.pg-goods-card
Товар

Домашний мёд

Элекмонар · сегодня
700 ₽ / л
Desktop columns150 px / 1fr
Media min-height160 px
Body padding20 px
Body gap18 px
≤720media column 110 px
Price16.8 px / 1.05rem · 600 blue
19 / TIMELINE

Фотоархив / timeline

Хронология является structural axis. Год находится отдельно от контентной карточки; источник фотографии обязателен как отдельная metadata строка.

Timeline

Chronological media entry

.pg-timeline
1930
архив

Село до больших дорог

Короткое описание события, места или людей на фотографии.

Источник: краеведческий музей — пример
Timeline gap34 px
Axis desktopleft 118 px, 1 px strong line
Item grid92 px / 1fr, gap 52 px
Content gridminmax(220px,.8fr) / 1.2fr, gap 24 px
Content padding22 px
Media radius / min-height20 px / 240 px
Source top gap14 px
≤720axis 12 px; item single column with 34 px left padding; content single column.
20 / TABLES

Data table

Для административных сведений и компактных key/value данных. Не использовать как универсальный responsive data grid для сложных табличных приложений.

Key/value

Administrative data

.pg-data-table
НаименованиеРадиоканал «Предгорье FM»
ТерриторияЧемальский район
Контактexample@predgorie.local
Font12 px / .75rem
Row padding14 px vertical
Label width34%
Label weight400 muted
Value weight600
≤720th/td become block, label sits directly above value.
21 / ALERTS

Inline alerts

Для локальной информации внутри flow. Не заменяют modal/dialog и не используются как декоративные цветные блоки.

Alert family

Info / warning / error

.pg-alert
i
Информация
Источник обновлён 4 минуты назад.
!
Предупреждение
Эфирная сетка временно неполна.
!
Ошибка
Не удалось получить данные.
Padding16×18 px
Gridauto / 1fr, gap 12 px
Radius12 px
Mark22×22 px circle
Font12 px / .75rem
22 / STATES

Empty / loading / error

Любой внешний источник должен иметь UI не только для happy path. Состояние должно объяснять, что произошло и что пользователь может сделать.

System states

Fallback family

.pg-state
∅
Пока пусто
В этом разделе ещё нет материалов.
загрузка…
!
Источник недоступен
Показываем последние корректные данные.
Min height210 px
Padding28 px
Border1 px dashed strong line
Radius28 px
Inner max360 px, centered, gap 12 px
State icon46×46 px circle
Skeletonmin 16 px, 8 px radius, 1.2s linear shimmer
24 / CONSENT

Cookie / privacy layer

Consent banner — самостоятельный overlay component. Согласие на персональные данные в форме — часть конкретной формы и не заменяется общим cookie banner.

Consent banner

Bottom overlay

.pg-consent
Positionfixed; right/bottom 20 px; left fallback 20 px
Widthmin(760px, viewport - 40px)
Padding / radius22 / 20 px
BackgroundInk at .96 + blur 14 px
z-index110
≤72014 px viewport inset, width viewport - 28 px
25 / RESPONSIVE

Breakpoints и мобильный контракт

Мобильная версия — отдельная композиция, а не масштабированный desktop. Ни один reusable component не должен создавать horizontal overflow на 390 px.

>960

Desktop: 3/4 column grids, two-column broadcast, two-column timeline content.

≤960

Tablet: 3/4 grids → 2; broadcast restructures; news feature → 1 column.

≤720

Mobile: all base grids → 1; shell inset 14 px; timeline/news/broadcast receive dedicated layouts.

Responsive rules

Обязательные проверки

390 pxнет horizontal overflow; controls остаются нажимаемыми; текст не обрезается по ширине.
Touch targetосновные buttons/inputs 44 px default; icon buttons 44×44.
Long contentgrid columns используют minmax(0,1fr), containers — min-width:0.
Imagesmax-width:100%; внутри компонентов explicit cover/contain behavior.
Safari/iPhoneобязательный реальный smoke test перед production для sticky/blur/media/PWA.
26 / DEV RULES

Контракт для разработчика

Короткий набор правил, чтобы дизайн-система не расползлась через три месяца.

Naming & states

CSS contract

PrefixReusable classes начинаются с pg-.
ElementBEM-like: .pg-module-card__title.
Variant--blue, --green, --danger.
Dynamic state.is-active, .is-current, .is-disabled или semantic attributes.
JS hooksdata-open-*, data-module, data-board-tab. JS не должен искать «красивый» CSS class.
TokensЦвет, radius, spacing, type scale берутся из predgorie-tokens.css.
Local CSSРазрешён для уникальной page composition; запрещён для создания второго варианта существующего component без явного решения.
Accessibility

Минимальный a11y contract

ButtonsNative button для действий; a для навигации.
Icon actionобязательный accessible name / aria-label.
Formsкаждый input связан с label; validation использует semantic attributes.
Focusfocus-visible ring нельзя убирать без равноценной замены.
Modalproduction implementation: role/dialog semantics, focus trap, focus restore, Escape.
Imagescontent image → meaningful alt; decoration → empty alt.
Motionshimmer/transitions должны быть отключаемы через prefers-reduced-motion перед production.
When adding a component

Definition of done

  • 1Есть реальный сценарий использования, а не абстракция «на будущее».
  • 2Определены anatomy, variants, sizes и states.
  • 3Все системные значения используют tokens.
  • 4Есть desktop и mobile behavior.
  • 5Есть empty/error/loading state, если компонент зависит от данных.
  • 6Есть доступная HTML-разметка и keyboard behavior.
  • 7Компонент добавлен в эту страницу с live preview + spec + code example.
  • 8Проверен на 1440 px и 390 px без overflow.
Технический долг текущего прототипа: в index.html ещё есть локальные inline-отступы и несколько точечных цветов. UI Kit 0.2 уже задаёт канонические recipes; следующий проход по самому лендингу должен заменить эти отклонения на системные классы, не меняя визуальную концепцию.
Готово