Как стать автором
Обновить

Семь простых советов о том, как не надо верстать

Время на прочтение14 мин
Количество просмотров21K
Утро тяжелого дня
Утро тяжелого дня

Эта статья является продолжением моего «крестового похода» против ветряных мельниц убогих современных тенденций в разметке и оформлении веб-приложений (статья1, статья2). И, поверьте — солидная ее часть — это толерантная, такая, чтобы никоим образом не нарушить NDA, переработка реального доноса код-ревью кода важного боевого проекта для руководства одной из команд в которых мне приходилось участвовать. До этого момента я породил уже три достаточно злых, токсичных длиннопоста для сообщества, и, о чудо — ни один из них не умудрился скатился в минус (но последний был близок). И на этот раз — я готов! Ибо этот пост именно о тех технологиях и подходах к верстке, которые мне[, конечно же, на основе коммерческого опыта] кажутся весьма неудачными и неэффективными, неадекватными в очень многих ситуациях. Конечно, существуют команды, проекты, требования когда каждый из этих подходов может окажется вполне приемлемым и уместным. Но на деле, чаще всего, имхо, оказывается, что поборники данных методов — безальтернативно «подсаживаются на любимую иглу» и упорно не хотят знать и уметь, использовать ничего другого... Мне вообще кажется, что мир вокруг нас сейчас это — утопия, практически целиком, максимально упрощенная тотальным засильем пролоббированного рептилоидами, мировой закулисой корпорациями тоталитарного глобального мейнстрима, и это одинаково касается всех сфер жизни, вот тут можно почитать мою философскую статью на тему применительно как раз к интерфейсам Куда подевались социальные сети? Пропаганда и реклама вместо общения... И даже наш любимый журнал о технической культуре, по сути, превратился в рекламную помойку, по большей части, унылое отражение глобального общественного тренда... Но, как известно — «главное попытаться», поэтому — поехали!

Ошибки в работе с пространством

Неоправданное и, при этом, неявное использование flexbox

Мемы в интернете по поводу сложностей в верстке
Мемы в интернете по поводу сложностей в верстке

Очень многие проблемы в верстке бывают связаны с тем, что проектирующие сейчас разметку разработчики используют «модную» flexbox-раскладку вообще везде и для всего — и для чего надо, и для чего ну совсем негоже — практически для любого позиционирования элементов, в том числе и для крупных лейаутов. Когда я вижу такое, то сразу начинаю подозревать, что автор никогда не верстал писем, сетки под IE6-7-8-9, «ворвался» совсем недавно, но, при этом, поленился, например, пройти базовые курсы, как, впрочем, видимо, и поучить-потренировать такой же свежий, только для modern bro — grid-layout. Часто так делают именно для передачи статичного макета который будут принимать pixel-perfect. Вам кажется удобным делать «гибким и резиновым» то, что «должно быть четко и статично», идеально точно? Вероятно, вы никогда не делаете муторную подгонку сложных замороченных и нелогичных художеств-хотелок дизайнеров/клиентов? Я понимаю когда нужно «простым образом позиционировать что-то посередине».) Ну это значит, что, например, с вашими любимыми Styled Components, в арсенале «утилит-компонентов» переиспользуемых по всему проекту должен быть некий явный контейнер <FlexboxContainer mode=”center” /> — явно выражающий это! Нет, «тыжпрограммистна JSX/CSS-in-JS» и панически избегаешь всего, что связано с дизайном и его самостоятельными абстракциями, извините... Многие все делают флексом, но это даже никак не обозначено — никакой дизайн-абстракции не существует в принципе. Просто пара правил, в результате — повторяющихся в коде стопитцот раз — в классах CSS-модулей безо всякого маломощного composes, в «инкапсуляциях» стилевых компонент или как не читаемая «ишак вижу — ишак пою» «утилита в стиле Tailwind» на разметке... Требования изменились, или, обыденно — фиксим что-то всплывшее-неожиданное, кто-то добавил обертку-контейнер не в том месте, потерялось или появилось ненужное правило flex-direction или justify-content — где-нибудь еще «все развалилось» — фиксим снова! «Баги есть — есть работа!».

Когда наконец закрыл висевший пару месяцев баг
Когда наконец закрыл висевший пару месяцев баг

