Подписка на блог

Ещё есть Тумблер и ЖЖ.

В  Телеграме пишу обо всяком и собираю по крупицам годноту, которая потом появляется в блоге.

А в  Твиттере иногда репощу всякие смехуечки и пишу о том, как всё плохо.

Все остальные соцсеточки только для того, чтобы пошарить ссылку на новый пост.

Игорь Адаменко

Годнота — 29

Сегодня про фронтенд, физику, английский язык, убийства и ЗППП.

Статьи

Видео

Однажды в Википедии

Книги

  • Омон Ра, Пелевина.
    Мне говорили, что первые книги Пелевина прям вставляют, но я и не думал, что настолько. Всячески советую.

Кино

  • Дэдпул 2.
    Если ходили на первую часть и вам понравилось, то обязательно сходите и на вторую. Она ещё кайфовее, ещё больше 18+. Грязные шутки, тонны крови, в общем, всё как надо :-)
  • Первому игроку приготовиться.
    Немного детский фильм, но зато столько пасхалок и отсылок в одном месте. Да и, в целом, весь сюжет про пасхалки и отсылки. Кажется, довольно символический фильм для всего этого постмодерна с его погоней за отсылками к отсылкам к отсылкам.

Сериалы

Интересное из Телеграма

Я как-то давно не публиковал годноту, потому что думал, что её собралось мало. Оказалось всё совсем наоборот. Потому я не смог выбрать какой-то один пост из интересных в канале. Держите сразу несколько, на совсем разные темы (надеюсь, РКН разрешит вам их посмотреть; если нет, то: 437, 443, 493 и 500):






Прошлые подборки по тегу — годнота. А собираю я их в Телеграме.

Ремарку нужна помощь

Пару месяцев назад Умпутун предложил мне написать фронтенд для системы комментирования, которую он сделал — Remark42. И вот что из этого получилось:

github.com/umputun/remark

Первоначальной целью было создание чего-то управляемого взамен Дискаса для сайта Радио-Т, что можно было бы в будущем расширять в нужные стороны. Плюс хотелось сделать визуально более лёгкий интерфейс. Мы не дизайнеры, потому сложно судить, получилось у нас или нет, но мы по крайней мере пытались не перебарщивать.

Как это обычно случается, теперь у меня не хватает времени на то, чтобы полноценно поддерживать проект, разрабатывать новые фичи и править баги, потому я ищу того (а лучше «тех»), кто возьмётся делать это вместо меня. Первое время я ещё послежу за процессом, а дальше и вовсе оставлю всё на энтузиастов, у которых больше времени и сил.

Почему, как мне кажется, вам будет интересно заниматься этим проектом?

Во-первых, это довольно плотная работа с бэкендом, АПИ и всем подобным. Вам придётся самостоятельно продумывать, как удобнее получать данные с сервера, как правильнее их хранить и преобразовывать.

Во-вторых, это про архитектуру проекта. Никто за вас не продумает, как обрабатывать данные, отправлять запросы, разбивать на компоненты и использовать библиотеки. И это круто, потому что можно самому принимать достаточно важные решения, и набивать шишки, которые в будущем можно будет называть «опытом разработки».

В-третьих, это про рабочее окружение. На начальном этапе нужно было быстро всё написать, чтобы проверить, взлетит или нет, потому, если у Умпутуна с кодом всё прекрасно, то у меня полный раздрай: почти нет тестов, нет моков, не все линтеры настроены, рабочий процесс налажен абы как. И это, с одной стороны грустно, и эксперты в этих ваших интернетах уже успели посетовать на то, что всё плохо, писал дебил и надо, конечно же, всё удалить и начать с нуля, и вообще вон из профессии. Но на практике это круто, потому что можно настроить всё это дело самостоятельно, разобраться, как оно работает, сделать так, как вам кажется правильным.

Таким образом, если вы начинающий разработчик, или у вас просто есть свободное время и желание сделать что-то хорошее, то приходите, у нас есть много задач (и не только на фронтенде), некоторые из которых без проблем можно сделать даже без глубоких знаний в предметной области (такие помечены как good-first-issue):

github.com/umputun/remark/issues

Ну и, конечно, нам очень нужны свежие идеи на тему того, что улучшить и как. Будет клёво, если вы нам расскажете, что сейчас не так, и посоветуете, как мы можем это исправить.

РКН и ссылки на Телеграм

