<rss version="2.0" xmlns:yandex="http://news.yandex.ru" xmlns:media="http://search.yahoo.com/mrss/" xmlns:turbo="http://turbo.yandex.ru" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Михаил Юдин</title><link>https://mikhail-yudin.ru/blog/frontend/</link><description/><generator>Hugo -- gohugo.io</generator><language>ru</language><lastBuildDate>Tue, 01 Jun 2021 00:00:00 +0000</lastBuildDate><image><url>https://mikhail-yudin.ru/images/brand/favicon.png</url><title>Михаил Юдин</title><link>https://mikhail-yudin.ru/</link></image><atom:link href="https://mikhail-yudin.ru/blog/frontend/index.xml" rel="self" type="application/rss+xml"/><item turbo="true"><title>Parsee - простой вкусный парсер сайтов</title><link>https://mikhail-yudin.ru/blog/frontend/parsee_prostoi_vkusniy_parser_saitov/</link><pubDate>Tue, 01 Jun 2021 00:00:00 +0000</pubDate><guid>https://mikhail-yudin.ru/blog/frontend/parsee_prostoi_vkusniy_parser_saitov/</guid><description>Если лень писать кучу кода, разбираться в написании парсеров, а нужно лишь получить например фото и заголовки страниц, пользуйте Parsee!
Что такое Parsee Parsee это в первую очередь парсер страниц. Пишешь ему путь к нужному контенту, он его достаёт.
Предпосылки создания Многие наверное пользуются Scrapy, Selenium и другими библиотеками или фреймворками для парсинга сайтов. Я пошёл сразу этим путём, когда встала задача найти где-нибудь логотипов.
В Scrapy например создаёшь проект, потом паучка (всё в 2 шага на автомате), потом пишешь логику парсинга.</description><turbo:content><![CDATA[
      		<header>
      			<h1>Parsee - простой вкусный парсер сайтов</h1></header>
            <div class='entry-content'>
              <p>Если лень писать кучу кода, разбираться в написании парсеров, а нужно лишь получить например фото и заголовки страниц, пользуйте <a href="https://github.com/fagci/parsee" target=_blank rel="noopener noreferrer">Parsee</a>!</p>
<h2 id="что-такое-parsee">Что такое Parsee</h2>
<p>Parsee это в первую очередь парсер страниц. Пишешь ему путь к нужному контенту, он его достаёт.</p>
<h2 id="предпосылки-создания">Предпосылки создания</h2>
<p>Многие наверное пользуются Scrapy, Selenium и другими библиотеками или фреймворками для парсинга сайтов. Я пошёл сразу этим путём, когда встала задача найти где-нибудь логотипов.</p>
<p>В Scrapy например создаёшь проект, потом паучка (всё в 2 шага на автомате), потом пишешь логику парсинга. Один паучок, второй, третий&hellip; А ещё разбираться как парсить разнородные куски, понимать как это всё сливается в единое целое&hellip; Как-то долго и непродуктивно получалось.</p>
<p>Перешёл на чистый requests + beautifulsoup4. Почти то же самое, только лишаемся многопоточности (писать каждый раз тоже время). Ещё эти повторяющиеся куски кода для обхода дерева&hellip;</p>
<p>И тут я подумал: есть же селекторы, неужели нельзя просто сказать &ldquo;найди это там-то&rdquo; и всё. Так заложилось начало проекта Parsee.</p>
<h2 id="этапы-создания">Этапы создания</h2>
<p>Сперва шёл по пути упрощения реализации простых действий, например обход узлов.</p>
<p>Получилось такое (собачка появилась позже):</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-python" data-lang="python"><span style="color:#ff79c6">for</span> page <span style="color:#ff79c6">in</span> parser <span style="color:#ff79c6">/</span> <span style="color:#f1fa8c">&#39;.links a@&#39;</span>:
    <span style="color:#ff79c6">for</span> p <span style="color:#ff79c6">in</span> page <span style="color:#ff79c6">/</span> <span style="color:#f1fa8c">&#39;h3:-soup-contains(&#34;Some title&#34;)+p&#39;</span>:
        <span style="color:#ff79c6">print</span>(p<span style="color:#ff79c6">.</span>text)
</code></pre></div><p>Потом понял, что и переход по страницам можно делать проще. И тут решил пойти по пути расширения синтаксиса селекторов.</p>
<p>Значок собачки &ldquo;@&rdquo; был выбран потому что в селекторах beautifulsoup4 этого нет, а сам значок чем-то напоминает портал =)</p>
<p>Затем понял, что можно весь обход записать в одну строку. Получилось следующее:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-sh" data-lang="sh">./parser.py http://site.org <span style="color:#f1fa8c">&#39;a@a@title&#39;</span>
</code></pre></div><p>Было неплохо, но на этом этапе произошло 2 плохие вещи:</p>
<ul>
<li>потерял возможность получать несколько элементов</li>
<li>для получения содержимого нужно было продолжать кодить =)</li>
</ul>
<p>В голову пришла идея, как сделать форматирование вывода. За пример взял утилиту jq. Она позволяет трансформировать JSON в разные форматы. Для Parsee в качестве разделителя выбрал знак процента.
Его логический смысл с натяжкой: взять часть, преобразовать.</p>
<p>И тут понеслось: после процента творил что хотел. В итоге получилась поддержка python кода для конечных узлов.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-sh" data-lang="sh">./parser.py http://site.org <span style="color:#f1fa8c">&#39;a@a@title%.text.strip()&#39;</span>
</code></pre></div><p>Точка это конечный узел DOM дерева.</p>
<h2 id="итоги">Итоги</h2>
<p>Проект <a href="https://github.com/fagci/parsee" target=_blank rel="noopener noreferrer">Parsee на GitHub</a>.</p>
<p>Пока на будущее стоят такие задачи:</p>
<ul>
<li>многопоточный обход страниц</li>
<li>обращение к узлам предыдущих страниц для их вывода</li>
<li>поддержка JavaScript</li>
</ul>

            </div>
      	]]></turbo:content></item><item turbo="true"><title>Чеклист оптимизации скорости загрузки веб-приложения ✓</title><link>https://mikhail-yudin.ru/blog/frontend/checklist-optimizacii-web-prilozheniya/</link><pubDate>Thu, 06 Aug 2020 00:00:00 +0000</pubDate><guid>https://mikhail-yudin.ru/blog/frontend/checklist-optimizacii-web-prilozheniya/</guid><description>Оставлю здесь список методов, способов повышения производительности веб-сервисов, веб-приложений и сайтов. Будет полезно мне при выкатывании очередного проекта в продакшен, а также всем заинтересованным в отзывчивых веб-приложениях и веб-интерфейсах.
Серверная часть На серверной стороне и на бэкэнде в целом улучшается параметр Time To First Byte (TTFB). Этот параметр показывает время от начала запроса до получения первого байта данных от сервера.
На начальном этапе время тратится на:
задержку сети; получение IP от DNS сервера; установку соединения.</description><enclosure url="https://mikhail-yudin.ru/blog/frontend/checklist-optimizacii-web-prilozheniya/2020-08-06-checklist-optimizacii-web-prilozheniya.jpg" type="image/jpeg"/><turbo:content><![CDATA[
      		<header>
      			<h1>Чеклист оптимизации скорости загрузки веб-приложения ✓</h1></header>
            <div class='entry-content'>
              <p>Оставлю здесь список методов, способов повышения производительности веб-сервисов, веб-приложений и сайтов.
Будет полезно мне при выкатывании очередного проекта в продакшен,
а также всем заинтересованным в отзывчивых веб-приложениях и веб-интерфейсах.</p>
<p><img src="/photos/timings_simple.png" alt="Тайминги загрузки страницы"></p>
<h2 id="серверная-часть">Серверная часть</h2>
<p>На серверной стороне и на бэкэнде в целом улучшается параметр <a href="https://developer.mozilla.org/ru/docs/%D0%A1%D0%BB%D0%BE%D0%B2%D0%B0%D1%80%D1%8C/time_to_first_byte" target=_blank rel="noopener noreferrer">Time To First Byte (TTFB)</a>.
Этот параметр показывает время от начала запроса до получения первого байта данных от сервера.</p>
<p>На начальном этапе время тратится на:</p>
<ul>
<li>задержку сети;</li>
<li>получение IP от DNS сервера;</li>
<li>установку соединения.</li>
</ul>
<p>TTFB складывается из времени на:</p>
<ul>
<li>передачу пакета данных запроса;</li>
<li>обработку запроса веб-приложением;</li>
<li>генерацию ответа (добавление заголовков, сжатие).</li>
</ul>
<p>Больше всего этот показатель влияет на скорость отклика на действие пользователя.
Как пример — переход на другую страницу, отправка формы, оформление заказа.
Правда некоторые моменты решаются и спиннерами, но это скорее хак (вынужденная мера) =)</p>
<p>Уменьшить это время можно несколькими способами:</p>
<ul>
<li>обеспечить высокую пропускную способность сети;</li>
<li>предусмотреть балансировку нагрузки;</li>
<li>использовать CDN доставки контента от ближайшего сервера или сервера с меньшим пингом;</li>
<li>оптимизировать веб-сервер для обработки большого количества запросов:
<ul>
<li>облегчить правила фаервола;</li>
<li>уменьшить количество прокси;</li>
<li>настроить буферы;</li>
<li>настроить кеширование статики.</li>
</ul>
</li>
<li>настроить сжатие (<a href="https://habr.com/ru/company/ruvds/blog/499278/" target=_blank rel="noopener noreferrer">оптимально brotli</a> со степенью компрессии около 5, значение выше не даёт значительного уменьшения);</li>
<li>уменьшить количество передаваемых заголовков в ответе;</li>
<li>использование протокола http2 для обработки запросов в одном соединении;</li>
<li>улучшить производительность самого веб-приложения.</li>
</ul>
<h2 id="оптимизация-веб-приложения">Оптимизация веб-приложения</h2>
<p>Веб-приложение обычно состоит из бэкэнда и фронтенда.</p>
<h3 id="бэкэнд">Бэкэнд</h3>
<p>Оптимизация бэкэнда (так же влияет на показатель TTFB) сводится к следующему:</p>
<ul>
<li>стараться использовать статику где это возможно;</li>
<li>настроить кеширование и буферизацию запросов к БД;</li>
<li>настроить кеширование страниц;</li>
<li>настроить кеширование скриптов прилжения (opcache, apc(u) cache, &hellip;);</li>
<li>оптимизировать запросы к БД;</li>
<li>минимизировать объём передаваемых данных от api.</li>
</ul>
<h3 id="фронтенд">Фронтенд</h3>
<p><img src="/photos/navigation_resource_timings.png" alt="Тайминги загрузки страницы сайта (источник: developer.mozilla.org)"></p>
<p>На фронтенде показателей больше:</p>
<ul>
<li><a href="https://developer.mozilla.org/ru/docs/%D0%A1%D0%BB%D0%BE%D0%B2%D0%B0%D1%80%D1%8C/First_contentful_paint" target=_blank rel="noopener noreferrer">First Contentful Paint (FCP)</a> — время до первой отрисовки;</li>
<li><a href="https://developer.mozilla.org/ru/docs/%D0%A1%D0%BB%D0%BE%D0%B2%D0%B0%D1%80%D1%8C/first_meaningful_paint" target=_blank rel="noopener noreferrer">First Meaningful Paint (FMP)</a> — время до отрисовки основной части контента;</li>
<li><a href="https://developer.mozilla.org/ru/docs/%D0%A1%D0%BB%D0%BE%D0%B2%D0%B0%D1%80%D1%8C/First_input_delay" target=_blank rel="noopener noreferrer">First Input Delay (FID)</a> — время отклика на событие, генерированное пользователем;</li>
<li><a href="https://web.dev/tbt/" target=_blank rel="noopener noreferrer">Total Blocking Time (TBT)</a> — суммарное время задач длительностью более 50мс;</li>
<li><a href="https://developer.mozilla.org/ru/docs/%D0%A1%D0%BB%D0%BE%D0%B2%D0%B0%D1%80%D1%8C/Time_to_interactive" target=_blank rel="noopener noreferrer">Time To Interactive (TTI)</a> — время до начала интерактивности элементов страницы.</li>
</ul>
<p>Оптимизируется всё это на нескольких уровнях.</p>
<h4 id="разметка-html">Разметка HTML</h4>
<ul>
<li>уменьшение вложенности DOM дерева;</li>
<li>минификация, использование табов для отступов.</li>
</ul>
<p><small>Эффект оптимизации: быстрее наступают FCP, FMP.</small></p>
<h4 id="стили-css">Стили CSS</h4>
<ul>
<li>вынесение критичных стилей в head &gt; style;</li>
<li>разделение стилей на куски и их дозагрузка после первого экрана;</li>
<li>упрощение селекторов CSS (по методологии БЭМ);</li>
<li>минификация.</li>
</ul>
<p><small>Эффект оптимизации: быстрее наступают FCP, FMP, отрисовка происходит значительно быстрее на большом количестве узлов DOM дерева.</small></p>
<h4 id="скрипты-javascript">Скрипты JavaScript</h4>
<ul>
<li>перенос, либо отложенная (async, defer) загрузка скриптов JavaScript;</li>
<li>requestAnimationFrame для анимаций;</li>
<li>requestIdleFrame для длительных задач;</li>
<li>WebWorkers для сложных длительных задач;</li>
<li>ServiceWorkers для обеспечения кеширования запросов;</li>
<li>ленивая загрузка данных (Infinite Scrolling);</li>
<li>минификация.</li>
</ul>
<p><small>Эффект оптимизации: значительное улучшение всех показателей при большом объёме JavaScript кода.</small></p>
<h4 id="изображения">Изображения</h4>
<ul>
<li>ленивая загрузка изображений;</li>
<li>правильный выбор формата изображений (jpeg — фото, png — чертежи или логотипы или постеры);</li>
<li><a href="/blog/frontend/imagemagick-convert-optimization-snippets">оптимизация изображений для веб</a>:
<ul>
<li>сжатие изображений по размеру в пикселях;</li>
<li>использование векторной графики или CSS3 где это возможно;</li>
<li>сжатие изображений по качеству;</li>
<li>использование современных способов сжатия (webp).</li>
</ul>
</li>
</ul>
<p><small>Эффект оптимизации: быстрее наступают FCP, FMP, уменьшается TTI (по наблюдениям), бонусом меньше трафика.</small></p>
<h4 id="шрифты">Шрифты</h4>
<ul>
<li>ленивая загрузка веб-шрифтов, в идеале использование <a href="/blog/frontend/most-recent-variant-of-css-system-font-families">системных - Web safe шрифтов</a>;</li>
<li>уменьшение количества шрифтов;</li>
<li>использование шрифтов только нужных языков;</li>
<li>выбор меньшего количества начертаний (font-weight).</li>
</ul>
<p><small>Эффект оптимизации: быстрее наступают FCP, FMP.</small></p>
<p>Список оптимизируемого будет пополняться, пока это всё, что вспомнил. =)</p>
<p>Оптимизируя приложения следите за своим временем. ;) Правило 20%/80% работает и здесь:
за 20% времени можно улучшить производительность приложения на 80%, остальное время
чаще всего тратится на долгое ковыряние за десятки милли- и даже микросекунд.</p>

            </div>
      	]]></turbo:content></item><item turbo="true"><title>Переход с PHPStorm или Intellij IDEA на VIM</title><link>https://mikhail-yudin.ru/blog/frontend/perehod-s-phpstorm-ili-idea-na-vim/</link><pubDate>Sat, 27 Jun 2020 00:00:00 +0000</pubDate><guid>https://mikhail-yudin.ru/blog/frontend/perehod-s-phpstorm-ili-idea-na-vim/</guid><description>Может кому-то показаться странным, зачем переходить с полноценной IDE с кучей возможностей на редактор, из которого даже выйти сложно. А в PHPStorm или IDEA одной мышью программировать можно. В моём случае переход был инициирован двумя вещами: желание использовать инструмент матёрых программистов и желание хоть как-то продолжать работать =D
