Всем привет! Давно хотел и наконец написал небольшую книжку — бодрое пособие по своей профессиональной области: актуальным подходам к разметке интерфейсов, экранному дизайну и доступности. Она о моем оригинальном подходе к созданию GUI, препроцессорам CSS (для объективности, немного и об альтернативных подходах), и его эффективном практическом использовании с javascript и популярными реактивными компонентными фреймворками Vue и React. Материал представлен аккуратно последовательно, но безумно интенсивно и динамично — ничего лишнего или даже слишком подробного — для того чтобы увлеченный и подготовленный читатель не потерял интереса и «проглотил на одном дыхании». С другой стороны, текст, достаточно сложный ближе к концу, и на всем протяжении — густо насыщенный идеями, ссылками на технологии и подходы — поэтому, очевидно, будет «на вырост» начинающим. Но, в любом случае, как и если вы только начали интересоваться данной тематикой, так и если уже давно занимаетесь веб-дизайном, версткой и программированием фронтенда — вам может быть полезно на него взглянуть.
Мотивация
Я уже много лет занимаюсь, прежде всего, разметкой интерфейсов, их оформлением и поведением, экранами и GUI. Участвовал на позиции верстальщика в небольших командах, стартапах, а также реализовал некоторое количество проектов самостоятельно как фрилансер, дизайнер и верстальщик. За это время я накопил некоторый опыт по специфическим проблемам в данной области, которым мне очень хочется поделиться.
Начну с некоторого огульно обобщающего и, поэтому, несколько провокационного наблюдения, «вброса»: большинство, не только начинающих, но даже опытных программистов испытывают своеобразное предубеждение-стереотип о некой «несерьезности» верстки интерфейса как отрасли деятельности в веб-индустрии — «верстка это не программирование!». Очень многие считают что заниматься разметкой «некруто и скучно» для «серьезного» специалиста. И как следствие — уделяют предмету мало своего внимания и времени, имеют мало реального релевантного опыта. Проще говоря, большинство разработчиков не любит и не умеет верстать. И уже и как причина и как следствие — зачастую сами подходы, технологии и архитектура используемые для организации GUI даже на серьезных коммерческих проектах — отставляют желать лучшего, не отвечают реалиям современной веб-индустрии, устарели и недостаточно эффективны. Неудачные, якобы временные, слабые решения и дальнейшие бесконечные быстрые «фиксы», кривые «кряки» наслаиваются друг-на-друга, кодовая база неоправданно распухает и становится все менее связной и контролируемой. Шаблоны, или ныне — в эру компонентных фреймворков — компоненты, стили и логика для них часто и закономерно превращаются в невнятную и крайне излишнюю по сути «свалку», «густой лес», «джунгли» с очевидно неподъемной ценой рефакторинга и масштабирования. Очевидно, что такое состояние системы может легко приводить к серьезным затруднениям, снижению темпов или даже срыву сроков и, ирония как раз в том, что в реальной коммерческой ситуации, авторам подобной громоздкой и неуклюжей системы, все равно придется потратить еще больше времени на то, чтобы, по крайней мере, исправить все оплошности и несовершенства разметки, оформления и логики для них, но делать это придется в изначально плохо организованном коде, по замкнутому кругу — только увеличивая беспорядок и вероятность сбоев. С того момента как вы перестаете быть «джуном», ваша ответственность состоит не только в том чтобы закрыть все «баги» на трекере и навернуть как можно скорее «фичи», любой ценой. Надежная и легко поддерживаемая система — продукт который вы продаете бизнесу.
Реальный жизненный кейс: будучи начинающим специалистом я работал удаленно в одном приятном стартапе. Когда проект запустили, после презентации многие присутствовавшие на ней высказались о том, что кегль основного текста и полей ввода в интерфейсе — мелковат. Получив задачу в трекере, я потратил всего пару минут, поправив одну переменную своей системы — чтобы поднять кегль на всех нужных полях и контролах, и еще 15 минут чтобы удостовериться что ничего действительно не сломалось ни на одном шаблоне. Ваша система изначально должна быть написана так, и только так, чтобы ее было можно легко скорректировать и улучшить, поддерживать и расширять. По-настоящему лаконичный и выразительный качественный код — невероятно мощно экономит время и нервы даже если вы работаете с проектом в одиночку. Кроме того, уважаемые авторитеты в коммерческом программировании утверждают [см. Роберт Мартин — «Чистая архитектура»] что «то что сделано изначально плохо» — в реальности, не только «никогда не будет переписано», но и приводит к постоянному крутому росту стоимости дальнейшей доставки нового функционала, а в перспективе способно полностью блокировать прогресс по проекту!
Хорошее понимание возможностей и ограничений современных браузерных технологий очень желательно и полезно не только для веб-разработчиков, но и для гуманитариев-дизайнеров проектирующих GUI. Часто даже востребованный специалист, выдающий приятный стиль и не делающих серьезных ошибок по UХ, не может внятно ответить на простейшие технические вопросы о своем дизайне при сдаче макета на верстку, то есть — «не понимает что рисует».
В этом пособии я стараюсь в достаточно доступной форме дать некоторые полезные идеи о современном экранном дизайне и эффективных способах его реализации для браузера. Я буду приводить конкретные примеры кода, но не ради специфических моментов и подробностей реализации, а, прежде всего, иллюстрируя и подчеркивая общие мысли, идеи, практические подходы, годные для написания крупных сложных современных проектов. Этот текст — не еще один занудный скучный учебник, а, скорее, «методичка углубленного спецкурса». Материал будет подаваться максимально интенсивно и насыщенно, и новичок может не справиться сходу с примерами кода или даже содержанием некоторых разделов. Не паникуйте. Важнее всего в нашей отрасли — научиться развиваться самостоятельно в выбранном направлении. Этот текст скорее призван помочь вам понять «что гуглить дальше?».
Кому будет полезен текст?
Начинающим. Вы минимально освоились со спецификациями HTML и CSS, начали пробовать JavaScript, успешно сверстали свои первые страницы, макеты и «хотите большего», хотите получить некий «дружеский пинок под зад», который поможет вам осознать дальнейшие горизонты, выйти на новый уровень и начать получать больше удовольствия и удовлетворения, как и от самой этой деятельности, так и от ее результатов.
Не верстающим как бог. Вы давно занимаетесь веб-программированием, но верстаете «нехотя и по-старинке», при этом, осознаете свой пробел в знаниях и навыках, созрели для того чтобы отбросить оковы лени и невежества, переломить ситуацию — ведь на каждом проекте разработка GUI для вас это скучная трудная рутина и мука.
Дизайнерам. Вы веб-дизайнер, но хотите начать верстать.
Эта работа о программировании дизайна и дизайне программных продуктов для фронтенда. Стоит иметь ввиду, что она написана, скорее, веб-дизайнером для веб-программистов, чем веб-программистом для веб-дизайнеров, хотя это и неточно. Я решил разделить текст на две части:
Препроцессор
В первой части представлена мощная практическая метода, дающая яркие идеи и четкие рекомендации по организации разработки стилей и разметки. Материал выбран таким образом, чтобы последовательно, но с крутой интенсивностью ввести, возможно даже, только еще минимально овладевшего HTML и CSS читателя, в мир удивительных возможностей препроцессора. Хочется показать, как можно стремительно организовать рабочую кухню, необходимую и достаточную для того чтобы эффективно создавать качественную адаптивную разметку. В этой части почти ничего не говорится о самом javascript, для того, чтобы, если читатель не освоил еще в достаточной степени язык программирования, но уже начал верстать — все равно смог бы все понять и начать внедрять на практике.
Препроцессор, JavaScript и фреймворки
Вторая часть не такая злая. Она, например, показывает в увлекательной форме некоторые углубленные интересные кейсы современного экранного дизайна и вопросов связанных с доступностью веб-приложений. В ней речь идет о практическом применении подходов к препроцессору представленных в первой части, в связке с javascript для GUI, особенно в контексте компонентных фреймворков.
Левон Гамбарян.
Июнь 2020 года.
Препроцессор
Простейший пример плохого кода
Безобразного кода с каждым днем становится все больше, несмотря на то, что больнее всего страдают от этого сами авторы, горе-разработчики. И если определенную функциональность или рендер можно покрыть тестами, то ошибки оформления выявляются только «ручками».
Также, просто парадоксально, что все программисты слышали о простых основополагающих принципах качественного программирования KISS и SOLID, но подавляющее большинство напрочь забывает о них, когда речь заходит об организации оформления, представления веб-интерфейса.
В реальной ситуации — заказчик, покупатель вашего кода в любой момент может захотеть внести любые правки, и вы должны быть максимально к этому готовы.
Давайте посмотрим на самый простейший пример плохого кода на CSS:
/* Примитивнейший пример обычного плохого кода на CSS */
/* Где-нибудь в файлах стилей: */
.selector--1 {
width: 200px;
height: 200px;
border: 1px solid #ADADAD;
border-radius: 3px;
/* ... и дальше еще огромное количество самых разных правил */
}
.selector--2 {
width: 200px;
height: 400px;
border: 1px solid #ADADAD;
border-radius: 3px;
/* ... и дальше еще огромное количество самых разных правил */
}
Не делайте так больше почти никогда! ))) Почему? Код валидный, «в браузере все по макету», да и все именно так обычно и пишут. Но все и не «верстают как бог», правильно? В контексте любого проекта чуть большего чем совсем крохотный, подобный код «плохой и чреват проблемами в будущем». Он конкретен и невыразителен, излишен, его сложно модифицировать и переиспользовать.
А как надо?
На сегодняшний день стандартной практикой для разработки CSS-кода является использование Препроцессора, который отнюдь не превращает формальную спецификацию CSS в язык программирования, но дает синтаксису необходимую силу и мощь, актуальные возможности и эффективность. Если говорить совсем просто, то без него, очень многие требования современного веб-дизайна иначе было бы крайне затруднительно реализовать, особенно в масштабе крупных проектов.
Справедливости ради, нужно упомянуть, что последние годы, в связи с стремительным ростом популярности компонентных js-фреймворков и их подходов, все больше сторонников набирают также различные «CSS-in-JS»-реализации (например: Styled Components). Скоро, вероятно, можно будет спокойно использовать переменные в самом CSS (CSS Custom Properties). Тема холиварная, существуют контексты и ситуации когда подобный CSS-in-JS подход может оказаться более оправданным и изящным, без сомнения. И даже существует масса реалистичных кейсов когда проще всего будет действительно обойтись несколькими наборами правил на CSS, а любое его расширение будет излишним. Но в общем случае, в реальной коммерческой практике, имхо, для верстки сложных дизайнов и интерфейсов удобнее и эффективнее всего сейчас использовать любой препроцессор, и, шок — даже с компонентным фреймворком, дальше я планирую показать «как именно это лучше всего делать». Препроцессоры дают максимум возможностей и позволяют стремиться к максимальной выразительности и переиспользуемости. Вот во что превратился бы «плохой код» выше в SCSS-синтаксисе, наверное — самого популярного на сегодняшний день препроцессора — Sass:
// В @/src/scss/utils/_variables.scss:
$colors__border: #adadad;
$border-radius: 3px;
// В @/src/scss/utils/_placeholders.scss:
%border-block {
border: 1px solid $colors__border;
border-radius: $border-radius;
}
// В @/src/scss/utils/_mixins.scss:
@mixin size($width, $height) {
width: $width;
height: $height;
}
// В любом месте проекта:
.selector {
$selector--1__size: 200px;
$selector--2__width: 200px;
$selector--2__height: 400px;
&--1,
&--2 {
@extend %border-block;
/* ... включение других сущностей препроцессора
и специфическиих правил общих для селекторов */
}
&--1 {
@include size($selector--1__size, $selector--1__size);
/* ... включение других сущностей препроцессора
и специфических правил уникальных для селектора */
}
&--2 {
@include size($selector--2__width, $selector--2__height);
/* ... включение других сущностей препроцессора
и специфических правил уникальных для селектора */
}
}
Точно тоже самое легко сделать и на, кажется, недооцененном, но очень удачном Stylus — совершенно не важно какой именно расширенный синтаксис вы используете, главное как и зачем. Очень много раз мне приходилось видеть плохой чужой код написанный якобы для препроцессора, видимо, «потому что так сейчас модно», но, на самом деле, практически ничем не отличающийся от кода CSS. Не делайте так! Препроцессор дает нам крайне ценную возможность абстрагировать общие качества гайдлайна, стиль и основанные на нем частные стили, организовать их намного более выразительно и лаконично, легко модифицировать и переиспользовать при необходимости.
В данном, вырванном из контекста, но, при этом, вполне жизненном примере, кажется, что кода препроцессора — сильно больше. Он еще и раскидан по нескольким разным файлам, что, как будто, еще все усложняет. Зачем так напрягаться, а? Прежде всего, привычка начинать писать разметку с переменных и обобщений — очевидно грамотная. Перестаньте плодить изолированные глухие кряки с магическими числами, начните применять абстракцию! Делайте хорошо сразу, потому что вы почти никогда и ничего не переделаете «потом», на самом деле. «Когда наш стартап наконец взлетит», и как раз во многом из-за такого отношения он может и не взлететь, в результате. Чем детализированнее и сложнее ваш интерфейс, его дизайн, тем больше строк и времени вы будете «экономить» просто оптимизируя общие наборы правил, переиспользуя стили. Кроме того, поддерживать код и, тем более, вносить серьезные изменения будет на порядок проще.
Первый пример демонстрирует что на начальных этапах развития проекта хорошо продуманного кода препроцессора может быть даже визуально несколько больше, чем неорганизованного, внутренне плоского, скучного CSS. Но давайте разберем очень часто встречающийся кейс, в котором мы очевидно сразу сильно экономим много трафика и явно оптимизируем возможную поддержку. Такое очень часто встречается: нам нужно создать большое количество, предположим — 20 штук — модификаторов одного селектора — квадратной иконки размеров в 100 пикселей — и поставить в них нужные картинки в бекграунд. В Sass мы можем написать цикл с интерполяцией для создания селектора модификатора и указания пути до ресурса. И хотя такая синтаксическая возможность не является чем-то идейно решающе важным — на практике она экономит кучу времени и повышает качество жизни на работе:
// В @/src/scss/utils/_variables.scss:
// Paths
$images__path--root: "../../assets/images/";
// Sizes
$icons__size: 100px;
// Views
$icons: 20;
// В любом месте проекта (в папке В @/src/scss/project/):
.icon {
// корректируем путь до картинок
$image-path: $image_path--root + "icons/";
@include size($icons__size, $icons__size); // эта примесь уже создана выше
@for $i from 1 through $icons {
&.icon--#{$i} {
background: url("#{$image-path}icon--#{$i}.svg") center center no-repeat;
}
}
}
Пример предполагает что в вашем проекте следующая структура:
.
└─ src
├─ assets
│ └─ images
│ ├─ icon--1.svg
│ ├─ icon--2.svg
│ └─ ...
└─ sscs
├─ project
│ └─ ...
└─ utils
├─ _mixins.scss
└─ _variables.scss
Теперь в шаблонах мы можем использовать:
<div class="icon icon--1"></div>
Если вы желаете чтобы картинки были с осмысленными именами — можете перебирать список:
.icon {
$image-path: $image_path--root + "icons/";
$images: "name1", "name2", "name3"; // Список имен
@include size($icons__size, $icons__size);
@each $image in $images {
&.icon--#{$image} {
background: url("#{$image-path}#{$image}.svg") center center no-repeat;
}
}
}
Ну и раз уж мы упомянули интерполяцию, необходимо вспомнить еще один простой кейс, который сейчас часто нужен и в котором вам она точно пригодится — «посчитать с переменной»:
.selector {
$width: 100px;
width: calc(100vw - #{$width});
}
Используйте на полную мощность и изучайте подробно свои ключевые инструменты. Применяйте свой интеллект и фантазию. В хорошем коде препроцессора наборы правил не повторяются и какдый селектор содержит только небольшое количество объявлений. Повторяющиеся и похожие наборы, большое количество самых разных объявлений в селекторах — признак плохо организованного кода. Пока бесконечный поиск и замена остаются вашим главным и единственным инструментом рефакторинга кода разметки — вы живете в каменном веке веб-технологий и еще даже не начинали верстать!)
Абстрагируй все!
Что такое дизайн, если совсем кратко? Дизайн — это «гайдлайн» — строгая система, набор стилевых правил и ограничений, перечень констант, аксиом и отношений в разметке и оформлении интерфейса, которым он неукоснительно должен соответствовать. Задача верстальщика в том чтобы правильно воспринять эту систему и максимально эффективно перевести ее с языка графических прототипов в работающий по заявленным требованиям код.
Поэтому маниакально абстрагируй все что только можно. Все что должно и может быть переиспользовано и любые конкретные значения, те, которые, когда-нибудь, но, в принципе, могут измениться. К примеру, на Stylus:
// В @/src/stylus/utils/variables.styl:
$colors = {
mint: #44c6a8,
// ... другие конкретные значения цветов
}
// Создаем "основной цвет", абстрагируясь от конкретного цвета
$colors['primary'] = $colors.mint
// ... другие "функциональные" цвета
Любое имеющее глобальное значение и потенциально переиспользуемое качество гайдлайна и дизайна должно быть отражено в файле переменных препроцессора. Теперь в любом месте где потребуется предоставить основной «брендовый» цвет:
.selector
color $colors.primary
Очевидно, что если весь остальной код будет аккуратно использовать правильную переменную — просто «по щелчку пальцев» возможно изменить этот основной цвет по всему интерфейсу! Все это логично и закономерно приводить нас к идее некой общей «стилевой базы», медиатора единого стиля оформления интерфейса. Такую глобальную абстракцию легко способен предоставить препроцессор, и ее, вероятно, будет удобно использовать даже для оформления «изолированных» компонентов.
Структура и стилевая база препроцессора
Дальше я покажу определенную логичную и простую структуру организации файлов препроцессора, которую использую на проектах сам. Но вы можете что-то делать совсем иначе и, в результате, прийти к совсем другой, удобной именно для ваших методов работы системе. Важно не следовать каким-то зазубренным до автоматизма теориям и якобы «лучшим практикам», а гибко и к месту применять возможности, и даже, вероятно — все время немного экспериментировать, стараясь расти и меняться к лучшему. Категорически важно только то, что ваши стили должны быть действительно всегда четко и понятно организованы.
На практике во многих командах «вытесняют» и игнорируют выгоду от более продвинутых и аккуратных подходов к разметке. Ведь это требует определенных затрат на коммуникацию. Например, очень часто файлы стилей даже на препроцессоре — тупо и незамысловато «складируются в кучу» и практически никак не связаны друг-с-другом. Признайтесь себе наконец честно: даже аккуратная подробная компонентность, хоть и позволяет решить проблему на самом примитивном физическом уровне, она вообще не решает ее глобально, на уровне абстракций. Наоборот — компонентность даже еще несколько затрудняет решение, так как строится именно на противоположных глобальности препроцессора, изолирующих подходах. Ваша куча это по прежнему невыразительная невнятная неповоротливая куча, просто теперь она еще и разделена на множество подобных и глобально излишних куч, поменьше.
Но давайте уже организуем препроцессор, если с SCSS:
.
└─ src
└─ sscs
├─ core // обшие и компилируемые сущности препроцессора
│ ├─ _animations.scss // keyframes
│ ├─ _base.scss // минимальная нормализация основных HTML-элементов
│ ├─ _grid.scss // сетки
│ ├─ _typography.scss // типографика
│ └─ _utilities.scss // быстрые удобные классы-утилиты для включения прямо в разметку
├─ libraries // папка с файлами стилизаций сторонних модулей
│ └─ _modal.scss - например какая-нибудь готовая модаль
├─ project // стили конкретного проекта
│ ├─ _elements.scss // отдельные простые элементы-компоненты
│ ├─ _fixes.scss // этот файл всегда должен быть практически пустой, и предназначен только для редких общеизвестных "собственных проблем браузеров"
│ ├─ _layout.scss - стили общей для всех страниц GUI-обертки над контентом интерфейса
│ └─ _widgets.scss - сложные составные комбинации простых элементов-компонентов
├─ utils // обшие и некомпилируемые основные сущности препроцессора
│ ├─ _functions.scss // на практике нужны крайне редко
│ ├─ _mixins.scss // параметризируемые и способные принимать контент примеси-микстуры
│ ├─ _placeholders.scss // повторяющиеся наборы правил - растворы
│ └─ _variables.scss // самый важный файл с переменными )
├─ _main.scss // точка сборки всех стилей препроцессора
└─ _stylebase.scss // стилевая база
То есть, на самом деле — порядок сборки всей кухни имеет значение, конечно же:
// В @/src/scss/_stylebase.scss:
// Stylebase
//////////////////////////////////////////////////////
//////////////////////////////////////////////////////
// Uncompiled kitchen
@import "./utils/_functions";
@import "./utils/_variables";
@import "./utils/_mixins";
@import "./utils/_placeholders";
// Core base normal style and common utils
@import "./core/_animations";
@import "./core/_typography";
@import "./core/_base";
@import "./core/_grid";
@import "./core/_utilities";
// В @/src/scss/_main.scss:
// Project styles
//////////////////////////////////////////////////////
//////////////////////////////////////////////////////
// Stylebase for components
@import "_stylebase";
// App styles
@import "./project/_fixes";
@import "./project/_elements";
@import "./project/_widgets";
@import "./project/_layout";
/* External libraries customization */
@import "./libraries/_modal";
Итак, «стилевой базой» мы будем называть некое основное ядро стилей, доступный всем остальным компонентам системы общий код препроцессора. Более детально, он состоит из условно двух разных видов файлов:
Растворяемые при компиляции инструменты-помощники, сущности позволяющие генерировать лаконичный, оптимальный, связный код:
- функции
- переменные
- параметризуемые примеси
- включения-плейсхолдеры
Компилируемые глобальные стили:
- анимации keyframes
- типографика
- базовая нормализация основных HTML-элементов
- сетки
- утилитарные классы-помощники для разметки
В папки @/src/scss/project
и @/src/scss/libraries
вы можете добавлять файлы по необходимости.
Удобно держать подобную минимальную «кухню» наготове в ваших стартовых проектах, чтобы с самого начала работы быстро настраивать стилизацию под конкретные требования и гайдлайн.
У меня вот, например, можно посмотреть — есть различные такие заготовки-«болванки» для быстрого старта на разных комбинациях актуальных технологий:
- React c TypeScript и Stylus;
- Vue c SCSS;
- Webpack c SCSS и Nunjucks.
Читать книжку дальше можно пройдя по ссылке: Как верстать веб-интерфейсы быстро, качественно и интересно.