С тех пор, как РКН начал блокировать Телеграм, прошло уже вроде больше месяца. Телеграм всё ещё жив. Но этот пост не про то, как немощен РКН, а про то, что все упустили из виду одну проблему.

Проблема

Домен t.me заблокирован у большинства пользователей.

В масштабах использования мессенджера это вообще не важно, потому что никак не влияет на отправку сообщений, файлов, стикеров и пр. Приложения чуть тормозят, но обходят блокировку. Домен же не меняет своего адреса, потому если у вас его заблокировали, сам он уже никак не разблокируется.

Многие пользуются VPN, потому даже не заметили этого. Но большинство для доступа к Телеграму используют SOCKS5-прокси, который никак не влияет на доступность домена. Потому, если вы где-то в интернете делитесь ссылкой на себя, на свой канал или на какой-то пост, то есть большая вероятность того, что по ней не смогут перейти.

Однако, РКН может заблокировать что угодно, но не может заблокировать принципы работы веба. Телеграм запускает приложение с помощью перехода по адресу, который начинается с протокола tg. Потому, если у пользователя уже установлено приложение, то заменив все ссылки на t.me ссылками с протоколом tg, можно сделать их все доступными независимо от блокировок.

Правда, тут возникает иная проблема. У пользователя может быть не установлено приложение, или из-за каких-то проблем схема может не сработать (например, пользователь установил себе бета-версию Телеграма, или какой-то вообще иной клиент, который по каким-то причинам не работает с протоколом tg). Потому легче совместить два решения в одно.

Решение

Если вы нажмёте на ссылку ниже, то увидите, о каком решении я говорю:

https://t.me/igoradamenko_channel

При открытии с десктопа пользователь попадёт на страницу, на которой написано о том, что происходит:

Если у него установлено приложение, то он увидит стандартный запрос:

Дальше всё как обычно.

На мобильных аналогичная история, даже проще. Там приложения могут перехватывать не только протоколы, но и целые адреса, потому обычно вы не видите страницы t.me, т. к. ОС сразу перенаправляет вас в приложение. С этим решением будет аналогичная история, потому что обычно на мобильных после перехода в приложение с помощью протокола, исходная страница закрывается. Потому пользователь тут тоже не увидит промежуточной страницы.

Относительно кода всё просто. Телеграм формирует ссылки примерно таким образом:

tg://resolve?domain=igoradamenko_channel # ссылка на канал или пользователя
tg://resolve?domain=igoradamenko_channel&post=454 # ссылка на конкретный пост в канале
tg://join?invite=ABCDEFGHIJKLMNOPQRSTUV # ссылка-инвайт

Потому всё, что от вас нужно, это переадресовать все телеграмные ссылки на какую-то промежуточную страницу, например так:

document.addEventListener('DOMContentLoaded', function () {
  [].slice
    .call(document.querySelectorAll('a[href^="https://t.me"]'))
    .forEach(function (x) {
      x.href = location.origin + '/tg.php?url=' + x.href;
      x.target = '_blank';
    });
});

На ней распознать переданную в параметре ссылку, сконвертировать её в вариант с протоколом, и переслать пользователя по ней, добавив на сервере в код страницы что-то вроде такого:

<script>
  window.location.href = 'tg://resolve?domain=igoradamenko_channel'
</script>

У этого решения есть две проблемы.

Первая — Телеграм может поменять структуру ссылок. Но мы поверим, что они так не сделают.

Вторая — наличие промежуточной страницы. На самом деле этого можно избежать, если накодить кучу хаков. У браузеров различное поведение при переходе на страницы с протоколом, который не поддерживается (в нашем случае поддержки не будет, если у пользователя не установлен Телеграм), потому можно для каждого из них написать код, который будет это поведение обрабатывать, и если что, перенаправлять пользователя на t.me, а надежде, что страница у него откроется. На Гитхабе можно найти решения вроде custom-protocol-detection.

Однако, я решил выбрать более надёжное и явное решение. Такие дела ¯\_(ツ)_/¯.

Четыре вопроса про БЭМ

Последнее время меня всё чаще спрашивают про БЭМ. Потому я собрал самые частые вопросы и ответы на них в статью, чтобы было легче отвечать. Как обычно ¯\_(ツ)_/¯.

БЭМ?

Сперва стоит оговорить несколько важных моментов.

Во-первых, речь пойдёт о БЭМ в вёрстке и БЭМ на файловой системе. Основные моменты можно прочитать разделе «Быстрый старт» документации. То есть, речь не о «полном БЭМ-стеке», который вкалывают себе ребята из Яндекса. Это слишком тяжёлый наркотик для обывателя. Не пробуйте, если совсем неопытны.