Совет 1. Особенно передавая статичный макет для pixel-perfect — опирайтесь на обычный статичный поток, используйте контейнеры, обертки которым можно выставлять высоту или абсолютное позиционирование относительно relative-родителя. Изучите, хотя бы бегло, старперские простые классические приемы, используйте Grid-раскладку для собственно сеток, а Flexbox-модуль — только там где это действительно необходимо и уместно. Чаще всего: для «мелкого», локального позиционирования внутри небольших конечных блоков, виджетов.

«Бесхребетная» разметка

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

Котик смотрит на твою верстку на React с CSS-in-JS
Котик смотрит на твою верстку на React с CSS-in-JS

Я заметил что пишущие на React c CSS-modules молодые фронтенд-программисты часто как будто «экономят divы». И понятно почему: «якобы излишнее обертывание элементов в блоки в стиле БЭМ» это уже не модно и они просто так уже не мыслят. У нас сейчас наконец-то все просто и ясно, никакого творчества-отсебятины: блок это компонент, внутри — элементы строго как в дизайне, ничего лишнего, плюс надежный модуль стилей для каждого! Даже если эта надежность совершенно излишняя и запутанная, а нам, наоборот, нужна легкая и быстрая модификация, переиспользование. О том как бывает тяжело изменять такую надежность и как на самом деле эта святая простота выглядит в коде — я расскажу немного ниже...

Совет 2. Не «экономте <div>-ы»! Формируйте контейнеры-обертки, блоки, ну, или вводите общие компоненты.

Магические кряки и хуки вместо просто хорошей верстки

Какой-нибудь очередной «манипулирующий с DOM» хук, например, опирающийся на «магические айдишники» (а ref-ы уже не получится использовать, так как у нас все в разных несвязанных компонентах) — всегда придет на помощь, даже если вместо этого нужно было вовремя осознать и написать один «лишний» <div> и простые стилевые правила к нему. Нет, мы не ищем простых путей, предпочитаем писать хуки на единственной технологии которую хорошо знаем и потом подбирать возвращающиеся баги когда эти кряки отваливаются. Не очевидное но важное заблуждение пишущих исключительно в описываемом стиле разработчиков в том, что сложный современный дизайн вообще возможно качественно и быстро «разметить» полностью избегая выделять отдельный слой глобальной стилевой абстракции и пользуясь одной только компетентностью, только ей ограничивая переиспользуемость. Может быть с помощью CSS-in-JS действительно удобно верстать некий строгий интерфейс с виджетами большой командой, в котором «рюшечки не важны». Но не, например, набор «бохатых-шикардосных» лендингов быстро с красивым размашистым дизайном и повторяющимися, но разнообразно модифицируемыми паттернами на них. Дальше я расскажу о том почему использование CSS-in-JS на подобных проектах это большая ошибка и мешает делать быструю качественную верстку.

Совет 3. Очень банальный, хорошо известен: делайте с помощью верстки все что можно сделать версткой. Делать плохую верстку для того чтобы потом фиксить ее хуками — это ненадежно, срывает сроки и общепризнанно плохой стиль.

Нипофиксиль, ниуспель
Нипофиксиль, ниуспель

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

В дизайне очень часто встречаются модификации, отклонения, исключения — дизайнеры все очень творческие, в клиенты иногда — еще более творческие, и, главное — требовательные. Давайте поглядим что нам нужно было сделать раньше для того чтобы модифицировать набор стилей на некотором элементе, скажем так: на HTML с глобальными стилями на любом препроцессоре, псевдокод:

// Исходный стиль
.style {}

// Разметка
<div class="style" />

// Добавляем модификатор
.style {
    &--mod {}
}

// Отправляем на разметку
<div class="style style--mod" />

А теперь возьмем некий модный проект на, конечно же — передовых технологиях от bigTech-гигантов занимающих 80-85% рынка вакансий, например React [c GraphQL] + Flow (TypeScript это для умников, энтерпрайза и игры в долгую, понятно) + да и такое бывает, честное слово: SCSS + «стандартной технологии для защиты стилей» CSS Modules + Styled Component тоже прикручены.)))

Забегая вперед: то, что раньше действительно, без преувеличений — делалось за две минуты, сейчас иногда занимает полчаса и чревато сложноуловимыми неочевидными ошибками. Вот этот наш простой самый распространенный будничный прием при переиспользовании разметки — нужно из «конечной вьюхи» прокинуть класс-модификацию для специфической стилизации элемента на данном специфическом виде. Теперь нам нужно проходить через всю цепочку компонентов чтобы передать класс, при этом, слов нет, везде проверяя что это string с Flow и нигде не ошибиться. Давайте посмотрим как выглядит наш код, переиспользуемый компонент-шаблон в библиотеке @src/components/Template/Template.js, очень упрощенно, в реальности все намного сложнее:

// @flow

// Libraries
import * as React from 'react';
import classNames from 'classnames';
// ...

// Components
import TemplateChild from './components/TemplateChild';
// ...

// Styles
import styles from './Template.scss';

type PropTypes = {
  children: React.Node,
  classes?: {
    root?: string,
    child?: {
      root?: string,
      class1?: string
      // ...
    }
  },
  prop1: type,
  prop2: type,
  ...
};

const Template = ({
  children,
  classes = {},
  prop1,
  prop2,
  // ...
}: PropTypes): React.Element<'div'> => {
  // Hooks
  // ...

  return (
    <div
      className={classNames(classes.root, styles.Root, {
        [styles.RootMod]: prop1,
        // ...
      })}
    >
      <TemplateChild
        className={{
          root: classes?.child?.root,
          class1: classes?.child?.class1
        }}
        // ...
      />
      { children }
    </div>
  );
};

export default Template;

Стили для него:

.Root {
	// ...
	
  &Mod {
  	// ...
  }
}

Индекс:

// @flow

export { default } from './Template';

Дальше: общий компонент-вида в @views/View/components/Template/Template.js - да, да, именно так — с тем же названием, но во вьюхе:

// @flow

// Libraries
import * as React from 'react';
import classNames from 'classnames';
// ...

// Components
import Template from '@components/Template';
import ViewCommonChild1 from './components/ViewCommonChild1';
import ViewCommonChild2 from './components/ViewCommonChild2';
// ...

// Styles
import styles from './Template.scss';

type PropTypes = {
  classes?: {
    root?: string,
    child1?: {
      root?: string,
      class1?: string,
      // ...
    },
    child2?: {
      root?: string,
      class1?: string,
      // ...
    },
  },
  prop1: type,
  // ..
};

const ViewCommon = ({
  classes = {},
  prop1,
  // ...
 }: PropTypes) => {
  // Hooks
  // ...

  return (
    <Template
      classes={{ root: classNames(classes?.root, styles.Root, {
          [styles.RootMod]: prop1,
        }) }}
    >
      <ViewCommonChild1
        classes={{
          root: classes?.child1?.root,
          class1: classes?.child1?.class1,
          // ...
        }}
      />
      <ViewCommonChild2
        classes={{
          root: classes?.child2?.root,
          class1: classes?.child2?.class1,
          // ...
        }}
      />
    </Template>
  );
};

export default ViewCommon;

Плюс индекс, стили и еще целая папка-матрешка с глубокой вложенностью вот этого всего, в дочерних компонентах и так далее... Причем вот тут — на «общем верхнем виде» — все будет действительно очень плохо, безобразно адово — количество объявлений типов в PropTypes компонента будет стремиться к стопитцот — и чем больше модифицируется ваша вьюха на конечных видах — тем стремительнее...

Ну и теперь мы наконец можем слепить конечный вид для роутера — который будет все это использовать в @views/View/Reals/RealView.js:

// @flow

// Libraries
import * as React from 'react';

// Components
import Template from '../components/Template/Template';

// Styles
import styles from './RealView.scss';

const RealView = (props): React.Element<typeof Template> => (
  <Template
    classes={{
      root: styles.Root,
      child1: {
        class1: styles.class1,
        // ...
      },
      child2: {
        class1: styles.class1,
        // ...
      },
      // ...
    }}
  />
);

export default RealView;

Ну, и, конечно — специфические стили для этого конечного вида.