Преимущества VIM перед IDEA быстрое начало работы с проектом: 200 мс против 2..3 минут на моём железе быстрая работа с текстом: обрамление строк списком HTML за 2,5 с vap&amp;lt;C-y&amp;gt;,ul&amp;gt;li* низкое потребление ресурсов: RAM 500-700 МБ при работающем NodeJS сервере LSP против 4+ ГБ с IDEA конфигурируемость: сами решаете как и что будет работать, используя VIMScript, плагины при отсутствии большого количества ресурсов работай удалённо по SSH, а с tmux вообще сказка работать можно с намного большим количеством проектов одновременно Начало освоения VIM Первые два года изучения VIM прошли безуспешно.</description><enclosure url="https://mikhail-yudin.ru/blog/frontend/perehod-s-phpstorm-ili-idea-na-vim/perehod-s-phpstorm-ili-idea-na-vim.png" type="image/png"/><turbo:content><![CDATA[
      		<header>
      			<h1>Переход с PHPStorm или Intellij IDEA на VIM</h1></header>
            <div class='entry-content'>
              <p>Может кому-то показаться странным, зачем переходить с полноценной IDE с кучей возможностей
на редактор, из которого даже выйти сложно. А в PHPStorm или IDEA одной мышью
программировать можно. В моём случае переход был инициирован двумя вещами:
желание использовать инструмент матёрых программистов и желание хоть как-то
продолжать работать =D</p>
<h2 id="преимущества-vim-перед-idea">Преимущества VIM перед IDEA</h2>
<ul>
<li>быстрое начало работы с проектом: 200 мс против 2..3 минут на моём железе</li>
<li>быстрая работа с текстом: обрамление строк списком HTML за 2,5 с <code>vap&lt;C-y&gt;,ul&gt;li*</code></li>
<li>низкое потребление ресурсов: RAM 500-700 МБ при работающем NodeJS сервере <a href="https://docs.microsoft.com/ru-ru/visualstudio/extensibility/language-server-protocol?view=vs-2019" target=_blank rel="noopener noreferrer">LSP</a> против 4+ ГБ с IDEA</li>
<li>конфигурируемость: сами решаете как и что будет работать, используя VIMScript, плагины</li>
<li>при отсутствии большого количества ресурсов работай удалённо по SSH, а с <a href="https://ru.wikipedia.org/wiki/Tmux" target=_blank rel="noopener noreferrer">tmux</a> вообще сказка</li>
<li>работать можно с намного большим количеством проектов одновременно</li>
</ul>
<h2 id="начало-освоения-vim">Начало освоения VIM</h2>
<p>Первые два года изучения VIM прошли безуспешно. Каждый год по месяцу уделял
время на изучение возможностей, но как-то не срослось. То ли функционал казался
скудным, то ли всё время уходило на изучение принципов работы в редакторе.</p>
<p>Сначала не понимал, зачем нужны эти режимы, если можно находясь только в одном
режиме работать с текстом как в обычном редакторе. Пиши и пиши, даже мышь вроде
работала для перемещения курсора =)</p>
<p>На третий год решил взяться изо всех сил изучить возможности VIM и создать свой
vimrc файл, настроить под себя. Как говорится, первый блин комом. Ну поставил
кучу плагинов, понадобавлял всяких полезностей собрав их с разных источников.
Потом сел за редактор на следующий день и понял что ничего не помню. И снова
бросил это дело.</p>
<h2 id="вынужденный-переход-на-vim-с-idea">Вынужденный переход на VIM с IDEA</h2>
<p>IDE Intellij IDEA очень хороша по своему функционалу. Из используемого мной это:</p>
<ul>
<li>поддержка тонны разнообразных плагинов от разработчиков и от сообщества</li>
<li>гибкость настроек</li>
<li>магия рефакторинга: сменив имя поля в таблице происходит автоматическое переименование использования этого поля по всему коду</li>
<li>очень удобная работа с базами данных</li>
<li>умное быстрое автодополнение</li>
<li>поддержка разделения экрана для работы с несколькими файлами одновременно</li>
<li>хранение истории правок
и многое многое другое.</li>
</ul>
<p>Однако однажды <a href="/blog/linux/rabotaem-udalyonno-po-ssh-na-karantine">перейдя на удалённую работу</a>,
работая с ноутбука и имея 4 гигабайта оперативки понял, что работа встаёт колом.
Оперативка съедалась вся при запущенном браузере (45%) и загруженной IDEA.
При этом по окончании рабочего дня своп использовался на 25%, это при условии,
что был включён zRam. Так продолжать работать было невозможно. И тут я решил
провести эксперимент и перейти на VIM полностью.</p>
<p>До очередной попытки чаще всего использовал VIM для правок конфигов на серверах.</p>
<p>Начал всё с чистого листа: создал новый vimrc файл и помаленьку стал добавлять
в него нужное для работы.</p>
<p>Первое и необходимое для работы это плагины. С ними проще и быстрее получить
нужный функционал. Сразу вспомнил, удобно когда менеджер плагинов сам
устанавливается при старте VIM. Для этого у меня уже был кусок конфига.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-vim" data-lang="vim"><span style="color:#ff79c6">if</span> empty(glob(<span style="color:#f1fa8c">&#39;~/.vim/autoload/plug.vim&#39;</span>))
  silent !curl -fLo ~<span style="color:#f1fa8c">/.vim/</span>autoload/plug.vim --create-dirs
        \ https:<span style="color:#f1fa8c">//</span>raw.githubusercontent.com<span style="color:#f1fa8c">/junegunn/</span>vim-plug<span style="color:#f1fa8c">/master/</span>plug.vim
  autocmd VimEnter * PlugInstall | source $MYVIMRC
<span style="color:#ff79c6">endif</span>
</code></pre></div><p>Если менеджер плагинов не установлен при запуске редактора, он скачивается
и запускается установка плагинов, указанных в директивах:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-vim" data-lang="vim">call plug#begin(<span style="color:#f1fa8c">&#39;~/.vim/plugged&#39;</span>)
<span style="color:#6272a4">
</span><span style="color:#6272a4">&#34; здесь плагины</span>

call plug#end()
</code></pre></div><p>Далее пошёл по просторам интернетов искать плагины, которые были нужны
при работе в IDE IDEA (ну или PHPStorm).</p>
<p><strong>Подсветка синтаксиса</strong> есть отдельно для каждого языка, но есть один мультиязыковый
плагин — <code>sheerun/vim-polyglot</code>, объединяющий разные языки.</p>
<p>Для <strong>автодополнения</strong> решил использовать <code>neoclide/coc.nvim</code>, т.к. он позволяет
получить результат как в VSCode.</p>
<p>Для вставки <strong>сниппетов</strong> пользуюсь тем же <code>coc</code> (ссылка на конфиг будет в конце поста),
плюс <code>honza/vim-snippets</code> — набор готовых сниппетов, можно создавать и свои.</p>
<p>Чтобы быстро <strong>переходить по файлам</strong> с помощью fuzzy (нечёткого) поиска воспользовался:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-vim" data-lang="vim">Plug <span style="color:#f1fa8c">&#39;junegunn/fzf&#39;</span>, { <span style="color:#f1fa8c">&#39;do&#39;</span>: { -&gt; fzf#install() } }
Plug <span style="color:#f1fa8c">&#39;junegunn/fzf.vim&#39;</span>
</code></pre></div><p>оба плагина нужны.</p>
<p>Для <strong>навигации по файлам</strong> использую <code>preservim/nerdtree</code>, как самый популярный.
Конфигурируется отображение иконок типов файлов, добавляются плагином
<code>ryanoasis/vim-devicons</code>, так что красоту навести можно.</p>
<p>Т.к. работаю с html разметкой, удобно написать <strong>emmet</strong> строку <code>ul&gt;li*5</code> и получить список. Это
делает <code>mattn/emmet-vim</code>.</p>
<p>Для работы с <strong>базами данных</strong> есть классный плагин от tpope: <code>tpope/vim-dadbod</code>.
Указываете в переменной <code>g:db</code> строку соединения с базой и спокойно отправляете
запросы на исполнение командой <code>DB</code>. По умолчанию отправляется весь буфер.</p>
<p>Для <strong>рефакторинга</strong> можно использовать разные техники, но для PHP решением
стал <code>adoy/vim-php-refactoring-toolbox</code>. Умеет создавать методы из кусков кода,
переименовывать методы, переменные и многое другое.</p>
<h2 id="итоги-перехода-с-idea-на-vim">Итоги перехода с IDEA на VIM</h2>
<p>Поработав около месяца-двух в редакторе VIM сомневаюсь что что-то заставит меня
сменить его на что-то иное. Теперь работать можно непосредственно на удалённой
машине по SSH. Кстати, поделился опытом <a href="/blog/linux/optimizaciya-i-uskorenie-vim">ускорения работы VIM по SSH</a>.</p>
<p>Кому интересно облегчение VIM,
<a href="/blog/linux/statusnaya-stroka-vim-bez-plaginov">сделал статусную строку без плагинов в 5 строк</a>.</p>
<p>Теперь как говорится, работать могу даже с хлебушка/холодильника/калькулятора. =)</p>
<p>На данный момент неудобно только редактировать множество строк в БД. Для этого
пользуюсь mysql-workbench.</p>
<p>Как и обещал,
<a href="https://github.com/fagci/dotfiles/blob/976c6fbcbeda91382b169d329e3f3a09449f9669/vim/.vimrc" target=_blank rel="noopener noreferrer">конфиг vimrc</a>
на момент написания поста.</p>
<p>Желаю удачи в переходе на редактор VIM!</p>

            </div>
      	]]></turbo:content></item><item turbo="true"><title>Подсветка активных ссылок на чистом JavaScript</title><link>https://mikhail-yudin.ru/blog/frontend/podsvetka-aktivnyh-ssylok-javascript/</link><pubDate>Mon, 04 May 2020 00:00:00 +0000</pubDate><guid>https://mikhail-yudin.ru/blog/frontend/podsvetka-aktivnyh-ssylok-javascript/</guid><description>Хорошей практикой считается подсвечивать текущую ссылку, чтобы показать пользователю, где он находится в данный момент.