Во-вторых, и это важно, каждый трактует БЭМ по-своему. И у этого есть одна очевидная причина — БЭМ почти не ограничивает. По сути вся методология — это набор советов, или, если угодно, максим, которые просто определяют границы добра и зла. А то, в каких пропорциях их смешивать (и смешивать ли) — это уже на вашей совести. Однако, это не значит, что можно воровать-убивать в коде потому что «художник так видит».

Вопрос первый, про именование сущностей и выделение блоков

Спрашивает М. из, наверное, славного города Ш.:

Я уже в который раз на это натыкаюсь и не могу определиться, как правильно.

Допустим, мне нужно сделать список. Как я обычно делаю:

.list
  .list__item
  .list__item

Тут всё ок. Но что, если этот пример усложнить? Если list__item — это сложный блок, у которого есть свои элементы, то нужно создавать отдельный блок или делать это элементами list?

Я думал, что правильно именно так:

.list
  .list__item
    .list-item  // новый блок
      .list-item__title
      .list-item__button
  .list__item

Но вот это создание дополнительного блока из двух элементов меня всегда напрягало, поэтому я задумался, может быть правильнее будет так:

.list
  .list__item
    .list__title
    .list__button
  .list__item

Но с другой стороны list__title говорит о том, что это заголовок листа, а не одного элемента. И вот это меня постоянно вводит в ступор.

Дорогой М.!

Давай разберём по порядку.

Во-первых, про выделение элемента в отдельный блок. Это можно сделать, но на практике нужно очень редко. Обычно это сигнал к тому, что блок в целом перегружен, что ты пытаешься сделать его очень универсальным, и скорее всего можно сделать намного проще, если позволить себе некоторое дублирование, или же введение большего количества модификаторов.

Часто такой выделенный в самостоятельный блок элемент называется «служебным» или «приватным». Когда-то так делали ребята из Яндекса, например, в своей библиотеке компонентов.

Приватным он называется потому, что в силу его истории его можно использовать только внутри блока, из которого его выделили (в твоём случае только внутри list). Формально — это нарушение методологии, которая утверждает, что блок должен быть независимым. На практике же — это сделка с совестью, которая или позволяет тебе делать так, или не позволяет. Если ты не уверен, что чётко понимаешь себе плюсы и минусы такого решения, то лучше так не делать.

Во-вторых, вот такой код не очень крут:

.list
  .list__item
    .list-item  // новый блок
      .list-item__title
      .list-item__button
  .list__item

В большинстве случаев правильнее будет сделать так:

.list
  .list-item.list__item
    .list-item__title
    .list-item__button
  .list-item.list__item

То есть, в подобных случаях удобнее смиксовать элемент item блока list с тем приватным блоком, что ты выделил. Тогда list-item будет отвечать только за внешний вид самого элемента списка, а list__item — за его позиционирование в рамках всего списка (которое может меняться в зависимости от модификаторов).

Удобнее, потому что так у тебя не появляется лишней обёртки, а это помогает избежать в будущем проблем с позиционированием.

В-третьих, вот этот код тоже решает поставленную проблему:

.list
  .list__item
    .list__title
    .list__button
  .list__item

И тут тебя верно смущает тот факт, что list__title как бы намекает, что это «название списка». Но это зависит от того, как именно ты и твоя команда понимает БЭМ. Для меня, например, это «какое-то название внутри списка». Это вполне может быть и названием пункта, и названием целого списка и пр. Некоторые для устранения таких разночтений делают, например, list__item-title. Однако, это не всегда хорошо и если таких элементов появляется много, то это как раз звоночек о том, что list__item перегружен, и возможно стоит выделить его в отдельный приватный блок.

Или же не париться и делать более абстрактные элементы, типа list__title.