Что, правда? Мы же просто хотим передать один несчастный модификатор? Но видим запутанную сложную структуру, множество файлов, и нигде в этом лабиринте нам надо не забыть передать стиль, еще и проверяя что это строка? Это не только жутко выглядит, отнимает лишнее время, крайне утомляет и раздражает, так еще и просто совершенно бессмысленно по сути. Как будто мы сами «вставляем себе палки в колеса». Справедливости ради, в CSS Modules присутствуют минимальные возможности переиспользования кода, некой низовой абстракции, или можно, например, выделять глобальные стили. Но первое, по моим наблюдениям, обычно не используется фэнами данного подхода, как и, например, препроцессор, если он прикручен, но, в результате, по сути «задавлен Модулями». У нас есть «исходные классы переиспользуемых компонентов-прототипов» и при необходимости они с трудом перекрываются «переданными по цепочке» классами от конечных вьюх. Код копируется и раздувается, содержит одно и тоже. В глазах рябит от одинаковых селекторов на разных уровнях. Поиск затруднен. Для того чтобы создать и передать простой класс-модификатор мы вынуждены вносить изменения не в два (конечная разметка и стили), а, чаще всего, в целые цепочки файлов — это очевидно увеличивает риск конфликтов при мерджах. А я то ожидал что «все наконец будет в одном месте»? Как бы не так! Ты мечешься по огромной сложной запутанной системе компонент и стилей для них, пытаясь сделать то, что бы мог надежно и понятно для последователей сделать за пару минут с нормальным глобальным препроцессором или даже просто на CSS. Я не заметил никакой «защиты» [которая часто вообще просто не нужна] — те же самые конфликты специфичности по-прежнему возможны. Ну, то есть, мы просто теперь очень дико, странно и трудно делаем то, что раньше делалось очень просто. Какие такие «глобальные конфликты» возможны если писать на БЭМ обычную надежную верстку без прокидывания классов и проверки их типа и прочих танцев с бубнами? Зачем мне такая «полезная» технология, что она дает в случае вот нескольких похожих друг-на-друга, но, при этом, всегда немного разных «бохато-шикардосных» лендосов? Мне показалось, только очень сильно и очевидно мешает? Мне кажется это «заговором» против абстракции в стилях и «обычной верстки» — которая способна формировать дополнительный к компонентной структуре фреймворка, легко стилизуемый классами CSS слой. Клиент ждет от нас хороший дизайн, а мы сосредотачиваемся на сомнительном оверинжиниринге, который, вероятно, этому только мешает, пишем функционально-декларативный Реакт [с GraphQL] и самыми модными технологиями для стилизации, типизацией — прямо как на каком-нибудь курсе «синьор за 3 месяца» или хакатоне. Только когда все это сталкивается с реальностью и дизайном боевого проекта — становится даже жарче чем на хакатоне.)

Совет 4. Никогда не использовать React c CSS-Modules на проектах где нужна быстрая, но четкая верстка и переиспользование. Эта технология не дает ничего действительно нужного и полезного, но очень сильно затрудняет и ограничивает самые простые привычные вещи: блокирует препроцессор и эффективную модификацию оформления.

Ах, да! У нас же еще есть Styled Component, и теперь у нас уже «почти полный набор известных технологий для стилизации» — как «на выставке». Также как и Модули, SC ограничивают возможности стилизации и переиспользования верстки [исключительно компонентностью]. Да мы можем удобно, понятно перекрывать исходные стили модификациями по пропсу в самом компоненте. Но также неминуемо возникает проблема с доставкой этих модифицирующих пропсов и опять выглядит некруто. А если компоненты не связаны? Маппить со стора, через контекст? Совсем чудовищно, когда можно «просто нормально верстать». Кроме того, при использовании обоих технологий одновременно: хеши SC встают слева от хешей Модулей, перекрывая их, что еще увеличивает сложность, беспорядок, вероятность ошибок.

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

Говоря о методологиях для разметки сразу вызывающих оторопь в контексте проектов со сложными нетривиальными дизайнами или же крайне сжатыми сроками, нельзя не вспомнить действительно древний подход не ассоциирующийся с мировым злом рептилоидами экосистемой самого популярного (почему-то здесь ассоциация с «продукцией» пивца Моргенштерна) ныне фронтенд-фреймворка Реакт. Это «утилитарный метод» получивший ныне свою вторую жизнь в качестве мейнстримно CSS-in-JS-ной реализации Tailwind с ее оголтелым слоганом-оксюмороном: «“Best practices” don’t actually work». Если вы видите на шаблонах нечто подобное:

<div class="ml-2 class1 pb-10 flex y_center class2" />
<div class="ml-2 mr-3 flex_wr class3" />

Значит это оно! Я встречал проекты в которых авторы еще и «ловко», «умно» генерили утилиты с помощью хэшей/мапов и миксинов препроцессора. В этом случае — у вас даже нет шанса просто найти класс по поиску среди обычного списка в стопитцот утилит (зато вхождений на разметке — абсолютно точно будет стопитцот)... Но даже продвинутый Tailwind «который используют bigTech-акулы-гиганты», с его обещанными плюшками в виде небольшого размера бандла или даже передачей «атомарного тренда» в дизайне — весьма сомнительно что способен предоставить настоящую гибкость и эффективность в условиях нереальных сроков или меняющихся/недорисованных макетов — а оно очень часто именно так и бывает в жизни. В любом случае, если вам нравиться устраивать такой нечитаемый кромешный ад на разметке вместо идеального БЭМа — пожалуйста. Только не зовите меня когда макеты изменятся, «наконец приедут гаджеты» и надо будет «все переверстать».)

