Если бы вы меня спросили, какой вопрос мне чаще всего задают про фронтенд-разработку, я бы ответил: «Как прокачаться в CSS?». Этот вопрос обычно озвучивают после того, как я делюсь сделанными мной CSS-иллюстрациями. Это то, что я люблю делать на CodePen.
Для многих CSS — неукротимый мифический зверь. Этот твит Криса заставил меня улыбнуться, потому что, несмотря на всю иронию, в нём много правды. Тем не менее, что, если я скажу вам, что вам нужно всего несколько свойств и техник, чтобы создать всё, что вы хотели? Правда в том, что это действительно так.
Я давно хотел написать подобную статью, но это сложная тема, потому что существует так много возможностей и так много техник, что, как правило, одно и то же можно сделать несколькими способами. То же самое относится и к CSS-иллюстрациям. Нет правильного или неправильного способа. Мы все рисуем на одном и том же холсте. Просто есть много разных инструментов, чтобы поместить все эти пиксели на страницу.
И хотя не существует универсального подхода для CSS-иллюстраций, я могу предложить несколько техник, которые помогут вам в вашем путешествии.
Время и практика Скопировать ссылку
CSS-иллюстрация требует много времени и практики. Чем точнее вы хотите быть и чем сложнее иллюстрация, тем больше времени это займёт. Самая затратная часть — не решение, какие свойства использовать и как, а отшлифовка результата до состояния, чтобы всё выглядело правильно. Будьте готовы изучить инспектор стилей в вашем браузере! Я также рекомендую попробовать VisBug, если вы этого ещё не сделали.
Два фантастических CSS-художника — Бен Эванс и Диана Смит. Оба недавно рассказывали о затратах времени на CSS-иллюстрации.
Я запостил мем про чашку, и в ответе Бена всё было прекрасно:
Когда в первый раз увидел твит, мне захотелось сделать это на CSS, но потом подумал, что мой ответ займет около месяца.
Это требует времени!
Трассировка вполне приемлема Скопировать ссылку
Зачастую мы уже имеем представление о том, что хотим нарисовать. В конце концов, эта статья не о дизайне. Речь идет о том, чтобы взять изображение и отрендерить его с помощью DOM и CSS. Я уверен, что эта техника существует с незапамятных времен. Но это то, чем я делюсь последние несколько месяцев.
- Найдите или создайте изображение, которое вы хотите нарисовать.
- Вставьте его в ваш HTML при помощи
<img>
. - Расположите его так, чтобы оно находилось прямо под вашей иллюстрацией.
- Уменьшите прозрачность изображения, чтобы его было видно, но не слишком.
- Трассируйте его с помощью DOM.
К моему удивлению, про эту технику не все знают. Но она бесценна для создания точных CSS-иллюстраций.
Следите за трюком в действии:
И попробуйте его здесь:
Обращайте внимание на отзывчивость Скопировать ссылку
Если вы начнёте использовать только две техники из этой статьи, пусть это будут трассировка из раздела выше и эта.
Есть немало фантастических примеров CSS-иллюстраций. Но одна из неприятных особенностей некоторых из них в том, что они не стилизованы — или даже не видны — на маленьких экранах. Мы живём в эпоху, когда очень важны первые впечатления от технологий. Рассмотрим на примере клавиатуры, нарисованной на CSS. Кто-то находит вашу работу, открывает её на своём смартфоне, а его встречает только половина иллюстрации или её небольшая часть. Они скорее всего упустили самые крутые части демо!
Вот мой приём: использовать единицы вьюпорта для иллюстраций и создавать свою собственную масштабированную единицу измерения.
Для размеров и позиционирования вы можете использовать либо масштабированную единицу измерения, либо проценты. Это особенно полезно, когда вам нужно использовать box-shadow
, потому что это свойство работает с единицами вьюпорта, но не работает с процентами.
Рассмотрим CSS-логотип egghead.io, который я создал ранее. Я нашел изображение, которое хотел использовать, и добавил его в DOM тегом <img>
.
<img src="egghead.png" alt="">
img {
height: 50vmin;
left: 50%;
opacity: 0.25;
position: fixed;
top: 50%;
transform: translate(-50%, -50%);
}
Высота 50vmin
— желаемый размер CSS-иллюстрации. Уменьшение прозрачности позволяет нам «трассировать» иллюстрацию по мере прогресса.
Теперь создадим нашу масштабированную единицу измерения.
/**
* размеры изображения 742 x 769
* ширина — 742
* высота — 769
* желаемый размер — 50vmin
*/
:root {
--size: 50;
--unit: calc((var(--size) / 769) * 1vmin);
}
Зная размеры изображения, мы можем создать единицу измерения, которая будет масштабироваться вместе с нашим изображением. Мы знаем, что высота — это самая большая сторона, поэтому используем её как основу для создания дробной единицы.
Получим что-то вроде такого:
--unit: 0.06501950585vmin;
Выглядит так себе, но поверьте, это нормально. Мы можем использовать её для задания размеров контейнера для иллюстации, используя calc()
.
.egg {
height: calc(769 * var(--unit));
position: relative;
width: calc(742 * var(--unit));
z-index: 2;
}
Если мы используем проценты или наше новое кастомное свойство --unit
для стилизации элементов внутри контейнера нашей CSS-иллюстрации, мы получим отзывчивую CSS-иллюстрацию… И всё это требует всего несколько строчек математики с использованием CSS-переменных!
Поизменяйте размеры этого демо, чтобы увидеть, что все пропорции сохраняются, ограничиваясь по размеру до 50vmin
.
Семь раз отмерь, один отрежь Скопировать ссылку
Следующий совет: замеряйте. Чёрт, вы даже можете взять рулетку, если работаете с физическим объектом!
Это может показаться немного странным, но я замерял эту сцену. Это телевизор с полками, которые стоят в моей гостиной. Все замеры сделаны в сантиметрах. Я использовал их для получения отзывчивой единицы измерения, основанной на фактической высоте телевизора. Благодаря кастомным свойствам мы можем дать имя этому числу и всем остальным. Это позволит легко запомнить, для чего оно предназначено.
:root {
--light-switch: 15;
--light-switch-border: 10;
--light-switch-top: 15;
--light-switch-bottom: 25;
--tv-bezel: 15;
--tv-unit-bezel: 4;
--desired-height: 25vmin;
--one-cm: calc(var(--desired-height) / var(--tv-height));
--tv-width: 158.1;
--tv-height: 89.4;
--unit-height: 42;
--unit-width: 180;
--unit-top: 78.7;
--tv-bottom: 114.3;
--scaled-tv-width: calc(var(--tv-width) * var(--one-cm));
--scaled-tv-height: calc(var(--tv-height) * var(--one-cm));
--scaled-unit-width: calc(var(--unit-width) * var(--one-cm));
--scaled-unit-height: calc(var(--unit-height) * var(--one-cm));
}
Как только мы вычислили переменную, можем использовать её везде. Я знаю, что мой телевизор 158,1 см в ширину и 89,4 см в высоту — подсмотрел в инструкции. Но в моей CSS-иллюстрации он всегда будет ограничен 25vmin
.
Используйте абсолютное позиционирование для всего Скопировать ссылку
Этот совет позволит сэкономить на нажатиях клавиш. Чаще всего вы будете пытаться позиционировать элементы абсолютно. Помогите себе и положите это правило куда-нибудь.
/* Имена классов могут отличаться */
.css-illustration *,
.css-illustration *:after,
.css-illustration *:before,
.css-illustration:after,
.css-illustration:before {
box-sizing: border-box;
position: absolute;
}
Ваша клавиатура скажет вам спасибо!
Или поиграйте в этой песочнице:
Придерживайтесь подхода Скопировать ссылку
Это, безусловно, самая сложная вещь. Какой подход вы используете к CSS-иллюстрации? С чего начинаете? Должны ли вы начать с внешней части и идти внутрь картинки? Это работает не так хорошо, как хотелось бы.
Скорее всего, вы попробуете несколько подходов и найдёте лучший способ решить задачу. Вы, конечно, будете переделывать некоторые вещи, но чем больше вы будете практиковаться, тем чаще будете замечать шаблонные вещи и разработаете подход, который лучше всего подходит для вас.
Я склонен соотнести свой подход с тем, как если бы вы создавали векторное изображение, в котором иллюстрация состоит из слоёв. Разделите её и нарисуйте на бумаге, если нужно. Но начните снизу и прорабатывайте свой путь наверх. Это обычно означает более крупные фигуры сначала, а более мелкие детали — позже. Вы всегда можете повозиться с контекстом наложения, когда вам нужно подвигать элементы.
Сохраняйте жёсткую структуру ваших стилей Скопировать ссылку
Это приводит нас к структуре. Старайтесь избегать плоской DOM-структуры для вашей иллюстрации. Сохранение вещей атомарными позволяет проще двигать части вашей иллюстрации. А ещё так гораздо проще показывать или прятать части иллюстрации или даже потом анимировать их. Рассмотрим демо CSS Snorlax. Руки, ноги, голова и прочие части являются отдельными элементами. Это сделало анимирование руки гораздо проще, чем если бы я пытался держать всё вместе, так как я смог просто применить анимацию к классу .snorlax__arm-left
.
Вот ускоренная запись того, как я делал это демо:
Работа с неудобными фигурами Скопировать ссылку
Есть довольно неплохая статья на CSS-Tricks про создание фигур с помощью CSS. Но как насчёт более «неуклюжих» фигур, таких как длинная кривая или даже обводка? В таких случаях нужно мыслить нестандартно. Такие свойства, как overflow
, border-radius
и clip-path
— отличные помощники.
Посмотрим на демо CSS Jigglypuff. Нажмите на чекбокс.
Вот ключ к созданию искривлённых фигур! У нас есть элемент, который гораздо больше тела с примененным border-radius
. Тогда мы задаем overflow: hidden
телу, чтобы обрезать этот лишний кусок.
Как мы можем сделать обводку? Это уже немного сложнее. Но мне нравится следующий приём: используйте прозрачный элемент с толстой рамкой. Затем примените border-radius
и обрежьте лишнее, если нужно.
Если вы нажмёте на чекбокс, покажется элемент, который мы используем для обводки этого угла. Другой приём может заключаться в том, чтобы наложить поверх круг, совпадающий по цвету с фоном. Это нормально, пока нам не понадобится изменить цвет фона. Хорошо, если у вас есть переменная или что-то вроде для этого цвета. Но такой подход слегка сложнее поддерживать.
clip-path
— ваш друг Скопировать ссылку
В последнем демо вы могли заметить парочку интересных CSS-свойств, например, clip-path
. Вам почти наверняка понадобится clip-path
, если вы хотите создавать сложные CSS-фигуры. Его особенно удобно использовать для обрезания краёв элементов, когда применение overflow
на родителе не помогает.
Вот небольшое демо, которое я собрал некоторое время назад, чтобы показать различные возможности clip-path
.
Есть ещё такое демо, которое берёт идеи из статьи Shapes of CSS и воссоздаёт фигуры при помощи clip-path
.
border-radius
— ещё один друг Скопировать ссылку
Вам точно понадобится border-radius
для создания кривых. Один необычный приём заключается в использовании синтаксиса с двумя значениями. Это позволяет задавать горизонтальный и вертикальный радиус для каждого угла.
Поиграйте с этим демо, чтобы по-настоящему оценить мощь border-radius
. Я призываю использовать проценты для значений, чтобы сохранять отзывчивость элементов.
Техники создания теней Скопировать ссылку
У вас уже есть все формы, всё хорошо продумано, правильные цвета везде, где нужно… Но что-то всё ещё выглядит не так. Скорее всего, это отсутствие теней.
Тени добавляют глубину и создают реалистичные ощущения. Посмотрим на это воссоздание иллюстрации Галь Шир. Галь великолепно использует тени и градиенты для создания красивой иллюстрации. Я подумал, что было бы интересно воссоздать её и добавить переключатель, который включает и выключает затенение, чтобы оценить разницу, которое оно создаёт.
Эффекты затенения часто создаются при помощи комбинации box-shadow
и background-image
.
Ключевая особенность этих свойств в том, что мы можем складывать их в список, разделённый запятой. Например, у котла в этом демо есть список градиентов, которые используются по всему его телу.
.cauldron {
background:
radial-gradient(25% 25% at 25% 55%, var(--rim-color), transparent),
radial-gradient(100% 100% at -2% 50%, transparent, transparent 92%, var(--cauldron-color)),
radial-gradient(100% 100% at -5% 50%, transparent, transparent 80%, var(--darkness)),
linear-gradient(310deg, var(--inner-rim-color) 25%, transparent), var(--cauldron-color);
}
Обратите внимание, что здесь используются radial-gradient()
и linear-gradient()
, и они не всегда содержат идеально круглые числовые значения. Опять же, такие числа нормальны. На самом деле вы потратите много времени, настраивая и подкручивая разные значения в инспекторе стилей.
То же самое применимо и к box-shadow
. Однако с ним мы также можем использовать значение inset
, чтобы создавать хитрые границы и дополнительную глубину.
.cauldron__opening {
box-shadow:
0 0px calc(var(--size) * 0.05px) calc(var(--size) * 0.005px) var(--rim-color) inset,
0 calc(var(--size) * 0.025px) 0 calc(var(--size) * 0.025px) var(--inner-rim-color) inset,
0 10px 20px 0px var(--darkness), 0 10px 20px -10px var(--inner-rim-color);
}
Конечно, бывают случаи, когда куда разумнее использовать filter: drop-shadow()
, чтобы получить желаемый эффект.
Сайт Линн Фишер a.singlediv.com — яркий пример этих свойств в действии. Потыкайте в разные элементы на этом сайте и исследуйте некоторые из иллюстраций, чтобы найти отличные способы применения box-shadow
и background-image
в иллюстрациях.
Свойство box-shadow
настолько мощное, что вы можете создать целую иллюстрацию только с ним. Я однажды пошутил о создании CSS-иллюстрации доллара.
Я использовал генератор, чтобы создать иллюстрацию всего из одного <div>
. Но Альваро Монторо пошёл дальше и написал генератор, который вместо этого использует box-shadow
.
Препроцессоры очень полезны Скопировать ссылку
Хоть это и не необходимо, использование препроцессоров может помочь содержать ваш код в чистоте и порядке. Например, Pug позволяет писать HTML быстрее, особенно когда нужно использовать циклы для работы с группой повторяющихся элементов. А дальше мы можем ограничить кастомные CSS-свойства таким образом, чтобы нужно было определить стили всего один раз, а затем переопределять их при необходимости.
Вот другой пример, который демонстрирует структуру по принципу DRY. Цветы свёрстаны с одинаковой разметкой, но каждый имеет свой собственный класс с индексом, который используется для переопределения CSS-свойств.
У первого цветка такие свойства:
.flower--1 {
--hue: 190;
--x: 0;
--y: 0;
--size: 125;
--r: 0;
}
Это первый, все остальные основаны на нём. Обратите внимание, что второй цветок немного смещён вправо и вверх. Всё, что нужно, это дать разные значения тем же кастомным свойствам:
.flower--2 {
--hue: 320;
--x: 140;
--y: -75;
--size: 75;
--r: 40;
}
Вот и всё! Скопировать ссылку
Продолжайте, используйте эти советы, придумывайте свои собственные, делитесь ими, а также делитесь своими CSS-шедеврами! И, эй, если у вас есть свои лайфхаки, пожалуйста, делитесь ими тоже! Это определённо то, чему учатся методом проб и ошибок. То, что работает у меня, может отличаться от того, что работает у вас, и мы можем учиться на этих разных подходах.