Как я уже говорил выше, БЭМ накладывает не так много ограничений, и скорее лишь указывает «как было бы правильнее». А как трактовать это — это уже зависит от команды. В каждом из решений есть свои изъяны:

  • list__item-title рождает новый уровень зависимости. Потому что получается, что есть отдельные элементы, которые привязаны к другим элементам. Но это решается просто игнорированием этого факта и принятием того, что это норма. Да, бывают элементы «заголовок пункта». И что? Не написано же, что это list__title-of-list__item. Просто заголовок какого-то пункта.
  • list-item__title рождает новый блок, который получается жёстко привязан к list и не может использоваться в отрыве от него. Но это решается принятием того, что у нас есть такие блоки. Иногда бывает намного удобнее декомпозировать таким образом, ничего не поделаешь. Как только мы принимаем, что у нас бывают подобные блоки, жить становится легче. Главное определить для себя, когда они нужны, а когда они излишни.
  • list__title же рождает непонятку, о которой ты написал. Но это решается бóльшим абстрагированием от общей сути блока. Это просто какое-то название внутри списка. Зато это даёт чуть больше гибкости. Например, у тебя может быть list__title_type_main и list__title_type_item. А может и не быть.
  • list-title, кстати, тоже решение. Оно, пожалуй, наиболее далёкое по смыслу от всего того, что есть тут. Но с другой стороны, если у тебя есть разные списки, то list-title вполне может быть таким элементом, который реализовывает заголовок внутри любого списка.

Ключевой смысл БЭМ в независимости между блоками, которую мы по умолчанию определяем. Если в каких-то местах нам нужно во имя чистоты абстракции и читабельности кода пожертвовать этой независимостью, лучше это сделать. Потому что поддерживать код куда важнее, чем писать его «абсолютно правильно».

Вопрос второй, про общие ресурсы

Вопрошает И., из славного города К.:

Что делать и как быть, если нужно зашарить один объект между блоками? Например, картинку.

Дорогой И.!

Здесь сперва нужно определиться с тем, какое именно предназначение у этого объекта. Их может быть несколько, вот наиболее часто встречающиеся:

  1. Самостоятельная единица, которая может использоваться без привязки к конкретным блокам. Например, логотип или какое-то отдельное изображение на каком-нибудь лэндинге, которое встречается там несколько раз.
  2. Обычное изображение, которое по каким-то причинам пришлось использовать в двух блоках сразу.

Теперь про каждую подробнее.

Самостоятельная единица

В общем-то, формулирование проблемы уже описывает решение. Если это изображение самостоятельное, его вполне можно выделить в блок. Проблема, с которой сталкиваются новички, обычно в том, что «Но тут же нет вёрстки!». Ну и ладно ¯\_(ツ)_/¯. БЭМ про абстрактные блоки, которые «реализуются» в различных технологиях. Условно говоря, PNG, JPEG, WEBP и пр. — это всё эти самые технологии, в которых может быть реализован блок.

Дальше только возникнет вопрос, как именно это удобнее всего подключать в какую-то иную вёрстку. В идеале у такого блока должно быть представление в виде класса. То есть, это может быть, например, что-то такое:

<img src="./block.png" class="block">

Тогда это успешно ложится на абстракцию БЭМ-дерева. Плюс это можно расширять. Например так:

<img src="./_type/_mobile/block_type_mobile.png" class="block block_type_mobile">

Т. о. мы выделили отдельное изображение для этого «блока с изображением», которое будем показывать, например, только на мобильных. Дальше ему можно добавить микс и использовать в какой-либо композиции:

<div class="parent-block">
  <img src="./_type/_mobile/block_type_mobile.png" class="block block_type_mobile parent-block__image">
</div>

Во всех этих примерах класс на изображении не несёт, будто бы, никакого смысла, потому что мы не задали ему стилей. И тут наступает очередной момент, когда нужно или принять методологию в качестве непреложной истины, или пойти на сделку с совестью.

В первом случае нужно будет оставить классы как есть, потому что БЭМ в вёрстке — он о классах. И блока не существует в БЭМ-дереве, если у него нет класса.

Во втором случае можно убрать классы, поскольку то, что это за изображение, и так понятно по пути его подключения, за счёт того, что у нас БЭМ на файловой системе. Делать так или нет, зависит только от того, насколько вы уверенно чувствуете себя в коде и методологии, и от соглашений, принятых в команде.

Внезапное дублирование

Иногда так бывает, что дизайнеры рисуют-рисуют дизайн, и тут вдруг переиспользуют какую-то картинку, которая просто удачно смотрится в двух местах.

В таком случае сперва нужно задуматься, нельзя ли её выделить в какой-то отдельный блок. То есть, точно ли в этих двух местах используется одна и та же картинка, или это просто «визуально одинаковые сущности». Тут поможет банальный вопрос: «Если картинка изменится в одном месте, нужно ли будет менять её в другом?». Если да, то это скорее всего независимая сущность и её нужно выделять в блок так, как было описано выше.

Если же нет, то можно просто положить две одинаковые картинки в разные блоки, и ничего страшного в этом нет ¯\_(ツ)_/¯.