Использовать для этого ресурсы сервера не оправдано. Исключением являются кешируемые на долгое время страницы или статические страницы. В остальных случаях серверу должно быть наплевать на то, какую страницу отправили пользователю. Как минимум в контент лезть ему не надо, чтоб указать класс активной ссылке. Это задача клиента, т.к. он сам запросил нужную страницу.
Решение в несколько строчек нисколько не будет нагружать клиента, отработает быстро.</description><enclosure url="https://mikhail-yudin.ru/blog/frontend/podsvetka-aktivnyh-ssylok-javascript/javascript-active-links-highlight.jpg" type="image/jpeg"/><turbo:content><![CDATA[
      		<header>
      			<h1>Подсветка активных ссылок на чистом JavaScript</h1></header>
            <div class='entry-content'>
              <p>Хорошей практикой считается подсвечивать текущую ссылку, чтобы показать пользователю, где он находится в данный момент.</p>
<p>Использовать для этого ресурсы сервера не оправдано. Исключением являются кешируемые на долгое время страницы или статические страницы.
В остальных случаях серверу должно быть наплевать на то, какую страницу отправили пользователю. Как минимум в контент лезть ему не надо, чтоб указать класс активной ссылке.
Это задача клиента, т.к. он сам запросил нужную страницу.</p>
<p>Решение в несколько строчек нисколько не будет нагружать клиента, отработает быстро. Скрипт можно вставлять прямо после навигационного меню например, если другие ссылки не нужно подсвечивать.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-javascript" data-lang="javascript"><span style="color:#ff79c6">const</span> doc <span style="color:#ff79c6">=</span> <span style="color:#8be9fd;font-style:italic">window</span>.<span style="color:#8be9fd;font-style:italic">document</span>;
<span style="color:#ff79c6">const</span> links <span style="color:#ff79c6">=</span> doc.querySelectorAll(<span style="color:#f1fa8c">&#34;a&#34;</span>); <span style="color:#6272a4">// любой нужный селектор
</span><span style="color:#6272a4"></span><span style="color:#ff79c6">const</span> linksCount <span style="color:#ff79c6">=</span> links.length;
<span style="color:#ff79c6">const</span> currentURL <span style="color:#ff79c6">=</span> doc.URL;

<span style="color:#ff79c6">for</span> (<span style="color:#8be9fd;font-style:italic">let</span> i <span style="color:#ff79c6">=</span> <span style="color:#bd93f9">0</span>; i <span style="color:#ff79c6">&lt;</span> linksCount; i<span style="color:#ff79c6">++</span>) {
  <span style="color:#8be9fd;font-style:italic">let</span> linkURL <span style="color:#ff79c6">=</span> links[i].href;

  <span style="color:#6272a4">// linkURL == currentURL, если не надо светить уровни выше
</span><span style="color:#6272a4"></span>  <span style="color:#ff79c6">if</span> (currentURL.startsWith(linkURL)) {
    links[i].classList.add(<span style="color:#f1fa8c">&#34;active&#34;</span>);
  }
}
</code></pre></div><p>Современное краткое решение:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-javascript" data-lang="javascript">(<span style="color:#8be9fd;font-style:italic">function</span>() {
  [...<span style="color:#ff79c6">this</span>.querySelectorAll(<span style="color:#f1fa8c">&#34;a&#34;</span>)]
    .filter(a =&gt; <span style="color:#ff79c6">this</span>.URL.startsWith(a.href))
    .forEach(a =&gt; a.classList.add(<span style="color:#f1fa8c">&#34;active&#34;</span>));
}.bind(<span style="color:#8be9fd;font-style:italic">window</span>.<span style="color:#8be9fd;font-style:italic">document</span>)());
</code></pre></div><p>Останется только назначить стили активным ссылкам с классом <code>active</code>.</p>

            </div>
      	]]></turbo:content></item><item turbo="true"><title>Рендер текстовых шаблонов по данным из массива PHP</title><link>https://mikhail-yudin.ru/blog/frontend/renderim-tekstoviye-shablony-po-dannym-iz-massiva/</link><pubDate>Mon, 04 May 2020 00:00:00 +0000</pubDate><guid>https://mikhail-yudin.ru/blog/frontend/renderim-tekstoviye-shablony-po-dannym-iz-massiva/</guid><description>Работая с текстовыми шаблонами для SEO например, встаёт задача использовать шаблоны строк, данные которых должны браться из массива.
Задача Задача проста — воспользоваться шаблонами строк для отображения данных из массива. Сделать это можно несколькими способами:
сгенерировать текста в самом PHP воспользоваться готовыми шаблонизаторами например Twig, Blade, Smarty, &amp;hellip; написать небольшую функцию для преобразования определённой подстроки в текст по данным из ассоциативного массива Решение Я предпочёл последний вариант, несмотря на подключенный Twig в проекте.</description><enclosure url="https://mikhail-yudin.ru/blog/frontend/renderim-tekstoviye-shablony-po-dannym-iz-massiva/php-render-text-template.jpg" type="image/jpeg"/><turbo:content><![CDATA[
      		<header>
      			<h1>Рендер текстовых шаблонов по данным из массива PHP</h1></header>
            <div class='entry-content'>
              <p>Работая с текстовыми шаблонами для SEO например, встаёт задача использовать шаблоны строк, данные которых должны браться из массива.</p>
<h2 id="задача">Задача</h2>
<p>Задача проста — воспользоваться шаблонами строк для отображения данных из массива. Сделать это можно несколькими способами:</p>
<ul>
<li>сгенерировать текста в самом PHP</li>
<li>воспользоваться готовыми шаблонизаторами например Twig, Blade, Smarty, &hellip;</li>
<li>написать небольшую функцию для преобразования определённой подстроки в текст по данным из ассоциативного массива</li>
</ul>
<h2 id="решение">Решение</h2>
<p>Я предпочёл последний вариант, несмотря на подключенный Twig в проекте.</p>
<p>Первый вариант решения задачи выглядит следующим образом:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-php" data-lang="php"><span style="color:#ff79c6">&lt;?php</span>

<span style="color:#f1fa8c">/**
</span><span style="color:#f1fa8c"> * PHP function to render string path template using nested array.
</span><span style="color:#f1fa8c"> * Renders template string like &#34;Nested array item value: {data.some.nested.item.value}&#34;
</span><span style="color:#f1fa8c"> * @param string $template
</span><span style="color:#f1fa8c"> * @param array $data
</span><span style="color:#f1fa8c"> * @return string
</span><span style="color:#f1fa8c"> */</span>
<span style="color:#ff79c6">public</span> <span style="color:#ff79c6">static</span> <span style="color:#ff79c6">function</span> <span style="color:#50fa7b">renderDataTemplate</span>(string <span style="color:#8be9fd;font-style:italic">$template</span>, <span style="color:#ff79c6">array</span> <span style="color:#8be9fd;font-style:italic">$data</span>)<span style="color:#ff79c6">:</span> string
{
  <span style="color:#ff79c6">return</span> preg_replace_callback(<span style="color:#f1fa8c">&#39;/{([^{}]+?)}/&#39;</span>, <span style="color:#ff79c6">static</span> <span style="color:#ff79c6">function</span> (<span style="color:#8be9fd;font-style:italic">$v</span>) <span style="color:#ff79c6">use</span> (<span style="color:#8be9fd;font-style:italic">$data</span>) {
    <span style="color:#8be9fd;font-style:italic">$temp</span> <span style="color:#ff79c6">=</span> <span style="color:#ff79c6">&amp;</span><span style="color:#8be9fd;font-style:italic">$data</span>;
    <span style="color:#ff79c6">foreach</span> (explode(<span style="color:#f1fa8c">&#39;.&#39;</span>, <span style="color:#8be9fd;font-style:italic">$v</span>[<span style="color:#bd93f9">1</span>]) <span style="color:#ff79c6">as</span> <span style="color:#8be9fd;font-style:italic">$key</span>) {
      <span style="color:#8be9fd;font-style:italic">$temp</span> <span style="color:#ff79c6">=</span> <span style="color:#ff79c6">&amp;</span><span style="color:#8be9fd;font-style:italic">$temp</span>[<span style="color:#8be9fd;font-style:italic">$key</span>] <span style="color:#ff79c6">??</span> <span style="color:#ff79c6">null</span>;
      <span style="color:#ff79c6">if</span> (<span style="color:#8be9fd;font-style:italic">$temp</span> <span style="color:#ff79c6">===</span> <span style="color:#ff79c6">null</span>) {
        <span style="color:#ff79c6">return</span> <span style="color:#ff79c6">null</span>;
      }
    }
    <span style="color:#ff79c6">return</span> <span style="color:#8be9fd;font-style:italic">$temp</span>;
  }, <span style="color:#8be9fd;font-style:italic">$template</span>);
}
</code></pre></div><p>Данная версия позволяет выполнять множественную замену вложенных шаблонов,
применяя функцию несколько раз для шаблона вида:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-yaml" data-lang="yaml">{variables.{data.item.type}.{id}.name}

</code></pre></div><p>так как разворачивает только ближайшие фигурные скобки.</p>
<p>Актуальная версия функции PHP для рендера шаблона по данным из ассоциативного массива: <a href="https://gist.github.com/fagci/5c189856f00f4e3c4329858db19b5841" target=_blank rel="noopener noreferrer">PHP function to render string path template using nested array</a>.</p>

            </div>
      	]]></turbo:content></item><item turbo="true"><title>Битва титанов: normalize.css против reset.css</title><link>https://mikhail-yudin.ru/blog/frontend/normalize-css-protiv-reset-css/</link><pubDate>Thu, 03 Oct 2019 00:00:00 +0000</pubDate><guid>https://mikhail-yudin.ru/blog/frontend/normalize-css-protiv-reset-css/</guid><description>Использовать normalize.css практично и менее затратно. Сейчас расскажу почему и что меня подвело к использованию normalize.css вместо reset.css.
Всё-таки normalize.css это не reset.css Как бы это не было очевидно, разница большая между сбросом и нормализацией. Задача reset.css — сбросить все возможные стили для переопределения потом в коде. Почему таки задумался о разнице между двумя способами привести стили к одному виду?
Верстая этот блог понял, что должен снова заморачиваться с типографикой, хотя у браузеров по умолчанию она уже есть какая-то.</description><enclosure url="https://mikhail-yudin.ru/blog/frontend/normalize-css-protiv-reset-css/normalize-css-protiv-reset-css.png" type="image/png"/><turbo:content><![CDATA[
      		<header>
      			<h1>Битва титанов: normalize.css против reset.css</h1></header>
            <div class='entry-content'>
              <p>Использовать normalize.css практично и менее затратно.
Сейчас расскажу почему и что меня подвело к использованию normalize.css вместо reset.css.</p>
<h2 id="всё-таки-normalizecss-это-не-resetcss">Всё-таки normalize.css это не reset.css</h2>
<p>Как бы это не было очевидно, разница большая между сбросом и нормализацией.
Задача reset.css — сбросить все возможные стили для переопределения потом в коде.
Почему таки задумался о разнице между двумя способами привести стили к одному виду?</p>
<p>Верстая этот блог понял, что должен снова <a href="/notes/web-tipografika-dlya-razrabotchikov/">заморачиваться с типографикой</a>, хотя у браузеров по умолчанию она уже есть какая-то.
Правда у каждого браузера она своя.</p>
<p>А normalize.css решает в том числе и эту проблему.
В итоге сделал выбор в сторону normalize.css против reset.css.
Вместе с <a href="/frontend/most-recent-variant-of-css-system-font-families">системными шрифтами</a> это решение позволяет меньшими затратами достичь нужного результата и быстрее предоставить пользователю контент.</p>
<h2 id="где-скачать-normalizecss">Где скачать normalize.css</h2>
<p>Известный источник с самым большим комьюнити — <a href="https://github.com/necolas/normalize.css/" target=_blank rel="noopener noreferrer">normalize.css</a> от <a href="https://github.com/necolas" target=_blank rel="noopener noreferrer">Nicolas Gallagher</a>.
Официальный сайт проекта <a href="https://necolas.github.io/normalize.css/" target=_blank rel="noopener noreferrer">necolas.github.io/normalize.css</a>.</p>
<p>Так же существует <strong>более лёгкая версия normalize.css</strong> для топовых браузеров:
<a href="https://github.com/sindresorhus/modern-normalize" target=_blank rel="noopener noreferrer">sindresorhus/modern-normalize</a>.</p>

            </div>
      	]]></turbo:content></item><item turbo="true"><title>CSS ищет баги в SEO: картинки без alt и внешние ссылки</title><link>https://mikhail-yudin.ru/blog/frontend/css-noalt-external-link-counter/</link><pubDate>Tue, 20 Aug 2019 00:00:00 +0000</pubDate><guid>https://mikhail-yudin.ru/blog/frontend/css-noalt-external-link-counter/</guid><description>В 2015 году писал CSS стиль для stylish, для подсчёта изображений без атрибута alt, чтобы видеть где и сколько забыл проставить.
Да, считать средствами css уже давно можно Посмотрев в доки на mozilla.org, увидим что в спецификации CSS 2.1, а это аж в середине 2011 года, появилась возможность использовать счётчики для их инкрементирования через свойства CSS. Кто не знает, как и что — привожу краткий пример для нумерации заголовков.
article { counter-reset: h; } article h2 { counter-increment: h; } article h2:before { content: counter(h) &amp;#39;.</description><enclosure url="https://mikhail-yudin.ru/blog/frontend/css-noalt-external-link-counter/css-noalt-external-link-counter.png" type="image/png"/><turbo:content><![CDATA[
      		<header>
      			<h1>CSS ищет баги в SEO: картинки без alt и внешние ссылки</h1></header>
            <div class='entry-content'>
              <p>В 2015 году писал CSS стиль для stylish, для подсчёта изображений без атрибута alt, чтобы видеть где и сколько забыл проставить.</p>
<h2 id="да-считать-средствами-css-уже-давно-можно">Да, считать средствами css уже давно можно</h2>
<p>Посмотрев в доки на mozilla.org, увидим что в <a href="https://www.w3.org/TR/CSS21/generate.html#counters" target=_blank rel="noopener noreferrer">спецификации CSS 2.1</a>, а это аж в <a href="https://ru.wikipedia.org/wiki/CSS" target=_blank rel="noopener noreferrer">середине 2011 года</a>, появилась возможность использовать счётчики для их инкрементирования через свойства CSS.
Кто не знает, как и что — привожу краткий пример для нумерации заголовков.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-css" data-lang="css"><span style="color:#ff79c6">article</span> {
  <span style="color:#ff79c6">counter-reset</span>: h;
}
<span style="color:#ff79c6">article</span> <span style="color:#ff79c6">h2</span> {
  <span style="color:#ff79c6">counter-increment</span>: h;
}
<span style="color:#ff79c6">article</span> <span style="color:#ff79c6">h2</span>:before {
  <span style="color:#ff79c6">content</span>: <span style="color:#8be9fd;font-style:italic">counter</span>(h) <span style="color:#f1fa8c">&#39;. &#39;</span>;
}
</code></pre></div><p>В этом примере для каждого материала article сбрасывается счётчик <code>h</code>, который будет хранить количество заголовков второго уровня.
Затем для каждого встречающегося тега h2 увеличиваем значение счётчика и потом приписываем в начало заголовков значение счётчика.</p>
<h2 id="что-я-сделал">Что я сделал</h2>
<p>Реализация старая, оставлю как есть.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-css" data-lang="css"><span style="color:#ff79c6">body</span> {
	<span style="color:#ff79c6">counter-reset</span>: noalt extLinks;
}

<span style="color:#ff79c6">img</span>:not<span style="color:#ff79c6">([</span><span style="color:#ff79c6">alt</span><span style="color:#ff79c6">])</span> {
  <span style="color:#ff79c6">counter-increment</span>: noalt;
}

<span style="color:#ff79c6">a</span><span style="color:#ff79c6">[</span><span style="color:#ff79c6">href</span> <span style="color:#ff79c6">^=</span> <span style="color:#f1fa8c">&#39;http&#39;</span><span style="color:#ff79c6">]</span> {
	<span style="color:#ff79c6">counter-increment</span>: extLinks;
}

<span style="color:#ff79c6">body</span>:after {
  <span style="color:#ff79c6">content</span>: <span style="color:#f1fa8c">&#39;noalt: &#39;</span> <span style="color:#8be9fd;font-style:italic">counter</span>(noalt) <span style="color:#f1fa8c">&#39;, external links: &#39;</span> <span style="color:#8be9fd;font-style:italic">counter</span>(extLinks);
  <span style="color:#ff79c6">position</span>: <span style="color:#ff79c6">fixed</span>;
  <span style="color:#ff79c6">bottom</span>: <span style="color:#bd93f9">0</span>;
  <span style="color:#ff79c6">right</span>: <span style="color:#bd93f9">0</span>;
  <span style="color:#ff79c6">z-index</span>: <span style="color:#bd93f9">1000000</span>;
  <span style="color:#ff79c6">padding</span>: <span style="color:#bd93f9">2</span><span style="color:#8be9fd">px</span> <span style="color:#bd93f9">16</span><span style="color:#8be9fd">px</span>;
  <span style="color:#ff79c6">background</span>: <span style="color:#8be9fd;font-style:italic">rgba</span>(<span style="color:#bd93f9">0</span>,<span style="color:#bd93f9">0</span>,<span style="color:#bd93f9">0</span>,<span style="color:#bd93f9">0.75</span>);
  <span style="color:#ff79c6">color</span>: <span style="color:#bd93f9">#0f0</span>;
  <span style="color:#ff79c6">font-size</span>: <span style="color:#bd93f9">11</span><span style="color:#8be9fd">px</span>;
  <span style="color:#ff79c6">line-height</span>: <span style="color:#ff79c6">normal</span>;
}
</code></pre></div><p>В коде выше помимо подсчёта изображений без атрибута alt ещё считал количество внешних ссылок. Критерием было наличие http в начале ссылки, что недостоверно, т.к. отслеживает только абсолютные ссылки. Доработайте сами. Тут показал только пример.</p>

            </div>
      	]]></turbo:content></item><item turbo="true"><title>Системные шрифты для веб 2019</title><link>https://mikhail-yudin.ru/blog/frontend/most-recent-variant-of-css-system-font-families/</link><pubDate>Mon, 12 Aug 2019 00:00:00 +0000</pubDate><guid>https://mikhail-yudin.ru/blog/frontend/most-recent-variant-of-css-system-font-families/</guid><description>Собрал подборку стандартных шрифтов CSS для сайтов. Стандартные веб шрифты ускоряют загрузку страницы и делают шрифты привычными для глаз.
Системные шрифты — что это такое Системные шрифты — это шрифты, предустановленные в операционной системе по умолчанию. Для каждой версии операционной системы свой список системных шрифтов по умолчанию, вот краткий список:
Windows Версия Шрифт 10 Segoe UI1 8.1 Segoe UI1 8 Segoe UI1 7 Segoe UI1 Vista Segoe UI1 XP Tahoma2 Me (Millennium Edition) MS Sans Serif3 2000 Tahoma2 98 Second Edition MS Sans Serif3 98 MS Sans Serif3 NT 4 MS Sans Serif3 95 MS Sans Serif3 NT 3.</description><enclosure url="https://mikhail-yudin.ru/blog/frontend/most-recent-variant-of-css-system-font-families/most-recent-variant-of-css-system-font-families.png" type="image/png"/><turbo:content><![CDATA[
      		<header>
      			<h1>Системные шрифты для веб 2019</h1></header>
            <div class='entry-content'>
              <p>Собрал подборку стандартных шрифтов CSS для сайтов. Стандартные веб шрифты ускоряют загрузку страницы и делают шрифты привычными для глаз.</p>
<h2 id="системные-шрифты-mdash-что-это-такое">Системные шрифты — что это такое</h2>
<p>Системные шрифты — это шрифты, предустановленные в операционной системе по умолчанию. Для каждой версии операционной системы свой список системных шрифтов по умолчанию, вот краткий список:</p>
<h3 id="windows">Windows</h3>
<table>
<thead>
<tr>
<th>Версия</th>
<th>Шрифт</th>
</tr>
</thead>
<tbody>
<tr>
<td>10</td>
<td>Segoe UI1</td>
</tr>
<tr>
<td>8.1</td>
<td>Segoe UI1</td>
</tr>
<tr>
<td>8</td>
<td>Segoe UI1</td>
</tr>
<tr>
<td>7</td>
<td>Segoe UI1</td>
</tr>
<tr>
<td>Vista</td>
<td>Segoe UI1</td>
</tr>
<tr>
<td>XP</td>
<td>Tahoma2</td>
</tr>
<tr>
<td>Me (Millennium Edition)</td>
<td>MS Sans Serif3</td>
</tr>
<tr>
<td>2000</td>
<td>Tahoma2</td>
</tr>
<tr>
<td>98 Second Edition</td>
<td>MS Sans Serif3</td>
</tr>
<tr>
<td>98</td>
<td>MS Sans Serif3</td>
</tr>
<tr>
<td>NT 4</td>
<td>MS Sans Serif3</td>
</tr>
<tr>
<td>95</td>
<td>MS Sans Serif3</td>
</tr>
<tr>
<td>NT 3.5</td>
<td>MS Sans Serif3</td>
</tr>
<tr>
<td>NT 3.1</td>
<td>MS Sans Serif3</td>
</tr>
<tr>
<td>3.1</td>
<td>MS Sans Serif3</td>
</tr>
<tr>
<td>3</td>
<td>Helv3</td>
</tr>
<tr>
<td>2</td>
<td>Helv3</td>
</tr>
<tr>
<td>1</td>
<td>Helv3</td>
</tr>
<tr>
<td>&mdash;</td>
<td>&mdash;</td>
</tr>
</tbody>
</table>
<p><a href="https://www.granneman.com/webdev/coding/css/fonts-and-formatting/default-fonts" target=_blank rel="noopener noreferrer">Источник</a></p>
<h3 id="mac-os">Mac OS</h3>
<table>
<thead>
<tr>
<th>Версия</th>
<th>Шрифт</th>
</tr>
</thead>
<tbody>
<tr>
<td>10.15</td>
<td>San Francisco</td>
</tr>
<tr>
<td>10.14 Mojave</td>
<td>San Francisco</td>
</tr>
<tr>
<td>10.13 High Sierra</td>
<td>San Francisco</td>
</tr>
<tr>
<td>10.12 Sierra</td>
<td>San Francisco</td>
</tr>
<tr>
<td>10.11 El Capitan</td>
<td>San Francisco</td>
</tr>
<tr>
<td>10.10 Yosemite</td>
<td>Helvetica Neue</td>
</tr>
<tr>
<td>10.9 Mavericks</td>
<td>Lucida Grande</td>
</tr>
<tr>
<td>10.8 Mountain Lion</td>
<td>Lucida Grande</td>
</tr>
<tr>
<td>10.7 Lion</td>
<td>Lucida Grande</td>
</tr>
<tr>
<td>10.6 Snow Leopard</td>
<td>Lucida Grande</td>
</tr>
<tr>
<td>10.5 Leopard</td>
<td>Lucida Grande</td>
</tr>
<tr>
<td>10.4 Tiger</td>
<td>Lucida Grande</td>
</tr>
<tr>
<td>10.3 Panther</td>
<td>Lucida Grande</td>
</tr>
<tr>
<td>10.2 Jaguar</td>
<td>Lucida Grande</td>
</tr>
<tr>
<td>10.1 Puma</td>
<td>Lucida Grande</td>
</tr>
<tr>
<td>10.0 Cheetah</td>
<td>Lucida Grande</td>
</tr>
</tbody>
</table>
<p><a href="https://www.granneman.com/webdev/coding/css/fonts-and-formatting/default-fonts" target=_blank rel="noopener noreferrer">Источник</a></p>
<h3 id="linux">Linux</h3>
<p>&hellip;</p>
<h3 id="ios">iOS</h3>
<table>
<thead>
<tr>
<th>Версия</th>
<th>Шрифт</th>
</tr>
</thead>
<tbody>
<tr>
<td>12</td>
<td>San Francisco</td>
</tr>
<tr>
<td>11</td>
<td>San Francisco</td>
</tr>
<tr>
<td>10</td>
<td>San Francisco</td>
</tr>
<tr>
<td>9</td>
<td>San Francisco</td>
</tr>
<tr>
<td>8</td>
<td>Helvetica Neue</td>
</tr>
<tr>
<td>7</td>
<td>Helvetica Neue</td>
</tr>
<tr>
<td>6</td>
<td>Helvetica Neue</td>
</tr>
<tr>
<td>5</td>
<td>Helvetica Neue</td>
</tr>
<tr>
<td>4</td>
<td>Helvetica Neue</td>
</tr>
<tr>
<td>iPhone OS 3</td>
<td>Helvetica</td>
</tr>
<tr>
<td>iPhone OS 2</td>
<td>Helvetica</td>
</tr>
<tr>
<td>iPhone OS 1</td>
<td>Helvetica</td>
</tr>
</tbody>
</table>
<p><a href="https://www.granneman.com/webdev/coding/css/fonts-and-formatting/default-fonts" target=_blank rel="noopener noreferrer">Источник</a></p>
<h3 id="android">Android</h3>
<table>
<thead>
<tr>
<th>Версия</th>
<th>Шрифт</th>
</tr>
</thead>
<tbody>
<tr>
<td>4.3 и ниже</td>
<td>Droid</td>
</tr>
<tr>
<td>5.0 Lollipop и выше</td>
<td>Roboto</td>
</tr>
</tbody>
</table>
<p><a href="https://en.wikipedia.org/wiki/Roboto" target=_blank rel="noopener noreferrer">Источник</a></p>
<h2 id="зачем-использовать-вместо-веб-шрифтов">Зачем использовать вместо веб-шрифтов</h2>
<p>Да, используя веб-шрифты, например <a href="https://fonts.google.com/" target=_blank rel="noopener noreferrer">Google Fonts</a>, получится очень красивая типографика. Здесь есть большой простор для фантазии, многообразие шрифтов и их бесплатность порадует каждого. Но не всегда пользователя.</p>
<p>Дело в том, что веб шрифты в любом случае нужно дополнительно загружать по сети, загружать в память, что уже вносит дополнительную задержку в отображение контента.</p>
<p>Помимо этого, прописываются дополнительные атрибуты, чтобы веб-шрифты подгружались как нужно. Другое дело системные семейства шрифтов.</p>
<h2 id="что-такое-эти-системные-семейства-шрифтов">Что такое эти системные семейства шрифтов</h2>
<p>Ещё такие шрифты называют веб-безопасными. Список системных шрифтов на <a href="https://www.w3schools.com/cssref/css_websafe_fonts.asp" target=_blank rel="noopener noreferrer">W3C Schools</a>. Так же смотрите запись в <a href="https://www.granneman.com/webdev/coding/css/fonts-and-formatting/default-fonts" target=_blank rel="noopener noreferrer">блоге Scott Granneman</a>.</p>
<p>Если интересуют надёжные источники — в блоге Scott Granneman перечислены ссылки.</p>
<p>После того, как выявлены шрифты, присутствующие в каждой из операционных систем, выбираются из них те, что похожи друг на друга в семействе.</p>
<p>В посте это опущу, оставлю пару ссылок:</p>
<p><a href="https://www.smashingmagazine.com/2015/11/using-system-ui-fonts-practical-guide/" target=_blank rel="noopener noreferrer">Using UI System Fonts In Web Design: A Quick Practical Guide</a>
<a href="https://furbo.org/2018/03/28/system-fonts-in-css/" target=_blank rel="noopener noreferrer">System Fonts in CSS</a></p>
<h2 id="мой-вариант">Мой вариант</h2>
<p>Полазив по GitHub, Medium и некоторым другим ресурсам, пришёл к такому виду стека системных шрифтов 2019 года:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-css" data-lang="css">:root {
  <span style="color:#8be9fd;font-style:italic">--font-family-sans-serif</span>: <span style="color:#ff79c6">-</span>apple-system, BlinkMacSystemFont, <span style="color:#f1fa8c">&#34;Segoe UI&#34;</span>, <span style="color:#f1fa8c">&#34;Roboto&#34;</span>, <span style="color:#f1fa8c">&#34;Helvetica Neue&#34;</span>, Arial, <span style="color:#ff79c6">sans-serif</span>, <span style="color:#f1fa8c">&#34;Apple Color Emoji&#34;</span>, <span style="color:#f1fa8c">&#34;Segoe UI Emoji&#34;</span>, <span style="color:#f1fa8c">&#34;Segoe UI Symbol&#34;</span>;
  <span style="color:#8be9fd;font-style:italic">--font-family-sans-serif-condensed</span>: <span style="color:#f1fa8c">&#34;DINNextW01-CondensedMed&#34;</span>, <span style="color:#f1fa8c">&#34;AvenirNextCondensed-Bold&#34;</span>, <span style="color:#f1fa8c">&#34;Futura-CondensedExtraBold&#34;</span>, HelveticaNeue-CondensedBold, <span style="color:#f1fa8c">&#34;Ubuntu Condensed&#34;</span>, <span style="color:#f1fa8c">&#34;Liberation Sans Narrow&#34;</span>, <span style="color:#f1fa8c">&#34;Franklin Gothic Demi Cond&#34;</span>, <span style="color:#f1fa8c">&#34;Arial Narrow&#34;</span>, <span style="color:#f1fa8c">&#34;Roboto Condensed&#34;</span>, <span style="color:#ff79c6">sans-serif</span><span style="color:#ff79c6">-</span><span style="color:#ff79c6">condensed</span>, Arial, <span style="color:#f1fa8c">&#34;Trebuchet MS&#34;</span>, <span style="color:#f1fa8c">&#34;Lucida Grande&#34;</span>, Tahoma, <span style="color:#ff79c6">sans-serif</span>;
  <span style="color:#8be9fd;font-style:italic">--font-family-serif</span>: Georgia, Times, Times New Roman, <span style="color:#ff79c6">serif</span>;
  <span style="color:#8be9fd;font-style:italic">--font-family-mono</span>: SFMono-Regular, Consolas, <span style="color:#f1fa8c">&#34;Liberation Mono&#34;</span>, Menlo, Courier, <span style="color:#ff79c6">monospace</span>;
}
.<span style="color:#50fa7b">condensed</span> {
  <span style="color:#ff79c6">font-family</span>: <span style="color:#50fa7b">var</span>(<span style="color:#ff79c6">--</span>font<span style="color:#ff79c6">-</span>family<span style="color:#ff79c6">-</span><span style="color:#ff79c6">sans-serif</span><span style="color:#ff79c6">-</span><span style="color:#ff79c6">condensed</span>);
  <span style="color:#ff79c6">font-stretch</span>: <span style="color:#ff79c6">condensed</span>;
}
</code></pre></div><p><a href="https://gist.github.com/fagci/b4a2409a52551a148405a4b65b20b6c5" target=_blank rel="noopener noreferrer">GitHub Gist</a></p>
<p>Ещё советую проверить, <a href="/notes/symbols-font-check/">какие символы видны</a> при использовании стандартных шрифтов на этом сайте.</p>

            </div>
      	]]></turbo:content></item><item turbo="true"><title>Jekyll календарь на чистом Liquid template engine</title><link>https://mikhail-yudin.ru/blog/frontend/jekyll-calendar-css-grid/</link><pubDate>Tue, 06 Aug 2019 00:00:00 +0000</pubDate><guid>https://mikhail-yudin.ru/blog/frontend/jekyll-calendar-css-grid/</guid><description>Захотел попробовать сделать для своего блога на Jekyll календарь с использованием Liquid template engine. Не хотелось использовать JS библиотеки, решил написать календарик на чистом Liquid.
