Статический сайт для GitHub Pages. Без бэкенда, без npm-зависимостей. 28 страниц, открытый прайс на 159 позиций, запись через WhatsApp.
Пять пунктов, без которых сайт работает, но не в полную силу.
В CNAME указан 100mashin.ru — это заглушка, домен ещё не куплен.
Важно: 100-машин.рф занят несвязанным бизнесом — автосалоном подержанных
машин с телефоном краснодарского кода. Использовать его нельзя.
Свободные варианты для проверки: 100mashin.ru, 100mashin36.ru,
stomashin.ru, sto-mashin.ru, 100mashin-vrn.ru.
После покупки:
- Замените домен в
CNAME(одна строка, безhttps://, без слэша). - Замените
https://100mashin.ruвsrc/pages/_shared.js(константаSITE) и вtools/build.mjs(константаSITE). - Пересоберите:
node tools/build.mjs. - Настройте DNS у регистратора:
; Апекс — все четыре A-записи обязательны
100mashin.ru. 3600 IN A 185.199.108.153
100mashin.ru. 3600 IN A 185.199.109.153
100mashin.ru. 3600 IN A 185.199.110.153
100mashin.ru. 3600 IN A 185.199.111.153
; IPv6 — дополнительно к A, не вместо
100mashin.ru. 3600 IN AAAA 2606:50c0:8000::153
100mashin.ru. 3600 IN AAAA 2606:50c0:8001::153
100mashin.ru. 3600 IN AAAA 2606:50c0:8002::153
100mashin.ru. 3600 IN AAAA 2606:50c0:8003::153
; www → апекс, БЕЗ имени репозитория
www.100mashin.ru. 3600 IN CNAME <ваш-логин>.github.io.
- В Settings → Pages укажите домен, дождитесь выпуска сертификата и включите Enforce HTTPS.
- Там же нажмите Verify domain и добавьте TXT-запись — это защищает от захвата поддомена.
Проверка DNS в PowerShell: Resolve-DnsName 100mashin.ru -Type A
Сейчас форма записи работает в режиме WhatsApp: заявка собирается в браузере и открывается в мессенджере с готовым текстом. Сеть не используется, ключи не нужны, всё работает сразу.
Если нужны дубли заявок на e-mail:
- Зарегистрируйтесь на web3forms.com (бесплатно,
250 заявок в месяц), укажите почту
autolaw@inbox.ru. - Впишите ключ в
assets/js/config.js:export const WEB3FORMS_KEY = 'ваш-ключ-отсюда';
- Больше ничего делать не нужно. Форма уже помечена атрибутами
data-bookingиdata-form, а собранный текст заявки лежит в скрытом полеmessage. Как только ключ задан,booking.jsпосле открытия WhatsApp дополнительно отправляет ту же заявку на почту черезform.js.
Пока ключ не задан, второй канал не активируется — иначе WhatsApp открывался бы
дважды. Логика в booking.js, функция hasMailChannel().
Важно про 152-ФЗ: Web3Forms размещён за пределами РФ, это трансграничная
передача данных. Раздел 4 в /politika/ описывает текущую схему (без передачи
через сайт). Если подключите Web3Forms, дополните раздел 7 политики и
проконсультируйтесь с юристом о необходимости уведомления Роскомнадзора.
- Создайте счётчик на metrika.yandex.ru.
- Впишите номер в
assets/js/config.js:export const METRIKA_ID = 12345678;
- Раскомментируйте блок Метрики в
src/partials/footer.html, подставив номер вместо00000000. - Пересоберите:
node tools/build.mjs.
Пока METRIKA_ID = 0, трекинг полностью выключен и не даёт ошибок.
Цели, которые нужно создать в интерфейсе Метрики (тип — «JavaScript-событие»):
| Идентификатор | Что означает |
|---|---|
PHONE_CLICK |
клик по телефону |
WHATSAPP_CLICK |
переход в WhatsApp |
| VK_CLICK | переход во ВКонтакте |
| BOOKING_SEND | отправлена заявка на запись |
| FORM_START | начал заполнять форму |
| FORM_SUBMIT | форма отправлена |
| CALC_USE | воспользовался калькулятором |
| PRICE_VIEW | дочитал прайс до середины |
| MAP_OPEN | открыл карту |
| ROUTE_CLICK | построил маршрут |
В src/partials/head.html замените заглушки:
<meta name="yandex-verification" content="REPLACE_YANDEX_VERIFICATION">
<meta name="google-site-verification" content="REPLACE_GOOGLE_VERIFICATION">Затем в Яндекс Вебмастере: добавьте sitemap.xml, задайте регион Воронеж
(Информация о сайте → Региональность) и создайте карточку в Яндекс Бизнесе,
чтобы данные совпадали с микроразметкой.
Своих фотографий на сайте нет — намеренно. Стоковые картинки с иностранными номерами выглядят фальшиво и вредят доверию.
Что снять и добавить в assets/img/:
- ремонтная зона, машины на подъёмниках;
- станочный участок: фрезеровка и опрессовка ГБЦ, расточка блока;
- мастера за работой (с их согласия), для страницы «О сервисе»;
- магазин запчастей, стеллажи;
- зона ожидания;
- въезд и вывеска на обеих площадках — особенно на Труда, 46И, там сложный проезд и это отмечено в отзывах;
- работы «до/после» для страницы
/raboty/.
Формат: <picture> с AVIF/WebP/JPEG, обязательные width и height,
осмысленный alt. Первое изображение на экране — fetchpriority="high"
без loading="lazy", остальные — loading="lazy" decoding="async".
├── index.html, ceny/, uslugi/, ... ← собранные страницы (не править вручную)
├── sitemap.xml ← генерируется сборщиком
├── CNAME, robots.txt, .nojekyll, manifest.webmanifest, favicon.ico
│
├── assets/
│ ├── css/
│ │ ├── styles.css дизайн-система: @layer reset→tokens→base→layout→components→utilities
│ │ └── components/
│ │ ├── hero.css первый экран, композиция главной, ритм секций
│ │ └── blocks.css адреса, контакты, прайс, отзывы, гарантия
│ ├── js/ ES-модули без зависимостей
│ │ ├── config.js ВСЕ бизнес-данные: телефоны, адреса, ключи
│ │ ├── app.js точка входа, вызывает остальные модули
│ │ ├── nav.js меню, ловушка фокуса, отметка текущей страницы
│ │ ├── prices.js рендер и фильтрация прайса
│ │ ├── calc.js калькулятор оценки
│ │ ├── booking.js запись → сообщение в WhatsApp
│ │ ├── form.js отправка форм, режим WhatsApp / Web3Forms
│ │ ├── deeplinks.js сборка ссылок WhatsApp
│ │ ├── map.js карта по клику
│ │ ├── lazyblock.js предзагрузка карт при приближении
│ │ ├── tabs.js доступные табы
│ │ └── analytics.js цели Метрики
│ ├── data/prices.json 159 позиций прайса
│ ├── fonts/ Golos Text, кириллический сабсет
│ └── img/
│
├── src/ ← ИСТОЧНИК, правьте здесь
│ ├── partials/ head, header, footer — общие для всех страниц
│ └── pages/ по файлу на страницу или группу
│ ├── _shared.js данные и переиспользуемые блоки
│ └── _service-page.js конструктор страниц услуг
│
└── tools/build.mjs сборщик
# собрать сайт
node tools/build.mjs
# только проверить, ничего не записывая
node tools/build.mjs --check
# качество разметки: h1, canonical, alt, JSON-LD, вес страниц
node tools/check.mjs
# логика модулей: прайс, калькулятор, слоты записи, ссылки
node tools/smoke.mjs
# мобильная адаптация в реальном браузере: 5 экранов x 12 страниц
# проверяет горизонтальную прокрутку, цели нажатия, перекрытия
node tools/mobile-audit.mjs
# визуальная согласованность: выравнивание колонок, единый набор радиусов
# и кеглей, контраст текста по WCAG, ритм отступов, пустоты в первом экране
node tools/check-design.mjs
# предзагрузка карт при подходе к блоку адресов
node tools/check-lazy.mjs
# посмотреть локально
python -m http.server 8000
# → http://localhost:8000mobile-audit.mjs требует установленного Chrome или Edge: он поднимает
браузер в headless-режиме и замеряет вычисленные стили, а не догадывается
по CSS. Остальные проверки работают на одном Node.
Правьте только src/. Файлы index.html, ceny/index.html и остальные
собранные страницы перезаписываются сборщиком.
Где что менять:
| Что нужно | Где править |
|---|---|
| Телефон, адрес, часы работы | assets/js/config.js и src/pages/_shared.js |
| Цены | assets/data/prices.json + таблицы в src/pages/uslugi-*.js |
| Меню, шапка, подвал | src/partials/header.html, footer.html |
| Текст страницы | соответствующий файл в src/pages/ |
| Новая страница услуги | добавить вызов servicePage({...}) в src/pages/uslugi-*.js |
| Стили | assets/css/styles.css |
Сборщик проверяет: дубли адресов, наличие и длину title и description,
ровно один <h1> на странице, валидность JSON-LD и все внутренние ссылки.
Если что-то не так, сборка сообщит об ошибке.
Сайт построен на проверенных данных: рейтинг 4,7 и 120 оценок на Яндекс Картах, регистрация ООО «СТО МАШИН» с 27.09.2018, 17 сотрудников по отчётности за 2025 год, нормочас 1200 ₽, цены шиномонтажа с карточки Яндекса.
Осознанные решения, которые лучше не отменять:
aggregateRatingв микроразметке отсутствует. Google запрещает самостоятельную разметку собственных отзывов, а перенос рейтинга Яндекса в свою разметку — нарушение. Рейтинг показывается виджетом Яндекса.- Отрицательные отзывы опубликованы на
/otzyvy/вместе с описанием изменений. Они всё равно видны на Картах; честный разбор работает лучше умолчания. - Часть цен помечена «оценка». Это позиции, рассчитанные по нормочасу, а не
взятые из подтверждённого прайса. Пометка задаётся флагом
estвprices.json. Перед публикацией стоит сверить их с мастерами. - Telegram не указан нигде — аккаунта у сервиса нет.
- Доступность для маломобильных посетителей описана честно: оборудованного
доступа нет, это указано в FAQ на
/o-nas/.
Список вопросов, ответы на которые улучшат сайт:
- Точные условия гарантии. Сроки в
/garantiya/составлены по отраслевой практике. Единственное упоминание в открытых источниках — «6 месяцев на капремонт», причём из жалобы. Нужно подтверждение владельца. - Суббота — рабочая или нет. Источники расходятся, на сайте указано «по предварительной записи».
- Количество постов, подъёмников, площадь, марки оборудования — сильный аргумент доверия, данных нет.
- Эвакуатор — свой или партнёрский, радиус выезда.
- Автостёкла — реальная услуга или устаревшая категория в справочнике.
- Имена мастеров для страницы «О сервисе». В отзывах упоминаются Игорь, Андрей, Денис, Руслан, Александр, Дмитрий, Михаил.
- Сертификат представителя масел — какой бренд, действует ли.
- Мотоциклы и грузоперевозки рефрижератором — направления упоминались во ВКонтакте в 2025 году, актуальны ли сейчас.
git add -A
git commit -m "Сайт автосервиса «100 Машин»"
git push -u origin mainЗатем Settings → Pages → Source: Deploy from a branch, ветка main,
папка / (root).
Обратите внимание: GitHub Pages отдаёт всё с Cache-Control: max-age=600 и
изменить это нельзя. Поэтому имена файлов без хешей — кеш обновляется сам в
течение 10 минут после публикации.
Каждое из них появилось после замера в браузере, а не по вкусу.
Правая колонка первого экрана. На всех страницах она заполнена карточкой
(heroAside() в src/pages/_shared.js). Раньше внутренние страницы были
одноколоночными, и при ширине 1440px справа оставалось 759px пустоты, а текст
был зажат в 417px из-за max-width: 42ch у .hero__body. Это ограничение
снято: ширину текста задаёт сам .hero__lead, а не контейнер.
Никакого абсолютного позиционирования плашек над текстом. Прежний
.hero__media-note был спозиционирован absolute в расчёте на фотографию,
которой нет, и накрывал список принципов. Правила удалены полностью.
Затемнённые оттенки для текста и заливок. Фирменные #e8590c и #1faa53
не проходят по контрасту, если на них белый текст полужирностью 600:
дают 3,58 и 3,03 при норме 4,5. Поэтому кнопки залиты --amber-600 и
--color-wa-hover, а зелёный текст ссылок использует отдельный токен
--color-wa-text (5,43 на белом). Звёзды рейтинга — --amber-700.
Номера в списках — --color-text-muted, а не --color-text-faint.
Разный ритм секций. Одинаковые отступы у всех секций читаются как машинная
раскладка. Модификаторы: .section--chapter (смысловой водораздел, больше
воздуха), .section--follow (продолжение мысли, меньше), .section--close.
На каждой странице есть хотя бы один акцент — это проверяет check-design.mjs.
Колонки выровнены по высоте. .split--stretch тянет врезку на всю высоту
строки: при align-items: start короткая колонка заканчивалась на 129px выше
длинной, и блок выглядел несобранным.
Категории прайса свёрнуты. Все 159 позиций разом давали простыню на
телефоне. Открыта первая группа, при поиске или выборе категории —
раскрываются все (assets/js/prices.js, функция render).
Фотографий на сайте нет намеренно. Стоковые снимки с чужими машинами и иностранными номерами вредят доверию сильнее, чем помогает картинка. Место под фотографии в вёрстке предусмотрено — см. раздел 5 выше.
Анимации только как дополнение. Появление секций реализовано через
animation-timeline: view() внутри @supports и
@media (prefers-reduced-motion: no-preference). Элементы видимы по умолчанию:
без поддержки браузера ничего не пропадает. opacity: 0 в базовом состоянии
не используется нигде.