16 KiB
Страница кейса: каркас и блоки контента
Инструкция завязана на эталон src/pages/cases/template-elements-demo.html: оттуда копируются готовые куски разметки. Текстовый дубль структуры для редактуры — texts/cases/template-elements-demo.md.
Где править
| Что | Путь |
|---|---|
| Разметка кейса | src/pages/cases/<имя>.html |
| Общие вставки (шапка кейса, футер, скрипты) | src/partials/*.html |
| Сборка в статику | npm run build:pages → public/cases/<имя>.html |
public/cases/*.html не редактировать вручную — они перезаписываются сборкой.
Локальные инструкции агенту без правки HTML в браузере: режим правок при npm run serve → docs/content-edit-mode.md → texts/content-edits/*.json → apply в src по запросу в чате.
Каркас body (как в template)
Порядок важен для единообразия с остальными кейсами:
{{> header-case}}— шапка с ссылкой на главную (внутри partial используется{{assetPrefix}}).{{> fab-stack}}— плавающие кнопки темы и т.п.<main id="case-main" class="section section--tight">→ внутри<div class="section__inner case-article">— весь уникальный контент кейса.- Нижние секции как на главной:
{{> projects-section}},{{> contact-section}},{{> footer-spacer}},{{> footer}},{{> cookie-notice}},{{> nav-more-floating}}. - Скрипты подключаются в
<head>через{{> head-links}}(preload +main.js,skill-connectors.js).
head у кейсов: meta, viewport, description, title, затем {{> head-links}} (preconnect/preload шрифтов и CSS/JS, стили, скрипты с defer), при необходимости локальный <style> для .case-article.
Пути к CSS и медиа
Файл страницы лежит в cases/, один уровень ниже корня сайта. В итоговом HTML пути от страницы:
- Стили:
../css/main.css - Картинки и видео проекта:
../assets/<папка-проекта>/файл.ext
В partials для ссылок на корень сайта используй {{assetPrefix}} (при сборке из cases/* подставится ../). В самой странице кейса чаще пишут литерал ../, как в template.
Текст и заголовки
Внутри .case-article:
- Вводная строка:
<p class="hero__eyebrow">…</p> - Заголовок:
<h1 class="case-page__h1">…</h1> - Подзаголовок (по желанию):
<p class="case-page__sub">…</p> - Обычные абзацы:
<p class="section__prose">…</p> - Секции:
<h2>, при необходимости<h3>
Списки — обычные <ul> / <ol> без обязательных классов (стили задаёт вложенный CSS страницы для .case-article).
Блоки (копировать из template)
Сетка карточек ссылок
- Обертка:
<div class="case-links-grid">. - Одна карточка:
<a class="case-link-card" href="…" target="_blank" rel="noopener noreferrer">. - Внутри:
<span class="case-link-card__media">(превью-картинка или favicon) и<span class="case-link-card__body">сcase-link-card__title,case-link-card__meta,case-link-card__url. - Вариант без тяжёлого превью: на
<a>добавить модификаторcase-link-card--favicon, вmedia— картинка сhttps://www.google.com/s2/favicons?domain=….
Изображения (figure.case-media)
| Модификатор | Поведение |
|---|---|
case-media--natural |
Натуральный масштаб (визуально ~половина логического размера, см. initCaseNaturalImagesHalfSize в main.js). Без data-image-viewer. |
case-media--content |
По ширине текстовой колонки; клик — полноэкранный просмотр, если на <img> есть data-image-viewer (обводка и hover как у .case-video-shell). |
case-media--wide |
Чуть шире колонки; тоже с data-image-viewer при необходимости просмотра. |
Общие атрибуты для картинок в кейсе: loading="lazy", decoding="async", осмысленный alt, при известных размерах — width и height.
Подпись: <figcaption>…</figcaption>. У блока с .case-video-shell подпись по центру под видео (как у кадров с data-image-viewer).
Длинный скрин (data-case-tall-media)
Для очень высокого кадра (полный экран приложения, длинная страница): в свёрнутом виде показывается только верх, внизу превью — плотный градиент («туман»), чтобы было видно, что картинка не целиком. Рамка как у кликабельных кадров. Кнопка «Развернуть» / «Свернуть» с шевроном поверх нижней части превью (внутри рамки); после сворачивания — прокрутка к превью (scrollIntoView на clip).
Ширина: на figure с data-case-tall-media задай data-case-tall-width="content" (по ширине текстовой колонки) или data-case-tall-width="wide" (как у обычной «широкой» картинки: вынос за колонку). Допустим и старый вариант без атрибута: class="case-media case-media--wide" на figure — эквивалентно wide. Не смешивай data-case-tall-width="content" с классом case-media--wide на одном figure.
Стили: .case-media-tall* и блок ширины в public/css/main.css. Логика: public/js/modules/cases/tall-media-expand.js (initCaseTallMediaExpand уже вызывается из public/js/main.js).
Как вставить в кейс
- На
figureпоставьclass="case-media",data-case-tall-media,data-case-tall-width="content"илиwide. - Внутри — разметка ниже: уникальный
idу.case-media-tall__clip, тот же id вaria-controlsу кнопки; подписи «Развернуть»/«Свернуть» в span сdata-case-tall-label-more/data-case-tall-label-less(у «Свернуть» изначальноhidden). - На
imgоставьdata-image-viewer, если нужен полноэкранный просмотр по клику. - Высоту превью (обрезки) задай на clip, например inline:
style="--case-tall-max: min(60vh, 28rem)". Если не задавать, в CSS по умолчаниюmin(70vh, 32rem). - Файлы клади в
public/assets/<папка-кейса>/…; в HTML страниц кейсов пути../assets/<папка>/файл.webp(относительноpublic/cases/).
Сниппет (подставь УНИКАЛЬНЫЙ-ID, путь к WebP, alt, при необходимости замени wide на content):
<figure class="case-media" data-case-tall-media data-case-tall-width="wide">
<div class="case-media-tall" data-case-tall-root>
<div
class="case-media-tall__clip"
id="УНИКАЛЬНЫЙ-ID"
data-case-tall-clip
style="--case-tall-max: min(70vh, 32rem)"
>
<img
src="../assets/<папка-кейса>/<файл>.webp"
alt="…"
width="1200"
loading="lazy"
decoding="async"
data-image-viewer
/>
<div class="case-media-tall__fog" aria-hidden="true"></div>
</div>
<div class="case-media-tall__actions">
<button
type="button"
class="case-media-tall__btn btn btn--ghost btn--flat"
data-case-tall-toggle
aria-expanded="false"
aria-controls="УНИКАЛЬНЫЙ-ID"
>
<span class="case-media-tall__btn-chev" aria-hidden="true"></span>
<span data-case-tall-label-more>Развернуть</span>
<span data-case-tall-label-less hidden>Свернуть</span>
</button>
</div>
</div>
<figcaption>…</figcaption>
</figure>
Живой пример: src/pages/cases/template-elements-demo.html (секция «Длинный скрин»); второй кейс — src/pages/cases/ai-platform-primo-rpa.html.
Галерея изображений сеткой (case-media-gallery)
Сетка из миниатюр с тем же полноэкранным просмотром, что у одиночных картинок (data-image-viewer на каждом <img>). Стили в public/css/main.css (блок .case-media-gallery).
Разметка:
- Обертка:
figure.case-media.case-media-gallery(+ опциональноcase-media-gallery--wide). - Внутри:
div.case-media-gallery__grid→ для каждой ячейкиdiv.case-media-gallery__item→<img … data-image-viewer />. - Число колонок на широком экране задаётся на
figure, например:style="--case-gallery-cols: 3"(целое ≥ 1). Если атрибут не указан, по умолчанию в CSS задано3. - Ширина: без модификатора — как обычный блок в колонке статьи; класс
case-media-gallery--wide— выход за колонку по той же схеме, что уcase-media--wide(viewport с боковыми отступами). На узких экранах (max-width: 640px) wide-галерея схлопывается в ширину колонки. - Адаптив сетки: примерно до
48rem— не больше двух колонок (min(2, var(--case-gallery-cols))); до28rem— одна колонка.
Пример целиком см. в src/pages/cases/template-elements-demo.html (два варианта: контентная ширина и wide).
Сетка + отложенный src: в ячейках можно задать реальный URL в data-src и плейсхолдер в src (крошечный data:image/gif;base64,…), чтобы initCaseSequentialImages подставлял картинки по порядку в DOM (в т.ч. строка за строкой в галерее). Пока у img есть data-src, просмотр по клику не открывается. Пример: src/pages/cases/lunacy.html.
Последовательная подгрузка (data-case-sequential-images)
- Оберни фрагмент статьи с картинками (часто весь
.case-article):data-case-sequential-imagesна контейнере. - У каждого кадра:
data-src="../assets/…/файл.webp"и временныйsrc— прозрачный GIF 1×1 (тот же паттерн, что в Lunacy-кейсе). - Логика:
public/js/modules/cases/sequential-images.js, вызовinitCaseSequentialImages()вpublic/js/main.jsдоinitCaseImageViewer(). - Просмотр:
public/js/modules/cases/image-viewer.jsслушает клики делегированием; не открывает плейсхолдер и не открывает, пока не снятdata-src.
Callout
<aside class="case-callout" data-callout>
<span class="case-callout__emoji case-callout__emoji--pulse" aria-hidden="true">📌</span>
<div class="case-callout__content">
<p>…</p>
</div>
</aside>
- Ширина: как у wide-медиа — блок шире колонки статьи (
min(100vw − 2×padding, content + 8rem)), на экранах до 640px — ширина колонки. - Иконка: в
.case-callout__emoji— цветные emoji (📌 💡 ✨ ✅ ⭐), не текстовые символы (✎ ✓ ◆ →); рендер через emoji-шрифт вmain.css. - Типографика: текст в
.case-callout__contentчуть крупнее основного абзаца (lead). - Живой пример:
src/pages/cases/design-system.html(секция Callout).
Видео
- Обертка:
<figure class="case-media">(часто с inline-отступами как в template) →<div class="case-video-shell">→<video controls preload="metadata" playsinline … data-case-video-player>. - Подпись
<figcaption>под видео — по центру колонки (стили вmain.css). - Видео в карточке и в fullscreen —
object-fit: contain(без обрезки); fullscreen через Plyr (iosNative: false), не нативныеcontrolsна<video>. - Постер: WebP в
poster. - Источники:
<source type="video/webm">, затемvideo/mp4.
Про конвертацию и постеры: docs/video-conversion.md.
Оптимизация изображений
PNG/JPEG → WebP по папке: docs/image-optimization.md, скрипт scripts/optimize-images.mjs.
Связь с JS
Подключаемые через {{> head-links}} сценарии уже инициализируют:
- последовательную подгрузку
data-srcвнутри[data-case-sequential-images](initCaseSequentialImages); - просмотр картинок по клику (
data-image-viewer, делегирование на документ); - плеер видео кейса (
data-case-video-player); - натуральный размер для
.case-media--natural img; - сворачивание длинного скрина (
[data-case-tall-media],initCaseTallMediaExpand).
Новые интерактивные паттерны — по правилам из docs/frontend-js-architecture.md (модуль в public/js/modules/cases/, вызов из main.js).
Чеклист нового кейса
- Скопировать
template-elements-demo.html→src/pages/cases/<slug>.html, заменитьmeta,title, контент. - Положить медиа в
public/assets/<slug>/(см.public/assets/README.mdиdocs/image-optimization.md). - Проверить все пути
../assets/…и../css/…. - Выполнить
npm run build:pagesи открытьpublic/cases/<slug>.htmlлокально. - При необходимости завести текстовый слепок в
texts/cases/<slug>.mdи ссылку на кейс вsrc/partials/projects-section.htmlпосле сборки главной.