Предыстория Делал календарик для отображения даты публикации постов. Решил не заморачиваться с написанием или поиском плагинов, взялся делать самостоятельно. К тому же, изучая CSS grid system понял, что делая минимум телодвижений можно заставить отображаться сквозной &amp;ldquo;список&amp;rdquo; дней с разбивкой на недели.
Jekyll календарь на чистом Liquid Первым делом думал о получении даты начала месяца.</description><enclosure url="https://mikhail-yudin.ru/blog/frontend/jekyll-calendar-css-grid/jekyll-calendar-css-grid.png" type="image/png"/><turbo:content><![CDATA[
      		<header>
      			<h1>Jekyll календарь на чистом Liquid template engine</h1></header>
            <div class='entry-content'>
              <p>Захотел попробовать сделать для своего блога на Jekyll календарь с использованием Liquid template engine. Не хотелось использовать JS библиотеки, решил написать календарик на чистом Liquid.</p>
<h2 id="предыстория">Предыстория</h2>
<p>Делал календарик для отображения даты публикации постов. Решил не заморачиваться с написанием или поиском плагинов, взялся делать самостоятельно. К тому же, изучая CSS grid system понял, что делая минимум телодвижений можно заставить отображаться сквозной &ldquo;список&rdquo; дней с разбивкой на недели.</p>
<h2 id="jekyll-календарь-на-чистом-liquid">Jekyll календарь на чистом Liquid</h2>
<p>Первым делом думал о получении даты начала месяца. Сделать это просто:</p>
<pre><code class="language-liquid" data-lang="liquid">{{ 'now' | date: '%Y-%m-01' }}
</code></pre><p>Следующее что стоило сделать — каким-то образом перебрать все дни. Сделать это можно легко. Благо в сутках 24 часа =)</p>
<pre><code class="language-liquid" data-lang="liquid">{% raw %}{% for i in (0..30) %}
{% assign day = 86400 | times: i | plus: month_starts_ts %}
{% endfor %}{% endraw %}
</code></pre><p>Оставалась ещё проблемка: выводить нужно дни до первого числа месяца, чтобы правильно отобразить календарь средствами CSS grid. Пришлось оглядываться назад на недельку и вставлять заглушки для дней с прошлого месяца. Можно конечно и отображать, но делать цвет шрифта приглушённым.</p>
<pre><code class="language-liquid" data-lang="liquid">{% raw %}
{% for i in (-7..30) %}

{% assign day = 86400 | times: i | plus: month_starts_ts %}
{% assign dow = day | date: '%u' %}
{% assign m = day | date: '%m' %}

{% unless fd %}
{% if dow == '7' %}{% assign fd = true %}{% endif %}
{% continue %}
{% endunless %}

{% if month == m %}

{% case dow %}
{% when '6' %}&lt;span class=&quot;calendar__we {{ has_posts }} &quot;&gt;
  {% when '7' %}&lt;span class=&quot;calendar__we {{ has_posts }}&quot;&gt;
  {% else %}&lt;span class=&quot;{{ has_posts }}&quot;&gt;
{% endcase %}{{ day | date: '%e' }}&lt;/span&gt;

{% else %}&lt;span&gt;&lt;/span&gt;{% endif %}

{% endfor %}
{% endraw %}
</code></pre><p>Таким образом, <code>fd</code> сигнализирует о том, что попали на первый день календарного месяца.</p>
<p>Осталось отобразить наличие опубликованных постов в определённый день. С наскоку заюзать фильтр по дате не удалось, сделал через обычное условие.</p>
<p>Итоговый вид скрипта:</p>
<pre><code class="language-liquid" data-lang="liquid">{% raw %}
{% assign month_starts = 'now' | date: '%Y-%m-01' %}
{% assign month_starts_ts = month_starts | date: &quot;%s&quot; %}
{% assign month = 'now' | date: &quot;%m&quot; %}
{% assign fd = false %}
&lt;div class=&quot;calendar&quot;&gt;
&lt;b&gt;Пн&lt;/b&gt;&lt;b&gt;Вт&lt;/b&gt;&lt;b&gt;Ср&lt;/b&gt;&lt;b&gt;Чт&lt;/b&gt;&lt;b&gt;Пт&lt;/b&gt;
&lt;b class=&quot;calendar__we&quot;&gt;Сб&lt;/b&gt;
&lt;b class=&quot;calendar__we&quot;&gt;Вс&lt;/b&gt;
{%- for i in (-7..30) %}
{%- assign day = 86400 | times: i | plus: month_starts_ts %}
{%- assign dow = day | date: '%u' %}
{%- assign m = day | date: '%m' %}
{%- assign dayf = day | date: &quot;%Y-%m-%d&quot; %}
{%- unless fd %}
{%- if dow == '7' %}{% assign fd = true %}{% endif %}
{%- continue %}
{%- endunless %}

{%- if month == m %}
{%- assign has_posts = '' %}
{%- for post in site.posts %}
    {%- assign d = post.date | date: &quot;%Y-%m-%d&quot; %}
    {%- if d == dayf %}{% assign has_posts = 'calendar__ext' %}{% break %}{% endif %}
{%- endfor %}
{%- case dow %}
{%- when '6' %}&lt;span class=&quot;calendar__we {{ has_posts }} &quot;&gt;
  {%- when '7' %}&lt;span class=&quot;calendar__we {{ has_posts }}&quot;&gt;
  {%- else %}&lt;span class=&quot;{{ has_posts }}&quot;&gt;
{%- endcase %}{{ day | date: '%e' }}&lt;/span&gt;
{%- else %}&lt;span&gt;&lt;/span&gt;{% endif %}
{%- endfor %}
&lt;/div&gt;
{% endraw %}
</code></pre><h2 id="вёрстка-календаря-средствами-css-grid">Вёрстка календаря средствами CSS grid</h2>
<p>С сеткой всё просто, когда знаешь, как пользоваться. Нужно указать ширину и высоту ячеек и их количество в строке.</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-scss" data-lang="scss"><span style="color:#50fa7b">.calendar</span> {
  <span style="color:#ff79c6">display</span>: <span style="color:#ff79c6">grid</span>;
  <span style="color:#ff79c6">grid-template-columns</span>: <span style="color:#ff79c6">repeat</span><span style="color:#ff79c6">(</span><span style="color:#ff79c6">7</span><span style="color:#ff79c6">,</span> <span style="color:#ff79c6">2rem</span><span style="color:#ff79c6">)</span>;
  <span style="color:#ff79c6">line-height</span>: <span style="color:#ff79c6">2rem</span>;
  <span style="color:#ff79c6">text-align</span>: <span style="color:#ff79c6">center</span>;
}
<span style="color:#50fa7b">.calendar__we</span> {
  <span style="color:#ff79c6">color</span>: #e22;
}
<span style="color:#50fa7b">.calendar__ext</span> {
  <span style="color:#ff79c6">font-style</span>: <span style="color:#ff79c6">normal</span>;
  <span style="color:#ff79c6">position</span>: <span style="color:#ff79c6">relative</span>;
  <span style="color:#ff79c6">&amp;</span>:after{
    <span style="color:#ff79c6">content</span>: <span style="color:#f1fa8c">&#39;•&#39;</span>;
    <span style="color:#ff79c6">position</span>: <span style="color:#ff79c6">absolute</span>;
    <span style="color:#ff79c6">top</span>: <span style="color:#ff79c6">-</span><span style="color:#50fa7b">.45em</span>;
    <span style="color:#ff79c6">right</span>: <span style="color:#50fa7b">.125em</span>;
  }
}
</code></pre></div><h2 id="заключение">Заключение</h2>
<p>Такой календарь удобно использовать скорее для бо́льших промежутков времени, т.к. всей картины периодичности постинга не видно. Однако, это мой первый опыт доскональной работы с Jekyll Liquid template engine и реализации календаря с использованием модного и удобного CSS grid.</p>

            </div>
      	]]></turbo:content></item><item turbo="true"><title>Игра про уничтожение блоков с использованием HTML5 canvas</title><link>https://mikhail-yudin.ru/blog/frontend/html5-canvas-block-destruction-game/</link><pubDate>Sat, 03 Aug 2019 00:00:00 +0000</pubDate><guid>https://mikhail-yudin.ru/blog/frontend/html5-canvas-block-destruction-game/</guid><description>В детстве очень любил простую игру, где нужно уничтожать блоки одного цвета, чтобы очистить экран. Как начал заниматься изучением HTML5 canvas, решил реализовать её.