Если вас смущает тот факт, что у пользователя будет дважды скачиваться одна и та же картинка, то просто настройте свою сборку так, чтобы этого не было. Например, если всё собирается Вебпаком, то у лоадера для файлов есть настройки, которые позволяют менять имя изображения на хэш. Т. о., если где-то внезапно будут использоваться два одинаковых изображения, они получат один и тот же хэш, потому в итоговом бандле будет лежать только один файл, и пользователь не будет скачивать его второй раз, когда он потребуется.

Вопрос третий, про модификаторы

И., из почти славного города Ж., интересуется:

А что делать, если я создаю модификатор только с одним значением? Например,
у меня есть кнопка, у неё есть какой-то размер, но тут вдруг мне нужна кнопка побольше. И я создаю button_size_x.

Получается, что у меня есть какое-то значение по умолчанию, но есть и модификатор. Однако, у него только одно значение. Глупость же какая-то, не?

Дорогой И.!

На самом деле и правда получается глупость. Неопытный верстальщик в этом случае вообще создаёт булев модификатор, вроде button_big и успокаивается. Но это ещё бóльшая ошибка.

Во-первых, почему не булев? Потому что «размер» — это такой параметр, который может иметь разное значение. И если мы изначально делаем его булевым модификатором, мы закрываем себе возможности для расширения (тут можно было бы отослать читателя на статью про принцип открытости-закрытости, но я пока не буду). И когда завтра понадобится сделать кнопку маленького размера, придётся переписывать тот код, что уже написан. Или же придётся вводить button_small. Объяснение того, почему это неправильно, пусть будет домашним заданием для читателя.

Во-вторых, а что же делать? А решение на самом деле простое. Для того, чтобы понимать, как поступать в таких случаях, удобно представлять блок как функцию, а модификаторы — как параметры. В нашем случае получается какая-то вот такая функция:

function drawButton(size) {
  if (size) {
    // рисует кнопку указанного размера
  } else {
    // рисует кнопку какого-то иного размера
  }
}

Для упрощения понимания можно представлять, что size тут — это числовое значение. Если у читателя есть какой-то опыт программирования, он может сразу понять, что плохого в такой функции. А плохо то, что неизвестно, какого размера кнопка будет по умолчанию. Вместо явной параметризации мы получаем «параметризацию + какое-то иное поведение».

Чтобы избавиться от этого иного поведения программисты чаще всего делают просто:

function drawButton(size = 10) {
  // рисует кнопку указанного размера
}

И в таком случае по нотации функции уже понятно, что она сделает, если размер не передан — будет использовано значение 10.

Аналогично нужно поступить и в данном случае. Нужно имеющийся размер кнопки выделить в модификатор, который будет выставляться по умолчанию (с помощью шаблонизаторов это делается легче лёгкого), либо задаваться везде вручную, если вам так комфортнее.

В данном случае значением по умолчанию для модификатора size может быть, например, m. Но это уже сильно зависит от контекста.

Вопрос четвёртый, про скрипты

В редакцию пишет Л. из города-героя М.:

Вот элемент в форме, в шаблоне:

<div onclick="handleClick"></div>

В доисторические времена функция handleClick лежала рядом в этом же файле:

<script>
  function handleClick() { }
</script>

И подключалась прозрачно.

Когда на Луне обнаружился черный обелиск и в нем БЭМ, наступили наши времена, и я вынес скрипты в отдельный файл.

Файл подключается, и почти все гуд, кроме одного неудобства: он, похоже, подключается уже после всей вёрстки (или наоборот, до).

В момент, когда вёрстка парсится браузером, никакой функции handleClick он еще (или уже?) не знает. Я нашел корявый выход — у всех связанных со скриптами элементов делаю айдишники и функцию инициализации вешаю на onload событие.

Все работает, но необходимость вместо простого указания у блока в коде онклика устраивать пляски с инитами на домлоаде — какой-то адок. Нечитаемо, ошибкоемко, и громоздко, и нелаконично, и пространно, и вычурно, и многословно, и тумач букв.

Есть ли какой-то выход?

Дорогой Л.!

Это уже не столько про БЭМ, сколько про особенности сборки, но давай всё равно разберёмся.

Если бы мы говорили о каких-то фреймворках, в которых компонентный подход навязывается насильно (Ангуляр, Реакт и пр.), то там бы такой проблемы не возникло, потому что там компонент — это штука, которая обмазана скриптами донельзя, и потому скрипты не нужно привязывать к вёрстке. Скорее даже наоборот.