Совет 6. Не используйте утилитарный подход или Tailwind в случае горящих сроков и меняющихся или не до конца известных требований.

Дурная компонентность

Чаще всего, я вижу, что в уже распиленных проектах структура на верхнем уровне в @src/components — структура компонент крайне куцая и скудная (некоторые адепты Риакт еще, кажется, любили выделять «контейнеры» — но, имхо, им самим часто не до конца понятно — по какому именно признаку)) ). А на видах, наоборот — глубокая и чрезмерная, еще и с огромным количеством стилей. Изменения иногда создают проблемы, приводят к потере кода при мерджах, а наименование при этом как-будто специально стремиться еще больше запутать и сбить с толку. Я то вообще радикал, и считаю что виды вообще не должны содержать стилей, а компоненты в библиотеке — не должны обвязываться со стором. И, на самом деле, для того чтобы осознать почему это так, чем крайне выгодно — нужно просто вдумчиво-аккуратно написать несколько больших проектов.

Мне всегда казалось что самый надежный и удобный для быстрого поиска подход с React — когда мы стремимся дать компоненту уникальное для проекта имя, файл называется также, и кроме того — экспортиться и используется он точно по этому самому маркеру (конечно, в реальности бывают исключения из правила). Очень удобно искать и все сильно упрощает. Или, может быть, кто-то мыслит и читает репо только в маштабах одной актуальной таски, но не всего проекта? Работая со Vue/Nuxt, например, удобно ввести соглашение, что мы называем и используем в разметке кастомные компоненты только в PascalCase-стиле. А сторонние — в kebab-case.

На самом деле, в реальности — иногда ваши коллеги даже не пишут собственно компонент работая с компонентным фреймворком, и кажется, даже не совсем понимают что это такое, для чего именно нужно. Они просто копируют куски кода или целые файлы по видам, с проекта на проект. Недавно у меня был опыт на невозможно-переборно-авральном, но крайне важном, значимом проекте с коллегой-фуллстеком на 10 лет старше меня (а мне уже 40). Он начал с верстки двух похожих страниц-форм, содержавших множество полей ввода. Сверстал одну страницу «обычной версткой» — без использования компонент, и, при этом — игнорируя требования макетов/дизайн-системы относительно внешнего вида и поведения плейсхолдеров или сообщений валидации. Потом скопировал все это на другой роут, переименовал классы и немного видоизменил под макеты. Готово — «пока так сойдет». Когда я вежливо поинтересовался «а как называется этот потрясающий стиль программирования на фронтенде?» коллега принялся авторитетно поучать про «инкапсуляцию», и то, что «компонент это модуль»))... Через месяца полтора команда справилась с основным объемом и руководство с дизайнерами подняли вопрос что «формы выглядят не по макету»! Если бы изначально было потрачено на пару часов больше и аккуратно использовались компоненты — достаточно бы было внести изменения действительно — в одном месте — чтобы все легко поправить... Вот тебе и инкапсуляция!

Помню, один из моих бывших боссов рассказывал про знакомого, который прошел жесточайший отбор в Google на должность некоего важного директора в Долине. Через какое-то время он посетил родину, и кореша за пивком поинтересовались закономерным: «А что ты, твой отдел делает?». Персонаж открыл какой-то из множества сервисов и ответил: «Вот эту кнопку!». Вероятно, это была очень важная и сложная суперкнопка, безусловно. Но многим из нас не так повезло в жизни, и на текущих проектах приходится делать очень много разных кнопок-контролов и всего прочего, с самыми разными людьми, и в разных обстоятельствах, ситуациях...

Совет 7. Полезно стараться организовать как можно более плоскую и понятную, подробную структуру компонент на проекте. При этом удобно формировать некую вообще практически линейную максимально детализированную «библиотеку» переиспользуемых компонент, а на видах их использовать. В идеале виды вообще не должны содержать стилей, а компоненты в библиотеке — не должны обвязываться со стором.

Сегодня мы многое поняли

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

Теги:
Хабы:
Всего голосов 18: ↑6 и ↓12-4
Комментарии87

Публикации

Истории

Работа

Веб дизайнер
42 вакансии

Ближайшие события