Поиграть можно тут: демка.
Суть игры Смысл игры был прост: есть поле, на котором разбросаны разноцветные блоки. Эти блоки гравитацией тянет вниз. Игрок может уничтожать блоки, выбирая один из группы одноцветных рядом по горизонтали и вертикали. Игра очень похожа &amp;ldquo;на три в ряд&amp;rdquo;, разница только в том, что блоки уничтожаются все по близлежащему цвету, не только в ряд.</description><enclosure url="https://mikhail-yudin.ru/blog/frontend/html5-canvas-block-destruction-game/block-destruction-game.png" type="image/png"/><turbo:content><![CDATA[
      		<header>
      			<h1>Игра про уничтожение блоков с использованием HTML5 canvas</h1></header>
            <div class='entry-content'>
              <p>В детстве очень любил простую игру, где нужно уничтожать блоки одного цвета, чтобы очистить экран. Как начал заниматься изучением HTML5 canvas, решил реализовать её.</p>
<p>Поиграть можно тут: <a href="/projects/block-destruction-game">демка</a>.</p>
<h2 id="суть-игры">Суть игры</h2>
<p>Смысл игры был прост: есть поле, на котором разбросаны разноцветные блоки. Эти блоки гравитацией тянет вниз. Игрок может уничтожать блоки, выбирая один из группы одноцветных рядом по горизонтали и вертикали. Игра очень похожа &ldquo;на три в ряд&rdquo;, разница только в том, что блоки уничтожаются все по близлежащему цвету, не только в ряд.</p>
<p>Задача игрока — убрать все блоки, или оставить как можно меньше. За уничтоженный блок начисляется очко. Всегда максимальное количество возможных очков равно площади поля. Поэтому даже если убраны не все блоки, можно соревноваться набирая большее количество очков.</p>
<h2 id="начало-реализации-игры-знакомство-с-html5-canvas">Начало реализации игры, знакомство с html5 canvas</h2>
<p>Первым делом стал заполнять экран блоками. Создал двумерный массив, рандомно назначил цвета.</p>
<p>Далее нужно было написать логику уничтожения блоков одного цвета. Вроде бы алгоритм простой, но столкнулся с трудностью обхода прилежащих блоков. Некоторое время экспериментируя, решил создать дополнительный массив, в котором хранил информацию об обойдённых блоках. Сама функция рекурсивно обходила их по всем четырём сторонам, назначала блоки, подлежащие уничтожению.</p>
<p>Мало сделать уничтожение блоков, предстояло сымулировать гравитацию. Сделать было легко, просто обходим столбцы и смещаем все блоки вниз. В игре есть ещё момент: когда столбец становится пуст, для уничтожения смежных блоков нужно удалить пустой столбец, или сместить оставшиеся блоки так, чтобы получились новые цветовые группы.</p>
<h2 id="оптимизация-отрисовки-html5-canvas">Оптимизация отрисовки html5 canvas</h2>
<p>Ладно, блоки уничтожаются. Процесс обхода оптимизирован. Но всё же игра на html5 canvas тормозила при уничтожении большого количества блоков. Причина была в том, что на перерисовку html5 canvas нужно время. Если блоков достаточно много (ведь играть на поле маленького размера не интересно), перерисовка занимала порой более 1 секунды.</p>
<p>Нужно было срочно с этим разбираться. Стал искать решения в интернетах.</p>
<p>Самое простое, что можно было сделать — это заливать области только после обозначения зон заливки. Но такое решение не особо помогло, нужно было применять более эффективный способ оптимизации.</p>
<p>В итоге я решил использовать информацию об обновившихся блоках, чтобы рисовать только в тех местах, где действительно произошли изменения. Так же создал CanvasBuffer для рисования в фоне и быстрой перерисовки участков.</p>
<p>После этих оптимизаций, игра по уничтожению блоков на html5 canvas получилась настолько быстрой, что при большом количестве блоков и небольшом количестве цветов было ощущение, будто песок осыпается под собственной тяжестью.</p>
<p>Код первоначальной версии игры можно посмотреть на github: <a href="https://github.com/fagci/block-destruction-game" target=_blank rel="noopener noreferrer">block-destruction-game</a>.</p>

            </div>
      	]]></turbo:content></item><item turbo="true"><title>Минимальный файл сброса стилей minireset.css</title><link>https://mikhail-yudin.ru/blog/frontend/minimalniy-reset-css/</link><pubDate>Tue, 30 Jul 2019 00:00:00 +0000</pubDate><guid>https://mikhail-yudin.ru/blog/frontend/minimalniy-reset-css/</guid><description>При создании нового веб проекта необходимо правильно начать разработку, чтобы потом не заморачиваться с лишними отступами, непонятно откуда появившимися, либо с тем, что блоки имеют не ту ширину, которую ожидаешь, изображения занимают больше дозволенной ширины и прочее, чего можно избежать при помощи файла сброса стилей.
