Files
site-portfolio/docs/case-page-blocks.md
T

16 KiB
Raw Blame History

Страница кейса: каркас и блоки контента

Инструкция завязана на эталон 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)

Порядок важен для единообразия с остальными кейсами:

  1. {{> header-case}} — шапка с ссылкой на главную (внутри partial используется {{assetPrefix}}).
  2. {{> fab-stack}} — плавающие кнопки темы и т.п.
  3. <main id="case-main" class="section section--tight"> → внутри <div class="section__inner case-article"> — весь уникальный контент кейса.
  4. Нижние секции как на главной: {{> projects-section}}, {{> contact-section}}, {{> footer-spacer}}, {{> footer}}, {{> cookie-notice}}, {{> nav-more-floating}}.
  5. Скрипты подключаются в <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).

Как вставить в кейс

  1. На figure поставь class="case-media", data-case-tall-media, data-case-tall-width="content" или wide.
  2. Внутри — разметка ниже: уникальный id у .case-media-tall__clip, тот же id в aria-controls у кнопки; подписи «Развернуть»/«Свернуть» в span с data-case-tall-label-more / data-case-tall-label-less (у «Свернуть» изначально hidden).
  3. На img оставь data-image-viewer, если нужен полноэкранный просмотр по клику.
  4. Высоту превью (обрезки) задай на clip, например inline: style="--case-tall-max: min(60vh, 28rem)". Если не задавать, в CSS по умолчанию min(70vh, 32rem).
  5. Файлы клади в 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.

Сетка из миниатюр с тем же полноэкранным просмотром, что у одиночных картинок (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).

Чеклист нового кейса

  1. Скопировать template-elements-demo.html → src/pages/cases/<slug>.html, заменить meta, title, контент.
  2. Положить медиа в public/assets/<slug>/ (см. public/assets/README.md и docs/image-optimization.md).
  3. Проверить все пути ../assets/… и ../css/….
  4. Выполнить npm run build:pages и открыть public/cases/<slug>.html локально.
  5. При необходимости завести текстовый слепок в texts/cases/<slug>.md и ссылку на кейс в src/partials/projects-section.html после сборки главной.