@ai.all/ds7 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,62 @@
1
+ # 01-Architecture · Atomic Design & Component Hierarchy
2
+
3
+ > Методология построения интерфейса основана на принципах [Atomic Design Брэда Фроста (Brad Frost)](https://atomicdesign.bradfrost.com/chapter-2/) с акцентом на **полную автономность (self-contained)** и **компонуемость (composability)** каждого блока.
4
+
5
+ ---
6
+
7
+ ## 1. Пять уровней иерархии
8
+
9
+ ```
10
+ [1. ATOMS] ───> Неделимые кирпичики: кнопки, иконки, бейджи, инпуты, цены
11
+ │
12
+ [2. MOLECULES] ───> Связки атомов: карточка товара, карточка тарифа, навигация слайдера
13
+ │
14
+ [3. ORGANISMS] ───> Автономные виджеты: слайдер карточек, сетка преимуществ, FAQ, калькулятор
15
+ │
16
+ [4. TEMPLATES] ───> Каркасы секций и лэндингов: Hero-секция, шаблон направления
17
+ │
18
+ [5. PAGES] ───> Маршруты Nuxt (app/pages/*): получение данных из app/data/ и передача в шаблоны
19
+ ```
20
+
21
+ ---
22
+
23
+ ## 2. Правила для каждого уровня
24
+
25
+ ### Уровень 1: Атомы (`app/components/ui/`)
26
+ - **Что входит:** `UiButton`, `UiIcon`, `UiPrice`, `UiProductBadge`, `UiField`, `UiLink`, `UiText`, `UiCrown`, `UiSun`.
27
+ - **Правило изоляции:** Атомы абсолютно «слепы». Они не знают о существовании страниц, товаров или калькулятора. Управляются исключительно типизированными входными пропсами.
28
+ - **Стилизация:** Базовый класс + короткие модификаторы (`.btn.-sun`, `.btn.-sm`, `.btn.-outline`).
29
+
30
+ ### Уровень 2: Молекулы (`app/components/ui/`)
31
+ - **Что входит:** `UiProductCard` (карточка товара), `UiSectionHeading` (заголовок секции с eyebrow и подписью), `UiFeatureCard` (карточка преимущества), `UiPlanCard` (карточка тарифа/пакета), `UiSliderNav` (стрелки и точки).
32
+ - **Правило изоляции:** Молекулы компонуются только из атомов и базовой HTML-разметки. Принимают данные через `defineProps` и слоты. Не имеют сайд-эффектов.
33
+ - **Вложенность:** Молекула должна идеально смотреться и в 4-колоночном гриде на всю ширину страницы, и в узком сайдбаре (за счет `@container`).
34
+
35
+ ### Уровень 3: Организмы (`app/components/ui/`, `sections/`, `site/`)
36
+ - **Что входит:** `UiCardSlider` (универсальный слайдер), `UiFeatureGrid` (сетка преимуществ), `UiHeroHook` (плашка-крючок под Hero), `UiFaq` (аккордеон), `CalcPanel` (панель расчета), `SiteHeader`, `SiteFooter`.
37
+ - **Правило изоляции:** Организмы функционально самодостаточны. Они сами управляют своим состоянием (скролл, активная вкладка, раскрытый аккордеон).
38
+ - **Слоты:** Организмы-контейнеры обязаны работать через слоты (`<template #default="{ item }">`). Слайдер не должен быть зашит на товары — он крутит любые переданные молекулы.
39
+
40
+ ### Уровень 4: Шаблоны и Секции (`app/components/sections/`)
41
+ - **Что входит:** `CalcSection`, `UiPageHero`, шаблоны направлений (`DirectionPage`).
42
+ - **Правило:** Шаблоны соединяют организмы в законченный блок страницы, передавая в них структуру. Никакой захардкоженной логики под конкретные URL.
43
+
44
+ ### Уровень 5: Страницы (`app/pages/`)
45
+ - **Что входит:** `index.vue`, `[slug].vue`, `movilidad.vue`, `mantenimiento.vue` и др.
46
+ - **Правило:** Страницы отвечают **только** за 3 вещи:
47
+ 1. Вызов composables для получения данных из `app/data/`;
48
+ 2. Установку метатегов (`usePageSeo`);
49
+ 3. Передачу данных в секции и шаблоны через пропсы.
50
+ - **Запрет:** В файлах `app/pages/` **категорически запрещена прямая инлайн-верстка карточек, кастомных слайдеров и специфических сеток**.
51
+
52
+ ---
53
+
54
+ ## 3. Принцип «Внутрь чего-то еще и еще глубже»
55
+
56
+ Любой компонент системы спроектирован так, чтобы его можно было вставить:
57
+ 1. В обычный поток страницы;
58
+ 2. Внутрь модального окна `UiModal`;
59
+ 3. Внутрь вкладки калькулятора или табов;
60
+ 4. Внутрь узкого сайдбара или всплывающей шторки.
61
+
62
+ Компонент рассчитывает свои размеры не от экрана (`@media`), а от родительского блока (`@container`).
@@ -0,0 +1,117 @@
1
+ # 01-Architecture · Component Anatomy & Container Queries
2
+
3
+ > Эталонный анатомический разрез Vue 3 SFC (Single File Component). По этой структуре строятся все компоненты в системе.
4
+
5
+ ---
6
+
7
+ ## 1. Анатомический шаблон компонента
8
+
9
+ ```vue
10
+ <script setup lang="ts">
11
+ /**
12
+ * UiProductCard
13
+ * Молекула карточки товара каталога.
14
+ * Поддерживает адаптивность по контейнеру и каскадные модификаторы.
15
+ */
16
+
17
+ // 1. Входные типизированные пропсы
18
+ const props = withDefaults(defineProps<{
19
+ title: string
20
+ description?: string
21
+ price?: number | null
22
+ badge?: 'top' | 'new' | 'sale'
23
+ specs?: Array<{ label: string; value: string }>
24
+ // Булевые пропсы-модификаторы для удобства в шаблонах:
25
+ top?: boolean
26
+ compact?: boolean
27
+ dark?: boolean
28
+ }>(), {
29
+ description: '',
30
+ price: null,
31
+ specs: () => [],
32
+ top: false,
33
+ compact: false,
34
+ dark: false
35
+ })
36
+
37
+ // 2. Вычисляемые классы-модификаторы
38
+ const rootClasses = computed(() => [
39
+ 'p-card',
40
+ props.top && '-top',
41
+ props.compact && '-compact',
42
+ props.dark && '-dark',
43
+ props.badge && `-${props.badge}`
44
+ ])
45
+ </script>
46
+
47
+ <template>
48
+ <article :class="rootClasses" data-ref="CARD-01">
49
+ <!-- Слот для изображения или графического арта -->
50
+ <div class="p-card__art">
51
+ <slot name="art" />
52
+ </div>
53
+
54
+ <!-- Информационная часть -->
55
+ <div class="p-card__body">
56
+ <h3 class="p-card__title">{{ title }}</h3>
57
+ <p v-if="description" class="p-card__desc">{{ description }}</p>
58
+
59
+ <!-- Список характеристик (одна колонка) -->
60
+ <dl v-if="specs.length" class="p-card__specs">
61
+ <div v-for="s in specs" :key="s.label">
62
+ <dt>{{ s.label }}</dt>
63
+ <dd>{{ s.value }}</dd>
64
+ </div>
65
+ </dl>
66
+ </div>
67
+
68
+ <!-- Футер с ценой и действием -->
69
+ <div class="p-card__footer">
70
+ <UiPrice v-if="price !== null" :amount="price" />
71
+ <slot name="action" />
72
+ </div>
73
+ </article>
74
+ </template>
75
+
76
+ <style>
77
+ /*
78
+ * Стили объявляются без scoped, но строго изолируются через префикс компонента.
79
+ * Это позволяет внешним родителям каскадно переопределять CSS-переменные.
80
+ */
81
+ .p-card {
82
+ --card-bg: var(--color-white);
83
+ --card-ink: var(--color-ink);
84
+
85
+ container-type: inline-size;
86
+ display: flex;
87
+ flex-direction: column;
88
+ background: var(--card-bg);
89
+ color: var(--card-ink);
90
+ border-radius: var(--radius-md);
91
+ padding: var(--space-4);
92
+ }
93
+
94
+ /* Контейнерный запрос: адаптация под размер родительского блока */
95
+ @container (max-width: 320px) {
96
+ .p-card {
97
+ padding: var(--space-2);
98
+ }
99
+ .p-card__title {
100
+ font-size: 0.9rem;
101
+ }
102
+ }
103
+ </style>
104
+ ```
105
+
106
+ ---
107
+
108
+ ## 2. Ключевые архитектурные правила:
109
+
110
+ 1. **`container-type: inline-size`:**
111
+ Компонент всегда объявляет свой контейнер, если имеет адаптивные состояния. Он не зависит от ширины экрана (`@media`), а реагирует на ширину бокса, в который его поместили (`@container`).
112
+ 2. **Локальные CSS-переменные (`--card-bg`, `--card-ink`):**
113
+ Все цвета и параметры привязаны к переменным компонента. Модификатор меняет переменные, а не 10 дочерних селекторов.
114
+ 3. **Стабильный идентификатор `data-ref="CARD-01"`:**
115
+ Связывает живой компонент в коде с разделом в брендбуке на `/ui`.
116
+ 4. **Слоты с фоллбэками:**
117
+ Для медиа (`#art`) и кнопок (`#action`) используются слоты, позволяя переиспользовать разметку под любые типы контента.
@@ -0,0 +1,101 @@
1
+ # 01-Architecture · Modifier System & Ancestry Scoping
2
+
3
+ > Система модификаторов обеспечивает максимальное переиспользование кода без создания дублирующих компонентов.
4
+
5
+ ---
6
+
7
+ ## 1. Синтаксис отдельных классов-модификаторов
8
+
9
+ Вместо длинных составных BEM-имен (`catalog-product-card--featured-large-dark`) используется компактная и легко читаемая схема: **базовый класс + отдельные короткие классы с дефисом**:
10
+
11
+ ```html
12
+ <!-- Карточка товара: топовая, новинка, компактный размер -->
13
+ <article class="p-card -top -new -compact">
14
+ <div class="p-card__art">...</div>
15
+ <h3 class="p-card__title">Panel Solar 450W</h3>
16
+ <div class="p-card__price">...</div>
17
+ </article>
18
+ ```
19
+
20
+ ### Преимущества:
21
+ 1. **Экономия байтов в HTML/CSS:** Классы короткие, вес страницы уменьшается.
22
+ 2. **Аддитивность:** Модификаторы можно комбинировать в любых сочетаниях (`-top -new`, `-dark -compact`).
23
+ 3. **Каскадное влияние:** Модификатор на корневом элементе прозрачно меняет свойства всех вложенных элементов.
24
+
25
+ ---
26
+
27
+ ## 2. Каскад стилей от модификатора
28
+
29
+ ```css
30
+ /* Базовый компонент */
31
+ .p-card {
32
+ --card-bg: var(--color-white);
33
+ --card-text: var(--color-ink);
34
+ --card-border: var(--color-ink-100);
35
+
36
+ background: var(--card-bg);
37
+ color: var(--card-text);
38
+ border: 1px solid var(--card-border);
39
+ border-radius: var(--radius-md);
40
+ padding: var(--space-4);
41
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
42
+ }
43
+
44
+ /* Модификатор: TOP товар */
45
+ .p-card.-top {
46
+ --card-border: var(--color-sun);
47
+ box-shadow: var(--shadow-lift);
48
+ }
49
+ .p-card.-top .p-card__title {
50
+ font-weight: 700;
51
+ }
52
+
53
+ /* Модификатор: Компактный режим */
54
+ .p-card.-compact {
55
+ padding: var(--space-2);
56
+ }
57
+ .p-card.-compact .p-card__title {
58
+ font-size: 0.85rem;
59
+ }
60
+
61
+ /* Модификатор: Темная тема */
62
+ .p-card.-dark {
63
+ --card-bg: var(--color-ink-950);
64
+ --card-text: var(--color-white);
65
+ --card-border: var(--color-ink-800);
66
+ }
67
+ ```
68
+
69
+ ---
70
+
71
+ ## 3. Управление контекстом от предка (Ancestry Scoping)
72
+
73
+ Если один и тот же рабочий компонент (слайдер, форма, плашка хука) должен выглядеть иначе на определенной странице (например, на странице для бизнеса `/empresas` фон должен быть графитовым, а на главной — песочным):
74
+
75
+ ❌ **Как делать НЕЛЬЗЯ:**
76
+ Создавать два разных файла: `HookHome.vue` и `HookEmpresas.vue`.
77
+
78
+ ✅ **Как делать ПРАВИЛЬНО:**
79
+ Использовать единый компонент `UiHeroHook`, а различия задавать через родительский контекст — класс страницы на теге `<body>`:
80
+
81
+ ```css
82
+ /* Базовый хук везде */
83
+ .hero-hook {
84
+ background: var(--color-paper);
85
+ color: var(--color-ink);
86
+ }
87
+
88
+ /* Тот же самый хук на странице для бизнеса (темная тема) */
89
+ body.page-empresas .hero-hook {
90
+ background: var(--color-ink-950);
91
+ color: var(--color-white);
92
+ }
93
+
94
+ /* Тот же компонент внутри модального окна */
95
+ .site-modal-box .hero-hook {
96
+ background: transparent;
97
+ padding: 0;
98
+ }
99
+ ```
100
+
101
+ Благодаря настройке в `usePageSeo.ts` каждая страница Nuxt автоматически проставляет свой класс на `<body>` (`page-home`, `page-hogares`, `page-empresas`, `page-movilidad`, `page-equipamiento`).
@@ -0,0 +1,52 @@
1
+ # 02-Design-System · Brandbook & Sandbox (/ui)
2
+
3
+ > Страница `/ui` в проекте — это не просто документация, а **живой интерактивный полигон компонентов (Storybook)** и единый язык коммуникации между человеком и ИИ.
4
+
5
+ ---
6
+
7
+ ## 1. Концепция стабильных меток компонентов (Component IDs)
8
+
9
+ Каждый переиспользуемый паттерн в дизайн-системе имеет уникальный краткий буквенно-цифровой идентификатор:
10
+
11
+ | ID | Название компонента | Назначение |
12
+ |---|---|---|
13
+ | **`BTN-01`** | Primary Action Button | Главная кнопка с заливкой `--color-sun` и стрелкой `→` |
14
+ | **`BTN-02`** | Outline Secondary Button | Второстепенная контурная кнопка |
15
+ | **`CTL-01`** | Segmented Tab Switcher | Сегментный переключатель вкладок/периодов |
16
+ | **`PRICE-01`** | Formatted Solar Price | Ценник с суффиксом валюты и опциональной пометой «от» |
17
+ | **`CARD-01`** | Base Product Card | Базовая карточка товара каталога с модификаторами |
18
+ | **`CARD-02`** | Plan / Tariff Card | Карточка тарифа/пакета со списком опций |
19
+ | **`SLD-01`** | Card Carousel / Slider | Универсальный слайдер со `scroll-snap` и навигацией |
20
+ | **`HOOK-01`** | Hero Overlap Hook | Плашка-крючок под обложкой (калькулятор счёта, пробег) |
21
+
22
+ ### Как это используется в работе:
23
+ Вместо того чтобы писать разработчику или ИИ:
24
+ > *«Сделай мне карточку товара, где сверху картинка, снизу цена, обведи желтой рамочкой и поставь скругление 12px...»*
25
+
26
+ Вы просто пишете:
27
+ > *«Поставь в этот блок `CARD-01 -top` и кнопку `BTN-01`»*.
28
+
29
+ И человек, и любой ИИ моментально понимают задачу на 100% однозначно, без разночтений и простыней текста.
30
+
31
+ ---
32
+
33
+ ## 2. Интерактивная песочница на странице `/ui`
34
+
35
+ Страница `/ui` (`app/pages/ui.vue`) содержит:
36
+ 1. **Интерактивные тумблеры состояний:**
37
+ Для каждого компонента выведены переключатели модификаторов:
38
+ - `[x] -top`
39
+ - `[x] -new`
40
+ - `[x] -compact`
41
+ - `[x] -dark`
42
+ 2. **Контекстный переключатель фона:**
43
+ Кнопки переключения подложки («На светлом фоне», «На темном фоне», «В модалке»), чтобы сразу видеть контрастность и поведение текста.
44
+ 3. **Кнопка «Скопировать код»:**
45
+ Рядом с каждым образцом расположена кнопка копирования эталонного Vue-сниппета для быстрой вставки в проект.
46
+
47
+ ---
48
+
49
+ ## 3. Правило обновления брендбука
50
+
51
+ Любой новый атом, молекула или токен, появляющийся в проекте, **обязан появиться на странице `/ui`**. Если компонента нет на `/ui`, он считается незавершенным.
52
+ Страница `/ui` закрыта от индексации (`noindex, nofollow`) и исключена из `sitemap.xml`.
@@ -0,0 +1,43 @@
1
+ # 02-Design-System · Minimalist Naming & CSS Minification
2
+
3
+ > Стандарт компактного и семантичного именования классов для уменьшения веса HTML/CSS и ускорения рендеринга.
4
+
5
+ ---
6
+
7
+ ## 1. Принцип краткости без потери смысла
8
+
9
+ Длинные классы раздувают DOM-дерево, увеличивают объем передаваемого HTML и замедляют парсинг.
10
+ Мы используем краткие, интуитивно понятные сокращения:
11
+
12
+ | Было (громоздко) | Стало в DS7 (минималистично и чисто) |
13
+ |---|---|
14
+ | `.catalog-product-card` | `.p-card` |
15
+ | `.catalog-product-card--featured` | `.p-card.-top` |
16
+ | `.catalog-product-card__description` | `.p-card__desc` |
17
+ | `.section-heading-eyebrow` | `.s-head__eye` |
18
+ | `.overlap-panel` | `.panel.-overlap` |
19
+ | `.hook-bill-calculator` | `.hook.-bill` |
20
+ | `.button-variant-primary-large` | `.btn.-sun.-lg` |
21
+
22
+ ---
23
+
24
+ ## 2. Формула синтаксиса классов
25
+
26
+ ```
27
+ .[компонент]__ [элемент] .- [модификатор]
28
+ ```
29
+
30
+ 1. **Компонент:** Короткое имя блока (`.btn`, `.p-card`, `.field`, `.slider`).
31
+ 2. **Элемент (если требуется):** Отделяется двумя подчеркиваниями (`.p-card__art`, `.p-card__body`, `.p-card__title`, `.p-card__price`).
32
+ 3. **Модификатор:** Отдельный класс через дефис (`.-top`, `.-dark`, `.-new`, `.-sm`, `.-lg`, `.-compact`).
33
+
34
+ ---
35
+
36
+ ## 3. Сжатие и минификация сборки
37
+
38
+ 1. **PurgeCSS / Tailwind Tree-Shaking:**
39
+ Все неиспользуемые стили вычищаются при сборке `nuxt generate`. В финальный CSS попадают только реально задействованные классы.
40
+ 2. **CSS Nano / Lightning CSS:**
41
+ Минификатор удаляет комментарии, склеивает правила, сокращает цвета (`#ffffff` → `#fff`, `0px` → `0`) и преобразует повторяющиеся структуры в сжатый бинарный вид для передачи по gzip/brotli.
42
+ 3. **Разделение критических и второстепенных стилей:**
43
+ Критический CSS встраивается в HTML, а вторичные стили сжимаются и кэшируются на год (`Cache-Control: immutable`).
@@ -0,0 +1,88 @@
1
+ # 02-Design-System · Tokens Specification
2
+
3
+ > Дизайн-токены определяют визуальную ДНК проекта. Все значения объявляются через CSS Custom Properties в файле `theme.css`.
4
+
5
+ ---
6
+
7
+ ## 1. Палитра цветов (Semantic Color System)
8
+
9
+ ```css
10
+ :root {
11
+ /* Базовые цвета поверхностей */
12
+ --color-paper: #f7f5ef; /* Теплый фон страниц */
13
+ --color-white: #ffffff; /* Белые плашки и карточки */
14
+ --color-ink: #1f1b16; /* Основной контрастный текст */
15
+ --color-ink-500: #6b6459; /* Второстепенный поясняющий текст */
16
+ --color-ink-100: rgba(31, 27, 22, 0.08); /* Границы и разделители */
17
+ --color-ink-950: #12100d; /* Глубокий темный фон для инверсий */
18
+
19
+ /* Фирменный солнечный акцент */
20
+ --color-sun: #ffd84d; /* Теплый солнечный акцент */
21
+ --color-sun-hover: #ffcf2e; /* Состояние ховера */
22
+
23
+ /* Семантические цвета */
24
+ --color-success: #2e7d32; /* Подтверждение, экономия */
25
+ --color-warning: #ed6c02; /* Предупреждение */
26
+ --color-error: #d32f2f; /* Ошибки */
27
+ }
28
+ ```
29
+
30
+ > **Правило контрастности:** Желтый цвет `--color-sun` запрещено использовать для текста. Он применяется только как фоновая заливка кнопок (`.btn.-sun`), бейджей или подложка-маркер (`box-shadow: inset 0 -0.3em 0 var(--color-sun)`).
31
+
32
+ ---
33
+
34
+ ## 2. Ритм вертикальных и горизонтальных отступов (Spacing Scale)
35
+
36
+ ```css
37
+ :root {
38
+ --space-1: 0.25rem; /* 4px */
39
+ --space-2: 0.5rem; /* 8px */
40
+ --space-3: 0.75rem; /* 12px */
41
+ --space-4: 1rem; /* 16px */
42
+ --space-6: 1.5rem; /* 24px */
43
+ --space-8: 2rem; /* 32px */
44
+ --space-12: 3rem; /* 48px */
45
+ --space-16: 4rem; /* 64px */
46
+
47
+ /* Семантический интервал секций (Responsive) */
48
+ --space-single: clamp(1.2rem, 3vw, 2.2rem); /* Внутренний отступ блоков */
49
+ --space-double: clamp(2.4rem, 6vw, 4.5rem); /* Стандартный интервал секций */
50
+ --space-triple: clamp(3.6rem, 9vw, 6.5rem); /* Крупные разделы на десктопе */
51
+
52
+ /* Базовая сетка страницы */
53
+ --content-width: 78rem; /* 1248px */
54
+ --page-gutter: clamp(1rem, 3.5vw, 2rem);
55
+ }
56
+ ```
57
+
58
+ ---
59
+
60
+ ## 3. Тени и глубина поверхностей (Elevation)
61
+
62
+ Вместо случайных значений `box-shadow` используются строгие семантические токены:
63
+
64
+ ```css
65
+ :root {
66
+ /* Обычные карточки и панели в состоянии покоя */
67
+ --shadow-lift: 0 4px 16px -2px rgba(31, 27, 22, 0.06);
68
+
69
+ /* Приподнятые карточки при ховере или раскрытые элементы */
70
+ --shadow-hover: 0 12px 32px -4px rgba(31, 27, 22, 0.12);
71
+
72
+ /* Всплывающие модальные окна */
73
+ --shadow-modal: 0 24px 64px -8px rgba(18, 16, 13, 0.24);
74
+ }
75
+ ```
76
+
77
+ ---
78
+
79
+ ## 4. Скругления (Border Radius)
80
+
81
+ ```css
82
+ :root {
83
+ --radius-sm: 0.375rem; /* 6px — бейджи, кнопки-чипы */
84
+ --radius-md: 0.75rem; /* 12px — карточки, инпуты */
85
+ --radius-lg: 1.25rem; /* 20px — большие секционные плашки */
86
+ --radius-full: 9999px; /* Таблетки, круглые кнопки */
87
+ }
88
+ ```
@@ -0,0 +1,71 @@
1
+ # 03-Performance · Smart Assets Pipeline (Tight-Trim & Responsive Media)
2
+
3
+ > Стандарт подготовки и вставки графики. Исключает вставку «сырых» тяжелых файлов и гарантирует идеальный LCP.
4
+
5
+ ---
6
+
7
+ ## 1. Правило обрезки избыточных полей (Tight-Trim)
8
+
9
+ Частая ошибка: дизайнер экспортирует PNG/WebP карточки товара или оборудования на холсте 1000×1000 px, где 60% площади — это прозрачные пустые поля вокруг предмета.
10
+
11
+ ### Почему это плохо:
12
+ * Файл весит лишние байты из-за пустых пикселей;
13
+ * Картинку невозможно ровно отцентрировать по сетке;
14
+ * На мобильных экранах сам товар становится крошечным из-за огромных невидимых полей.
15
+
16
+ ### Стандарт DS7 (Tight-Trim):
17
+ 1. **Кадрирование по контуру:** Все прозрачные поля вокруг объекта обрезаются вплотную (если вокруг нет реальной рассеянной тени).
18
+ 2. **Прозрачный фон:** Изображения оборудования (панели, инверторы, зарядки) сохраняются на прозрачном фоне, чтобы легко ложиться на светлые, темные и цветные подложки.
19
+ 3. **Стабильное соотношение сторон:** В коде карточки задается контейнер со стабильным `aspect-ratio` (например, `aspect-ratio: 4/3` или `1/1`), а сама картинка центрируется через `object-fit: contain`.
20
+
21
+ ---
22
+
23
+ ## 2. Адаптивная вставка через `<picture>` и `srcset`
24
+
25
+ Каждое растровое изображение подготавливается под конкретные размеры экрана:
26
+
27
+ ```html
28
+ <picture>
29
+ <!-- 1. Мобильные устройства (ширина до 640px): компактный легкий файл -->
30
+ <source
31
+ media="(max-width: 640px)"
32
+ srcset="/images/p/hero-mobile.webp 1x, /images/p/hero-mobile@2x.webp 2x"
33
+ type="image/webp"
34
+ >
35
+ <!-- 2. Десктопные экраны: полноразмерный файл с поддержкой Retina -->
36
+ <source
37
+ srcset="/images/p/hero.webp 1x, /images/p/hero@2x.webp 2x"
38
+ type="image/webp"
39
+ >
40
+ <!-- 3. Фоллбэк img с явными габаритами для нулевого CLS -->
41
+ <img
42
+ src="/images/p/hero.webp"
43
+ alt="Instalación solar fotovoltaica en Sevilla"
44
+ width="1600"
45
+ height="900"
46
+ fetchpriority="high"
47
+ loading="eager"
48
+ decoding="async"
49
+ >
50
+ </picture>
51
+ ```
52
+
53
+ ---
54
+
55
+ ## 3. Правило `fetchpriority` и `loading`:
56
+
57
+ | Расположение изображения | `loading` | `fetchpriority` | `decoding` | Назначение |
58
+ |---|---|---|---|---|
59
+ | **Главный баннер Hero (LCP)** | `eager` | `high` | `async` | Мгновенно загружается первым |
60
+ | **Карточки товаров (2-й экран)** | `lazy` | `auto` | `async` | Загружается перед появлением в поле зрения |
61
+ | **Нижние секции, отзывы, футер** | `lazy` | `low` | `async` | Загружается только при доскролле |
62
+
63
+ ---
64
+
65
+ ## 4. Скрипт автоматической подготовки ассетов
66
+
67
+ В папке `tools/` рекомендуется держать легковесный скрипт (Python / Sharp), который:
68
+ * Принимает новые картинки;
69
+ * Обрезает прозрачные поля (`trim`);
70
+ * Конвертирует в WebP и AVIF с качеством 82%;
71
+ * Генерирует мобильные копии (`-400.webp`) и ретина-копии (`@2x.webp`).
@@ -0,0 +1,46 @@
1
+ # 03-Performance · Critical CSS & Zero Render-Blocking
2
+
3
+ > Как гарантировать First Contentful Paint (FCP) менее 0.4 секунды и 100 баллов в Google Lighthouse по категории Performance.
4
+
5
+ ---
6
+
7
+ ## 1. В чём проблема стандартного CSS
8
+
9
+ Когда браузер загружает обычную веб-страницу:
10
+ 1. Скачивается `index.html`;
11
+ 2. Браузер находит в `<head>` тег `<link rel="stylesheet" href="/_nuxt/entry.css">`;
12
+ 3. **Рендеринг страницы полностью блокируется (белый экран)**, пока файл `entry.css` (часто 50–150 КБ) скачивается по мобильной сети и парсится;
13
+ 4. Пользователь видит контент только через 1.5–3.0 секунды.
14
+
15
+ ---
16
+
17
+ ## 2. Архитектура Critical CSS в Nuxt 4
18
+
19
+ Critical CSS разделяет стили сайта на две части:
20
+ 1. **Above-the-fold (Критический CSS):** Стили шапки, сетки контейнера, типографики и фонового блока первого экрана (Hero).
21
+ 2. **Below-the-fold (Вторичный CSS):** Стили калькулятора, отзывов, карты, футера, модальных окон.
22
+
23
+ ### Настройка в `nuxt.config.ts`:
24
+
25
+ ```ts
26
+ export default defineNuxtConfig({
27
+ experimental: {
28
+ // Автоматическое извлечение и встраивание критических стилей в HTML
29
+ inlineSSRStyles: true
30
+ }
31
+ })
32
+ ```
33
+
34
+ ### Как это работает:
35
+ При статической генерации (`npx nuxt generate`):
36
+ * Nuxt парсит HTML каждой страницы и определяет классы, нужные для первого экрана;
37
+ * Эти правила вставляются прямо внутрь `<style>` в `<head>` документа `index.html`;
38
+ * Полная таблица стилей подключается асинхронно: `<link rel="preload" as="style">`.
39
+
40
+ ---
41
+
42
+ ## 3. Результат для пользователя и Google Lighthouse
43
+
44
+ * Браузер получает `index.html` и **в ту же миллисекунду рисует идеальный первый экран**, не делая ни одного дополнительного сетевого запроса за CSS.
45
+ * **FCP (First Contentful Paint):** снижается с 1.8s до **0.35s**.
46
+ * **Speed Index:** зеленая зона (98–100 баллов).
@@ -0,0 +1,63 @@
1
+ # 03-Performance · Fonts & Zero Layout Shift (CLS = 0.000)
2
+
3
+ > Как исключить «прыжки» текста при загрузке шрифтов и свести показатель Cumulative Layout Shift (CLS) к абсолютному нулю.
4
+
5
+ ---
6
+
7
+ ## 1. Анатомия проблемы FOUT и прыжков текста
8
+
9
+ Когда веб-шрифт Montserrat подгружается через `@font-face`:
10
+ 1. Браузер сначала рисует текст стандартным системным шрифтом (Arial или Helvetica);
11
+ 2. У системного шрифта другая ширина букв и высота строки (x-height);
12
+ 3. Через секунду подгружается `montserrat.woff2`, шрифт переключается, строки переносятся на новое место, и **вся страница дергается вниз на 20–40 пикселей**.
13
+ 4. Пользователь случайно кликает не туда, а Google фиксирует плохой показатель CLS (> 0.15).
14
+
15
+ ---
16
+
17
+ ## 2. Комплексное решение проблемы в DS7
18
+
19
+ ### Шаг 1: Прямой Preload в `<head>`
20
+ Не ждем, пока CSS файл будет прочитан. Браузер начинает качать шрифт с первой миллисекунды:
21
+
22
+ ```ts
23
+ // nuxt.config.ts -> app.head
24
+ link: [
25
+ {
26
+ rel: 'preload',
27
+ href: '/fonts/montserrat-latin-var.woff2',
28
+ as: 'font',
29
+ type: 'font/woff2',
30
+ crossorigin: 'anonymous'
31
+ }
32
+ ]
33
+ ```
34
+
35
+ ### Шаг 2: Модуль `@nuxtjs/fontaine` (Синхронизация пропорций)
36
+ Fontaine анализирует геометрию шрифта Montserrat и создает локальный псевдо-шрифт:
37
+
38
+ ```css
39
+ @font-face {
40
+ font-family: 'Montserrat-Fallback';
41
+ src: local('Arial');
42
+ ascent-override: 95.8%;
43
+ descent-override: 28.2%;
44
+ line-gap-override: 0%;
45
+ size-adjust: 101.4%;
46
+ }
47
+ ```
48
+
49
+ **Магия Fontaine:** Буквы локального системного шрифта искусственно сжимаются и растягиваются так, чтобы **пиксель-в-пиксель занимать то же пространство**, что и Montserrat.
50
+ Когда реальный шрифт скачивается, форма букв меняется, но **ни одна строчка на странице не сдвигается**.
51
+
52
+ ### Шаг 3: `font-display: swap`
53
+ Шрифт отображается мгновенно, экран не ждет загрузки (нет белого текста FOIT).
54
+
55
+ ---
56
+
57
+ ## 3. Чек-лист проверки CLS = 0.000
58
+
59
+ 1. [x] Preload для основного начертания шрифта (`.woff2`).
60
+ 2. [x] Настроен fallback через Fontaine (`size-adjust`).
61
+ 3. [x] У всех логотипов и SVG-иконок жестко заданы атрибуты `width` и `height`.
62
+ 4. [x] У всех карточек и медиа-блоков зафиксирован CSS `aspect-ratio`.
63
+ 5. [x] Плавающие панели (`SiteIntro`, куки-баннер) позиционируются через `position: fixed` и не выталкивают контент.