Тернистый путь Существует много вариантов, версий normalize.css, reset.css, у каждой есть свои достоинства и недостатки. Приходится вносить правки иногда, переопределять уже существующие стили. К тому же, многие решения довольно тяжёлые, ведь зачем всё приводить к нормализованному виду, чтобы затем переопределять многие из свойств?</description><enclosure url="https://mikhail-yudin.ru/blog/frontend/minimalniy-reset-css/minimalniy-reset-css.png" type="image/png"/><turbo:content><![CDATA[
      		<header>
      			<h1>Минимальный файл сброса стилей minireset.css</h1></header>
            <div class='entry-content'>
              <p>При создании нового веб проекта необходимо правильно начать разработку, чтобы потом не заморачиваться с лишними отступами, непонятно откуда появившимися, либо с тем, что блоки имеют не ту ширину, которую ожидаешь, изображения занимают больше дозволенной ширины и прочее, чего можно избежать при помощи файла сброса стилей.</p>
<h2 id="тернистый-путь">Тернистый путь</h2>
<p>Существует много вариантов, версий normalize.css, reset.css, у каждой есть свои достоинства и недостатки. Приходится вносить правки иногда, переопределять уже существующие стили. К тому же, многие решения довольно тяжёлые, ведь зачем всё приводить к нормализованному виду, чтобы затем переопределять многие из свойств?</p>
<p>До того, как я обнаружил для себя reset.css, приходилось каждый раз указывать своё значение margin, padding и box-sizing, сбрасывать значение list-style для немаркированных списков, потому как при первоначальной вёрстке они чаще всего используются для построения многоуровневых меню, иногда и сами списки перестилизуются.</p>
<p>Чтобы всего этого избежать, можно воспользоваться минимальным файлом сброса стилей.</p>
<h2 id="легковесное-решение">Легковесное решение</h2>
<p>Можно пойти по пути минимализма и сбросить только основные свойства, которые чаще всего требуют сброса. Раньше делал так:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-css" data-lang="css">
<span style="color:#ff79c6">*</span> {
  <span style="color:#ff79c6">margin</span>: <span style="color:#bd93f9">0</span>;
  <span style="color:#ff79c6">padding</span>: <span style="color:#bd93f9">0</span>;
  <span style="color:#ff79c6">box-sizing</span>: <span style="color:#ff79c6">border-box</span>;
}

</code></pre></div><p>И этого было вполне достаточно. Но позже я понял, что так делать не стоит. Селектор так себе, заголовки страдают и ещё кое-какие грабли появляются от такой простоты.</p>
<p>Позже нашёл простое современное решение — минимальный и в то же время более полноценный вариант сброса стилей <a href="https://github.com/jgthms/minireset.css" target=_blank rel="noopener noreferrer">minireset.css</a></p>
<p>В итоге когда начинаю проект, мне хватает только minireset.css для сброса основных стилей и дальше уже приятно вести разработку. Конечно иногда возникает желание дописать что-то в исходник minireset.css, но пока это не дорастает до pull request.</p>

            </div>
      	]]></turbo:content></item><item turbo="true"><title>Семантическая вёрстка сайта</title><link>https://mikhail-yudin.ru/blog/frontend/semanticheskaya-verstka-sayta/</link><pubDate>Wed, 03 Jul 2019 00:00:00 +0000</pubDate><guid>https://mikhail-yudin.ru/blog/frontend/semanticheskaya-verstka-sayta/</guid><description>С приходом HTML5 я стал задаваться вопросом: как правильно использовать семантическую вёрстку используя новые теги? В посте поделюсь опытом вёрстки этого блога с применением семантических тегов.
