03. Partials — кирпичи сайта
Partial — от английского partial, «частичный». Это файл, который содержит не целую страницу, а только её фрагмент: шапку, меню, футер. Собираются такие фрагменты в готовые страницы через SSI, который мы настроили в предыдущем разделе.
Идея проста: то, что повторяется на всех страницах, пишем один раз. И подключаем в нужное место через одну строку.
Зачем нужны partials
Без partials каждая страница сайта содержала бы полный код шапки, меню и футера. У нас восемь страниц — значит, восемь копий шапки, восемь копий меню, восемь копий футера. Поменяли телефон в футере — правим в восьми файлах и молимся, чтобы нигде не забыть.
С partials всё иначе: телефон правится в одном файле. На всех восьми страницах он обновится автоматически.
Без partials / С partialsБЕЗ PARTIALS С PARTIALS
index.html - шапка - меню index.html ----+
services.html - шапка - меню services.html -+
about.html - шапка - меню about.html ----+
... ... |
Каждая |
Каждая страница содержит страница |
8 копий одного и того же ссылается на |
кода шапки один файл |
v
partials/
header.html
nav.html
footer.html
head-links.html
8 правок при изменении 1 правка
Какие partials есть в проекте
- head-links.html — служебные теги для head: фавиконы, og-теги, twitter-теги, ссылки на стили.
- header.html — левая часть шапки: логотип «Е», название, кнопка-бургер.
- nav.html — главное меню из шести ссылок.
- footer.html — нижний блок: реквизиты, копирайт, код Метрики.
Разберём каждый по порядку. Построчно, с пояснением каждого символа и отступа.
3.1. partials/head-links.html
Этот файл подключается внутри head каждой страницы. Он содержит всё то, что нужно всем страницам одинаково: три фавикона, шесть og-тегов, два twitter-тега и два подключения стилей. Уникальные title, meta description и og:title остаются в самих страницах — они у каждой свои.
<!-- ============================================================
ФАЙЛ: partials/head-links.html
АВТОР: Ефимов Андрей Михайлович
НАЗНАЧЕНИЕ: Повторяющиеся ссылки для head — фавиконы,
og-теги, twitter-теги, стили.
ПОДКЛЮЧАЕТСЯ: внутри head каждой страницы через SSI-директиву
include virtual на этот файл.
ЗАВИСИТ ОТ: файлов favicon.svg, favicon-32.png,
apple-touch-icon.png, style.css, scroll-top.css
в корне.
ОТ НЕГО ЗАВИСЯТ: все страницы сайта.
ВАЖНО №1: Google Fonts намеренно исключены — системный стек
шрифтов устраняет «дёргание» при загрузке.
ВАЖНО №2: НЕ размещать здесь SSI-директиву include на самого
себя — это вызывает зацикливание страницы.
ВАЖНО №3: favicon.svg идёт ПЕРВЫМ и единственным тегом
с rel="icon" type="image/svg+xml". Дублирование
SVG-тегов сбивает браузер с толку.
============================================================ -->
<!-- Фавикон: основной SVG (современные браузеры) -->
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<!-- Фавикон: PNG 32×32 (браузеры без поддержки SVG) -->
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png" />
<!-- Apple Touch Icon: иконка на домашнем экране iOS -->
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<!-- Theme color: окрашивает адресную строку мобильного браузера -->
<meta name="theme-color" content="#33ff33" />
<!-- Open Graph: общие теги для превью в соцсетях -->
<meta property="og:type" content="website" />
<meta property="og:site_name" content="Ефимов Андрей Михайлович" />
<meta property="og:image" content="https://secserpr.ru/og-image.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:locale" content="ru_RU" />
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="https://secserpr.ru/og-image.png" />
<!-- Стили -->
<link rel="stylesheet" href="/style.css" />
<link rel="stylesheet" href="/scroll-top.css" />
Блок-паспорт в комментарии
Первые строки — комментарий с паспортом файла. Он не отображается на странице, но его читает тот, кто откроет исходник. Именно здесь содержится ответ на вопрос «а зачем этот файл нужен».
Строка ПОДКЛЮЧАЕТСЯ: внутри head каждой страницы через SSI — ключевая. Она говорит: этот файл попадает не как обычный HTML, а через серверное включение.
Строка «Google Fonts намеренно исключены» напоминает, почему мы не подключаем внешние шрифты. Строка «НЕ размещать здесь SSI-директиву include на самого себя» — защита от классической ошибки зацикливания (разбиралась в разделе 02). Строка про «favicon.svg идёт первым и единственным тегом с rel=icon type=image/svg+xml» — напоминание: если добавить ещё один SVG-тег, браузер возьмёт последний, а первый проигнорирует. Именно эта ошибка была в старой версии файла.
Фавикон: три файла
В файле — три тега для фавикона. Не один, а три, потому что разные браузеры и устройства предпочитают разные форматы. Все три файла лежат в корне сайта.
Разбор трёх фавиконов1. SVG-файл /favicon.svg
Используют: современные браузеры (Chrome, Firefox, Safari 15+)
Плюс: векторный, масштабируется без потерь
Основной вариант для сайта 2026 года
2. PNG 32×32 /favicon-32.png
Используют: браузеры без поддержки SVG
Плюс: старый стандартный формат, надёжный
3. Apple Touch Icon /apple-touch-icon.png
Используют: iOS при добавлении сайта на домашний экран
Плюс: специфика мобильных устройств Apple
Смотрим на первый — основной:
SVG-фавикон<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
Стандартный тег link с атрибутом rel="icon" сообщает браузеру: «это иконка сайта». Атрибут type="image/svg+xml" указывает формат — SVG. Путь /favicon.svg — абсолютный, с ведущей косой чертой: файл лежит в корне сайта.
Внутри favicon.svg — тот же самый код, что и в логотипе шапки: зелёный квадрат с чёрной буквой «Е». Единый визуальный код бренда.
В первой версии проекта фавикон был встроен прямо в атрибут href как data-URI — длинная строка вида href="data:image/svg+xml,%3Csvg...". Идея была: «один файл меньше, один HTTP-запрос меньше». Но на практике вышло иначе. Во-первых, браузер видел два тега с одинаковым rel="icon" type="image/svg+xml" — data-URI и внешний файл — и брал только последний. Первый висел зря. Во-вторых, длинный закодированный SVG парсится заново на каждой странице при каждой загрузке. Внешний файл /favicon.svg кэшируется браузером один раз — это быстрее и проще.
Theme color
Meta theme-color<meta name="theme-color" content="#33ff33" />
Мета-тег, который окрашивает адресную строку мобильного браузера в указанный цвет. На Android Chrome адресная строка сверху подсветится зелёным #33ff33 — тем же цветом бренда.
Это не обязательный тег. Но он даёт эффект «единого целого»: сайт и адресная строка сливаются в одно визуальное пространство.
Шесть og-тегов и два twitter-тега
Следующий блок — общие теги для соцсетей. Их задача: когда кто-то кидает ссылку на страницу сайта в Telegram, WhatsApp или VK — мессенджер показывает превью с картинкой, заголовком и описанием.
Og-теги построчноog:type = website тип объекта
og:site_name = «Ефимов...» имя сайта
og:image = полный URL картинка превью
og:image:width = 1200 ширина картинки
og:image:height = 630 высота картинки
og:locale = ru_RU язык
Два twitter-тега:
- twitter:card =
summary_large_image— тип карточки: «большая картинка сверху». Используется и в Telegram, и в WhatsApp — они читают Twitter Card как запасной источник. - twitter:image — та же картинка, что у og:image.
og:image:width и og:image:height — подсказка мессенджеру, чтобы он не грузил картинку заранее, а сразу знал пропорции. Скорость.
Общие og-теги (тип, имя сайта, картинка, локаль) — одинаковые для всех страниц, поэтому в head-links.html. А og:title, og:description, og:url — уникальные, поэтому остаются в самих страницах. Разделение по «повторяемости»: общее — в partial, частное — в странице.
Строки подключения стилей
Стили<link rel="stylesheet" href="/style.css" />
<link rel="stylesheet" href="/scroll-top.css" />
Две строки — два тега link с атрибутом rel="stylesheet" («таблица стилей»).
Обратите внимание на путь: href="/style.css" — абсолютный, с ведущей косой чертой. Он говорит браузеру: «файл лежит в корне сайта». Не «рядом с текущей страницей», а именно в корне.
Это критично для partials. Если бы мы написали href="style.css", то из папки /blog/ браузер искал бы файл по адресу /blog/style.css — и не нашёл бы. Абсолютный путь работает всегда — и с любой страницы, и из любой папки.
style.css — основные стили. Их подключают все страницы. scroll-top.css — только стили кнопки «Наверх». Технически можно было бы объединить. Мы разделили по одной причине: если кнопка «Наверх» когда-нибудь уедет или будет переделана — править придётся только её файл, не рискуя задеть основные стили.
3.2. partials/header.html
Этот файл вставляется первым внутри header каждой страницы. Он содержит левую часть шапки — логотип и название сайта, а также кнопку-бургер для мобильных.
partials/header.html<!-- Блок-паспорт -->
<div class="header-left">
<svg class="icon" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect width="100" height="100" fill="#33ff33" />
<text x="50" y="72" font-size="70" text-anchor="middle"
fill="#000000" font-family="Arial, sans-serif"
font-weight="bold">Е</text>
</svg>
<div class="header-text">
<h1>Ефимов Андрей Михайлович</h1>
<div class="sub">Цифровая трансформация службы безопасности</div>
</div>
</div>
<button class="burger" aria-label="Меню">
<span></span>
<span></span>
<span></span>
</button>
Левая часть: логотип и текст
div class="header-left" — контейнер, объединяющий логотип и текст. Через CSS мы говорим ему: «выстрой детей в строку, добавь между ними 20 пикселей» (правило .header-left { display: flex; gap: 20px; }). Без этой обёртки логотип и текст не знали бы, что они — единая группа.
Дальше — SVG-логотип. Он такой же, как в фавиконе, только не в data-URI, а в виде обычных тегов SVG. Смотрим на атрибуты:
- class="icon" — класс для стилизации. В style.css правило
.header .icon { width: 48px; height: 48px; }задаёт размеры. - viewBox="0 0 100 100" — «внутренняя система координат» SVG. Говорит: рисуем в квадрате 100×100. Реальные размеры задаются в CSS.
- xmlns="http://www.w3.org/2000/svg" — обязательный атрибут для inline-SVG. Без него некоторые браузеры не станут отображать графику.
Внутри SVG — два элемента:
- rect width="100" height="100" fill="#33ff33" — прямоугольник на всю область, залитый зелёным.
- text x="50" y="72" ...Е — буква «Е». Координаты x="50" y="72" — центр квадрата по горизонтали и чуть ниже центра по вертикали (чтобы визуально буква казалась по центру, а не висела сверху).
Ниже — div class="header-text" с заголовком и подзаголовком. Заголовок — h1, потому что это главный заголовок страницы с точки зрения семантики. Каждая страница имеет один h1. Остальные заголовки — h2, h3.
Кнопка-бургер
Бургер<button class="burger" aria-label="Меню">
<span></span>
<span></span>
<span></span>
</button>
Кнопка button, а не ссылка или div. Это осознанный выбор: по клику происходит действие (открытие меню), а не переход по адресу. Для действий семантически правильен именно button.
Три пустых span — три полоски будущего бургера. Внутри — ничего, потому что рисоваться они будут через CSS (правило .burger span { display: block; width: 28px; height: 3px; background: #1a3a5c; }). Пустые элементы для декоративных полосок — обычный приём.
Атрибут aria-label="Меню" — служебный для screen-reader. Слепой пользователь услышит «Меню, кнопка», а не «пустая кнопка». Это требование доступности.
Обратите внимание: тег header открывается и закрывается в самой странице, а не в partials. Это сделано потому, что между открывающим и закрывающим тегом вставляется ещё и nav.html. Если бы header был внутри header.html, мы не смогли бы вставить nav внутрь него через SSI — тег бы закрылся раньше.
3.3. partials/nav.html
Навигация вынесена в отдельный файл. Причина проста: это самая часто изменяемая часть сайта. Добавить раздел, убрать, переставить порядок — всё делается здесь, в одном месте.
partials/nav.html<!-- Блок-паспорт -->
<nav class="nav">
<a href="/index.html">Главная</a>
<a href="/services.html">Услуги</a>
<a href="/about.html">Обо мне</a>
<a href="/projects.html">Кейсы</a>
<a href="/offer.html">КП</a>
<a href="/contacts.html">Контакты</a>
</nav>
Ничего сложного с виду — шесть ссылок. Но есть два момента, на которые стоит обратить внимание.
Абсолютные пути в ссылках
Все ссылки начинаются с косой черты: /index.html, /services.html и т.д. Это уже обсуждалось, но здесь особенно важно. Если бы мы написали href="services.html", из какой-нибудь будущей вложенной страницы (например, /blog/article.html) ссылка вела бы в /blog/services.html — и не работала бы.
Ведущая косая черта означает: «считай от корня сайта». Так ссылки работают всегда, из любой точки.
Класс active НЕ прописан вручную
Обратите внимание: ни на одной ссылке нет class="active". В старой версии сайта приходилось на каждой странице вручную помечать активную ссылку. Забыл — и на странице «Услуги» подсвечена «Главная». Хаос.
У нас за это отвечает JavaScript. В script.js есть модуль, который:
- Берёт текущий адрес из window.location.pathname.
- Сравнивает его с href каждой ссылки в меню.
- Если совпало — добавляет этой ссылке класс active.
Благодаря этому мы больше никогда не думаем про активную ссылку. Она подсвечивается сама. Скрипт разберём в Части III.
Когда блог будет готов, добавляем в nav.html одну строку: <a href="/blog.html">Блог</a>. Всё. На всех страницах сайта появится новая ссылка в меню. Никаких правок в других файлах.
3.4. partials/footer.html
Нижний блок — самый «текстовый» из partials. Реквизиты, копирайт, ссылка на политику и код Метрики. Вставляется в конце каждой страницы, внутри контейнера.
partials/footer.html<!-- Блок-паспорт -->
<div class="footer">
<div class="left">
<strong>Исполнитель:</strong><br />
Индивидуальный предприниматель Ефимов Андрей Михайлович<br />
ИНН 780519548222 / ОГРНИП 324784700301772<br />
Тел.: +7 994 432 57 40<br />
E-mail: 9944325740@mail.ru
</div>
<div class="right">
<strong>Банковские реквизиты:</strong><br />
Р/с 40802810220001003235<br />
...
</div>
</div>
<div class="copyright">
© 2026 Ефимов Андрей Михайлович. Все права защищены.<br />
<a href="/privacy.html">Политика обработки данных</a>
</div>
Два блока: left и right
Футер построен как flex-контейнер с двумя дочерними блоками. В CSS правило .footer { display: flex; justify-content: space-between; } разводит их по краям: левый прижат влево, правый — вправо.
На мобильных (ширина ≤ 768 пикселей) медиа-запрос переключает flex-direction в column — блоки встают друг под друга, выравниваются по центру.
Тег br — перенос строки
Внутри блоков футера каждая строка заканчивается тегом br. Это не самый «семантически чистый» способ, но для реквизитов — практичный. Альтернатива — каждую строку оборачивать в p, но тогда между строками были бы большие отступы, а нам нужны плотные строчки реквизитов.
Слеш / в конце тега — «самозакрывающийся тег». В HTML5 он не обязателен, но мы ставим для единообразия и совместимости.
Те же самые реквизиты есть на странице offer.html (в разделе «Условия сотрудничества»), contacts.html и privacy.html. Если ИНН, банк или счёт изменятся — надо править в нескольких местах. В будущем это можно вынести в отдельный partial, но пока реквизиты меняются редко — оставили так.
Блок копирайта
Отдельный блок, потому что по стилю он отличается: меньше шрифт, серый цвет, тонкая линия сверху. Это «подвал подвала» — метаинформация.
Ссылка на /privacy.html — юридическое требование. По закону о персональных данных у каждой формы сбора данных должна быть доступная политика. Мы используем её как ссылку в подвале — стандартная практика.
Код Яндекс.Метрики в конце
Метрика встроена именно в footer.html, не в каждую страницу. Это значит: как только partial раскрылся на любой странице — счётчик загрузился. Одна запись — восемь страниц работают с аналитикой.
В файле используется счётчик 112503289. Этот номер встречается в коде трижды: в URL загрузки скрипта, в вызове ym() и в теге noscript. При смене аккаунта — менять во всех трёх местах.
Если случайно вставить код Метрики и в footer.html, и в саму страницу — счётчик загрузится дважды, статистика будет двойная. Одно место, одно правило.
3.5. Ловушка SSI: зацикливание через комментарий
Есть ошибка, в которую очень легко попасть. И она невидима до момента, пока не натворит бед.
SSI-директивы выполняются даже внутри HTML-комментариев. То есть вот такая строка:
Опасный примерПример: подключить шапку можно так:
include virtual="/partials/header.html"
(внутри HTML-комментария)
Выглядит как безобидный комментарий-подсказка. Но сервер выполнит директиву include несмотря на то, что она внутри комментария.
И если такая строка попадёт в сам файл head-links.html, произойдёт зацикливание: сервер попытается включить head-links.html в самого себя, потом ещё раз, и так — до бесконечности. Страница раздуется до сотен килобайт мусора.
В паспортах partials нельзя размещать строку include как пример. Если нужно упомянуть механизм — пишем словами: «подключается через SSI-директиву include», без самого символа #.
Именно поэтому в паспортах наших partials формулировка — без реального синтаксиса include.
Итог
- Partial — это фрагмент страницы в отдельном файле.
- У нас четыре partials: head-links.html, header.html, nav.html, footer.html.
- Каждый partial — с блоком-паспортом в начале: что это, зачем, где подключается.
- В partials всегда абсолютные пути: /style.css, /partials/nav.html.
- Активная ссылка в меню ставится автоматически через JavaScript.
- Код Метрики (счётчик 112503289) — только в footer.html, никогда в самих страницах.
- Ловушка: не размещать include в HTML-комментарии — SSI выполнит директиву и зациклит страницу.
- head-links.html — самый насыщенный partial: 3 фавикона, theme-color, 6 og-тегов, 2 twitter-тега, 2 подключения стилей.
Теперь у нас есть все «кирпичи». Следующая часть — разбор стилей. Как устроен style.css, почему в нём секции, и что делает каждый медиа-запрос.