Но в твоём случае всё несколько иначе, потому что ты, видимо, привязываешь какую-то вёрстку к какому-то скрипту, и не можешь привязать их надёжно, чтобы они не разваливались.

Тот вариант, что ты предлагаешь — положить скрипты прямо в вёрстку — вполне рабочий, и в нём нет каких-то особых проблем. Если мы говорим об одной маленькой страничке, где есть пара блоков и небольшие скрипты для них, то это возможно куда более правильный путь, нежели выделять скрипты в отдельный файл и пр.

На практике же используется, обычно, несколько иной подход. А именно:

  1. Скрипты подключаются в самом конце тега body.
  2. Скрипты пишутся с оглядкой на то, что инстансов блока может быть больше одного.

Первый пункт позволяет нам быть уверенными в том, что скрипты начнут выполняться после того, как распарсится вся вёрстка. Иногда это не нужно, но чаще всего этого достаточно.

Второй пункт позволяет нам не плясать с идентификаторами, а использовать всё те же классы, которыми мы оперируем в БЭМ.

В общем случае получается что-то такое. Вёрстка:

<!-- ... -->
<body>
  <!-- ... -->
  <div class="block">
    <!-- ... -->
  </div>
  <!-- ... -->

  <script src="app.js"></script>
</body>
<!-- ... -->

JS-файл, при этом, скорее всего будет собираться как-то из других JS-файлов (из-за того, что у нас БЭМ на файловой системе), но предположим, что блок всего один. Тогда примерно вот таким будет JS-файл:

Array.from(document.querySelectorAll('.block')).forEach(initBlock);

function initBlock(node) {
  // какие-то действия над блоком 
}

В зависимости от условий, в которых всё это применяется, могут быть какие-то изменения. Например, если по каким-то причинам нельзя подключать скрипты в конце документа, то скрипт может принимать такой вид:

document.addEventListener('DOMContentLoaded', () => Array.from(document.querySelectorAll('.block')).forEach(initBlock));

function initBlock(node) {
  // какие-то действия над блоком 
}

Или, если есть уверенность, что это очень нужно (не стоит так делать без чёткого понимания разницы), то он может быть и таким:

window.addEventListener('load', () => Array.from(document.querySelectorAll('.block')).forEach(initBlock));

function initBlock(node) {
  // какие-то действия над блоком 
}

Если скриптов на проекте много, то всё это можно спрятать в какую-то функцию, которая будет использоваться во всех файлах для инициализации блока. Типа такого:

// определённая где-то глобально функция
function initBlock(selector, fn) {
  Array.from(document.querySelectorAll(selector)).forEach(fn);
}

// локальное применение для конкретного блока
initBlock('.block', node => {
  // какие-то действия над блоком
});

Главное во всей этой истории то, что «какие-то действия над блоком» должны быть написаны так, чтобы подразумевалось, что таких блоков может быть любое количество на странице.

∗∗∗

Кажется, это были четыре наиболее часто задаваемых вопроса. Если вам кажется, что я что-то упустил или где-то неправ — пишите в комментарии. А пока вот несколько полезных ссылок по теме:

  • Описание методологии от Яндекса.
    Кажется, с каждой итерацией обновления этой документации, ребята из Яндекса всё дальше уходят от описания полного БЭМ-стека к более абстрактному описанию методологии. Это не может не радовать.
  • Принципы SOLID.
    Забавно, но в БЭМ, кажется, больше от программирования, чем от вёрстки. К слову, некоторые из этих принципов и их применение к БЭМ описаны в официальной документации.
  • Базовые понятия и принципы БЭМ.
    Мой коллега когда-то написал вот такую довольно академичную выдержку из своего доклада. Читать нужно аккуратно и вдумчиво, как будто это учебник по математике.
Блок кода, который тут только для того, чтобы Эгея подключила highlight.js, потому что иначе она не хочет этого делать. (╯°□°)╯︵ ┻━┻)

Годнота — 28

Сегодня про развитие специалиста, доказательную медицину и флекс.

Статьи

Видео

Однажды в Википедии

Книги

  • «Кто бы мог подумать!», Ася Казанцева.
    Крутая книжка, написал коротко о каждой главе в канале.

Кино

Сериалы

Интересное из Телеграма



Прошлые подборки по тегу — годнота. А собираю я их в Телеграме (кажется, около сотни постов не вошло в опубликованный список).

Ctrl + ↓ Ранее