С чего всё началось Верстая разные проекты, в голову приходили мысли: зачем нужны все эти &amp;lt;main&amp;gt;, &amp;lt;nav&amp;gt;, &amp;lt;section&amp;gt;? Ведь можно и дальше спокойно верстать дивами.
Чем дольше верстаешь сайты, тем больше хочется видеть лаконичный код, структуру которого можно сразу проследить и продолжить вносить правки. Но это ещё не всё&amp;hellip;</description><enclosure url="https://mikhail-yudin.ru/blog/frontend/semanticheskaya-verstka-sayta/semanticheskaya-verstka.png" type="image/png"/><turbo:content><![CDATA[
      		<header>
      			<h1>Семантическая вёрстка сайта</h1></header>
            <div class='entry-content'>
              <p>С приходом HTML5 я стал задаваться вопросом: как правильно использовать семантическую вёрстку используя новые теги? В посте поделюсь опытом вёрстки этого блога с применением семантических тегов.</p>
<h2 id="с-чего-всё-началось">С чего всё началось</h2>
<p>Верстая разные проекты, в голову приходили мысли: зачем нужны все эти <code>&lt;main&gt;</code>, <code>&lt;nav&gt;</code>, <code>&lt;section&gt;</code>? Ведь можно и дальше спокойно верстать дивами.</p>
<p>Чем дольше верстаешь сайты, тем больше хочется видеть лаконичный код, структуру которого можно сразу проследить и продолжить вносить правки. Но это ещё не всё&hellip;</p>
<h2 id="первое-знакомство-с-семантическими-тегами">Первое знакомство с семантическими тегами</h2>
<p>Первый раз я с ними встретился, когда пошли слухи об HTML5. Шаблон типовой страницы был прост в отличие от XHTML.</p>
<p>Например:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-html" data-lang="html"><span style="color:#ff79c6">&lt;!DOCTYPE html&gt;</span>
</code></pre></div><p>вместо</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-html" data-lang="html"><span style="color:#ff79c6">&lt;!DOCTYPE html PUBLIC &#34;-//W3C//DTD XHTML 1.0 Strict//EN&#34; &#34;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&#34;&gt;</span>
</code></pre></div><p>выглядит намного лучше. Да ещё и экономия. К тому же, стало возможным опускать тег <code>&lt;html&gt;</code> в корне документа.</p>
<p>Раньше не обращал внимания на семантические теги, но хотелось везде их использовать. Модно же.</p>
<h2 id="доступность-контента">Доступность контента</h2>
<p>Позже, когда HTML5 стал популярным, я наткнулся на статьи, в которых говорилось о том, что люди с плохим зрением пользуются скрин ридерами, и таки семантическая вёрстка помогает лучше воспринимать контент сайта.</p>
<p>Тогда я познакомился с элементом <code>&lt;nav&gt;</code>, который служит для определения блока навигации. Позже узнал<sup>1</sup>, что использовать связку <code>nav &gt; ul</code> совсем не обязательно и даже вредно, т.к. читалки натыкаются на список и не могут перечислить все названия ссылок за один проход.</p>
<h2 id="seo">SEO</h2>
<p>Немного позже, столкнувшись со статейным контентом стало интересно, что существуют такие теги, как <code>&lt;header&gt;</code>, <code>&lt;article&gt;</code> и <code>&lt;footer&gt;</code>. Тег <code>&lt;article&gt;</code> позволяет определить зону с материалом. Это помогает не только семантически правильно разметить контент сайта, но и помочь сайту лучше выглядеть в поисковой выдаче.</p>
<h2 id="каркас-сайта">Каркас сайта</h2>
<p>Чуть более детально разбираясь в структуре сайта и выделяя семантические блоки, обнаружил, что есть полезный тег <code>&lt;main&gt;</code>. Он определяет основную часть сайта с контентом. При этом использоваться этот тег может только один раз, что позволяет при написании стилей писать селектор <code>main</code> и не использовать классы. Это ещё один шаг к написанию css фреймворков с низким порогом вхождения и лаконичностью.</p>
<h2 id="собственно-блог">Собственно, блог</h2>
<p>Перейдём к практике.</p>
<p>Этот блог я стараюсь верстать, используя минимум телодвижений, без единого фреймворка css/js. Использую максимум семантики, пытаясь обеспечить лучшую доступность и адаптивность для лучшего пользовательского опыта.</p>
<p>Итак, у нас есть основной каркас сайта, который чаще всего приводится к виду:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-html" data-lang="html"><span style="color:#ff79c6">&lt;!DOCTYPE html&gt;</span>
&lt;<span style="color:#ff79c6">html</span> <span style="color:#50fa7b">lang</span><span style="color:#ff79c6">=</span><span style="color:#f1fa8c">&#34;ru&#34;</span>&gt;
  &lt;<span style="color:#ff79c6">head</span>&gt;&lt;/<span style="color:#ff79c6">head</span>&gt;
  &lt;<span style="color:#ff79c6">body</span>&gt;
    &lt;<span style="color:#ff79c6">header</span>&gt;&lt;/<span style="color:#ff79c6">header</span>&gt;
    &lt;<span style="color:#ff79c6">main</span>&gt;&lt;/<span style="color:#ff79c6">main</span>&gt;
    &lt;<span style="color:#ff79c6">footer</span>&gt;&lt;/<span style="color:#ff79c6">footer</span>&gt;
  &lt;/<span style="color:#ff79c6">body</span>&gt;
&lt;/<span style="color:#ff79c6">html</span>&gt;
</code></pre></div><p>У сайтов обычно есть шапка и подвал, но это не обязательно, и зона с контентом страницы.</p>
<p>В шапке часто можно встретить навигацию. Следуя последним рекомендациям в интернетах, я пришёл к мнению, что оптимальным вариантом реализации <em>одноуровневого</em> меню будет:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-html" data-lang="html">&lt;<span style="color:#ff79c6">nav</span>&gt;
  &lt;<span style="color:#ff79c6">a</span> <span style="color:#50fa7b">href</span><span style="color:#ff79c6">=</span><span style="color:#f1fa8c">&#34;&#34;</span>&gt;Link 1&lt;/<span style="color:#ff79c6">a</span>&gt;
  &lt;<span style="color:#ff79c6">a</span> <span style="color:#50fa7b">href</span><span style="color:#ff79c6">=</span><span style="color:#f1fa8c">&#34;&#34;</span>&gt;Link 2&lt;/<span style="color:#ff79c6">a</span>&gt;
  &lt;<span style="color:#ff79c6">a</span> <span style="color:#50fa7b">href</span><span style="color:#ff79c6">=</span><span style="color:#f1fa8c">&#34;&#34;</span>&gt;Link 3&lt;/<span style="color:#ff79c6">a</span>&gt;
&lt;/<span style="color:#ff79c6">nav</span>&gt;
</code></pre></div><p>Дальше у нас следует основной контент страницы. В нём могут присутствовать сайдбары с навигацией или чем-то другим, а так же набор материалов, либо один материал. Например:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-html" data-lang="html">&lt;<span style="color:#ff79c6">main</span>&gt;
  &lt;<span style="color:#ff79c6">aside</span>&gt;&lt;/<span style="color:#ff79c6">aside</span>&gt;
  &lt;<span style="color:#ff79c6">article</span>&gt;&lt;/<span style="color:#ff79c6">article</span>&gt;
&lt;/<span style="color:#ff79c6">main</span>&gt;
</code></pre></div><p>Статья обычно состоит так же, как и страница из заголовка (шапки), footnotes (подвала) и контента. В общем случае, структура такова:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-html" data-lang="html">&lt;<span style="color:#ff79c6">article</span>&gt;
  &lt;<span style="color:#ff79c6">header</span>&gt;
    &lt;<span style="color:#ff79c6">h1</span>&gt;Article title&lt;/<span style="color:#ff79c6">h1</span>&gt;
    &lt;<span style="color:#ff79c6">p</span>&gt;Author, &lt;<span style="color:#ff79c6">time</span>&gt;2019-07-03&lt;/<span style="color:#ff79c6">time</span>&gt;
  &lt;/<span style="color:#ff79c6">header</span>&gt;
  &lt;<span style="color:#ff79c6">div</span>&gt;Article content&lt;/<span style="color:#ff79c6">div</span>&gt;
  &lt;<span style="color:#ff79c6">footer</span>&gt;
    &lt;<span style="color:#ff79c6">small</span>&gt;viewed 15 times&lt;/<span style="color:#ff79c6">small</span>&gt;
  &lt;/<span style="color:#ff79c6">footer</span>&gt;
&lt;/<span style="color:#ff79c6">article</span>&gt;
</code></pre></div><p>На этом о моей практике вёрстки семантическими тегами всё.</p>
<h2 id="что-дальше">Что дальше?</h2>
<p>Позже планируется раскрыть темы:</p>
<ul>
<li>разметка страниц при помощи микроформата</li>
<li>использование h1 в качестве заголовка статьи и наличие h1 в теле статьи</li>
<li>мой способ адаптивной вёрстки сайтов</li>
<li>рассуждения на тему SEO</li>
</ul>
<h2 id="полезные-материалы">Полезные материалы</h2>
<p><a href="https://www.pluralsight.com/guides/semantic-html" target=_blank rel="noopener noreferrer">Эта статья помогла определиться с вариантом реализацией навигации</a></p>
<ol>
<li><a href="https://css-tricks.com/navigation-in-lists-to-be-or-not-to-be/" target=_blank rel="noopener noreferrer">Пост на тему списков в навигации с большим количеством комментариев</a></li>
</ol>

            </div>
      	]]></turbo:content></item><item turbo="true"><title>Оптимизация изображений для Веба при помощи ImageMagick</title><link>https://mikhail-yudin.ru/blog/frontend/imagemagick-convert-optimization-snippets/</link><pubDate>Sun, 30 Jun 2019 00:00:00 +0000</pubDate><guid>https://mikhail-yudin.ru/blog/frontend/imagemagick-convert-optimization-snippets/</guid><description>Часто при работе с изображениями встаёт задача оптимизировать изображения для показа в Веб. Инструментов достаточно много, в том числе и онлайн, однако больше контроля над процессом и более эффективный результат можно получить используя утилиту convert из библиотеки ImageMagick.
