[
  {
    "path": ".gitignore",
    "content": ".idea/\n.DS_Store\n"
  },
  {
    "path": "01_technologies.md",
    "content": "# Основные возможности и используемые технологии\n\n* Система сборки [Gulp](https://gulpjs.com/)\n* Оптимизация изображений.\n* Генерация PNG- и SVG-спрайтов.\n* Шаблонизация с помощью [Pug](https://pugjs.org/).\n* CSS-препроцессор [SCSS](http://sass-lang.com/) и [Autoprefixer](https://autoprefixer.github.io/ru/).\n* ES6 и [jQuery](https://jquery.com/).\n* Встроенное определение устройства, браузера и операционной системы пользователя.\n* Проверка кода линтерами ([pug-lint](https://www.npmjs.com/package/pug-lint), [stylelint](https://stylelint.io/), [ESLint](http://eslint.org/)).\n* [Browsersync](https://www.browsersync.io/), автоматическое обновление страницы при разработке.\n* Возможность быстро создать архив проекта.\n* Множество дополнительных параметров сборки.\n"
  },
  {
    "path": "02_requirements.md",
    "content": "# Минимальные требования\n\n* node >= 9.5.0\n* npm >= 5.6.0\n* gulp >= 4.0.0\n* gulp-cli >= 2.0.1\n\n[Ссылка на инструкцию по переходу с gulp 3 на gulp 4](https://demisx.github.io/gulp4/2015/01/15/install-gulp4.html).\n"
  },
  {
    "path": "03_installation.md",
    "content": "# Начало работы\n\nДля установки рекомендуется использовать [Yeoman](http://yeoman.io/):\n\n```bash\nnpm install -g yo\n```\n\nПосле yeoman, необходимо установить шаблон самой сборки:\n\n```bash\nnpm install -g generator-ninelines-template\n```\n\nТеперь находясь в пустой папке с проектом выполняем команду:\n\n```bash\nyo ninelines-template\n```\n\nГенератор задаст несколько вопросов:\n\n- Название проекта (по умолчанию — название папки проекта).\n- Описание проекта.\n- Запустить ли `npm install` (по умолчанию — да). Если пропустить установку npm-пакетов, то перед началом работы над проектом необходимо самостоятельно запустить `npm install`.\n\nТеперь можно запустить `gulp` и приступить к работе.\n"
  },
  {
    "path": "04_tasks.md",
    "content": "# Gulp-задачи\n\n* `default` — основная задача, запускает `build`, `watch` и `serve`.\n* `build` — сборка всех файлов, запускает задачи `copy`, `images`, `sprites:png`, `sprites:svg`, `pug`, `scss`, `js`.\n* `watch` — запускает слежение за файлами, так что при изменении они автоматически пересобираются.\n* `serve` — запускает сервер Browsersync.\n* `pug` — запускает сборку Pug-шаблонов.\n* `images` — запускает сборку изображений.\n* `sprites:png` — запускает генерацию PNG-спрайтов.\n* `sprites:svg` — запускает генерацию SVG-спрайтов.\n* `scss` — запускает сборку стилей.\n* `js` — запускает сборку скриптов.\n* `copy` — запускает сборку дополнительных ресурсов.\n* `lint` — последовательно запускает линтеры `lint:js`, `lint:pug`, `lint:scss`.\n* `lint:js` — проверяет JavaScript-файлы линтером [ESLint](http://eslint.org/).\n* `lint:pug` — проверяет Pug-файлы линтером [pug-lint](https://github.com/pugjs/pug-lint).\n* `lint:scss` — проверяет SCSS-файлы линтером [stylelint](https://stylelint.io/).\n* `optimize:svg` — оптимизирует и форматирует код SVG-файлов в папке `src/images`.\n* `optimize:images` — оптимизирует изображения в папке `src/images`.\n* `zip` — создает архив проекта.\n\n## Дополнительные параметры:\n\n* `--ci` — включает режим CI (`--no-cache --no-notify --no-open --throw-errors`).\n* `--fix` — автоматически исправляет ошибки при проверке кода линтером (только для `lint:js`).\n* `--minify` — включает минификацию файлов (только для `sprites:svg`, `pug`, `scss` и `js`).\n* `--minify-html` — включает минификацию HTML-файлов (имеет приоритет перед `--minify`).\n* `--minify-css` — включает минификацию CSS-файлов (имеет приоритет перед `--minify`).\n* `--minify-js` — включает минификацию JS-файлов (имеет приоритет перед `--minify`).\n* `--minify-svg` — включает минификацию SVG-файлов (имеет приоритет перед `--minify`).\n* `--no-cache` — отключает кэширование (только для `copy`, `images` и `pug`).\n* `--no-debug` — отключает отладочный вывод списка обрабатываемых файлов.\n* `--no-notify` — отключает уведомления об ошибках.\n* `--no-open` — отключает автоматический запуск браузера (только для `serve`).\n* `--port` — задает порт сервера (только для `serve`).\n* `--spa` — включает режим одностраничного приложения (только для `serve`).\n* `--throw-errors` — прерывает сборку при возникновении ошибки.\n"
  },
  {
    "path": "05_structure.md",
    "content": "# Структура папок и файлов\n\n```text\nninelines-template\n├── src\n│   ├── images\n│   │   └── sprites\n│   │       ├── png\n│   │       │   └── .keep\n│   │       └── svg\n│   │           └── .keep\n│   ├── js\n│   │   ├── vendor\n│   │   │   └── .keep\n│   │   ├── main.js\n│   │   └── vendor.js\n│   ├── pug\n│   │   ├── mixins\n│   │   │   └── svg.pug\n│   │   ├── base.pug\n│   │   └── mixins.pug\n│   ├── resources\n│   │   └── fonts\n│   │       └── .keep\n│   ├── scss\n│   │   ├── functions\n│   │   │   └── _sprites.scss\n│   │   ├── mixins\n│   │   │   ├── _clearfix.scss\n│   │   │   ├── _retina.scss\n│   │   │   ├── _sprites.scss\n│   │   │   ├── _triangle.scss\n│   │   │   └── _visually-hidden.scss\n│   │   ├── vendor\n│   │   │   └── .keep\n│   │   ├── _base.scss\n│   │   ├── _fonts.scss\n│   │   ├── _functions.scss\n│   │   ├── _mixins.scss\n│   │   ├── _sprites.hbs\n│   │   ├── _sprites.scss\n│   │   ├── _variables.scss\n│   │   ├── _vendor.scss\n│   │   └── main.scss\n│   └── index.pug\n├── .babelrc\n├── .editorconfig\n├── .eslintignore\n├── .eslintrc\n├── .gitignore\n├── .npmrc\n├── .pug-lintrc.json\n├── .stylelintignore\n├── .stylelintrc\n├── gulpfile.js\n├── package.json\n├── README.md\n└── webpack.config.js\n```\n\n## `src`\n\nВ папке `src` хранятся исходные файлы проекта.\n\n## `src/images`\n\nПапка `images` предназначена для хранения изображений.\nПри сборке файлы из данной папки попадают в `build/images`.\n\n## `src/images/sprites`\n\nПапка `src/images/sprites` предназначена для хранения векторных (SVG) и растровых (PNG) иконок.\n\n## `src/images/sprites/png`\n\nПапка `src/images/sprites/png` предназначена для хранения растровых иконок.\nПри сборке файлы из данной папки объединяются в два спрайта: `build/images/sprites.png` и `build/images/sprites@2x.png`.\n\n## `src/images/sprites/svg`\n\nПапка `src/images/sprites/svg` предназначена для хранения векторных иконок.\nПри сборке файлы из данной папки объединяются в один спрайт: `build/images/sprites.svg`.\n\n## `src/js`\n\nПапка `src/js` предназначена для хранения скриптов.\n\n## `src/js/vendor`\n\nПапка `src/js/vendor` предназначена для хранения скриптов сторонних библиотек, которых нет в репозитории npm.\n\n## `src/js/main.js`\n\nФайл `src/js/main.js` предназначен для хранения основной логики сайта.\nПри сборке данный файл попадает в `build/js`.\n\n## `src/js/vendor.js`\n\nФайл `src/js/vendor.js` предназначен для подключения сторонних библиотек.\n\nПри сборке данный файл попадет в `build/js`.\n\n## `src/pug`\n\nПапка `src/pug` предназначена для хранения шаблонов.\n\n## `src/pug/mixins`\n\nПапка `src/pug/mixins` предназначена для хранения Pug-миксин.\n\n## `src/pug/base.pug`\n\nВ файле `src/pug/base.pug` хранится базовый шаблон страниц сайта.\n\n## `src/pug/mixins.pug`\n\nФайл `src/pug/mixins.pug` предназначен для подключения Pug-миксин из папки `src/pug/mixins`.\n\n## `src/resources`\n\nПапка `src/resources` предназначена для хранения различных файлов проекта.\nПри сборке файлы из данной папки попадают в `build`.\n\n## `src/resources/fonts`\n\nПапка `src/resources/fonts` предназначена для хранения шрифтов.\nПри сборке файлы из данной папки попадают в `build/fonts`.\n\n## `src/scss`\n\nПапка `src/scss` предназначена для хранения стилей.\n\n## `src/scss/functions`\n\nПапка `src/scss/functions` предназначена для хранения SCSS-функций.\n\n## `src/scss/mixins`\n\nПапка `src/scss/mixins` предназначена для хранения SCSS-миксин.\n\n## `src/scss/vendor`\n\nПапка `src/scss/vendor` предназначена для хранения стилей сторонних библиотек, которых нет в репозитории npm.\n\n## `src/scss/_base.scss`\n\nФайл `src/scss/_base.scss` предназначен для хранения базовых стилей.\n\n## `src/scss/_fonts.scss`\n\nФайл `src/scss/_fonts.scss` предназначен для подключения шрифтов.\n\n## `src/scss/_functions.scss`\n\nФайл `src/scss/_functions.scss` предназначен для подключения функций из папки `src/scss/functions`.\n\n## `src/scss/_mixins.scss`\n\nФайл `src/scss/_mixins.scss` предназначен для подключения миксин из папки `src/scss/mixins`.\n\n## `src/scss/_sprites.hbs`\n\n`src/scss/_sprites.hbs` — шаблон, на основе которого генерируется содержимое файла `src/scss/_sprites.scss`.\n\n## `src/scss/_sprites.scss`\n\nФайл `src/scss/_sprites.scss` предназначен для работы с PNG-спрайтами.\nСодержимое данного файла автоматически генерируется на основе шаблона `src/scss/_sprites.hbs` и иконок из папки `src/images/sprites/png`.\n\n## `src/scss/_variables.scss`\n\nФайл `src/scss/_variables.scss` предназначен для хранения SCSS-переменных.\n\n## `src/scss/_vendor.scss`\n\nФайл `src/scss/_vendor.scss` предназначен для подключения стилей сторонних библиотек.\n\n## `src/scss/main.scss`\n\nФайл `src/scss/main.scss` предназначен для хранения основных стилей сайта.\nПри сборке данный файл преобразуется в CSS и сохраняется в `build/css` вместе с файлом `main.css.map`.\n\n## `src/index.pug`\n\n`src/index.pug` — шаблон главной страницы.\nПри сборке все Pug-файлы из папки `src` преобразуются в HTML и сохраняются в `build`.\n\n## `.babelrc`\n\n`.babelrc` — файл настроек JavaScript-транспайлера Babel.\n\n## `.editorconfig`\n\n`.editorconfig` — файл настроек редактора.\n\n## `.eslintignore`\n\n`.eslintignore` — файл настроек ESLint для игнорирования файлов.\n\n## `.eslintrc`\n\n`.eslintrc` — файл настроек ESLint.\n\n## `.gitignore`\n\n`.gitignore` — файл настроек Git для игнорирования файлов.\n\n## `.npmrc`\n\n`.npmrc` — файл настроек npm.\n\n## `.pug-lintrc.json`\n\n`.pug-lintrc.json` — файл настроек pug-lint.\n\n## `.stylelintignore`\n\n`.stylelintignore` — файл настроек stylelint для игнорирования файлов.\n\n## `.stylelintrc`\n\n`.stylelintrc` — файл настроек stylelint.\n\n## `gulpfile.js`\n\n`gulpfile.js` — основной файл сборки, содержащий Gulp-задачи.\n\n## `package.json`\n\n`package.json` — файл, содержащий базовую информацию о проекте и список требуемых библиотек.\n\n## `README.md`\n\n`README.md` — описание проекта.\n\n## `webpack.config.js`\n\n`webpack.config.js` — файл настроек webpack.\n"
  },
  {
    "path": "06_libraries.md",
    "content": "# Подключение сторонних библиотек\n\nБиблиотеки подключаются с помощью npm.\nПри установке следует указывать ключ `--save` или `--save-dev`.\n\nПример:\n\n```bash\nnpm install --save jquery\nnpm install --save-dev gulp\n```\n\n`--save` указывается для библиотек, код которых попадает в итоговую сборку (папку `build`) и будет использоваться на сайте.\n\n`--save-dev` указывается для библиотек, которые используются только для сборки.\n\nПосле установки необходимо подключить нужные файлы библиотеки:\n\n* скрипты — в `src/js/vendor.js` или `src/js/main.js`.\n* стили — в `src/scss/_vendor.scss`.\n* изображения — в `src/images`.\n* любые другие файлы — в `src/resources`.\n\nПолный пример, описывающий установку библиотеки fancybox:\n\n1. Установка:\n\n   ```bash\n   npm install --save fancybox\n   ```\n\n2. Подключение скриптов в файл `src/js/vendor.js`:\n\n   ```js\n   import 'fancybox';\n   ```\n\n3. Подключение стилей в файл `src/scss/_vendor.scss`:\n\n   ```scss\n   $fancybox-image-url: \"../images\";\n\n   @import \"../../node_modules/fancybox/dist/scss/jquery.fancybox\";\n   ```\n\n4. Копирование изображений в `src/images`:\n\n   ```text\n   ninelines-template\n   └── src\n       ├── images\n       │   ├── blank.gif\n       │   ├── fancybox_loading.gif\n       │   ├── fancybox_loading@2x.gif\n       │   ├── fancybox_overlay.png\n       │   ├── fancybox_sprite.png\n       │   ├── fancybox_sprite@2x.png\n       │   └── ...\n       └── ...\n   ```\n\nЕсли библиотека отсутствует в npm, либо её нужно модифицировать, то файлы следует скачать и закинуть в папки `src/js/vendor` и `src/scss/vendor`.\n"
  },
  {
    "path": "07_images.md",
    "content": "# Работа с изображениями\n\nИзображения следует хранить в папке `src/images`.\nПри запуске задачи `images` файлы из папки `src/images` копируются в `build/images`.\n\n```text\nninelines-template\n├── build\n│   └── images\n└── src\n    └── images\n```\n\nДля оптимизации изображений можно использовать задачу `optimize:images`.\n\n> `optimize:images` оптимизирует только исходные файлы из папки `src/images`!\n\nПредварительно оптимизированные изображения рекомендуется хранить в папке `src/resources/images`.\nВ таком случае при запуске задачи `optimize:images` данные файлы не будут затронуты.\n\n```text\nninelines-template\n└── src\n    └── resources\n        └── images\n```\n\n## Работа с PNG-спрайтами\n\nРабота с PNG-спрайтами строится следующим образом:\n\n1. Берем две версии иконки — обычную и retina (увеличенную в два раза).\n   Сохраняем в `src/images/sprites/png`:\n\n   ```text\n   ninelines-template\n   └── src\n       └── images\n           └── sprites\n               └── png\n                   ├── phone.png\n                   └── phone@2x.png\n   ```\n\n2. Запускаем задачу `sprites:png` (если уже запущен `gulp watch` или `gulp`, то данный шаг можно пропустить):\n\n   ```bash\n   gulp sprites:png\n   ```\n\n3. Генератор оптимизирует и объединяет иконки в спрайты:\n\n   ```text\n   ninelines-template\n   └── build\n       └── images\n           ├── sprites.png\n           └── sprites@2x.png\n   ```\n\n   На основе предзаданного шаблона `src/scss/_sprites.hbs` генерируется файл `src/scss/_sprites.scss`, содержащий вспомогательную информацию о получившихся спрайтах:\n\n   ```text\n   ninelines-template\n   └── src\n       └── scss\n           ├── _sprites.hbs\n           └── _sprites.scss\n   ```\n\n   Для каждой иконки создается CSS-класс в формате `.sprite-[name]`.\n   В нашем случае получим класс `.sprite-phone`.\n\n   В сборке также содержится ряд SCSS-функций и миксин для работы со спрайтами.\n\n   `src/scss/functions/_sprites.scss`:\n\n   ```scss\n   @function sprite($name, $size: normal)  { /* ... */ }\n   @function sprite-width($name, $size: normal)  { /* ... */ }\n   @function sprite-height($name, $size: normal)  { /* ... */ }\n   @function sprite-image($name, $size: normal)  { /* ... */ }\n   @function sprite-x($name, $size: normal)  { /* ... */ }\n   @function sprite-y($name, $size: normal)  { /* ... */ }\n   @function sprite-total-width($name, $size: normal)  { /* ... */ }\n   @function sprite-total-height($name, $size: normal) { /* ... */ }\n   ```\n\n   `src/scss/mixins/_srites.scss`:\n\n   ```scss\n   @mixin sprite-width($name, $size: normal)  { /* ... */ }\n   @mixin sprite-height($name, $size: normal)  { /* ... */ }\n   @mixin sprite-background-image($name, $size: normal)  { /* ... */ }\n   @mixin sprite-background-position($name, $size: normal)  { /* ... */ }\n   @mixin sprite-background-size($name, $size: normal)  { /* ... */ }\n   @mixin sprite-background($name, $size: normal)  { /* ... */ }\n   @mixin sprite($name)  { /* ... */ }\n   ```\n\n4. Полученные спрайты можно использовать в Pug (с помощью классов):\n\n   ```jade\n   footer\n       a(href=\"tel:+71234567890\")\n           span.sprite-phone\n           | +7 (123) 456-78-90\n   ```\n\n   Или в SCSS (с помощью миксин):\n\n   ```scss\n   footer {\n       a {\n           &::before {\n               @include sprite(\"phone\");\n\n               content: \"\";\n           }\n       }\n   }\n   ```\n\n## Работа с SVG-спрайтами\n\nПринцип работы с SVG-спрайтами:\n\n1. Получаем векторные иконки в формате `.svg` (либо заранее подготовленные, либо экспортируем с помощью редактора).\n   Сохраняем в папку `src/images/sprites/svg`:\n\n   ```text\n   ninelines-template\n   └── src\n       └── images\n           └── sprites\n               └── svg\n                   └── phone.svg\n   ```\n\n2. Запускаем задачу `sprites:svg` (если уже запущен `gulp watch` или `gulp`, то данный шаг можно пропустить):\n\n   ```bash\n   gulp sprites:svg\n   ```\n\n3. Генератор оптимизирует и объединяет иконки в один спрайт:\n\n   ```text\n   ninelines-template\n   └── build\n       └── images\n           └── sprites.svg\n   ```\n\n   В сборке содержится Pug-миксин для подключения SVG-спрайтов.<br>\n   `src/pug/mixins/svg.pug`:\n\n   ```jade\n    mixin svg(name)\n       svg&attributes(attributes)\n           use(xlink:href=`${baseDir}images/sprites.svg#${name}`)\n   ```\n\n4. Подключаем иконку в Pug:\n\n   ```jade\n   footer\n       a(href=\"tel:+71234567890\")\n           +svg(\"phone\")\n           | +7 (123) 456-78-90\n   ```\n\n   При необходимости иконку можно стилизовать:\n\n   ```scss\n   footer {\n       a {\n           svg {\n               display: inline-block;\n               vertical-align: middle;\n               width: 30px;\n               height: 30px;\n               fill: $color-black;\n           }\n       }\n   }\n   ```\n\n   Если цвет заливки или обводки не удается изменить с помощью CSS, то необходимо открыть SVG-файл иконки в редакторе и удалить соответствующие атрибуты (`fill`, `stroke`) из кода.\n\n## Избавляемся от обрезанных краев SVG-иконок\n\n1. Общий пример:\n\n    Шаг 1: исходная иконка без полей\n    ```html\n    <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{width}\" height=\"{height}\" viewBox=\"0 0 {width} {height}\">\n        <path d=\"...\"/>\n        <path d=\"...\"/>\n        <path d=\"...\"/>\n    </svg>\n    ```\n\n    Шаг 2: добавляем поле размером {padding}\n    ```html\n    <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{width + 2 * padding}\" height=\"{height + 2 * padding}\" viewBox=\"0 0 {width + 2 * padding} {height + 2 * padding}\">\n        <g transform=\"translate({padding} {padding})\">\n            <path d=\"...\"/>\n            <path d=\"...\"/>\n            <path d=\"...\"/>\n        </g>\n    </svg>\n    ```\n\n    Шаг 3: запускаем optimize:svg и получаем иконку без лишних трансформаций\n    ```html\n    <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{width + 2 * padding}\" height=\"{height + 2 * padding}\" viewBox=\"0 0 {width + 2 * padding} {height + 2 * padding}\">\n        <path d=\"...\"/>\n        <path d=\"...\"/>\n        <path d=\"...\"/>\n    </svg>\n    ```\n\n2. Конкретный пример:\n\n    Шаг 1: исходная иконка без полей\n    ```html\n    <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"30\" viewBox=\"0 0 20 30\">\n        <path d=\"...\"/>\n        <path d=\"...\"/>\n        <path d=\"...\"/>\n    </svg>\n    ```\n\n    Шаг 2: добавляем поле размером 1px\n    ```html\n    <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"22\" height=\"32\" viewBox=\"0 0 22 32\">\n        <g transform=\"translate(1 1)\">\n            <path d=\"...\"/>\n            <path d=\"...\"/>\n            <path d=\"...\"/>\n        </g>\n    </svg>\n    ```\n\n    Шаг 3: запускаем optimize:svg и получаем иконку без лишних трансформаций\n    ```html\n    <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"22\" height=\"32\" viewBox=\"0 0 22 32\">\n        <path d=\"...\"/>\n        <path d=\"...\"/>\n        <path d=\"...\"/>\n    </svg>\n    ```\n"
  },
  {
    "path": "08_templates.md",
    "content": "# Работа с шаблонизатором Pug\n\n> При работе с шаблонизатором **важно** придерживаться установленных [правил по оформлению кода](16_codestyle-pug.md).\n\nВ сборке используется шаблонизатор [Pug](https://pugjs.org/) (ранее назывался Jade).\n\nPug предоставляет множество возможностей, упрощающих работу с шаблонами:\n\n* Переменные.\n* Циклы.\n* Условия.\n* Фильтры.\n* Наследование шаблонов.\n* Миксины.\n\nШаблоны страниц размещаются в `src`, а дополнительные файлы и миксины в `src/pug`:\n\n```text\nninelines-template\n└── src\n    ├── pug\n    │   ├── mixins\n    │   │   └── svg.pug\n    │   ├── base.pug\n    │   └── mixins.pug\n    └── index.pug\n```\n\nЗа сборку и преобразование Pug в HTML отвечает задача `pug`:\n\n```bash\ngulp pug\n```\n\nПосле выполнения команды в папке `build` появятся HTML-файлы:\n\n```text\nninelines-template\n└── build\n    └── index.html\n```\n\n## Базовый шаблон и создание страниц\n\nВ качестве базового шаблона используется `src/pug/base.pug`.\n\nПример наследования и использования шаблона:\n\n```jade\nextends pug/base\n\nblock content\n    // Содержимое страницы\n```\n\nБазовый шаблон определяет блоки (участки кода или место в шаблоне), которые можно изменять и дополнять при наследовании.\n\n### `vars`\n\nБлок `vars` хранит основные настройки шаблона:\n\n* `baseDir` — корневая директория сайта (по умолчанию `/`).\n\n* `title` — заголовок страницы (используется в `<title>` и метатегах).\n\n* `description` — описание страницы (используется в метатегах).\n\n* `image` — изображение страницы (используется в метатегах).\n\n* `html` — настройки тега `<html>`:\n  * `html.attrs` — объект для задания дополнительных атрибутов.\n  * `html.classList` — массив классов.\n\n* `body` — настройки тега `<body>`:\n  * `body.attrs` — объект для задания дополнительных атрибутов.\n  * `body.classList` — массив классов.\n\n* `meta` — значения метатегов.\n\n* `link` — значения тегов `<link>`.\n\nПример использования:\n\n```jade\nprepend vars\n    - title = 'Заголовок'\n    - description = 'Описание'\n    - image = 'http://example.com/images/image.png'\n\nappend vars\n    - link.icon16x16 = '/favicon_16x16.png'\n    - link.icon32x32 = '/favicon_32x32.png'\n```\n\n### `head-start`\n\nБлок `head-start` является альтернативой `prepend meta`.\n\n### `meta`\n\nВ блоке `meta` подключаются метатеги.\n\nПример использования:\n\n```jade\nappend meta\n    meta(name=\"referrer\" content=\"none\")\n```\n\n### `links`\n\nВ блоке `links` подключаются внешние ресурсы.\n\nПример использования:\n\n```jade\nappend links\n    link(rel=\"prefetch\" href=\"/images/background.jpg\")\n```\n\n### `styles`\n\nВ блоке `styles` подключаются стили.\n\nПример использования:\n\n```jade\nappend styles\n    link(rel=\"stylesheet\" href=\"/css/custom.css\")\n```\n\n### `head-end`\n\nБлок `head-end` является альтернативой `append links`.\n\n### `body-start`\n\nБлок `body-start` является альтернативой `prepend content`.\n\n### `content`\n\nБлок `content` предназначен для хранения содержимого страницы.\n\nПример использования:\n\n```jade\nblock content\n    .container\n        h1\n            | Заголовок страницы\n```\n\n### `scripts`\n\nВ блоке `scripts` подключаются скрипты.\n\nПример использования:\n\n```jade\nappend scripts\n    script(src=\"/js/custom.js\")\n```\n\n### `body-end`\n\nБлок `body-end` является альтернативой `append scripts`.\n\n## Правила написания кода и использование линтера\n\nВ сборку интегрирован линтер [pug-lint](https://www.npmjs.com/package/pug-lint).\nФайл настроек — `.pug-lintrc.json`.\nДанный линтер позволяет поддерживать Pug-код в соответствии с заданным регламентом.\n\nПроверка осуществляется с помощью задачи `lint:pug`.\n\nПример использования (`src/index.pug`):\n\n```jade\nextends pug/base\n\nappend vars\n  - html.classList.push('page-index')\n\nblock content\n  a(href='#').link Ссылка\n```\n\nРезультаты проверки:\n\n```text\nninelines-template/src/index.pug:7:14\n    5|\n    6| block content\n  > 7|   a(href='#').link Ссылка\n--------------------^\n    8|\n\nAll class literals must be written before any attribute blocks\n\nninelines-template/src/index.pug:7:5\n    5|\n    6| block content\n  > 7|   a(href='#').link Ссылка\n-----------^\n    8|\n\nInvalid attribute quote mark found\n\nninelines-template/src/index.pug:4:1\n    2|\n    3| append vars\n  > 4|   - html.classList.push('page-index')\n-------^\n    5|\n    6| block content\n    7|   a(href='#').link Ссылка\n\nInvalid indentation\n\nninelines-template/src/index.pug:7:1\n    5|\n    6| block content\n  > 7|   a(href='#').link Ссылка\n-------^\n    8|\n\nInvalid indentation\n```\n\nИсправленный код:\n\n```jade\nextends pug/base\n\nappend vars\n    - html.classList.push('page-index')\n\nblock content\n    a.link(href=\"#\")\n        | Ссылка\n```\n\nВ дополнение к проверкам кода линтером следует придерживаться следующих правил:\n\n* Повторяющиеся участки кода по возможности выносить в отдельные миксины.\n* Схожие по структуре страницы выносить в отдельный шаблон и наследоваться от него.\n"
  },
  {
    "path": "09_styles.md",
    "content": "# Работа со стилями\n\n> При работе со стилями **важно** придерживаться установленных [правил по оформлению кода](17_codestyle-scss.md).\n\nВ сборке используется препроцессор [SCSS](http://sass-lang.com/) и PostCSS-плагин [Autoprefixer](https://autoprefixer.github.io/ru/).\n\nСтили размещаются в папке `src/scss`:\n\n```text\nninelines-template\n└── src\n    └── scss\n        ├── functions\n        │   ├── _responsive.scss\n        │   └── _sprites.scss\n        ├── mixins\n        │   ├── _breakpoint.scss\n        │   ├── _clearfix.scss\n        │   ├── _retina.scss\n        │   ├── _sprites.scss\n        │   ├── _triangle.scss\n        │   └── _visually-hidden.scss\n        ├── vendor\n        │   └── .keep\n        ├── _base.scss\n        ├── _fonts.scss\n        ├── _functions.scss\n        ├── _mixins.scss\n        ├── _sprites.hbs\n        ├── _sprites.scss\n        ├── _variables.scss\n        ├── _vendor.scss\n        └── main.scss\n```\n\nЗа сборку и преобразование SCSS в CSS отвечает задача `scss`:\n\n```bash\ngulp scss\n```\n\nПосле выполнения команды в папке `build/css` появятся файлы `main.css` и `main.css.map`:\n\n```text\nninelines-template\n└── build\n    └── css\n        ├── main.css\n        └── main.css.map\n```\n\n## Правила написания кода\n\n### БЭМ\n\nДля именования классов рекомендуется использовать [БЭМ-нотацию](https://ru.bem.info/methodology/naming-convention/).\n\n```scss\n.block {\n    &__element {\n        &--modificator {\n            // ...\n        }\n    }\n}\n```\n\n### Классы состояний\n\nКлассы состояний рекомендуется записывать кратко:\n\n```scss\n.is-active {\n    // ...\n}\n\n.is-current {\n    // ...\n}\n\n.is-open {\n    // ...\n}\n\n.is-hidden {\n    // ...\n}\n```\n\n### Порядок CSS-свойств\n\nCSS-свойства следует записывать в определенном порядке. Порядок задан в файле `.stylelintrc` (ключ `order/properties-order`).\nПроверить правильность порядка свойств можно с помощью линтера:\n\n```bash\ngulp lint:scss\n```\n\n### Переменные\n\nВ файл `src/scss/_variables.scss` следует выносить лишь основные переменные:\n\n* `font-family` для шрифтов. Пример:\n\n  ```scss\n  $font-family-roboto: Roboto, sans-serif;\n  $font-family-pt-serif: PT Serif, serif;\n  ```\n\n* Цвета. Пример:\n\n  ```scss\n  $color-aqua-deep: #005741;\n  $color-black: #000;\n  $color-white: #fff;\n  ```\n\n  Для именования цветов можно пользоваться [данным сервисом](http://chir.ag/projects/name-that-color/).\n\nПеременные, используемые лишь в одном блоке или компоненте следует записывать в том же файле, где они используются.\n\n### `@mixin` и `@extend`\n\nПовторяющиеся участки кода (20-30 строк и более), отличающиеся лишь значениями, следует выносить в отдельные миксины.\n\nНе рекомендуется использовать директиву `@extend`. Вместо неё следует воспользоваться `@mixin`.\n\n### Вендорные префиксы\n\nВ SCSS-коде не должно присутствовать вендорных префиксов. Они автоматически расставляются в процессе сборки. Однако существуют исключения и некоторые префиксы необходимо добавлять вручную.\n\n**Неправильно:**\n\n```scss\ninput {\n    -webkit-transition: border-color 0.3s;\n    transition: border-color 0.3s;\n\n    &::-webkit-input-placeholder {\n        color: #000;\n    }\n\n    &:-moz-placeholder {\n        color: #000;\n    }\n\n    &::-moz-placeholder {\n        color: #000;\n    }\n\n    &:-ms-input-placeholder {\n        color: #000;\n    }\n\n    &::placeholder {\n        color: #000;\n    }\n}\n```\n\n**Правильно:**\n\n```scss\ninput {\n    transition: border-color 0.3s;\n\n    &::placeholder {\n        color: #000;\n    }\n}\n```\n\n## Использование линтера\n\nВ сборку интегрирован линтер [stylelint](https://stylelint.io/).\nФайл настроек — `.stylelintrc`.\nДанный линтер позволяет поддерживать SCSS-код в соответствии с заданным регламентом.\n\nПроверка осуществляется с помощью задачи `lint:scss`:\n\n```bash\ngulp lint:scss\n```\n\nПример использования:\n\n```scss\n.block {\n  &__element {\n    display: inline-block\n  }\n  border-radius: 0px;\n  height: 30px;\n  width:30px;\n}\n```\n\nРезультаты проверки:\n\n```text\n2:3     ⚠  Expected indentation of 1 tab (indentation) [stylelint]\n3:5     ⚠  Expected indentation of 2 tabs (indentation) [stylelint]\n3:25    ⚠  Expected a trailing semicolon (declaration-block-trailing-semicolon) [stylelint]\n4:3     ⚠  Expected indentation of 1 tab (indentation) [stylelint]\n5:3     ⚠  Expected indentation of 1 tab (indentation) [stylelint]\n5:3     ⚠  Expected declaration to come before rule (order/order) [stylelint]\n5:3     ⚠  Expected empty line before declaration (declaration-empty-line-before) [stylelint]\n5:19    ⚠  Unexpected unit (length-zero-no-unit) [stylelint]\n6:3     ⚠  Expected indentation of 1 tab (indentation) [stylelint]\n7:3     ⚠  Expected indentation of 1 tab (indentation) [stylelint]\n7:3     ⚠  Expected \"width\" to come before \"height\" (order/properties-order) [stylelint]\n7:9     ⚠  Expected single space after \":\" with a single-line declaration (declaration-colon-space-after) [stylelint]\n```\n\nИсправленный код:\n\n```scss\n.block {\n    border-radius: 0;\n    width:30px;\n    height: 30px;\n\n    &__element {\n        display: inline-block\n    }\n}\n```\n"
  },
  {
    "path": "10_scripts.md",
    "content": "# Работа со скриптами\n\n> При работе со скриптами **важно** придерживаться установленных [правил по оформлению кода](18_codestyle-javascript.md).\n\nСкрипты размещаются в папке `src/js`:\n\n```text\nninelines-template\n└── src\n    └── js\n        ├── vendor\n        │   └── .keep\n        ├── main.js\n        └── vendor.js\n```\n\nЗа сборку и преобразование JS отвечает задача `js`:\n\n```bash\ngulp js\n```\n\nПосле выполнения команды в папке `build/js` появятся файлы `main.js` и `vendor.js`:\n\n```text\nninelines-template\n└── build\n    └── js\n        ├── main.js\n        └── vendor.js\n```\n\nТакже дополнительно подключены библиотеки:\n\n* [jQuery](https://jquery.com/)\n* [ninelines-ua-parser](https://github.com/ninelines-team/ninelines-ua-parser)\n\n[ninelines-ua-parser](https://github.com/ninelines-team/ninelines-ua-parser) основана на\n[ua-parser-js](https://github.com/faisalman/ua-parser-js) и отвечает за определение устройства, браузера и операционной\nсистемы пользователя, а также автоматически проставляет классы `<html>` элементу:\n\n* `.is-os-mac-os`\n* `.is-os-windows`\n* `.is-os-linux`\n* `.is-os-android`\n* `.is-os-ios`\n* `.is-device-mobile`\n* `.is-device-tablet`\n* `.is-device-desktop`\n* `.is-engine-webkit`\n* `.is-engine-gecko`\n* `.is-browser-chrome`\n* `.is-browser-firefox`\n* `.is-browser-ie`\n* `.is-browser-safari`\n\nДанные классы можно использовать для стилизации элементов:\n\n```scss\n.for-desktop {\n    .is-device-mobile & {\n        display: none;\n    }\n}\n```\n\n## Правила написания кода\n\n### Короткие именна переменных\n\nНе следует сокращать имена переменных.\n\n**Неправильно:**\n\n```js\n$('.elements').each((i, e) => {\n    // ...\n});\n```\n\n**Правильно:**\n\n```js\n$('.elements').each((index, element) => {\n    // ...\n});\n```\n\nИсключение могут составить имена счетчиков в цикле (`i`, `j`, `k`):\n\n```js\nfor (let i = 0; i < 10; i++) {\n    // ...\n}\n```\n\n### Именование jQuery-переменных\n\nНазвание переменных, являющихся jQuery-объектами, следует начинать с `$`.\n\n**Неправильно:**\n\n```js\nlet element = $('.element');\n```\n\n**Правильно:**\n\n```js\nlet $element = $('.element');\n```\n\n### jQuery-селекторы\n\nСледует избегать дублирования jQuery-селекторов.\nЕсли обращение к элементу происходит многократно, то jQuery-объект можно сохранить в отдельную переменную, либо переписать код так, чтобы избежать дублирования.\n\n**Неправильно:**\n\n```js\n$('.element').on('click', () => {\n    // ...\n});\n\n$('.element').on('mouseenter', () => {\n    // ...\n});\n```\n\n**Правильно:**\n\n```js\nlet $element = $('.element');\n\n$element.on('click', () => {\n    // ...\n});\n\n$element.on('mouseenter', () => {\n    // ...\n});\n```\n\nИли так:\n\n```js\n$('.element')\n    .on('click', () => {\n        // ...\n    })\n    .on('mouseenter', () => {\n        // ...\n    });\n```\n\n### Обработка событий с помощью jQuery\n\nДля создания обработчика событий следует использовать функцию [`.on()`](http://api.jquery.com/on/).\n\n**Неправильно:**\n\n```js\n$('button').click(() => {\n    // ...\n});\n\n$('form').submit(() => {\n    // ...\n});\n```\n\n**Правильно:**\n\n```js\n$('button').on('click', () => {\n    // ...\n});\n\n$('form').on('submit', () => {\n    // ...\n});\n```\n\n## Использование линтера\n\nВ сборку интегрирован линтер [ESLint](http://eslint.org/).\nФайл настроек — `.eslintrc`.\nДанный линтер позволяет поддерживать JavaScript-код в соответствии с заданным регламентом.\n\nПроверка осуществляется с помощью задачи `lint:js`:\n\n```bash\ngulp lint:js\n```\n\nПример использования:\n\n```js\nvar $form = $('.form')\n$form.on(\"submit\", function () {\n  $.post('ajax.php', function (data) {\n    $(\".result\").html(data);\n  })\n})\n```\n\nРезультаты проверки:\n\n```text\n  1:1   error    Expected blank line after variable declarations    newline-after-var\n  1:1   error    Unexpected var, use let or const instead           no-var\n  1:23  error    Missing semicolon                                  semi\n  2:10  error    Strings must use singlequote                       quotes\n  2:20  error    Unexpected function expression                     prefer-arrow-callback\n  2:20  warning  Unexpected unnamed function                        func-names\n  3:1   error    Expected indentation of 1 tab but found 2 spaces   indent\n  3:22  warning  Unexpected unnamed function                        func-names\n  3:22  error    Unexpected function expression                     prefer-arrow-callback\n  4:1   error    Expected indentation of 2 tabs but found 4 spaces  indent\n  4:7   error    Strings must use singlequote                       quotes\n  5:1   error    Expected indentation of 1 tab but found 2 spaces   indent\n  5:5   error    Missing semicolon                                  semi\n  6:3   error    Missing semicolon                                  semi\n\n✖ 14 problems (12 errors, 2 warnings)\n  12 errors, 0 warnings potentially fixable with the `--fix` option.\n```\n\nESlint сообщает о 14 найденных ошибках, причем большая часть из них может быть исправлена автоматически.\nЗа это отвечает ключ `--fix`, который можно указать при запуске задачи `lint:js`:\n\n```bash\ngulp lint:js --fix\n```\n\nИсправленный код:\n\n```js\nlet $form = $('.form');\n\n$form.on('submit', () => {\n    $.post('ajax.php', (data) => {\n        $('.result').html(data);\n    });\n});\n```\n"
  },
  {
    "path": "11_resources.md",
    "content": "# Работа с дополнительными ресурсами\n\nДополнительными ресурсами считается все то, что не попадает ни под одну из предыдущих категорий файлов.\nЭто могут быть различные favicon, шрифты, аудио, видео, документы и прочее.\n\nПодобные файлы следует хранить в папке `src/resources`.\n\n```text\nninelines-template\n└── src\n    └── resources\n        └── ...\n```\n\nЗадача `copy` копирует содержимое папки `src/resources` в `build`:\n\n```bash\ngulp copy\n```\n\n## Работа со шрифтами\n\nПодключить шрифт можно двумя способами:\n\n* CDN ([Google Fonts](https://fonts.google.com/))\n* Конвертировать и подключить с помощью [`@font-face`](https://developer.mozilla.org/ru/docs/Web/CSS/@font-face).\n\n### Подключение шрифта с помощью Google Fonts\n\nЕсли шрифт и его требуемая языковая версия имеются в Google Fonts, то данный вариант подключения является приоритетным.\n\nПорядок подключения шрифта на примере Roboto:\n\n1. Находим шрифт — [Roboto](https://fonts.google.com/specimen/Roboto)\n2. Нажимаем `Select this font`.\n3. Открываем появившееся снизу экрана окно.\n4. Во вкладке `Customize` выбираем нужное начертание и языковую версию.\n5. Во вкладке `Embed` переключаемся в `@import` и копируем содержимое тега `<style>`.\n6. В файл `src/scss/_fonts.scss` вставляем скопированный `@import`.\n7. В файл `src/scss/_variables.scss` добавляем переменную `$font-family-roboto`.\n8. Используем переменную в стилях.\n\n### Конвертирование шрифта и подключение с помощью `@font-face`\n\nЕсли шрифт отсутствует в Google Fonts, или нет подходящей языковой версии (отсутствует кириллица), то необходимо получить файл шрифта.\n\nТребуемые форматы — `.woff` (обязательно) и `.woff2` (опционально).\n\nФайлы `.ttf`, `.otf` или `.eot` можно сконвертировать в `.woff` и `.woff2` с помощью онлайн сервисов:\n\n* [onlinefontconverter.com](https://onlinefontconverter.com/)\n* [everythingfonts.com](https://everythingfonts.com/)\n\nПолученные файлы следует хранить в папке `src/resources/fonts`:\n\n```text\nninelines-template\n└── src\n    └── resources\n        └── fonts\n            └── ...\n```\n\nПодключение шрифтов происходит в файле `src/scss/_fonts.scss`:\n\n```scss\n@font-face {\n    src: url(\"../fonts/my-font-regular.woff2\") format(\"woff2\"), url(\"../fonts/my-font-regular.woff\") format(\"woff\");\n    font-family: \"My Font\";\n    font-weight: 400;\n    font-style: normal;\n}\n```\n\nПри наличии множества начертаний шрифты следует подключать в следующем порядке:\n\n1. 100 normal (thin)\n2. 100 italic (thin italic)\n3. 200 normal (extra light)\n4. 200 italic (extra light italic)\n5. 300 normal (light)\n6. 300 italic (light italic)\n7. 400 normal (regular)\n8. 400 italic (regular italic)\n9. 500 normal (medium)\n10. 500 italic (medium italic)\n11. 600 normal (semi bold)\n12. 600 italic (semi bold italic)\n13. 700 normal (bold)\n14. 700 italic (bold italic)\n15. 800 normal (extra bold)\n16. 800 italic (extra bold italic)\n17. 900 normal (heavy)\n18. 900 italic (heavy italic)\n\nПример:\n\n```scss\n@font-face {\n    src: url(\"../fonts/my-font-light.woff\") format(\"woff\");\n    font-family: \"My Font\";\n    font-weight: 300;\n    font-style: normal;\n}\n\n@font-face {\n    src: url(\"../fonts/my-font-light-italic.woff\") format(\"woff\");\n    font-family: \"My Font\";\n    font-weight: 300;\n    font-style: italic;\n}\n\n@font-face {\n    src: url(\"../fonts/my-font-regular.woff\") format(\"woff\");\n    font-family: \"My Font\";\n    font-weight: 400;\n    font-style: normal;\n}\n\n@font-face {\n    src: url(\"../fonts/my-font-regular-italic.woff\") format(\"woff\");\n    font-family: \"My Font\";\n    font-weight: 400;\n    font-style: italic;\n}\n\n@font-face {\n    src: url(\"../fonts/my-font-bold.woff\") format(\"woff\");\n    font-family: \"My Font\";\n    font-weight: 700;\n    font-style: normal;\n}\n\n@font-face {\n    src: url(\"../fonts/my-font-bold-italic.woff\") format(\"woff\");\n    font-family: \"My Font\";\n    font-weight: 700;\n    font-style: italic;\n}\n```\n\nПосле подключения шрифта в файл `src/scss/_variables.scss` следует добавить переменную в формате `$font-family-[name]`.\nПример:\n\n```scss\n$font-family-my-font: \"My Font\", sans-serif;\n```\n\nЗатем переменную можно использовать в любом SCSS-файле:\n\n```scss\nbody {\n    font-family: $font-family-my-font;\n}\n```\n"
  },
  {
    "path": "13_pixel-perfect.md",
    "content": "# Pixel-perfect или верстка в соответствии с макетом\n\nPixel-perfect — техника верстки, при которой результат максимально совпадает с исходным макетом.\n\nПод этим понимается соответствие:\n\n* положения элементов;\n* размеров;\n* отступов;\n* цветов;\n* шрифтов;\n* размеров текста;\n* межстрочного и межбуквенного интервалов.\n\nЧто не входит в pixel-perfect:\n\n* Аболютное соответствие макету.\n\n  Несмотря на свое название, техника pixel-perfect не подразумевает стопроцентного совпадения с макетом.\n  Различия могут быть и будут.\n\n  Дизайнер при создании макета может сделать что-то «на глаз» или просто ошибиться: сбиться с сетки, выровнять слой немного не по центру, использовать не тот шрифт или размер текста, подобрать цвет не по палитре и так далее.\n\n  Не нужно повторять того же в верстке:\n\n  * Если положение элемента отклоняется от сетки или других подобных элементов, то его следует выровнять.\n  * Если элемент почти по центру, то вероятно он должен быть строго по центру.\n  * Если есть подозрение, что в каком-то месте макета сбился шрифт, то скорее всего он действительно сбился, и вместо него должен использоваться другой.\n  * Если несколько цветов, используемых в макете, незначительно отличаются HEX-кодом и неотличимы на глаз, то вероятно это должен быть один цвет.\n\n  Также стоит учесть различный рендеринг страницы в различных средах.\n\n  Операционные системы по-разному отображают текст.\n  Графические редакторы и браузеры по-разному отображают текст.\n  Браузеры по-разному интерпретируют дробные значения.\n\n  Все эти факторы в сумме не позволяют достичь абсолютного соответствия макету.\n\n  Погрешности от нескольких пикселей до нескольких десятков пикселей вполне допустимы.\n\n* Соответствие макету на всех возможных разрешениях.\n\n  Нарисовать макет для всех возможных разрешений крайне затруднительно.\n  И уж тем более затруднительно сверстать все это многообразие.\n\n  На деле верстальщик получает лишь несколько основных макетов:\n\n  * десктопная версия;\n  * планшетная версия (очень редко);\n  * мобильная версия.\n\n  На эти имеющиеся макеты и следует ориентироваться.\n  Применение pixel-perfect к промежуточным размерам сайта недопустимо.\n\n## Инструменты для работы с pixel-perfect\n\nОдним из лучших инструментов для pixel-perfect верстки является плагин [PerfectPixel](http://www.welldonecode.com/perfectpixel/).\n\nПреимущества плагина:\n\n* Поддержка основными браузерами: Chrome, Firefox, Opera (в планах поддержка Safari, IE и Edge).\n* Нет необходимости ставить дополнительный код на сайт.\n* Удобный интерфейс.\n* Возможность позиционировать и масштабировать слой, изменять прозрачность.\n* Функция блокировки слоя.\n* Режим инверсии (позволяет проще всего увидеть отличия от макета).\n\nНет необходимости устанавливать данный плагин во все браузеры одновременно. В основном при разработке используется Chrome, поэтому достаточно установить плагин только в него, а остальные браузеры проверить визуально на глаз.\n\nТакже необходимо учесть, что на разных операционных системах сайт может отображаться с небольшыми различиями. Например проверив верстку на соответствие макету в ОС Windows, используя Chrome вы получили идеальное соответствие с макетом. Далее, вы открываете сайт в Mac OS, используя Chrome и видите, что есть небольшие расхождения. Если данные рахождения как-то влияют на визуальное восприятие и изменяют структуру сайта, то их необходимо поправить, в других случаях - это считается нормой.\n"
  },
  {
    "path": "15_metatags.md",
    "content": "# Работа с метатегами\n\nВ данном разделе приведен список основных метатегов `<meta>` и `<link>` с примерами.\nМенее значащие или устаревшие метатеги намеренно не указаны.\n\n## Базовые метатеги\n\nБазовые метатеги задают основную информацию о сайте.\n\n### `charset`\n\nЗадает кодировку страницы.\n\n> Значение по умолчанию в сборке — `utf-8`.\n\nПример:\n\n```jade\nappend vars\n    - meta.charset = 'utf-8'\n```\n\n### `description`\n\nЗадает описание страницы.\n\n> Оптимальная длинна описания — не больше 160 символов.\n\nПример:\n\n```jade\nappend vars\n    - meta.description = 'Описание страницы'\n```\n\nИли так:\n\n```jade\nprepend vars\n    - description = 'Описание страницы'\n```\n\nПри этом также будут определены значения `og:description` и `twitter:description`.\n\n### `keywords`\n\nЗадает список ключевых слов.\n\nПример:\n\n```jade\nappend vars\n    - meta.keywords = ['список', 'ключевых', 'слов']\n```\n\nИли так:\n\n```jade\nappend vars\n    - meta.keywords = 'список, ключевых, слов'\n```\n\n### `title`\n\nЗадает заголовок страницы (`<title>`).\n\nПример:\n\n```jade\nprepend vars\n    - title = 'Заголовок страницы'\n```\n\nПри этом также будут определены значения `og:title` и `twitter:title`.\n\n### `viewport`\n\nПозволяет управлять отображением страницы на мобильных устройствах.\n\n> Значение по умолчанию в сборке — `width=device-width`.\n\nПример:\n\n```jade\nappend vars\n    - meta.viewport = 'width=device-width, initial-scale=1'\n```\n\n### `icon`\n\nЗадает favicon сайта.\n\nПример:\n\n```jade\nappend vars\n    - link.icon = 'favicon.ico'\n```\n\nТакже можно задать иконки разных размеров:\n\n```jade\nappend vars\n    - link.icon16x16 = 'favicon-16x16.png'\n    - link.icon32x32 = 'favicon-32x32.png'\n    - link.icon96x96 = 'favicon-96x96.png'\n    - link.icon128x128 = 'favicon-128x128.png'\n    - link.icon196x196 = 'favicon-196x196.png'\n```\n\n### `manifest`\n\nЗадает ссылку на `manifest.json`.\n\nПример:\n\n```jade\nappend vars\n    - link.manifest = '/manifest.json'\n```\n\nМанифест веб-приложения предоставляет информацию о приложении (такую как имя, авторство, иконку и описание) в формате JSON-файла.\nЦель манифеста — установить веб-приложение на рабочий стол устройства, предоставляя более быстрый доступ.\n\n### `format-detection`\n\nПозволяет отключить определение номера телефона, адреса, даты или почты.\n\n```jade\nappend vars\n    - meta.formatDetection.telephone = false\n```\n\nИли так:\n\n```jade\nappend vars\n    - meta.formatDetection = 'telephone=no'\n```\n\n### `theme-color`\n\nЗадает цвет вкладки мобильного браузера.\n\nПример:\n\n```jade\nappend vars\n    - meta.themeColor = '#4285f4'\n```\n\n## Метатеги Apple\n\nМетатеги Apple задают дополнительную информацию для устройств на iOS (iPhone, iPad).\n\n### `apple-mobile-web-app-capable`\n\nВключает режим полноэкранного приложения iOS, в котором скрывается адресная строка и панель навигации.\n\nПример:\n\n```jade\nappend vars\n    - meta.appleMobileWebAppCapable = 'on'\n```\n\n### `apple-mobile-web-app-status-bar-style`\n\nЗадает стиль строки состояния в полноэкранном режиме iOS.\n\nПример:\n\n```jade\nappend vars\n    - meta.appleMobileWebAppCapable = 'on'\n    - meta.appleMobileWebAppStatusBarStyle = 'black'\n```\n\n### `apple-mobile-web-app-title`\n\nЗадает заголовок для иконки запуска в iOS.\n\n> Если не указано, то будет использоваться значение тега `<title>`.\n\nПример:\n\n```jade\nappend vars\n    - meta.appleMobileWebAppTitle = 'Название сайта'\n```\n\n### `apple-touch-icon`\n\nЗадает иконку сайта в iOS.\n\nПример:\n\n```jade\nappend vars\n    - link.appleTouchIcon = 'images/touch-icon.png'\n```\n\nТакже можно задать иконки разных размеров:\n\n```jade\nappend vars\n    - link.appleTouchIcon40x40 = 'images/touch-icon-40x40.png'\n    - link.appleTouchIcon57x57 = 'images/touch-icon-57x57.png'\n    - link.appleTouchIcon58x58 = 'images/touch-icon-58x58.png'\n    - link.appleTouchIcon60x60 = 'images/touch-icon-60x60.png'\n    - link.appleTouchIcon72x72 = 'images/touch-icon-72x72.png'\n    - link.appleTouchIcon76x76 = 'images/touch-icon-76x76.png'\n    - link.appleTouchIcon80x80 = 'images/touch-icon-80x80.png'\n    - link.appleTouchIcon87x87 = 'images/touch-icon-87x87.png'\n    - link.appleTouchIcon114x114 = 'images/touch-icon-114x114.png'\n    - link.appleTouchIcon120x120 = 'images/touch-icon-120x120.png'\n    - link.appleTouchIcon144x144 = 'images/touch-icon-144x144.png'\n    - link.appleTouchIcon152x152 = 'images/touch-icon-152x152.png'\n    - link.appleTouchIcon167x167 = 'images/touch-icon-167x167.png'\n    - link.appleTouchIcon180x180 = 'images/touch-icon-180x180.png'\n    - link.appleTouchIcon1024x1024 = 'images/touch-icon-1024x1024.png'\n```\n\n### `apple-touch-icon-precomposed`\n\nЗадает precomposed-иконку в iOS.\nИспользуется в том случае, если к иконке не нужно применять системные стили.\n\nПример:\n\n```jade\nappend vars\n    - link.appleTouchIconPrecomposed = 'images/touch-icon-precomposed.png'\n```\n\nТакже можно задать иконки разных размеров:\n\n```jade\nappend vars\n    - link.appleTouchIconPrecomposed40x40 = 'images/touch-icon-precomposed-40x40.png'\n    - link.appleTouchIconPrecomposed57x57 = 'images/touch-icon-precomposed-57x57.png'\n    - link.appleTouchIconPrecomposed58x58 = 'images/touch-icon-precomposed-58x58.png'\n    - link.appleTouchIconPrecomposed60x60 = 'images/touch-icon-precomposed-60x60.png'\n    - link.appleTouchIconPrecomposed72x72 = 'images/touch-icon-precomposed-72x72.png'\n    - link.appleTouchIconPrecomposed76x76 = 'images/touch-icon-precomposed-76x76.png'\n    - link.appleTouchIconPrecomposed80x80 = 'images/touch-icon-precomposed-80x80.png'\n    - link.appleTouchIconPrecomposed87x87 = 'images/touch-icon-precomposed-87x87.png'\n    - link.appleTouchIconPrecomposed114x114 = 'images/touch-icon-precomposed-114x114.png'\n    - link.appleTouchIconPrecomposed120x120 = 'images/touch-icon-precomposed-120x120.png'\n    - link.appleTouchIconPrecomposed144x144 = 'images/touch-icon-precomposed-144x144.png'\n    - link.appleTouchIconPrecomposed152x152 = 'images/touch-icon-precomposed-152x152.png'\n    - link.appleTouchIconPrecomposed167x167 = 'images/touch-icon-precomposed-167x167.png'\n    - link.appleTouchIconPrecomposed180x180 = 'images/touch-icon-precomposed-180x180.png'\n    - link.appleTouchIconPrecomposed1024x1024 = 'images/touch-icon-precomposed-1024x1024.png'\n```\n\n### `mask-icon`\n\nЗадает маску для иконки закрепленного сайта в iOS.\n\nПример:\n\n```jade\nappend vars\n    - link.maskIcon.href = 'mask-icon.svg'\n    - link.maskIcon.color = 'red'\n```\n\n## Метатеги Microsoft\n\nМетатеги Microsoft задают дополнительную информацию для IE и Edge на Windows.\n\n### `application-name`\n\nЗадает заголовок закрепленного сайта в IE11 и Edge.\n\n> Если не указано, то будет использоваться значение тега `<title>`.\n\nПример:\n\n```jade\nappend vars\n    - meta.applicationName = 'Название сайта'\n```\n\n### `msapplication-TileColor`\n\nЗадает цвет плитки в Windows 10.\n\nПример:\n\n```jade\nappend vars\n    - meta.msapplicationTileColor = '#FF3300'\n```\n\n### `msapplication-TileImage`\n\nЗадает фоновое изображение плитки в Windows 10.\n\nПример:\n\n```jade\nappend vars\n    - meta.msapplicationTileImage = 'images/tile-image.png'\n```\n\n### `msapplication-square70x70logo`\n\nЗадает иконку малой плитки в Windows 10.\n\nПример:\n\n```jade\nappend vars\n    - meta.msapplicationSquare70x70logo = 'images/ms-logo-70x70.png'\n```\n\n### `msapplication-square150x150logo`\n\nЗадает иконку средней плитки в Windows 10.\n\nПример:\n\n```jade\nappend vars\n    - meta.msapplicationSquare150x150logo = 'images/ms-logo-150x150.png'\n```\n\n### `msapplication-wide310x150logo`\n\nЗадает иконку широкой плитки в Windows 10.\n\nПример:\n\n```jade\nappend vars\n    - meta.msapplicationSquare310x150logo = 'images/ms-logo-310x150.png'\n```\n\n### `msapplication-square310x310logo`\n\nЗадает иконку большой плитки в Windows 10.\n\nПример:\n\n```jade\nappend vars\n    - meta.msapplicationSquare310x310logo = 'images/ms-logo-310x310.png'\n```\n\n### `msapplication-notification`\n\nПозволяет определить до пяти XML-файлов, которые будут опрашиваться с определенным интервалом.\nЭти файлы могут содержать уведомления, отображаемые на плитке.\n\nПример:\n\n```jade\nappend vars\n    - meta.msapplicationNotification = 'frequency=30; polling-uri=notifications/feed-1.xml; polling-uri2=notifications/feed-2.xml'\n```\n\n### `X-UA-Compatible`\n\nПозволяет управлять режимом совместимости браузеров IE8+.\n\n> Значение по умолчанию в сборке — `IE=edge`.\n\nПример:\n\n```jade\nappend vars\n    - meta.XUACompatible = 'IE=edge'\n```\n\n## Метатеги [Open Graph](http://ogp.me/)\n\nМетатеги Open Graph задают дополнительную информацию, используемую социальными сетями (VK, Twitter, Google Plus, Одноклассники и так далее) для оформления публикаций.\n\n### `og:url`\n\nЗадает адрес страницы в Open Graph.\n\n> Требуется указывать полный URL.\n\nПример:\n\n```jade\nappend vars\n    - meta.ogUrl = 'http://example.com/'\n```\n\n### `og:locale`\n\nЗадает локаль страницы в Open Graph.\n\n> Значение по умолчанию в сборке — `ru_RU`.\n\nПример:\n\n```jade\nappend vars\n    - meta.ogLocale = 'ru_RU'\n```\n\n### `og:title`\n\nЗадает заголовок страницы в Open Graph.\n\nПример:\n\n```jade\nappend vars\n    - meta.ogTitle = 'Заголовок страницы'\n```\n\nИли так:\n\n```jade\nappend vars\n    - title = 'Заголовок страницы'\n```\n\n### `og:description`\n\nЗадает описание страницы в Open Graph.\n\nПример:\n\n```jade\nprepend vars\n    - meta.ogDescription = 'Описание страницы'\n```\n\nИли так:\n\n```jade\nprepend vars\n    - description = 'Описание страницы'\n```\n\n### `og:image`\n\nЗадает изображение страницы в Open Graph.\n\n> Требуется указывать полный URL.\n\nПример:\n\n```jade\nappend vars\n    - meta.ogImage = 'http://example.com/images/og-image.png'\n```\n\nИли так:\n\n```jade\nprepend vars\n    - image = 'http://example.com/images/og-image.png'\n```\n\n### `og:image:type`\n\nЗадает MIME-тип изображения в Open Graph.\n\nПример:\n\n```jade\nappend vars\n    - meta.ogImageType = 'image/jpeg'\n```\n\n### `og:image:width`\n\nЗадает ширину изображения в Open Graph.\n\nПример:\n\n```jade\nappend vars\n    - meta.ogImageWidth = 1200\n```\n\n### `og:image:height`\n\nЗадает высоту изображения в Open Graph.\n\nПример:\n\n```jade\nappend vars\n    - meta.ogImageHeight = 600\n```\n\n### `og:image:alt`\n\nЗадает описание изображения в Open Graph.\n\nПример:\n\n```jade\nappend vars\n    - meta.ogImageAlt = 'Описание изображения'\n```\n\n## Метатеги Twitter\n\nМетатеги Twitter задают дополнительную информацию, используемую для оформления Twitter-публикаций.\n\n### `twitter:card`\n\nЗадает тип карточки Twitter.\n\n> Значение по умолчанию в сборке — `summary_large_image`.\n\nПример:\n\n```jade\nappend vars\n    - meta.twitterCard = 'summary_large_image'\n```\n\n### `twitter:site`\n\nЗадает `@username` сайта в Twitter.\n\nПример:\n\n```jade\nappend vars\n    - meta.twitterSite = '@username'\n```\n\n### `twitter:creator`\n\nЗадает `@username` автора контента в Twitter.\n\nПример:\n\n```jade\nappend vars\n    - meta.twitterCreator = '@username'\n```\n\n### `twitter:title`\n\nЗадает заголовок страницы в Twitter.\n\nПример:\n\n```jade\nappend vars\n    - meta.twitterSite = 'Заголовок страницы'\n```\n\nИли так:\n\n```jade\nprepend vars\n    - title = 'Заголовок страницы'\n```\n\n### `twitter:description`\n\nЗадает описание страницы в Twitter.\n\nПример:\n\n```jade\nappend vars\n    - meta.twitterDescription = 'Описание страницы'\n```\n\nИли так:\n\n```jade\nprepend vars\n    - description = 'Описание страницы'\n```\n\n### `twitter:image`\n\nЗадает изображение страницы в Twitter.\n\n> Требуется указывать полный URL.\n\nПример:\n\n```jade\nappend vars\n    - meta.twitterImage = 'http://example.com/images/twitter-image.png'\n```\n\nИли так:\n\n```jade\nprepend vars\n    - image = 'http://example.com/images/twitter-image.png'\n```\n"
  },
  {
    "path": "16_codestyle-pug.md",
    "content": "# Синтаксис и форматирование\n\n* Для отступов используется символ табуляции `\\t`.\n\n* Для переноса строк используется символ `\\n` (LF).\n\n* Не должно быть пробелов в конце строк.\n\n* Максимальная длина строки — 120 символов.\n\n* Между большими блоками кода следует оставлять одну пустую строку.\n\n**Неправильно** (нет пустых строк):\n\n```jade\n.products\n    .product\n        img.product__image(src=\"images/products/1.png\" alt=\"\")\n        .product__title\n            | Название товара\n        .product__description\n            | Описание товара\n        .product__price\n            | 12345\n    .product\n        img.product__image(src=\"images/products/2.png\" alt=\"\")\n        .product__title\n            | Название товара\n        .product__description\n            | Описание товара\n        .product__price\n            | 45678\n    .product\n        img.product__image(src=\"images/products/3.png\" alt=\"\")\n        .product__title\n            | Название товара\n        .product__description\n            | Описание товара\n        .product__price\n            | 90123\n.navigation\n    a.navigation__link(href=\"/catalog?page=2\")\n        | Следующая страница\n```\n\n**Неправильно** (слишком много пустых строк):\n\n```jade\n.products\n\n    .product\n        img.product__image(src=\"images/products/1.png\" alt=\"\")\n\n        .product__title\n            | Название товара\n\n        .product__description\n            | Описание товара\n\n        .product__price\n            | 12345\n\n\n    .product\n        img.product__image(src=\"images/products/2.png\" alt=\"\")\n\n        .product__title\n            | Название товара\n\n        .product__description\n            | Описание товара\n\n        .product__price\n            | 45678\n\n\n    .product\n        img.product__image(src=\"images/products/3.png\" alt=\"\")\n\n        .product__title\n            | Название товара\n\n        .product__description\n            | Описание товара\n\n        .product__price\n            | 90123\n\n\n.navigation\n    a.navigation__link(href=\"/catalog?page=2\")\n        | Следующая страница\n```\n\n**Правильно:**\n\n```jade\n.products\n    .product\n        img.product__image(src=\"images/products/1.png\" alt=\"\")\n        .product__title\n            | Название товара\n        .product__description\n            | Описание товара\n        .product__price\n            | 12345\n\n    .product\n        img.product__image(src=\"images/products/2.png\" alt=\"\")\n        .product__title\n            | Название товара\n        .product__description\n            | Описание товара\n        .product__price\n            | 45678\n\n    .product\n        img.product__image(src=\"images/products/3.png\" alt=\"\")\n        .product__title\n            | Название товара\n        .product__description\n            | Описание товара\n        .product__price\n            | 90123\n\n.navigation\n    a.navigation__link(href=\"/catalog?page=2\")\n        | Следующая страница\n```\n\n# Однострочная вложенность\n\nКак правило теги вкладываются друг на разных строках:\n\n```jade\nul\n    li\n        a(href=\"/page/1\")\n            | 1\n    li\n        a(href=\"/page/2\")\n            | 2\n    li\n        a(href=\"/page/3\")\n            | 3\n```\n\nНо в случае, **если** строк в файле довольно много и их сокращение **увеличивает читаемость**, то допустимо\nиспользовать однострочную запись:\n\n```jade\nul\n    li: a(href=\"/page/1\") 1\n    li: a(href=\"/page/2\") 2\n    li: a(href=\"/page/3\") 3\n```\n\nВ иных случаях не следует использовать такой способ записи.\n\n# Самозакрывающиеся теги\n\nСамозакрывающиеся теги (`img`, `meta`, `link`) определяются автоматически, поэтому не следует указывать это явно.\n\n# Классы\n\nКлассы записываются сразу после тега:\n\n```jade\nul.navigation\n    li.navigation__item\n```\n\nЕсли у элемента не указан тег, то он принимается за `div` (по этой причине не следует явно указывать тег `div`):\n\n```jade\n.container\n    .block\n```\n\nБудет преобразовано в:\n\n```html\n<div class=\"container\">\n    <div class=\"block\"></div>\n</div>\n```\n\nАтрибут `class` используется только в том случае, если он задается с помощью переменной или через условие:\n\n```jade\n- page = 'index'\n\n.menu\n    a.menu__item(class={'menu__item--active': page === 'index'} href='/')\n        | Главная страница\n    a.menu__item(class={'menu__item--active': page === 'catalog'} href='/catalog')\n        | Каталог\n    a.menu__item(class={'menu__item--active': page === 'contacts'} href='/contacts')\n        | Контакты\n```\n\n# Идентификаторы\n\nИдентификатор записывается после класса или тега.\n\n**Неправильно:**\n\n```jade\nul#top-menu.menu\n```\n\n**Правильно:**\n\n```jade\nul.menu#top-menu\n\n// или так\n.menu#top-menu\n\n// или так\n#top-menu\n```\n\nАтрибут `id` используется только в случае, если он задается с помощью переменной.\n\n# Атрибуты\n\nАтрибуты записываются в круглых скобках после указания тега, класса и/или идентификатора.\n\n```jade\nform(action=\"/login.php\" method=\"post\")\n    input(type=\"email\" name=\"email\" placeholder=\"E-mail\")\n    input(type=\"password\" name=\"password\" placeholder=\"Пароль\")\n    button(type=\"submit\")\n        | Войти\n```\n\nНе следует дублировать запись атрибутов.\n\n**Неправильно:**\n\n```jade\nimg(src=\"images/photo.jpg\")(alt=\"\")\n```\n\n**Правильно:**\n\n```jade\nimg(src=\"images/photo.jpg\" alt=\"\")\n```\n\n# Кавычки\n\nДля указания значения атрибутов используются двойные кавычки.\n\n**Неправильно:**\n\n```jade\ninput(type='text' name='name')\n```\n\n**Правильно:**\n\n```jade\ninput(type=\"text\" name=\"name\")\n```\n\n# Разделение атрибутов\n\nАтрибуты разделяются одним пробелом.\n\n**Неправильно:**\n\n```jade\nimg(src=\"images/logo.png\", srcset=\"images/logo@2x.png 2x\", alt=\"\")\n```\n\n**Правильно:**\n\n```jade\nimg(src=\"images/logo.png\" srcset=\"images/logo@2x.png 2x\" alt=\"\")\n```\n\n# Нестандартные атрибуты\n\nАтрибуты, в названии которых используются нестандартные символы (`(`, `)`, `[`, `]`, `:`, `@`) записываются в одинарных\nкавычках.\n\n**Неправильно:**\n\n```jade\n// Не скомпилируется\nbutton(type=\"button\" (click)=\"send()\")\n    | Отправить\n```\n\n```jade\nbutton(type=\"button\" v-on:click=\"send()\")\n    | Отправить\n```\n\n```jade\nbutton(type=\"button\" @click=\"send()\")\n    | Отправить\n```\n\n**Правильно:**\n\n```jade\n// Не скомпилируется\nbutton(type=\"button\" '(click)'=\"send()\")\n    | Отправить\n```\n\n```jade\nbutton(type=\"button\" 'v-on:click'=\"send()\")\n    | Отправить\n```\n\n```jade\nbutton(type=\"button\" '@click'=\"send()\")\n    | Отправить\n```\n\n# Многострочная запись атрибутов\n\nЕсли атрибутов слишком много и/или длина строки превышает установленный предел, то следует записать каждый атрибут\nв отдельной строке:\n\n```jade\ninput(\n    type=\"text\"\n    name=\"name\"\n    placeholder=\"Имя\"\n    maxlength=\"20\"\n    required\n    autocomplete=\"on\"\n)\n```\n\n# Использование переменных в атбирутах\n\nВ значение атрибута можно передать переменную или любое JS-выражение:\n\n```jade\nimg(src=`images/image-${image.number}.jpg` alt=image.description)\n```\n\nПо умолчанию значения атрибутов экранируются.\n\n```jade\n.pagination(v-if=\"items.length > 5\")\n```\n\nБудет преобразовано в:\n\n```html\n<div class=\"pagination\" v-if=\"items.length &gt; 5\"></div>\n```\n\nОтключить экранирование значения атрибута можно заменив `=` на `!=`.\n\n```jade\n.pagination(v-if!=\"items.length > 5\")\n```\n\nБудет преобразовано в:\n\n```html\n<div class=\"pagination\" v-if=\"items.length > 5\"></div>\n```\n\n# JSON\n\nJSON в атрибуте записывается в виде JS-объекта, а не строки.\n\n**Неправильно:**\n\n```jade\n.slider(data-options=\"{autoplay: true, arrows: false, fade: true}\")\n```\n\n**Правильно:**\n\n```jade\n.slider(data-options={\n    autoplay: true,\n    arrows: false,\n    fade: true\n})\n\n// или так\n.slider(\n    data-options={\n        autoplay: true,\n        arrows: false,\n        fade: true\n    }\n)\n```\n\n# Атрибут style\n\nАтрибут style также при необходимости можно записать в виде JS-объекта.\n\n```jade\n.element(\n    style={\n        '-webkit-transform': 'rotate(45deg)',\n        'transform': 'rotate(45deg)'\n    }\n)\n```\n\n# Использование `&attributes`\n\n`&attributes` либо в миксинах, либо в том случае, если необходимо задать множество атрибутов элемента с помощью\nпеременной.\n\n**Неправильно:**\n\n```jade\na(href=\"/\")&attributes({target: '_blank'})\n    | На главную\n```\n\n**Правильно:**\n\n```jade\na(href=\"/\" target=\"_blank\")\n    | На главную\n```\n\n```jade\n- attrs = {class: 'no-js', lang: 'ru'}\n\ndoctype html\nhtml&attributes(attrs)\n    head\n    body\n```\n\n```jade\nmixin button()\n    button.button&attributes(attributes)\n        block\n\n+button().button--large(type=\"submit\")\n    | Войти\n```\n\n# Вывод текста\n\nТекст следует выводить на следующей строке с помощью символа `|`.\n\n**Неправильно:**\n\n```jade\n.product\n    img.product__image(src=\"images/products/1.png\" alt=\"\")\n    .product__title Название товара\n    .product__description Описание товара\n    .product__price 12345\n```\n\n**Правильно:**\n\n```jade\n.product\n    img.product__image(src=\"images/products/1.png\" alt=\"\")\n    .product__title\n        | Название товара\n    .product__description\n        | Описание товара\n    .product__price\n        | 12345\n```\n\nВ случае, **если** строк в файле довольно много и их сокращение **увеличивает читаемость**, то допустимо использовать\nоднострочную запись.\n\n# Использование JS в Pug\n\nPug позволяет использовать JS в шаблонах:\n\n```jade\n// Однострочная запись\n- title = 'Title'\n- description = 'Description'\n\n// Многострочная запись\n-\n    attrs = {\n        class: 'no-js',\n        lang: 'ru'\n    }\n```\n\nНе следует использовать эту возможность для создания условий и циклов, так как в Pug уже имеются специальные\nконструкции.\n\n# Вывод переменных\n\nСуществует два варианта вывода переменных — экранированный и неэкранированный:\n\n```jade\n// Экранированный\n.product\n    .product__title= title\n    .product__description= description\n    .product__price= price\n\n// Неэкранированный\n.product\n    .product__title!= title\n    .product__description!= description\n    .product__price!= price\n```\n\n# Вывод переменных в тексте\n\nПри необходимости вывести переменную в тексте следует воспользоваться конструкцией `#{}` (для экранированного вывода)\nили `!{}` (для неэкранированного вывода):\n\n```jade\n.product\n    .product__title\n        | Название товара: #{title}\n    .product__description\n        | Описание: #{description}\n    .product__price\n        | Цена: !{price}р.\n```\n\n# Вывод элементов в тексте\n\nЭлементы в тексте записываются с помощью конструкции `#[]`.\n\n**Неправильно:**\n\n```jade\n.product\n    .product__title\n        | <b>Название товара:</b><br>\n        | <a href=\"/product/1\">#{title}</a>\n    .product__description\n        | <b>Описание:</b><br>\n        | #{description}\n    .product__price\n        | <b>Цена:</b><br>\n        | !{price}р.\n```\n\n```jade\n.product\n    .product__title\n        b Название товара:\n        br\n        a(href=\"/product/1\") #{title}\n    .product__description\n        b Описание:\n        br\n        | #{description}\n    .product__price\n        b Цена:\n        br\n        | !{price}р.\n```\n\n**Правильно:**\n\n```jade\n.product\n    .product__title\n        | #[b Название товара:]#[br]\n        | #[a(href=\"/product/1\") #{title}]\n    .product__description\n        | #[b Описание:]#[br]\n        | #{description}\n    .product__price\n        | #[b Цена:]#[br]\n        | !{price}р.\n```\n\nПосле `#[br]` следует ставить перенос строки.\n\n# Вывод кода\n\nПри необходимости записать `<script>` или `<style>` непосредственно в Pug, то можно воспользоваться следующей\nконструкцией:\n\n```jade\nscript.\n    (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){\n    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),\n    m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)\n    })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');\n\n    ga('create', 'UA-123456789-00', 'auto');\n    ga('send', 'pageview');\n\nstyle.\n    img[href*=\"tns-counter\"] {\n        position: absolute;\n        left: -9999px;\n    }\n```\n\n# Управляющие конструкции\n\n* `if` — используется для условий.\n\n**Неправильно:**\n\n```jade\n- if (products.length) {\n    .products\n        // ...\n- }\n```\n\n**Правильно:**\n\n```jade\nif products.length\n    .products\n        // ...\n```\n\n* `unless` — предназначена для условий, но не используется. Вместо нее следует использовать `if`.\n\n* `case` — предназначена для условий. Практически не используется.\n\n* `each` — предназначена для создания циклов.\n\n**Неправильно:**\n\n```jade\n- for (var i = 0; i < products.length; i++) {\n    - var product = products[i];\n    .product\n        .product__title= product.title\n        .product__description= product.description\n        .product__price= product.price\n- }\n```\n\n**Правильно:**\n\n```jade\neach product in products\n    .product\n        .product__title= product.title\n        .product__description= product.description\n        .product__price= product.price\n```\n\n```jade\neach i in Array.from(Array(10).keys())\n    img(src=`images/frame-${i}.jpg` alt=\"\")\n```\n\n* `while` — не используется.\n\n# Наследование\n\nБазовый шаблон (`base.pug`) не предназначен для редактирования. Не стоит в нем задавать значения переменных, подключать\nкакие-либо скрипты или стили, и уж тем более писать код страницы. На то он и базовый. В нем описывается самый основной\nкод страницы. Допустимо лишь подключать миксины в этом файле.\n\nЕсли возникает необходимость изменить этот файл (подключить какие-то стили или скрипты, добавить шапку и подвал сайта),\nто лучше создать другой базовый шаблон (например `custom-base.pug`) унаследованный от `base.pug` (а то и вовсе\nнаписанный с нуля), и уже работать с этим файлом и наследовать страницы от него.\n\nПри переопределении блоков можно использовать сокращенную запись.\n\n**Неправильно:**\n\n```jade\nblock append content\n    // ...\n```\n\n**Правильно:**\n\n```jade\nappend content\n    // ...\n```\n\n# Подключение файлов\n\nЕсли страница состоит из нескольких независимых блоков, то каждый следует вынести в отдельный файл.\n\n**Неправильно:**\n\n```jade\n.header\n    // ...\n.banner\n    // ...\n.container\n    .sidebar\n        // ...\n    .content\n        .filter\n            // ...\n        .products\n            // ...\n        .pagination\n            // ...\n.footer\n    // ...\n```\n\n**Правильно:**\n\n```jade\ninclude pug/header\ninclude pug/banner\n.container\n    include pug/sidebar\n    .content\n        include pug/filter\n        include pug/products\n        include pug/pagination\n```\n\nЕсли на странице используются какие-либо счетчики (Google Analytics, Яндекс Метрика и тому подобноее), то этот также\nследует выносить в отдельный файл:\n\n```jade\nprepend scripts\n    include pug/counters\n```\n\nПри подключении Pug-файлов не нужно указывать расширение.\n\n**Неправильно:**\n\n```jade\ninclude pug/header.pug\n```\n\n**Правильно:**\n\n```jade\ninclude pug/header\n```\n\nПодключать можно не только Pug, но и любые другие текстовые файлы. Например svg:\n\n```jade\n.header\n    a.header__logo(href=\"/\")\n        include ../images/logo.svg\n```\n\n# Миксины\n\nЕсли на сайте имеется какой-либо шаблонный блок или компонент (кнопка, карточка, статья), который встречается более\nодного раза, то его следует вынести в отдельный миксин.\n\n**Неправильно:**\n\n```jade\n.products\n    .product\n        a.product__image(href=\"/product/1\")\n            img(src=\"/images/image-1.jpg\" alt=\"\")\n        a.product__title(href=\"/product/1\")\n            | Название товара\n        .product__description\n            | Описание товара\n        .product__price\n            | 12345\n        a.product__link(href=\"/product/1\")\n            | Подробнее\n\n    .product\n        a.product__image(href=\"/product/2\")\n            img(src=\"/images/image-2.jpg\" alt=\"\")\n        a.product__title(href=\"/product/2\")\n            | Название товара\n        .product__description\n            | Описание товара\n        .product__price\n            | 67890\n        a.product__link(href=\"/product/2\")\n            | Подробнее\n```\n\n**Правильно:**\n\nСоздаем миксин в файле src/pug/mixins/product.pug\n\n```jade\nmixin product(options)\n    .product&attributes(attributes)\n        a.product__image(href=options.href)\n            img(src=options.image alt=\"\")\n        a.product__title(href=options.href)= options.title\n        .product__description= options.description\n        .product__price= options.price\n        a.product__link(href=options.href)\n            | Подробнее\n```\n\nПодключаем миксин в `base.pug`:\n\n```jade\ninclude mixins/svg\ninclude mixins/product\n```\n\nПосле можно использовать созданный миксин на любой странице:\n\n```jade\n.products\n    +product({\n        href: '/product/1',\n        image: '/images/image-1.jpg',\n        title: 'Название товара',\n        description: 'Описание товара',\n        price: 12345\n    })\n\n    +product({\n        href: '/product/2',\n        image: '/images/image-2.jpg',\n        title: 'Название товара',\n        description: 'Описание товара',\n        price: 67890\n    })\n```\n\nВ миксин также можно передать содержимое (в основном используется при передаче большого количества контента, например\nв статьях):\n\n```jade\nmixin article(title)\n    article.article\n        h1.article__title= title\n        .article__content\n            block\n\n+article('Заголовок статьи')\n    | Содержимое статьи\n```\n\n# Комментарии\n\nДля комментариаев используется синтаксис `//` и `//-`:\n\n```jade\n// Однострочный комментарий, который попадет в html\n\n//- Однострочный комментарий, который не попадет в html\n\n//\n    Многострочный комментарий,\n    который попадет в html\n\n//-\n    Многострочный комментарий,\n    который не попадет в html\n```\n"
  },
  {
    "path": "17_codestyle-scss.md",
    "content": "# Синтаксис и форматирование\n\n* Для отступов используется символ табуляции `\\t`.\n\n* Для переноса строк используется символ `\\n` (LF).\n\n* Не должно быть пробелов в конце строк.\n\n* Максимальная длина строки — 120 символов.\n\n* `!important` используется только для того, чтобы перебить значение `style`-атрибута.\n\n* В коде не должно быть вендорных префиксов. (есть исключения)\n\n**Неправильно:**\n\n```scss\na {\n    -webkit-transition: color 0.3s;\n    -moz-transition: color 0.3s;\n    transition: color 0.3s;\n}\n```\n\n**Правильно:**\n\n```scss\na {\n    transition: color 0.3s;\n}\n```\n\nВендорные префиксы допустимы только в том случае, если вариант без префикса отсутствует.\n\n```scss\n@mixin retina {\n    @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {\n        @content;\n    }\n}\n```\n\n* Селекторы, свойства, `@`-правила, названия переменных, миксин, функций, `#`-цвета — все записывается в нижнем\nрегистре.\n\n**Неправильно:**\n\n```scss\n$Color-Cod-Gray: #1A1A1A;\n\n.Form {\n    INPUT {\n        color: $Color-Cod-Gray;\n    }\n}\n```\n\n**Правильно:**\n\n```scss\n$color-cod-gray: #1a1a1a;\n\n.form {\n    input {\n        color: $color-cod-gray;\n    }\n}\n```\n\n* CSS-правила отделяются друг от друга пустой строкой.\n\n**Неправильно:**\n\n```scss\n.header {\n    position: relative;\n    margin: 0 auto;\n    padding: 15px;\n    max-width: 1000px;\n    background: url(\"../images/header.jpg\") 50% 50% no-repeat;\n    @include retina {\n        background-image: url(\"../images/header@2x.jpg\");\n    }\n    @media (min-width: 1000px) {\n        padding-top: 30px;\n        padding-bottom: 30px;\n    }\n}\n```\n\n**Правильно:**\n\n```scss\n.header {\n    position: relative;\n    margin: 0 auto;\n    padding: 15px;\n    max-width: 1000px;\n    background: url(\"../images/header.jpg\") 50% 50% no-repeat;\n\n    @include retina {\n        background-image: url(\"../images/header@2x.jpg\");\n    }\n\n    @media (min-width: 1000px) {\n        padding-top: 30px;\n        padding-bottom: 30px;\n    }\n}\n```\n\n* Не должно быть более одной пустой строки подряд.\n\n**Неправильно:**\n\n```scss\n.button {\n    display: inline-block;\n\n\n    &::before {\n        content: \"\";\n    }\n}\n```\n\n**Правильно:**\n\n```scss\n.button {\n    display: inline-block;\n\n    &::before {\n        content: \"\";\n    }\n}\n```\n\n* Между свойствами не должно быть пустых строк.\n\n**Неправильно:**\n\n```scss\n.button {\n    display: inline-block;\n\n    border: solid 1px $color-black;\n    border-radius: 4px;\n\n    padding: 5px 10px;\n\n    font-family: $font-family-roboto;\n    font-weight: 300;\n    font-size: 16px;\n    line-height: 1.5;\n\n    text-align: center;\n    text-decoration: none;\n\n    cursor: pointer;\n}\n```\n\n**Правильно:**\n\n```scss\n.button {\n    display: inline-block;\n    border: solid 1px $color-black;\n    border-radius: 4px;\n    padding: 5px 10px;\n    font-family: $font-family-roboto;\n    font-weight: 300;\n    font-size: 16px;\n    line-height: 1.5;\n    text-align: center;\n    text-decoration: none;\n    cursor: pointer;\n}\n```\n\n* При перечислении селекторов каждый записывается на отдельной строке.\n\n**Неправильно:**\n\n```scss\n.prev, .next {\n    position: absolute;\n    top: 50%;\n}\n```\n\n**Правильно:**\n\n```scss\n.prev,\n.next {\n    position: absolute;\n    top: 50%;\n}\n```\n\n* Перед `{` ставится один пробел, а после — один перенос строки.\n\n**Неправильно:**\n\n```scss\n.header{\n    position: relative;\n}\n\n.banner  {\n    max-width: 1000px;\n}\n\n.footer\n{\n    margin-top: 15px;\n}\n\n.container {\n\n    margin: 0 auto;\n}\n```\n\n**Правильно:**\n\n```scss\n.header {\n    position: relative;\n}\n\n.banner {\n    max-width: 1000px;\n}\n\n.footer {\n    margin-top: 15px;\n}\n\n.container {\n    margin: 0 auto;\n}\n```\n\n* Перед `}` не должно быть пустых строк.\n\n**Неправильно:**\n\n```scss\na {\n    text-decoration: none; }\n\np {\n    margin: 15px 0;\n\n}\n```\n\n**Правильно:**\n\n```scss\na {\n    text-decoration: none;\n}\n\np {\n    margin: 15px 0;\n}\n```\n\n* Перед `:` и `,` не должно быть пробелов, а после ставится один пробел.\n\n**Неправильно:**\n\n```scss\n.container {\n    padding  :  0 15px;\n    max-width:  1000px;\n    background: none;\n}\n\n.button {\n    box-shadow: inset 0 0 2px 1px $color-black ,0 5px 4px 0 rgba($color-black, 0.25);\n    transition: color 0.3s , opacity 0.3s;\n}\n```\n\n**Правильно:**\n\n```scss\n.container {\n    padding: 0 15px;\n    max-width: 1000px;\n    background: none;\n}\n\n.button {\n    box-shadow: inset 0 0 2px 1px $color-black, 0 5px 4px 0 rgba($color-black, 0.25);\n    transition: color 0.3s, opacity 0.3s;\n}\n```\n\n* После открывающей и до закрывающей скобки не должно быть пробелов.\n\n**Неправильно:**\n\n```scss\na[ href ] {\n    color: darken( $color-blue, 10% );\n    cursor: pointer;\n}\n```\n\n**Правильно:**\n\n```scss\na[href] {\n    color: darken($color-blue, 10%);\n    cursor: pointer;\n}\n```\n\n* Перед и после операторов (`+`, `-`, `*`, `/`) и комбинаторов (`+`, `>`, `~`) ставится один пробел.\n\n**Неправильно:**\n\n```scss\n.sidebar {\n    width: calc((100%- 30px)/2);\n\n    >a {\n        display: block;\n    }\n}\n```\n\n**Правильно:**\n\n```scss\n.sidebar {\n    width: calc((100% - 30px) / 2);\n\n    > a {\n        display: block;\n    }\n}\n```\n\n* Строки записываются в двойных кавычках.\n\n**Неправильно:**\n\n```scss\nli {\n    list-style: none;\n\n    &::before {\n        content: '';\n        display: inline-block;\n        vertical-align: middle;\n        width: 10px;\n        height: 10px;\n        background-image: url(../images/point.png);\n    }\n}\n```\n\n**Правильно:**\n\n```scss\nli {\n    list-style: none;\n\n    &::before {\n        content: \"\";\n        display: inline-block;\n        vertical-align: middle;\n        width: 10px;\n        height: 10px;\n        background-image: url(\"../images/point.png\");\n    }\n}\n```\n\n* Пути записываются в двойных кавычках.\n\n**Неправильно:**\n\n```scss\n@import 'vendor/player';\n\n.button-play {\n    background: url(../images/play.png) 50% 50% no-repeat;\n}\n```\n\n**Правильно:**\n\n```scss\n@import \"vendor/player\";\n\n.button-play {\n    background: url(\"../images/play.png\") 50% 50% no-repeat;\n}\n```\n\n* Значения атрибутов записываются в двойных кавычках.\n\n**Неправильно:**\n\n```scss\na[target=_blank]::after {\n    content: \"\";\n    display: inline-block;\n    width: 15px;\n    height: 15px;\n    background-image: url(\"../images/new-page.png\");\n}\n```\n\n**Правильно:**\n\n```scss\na[target=\"_blank\"]::after {\n    content: \"\";\n    display: inline-block;\n    width: 15px;\n    height: 15px;\n    background-image: url(\"../images/new-page.png\");\n}\n```\n\n* Названия шрифтов записываются в двойных кавычках, за исключением ключевых слов.\n\n**Неправильно:**\n\n```scss\n$font-family-roboto: Roboto, sans-serif;\n\n.button {\n    font-family: $font-family-roboto;\n}\n```\n\n**Правильно:**\n\n```scss\n$font-family-roboto: \"Roboto\", sans-serif;\n\n.button {\n    font-family: $font-family-roboto;\n}\n```\n\n* Псевдоэлементы записываются с помощью `::`.\n\n**Неправильно:**\n\n```scss\n.list {\n    &__item {\n        &:before {\n            content: \"\";\n            display: inline-block;\n            vertical-align: middle;\n            border-radius: 50%;\n            width: 8px;\n            height: 8px;\n            background-color: currentColor;\n        }\n    }\n}\n```\n\n**Правильно:**\n\n```scss\n.list {\n    &__item {\n        &::before {\n            content: \"\";\n            display: inline-block;\n            vertical-align: middle;\n            border-radius: 50%;\n            width: 8px;\n            height: 8px;\n            background-color: currentColor;\n        }\n    }\n}\n```\n\n* Не должно быть дублирующихся свойств.\n\n**Неправильно:**\n\n```scss\n.button {\n    display: inline-block;\n    border: solid 1px $color-black;\n    border-radius: 4px;\n    padding: 5px;\n    font-family: $font-family-roboto;\n    font-weight: 300;\n    font-size: 16px;\n    line-height: 1.5;\n    text-align: center;\n    text-decoration: none;\n    cursor: pointer;\n    border: none;\n}\n```\n\n**Правильно:**\n\n```scss\n.button {\n    display: inline-block;\n    border: none;\n    border-radius: 4px;\n    padding: 5px;\n    font-family: $font-family-roboto;\n    font-weight: 300;\n    font-size: 16px;\n    line-height: 1.5;\n    text-align: center;\n    text-decoration: none;\n    cursor: pointer;\n}\n```\n\n* `font-weight` записывается в числовом формате.\n\n**Неправильно:**\n\n```scss\nb {\n    font-weight: bold;\n}\n```\n\n**Правильно:**\n\n```scss\nb {\n    font-weight: 700;\n}\n```\n\n* У нулевых значений не указываются единицы измерений (кроме времени).\n\n**Неправильно:**\n\n```scss\n.button {\n    margin: 0px auto;\n    padding: 5px 0px 6px;\n}\n```\n\n**Правильно:**\n\n```scss\n.button {\n    margin: 0 auto;\n    padding: 5px 0 6px;\n}\n```\n\n* Для чисел в интервале `(-1, 1)` указывается ведущий ноль.\n\n**Неправильно:**\n\n```scss\na {\n    transition: color .3s;\n}\n```\n\n**Правильно:**\n\n```scss\na {\n    transition: color 0.3s;\n}\n```\n\n* Вместо именованных цветов используется `#`-запись.\n\n**Неправильно:**\n\n```scss\n$color-black: black;\n```\n\n**Правильно:**\n\n```scss\n$color-black: #000;\n```\n\n* Для записи `#`-цвета следует использовать сокращенный вариант, если это возможно.\n\n**Неправильно:**\n\n```scss\n$color-black: #000000;\n```\n\n**Правильно:**\n\n```scss\n$color-black: #000;\n```\n\n* При записи `rgba`-цвета задается `#`-значением или переменной.\n\n**Неправильно:**\n\n```scss\n.overlay {\n    background-color: rgba(0, 0, 0, 0.5);\n}\n```\n\n**Правильно:**\n\n```scss\n.overlay {\n    background-color: rgba($color-black, 0.5);\n}\n```\n\n# `@extend`\n\nВместо директивы `@extend` следует использовать `@mixin`.\n\n# Порядок правил\n\n* CSS-переменные.\n* `$`-переменные.\n* `@include` без контента\n* Свойства\n* `&::before`\n* `&::after`\n* Различные селекторы\n* `&:link`\n* `&:visited`\n* `&:focus`\n* `&:hover`\n* `&:active`\n* `&:first-child`\n* `&:last-child`\n* `&:nth-child()`\n* `&[attr]`\n* `&.modifier`\n* `&--modifier`\n* `@include` с контентом\n* `@media`\n\n# Порядок свойств\n\n* `all`\n* `print-color-adjust`\n* `appearance`\n* `counter-increment`\n* `counter-reset`\n* `content`\n* `quotes`\n* `position`\n* `left`\n* `right`\n* `top`\n* `bottom`\n* `z-index`\n* `display`\n* `columns`\n* `column-width`\n* `column-count`\n* `column-fill`\n* `column-gap`\n* `column-rule`\n* `column-rule-style`\n* `column-rule-width`\n* `column-rule-color`\n* `column-span`\n* `break-after`\n* `break-before`\n* `break-inside`\n* `page-break-after`\n* `page-break-before`\n* `page-break-inside`\n* `orphans`\n* `widows`\n* `flex`\n* `flex-grow`\n* `flex-shrink`\n* `flex-basis`\n* `flex-flow`\n* `flex-direction`\n* `flex-wrap`\n* `place-content`\n* `place-items`\n* `place-self`\n* `align-content`\n* `align-items`\n* `align-self`\n* `justify-content`\n* `justify-items`\n* `justify-self`\n* `order`\n* `clear`\n* `float`\n* `grid`\n* `grid-area`\n* `grid-auto-columns`\n* `grid-auto-flow`\n* `grid-auto-rows`\n* `grid-column`\n* `grid-column-end`\n* `grid-column-gap`\n* `grid-column-start`\n* `grid-gap`\n* `grid-row`\n* `grid-row-end`\n* `grid-row-gap`\n* `grid-row-start`\n* `grid-template`\n* `grid-template-areas`\n* `grid-template-columns`\n* `grid-template-rows`\n* `list-style`\n* `list-style-type`\n* `list-style-position`\n* `list-style-image`\n* `caption-side`\n* `empty-cells`\n* `table-layout`\n* `vertical-align`\n* `clip-path`\n* `mask`\n* `mask-clip`\n* `mask-composite`\n* `mask-image`\n* `mask-mode`\n* `mask-origin`\n* `mask-position`\n* `mask-position-x`\n* `mask-position-y`\n* `mask-repeat`\n* `mask-repeat-x`\n* `mask-repeat-y`\n* `mask-size`\n* `mask-type`\n* `shape-image-threshold`\n* `shape-margin`\n* `shape-outside`\n* `contain`\n* `overflow`\n* `overflow-x`\n* `overflow-y`\n* `overflow-anchor`\n* `overflow-wrap`\n* `margin`\n* `margin-top`\n* `margin-right`\n* `margin-bottom`\n* `margin-left`\n* `margin-before`\n* `margin-end`\n* `margin-after`\n* `margin-start`\n* `margin-collapse`\n* `margin-top-collapse`\n* `margin-bottom-collapse`\n* `margin-before-collapse`\n* `margin-after-collapse`\n* `outline`\n* `outline-style`\n* `outline-width`\n* `outline-color`\n* `outline-offset`\n* `outline-radius`\n* `outline-radius-topleft`\n* `outline-radius-topright`\n* `outline-radius-bottomright`\n* `outline-radius-bottomleft`\n* `border`\n* `border-style`\n* `border-width`\n* `border-color`\n* `border-top`\n* `border-top-style`\n* `border-top-width`\n* `border-top-color`\n* `border-right`\n* `border-right-style`\n* `border-right-width`\n* `border-right-color`\n* `border-bottom`\n* `border-bottom-style`\n* `border-bottom-width`\n* `border-bottom-color`\n* `border-left`\n* `border-left-style`\n* `border-left-width`\n* `border-left-color`\n* `border-before`\n* `border-before-style`\n* `border-before-width`\n* `border-before-color`\n* `border-end`\n* `border-end-style`\n* `border-end-width`\n* `border-end-color`\n* `border-after`\n* `border-after-style`\n* `border-after-width`\n* `border-after-color`\n* `border-start`\n* `border-start-style`\n* `border-start-width`\n* `border-start-color`\n* `border-collapse`\n* `border-image`\n* `border-image-source`\n* `border-image-slice`\n* `border-image-width`\n* `border-image-outset`\n* `border-image-repeat`\n* `border-radius`\n* `border-top-left-radius`\n* `border-top-right-radius`\n* `border-bottom-right-radius`\n* `border-bottom-left-radius`\n* `border-spacing`\n* `padding`\n* `padding-top`\n* `padding-right`\n* `padding-bottom`\n* `padding-left`\n* `padding-before`\n* `padding-end`\n* `padding-after`\n* `padding-start`\n* `width`\n* `height`\n* `min-width`\n* `min-height`\n* `max-width`\n* `max-height`\n* `box-decoration-break`\n* `box-shadow`\n* `box-sizing`\n* `src`\n* `font`\n* `font-family`\n* `font-weight`\n* `font-style`\n* `font-display`\n* `font-feature-settings`\n* `font-kerning`\n* `font-smoothing`\n* `font-stretch`\n* `font-synthesis`\n* `font-variant`\n* `font-variant-alternates`\n* `font-variant-caps`\n* `font-variant-east-asian`\n* `font-variant-ligatures`\n* `font-variant-numeric`\n* `font-variant-position`\n* `font-size`\n* `font-size-adjust`\n* `unicode-bidi`\n* `unicode-range`\n* `line-break`\n* `line-height`\n* `letter-spacing`\n* `word-break`\n* `word-spacing`\n* `word-wrap`\n* `white-space`\n* `hyphens`\n* `tab-size`\n* `text-align`\n* `text-align-last`\n* `text-combine-upright`\n* `text-decoration`\n* `text-decoration-style`\n* `text-decoration-line`\n* `text-decoration-color`\n* `text-decoration-skip`\n* `text-emphasis`\n* `text-emphasis-style`\n* `text-emphasis-color`\n* `text-emphasis-position`\n* `text-fill-color`\n* `text-indent`\n* `text-justify`\n* `text-orientation`\n* `text-overflow`\n* `text-rendering`\n* `text-security`\n* `text-shadow`\n* `text-size-adjust`\n* `text-stroke`\n* `text-stroke-width`\n* `text-stroke-color`\n* `text-transform`\n* `text-underline-position`\n* `direction`\n* `writing-mode`\n* `ruby-align`\n* `ruby-position`\n* `color`\n* `caret-color`\n* `tap-highlight-color`\n* `d`\n* `x`\n* `y`\n* `cx`\n* `cy`\n* `r`\n* `rx`\n* `ry`\n* `fill`\n* `fill-opacity`\n* `fill-rule`\n* `stroke`\n* `stroke-dasharray`\n* `stroke-dashoffset`\n* `stroke-linecap`\n* `stroke-linejoin`\n* `stroke-miterlimit`\n* `stroke-opacity`\n* `stroke-width`\n* `alignment-baseline`\n* `baseline-shift`\n* `dominant-baseline`\n* `clip-rule`\n* `color-interpolation`\n* `color-interpolation-filters`\n* `color-rendering`\n* `flood-color`\n* `flood-opacity`\n* `lighting-color`\n* `marker`\n* `marker-end`\n* `marker-mid`\n* `marker-start`\n* `paint-order`\n* `shape-rendering`\n* `stop-color`\n* `stop-opacity`\n* `text-anchor`\n* `offset`\n* `offset-position`\n* `offset-path`\n* `offset-distance`\n* `offset-anchor`\n* `offset-rotate`\n* `background`\n* `background-image`\n* `background-position`\n* `background-position-x`\n* `background-position-y`\n* `background-size`\n* `background-repeat`\n* `background-repeat-x`\n* `background-repeat-y`\n* `background-origin`\n* `background-clip`\n* `background-attachment`\n* `background-color`\n* `background-blend-mode`\n* `image-orientation`\n* `image-rendering`\n* `object-fit`\n* `object-position`\n* `opacity`\n* `visibility`\n* `filter`\n* `isolation`\n* `mix-blend-mode`\n* `zoom`\n* `backface-visibility`\n* `perspective`\n* `perspective-origin`\n* `perspective-origin-x`\n* `perspective-origin-y`\n* `transform`\n* `transform-box`\n* `transform-origin`\n* `transform-origin-x`\n* `transform-origin-y`\n* `transform-origin-z`\n* `transform-style`\n* `transition`\n* `transition-property`\n* `transition-duration`\n* `transition-delay`\n* `transition-timing-function`\n* `animation`\n* `animation-name`\n* `animation-duration`\n* `animation-delay`\n* `animation-timing-function`\n* `animation-iteration-count`\n* `animation-direction`\n* `animation-fill-mode`\n* `animation-play-state`\n* `will-change`\n* `cursor`\n* `pointer-events`\n* `touch-action`\n* `user-drag`\n* `user-focus`\n* `user-select`\n* `user-zoom`\n* `resize`\n* `scroll-behavior`\n* `scroll-snap-coordinate`\n* `scroll-snap-destination`\n* `scroll-snap-type`\n* `scroll-snap-type-x`\n* `scroll-snap-type-y`\n"
  },
  {
    "path": "18_codestyle-javascript.md",
    "content": "# Синтаксис и форматирование\n\n* Для отступов используется символ табуляции `\\t`.\n\n* Для переноса строк используется символ `\\n` (LF).\n\n* Не должно быть пробелов в конце строк.\n\n* Максимальная длина строки — 120 символов.\n\n* Всегда ставится `;`.\n\n**Неправильно:**\n\n```js\n$('form').on('submit', (event) => {\n    let $form = $(event.currentTarget)\n\n    event.preventDefault()\n\n    if (validate($form)) {\n        $.post($form.attr('action'))\n            .done(() => {\n                showSuccessModal()\n            })\n            .fail(() => {\n                showErrorModal()\n            })\n    }\n})\n```\n\n**Правильно:**\n\n```js\n$('form').on('submit', (event) => {\n    let $form = $(event.currentTarget);\n\n    event.preventDefault();\n\n    if (validate($form)) {\n        $.post($form.attr('action'))\n            .done(() => {\n                showSuccessModal();\n            })\n            .fail(() => {\n                showErrorModal();\n            });\n    }\n})\n```\n\n* Используется строгое сравнение `===` и `!==` (вместо `==` и `!=`).\n\n**Неправильно:**\n\n```js\n$('.test__button').on('click', () => {\n    if (test.currentQuestionIndex == test.questions.length - 1) {\n        test.finish();\n    }\n\n    test.answer();\n});\n```\n\n**Правильно:**\n\n```js\n$('.test__button').on('click', () => {\n    if (test.currentQuestionIndex === test.questions.length - 1) {\n        test.finish();\n    }\n\n    test.answer();\n});\n```\n\n* Не должно быть неиспользуемых переменных.\n\n**Неправильно:**\n\n```js\nlet dx = event.clientX - start.clientX;\nlet dy = event.clientY - start.clientY;\n\n$slider.css({\n    '-webkit-transform': `translate(${dx}px, 0)`,\n    'transform': `translate(${dx}px, 0)`,\n});\n```\n\n**Правильно:**\n\n```js\nlet dx = event.clientX - start.clientX;\n\n$slider.css({\n    '-webkit-transform': `translate(${dx}px, 0)`,\n    'transform': `translate(${dx}px, 0)`,\n});\n```\n\n* Переменные объявляются на отдельных строках.\n\n**Неправильно:**\n\n```js\nfunction showPage($page) {\n    let $currentPage = $('.page--current'),\n        $header = $('.header'),\n        $footer = $('.footer');\n\n    new TimelineMax()\n        .to([\n            $header,\n            $footer,\n            $currentPage,\n        ], 1, {\n            opacity: 0,\n            onComplete() {\n                $currentPage.removeClass('page--current');\n            },\n        })\n        .from([\n            $header,\n            $footer,\n            $page,\n        ], 1, {\n            clearProps: 'all',\n            onStart() {\n                $page.addClass('page--current');\n            },\n        });\n}\n```\n\n**Правильно:**\n\n```js\nfunction showPage($page) {\n    let $currentPage = $('.page--current');\n    let $header = $('.header');\n    let $footer = $('.footer');\n\n    new TimelineMax()\n        .to([\n            $header,\n            $footer,\n            $currentPage,\n        ], 1, {\n            opacity: 0,\n            onComplete() {\n                $currentPage.removeClass('page--current');\n            },\n        })\n        .from([\n            $header,\n            $footer,\n            $page,\n        ], 1, {\n            clearProps: 'all',\n            onStart() {\n                $page.addClass('page--current');\n            },\n        });\n}\n```\n\n* Не следует сокращать названия переменных и функций.\n\n**Неправильно:**\n\n```js\n$('.js-scroll').on('click', (e) => {\n    let $l = $(e.currentTarget);\n    let h = $l.attr('href');\n    let t = $(h).offset().top;\n\n    $('html, body').animate({\n        scrollTop: t,\n    }, 500);\n});\n```\n\n**Правильно:**\n\n```js\n$('.js-scroll').on('click', (event) => {\n    let $link = $(event.currentTarget);\n    let href = $link.attr('href');\n    let top = $(href).offset().top;\n\n    $('html, body').animate({\n        scrollTop: top,\n    }, 500);\n});\n```\n\n* После определения переменных следует оставлять пустую строку.\n\n**Неправильно:**\n\n```js\n$('form').on('submit', (event) => {\n    let $form = $(event.currentTarget);\n    let $button = $form.find('submit');\n    event.preventDefault();\n    $button.prop('disabled', true);\n    submitForm($form);\n});\n```\n\n**Правильно:**\n\n```js\n$('form').on('submit', (event) => {\n    let $form = $(event.currentTarget);\n    let $button = $form.find('submit');\n\n    event.preventDefault();\n    $button.prop('disabled', true);\n    submitForm($form);\n});\n```\n\n* Операторы в выражениях отделяются одним пробелом.\n\n**Неправильно:**\n\n```js\nfunction sum(a, b) {\n    return a+b;\n}\n```\n\n**Правильно:**\n\n```js\nfunction sum(a, b) {\n    return a + b;\n}\n```\n\n* Перед `return` следует оставлять пустую строку.\n\n**Неправильно:**\n\n```js\nfunction getQueryParam(key) {\n    let params = location.search.slice(1).split('&');\n\n    for (let param of params) {\n        param = param.split('&');\n\n        if (param[0] === key) {\n            return param[1];\n        }\n    }\n    return null;\n}\n```\n\n**Правильно:**\n\n```js\nfunction getQueryParam(key) {\n    let params = location.search.slice(1).split('&');\n\n    for (let param of params) {\n        param = param.split('&');\n\n        if (param[0] === key) {\n            return param[1];\n        }\n    }\n\n    return null;\n}\n```\n\n* Многострочные конструкции и выражения отделяются пустой строкой.\n\n**Неправильно:**\n\n```js\nfunction f() {\n    console.log('f');\n}\nfunction g() {\n    console.log('g');\n}\nlet timer = {\n    start() {\n        this.stop();\n        this.intervalId = setInterval(() => {\n            console.log('Tick');\n        }, 1000);\n    },\n    stop() {\n        clearInterval(this.intervalId);\n    },\n};\nif (x > 5) {\n    x = 0;\n}\nif (!items.length) {\n    items.push(42);\n}\nswitch (number) {\n    case 4:\n        console.log('a');\n        break;\n    case 5:\n        console.log('b');\n        break;\n    case 1:\n        console.log('c');\n        break;\n    default:\n        console.log('d');\n        break;\n}\nfor (let i = 0; i < 10; i++) {\n    y *= i;\n}\n$('.button').on('click', (event) => {\n    event.preventDefault();\n    run();\n});\n```\n\n**Правильно:**\n\n```js\nfunction f() {\n    console.log('f');\n}\n\nfunction g() {\n    console.log('g');\n}\n\nlet timer = {\n    start() {\n        this.stop();\n\n        this.intervalId = setInterval(() => {\n            console.log('Tick');\n        }, 1000);\n    },\n\n    stop() {\n        clearInterval(this.intervalId);\n    },\n};\n\nif (x > 5) {\n    x = 0;\n}\n\nif (!items.length) {\n    items.push(42);\n}\n\nswitch (number) {\n    case 4:\n        console.log('a');\n        break;\n\n    case 5:\n        console.log('b');\n        break;\n\n    case 1:\n        console.log('c');\n        break;\n\n    default:\n        console.log('d');\n        break;\n}\n\nfor (let i = 0; i < 10; i++) {\n    y *= i;\n}\n\n$('.button').on('click', (event) => {\n    event.preventDefault();\n    run();\n});\n```\n\n* Не более одной пустой строки подряд.\n\n**Неправильно:**\n\n```js\n$('.js-scroll-to').on('click', (event) => {\n    event.preventDefault();\n    scrollTo(event.currentTarget.href);\n});\n\n\n$('.js-close').on('click', (event) => {\n    $(event.currentTarget).parent().removeClass('is-open');\n});\n```\n\n**Правильно:**\n\n```js\n$('.js-scroll-to').on('click', (event) => {\n    event.preventDefault();\n    scrollTo(event.currentTarget.href);\n});\n\n$('.js-close').on('click', (event) => {\n    $(event.currentTarget).parent().removeClass('is-open');\n});\n```\n\n* Не следует использовать однострочную запись `if` без фигурных скобок.\n\n**Неправильно:**\n\n```js\nfunction resizeItems() {\n    if (!items.length) return;\n\n    items.forEach((item) => {\n        item.resize();\n    });\n}\n```\n\n**Правильно:**\n\n```js\nfunction resizeItems() {\n    if (!items.length) {\n        return;\n    }\n\n    items.forEach((item) => {\n        item.resize();\n    });\n}\n```\n\n* Для строк используются одинарные кавычки.\n\n**Неправильно:**\n\n```js\nlet name = \"John Doe\"\n```\n\n**Правильно:**\n\n```js\nlet name = 'John Doe';\n```\n\n# ES6 возможности\n\n* Используются шаблонные строки вместо конкатенации.\n\n**Неправильно:**\n\n```js\nfunction getDateTime() {\n    let now = new Date();\n    let year = now.getFullYear().toString().padStart(2, '0');\n    let month = (now.getMonth() + 1).toString().padStart(2, '0');\n    let day = now.getDate().toString().padStart(2, '0');\n    let hours = now.getHours().toString().padStart(2, '0');\n    let minutes = now.getMinutes().toString().padStart(2, '0');\n\n    return year + '.' + month + '.' + day + ' ' + hours + ':' + minutes;\n}\n```\n\n**Правильно:**\n\n```js\nfunction getDateTime() {\n    let now = new Date();\n    let year = now.getFullYear().toString().padStart(2, '0');\n    let month = (now.getMonth() + 1).toString().padStart(2, '0');\n    let day = now.getDate().toString().padStart(2, '0');\n    let hours = now.getHours().toString().padStart(2, '0');\n    let minutes = now.getMinutes().toString().padStart(2, '0');\n\n    return `${year}.${month}.${day} ${hours}:${minutes}`;\n}\n```\n\n* Используется `let` вместо `var`.\n\n**Неправильно:**\n\n```js\nvar name = 'John';\nvar surname = 'Doe';\nvar fullname = `${name} ${surname}`;\n```\n\n**Правильно:**\n\n```js\nlet name = 'John';\nlet surname = 'Doe';\nlet fullname = `${name} ${surname}`;\n```\n\n**Правильно:**\n\n```js\n```\n\n* Используются стрелочные функции вместо анонимных.\n\n**Неправильно:**\n\n```js\n$('form').on('submit', function (event) {\n    event.preventDefault();\n    submitForm(this);\n});\n```\n\n**Правильно:**\n\n```js\n$('form').on('submit', (event) => {\n    event.preventDefault();\n    submitForm(event.currentTarget);\n});\n```\n\n* Следует использовать сокращенную запись ключей объекта.\n\n**Неправильно:**\n\n```js\nfunction getObjectPosition(object) {\n    let $offset = $(object).offset();\n    let x = $offset.top;\n    let y = $offset.left;\n\n    return {\n        x: x,\n        y: y,\n    };\n}\n```\n\n**Правильно:**\n\n```js\nfunction getObjectPosition(object) {\n    let $offset = $(object).offset();\n    let x = $offset.top;\n    let y = $offset.left;\n\n    return {\n        x,\n        y,\n    };\n}\n```\n\n* Следует использовать сокращенную запись методов объекта.\n\n**Неправильно:**\n\n```js\nTweenMax.from($element, 1, {\n    opacity: 0,\n    clearProps: 'all',\n    onStart: () => {\n        $element.removeClass('is-hidden');\n    },\n});\n```\n\n**Правильно:**\n\n```js\nTweenMax.from($element, 1, {\n    opacity: 0,\n    clearProps: 'all',\n    onStart() {\n        $element.removeClass('is-hidden');\n    },\n});\n```\n"
  },
  {
    "path": "19_video-js.md",
    "content": "# Video.js\n\n[Video.js](https://github.com/videojs/video.js) - это библиотека с открытым исходным кодом, предназначенная для\nсоздания видео плеера.\n\nСама по себе библиотека очень проста. Дополнительная функциональность поставляется в плагинах(плейлисты, аналитика,\nреклама, и расширенные форматы видео - `HLS` или `DASH`).\n\n## Установка\n\n### NPM\n\n```bash\nnpm install --save video.js\n```\n\n### CDN\n\n```html\n<script src=\"http://vjs.zencdn.net/6.9.0/video.js\"></script>\n```\n\n```html\n<link href=\"http://vjs.zencdn.net/6.9.0/video-js.css\" rel=\"stylesheet\">\n```\n\n## Инициализация\n\nНа странице должен присутствовать тег:\n\n```html\n<video class=\"video-js\"></video>\n```\n\nПередаем строку содержащую `id` элемента:\n\n```js\nlet video = videojs('id');\n```\n\nИли `DOM` элемент\n\n```js\nlet video = videojs(document.querySelector('.video-js'));\n```\n\n## Опции\n\nОпции передаются вторым параметром:\n\n```js\nlet video = videojs('my-video', {\n    autoplay: false,\n});\n```\n\n**Основные опции:**\n\n* `autoplay: boolean` - автоматическое воспроизведение;\n* `controls: boolean` - отображать ли интерфейс плеера;\n* `loop: boolean` - зацикливание воспроизведения видео;\n* `muted: boolean` - приглушение звука;\n* `poster: string` - ссылка на превью видео;\n* `width: string|number` - ширина видео;\n* `height: string|number` - высота видео;\n\n**Дополнительные опции:**\n\n* `fluid: boolean` - подгонять ли видео под размер контейнера;\n* `aspectRatio: string` - соотношение сторон видео (16:9, 4:3);\n\n## Методы\n\n* `src(string|array)` - позволяет задать источник видео;\n\n   ```js\n   video.src('/path/to/video.mp4');\n\n   video.src([\n       {\n           type: 'video/mp4',\n           src: '/path/to/video.mp4',\n       },\n       {\n           type: 'video/webm',\n           src: '/path/to/video.webm',\n       },\n       {\n           type: 'video/ogg',\n           src: '/path/to/video.ogg',\n       },\n   ]);\n   ```\n\n* `poster(string)` - позволяет задать превью видео;\n\n* `play()` - воспроизводит видео;\n\n* `pause()` - ставит видео на паузу;\n\n* `paused()` - возвращает `true`, если видео стоит на паузе, иначе `false`;\n\n* `dispose()` - полностью удаляет плеер (вызывает событие `dispose`, удаляет все обработчики событий, удаляет `DOM` элементы);\n\n* `volume(number)` - задает горомкость звука (число от `0` до `1`); если вызвать без параметра - возвращает текущее значение;\n\n* `muted(bolean)` - возвращае `true`, если звук выключен, иначе `false`; если передано `true` - выключает звук.\n\n* `requestFullscreen()` - вход в полноэкранный режим;\n\n* `exitFullscreen()` - выход из полноэкранного режима;\n\n* `isFullscreen()` - возвращает `true` если видео находится в полноэкранном режиме, иначе `false`;\n\n* `currentTime(number)` - возвращает текущее место воспроизведения (в секундах); если передать число - устанавливает текущее место;\n\n* `duration()` - возвращает длину видео;\n\n* `remainingTime()` - возвращает оставшееся время;\n\n**Пример:**\n\n```js\nlet video = videojs('video', {\n    controls: true,\n    autoplay: false,\n    loop: false,\n    poster: '/video/cover.jpg',\n});\n\nvideo.src({\n    src: '/video/video.mp4',\n    withCredentials: true,\n});\n\nvideo.on('ready', () => {\n    // ...\n});\n\n```\n\n## События\n\nСобытия те же, что у нативного элемента `video`.\nПолный список [тут](https://developer.mozilla.org/ru/docs/Web/Guide/Events/Media_events).\n\n**Пример:**\n\n```js\nvideo.on('dispose', () => {\n    // ...\n});\n\nvideo.on('play', () => {\n    // ...\n});\n\nvideo.on('ended', () => {\n    // ...\n});\n```\n\n## Стриминг (HLS)\n\nДля стриминга `video.js` использует плагин [videojs-contrib-hls](https://github.com/videojs/videojs-contrib-hls).\n\nКак подготовить видео - описано [тут](20_hls.md)\n\n### Установка\n\n```bash\nnpm install --save videojs-contrib-hls\n```\n\n### Инициализация\n\n```js\nlet video = videojs('my-video', {\n    controls: true,\n    autoplay: true,\n    loop: false,\n    poster: '/video/cover.jpg',\n        html5: {\n            nativeAudioTracks: false,\n            nativeVideoTracks: false,\n            nativeTextTracks: false,\n        hls: {\n            overrideNative: true,\n        },\n    },\n});\n\nvideo.src({\n    src: 'video-name.m3u8',\n    type: 'application/x-mpegURL',\n    withCredentials: true,\n});\n```\n\nДля воспроизведения стриминга с других серверов, необходимо установить свойство `withCredentials: false`.\n\n## Выбор качества воспроизводимого видео\n\nДля ручного выбора качества видео, необходимо установить 2 плагина:\n\n```bash\nnpm install --save videojs-contrib-quality-levels videojs-hls-quality-selector\n```\n\nПосле указания источника видео, нужно вызвать метод `.hlsQualitySelector()` для экземпляра видео.\n\n**Пример**\n\n```js\nvideo.src({\n    src: `video.m3u8`,\n    type: 'application/x-mpegURL',\n    withCredentials: false,\n});\n\nvideo.hlsQualitySelector();\n```\n\n## Субтитры\n\nДля субтриров используются файлы в формате `.vtt`.\n\n**Пример**\n\n```\nWEBVTT\n\n00:01.000 --> 00:04.000\nNever drink liquid nitrogen.\n\n00:05.000 --> 00:09.000\n- It will perforate your stomach.\n```\n\nЧто бы подключить субтитры к видео - нужно использовать метод `.addRemoteTextTrack()`.\n\n**Пример**\n\n```js\nvideo.addRemoteTextTrack({\n    label: 'Russian',\n    kind: 'captions',\n    src: 'subtitles.vtt',\n    default: true,\n}, false);\n```\n\nЗадать стили субтитрам можно так (здесь используется `!important`, так как video.js задает стили инлайн):\n\n```scss\n.vjs-text-track-display {\n    display: block;\n    pointer-events: none;\n\n    div {\n        font-family: Arial, sans-serif !important;\n        font-size: 40px !important;\n        background-color: transparent !important;\n    }\n}\n```\n\n## Стилизация\n\nПо умолчанию используется стандартный скин.\n\n```html\n<link href=\"http://vjs.zencdn.net/6.7.1/video-js.css\" rel=\"stylesheet\">\n```\n\n**Стандартный скин:**\n\n![Стандартная стилизация](images/19/image-1.jpg)\n\n**Отображение без стилей:**\n\n![Отображение без стилей](images/19/image-2.jpg)\n\nДля кастомной стилизаци, нужно использовать свои стили.\n\n**Пример своей стилизации:**\n\n![Своя стилизация](images/19/image-3.jpg)\n\n- Контейнер\n\nВписываем контейнер в блок, у которого заданы размеры требуемые\nпо макету.\n\n```html\n<div id=\"video\" class=\"video-js\"></div>\n```\n\n```scss\n.video-js {\n    width: 100%;\n    height: 100%;\n}\n```\n\n- Элемент `<video>`\n\nПодгоняем формат, для предотвращения деформирования видео.\n\n```scss\n.vjs-tech {\n    width: auto;\n    height: 100vh;\n    min-width: 100vw;\n    object-fit: cover;\n\n    @media (min-aspect-ratio: 1920 / 1080) {\n        width: 100vw;\n        height: auto;\n        min-height: 100vh;\n    }\n}\n\n```\n\n- Обложка `.vjs-poster`\n\n- Спинер `.vjs-loading-spinner`\n\n- Кнопка воспроизведение\\пауза `.vjs-play-control`\n\n- Кнопка вкл\\выкл звук `.vjs-mute-control`\n\n- Полоса громкости `.vjs-volume-bar`\n\n- Текущее время видео `.vjs-current-time`\n\n- Длительность видео `.vjs-duration`\n\n- Оставшееся время `.vjs-remaining-time`\n\n- Прогресс бар `.vjs-progress-control`\n\n- Кнопка полноэкранного режима `.vjs-fullscreen-control`\n\n**Пример:**\n\n```scss\n.vjs-progress-control {\n    display: block;\n}\n\n.vjs-progress-holder {\n    position: relative;\n    height: 4px;\n    cursor: pointer;\n\n    &::before {\n        content: \"\";\n        position: absolute;\n        left: 0;\n        width: 100%;\n        height: 4px;\n        background-color: #fff;\n    }\n}\n\n.vjs-play-progress {\n    position: relative;\n    display: block;\n    width: 0;\n    height: 4px;\n    background-color: #000;\n\n    .vjs-control-text {\n        display: none;\n    }\n\n    .vjs-time-tooltip {\n       display: none;\n    }\n}\n```\n"
  },
  {
    "path": "20_hls.md",
    "content": "# HTTP Live Streaming (HLS)\n\n`HLS` — протокол для потоковой передачи медиа (аудио/видео), на основе `HTTP`.\n\nВ основе работы лежит принцип разбиения цельного потока на небольшие фрагменты, последовательно скачиваемые по `HTTP`.\n\nВ начале сессии скачивается плей-лист в формате `.m3u8` (обычные текстовый файл), содержащий метаданные об имеющихся вложенных потоках.\n\nСами потоки находятся в файлах с расширением `.ts`.\n\nВидео обычно кодируется с помощью `H264/h265`, аудио `AAC`.\n\n[Ссылка на статью](https://developer.apple.com/library/content/referencelibrary/GettingStarted/AboutHTTPLiveStreaming/about/about.html)\n\n\n## Конвертация (подготовка файлов для стриминга) `.mp4` в `.m3u8` и `.ts`\n\n### FFMPEG\n\n1. Скачиваем и устанавливаем [FFMPEG](http://www.ffmpeg.org/download.html).\nУ многих возникает вопрос как установить FFMPEG на Windows, поэтому ответ на этот вопрос можно найти [по ссылке](https://windowsloop.com/install-ffmpeg-windows-10/).\n2. Прописываем `FFMPEG` в `PATH`.\n3. Переходим в папку с видео:\n\n   ```bash\n   cd video_folder_name/\n   ```\n\n4. Создаем папки под видео:\n\n   ```bash\n   mkdir 1080 720 406 270 180\n   ```\n\n5. Нарезаем видео:\n\n   ```bash\n   ffmpeg \\\n     -i video_name.mp4 \\\n     -s 1920x1080 -c:a aac -c:v libx264 -map 0 -f segment -segment_time 10 -segment_format mpegts -segment_list 1080/playlist-1080.m3u8 \"1080/video_name-1080-%d.ts\" \\\n     -s 1280x720 -c:a aac -c:v libx264 -map 0 -f segment -segment_time 10 -segment_format mpegts -segment_list 720/playlist-720.m3u8 \"720/video_name-720-%d.ts\" \\\n     -s 720x406 -c:a aac -c:v libx264 -map 0 -f segment -segment_time 10 -segment_format mpegts -segment_list 406/playlist-406.m3u8 \"406/video_name-406-%d.ts\" \\\n     -s 480x270 -c:a aac -c:v libx264 -map 0 -f segment -segment_time 10 -segment_format mpegts -segment_list 270/playlist-270.m3u8 \"270/video_name-270-%d.ts\" \\\n     -s 320x180 -c:a aac -c:v libx264 -map 0 -f segment -segment_time 10 -segment_format mpegts -segment_list 180/playlist-180.m3u8 \"180/video_name-180-%d.ts\"\n   ```\n\n   Аналогичная команда для cmd:\n\n   ```bash\n   ffmpeg ^\n     -i video_name.mp4 ^\n     -s 1920x1080 -c:a aac -c:v libx264 -map 0 -f segment -segment_time 10 -segment_format mpegts -segment_list 1080/playlist-1080.m3u8 \"1080/video_name-1080-%d.ts\" ^\n     -s 1280x720 -c:a aac -c:v libx264 -map 0 -f segment -segment_time 10 -segment_format mpegts -segment_list 720/playlist-720.m3u8 \"720/video_name-720-%d.ts\" ^\n     -s 720x406 -c:a aac -c:v libx264 -map 0 -f segment -segment_time 10 -segment_format mpegts -segment_list 406/playlist-406.m3u8 \"406/video_name-406-%d.ts\" ^\n     -s 480x270 -c:a aac -c:v libx264 -map 0 -f segment -segment_time 10 -segment_format mpegts -segment_list 270/playlist-270.m3u8 \"270/video_name-270-%d.ts\" ^\n     -s 320x180 -c:a aac -c:v libx264 -map 0 -f segment -segment_time 10 -segment_format mpegts -segment_list 180/playlist-180.m3u8 \"180/video_name-180-%d.ts\"\n   ```\n\n6. Делаем обложку (если обложка не предоставлялась дизайнером):\n\n   ```bash\n   ffmpeg -i video_name.mp4 -ss 00:00:00 -vframes 1 cover.jpg\n   ```\n\n   <details>\n     <summary>Удобнее это делать, используя переменную с названием видео:</summary>\n\n     ```bash\n     video_name=\"название_видео\"\n\n     mkdir 1080 720 406 270 180\n\n     ffmpeg\n       -i $video_name.mp4 \\\n       -s 1920x1080 -c:a aac -c:v libx264 -map 0 -f segment -segment_time 10 -segment_format mpegts -segment_list 1080/playlist-1080.m3u8 \"1080/$video_name-1080-%d.ts\"\n       -s 1280x720 -c:a aac -c:v libx264 -map 0 -f segment -segment_time 10 -segment_format mpegts -segment_list 720/playlist-720.m3u8 \"720/$video_name-720-%d.ts\"\n       -s 720x406 -c:a aac -c:v libx264 -map 0 -f segment -segment_time 10 -segment_format mpegts -segment_list 406/playlist-406.m3u8 \"406/$video_name-406-%d.ts\"\n       -s 480x270 -c:a aac -c:v libx264 -map 0 -f segment -segment_time 10 -segment_format mpegts -segment_list 270/playlist-270.m3u8 \"270/$video_name-270-%d.ts\"\n       -s 320x180 -c:a aac -c:v libx264 -map 0 -f segment -segment_time 10 -segment_format mpegts -segment_list 180/playlist-180.m3u8 \"180/$video_name-180-%d.ts\"\n\n     ffmpeg -i $video_name.mp4 -ss 00:00:00 -vframes 1 cover.jpg\n     ```\n\n     Аналогичная команда для cmd:\n\n     ```bash\n     SET video_name=\"название_видео\"\n\n     mkdir 1080 720 406 270 180\n\n     ffmpeg\n       -i %video_name%.mp4 ^\n       -s 1920x1080 -c:a aac -c:v libx264 -map 0 -f segment -segment_time 10 -segment_format mpegts -segment_list 1080/playlist-1080.m3u8 \"1080/%video_name%-1080-%d.ts\" ^\n       -s 1280x720 -c:a aac -c:v libx264 -map 0 -f segment -segment_time 10 -segment_format mpegts -segment_list 720/playlist-720.m3u8 \"720/%video_name%-720-%d.ts\" ^\n       -s 720x406 -c:a aac -c:v libx264 -map 0 -f segment -segment_time 10 -segment_format mpegts -segment_list 406/playlist-406.m3u8 \"406/%video_name%-406-%d.ts\" ^\n       -s 480x270 -c:a aac -c:v libx264 -map 0 -f segment -segment_time 10 -segment_format mpegts -segment_list 270/playlist-270.m3u8 \"270/%video_name%-270-%d.ts\" ^\n       -s 320x180 -c:a aac -c:v libx264 -map 0 -f segment -segment_time 10 -segment_format mpegts -segment_list 180/playlist-180.m3u8 \"180/%video_name%-180-%d.ts\"\n\n     ffmpeg -i %video_name%.mp4 -ss 00:00:00 -vframes 1 cover.jpg\n     ```\n   </details>\n\n   На выходе получаем видео, раскиданное по папкам с нужным размером и разбитое на файлы `.ts`, манифесты `.m3u8` (содержащие метаинформацию о файлах для каждого размера) и обложку `cover.jpg`.\n\n7. Создаем мастер файл для объединения всех манифестов `video-name.m3u8`:\n\n   ```\n   #EXTM3U\n   #EXT-X-VERSION:3\n   #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=3880000,RESOLUTION=1920x1080,CODECS=\"mp4a.40.2,avc1.77.30\",CLOSED-CAPTIONS=NONE\n   1080/playlist-1080.m3u8\n   #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1810000,RESOLUTION=1280x720,CODECS=\"mp4a.40.2,avc1.77.30\",CLOSED-CAPTIONS=NONE\n   720/playlist-720.m3u8\n   #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=800000,RESOLUTION=720x406,CODECS=\"mp4a.40.2,avc1.77.30\",CLOSED-CAPTIONS=NONE\n   406/playlist-406.m3u8\n   #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=600000,RESOLUTION=480x270,CODECS=\"mp4a.40.2,avc1.77.30\",CLOSED-CAPTIONS=NONE\n   270/playlist-270.m3u8\n   #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=410000,RESOLUTION=320x180,CODECS=\"mp4a.40.2,avc1.77.30\",CLOSED-CAPTIONS=NONE\n   180/playlist-180.m3u8\n   ```\n\n## Стандартные размеры видео для HLS\n\n* **180p** — 320x180px\n* **270p** — 480x270px\n* **406p** — 720x406px\n* **720p** — 1280x720px\n* **1080p** — 1920x1080px\n\nТочки `p` — определяющие название файла, берутся от высоты видео.\n"
  },
  {
    "path": "21_bem.md",
    "content": "# Использование БЭМ методологии\n\nЧто такое БЭМ и всю информацио о методологии можно прочесть [здесь](https://ru.bem.info/methodology/).\n\n* Используется стиль именования [«Two Dashes»](https://ru.bem.info/methodology/naming-convention/#%D1%81%D1%82%D0%B8%D0%BB%D1%8C-two-dashes).\n\n* Название блока должно быть логически понятным: header, footer, nav, sidebar, content, list и т.п.\n\n* Элемент - составная часть блока. Не должен использоваться вне блока и не должен являться частью другого элемента.\n\n* Модификатор не должен использоваться самостоятельно.\n\n* Допустимы модификаторы со значениями, но желательно использовать в очень редких случаях.\n\n* Допустимо использование миксов.\n\n* В идеале, каждый блок должен представлять из себя независимый компонент. Такой компонент позволяет без труда использовать его много раз в повторяющихся местах сайта, быстро и безболезненно редактировать и дополнять его, а также переносить из проекта в проект (универсальный компонент).\n\n**Неправильно:**\n\n```jade\n.header--fixed\n  .header__top\n    button.header__top__burger\n    a.header__top__logo(href=\"#\")\n    \n  .header__bottom\n    .header__nav\n      .header__nav__item\n        a.header__nav__item__link(href=\"#\")\n      .header__nav__item--active\n        a.header__nav__item--active__link(href=\"#\")\n      .header__nav__item\n        a.header__nav__item__link(href=\"#\")\n```\n\n**Правильно:**\n\n```jade\nheader.header.header--fixed\n  .header__top\n    button.header__burger(type=\"button\")\n    a.header__logo(href=\"#\")\n    \n  .header__bottom\n    nav.nav\n      .nav__item\n        a.nav__link(href=\"#\")\n      .nav__item.nav__item--active\n        a.nav__link(href=\"#\")\n      .nav__item\n        a.nav__link(href=\"#\")\n```\n\n**Правильно:**\n\n```jade\nheader.header.is-fixed\n  .header__top\n    button.header__burger(type=\"button\")\n    a.header__logo(href=\"#\")\n    \n  .header__bottom\n    nav.nav\n      .nav__item\n        a.nav__link(href=\"#\")\n      .nav__item.is-active\n        a.nav__link(href=\"#\")\n      .nav__item\n        a.nav__link(href=\"#\")\n```\n"
  },
  {
    "path": "22_crossbrowser-adaptive.md",
    "content": "# Кроссбраузерность\n\nИсходя из нашей статистики, основная часть сайтов должна поддерживаться в последних версиях таких браузеров, как:\n\n## Десктоп\n\n* Chrome\n* Safari\n* Firefox\n* Internet Explorer 11\n* Edge\n* Yandex Browser (русскоязычный сегмент)\n* Opera (русскоязычный сегмент)\n\n## Мобильные устройства и планшеты\n\n* Chrome\n* Safari\n\nВ определенных случаях данный список может меняться.\n\n# Адаптивность\n\nДля основной части сайтов используются два базовых брейкпоинта (в SCSS созданы для этого миксины mobile и desktop):\n\n* с 320 до 1024 (мобильные устройства и планшеты)\n* с 1025 и выше (десктоп)\n\nОстальные брейкпоинты добавляются по мере надобности, либо в зависимости от предоставленных макетов.\n\nСайт должен быть полностю адаптирован по ширине начиная от 320 и по высоте начиная от 550 пикселей.\n"
  },
  {
    "path": "23_perfomance.md",
    "content": "# Советы для оптимизации производительности сайта\n\nВ данном разделе документации собраны некоторые советы для разработки, которые помогут улучшить производительность сайта.\n\n# 1. Оптимизация растровых изображений\n\nФормат изображений выбираем отталкиваясь от прозрачности, если прозрачность присутствует - png, если ее нет - jpg. Картинки формата jpg получаются более легкими и лучше сжимаются. Конечно есть случаи, где требуется использовать тот или иной формат по мере надобности.\n\nИспользуйте **srcset** для выбора необходимого изображения под нужное разрешение.\n\n```html\n<img src=\"/images/example.jpg\" srcset=\"/images/example-retina.jpg 2x\" alt=\"image\">\n```\n\nКартинки должны быть сжаты, для этого можно использовать любой онлайн сервис или ПО для сжатия картинок. Конкретного предпочтения по сервисам нет, главное чтобы после сжатия картинки не слишком теряли качество. Вот небольшой список онлайн сервисов:\n\n+ <https://tinypng.com>\n+ <https://imagecompressor.com>\n+ <https://compressor.io>\n+ <https://imagify.io>\n+ <https://kraken.io>\n\n# 2. Оптимизация JavaScript кода\n\nНекоторые советы, которые помогут оптимизировать работу скриптов.\n\n## 2.1 Кеширование выборки и вычислений\n\nКеширование позволяет сократить кол-во обращений к дом-узлам или сократить кол-во вычислений, сделав это всего один раз и записав в память.\n\n**неправильно**\n\n```js\nlet $header = $('.header');\nlet $headerNav = $('.header__nav');\nlet $headerItem = $('.header__item')\n\nfunction getItemTopPosition() {\n  $headerItem.each((index, item) => {\n    let itemTopPosition = $header.height() / 2 -   $(item).offset().top;\n\n    console.log(itemTopPosition);\n  });\n}\n```\n\n**правильно**\n\n```js\nlet $header = $('.header');\nlet $headerNav = $header.find('.header__nav');\nlet $headerItem = $headerNav.find('.header__item');\n\nlet headerHalfHeight = $header.height() / 2;\n\nfunction getItemTopPosition() {\n  $headerItem.each((index, item) => {\n    let itemTopPosition = headerHalfHeight - $(item).offset().top;\n\n    console.log(itemTopPosition);\n  });\n}\n```\n\n## 2.2 Чистка обработчиков событий\n\nВ наших проектах мы часто используем роутер, поэтому создаем функции для инициализации страницы на которую переходим. В функции инициализации вешаются обработчики на элементы и часто их забывают отключать. Получается такая ситуация - перешли на страницу первый раз, повесились обработчики. Ушли со страницы и вернулись еще раз, произошла инициализация и обработчики повесились еще раз и т.д. В итоге при выполнении какого-то события оно будет отработано несколько раз. Для предотвращения такой ситуации можно сделать так:\n\n```js\nfunction initPage() {\n  $('.element')\n    .off('.some-event')\n    .on('click.some-event', () => {\n      // do smth\n    });\n}\n```\n\nИли создать функцию сброса параметров после ухода со страницы, в которой можно не только сбрасывать обработчики но и обнулять какие-то вычисления.\n\n```js\nlet currentPage = null;\nlet pageHeight = null;\n\nfunction initPage(page) {\n  let $page = $(page);\n\n  currentPage = $page;\n  pageHeight = $page.height();\n\n  $page.on('scroll.page', () => {\n    // do smth\n  });\n}\n\nfunction resetPage() {\n  currentPage.off('.page');\n\n  currentPage = null;\n  pageHeight = null;\n}\n```\n\nСитуации бывают разные, поэтому необходимо следить за тем, чтобы обработчики не дублировались, иначе двойные, тройные … n-ые вычисления могут привести к значительному снижению производительности.\n\n## 2.3 Использование throttle\n\nСуществуют ситуации когда нам необходимо делать какие-то ресурсоемкие вычисления например на движение мыши или скролл. Для того, чтобы снизить нагрузку мы можем использовать **throttle** - функция, которая позволяет выполнить какое-то действие с задержкой в заданное кол-во времени. Подробнее можно почитать [здесь](https://learn.javascript.ru/task/throttle) или “покурить” гугл. Приведу небольшой пример.\nЗдесь функция **getScrollPercentProgress** будет выполняться каждый раз при скролле как только браузер сможет обработать этот код.\n\n```js\nfunction getScrollPercentProgress() {\n  return (pageYOffset + innerHeight) / document.body.clientHeight * 100;\n}\n\nwindow.onscroll = () => {\n  console.log(getScrollPercentProgress());\n};\n```\n\nЗдесь же **getScrollPercentProgress** будет вызываться каждые 100мс, тем самым снижая кол-во выполнений. Также для уменьшения вычислений создаем сразу две переменные, которые будут хранить высоту окна и высоту документа.\n\n```js\nlet windowHeight = innerHeight;\nlet documentHeight = document.body.clientHeight;\n\nfunction getScrollPercentProgress() {\n  return (pageYOffset + windowHeight ) / documentHeight * 100;\n}\n\nwindow.onscroll = throttle(() => {\n  console.log(getScrollPercentProgress());\n}, 100);\n```\n\n## 2.4 Использование debounce\n\n**Debounce** позволяет отложить вызов функции, пока не пройдет заданный промежуток времени с момента последнего вызова, например:\n\nВ данном случае функция **rebuildSmth** будет выполняться непрерывно, пока мы меняем размер окна.\n\n```js\nfunction rebuildSmth() {\n  $('.elements').height(innerHeight);\n // здесь выполняется какой-то супер сложный код\n}\n\nwindow.onresize = rebuildSmth;\n```\n\nА здесь она выполнится один раз, через 500 мс с момента последнего изменения размера.\n\n```js\nlet windowHeight = innerHeight;\nlet $elements = $('.elements');\n\nfunction rebuildSmth() {\n  $elements.height(innerHeight);\n // здесь выполняется какой-то супер сложный код\n}\n\nwindow.onresize = debounce(rebuildSmth, 500);\n```\n\n# 3. Оптимизация CSS-кода\n\nКасаемо производительности css все намного проще. Главное, что стоит запомнить - браузер читает селекторы справа налево и порядок селекторов по производительности (от более производительных к менее):\n\n1. идентификатор (#block)\n2. класс (.block)\n3. тип (div)\n4. сосед по уровню (h1 + p)\n5. дочерний элемент (div > ul)\n6. вложенный элемент (div a)\n7. общий селектор (*)\n8. атрибут ([type=”button”])\n9. псевдоклассы/псевдоэлементы (a:hover)\n\nВыбор элементов в css влияет на производительность, в том числе, на то, как быстро отображается страница. Однако в реальном использовании разница совсем невелика и большого прироста производительности можно не ожидать.\nИспользуйте простые css-селекторы, меньше вложений и короче цепочки. Идеальный вариант - прямые селекторы только по классу (.element), благо БЭМ это позволяет. Подобное правильно также хорошо подходит для выбора дом-узла в js.\n\n**плохо**\n\n```css\ndiv ul li[data-type=\"link\"] a {\n  color: red;\n}\n\ndiv.article {\n  display: block;\n}\n\n.article li a {\n  text-decoration: none;\n}\n\n.article:last-child ul * {\n  background: none;\n}\n```\n\n**хорошо**\n\n```css\n.link {\n  color: red;\n}\n\n.article {\n  display: block;\n}\n\n.article__link {\n  text-decoration: none;\n}\n\n.article__item {\n  background: none;\n}\n```\n\n# 4. Оптимизация анимации\n\n## 4.1 Не анимируйте сложные свойства\n\nНа данный момент браузеры хорошо оптимизируют анимацию свойств **opacity** и **transform**. Анимирование остальных свойств лучше избегать или стараться использовать по минимуму, особенно это касается мобильных устройств.\n\n**плохо**\n\n```css\n.menu {\n  position: fixed;\n  left: -100%;\n  top: 0;\n  background: transparent;\n  transition: all 0.3s;\n}\n\n.menu.is-opened {\n  left: 0;\n  box-shadow: 10px 0 30px rgba(255, 255, 255, 0.3);\n  background: #fff;\n}\n```\n\n**хорошо**\n\n```css\n.menu {\n  position: fixed;\n  left: 0;\n  top: 0;\n  z-index: 1;\n  transform: translate3d(-100%, 0, 0);\n  transition: transform 0.3s;\n}\n\n.menu::before {\n  content: \"\";\n  position: absolute;\n  left: 0;\n  top: 0;\n  z-index: -1;\n  display: block;\n  width: 100%;\n  height: 100%;\n  box-shadow: 10px 0 30px rgba(0, 0, 0, 0.3);\n  background: #fff;\n  opacity: 0;\n  transition: opacity 0.3s;\n}\n\n.menu.is-opened {\n  transform: none;\n}\n\n.menu.is-opened::before {\n  opacity: 1;\n}\n```\n\nОбъем кода получается больше, однако производительность, особенно на мобильных и слабых устройствах, будет заметна. Также обратите внимание на свойство transition, всегда указывайте какие свойства элемента вам нужны для анимации, чтобы не анимировать сразу все.\n\n## 4.2 Используйте requestAnimationFrame вместо setInterval\n\n**RequestAnimationFrame (RAF)** лучше справляется с анимацией, чем интервал. Он хорошо оптимизирован браузерами и лучше экономит ресурсы, что немаловажно для мобильных и слабых устройств.\n\n**плохо**\n\n```js\nlet intervalId = setInterval(() => {\n  // код вашей анимации\n\n  if (i > 1000) {\n    clearInterval(intervalId);\n  }\n}, 1000 / 60);\n```\n\n**хорошо**\n\n```js\nlet rafId;\n\nfunction animate() {\n  rafId = requestAnimationFrame(animate);\n\n  // код вашей анимации\n\n  if (i > 1000) {\n    cancelAnimationFrame(rafId);\n  }\n}\n\nrequestAnimationFrame(animate);\n```\n\nПоворот по оси Z помогает убрать лишние дергания анимации, особенно заметно в IE и Edge при анимации scale.\n\n# 5. Другие решения\n\n## 5.1 Lazy loading (ленивая загрузка)\n\nВ интернете много пояснений о том, что такое ленивая загрузка и как ее реализовать, если коротко - это способ загрузки данных по мере надобности.\n\nНапример в нашем случае, когда мы разрабатываем SPA, содержимое сайта загружается все и сразу т.е. мы ждем пока загрузятся все картинки, видео, фреймы и т.п. (со всех страниц), а их может быть очень много и вкупе они имеют большой вес. Благодаря ленивой загрузке можно сделать так, чтобы изначально загружались ресурсы только одной страницы или только те, которые пользователь увидит изначально на экране (плюс взять небольшой запас). Далее пользователь переходит по страницам, открывает попапы, переходит по секциям и т.д., и по мере надобности подгружаются недостающие ресурсы. Т.е. смысл в том, что мы не отдаем пользователю сразу все, а только то, что ему необходимо на данный момент.\n"
  },
  {
    "path": "24_git.md",
    "content": "# Работа с Git\n\nДля работы с репозиториями мы используем [Bitbucket](https://bitbucket.org).\n\n## Работа с ветками\n\n* master - основная ветка\n* production - ветка для боевого сайта\n\nДля работы каждый разработчик должен создавать свою ветку с именем **dev-[Ваше имя]**, например dev-vasya.\n\nТ.к. у нас настроен автодеплой, то после выполнения очередной задачи свою ветку необходимо сливать с веткой **master**, чтобы изменения попали на тестовый сервер. Пушить в ветку **master** нельзя, для объединения необходимо выполнить **pull request (далее PR)** из вашей ветки в ветку мастер. PR в мастер может принимать любой разработчик.\n\nДля отправки наработок на боевой сайт (продакшн), необходимо сделать PR в ветку **production**. В данном случае запрос на объединение сможет принять только администратор репозитория.\n\nДля новичков делать PR в мастер необходимо минимум 3 раза в день. Для этого можно разбить основную задачу на 3 логические части. В остальных случаях PR нужно делать по мере необходимости или выполнения задачи.\n\n**Обязательно!** Перед началом работы выполняйте **git pull**, чтобы стянуть последние наработки по проекту.\n\n## Коммиты\n\nК коммитам строгих требований нет. Сообщения коммитов должны быть корткие и доносить основной смысл выполненного объема работ. Желательно, чтобы сообщение коммита было на английском языке.\n\n## Конфликты\n\nПри возникновении конфликтов обязательно свяжитесь с разработчиком, который вносил изменения, чтобы вместе решить конфликты и не затереть актуальные наработки.\n\nЕсли такой возможности нет, внимательно изучите конфликты и саму задачу, чтобы понять какая часть является актуальной. После разрешения конфликтов и отправки наработок в ветку, необходимо сообщить в чат проекта, менеджеру о произошедшем и попросить перепроверить актуальность изменений.\n"
  },
  {
    "path": "25_checklist.md",
    "content": "## Проверка переходов по ссылкам с передачей GET-параметров\n\nGET-параметр - параметр, который передается серверу при помощи URL. Данные параметры находятся в URL сразу после знака вопроса `?` и состоят из пары - `ключ = значение`. Несколько GET-параметров разделяются между собой знаком амперсанда `&`.\n\nПример ссылки с GET-параметрами: `https://site.com?param1=value1&param2=value2`. Здесь можно выделить два параметра - `param1`, который имеет значение `value1` и `param2`, который имеет значение `value2`.\n\nПри проверке переходов необходимо подставить любой GET-параметр (или несколько) в URL сайта, произвести переход по полученной ссылке и посмотреть не пропадут ли параметры из URL.\nНапример есть ссылка на сайт - `site.com`, добавляем к ней GET-параметры - `?test_param=value`, в результате получим подобную ссылку - `site.com?test_param=value`, производим переход и смотрим не изменился ли URL.\n\nЧасто, причиной пропажи параметров является манипуляция URL, в каком-то из участков кода. Например меняется адрес при входе на страницу при помощи `window.location`, `history.pushState`, `history.replaceState` и т.п. В таком случае необходимо обязательно предусмотреть возможность сохранения передаваемых параметров.\n\nТакже, если на сайте используются якоря `(site.com#section)`, необходимо проверять их работоспособность с передачей параметров `(site.com?test_param=value#section)`.\n\n## Проверка на соответствие макету\n\nРезультат верстки должен соответствовать макету.\n\nПроверка осуществляется с помощью расширения [PerfectPixel](https://chrome.google.com/webstore/detail/perfectpixel-by-welldonec/dkaagdgjmgdmbnecmcefdhjekcoceebi?hl=ru), в браузере Chrome под ОС Windows, Linux или Mac.\n\nДопустимы незначительные отличия, связанные с:\n- различием в рендеринге шрифтов\n- ошибками в макете (различные отступы или размеры у однотипных элементов, погрешности в цветах)\n- заменой контента (текст, изображения, видео)\n\nИные отличия недопустимы.\n\nЕсли верстка по тем или иным причинам расходится с макетом, то об этом следует сообщить менеджеру проекта.\n\n## Проверка кода линтером\n\nСледует проверять код линтером.\n\nЕсли при проверке кода линтером выявлены ошибки, то их следует исправить, либо сообщить об этом разработчику, ответственному за данный код.\n\nЕсли при работе с линтером появляются подозрения на некорреткную настройку или баги, то об этом следует сообщить разработчику, ответственному за настройку линтера ([@beliarh](https://github.com/beliarh)).\n\n## Проверка кода валидатором\n\nСледует проверять код [валидатором](https://validator.w3.org/unicorn/).\n\n## Проверка фавиконки\n\nНа сайте должна быть фавиконка.\n\nПри отсутствии фавиконки следует запросить ее у менеджера проекта.\n\n## Шрифты\n\nНа сайте должны использоваться корректные шрифты.\n\nОбязательный формат - `woff`.\nОпциональный формат - `woff2`.\n\nНедопустимо использовать `woff2`, без использования `woff`.\n\nДопустимо использовать только `woff`.\n\nКрайне желательно использовать и `woff` и `woff2`.\n\nВ используемом шрифте не должно быть битых символов.\n\n## Проверка на типографирование\n\nТекст на сайте должен быть типографирован, т.е. прогнан через [Типограф](https://www.artlebedev.ru/typograf/). Если в тексте присутствуют лишние и битые символы, от них необходимо избавиться.\n\n## Проверка выделения текста\n\nСодержимое текстовых блоков должно корректно выделяться.\n\n## Проверка иконок и изображений\n\nИконки на сайте должны быть сделаны с помощью SVG, либо PNG + @2x PNG (если иконка имеет сложные растровые эффекты, SVG отсутствует или невозможно экспортировать иконку в формате SVG).\n\nДля контентных изображений должна быть указана @2x-версия.\n\nSVG-иконки должны иметь внутренние отступы, чтобы избежать обрезанных краев в разных браузерах.\n\n## Проверка интерактивных элементов\n\nСледует проверять корректность работы интерактивных элементов:\n\n* Слайдеры\n* Всплывающие окна\n* Аудио и видеоплееры\n* Табы\n* Тесты и опросы\n* Элементы форм\n* Валидация полей\n* Отправка форм\n* Прочие элементы, обладающие сложной логикой и неуказанные в данном списке\n\n## Проверка свайпа и drag'n'drop\n\nЕсли в каком либо блоке сайта допустимо и логично реагировать на свайп или drag'n'drop, и это поведение не реализовано, то об этом следует сообщить менеджеру проекта и разработчику.\n\nСвайп можно использоват в мобильной и планшетной версии для перехода между слайдами или для постраничной навигации (если таковая имеется).\n\nDrag'n'drop можно использовать в слайдерах и элементах, поведение которых предполагает (или допускает) перетаскивание.\n\n## Проверка навигации по сайту с помощью клавиатуры\n\nНа сайте должна корректно работать навигация с помощью клавиатуры. В частности следует проверять работу следующих клавиш (и комбинаций клавиш):\n\n* Стрелки влево, вправо, вверх и вниз\n* Tab\n* Shift + Tab\n* Enter\n* Esc\n* Пробел\n* Shift + Пробел\n* Page Up\n* Page Down\n* Home\n* End\n\n## Проверка кнопок\n\nКнопки, клик по которым не ведет на другую страницу, а лишь выполняет какое-либо действие, должны быть сделаны тегом `<button>`.\n\nУ тега `<button>` должен быть указан атрибут `type`.\n\n## Проверка ссылок\n\nСсылки - интерактивные элементы, при клике на которые происходит переход на другую страницу или внешний ресурс.\n\nСсылки-якоря - интерактивные элементы, при клике на которые происходит скролл к нужному месту страницы.\n\nВсе ссылки должны быть сделаны тегом `<a>`.\n\nУ всех ссылок должен быть указан атрибут `href`.\n\nУ внутренних ссылок атрибут `href` должен начинаться с `/`.\n\nУ внешних ссылок должен быть указан атрибут `target=\"_blank\"`.\n\nУ ссылок-якорей в атрибуте `href` должен быть указан хеш.\n\nСсылки должны быть корректными и вести на соответствующие страницы.\n\n## Проверка кликабельной области элементов\n\nЕсли у кнопки или отдельностоящей ссылки нет явно ограниченной кликабельной области, то за такую следует принять размер элемента плюс поля 5-15px.\n\nКликабельная область должна иметь размер минимум 20-30px по высоте и ширине (если нет явной границы).\n\nСодержимое кнопки или ссылки должно быть по центру кликабельной области.\n\nДля ссылок в тексте размер кликабельной области не контролируется.\n\n## Проверка анимаций\n\nЛюбая анимация на сайте (переходы, смена слайдов, ховеры) должна работать плавно, без рывков и мерцаний.\n\nНе должно быть зависаний или долгих пауз во время анимации.\n\nАнимация однотипных элементов должна быть одинаковой на всех блоках и страницах.\n\nНа всех интерактивных элементах должен быть плавный ховер.\n\n## Проверка верстки стресстестом\n\n**TODO**\n\n## Проверка сайта с включенным блокировщиком рекламы\n\nСайт должен проверяться с включенным блокировщиком рекламы.\n\nЭлементы сайта не должны блокироваться.\n\n## Проверка переходов на страницы по прямой ссылке\n\nСледует проверять корректность загрузки всех страниц сайта.\n\nТакже следует проверять корректность загрузки страницы с дополнительными GET-параметрами и хешем.\nЗаданные GET-параметры и хеш при этом не должны пропадать.\n\n## Проверка переходов между страницами\n\nСледует проверять корректность перехода между всеми страницами сайта.\n\nНапример, если на сайте несколько страниц:\n\n* `/a`\n* `/b`\n* `/c`\n\nТо следует проверять переходы:\n\n* `/a` => `/a`\n* `/a` => `/b`\n* `/a` => `/c`\n* `/b` => `/a`\n* `/b` => `/b`\n* `/b` => `/c`\n* `/c` => `/a`\n* `/c` => `/b`\n* `/c` => `/c`\n\nЕсли какой-либо переход невозможен, то его следует игнорировать.\n\nТакже следует проверять корректность перехода по несуществующему адресу.\nНа сайте должна быть предусмотрена страница с 404 ошибкой, либо должен срабатывать редирект на главную.\n\n## Проверка навигации с помощью истории браузера\n\nНа сайте должны корректно работать переходы между страницами с помощью истории браузера (стрелки влево и вправо рядом с адресной строкой).\n\n## Кроссбраузерность и кроссплатформенность\n\nНа проектах поддерживаются последние версии браузеров (если не указано иного).\n\nСайт следует проверять в следующих системах:\n\n* Windows\n    * Chrome\n    * Firefox\n    * Edge\n    * IE 11\n    * Yandex (русскоязычный сегмент)\n    * Opera (русскоязычный сегмент)\n* macOS\n    * Safari\n    * Chrome\n* Android\n    * Chrome\n* iOS\n    * Safari\n    * Chrome\n\n## Проверка адаптивности (десктоп)\n\nОсновные размеры мониторов:\n\n* 2560x1440\n* 2560x1080\n* 1920x1200\n* 1920x1080\n* 1650x1050\n* 1600x900\n* 1440x900\n* 1366x768\n* 1280x1024\n* 1280x720\n\nПри проверке адаптивности следует проверять не только указанные размеры, но также и промежуточные.\nПричина в том, что размер окна сайта не равен разрешению монитора (за исключением полноэкранного режима).\nЧасть пространства занимает системная панель, часть - верхняя панель браузера.\nК тому же иногда пользователи открывают окно браузера не на весь экран.\n\nПроверка осуществляется в панели разработчика в режиме `Responsive` и начинается с наибольшего экрана (2560x1440).\n\nМинимальные размеры до которых стоит проверять адаптивность десктопной версии - 1025px по ширине, и 550px по высоте.\n\nПроверка адаптивности проходит по следующему алгоритму:\n\n1. Задается размер окна 2560x1440.\n2. Путем перетаскивания и постепенного уменьшения высоты (до минимальной - 550px) проверяется корректность отображения сайта.\n3. Далее ширина окна немного уменьшается (на 25-50px).\n4. Пункты 2 и 3 повторяются до тех пор, пока ширина окна не достигнет минимальной - 1025px.\n\n## Проверка адаптивности (планшеты и мобильные устройства)\n\nДля начала сайт нужно проверить в мобильном эмуляторе браузера.\n\nКак и в случае с десктопной версией проверяются не только размеры, соответствующие разрешению экрана, но также и промежуточные.\n\nМинимальная ширина экрана до которой следует проверять адаптивность - 320px.\n\nПроверять следует как портретную, так и альбомную ориентацию.\n\nВ альбомной ориентации размер элементов не должен сильно отличаться от портретной.\nНе должно быть такого, что в альбомной ориентации элементы слишком мелкие или большие.\n\nОбязательно проверять сайт на реальных устройствах (либо в [BrowserStack](https://www.browserstack.com/)):\n\n* Android\n    * Любое мобильное устройство и планшет с актуальной версией системы (6+)\n        * Chrome\n* iOS\n    * iPhone SE/7/8/X и iPad Mini/Air/Pro\n        * Safari\n        * Chrome\n\nПри проверке сайта на реальных устройствах страница должна корректно отображаться и скроллиться.\n\n## Проверка работоспособности сайта при ресайзе\n\nНеобходимо проверять и дорабатывать логику сайта при ресайзе, чтобы сайт при изменении размеров окна не ломался.\n\nНапример на сайте имеется слайдер, который отображается на мобильной версии, но при этом на десктопе отсутствует, либо отключен. В данном случае необходимо предусмотреть логику создания / удаления (отключения) слайдера при переходе с мобильной версии на десктоп и обратно, по ресайзу, без перезагрузки сайта.\nВ качестве дополнительных примеров можно взять уникальные случаи - `\"кирпичная\" сетка, параллакс элементов, анимация движения элементов по скроллу` и т.д. Подобные вещи часто \"ломаются\" именно при изменении размеров окна, поэтому нужно не забывать предусматривать корректировку логики по ресайзу.\n\nДля чего это необходимо:\n* исключается некорректное отображение сайта при изменении ориентации на устройствах\n* исключается некорректное отображение сайта при масштабировании или случайном изменении размеров окна браузера\n* удобство при тестировании, нет необходимости перезагружать сайт\n\nДля оптимизации нагрузки, всю логику выполнения по ресайзу можно делать с задержкой. Например изменился размер окна, а все корректировки логики выполнились только спустя 1 секунду. В этом могут помочь техники `throttling` и `debouncing`.\n\nТакже необходимо следить за тем, чтобы ресайз не препятствовал отображению сайта, например на мобильных устройствах при скролле скрываются / открываются навигационные панели браузера, это вызывает событие ресайза. В этом случае на сайте могут происходить корректировки размеров, положения и т.д. чего-либо, однако, это может быть лишним т.к. данные корретировки необходимы будут только при изменении ширины окна, а не высоты.\n\n## Проверка метатегов\n\nНа сайте должны быть указаны метатеги (`title`, `description`, `image`).\n\nЕсли сайт многостраничный, то метатеги должны быть указаны на каждой странице.\n\nЕсли сайт одностраничный и использует `shareSettings.php` для шаринга, то метатеги должны быть указаны именно в этом файле. При этом необходимо подставить переменные в pug-файле.\n\n## Проверка шеринга\n\nНа сайте должен работать шеринг (при наличии).\n\nЕсли сайт многостраничный, то должна шариться ссылка на страницу с основными параметрами (при наличии таковых).\n\nЕсли сайт многостраничный, то должна шариться ссылка на `share.php` с требуемыми параметрами.\n\nПри шаринге в ссылку не должны попасть лишние параметры (например, UTM-метки).\n\nПроверка и отладка шаринга осуществляется с помощью следующих инструментов:\n\n* https://developers.facebook.com/tools/debug/\n* https://cards-dev.twitter.com/validator\n* https://vk.com/dev/pages.clearCache\n* https://search.google.com/structured-data/testing-tool\n* https://telegram.me/webpagebot\n\nПри шаринге сайта в соц. сети должны корректно отобразиться:\n\n* Заголовок (соответствующий заданному метатегу, необрезанный, без искажений кодировки и битых символов)\n* Описание (соответствующее заданному метатегу, необрезанное, без искажений кодировки и битых символов)\n* Изображение (соответствующее заданному метатегу)\n\nСсылка шаринга должна вести на ту же страницу, которую шарили, либо на страницу, соответствующую заданной логике.\nВ случае использования `share.php` ссылка должна перенаправлять на нужную страницу.\n\nЕсли расшариваемая страница недоступна по прямой ссылке (т.е. переход на нее происходит по внутренней логике сайта), то следует шарить главную страницу.\n\n## Проверка аналитики\n\nДля проверки аналитики используется расширение [Google Analytics Debugger](https://chrome.google.com/webstore/detail/google-analytics-debugger/jnkmfdileelhofjcijamephohjechhna).\n\nПосле установки в панели расширений появится иконка GA Debug (в виде письма).\n\nНа проверяемом сайте следует открыть панель разработчика и включить GA Debug. На иконке появится надпись `ON`.\n\nПосле чего страница перезагрузится и в консоли появится множество сообщений от расширения.\n\nИзбавиться от лишних сообщений можно, если вписать в поле `Filter` консоли значение `Running command` (все сообщения от данного расширения помещается данным префиксом).\nПри фильтрации сообщений консоли следует быть внимательным, так как фильтруются абсолютно все сообщения, в том числе ошибки и собственные вызовы `console.log`.\n\nПосле этого следует поочередно проверить указанные в ТЗ события.\n\n## Проверка контента\n\nНа сайте должен быть актуальный контент:\n\n* Текст\n* Ссылки\n* Изображения\n* Видео\n* Аудио\n\nЗапросить актуальный контент можно у менеджера проекта.\n\nПри наличии ошибок в тексте (орфографических, пунктуационных, логических) следует сообщить об этом менеджеру проекта.\n\nПри наличии недоступных ресурсов (недоступные изображения, видео или аудио, 404 ошибки) следует сообщить об этом менеджеру проекта.\n\nПри выявлении несоответствия контента (перепутан текст, изображение, видео или аудио) следует сообщить об этом менеджеру проекта.\n\n## Проверка размера загружаемых ресурсов\n\nНа сайте не должно быть чрезмерно больших файлов.\n\nРазмер изображений должен соответствовать размеру на сайте (например, для элемента размером 300x300 не должно использоваться изображение размером 2000x2000).\n\nНе должно быть мегабайтных JPG. Такие файлы следует оптимизировать вручную с ограничением максимального качества.\n\nРазмер видео не должен быть больше 100 мегабайт (за исключением продолжительных видео и адаптивного стриминга).\n\n## Проверка производительности сайта\n\n**TODO**\n"
  },
  {
    "path": "26_short-checklist.md",
    "content": "1. Переходы по ссылкам с передачей GET-параметров.\n2. Соответствие макету.\n3. Проверка кода линтером.\n4. Проверка всех страниц сайта HTML-валидатором (https://html5.validator.nu/).\n5. Имеется фавиконка.\n6. Корректные шрифты (woff + woff2).\n7. Типографированный текст. Отсутствуют битые символы.\n8. Текст на сайте корректно выделяется.\n9. Корректные svg-иконки.\n10. Интерактивные элементы работают корректно (слайдеры, всплывающие окна, аудио и видеоплееры, табы, тесты, опросы, формы, выпадающие списки и прочее).\n11. В формах реализована валидация полей.\n12. Если на сайте присутствует кастомный скролл, то навигация с помощью клавиатуры должна работать корректно (Space, Shift + Space, Page Up, Page Down, Home, End, стрелки)\n13. Кликабельные элементы, не ведущие на другие страницы, сделаны кнопками (`<button type=\"button\">`).\n14. Кликабельные элементы, ведущие на другие страницы (за исключением кнопок для отправки форм), сделаны ссылками с корректным href (`<a>`).\n15. У всех кликабельных элементов достаточная кликабельная область.\n16. Анимации на сайте работают корректно, плавно, без задержек и подвисаний.\n17. На всех интерактивных элементах должен быть ховер.\n18. Стресстест верстки (в местах с динамическим контентом прописать много текста с очень длинными словами и проверить не ломается ли верстка, также проверить не ломается ли при малом количестве контента).\n19. Сайт не ломается блокировщиком рекламы.\n20. Страницы открываются по прямой ссылке (для SPA).\n21. Переходы между страницами работают корректно (для SPA).\n22. Навигация с помощью истории браузера работает корректно (для SPA).\n23. Проверка сайта в Windows + Chrome.\n24. Проверка сайта в Windows + Firefox.\n25. Проверка сайта в Windows + Edge.\n26. Проверка сайта в Windows + IE 11.\n27. Проверка сайта в Windows + Yandex. (русскоязычный сегмент)\n28. Проверка сайта в Windows + Opera. (русскоязычный сегмент)\n29. Проверка сайта в macOS + Chrome.\n30. Проверка сайта в macOS + Safari.\n31. Проверка сайта в Android + Chrome (планшет).\n32. Проверка сайта в iOS + Safari (планшет).\n33. Проверка сайта в Android + Chrome (мобильное устройство).\n34. Проверка сайта в iOS + Safari (мобильное устройство).\n35. Сайт корректно адаптируется в десктопной версии (от 1025x550 до 2560x1440).\n36. Сайт корректно адаптируется в мобильной и планшетной версии (от 320 до 1024).\n37. Сайт корректно отображается в альбомной ориентации (мобильная и планшетная версия).\n38. Сайт не ломается при ресайзе окна. По нeoбxoдимocти происходит переинициализация логики сайта при смене десктоп - мобайл и наоборт (мобайл - десктоп).\n39. Указаны метатеги title, description и image (при наличии).\n40. Корректная работа шеринга. Сброс кэша (если нужно):\n- https://developers.facebook.com/tools/debug/\n- https://cards-dev.twitter.com/validator\n- https://vk.com/dev/pages.clearCache\n- https://search.google.com/structured-data/testing-tool\n- https://telegram.me/webpagebot\n41. Корректная работа аналитики.\n42. Актуальный контент (текста, ссылки, картинки, видео, аудио).\n"
  },
  {
    "path": "27_validation.md",
    "content": "# Базовая валидация форм\n\nРассмотрим требования только к часто встречающимся полям. Валидация остальных полей выполняется индивидуально.\n\n### поле для ввода ФИО или только имени:\n\n* ограничение минимального кол-ва символов\n* ограничение максимального кол-ва символов\n* ввод только букв и пробелов\n* удаление пробелов в начале и конце строки перед отправкой\n\n### поле для ввода email:\n\n* удаление пробелов в начале и конце строки перед отправкой\n* маска ввода\n\n```regex\n^([a-z0-9_-]+\\.)*[a-z0-9_-]+@[a-z0-9_-]+(\\.[a-z0-9_-]+)*\\.[a-z]{2,6}$\n```\n\n```regex\n^([A-Za-z0-9_\\-\\.])+\\@([A-Za-z0-9_\\-\\.])+\\.[A-Za-z]\n```\n\n### поле для ввода телефона:\n\n* ввод только цифр и некоторых символов (плюс, пробел, тире, круглые скобки)\n* удаление пробелов в начале и конце строки перед отправкой\n* маска ввода\n\n```regex\n^((8|\\+7)[\\- ]?)?(\\(?\\d{3}\\)?[\\- ]?)?[\\d\\- ]{7,10}$\n```\n\n```regex\n/^(\\s*)?(\\+)?([- _():=+]?\\d[- _():=+]?){10,14}(\\s*)?$/\n```\n\n---\n\nТребования к этим полям также могут меняться на разных проектах.\n\nРегулярные выражения даны для примера, вы можете использовать свои.\n"
  },
  {
    "path": "28_dynamic-share-for-spa.md",
    "content": "# Настройка динамических шерингов для SPA\n\nС недавнего времени в сборку был добавлен новый файл `shareSettings.php`, в папку `resources`. В нем необходимо указывать все данные по шерингам. Во избежание ошибок при сборке, данный файл нельзя удалять из проекта. Он автоматически удаляется в конечном билде.\n\nТеперь нет необходимости добавлять логику с корректировкой URL для `share.js`, создавать отдельный файл `share.php` и в нескольких местах менять данные шеринга. Достаточно один раз прописать все данные в `shareSettings.php`.\n\n### базовый код файла shareSettings.php:\n\n```php\n$protocol = $_SERVER['PROTOCOL'] = isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) ? 'https' : 'http';\n$host = $protocol . '://' . $_SERVER['HTTP_HOST'];\n$title = '';\n$description = '';\n$image = $host . '/images/';\n\n// Uncomment the code below and fill in the pages if necessary\n// $pages = [\n//     '/page/1' => [\n//         'title' => '',\n//         'description' => '',\n//         'image' => '/images/',\n//     ],\n// ];\n\n$page = @$pages[$_SERVER['REQUEST_URI']];\n\nif ($page) {\n    $title = !is_null(@$page['title']) ? $page['title'] : $title;\n    $description = !is_null(@$page['description']) ? $page['description'] : $description;\n    $image = !is_null(@$page['image']) ? $host . $page['image'] : $image;\n}\n```\n\n### пример кода с указанными данными для шеринга:\n\n```php\n$protocol = $_SERVER['PROTOCOL'] = isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) ? 'https' : 'http';\n$host = $protocol . '://' . $_SERVER['HTTP_HOST'];\n$title = 'Базовый заголовок страницы';\n$description = 'Базовое описание страницы';\n$image = $host . '/images/share/main.jpg';\n\n$pages = [\n    '/article' => [\n        'title' => 'Заголовок статьи',\n        'description' => 'Описание статьи',\n        'image' => '/images/share/article.jpg',\n    ],\n    '/test' => [\n        'title' => 'Заголовок теста',\n        'description' => 'Описание теста',\n        'image' => '/images/share/test.jpg',\n    ],\n    '/test?result=1' => [\n        'title' => 'Заголовок результатов теста №1',\n        'description' => 'Описание результатов теста №1',\n        'image' => '/images/share/test.jpg',\n    ],\n    '/test?result=2' => [\n        'title' => 'Заголовок результатов теста №2',\n        'description' => 'Описание результатов теста №2',\n        'image' => '/images/share/test.jpg',\n    ],\n    '/product' => [\n        'title' => 'Заголовок продуктовой страницы',\n        'description' => 'Описание продуктовой страницы',\n        'image' => '/images/share/product.jpg',\n    ],\n];\n\n$page = @$pages[$_SERVER['REQUEST_URI']];\n\nif ($page) {\n    $title = !is_null(@$page['title']) ? $page['title'] : $title;\n    $description = !is_null(@$page['description']) ? $page['description'] : $description;\n    $image = !is_null(@$page['image']) ? $host . $page['image'] : $image;\n}\n```\n\n---\n\nЧтобы все данные из этого файла попали в разметку страницы при входе, необходимо обязательно внести небольшие изменения в корневой файл `index.pug`.\n\n### пример кода для index.pug:\n\n```jade\nextends pug/base\n\nprepend vars\n    - title = '<?= htmlspecialchars($title) ?>'\n    - description = '<?= htmlspecialchars($description) ?>'\n    - image = '<?= htmlspecialchars($image) ?>'\n\nappend vars\n    //- ...\n\nblock content\n    //- ...\n```\n\nТеперь на локальном сервере во вкладке будет отображаться подобное - `<?= htmlspecialchars($title) ?>`. Не стоит пугаться, корректный заголовок (а также description и image) будут работать на тестовом, а также боевом серверах.\n\nЭто происходит по той причине, что `index.html`, который создается в результате обычного билда, просто не умеет читать php-код. А на сервер вместо `index.html` будет добавляться `index.php`, который прекрасно распознает все эти переменные.\n\n---\n\nТакже в папке `resources` теперь хранится файл `.htaccess`, который также нельзя удалять и для него тоже настроено автоудаление, если сборка не запущена в режиме SPA. Этот файл содержит настройки для корректной работы сайта в режиме SPA.\n"
  },
  {
    "path": "29_helpers.md",
    "content": "# Всппомогательные функции\n\nДля облегчения некоторой части базовых настроек в сборку добавленны вспомогательные функций для js и scss.\n\nИх использование не обяхательно, но оно облегчит некоторую часть работы.\n\n## Вспомогательные функции PUG\n\n#### Миксины `lazyElements`\n\n\n#### Миксин `svg`\n\nПодгрузка свг элемента из файла спрайтов\n\n## Вспомогательные функции JS\n\n- `lastPageYOffset` - содержит позицию скролла после использования `saveScrollPosition`.\n\n- `debounce` - полезен для функций, которые получают/обновляют данные, и мы знаем, что повторный вызов в течение короткого промежутка времени не даст ничего нового, синтаксис похож на `setTimeout`.\n\n- `saveScrollPosition` - полезен при открытии модальных окон, используется для сохранения текущей позиции горизонтального скролла.\n\n- `restoreScrollPosition` - полезен при закрытии модальных окон, используется для сброса сохраненной позиции горизонтального скролла.\n\n- `scrollTo` - плавный скролл до элемента, принимает 3 параметра, элемент до которого нужно скроллить / время анимации / смещение (+/-).\n\n- `getScrollbarWidth` - полез при открытии модальных окон, чтобы контент не прыгал, возвращает ширину скролл-бара.\n\n- `hasHoverSupport` - определяет доступность ховера, на различных устройствах, помогает избежать ховеров на мобилках и планшетах.\n\n- `actualYear` - определяет актуальных год (в основном нужно для копирайтов в футере)\n\n- `backToTop` - Кнопка возврат наверх, есть возможность показывать и скрывать эту кнопку через класс\n\n- `counter` - Счетчик значений больше/меньше + ввод через input\n\n- `dropEye` - Переключатель глаза и показ символов в поле ввода пароля\n\n- `lazyLoading` - Отложенная загрузка изображений + тригер для ручного вызова\n\n- `numberFormat` - Форматирование числа, добавляет тысячный разделитель\n\n- `scrollToAnchor` - Плавный переход к якорю\n\n- `transchoice` - Плюрализация (множественность) текста\n\nОстальные переменные добавленны по мере популярнисти использования на проектах, для каждого проекта этот список можно индивидуально донастроить\n\n## Вспомогательные функции SCSS\n\n- `max / min` - вспомогательные функции для `supports-safe-area-insets`\n\n- `hover / active-hover / active / disabled` - вспомогательные миксины для корректного доступа к ховеру, так же небольшие хелперы для активного/неактивного состояния элементов и ховера при активном состоянии\n\n#### Миксин `supports-safe-area-insets`\n\nВспомогательный миксин для использования всего пространства экрана на iOs устройствах начиная с iPhone X\n\nСейчас на большинстве сайтов можно наблюдать такую картину\n\n![safe-area error](images/29/safe-areas-error.png)\n\nВизуально выглядит это плохо и для решения таких проблем появилась возмоность использовать переменные среды агента пользователя (safe-area), которые выглядит следующим образом\n\n![safe-area true](images/29/safe-areas.png)\n\nНачнём сначала, для правильности работы в шапке сайта нужно указывать специальный viewport.\n\n```html\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, viewport-fit=cover\">\n```\n\n##### Пример использования safe-area\n```scss\n    body {\n        padding: 12px;\n\n        @include supports-safe-area-insets {\n            body {\n                padding-top: max(12px, env(safe-area-inset-top))\n                padding-right: max(12px, env(safe-area-inset-right))\n                padding-bottom: max(12px, env(safe-area-inset-bottom))\n                padding-left: max(12px, env(safe-area-inset-left));\n            }\n        }\n    }\n```\n\nРазумеется любое из этих 4-х значений можно использовать по отдельности и с любым css свойством, главное знать и понимать когда конкретно это необходимо.\nВ итоге, при правильном использовании safe-area мы получаем такую картину.\n\n![safe-area success](images/29/safe-areas-success.png)\n\n#### Миксин `text-border`\n\n`text-border($color, $borderColor, $ieColor, $width: 1px, $ieWidth: 1px)` - Вспомогательный миксин для корректного использования окантовки у текста, предусмотрен полифилл для старых IE.\n\nПервые 3 свойства обязательны для заполнения, `$color`: цвет текста в основной массе браузеров, `$borderColor` - цвет окантовки для всех браузеров, `$ieColor` - цвет текста для IE, так в нём недоступно свойство `transparent`, остальные свойства используются редко, поэтому в них добавленны базовые значения\n\n##### Пример использования text-border\n```scss\n    @include text-border(transparent, #000, #fff);\n```\n\n#### Миксин `font-face`\n\n`font-face($url, $font-family, $font-weight, $font-style)` - Вспомогательный миксин для задания шрифтов, все поля обязательны для заполнения\n\n##### Пример использования font-face\n```scss\n    @include font-face(\"../fonts/GraphikRBCLC/GraphikRBCLC-Regular\", \"GraphikRBCLC\", 400, normal);\n```\n\n#### Миксин `retina`\n\nВспомогательный миксин для добавления `background-images` для дисплеев с 2х экранами\n\n#### Миксин `placeholder`\n\nВспомогательный миксин для стилизации подсказок у полей ввода\n\n#### Миксины в папке `libs`\n\nВспомогательные функции для работы некоторых других функций, так же их можно использовать любом другом месте\n\n#### Миксин `image-rendering`\n\nКачество рендеринга изображений, при использовании `background-size`\n\n#### Миксин `svg-to-data-url`\n\nПреобразует SVG в URL-адрес данных, чтобы этот SVG можно было использовать в качестве фонового изображения\n\n#### Миксин `object-fit`\n\nМиксин добавляет `object-fit` и надстройки для полифилов, так же можно задать позицию\n\n#### Миксин `triangle`\n\nМиксин создаёт треугольник в любую из сторон, можно менять цвет, подходит для кнопок плей\n"
  },
  {
    "path": "README.md",
    "content": "# Оглавление\n\n* [Основные возможности и используемые технологии](01_technologies.md)\n* [Минимальные требования](02_requirements.md)\n* [Начало работы](03_installation.md)\n* [Gulp-задачи](04_tasks.md)\n* [Структура папок и файлов](05_structure.md)\n* [Подключение сторонних библиотек](06_libraries.md)\n* [Работа с изображениями](07_images.md)\n  * [Работа с PNG-спрайтами](07_images.md#Работа-с-png-спрайтами)\n  * [Работа с SVG-спрайтами](07_images.md#Работа-с-svg-спрайтами)\n  * [Избавляемся от обрезанных краев SVG-иконок](07_images.md#избавляемся-от-обрезанных-краев-svg-иконок)\n* [Работа с шаблонизатором Pug](08_templates.md)\n* [Работа со стилями](09_styles.md)\n* [Работа со скриптами](10_scripts.md)\n* [Работа с дополнительными ресурсами](11_resources.md)\n  * [Работа со шрифтами](11_resources.md#Работа-со-шрифтами)\n* [Pixel-perfect или верстка в соответствии с макетом](13_pixel-perfect.md)\n* [Работа с метатегами](15_metatags.md)\n  * [Базовые метатеги](15_metatags.md#Базовые-метатеги)\n  * [Метатеги Apple](15_metatags.md#Метатеги-apple)\n  * [Метатеги Microsoft](15_metatags.md#Метатеги-microsoft)\n  * [Метатеги Open Graph](15_metatags.md#метатеги-open-graph)\n  * [Метатеги Twitter](15_metatags.md#Метатеги-twitter)\n* [Использование БЭМ методологии](21_bem.md)\n* [Оформление Pug-кода](16_codestyle-pug.md)\n* [Оформление SCSS-кода](17_codestyle-scss.md)\n* [Оформление JavaScript-кода](18_codestyle-javascript.md)\n* [Кроссбраузерность и адаптивность](22_crossbrowser-adaptive.md)\n* Дополнительная информация\n  * [Работа с video.js](19_video-js.md)\n  * [Работа с HLS](20_hls.md)\n  * [Базовая валидация форм](27_validation.md)\n  * [Производительность](23_perfomance.md)\n  * [Работа с Git](24_git.md)\n  * [Настройка динамических шерингов для SPA](28_dynamic-share-for-spa.md)\n  * [Вспомогательные функции и миксины для упрощения работы](29_helpers.md)\n  * [Подробный чеклист по тестированию сайтов](25_checklist.md)\n  * [Короткий чеклист по тестированию сайтов](26_short-checklist.md)\n"
  }
]