Оглавление статьи
- Вводная часть
- Архитектура шаблона
- Содержимое файла .editorconfig
- Содержимое файла .gitignore
- Содержимое файла index.html
- Содержимое файла manifest.json
- Содержимое файла README.md
- Содержимое файла style.css
- Содержимое файла script.js
Вводная часть
Основная идея заключается в создании заготовки простенького шаблона, который можно было бы повторно использовать для верстки каких то не больших страничек. Так же хотелось, чтобы шаблон имел базовое SEO и заготовки в виде скриптов Google Analytics и Yandex Metrika.
Архитектура шаблона
Будем использовать стандартный подход в именовании и расположении файлов html проекта. Создайте в корне проекта следующие файлы и каталоги.
./
├── css
├── fonts
├── img
├── js
├── .editorconfig
├── .gitignore
├── index.html
├── manifest.json
└── README.md
- css - содержит стили
- fonts - содержит шрифты
- img - содержит изображения
- js - содержит JavaScript скрипты
- .editorconfig - содержит правила написания кода
- .gitignore - содержит перечень файлов и каталогов, которые не будут добавлены в репозиторий git
- index.html - содержит разметку главной страницы
- manifest.json - содержит информацию об иконках, цветовой теме, ориентации экрана, начальном URL и т.д.
- README.md - содержит описание проекта и инструкцию по его развертыванию
Содержимое файла .editorconfig
Более подробно ознакомиться с настройками конфигурационного файла .editorconfig можно на официальном сайте.
# Корневой файл .editorconfig
root = true
# Все файлы
[*]
charset = utf-8 # кодировка
indent_style = space # отступ через пробел
indent_size = 2 # размер отступа 2 символа
end_of_line = lf # unix стиль новой строки
trim_trailing_whitespace = true # удалять пробелы в конце строк
insert_final_newline = true # оставлять в конце файла пустую строку
# Md файлы
[*.md]
trim_trailing_whitespace = false # удалять пробелы в конце строк
Содержимое файла .gitignore
Содержит перечень файлов и каталогов, которые не будут добавлены в репозиторий git. Исключим стандартные каталоги создаваемые программами VSCode и PhpStorm.
.idea
.vscode
Содержимое файла index.html
Создадим файл index.html и с помощью Emmet разворачиваем базовую разметку, введя ! знак, и нажав Tab. Должно получиться примерно следующее.
<!doctype html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Document</title>
</head>
<body>
</body>
</html>
Базовые дополнения
Внесем базовые нюансы для нашего шаблона.
<!-- class="no-js" - по умолчанию предполагаем, что у пользователя отключен в браузере JS. -->
<!-- Это позволит стилизовать некоторые задействованные в js элементы как то иначе, когда JS отключен. -->
<html class="no-js" lang="ru">
<!-- Мета информация с кратким описанием страницы -->
<meta name="description" content="Описание главной страницы">
<!-- Скрипт удаляющий из html тега класс no-js. -->
<!-- Если у пользователя включен JS, то смысла в альтернативной стилизации элементов нет. -->
<script>document.documentElement.classList.remove('no-js')</script>
Добавим стили
Подключим в шаблон CSS стили. Параллельно с этим пунктом создадим в каталоге css файл style.css, а так же скачаем в него библиотеку normalize.css.
В каталоге css должна получиться следующая структура.
./css/
├── normalize.css
└── style.css
Подключим файлы стилей в файл index.html.
<!-- Добавим заготовку под критические стили. -->
<!-- Сюда будет выноситься часть скриптов, участвующих в стилизации первого экрана. -->
<style></style>
<!-- Подключим CSS файлы. -->
<!-- К названию файлы добавим так же номер версии ?v=0.0.1, для борьбы с кешированием. -->
<!-- Атрибут onload="this.rel='stylesheet'" дает возможность браузеру отложить загрузку файла
со стилями и приступить непосредственно к чтению тегов страницы. Только после полной отрисовки DOM дерева
будет загружен файл со стилями. Что положительно влияет на показатели Page Speed. -->
<link rel="preload" as="style" href="/css/normalize.css?v=0.0.1" onload="this.rel='stylesheet'">
<link rel="preload" as="style" href="/css/style.css?v=0.0.1" onload="this.rel='stylesheet'">
<!-- Так как атрибут onload сработает только при включенном JS мы подстрахуемся и добавим возможность
загружать CSS стили стандартным способом при выключенном JS. -->
<noscript>
<link rel="stylesheet" href="/css/normalize.css?v=0.0.1">
<link rel="stylesheet" href="/css/style.css?v=0.0.1">
</noscript>
Добавим предзагрузку шрифтов
Когда мы используем в проекте не стандартные шрифты, рекомендуется заранее подсказать браузеру об их загрузки. Для того чтобы браузер заранее начал скачивать пользовательские шрифты.
Добавим в каталог fonst какой-нибудь кастомный шрифты формата woff2, например Roboto и подключим его в файле index.html
<link rel="preload" as="font" href="/fonts/roboto/roboto-regular.woff2" type="font/woff2" crossorigin="anonymous">
Добавим SEO мета данные
Добавим поддержку Open Graph, Twitter Card и микроразметку JSON-LD.
<!-- Основная ссылка на текущую страницу. -->
<link rel="canonical" href="http://localhost:3000">
<!-- Мета данные для Apple устройств -->
<meta name="application-name" content="Главная страница — Название сайта">
<meta name="apple-mobile-web-app-title" content="Главная страница — Название сайта">
<!-- Мета данные Open Graph -->
<meta property="og:title" content="Главная страница — Название сайта">
<meta property="og:locale" content="ru">
<meta property="og:description" content="Описание главной страницы">
<meta property="og:url" content="http://localhost:3000">
<meta property="og:site_name" content="Описание главной страницы">
<meta property="og:type" content="website">
<meta property="og:image" content="http://localhost:3000/img/cover.jpg">
<!-- Мета данные Twitter Card -->
<meta property="twitter:card" content="summary">
<meta property="twitter:title" content="Главная страница — Название сайта">
<meta property="twitter:image" content="http://localhost:3000/img/cover.jpg">
<!-- Микроразметку JSON-LD для простой страницы -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebSite",
"headline": "Главная страница — Название сайта",
"description": "Описание главной страницы",
"name": "Описание главной страницы",
"url": "http://localhost:3000"
}
</script>
<!-- Микроразметку JSON-LD для страницы со статей -->
<!-- Необходимо использовать один из вариантов в зависимости от назначения страницы. -->
<!--
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"dateModified": "2023-12-31T00:00:00+06:00",
"datePublished": "2023-12-31T00:00:00+06:00",
"headline": "Страница — Название сайта",
"description": "Описание страницы",
"name": "Описание страницы",
"url": "http://localhost:3000"
}
</script>
-->
Добавим стилизацию страницы для мобильных устройств
Данная стилизация позволит придать акцентный цвет браузеру на мобильных устройствах.
<meta name="theme-color" content="#000000">
<meta name="msapplication-navbutton-color" content="#000000">
<meta name="apple-mobile-web-app-status-bar-style" content="#000000">
Добавим фавиконки
Небольшая иконка отображающаяся на вкладке браузера. Рекомендуется подключать сразу несколько иконок различного размера, для корректного отображения ее на различных устройствах.
Загрузим в каталог ./img/favicons следующие иконки:
- favicon.ico - размер иконки должен быть 32х32
- icon.svg - масштабируемая иконка под любые размеры
- apple-touch-icon.png - размер иконки должен быть 180х180
- icon-192.png - размер иконки должен быть 192х192, будет использоваться в файле manifest.json
- icon-512.png - размер иконки должен быть 512х512, будет использоваться в файле manifest.json
Подключим фавиконки в файл index.html.
<link rel="icon" href="/img/favicons/favicon.ico" sizes="any">
<link rel="icon" href="/img/favicons/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/img/favicons/apple-touch-icon.png">
<link rel="manifest" href="/manifest.json">
Добавим Google Analytics и Yandex Metrika
Подключим скрипты для индексации страницы в поисковых системах.
<!-- Google Analytics -->
<!-- <script async>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); })(window,document,'script','dataLayer','GTM-XXXXXX');</script> -->
<!-- Yandex Metrika -->
<!-- <script async>(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)}; m[i].l=1*new Date();k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)}) (window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym"); ym(XXXXXX, "init", {});</script> -->
После открывающего тега body подключим эти же скрипты, но только для браузера в котором отключен JS.
<!-- Google Analytics -->
<!--<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXX" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>-->
<!-- Yandex Metrika -->
<!--<noscript><img src="https://mc.yandex.ru/watch/XXXXXX" style="position:absolute; left:-9999px;" alt=""></noscript>-->
В дальнейшем получив уникальный идентификатор необходимо будет заменить в скриптах GTM-XXXXXX и XXXXXX на свои.
Добавим уведомление об отключеном JS
Создадим специфичную разметку с уведомлением для пользователей с отключенным JS.
<!-- JavaScript отключен -->
<noscript><p class="notice"><strong>JavaScript отключен</strong>, некоторые функции сайта не доступны. Пожалуйста <a rel="nofollow" href="https://support.google.com/adsense/answer/12654" target="_blank">включите JavaScript</a>!</p></noscript>
Добавим svg спрайт
Создадим вручную svg спрайт, для этого скачайте любую svg иконку и скопируйте ее path тег целиком, для того, чтобы позже добавить ее на страницу.
<!-- Svg sprite -->
<!-- Блок содержащий спрайт скрываем, чтобы всего его содержимое не отображалось на странице. -->
<div hidden>
<svg xmlns="http://www.w3.org/2000/svg">
<!-- Добавляем содержимое скаченной svg иконки в тег symbol. -->
<!-- Каждой иконки в наборе svg спрайта необходимо задавать уникальный id. -->
<!-- В данном случае у нас одна иконка с id="icon-user". -->
<symbol id="icon-user" viewBox="0 0 24 24" fill="none">
<path d="M17.2166 19.3323C15.9349 17.9008 14.0727 17 12 17C9.92734 17 8.06492 17.9008 6.7832 19.3323M12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12C21 16.9706 16.9706 21 12 21ZM12 14C10.3431 14 9 12.6569 9 11C9 9.34315 10.3431 8 12 8C13.6569 8 15 9.34315 15 11C15 12.6569 13.6569 14 12 14Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</symbol>
</svg>
</div>
Добавим базовую разметку
Создадим некоторую разметку на которой протестируем функционал шаблона.
<div class="container">
<!-- Таким образом выводится нужная картинка из svg спрайта. -->
<!-- Вывод производится по ее id имени. -->
<svg width="50" height="50" aria-hidden="true"><use href="#icon-user"></use></svg>
<h2>Простой HTML шаблон</h2>
<p>Пример реализации простого <b>HTML</b> шаблона, без <b>Gulp</b> сборки. С базовой <b>SEO</b> настройкой.</p>
<p>Подойдет для верстки простых одностраничных сайтов (landing page).</p>
<!-- В каталог img добавил некоторую картинку и отобразил ее на странице. -->
<p style="margin-top: 40px"><img src="/img/pagespeed.jpg" width="653" height="739" alt="Page Speed статистика" loading="lazy" fetchpriority="high"></p>
</div>
Добавим скрипт
Подключим в шаблон JS скрипт. Создадим в каталоге js файл script.js и подключим его в шаблон.
<script type="module" src="/js/script.js?v=0.0.1"></script>
Конечный вариант
Мы полностью закончили с заполнением содержимого файла index.html и вот, что у нас получилось.
<!DOCTYPE html>
<html class="no-js" lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Главная страница — Название сайта</title>
<meta name="description" content="Описание главной страницы">
<script>document.documentElement.classList.remove('no-js')</script>
<!-- Критические стили -->
<style></style>
<link rel="preload" as="style" href="/css/normalize.css?v=0.0.1" onload="this.rel='stylesheet'">
<link rel="preload" as="style" href="/css/style.css?v=0.0.1" onload="this.rel='stylesheet'">
<noscript>
<link rel="stylesheet" href="/css/normalize.css?v=0.0.1">
<link rel="stylesheet" href="/css/style.css?v=0.0.1">
</noscript>
<link rel="preload" as="font" href="/fonts/roboto/roboto-regular.woff2" type="font/woff2" crossorigin="anonymous">
<link rel="canonical" href="http://localhost:3000">
<meta name="application-name" content="Главная страница — Название сайта">
<meta name="apple-mobile-web-app-title" content="Главная страница — Название сайта">
<meta property="og:title" content="Главная страница — Название сайта">
<meta property="og:locale" content="ru">
<meta property="og:description" content="Описание главной страницы">
<meta property="og:url" content="http://localhost:3000">
<meta property="og:site_name" content="Описание главной страницы">
<meta property="og:type" content="website">
<meta property="og:image" content="http://localhost:3000/img/cover.jpg">
<meta property="twitter:card" content="summary">
<meta property="twitter:title" content="Главная страница — Название сайта">
<meta property="twitter:image" content="http://localhost:3000/img/cover.jpg">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebSite",
"headline": "Главная страница — Название сайта",
"description": "Описание главной страницы",
"name": "Описание главной страницы",
"url": "http://localhost:3000"
}
</script>
<!--
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"dateModified": "2023-12-31T00:00:00+06:00",
"datePublished": "2023-12-31T00:00:00+06:00",
"headline": "Страница — Название сайта",
"description": "Описание страницы",
"name": "Описание страницы",
"url": "http://localhost:3000"
}
</script>
-->
<meta name="theme-color" content="#000000">
<meta name="msapplication-navbutton-color" content="#000000">
<meta name="apple-mobile-web-app-status-bar-style" content="#000000">
<link rel="icon" href="/img/favicons/favicon.ico" sizes="any">
<link rel="icon" href="/img/favicons/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/img/favicons/apple-touch-icon.png">
<link rel="manifest" href="/manifest.json">
<!-- Google Analytics -->
<!-- <script async>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); })(window,document,'script','dataLayer','GTM-XXXXXX');</script> -->
<!-- Yandex Metrica -->
<!-- <script async>(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)}; m[i].l=1*new Date();k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)}) (window, document, "script", "https://mc.yandex.ru/metrika/tag.js", "ym"); ym(XXXXXX, "init", {});</script> -->
</head>
<body>
<!-- Google Analytics -->
<!--<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXX" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>-->
<!-- Yandex Metrica -->
<!--<noscript><img src="https://mc.yandex.ru/watch/XXXXXX" style="position:absolute; left:-9999px;" alt=""></noscript>-->
<!-- JavaScript отключен -->
<noscript><p class="notice"><strong>JavaScript отключен</strong>, некоторые функции сайта не доступны. Пожалуйста <a rel="nofollow" href="https://support.google.com/adsense/answer/12654" target="_blank">включите JavaScript</a>!</p></noscript>
<!-- Svg sprite -->
<div hidden>
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="icon-user" viewBox="0 0 24 24" fill="none">
<path d="M17.2166 19.3323C15.9349 17.9008 14.0727 17 12 17C9.92734 17 8.06492 17.9008 6.7832 19.3323M12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12C21 16.9706 16.9706 21 12 21ZM12 14C10.3431 14 9 12.6569 9 11C9 9.34315 10.3431 8 12 8C13.6569 8 15 9.34315 15 11C15 12.6569 13.6569 14 12 14Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</symbol>
</svg>
</div>
<div class="container">
<svg width="50" height="50" aria-hidden="true"><use href="#icon-user"></use></svg>
<h2>Простой HTML шаблон</h2>
<p>Пример реализации простого <b>HTML</b> шаблона, без <b>Gulp</b> сборки. С базовой <b>SEO</b> настройкой.</p>
<p>Подойдет для верстки простых одностраничных сайтов (landing page).</p>
<p style="margin-top: 40px"><img src="/img/pagespeed.jpg" width="653" height="739" alt="Page Speed статистика" loading="lazy" fetchpriority="high"></p>
</div>
<script type="module" src="/js/script.js?v=0.0.1"></script>
</body>
</html>
Содержимое файла manifest.json
manifest.json метаинформация о приложении или расширении, которая позволяет платформе или браузеру правильно обрабатывать и представлять ваше приложение. Ознакомиться со значением свойств можно на сайте mdn.
{
"name": "Название сайта",
"short_name": "Название сайта",
"description": "Описание сайта",
"lang": "ru",
"dir": "ltr",
"id": "/",
"start_url": "/",
"scope": "/",
"display": "minimal-ui",
"orientation": "any",
"theme_color": "#000000",
"background_color": "#000000",
"prefer_related_applications": false,
"icons": [
{
"src": "/img/favicons/icon-192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "/img/favicons/icon-512.png",
"type": "image/png",
"sizes": "512x512"
},
{
"src": "/img/favicons/icon.svg",
"sizes": "any",
"type": "image/svg",
"purpose": "maskable"
}
]
}
В целом значение многих свойств понятны из контекста выделю лишь некоторые:
- dir - направление текста приложения, может быть слева-направо, справа-налево
- id - позволяет явно определить идентификатор, используемый для вашего приложения
- start_url - сообщает браузеру по какой ссылку открывать приложение
- scope - набор URL-адресов, которые браузер считает принадлежащими вашему приложению, и используется для принятия решения о том, когда пользователь покинул приложение
- display - настраивает вид пользовательского интерфейса браузера, может иметь значение fullscreen, standalone, minimal-ui, browser
- prefer_related_applications - это логическое значение, которое используется в качестве подсказки пользовательскому агенту, чтобы сказать, что связанные приложения должны быть предпочтительнее веб-приложения.
Содержимое файла README.md
Пользовательское описание проекта в формате Markdown, так же может включать в себя инструкцию по развертыванию.
# Простой HTML шаблон
Пример реализации простого **HTML** шаблона, без **Gulp** сборки. С базовой **SEO** настройкой.
Подойдет для верстки простых одностраничных сайтов (landing page).
![Page Speed статистика](https://raw.githubusercontent.com/eliofery/simple-html-template/main/img/pagespeed.jpg)
## Режим разработки
```bash
npx browser-sync start --server --no-notify --no-ui --files '*.html,css/**/*.css,img/*'
```
Содержимое файла style.css
Базовое содержимое стилей. Хорошо бы вынести содержимое переменных и базовых стилей в отдельные файлы, но пока ограничимся одним файлом.
/* Переменные */
:root {
--fz-label: 117px;
--fz-heading: 78px;
--fz-heading-2: 55px;
--fz-heading-3: 24px;
--fz-heading-4: 18px;
--fz-heading-5: 16px;
--color-black: #000;
--color-white: #fff;
--width-container: 360px;
--gutter: 20px;
}
@media screen and (min-width: 768px) {
:root {
--width-container: 768px;
--gutter: 40px;
}
}
@media screen and (min-width: 1440px) {
:root {
--width-container: 1440px;
}
}
/* Базовая разметка */
@font-face {
font-family: Roboto;
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("../fonts/roboto/roboto-regular.woff2") format("woff2");
}
*,
*::before,
*::after {
box-sizing: inherit;
}
html {
box-sizing: border-box;
height: 100%;
}
body {
position: relative;
min-width: var(--width-container);
min-height: 100%;
color: var(--color-black);
font-family: "Roboto", Arial, sans-serif;
font-size: var(--fz-heading-5);
line-height: 1.5;
background-color: var(--color-white);
}
img {
max-width: 100%;
height: auto;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
border: 0;
clip: rect(0,0,0,0);
-webkit-clip-path: inset(100%);
clip-path: inset(100%);
}
.container {
max-width: var(--width-container);
margin: 0 auto;
padding: 0 var(--gutter);
}
.notice {
margin: 0;
padding: 10px;
color: white;
text-align: center;
background-color: black;
}
.notice a {
color: yellow;
}
.notice a:hover,
.notice a:focus {
text-decoration: none;
}
Содержимое файла script.js
Изначальное содержимое будет пустым, так как на страницу шаблона мы не добавляли динамических элементов.
Простой HTML шаблон готов к использованию. С полным кодом статьи можно ознакомиться в моем репозитории.