Часто нужно сделать так, чтобы изображения подгружались в полном размере, чтобы не плыла вёрстка или не было пустого пространства до полной загрузки изображения. Да и к тому же будет достигнут эффект размытия при загрузке изображения.</description><enclosure url="https://mikhail-yudin.ru/blog/frontend/imagemagick-convert-optimization-snippets/imagemagick-convert-optimization.png" type="image/png"/><turbo:content><![CDATA[
      		<header>
      			<h1>Оптимизация изображений для Веба при помощи ImageMagick</h1></header>
            <div class='entry-content'>
              <p>Часто при работе с изображениями встаёт задача оптимизировать изображения для показа в Веб. Инструментов достаточно много, в том числе и онлайн, однако больше контроля над процессом и более эффективный результат можно получить используя утилиту convert из библиотеки ImageMagick.</p>
<p>Часто нужно сделать так, чтобы изображения подгружались в полном размере, чтобы не плыла вёрстка или не было пустого пространства до полной загрузки изображения. Да и к тому же будет достигнут эффект размытия при загрузке изображения.</p>
<p>Реализовать это очень просто:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-shell" data-lang="shell">convert -strip -interlace Plane -quality <span style="color:#bd93f9">85</span> input-file.jpg output-file.jpg 
</code></pre></div><p>Подробнее про опции:</p>
<p><code>-strip</code> убирает лишнюю метаинформацию<br>
<code>-quality</code> устанаввливает качество результата в процентах; рекомендуется в диапазоне 75..90<br>
<code>-interlace Plane</code> определяет режим подгрузки изображения, в данном случае всё изображение при загрузке в браузере будет отображаться постепенно из размытого состояния к более чёткому.</p>
<p>При работе с PNG изображениями можно достичь меньшего размера, если урезать палитру до минимально необходимого набора цветов:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-shell" data-lang="shell">convert -colors <span style="color:#bd93f9">8</span> input-file.jpg output-file.jpg 
</code></pre></div><p>Где:</p>
<p><code>-colors 8</code> указывает на количество цветов в выходном файле.</p>
<p>После поисков на просторах интернетов сделал скрипт, который проходит по всем поддиректориям и оптимизирует картинки для веб с оптимальным качеством при этом на выходе получаются файлы с малым весом:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-shell" data-lang="shell"><span style="color:#ff79c6">#!/usr/bin/env bash
</span><span style="color:#ff79c6"></span>
find . -name <span style="color:#f1fa8c">&#39;*.jpg&#39;</span> -exec mogrify <span style="color:#f1fa8c">\
</span><span style="color:#f1fa8c"></span>    -filter Triangle <span style="color:#f1fa8c">\
</span><span style="color:#f1fa8c"></span>    -define filter:support<span style="color:#ff79c6">=</span><span style="color:#bd93f9">2</span> <span style="color:#f1fa8c">\
</span><span style="color:#f1fa8c"></span>    -unsharp 0.25x0.25+8+0.065 <span style="color:#f1fa8c">\
</span><span style="color:#f1fa8c"></span>    -dither None <span style="color:#f1fa8c">\
</span><span style="color:#f1fa8c"></span>    -posterize <span style="color:#bd93f9">136</span> <span style="color:#f1fa8c">\
</span><span style="color:#f1fa8c"></span>    -quality <span style="color:#bd93f9">82</span> <span style="color:#f1fa8c">\
</span><span style="color:#f1fa8c"></span>    -define jpeg:fancy-upsampling<span style="color:#ff79c6">=</span>off <span style="color:#f1fa8c">\
</span><span style="color:#f1fa8c"></span>    -define png:compression-filter<span style="color:#ff79c6">=</span><span style="color:#bd93f9">5</span> <span style="color:#f1fa8c">\
</span><span style="color:#f1fa8c"></span>    -define png:compression-level<span style="color:#ff79c6">=</span><span style="color:#bd93f9">9</span> <span style="color:#f1fa8c">\
</span><span style="color:#f1fa8c"></span>    -define png:compression-strategy<span style="color:#ff79c6">=</span><span style="color:#bd93f9">1</span> <span style="color:#f1fa8c">\
</span><span style="color:#f1fa8c"></span>    -define png:exclude-chunk<span style="color:#ff79c6">=</span>all <span style="color:#f1fa8c">\
</span><span style="color:#f1fa8c"></span>    -interlace Plane -colorspace sRGB <span style="color:#f1fa8c">\
</span><span style="color:#f1fa8c"></span>    -strip <span style="color:#8be9fd;font-style:italic">$1</span> <span style="color:#ff79c6">{}</span> + 
</code></pre></div><p>Для быстрого создания изображений в формате WebP из JPEG можно воспользоваться этим:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-shell" data-lang="shell"><span style="color:#ff79c6">for</span> i in *.jpg; <span style="color:#ff79c6">do</span> cwebp -q <span style="color:#bd93f9">80</span> <span style="color:#8be9fd;font-style:italic">$i</span> -o <span style="color:#8be9fd;font-style:italic">$i</span>.webp;<span style="color:#ff79c6">done</span>
</code></pre></div><p>На выходе получим файлы с расширением .jpg.webp.</p>
<p>Способ с обработкой поддиректорий с изображениями и параллельным выполнением:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-shell" data-lang="shell">find . -name <span style="color:#f1fa8c">&#34;*.jpg&#34;</span> | parallel -eta cwebp <span style="color:#ff79c6">{}</span> -o <span style="color:#ff79c6">{</span>.<span style="color:#ff79c6">}</span>.webp
</code></pre></div><p>На выходе получим рядом лежащие файлы с расширением <code>.webp</code>.</p>
<p>Бонус для пишущих на PHP — определение поддержки WebP на стороне сервера:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2"><code class="language-php" data-lang="php">$has_webp = strpos($_SERVER[&#39;HTTP_ACCEPT&#39;], &#39;image/webp&#39;) !== false || strpos($_SERVER[&#39;HTTP_USER_AGENT&#39;], &#39; Chrome/&#39;) !== false;
</code></pre></div>
            </div>
      	]]></turbo:content></item></channel></rss>