Краткий обзор процесса и инструментов, используемых для создания праздничного календаря Designcember.
В духе декабря и множества календарей, которые люди используют для обратного отсчёта и празднования, мы решили выделить веб-контент от сообщества и команды Chrome. Каждый день мы освещали один из материалов, связанных с разработкой и дизайном интерфейсов, в общей сложности 31 публикацию, включая 26 новых демонстрационных сайтов, инструментов, объявлений, подкастов, видео, статей и примеров.
Полную версию можно посмотреть на designcember.web.app .

Обзор
Нашей целью было создать доступный, оригинальный, современный и адаптивный веб-интерфейс, занимая как можно меньше байтов. Мы хотели подчеркнуть новые адаптивные API, такие как контейнерные запросы, и включить прекрасный пример тёмной темы на сайте, ориентированном на дизайн и насыщенном ресурсами. Для этого мы сжали файлы, предложили несколько форматов, использовали инструменты сборки, оптимизированные для генерации статических сайтов, добавили новый полифил и многое другое.
Начиная с прихоти
Идея сайта-календаря Designcember заключалась в том, чтобы он служил витриной для всех работ, которые мы хотели бы осветить в течение декабря, одновременно выполняя роль демонстрационного сайта. Мы решили создать адаптивный многоквартирный дом, который мог бы становиться выше и уже, или ниже и шире, с окнами, которые могли бы менять положение в раме. Каждое окно представляло один день (и, следовательно, один элемент контента). Мы работали с иллюстратором Элис Ли, чтобы воплотить нашу идею в жизнь.

Элис вдохновляла, делясь своими процессами и набросками, которые были захватывающими даже на ранних этапах. Пока она работала над дизайном, мы ломали голову над архитектурой. Первые обсуждения касались макропланировки, здания и его окон. Как окна будут адаптироваться к одной, двум или трём колоннам по мере появления большего пространства для просмотра? Насколько они смогут сжиматься или растягиваться? Каким будет максимальный размер здания? Насколько будут смещаться окна?
Вот предварительный просмотр адаптивного прототипа с использованием grid-auto-flow: dense демонстрирующий автоматическое размещение окон с помощью алгоритма сетки. Мы быстро поняли, что, хотя сетки с соотношением сторон прекрасно подходят для демонстрации изображений, они не позволяют окнам расширяться и сжиматься в неравномерном доступном пространстве и демонстрировать мощь контейнерных запросов.

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

Каждое окно должно было бы справляться с определённой степенью турбулентности, связанной с изменением размера. Ниже представлен прототип окна, демонстрирующий его реакцию на турбулентность и показывающий, насколько сильной может быть адаптация каждого интерактивного окна.
Анимация окон с помощью спрайт-листов
В некоторых окнах реализована анимация, делающая интерфейс более интерактивным. Анимация рисуется вручную, кадр за кадром, в Photoshop. Каждый кадр экспортируется, преобразуется в спрайт-лист с помощью этого генератора спрайт-листов и оптимизируется с помощью Squoosh. CSS-анимация затем использует background-position-x и animation-timing-function как показано в следующем примере.
.una
background: url("/day1/una_sprite.webp") 0% 0%;
background-size: 400% auto;
}
.day:is(:hover, :focus-within) .una {
animation: una-wave .5s steps(1) alternate infinite;
}
@keyframes una-wave {
0% { background-position-x: 0%; }
25% { background-position-x: 300%; }
50% { background-position-x: 200%; }
75% { background-position-x: 100%; }
}

Некоторые анимации, например, копилка из шестого дня , представляли собой пошаговые CSS-анимации. Мы добились этого эффекта, используя похожую технику, используя steps() , с той разницей, что ключевыми кадрами были позиции CSS-преобразований, а не позиции фона.
CSS-маскировка
Некоторые окна имели уникальную форму. Мы использовали маски и aspect-ratio , чтобы создать масштабируемое, уникальное и адаптивное окно.
Чтобы создать маску, такую как эта для восьмого окна, потребовались некоторые классические навыки работы с Photoshop, а также немного знаний о том, как работают маски в интернете. Давайте рассмотрим окно для восьмого дня.

Чтобы стать маской, внутреннюю фигуру типа четырёхлистного клевера необходимо изолировать как самостоятельную фигуру и залить белым цветом. Белый цвет определяет CSS, какой контент останется, а всё, что находится за белой границей, останется. В Photoshop внутренняя часть окна была выбрана, растушевана на 1 пиксель (для устранения проблем со сглаживанием), затем залита белым цветом и экспортирована с той же высотой и шириной, что и рамка окна. Таким образом, рамка и маска могли быть наложены друг на друга, отображая внутреннее содержимое рамки, как и ожидалось.

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

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

Сплющивание искусства
Чтобы сохранить точность иллюстрации и избежать размытия на экранах высокого разрешения, Alice использовала соотношение пикселей 3x. Планировалось использовать imgix и размещать оптимизированные изображения и форматы на их сервере, но мы обнаружили, что ручная настройка с помощью инструмента Squoosh может сэкономить нам 50% и более.

Иллюстрация имеет свои специфические сложности при сжатии, особенно мазки кисти и прозрачный шероховатый край, использованные Элис. Мы решили преобразовать каждое из трёх экспортированных изображений в формате PNG в форматы PNG, WebP и AVIF меньшего размера. Каждый тип файла обладает своими уникальными возможностями сжатия, и для поиска общих настроек оптимизации потребовалось сжать более 50 изображений.
Интерфейс командной строки Squoosh стал незаменимым инструментом при оптимизации более 200 изображений — ручная оптимизация заняла бы несколько дней. Получив общие настройки оптимизации, мы предоставили их в виде команд командной строки и пакетно обработали целые папки PNG-изображений, превратив их в сжатые версии в форматах WebP и AVIF.
Вот пример использования команды AVIF CLI squoosh:
npx @squoosh/cli --quant '{"enabled":true,"zx":0,"maxNumColors":256,"dither":1}' --avif '{"cqLevel":19,"cqAlphaLevel":17,"subsample":1,"tileColsLog2":0,"tileRowsLog2":0,"speed":6,"chromaDeltaQ":false,"sharpness":5,"denoiseLevel":0,"tune":0}' image-1.png image-2.png image-3.png
После того как оптимизированные изображения были добавлены в репозиторий, мы можем начать загружать их из HTML:
<picture>
<source srcset="/day1/inner-frame.avif" type="image/avif">
<source srcset="/day1/inner-frame.webp" type="image/webp">
<img alt="" decoding="async" role="presentation" src="/day1/inner-frame.png">
</picture>
Написание исходного кода изображения было утомительным, поэтому мы создали компонент Astro для встраивания изображений с помощью одной строки кода.
<Pic filename="day1/inner-frame" role="presentation" />
Пользователи экранного доступа и клавиатуры
Значительная часть опыта взаимодействия с Designcember формируется благодаря арт-объектам и интерактивным окнам. Нам было важно, чтобы пользователь клавиатуры мог пользоваться сайтом и заглядывать в окна, а пользователи программ чтения с экрана получали приятный озвученный опыт.
Например, при встраивании изображений мы использовали role="presentation" , чтобы обозначить изображение как презентационное для программ чтения с экрана. Мы посчитали, что использование от 5 до 12 фрагментированных alt описаний будет неудобным. Поэтому мы обозначили изображения как презентационные и добавили общее описание окна. Перемещение между окнами в программе чтения с экрана создаёт приятное ощущение повествования, которое, как мы надеялись, поможет передать ту причудливость и увлекательность, которыми стремится поделиться сайт.
В следующем видео показана демонстрация работы с клавиатурой. Клавиши Tab, Enter, пробел и Escape используются для управления фокусом между всплывающими окнами и окнами.
В интерфейсе экранного чтения предусмотрены специальные атрибуты ARIA, которые делают контент более понятным. Например, ссылки на дни обозначаются как «один» или «два», но с добавлением ARIA они обозначаются как «День первый» и «День второй». Кроме того, все изображения объединены в одну метку, поэтому каждое окно имеет описание.
Astro, статический, компонентно-управляемый генератор сайтов
Astro упростил совместную работу команды над сайтом. Модель компонентов была знакома как разработчикам Angular, так и React, а система стилей имён классов с ограниченной областью действия помогала каждому разработчику быть уверенным, что его работа над окном не будет конфликтовать с работой других разработчиков.
Дни как компоненты
Каждый день представлял собой компонент , извлекающий данные о состоянии из хранилища данных во время сборки . Это позволяло нам запускать логику шаблона до того, как HTML-код попадал в браузер. Эта логика определяла, следует ли отображать подсказку для дня, поскольку для неактивных дней всплывающие окна отсутствуют.
Сборки выполняются каждый час, и хранилище данных о времени сборки открывает доступ к новому дню, когда сервер сборки переваливает за полночь. Эти самообновляющиеся и самодостаточные небольшие системы поддерживают сайт в актуальном состоянии.
Ограниченные стили и открытые свойства
Стили Astroscopes были написаны внутри компонентной модели , что упростило распределение рабочей нагрузки между членами команды и сделало использование Open Props более удобным. Стили Open Props normalize.css оказались очень полезны при работе с адаптивной (светлой и тёмной) темой, а также помогли упорядочить контент, такой как абзацы и заголовки.
Будучи одними из первых пользователей Astro, мы столкнулись с несколькими проблемами PostCSS. Например, мы не смогли обновить Astro до последней версии из-за слишком большого количества проблем со сборкой. Можно было бы уделить этому больше времени, оптимизировав процессы сборки и разработки.
Гибкие контейнеры
Некоторые окна увеличивались и уменьшались, сохраняя соотношение сторон для сохранения своего дизайна. Мы использовали другие окна, чтобы продемонстрировать мощь компонентной архитектуры с помощью контейнерных запросов. Контейнерные запросы позволяли окнам сохранять свою индивидуальную информацию об адаптивном стиле и корректировать его в зависимости от собственных размеров. Некоторые окна становились узкими и широкими, и им требовалось корректировать размер и расположение медиа-элементов внутри них.

По мере появления большего пространства для окна мы могли бы адаптировать его размер или дочерние элементы. Оказалось, что для реализации адаптивных окон контейнерные запросы не просто интересны для демонстрации, они необходимы и значительно упрощают организацию определённых макетов.
.day {
container: inline-size;
}
.day > .pane {
min-block-size: 250px;
@container (min-width: 220px) {
min-block-size: 300px;
}
@container (min-width: 260px) {
min-block-size: 310px;
}
@container (min-width: 360px) {
min-block-size: 450px;
}
}
Этот подход отличается от сохранения пропорций. Он обеспечивает больше контроля и возможностей. При достижении определённого размера многие дети начинают менять положение, чтобы адаптироваться к новой компоновке.
Запросы-контейнеры также позволили нам поддерживать блочное (вертикальное) ограничение, поэтому по мере увеличения длины окна мы могли корректировать его стили. Это видно в запросах по высоте, которые мы использовали отдельно, а также в запросах по ширине:
.person {
place-self: flex-end;
margin-block: 25% 50%;
margin-inline-start: -15%;
z-index: var(--layer-1);
@container (max-height: 350px) and (max-width: 425px) {
place-self: center flex-end;
inline-size: 50%;
inset-block-end: -15%;
margin-block-start: -2%;
margin-block-end: -25%;
z-index: var(--layer-2);
}
}
Мы также использовали контейнерные запросы, чтобы показывать и скрывать детали, поскольку при малых размерах изображение становилось всё более плотным, а при больших — всё более пустым. Окно девять — отличный пример того, как это сработало:
Кроссбраузерная поддержка
Для создания качественного современного кроссбраузерного интерфейса, особенно для экспериментальных API, таких как запросы контейнеров, нам нужен качественный полифилл. Мы обратились к нашей команде, и Сурма возглавил сборку нового полифилла для запроса контейнеров . Этот полифилл основан на ResizeObserver , MutationObserver и функции CSS :is() . Поэтому все современные браузеры поддерживают этот полифилл, в частности, Chrome и Edge с версии 88, Firefox с версии 78 и Safari с версии 14. Использование полифилла допускает любой из следующих синтаксисов:
/* These are all equivalent */
@container (min-width: 200px) {
/* ... */
}
@container (width >= 200px) {
/* ... */
}
@container size(width >= 200px) {
/* ... */
}
Темный режим

Последним важным штрихом для сайта Designcember стала красивая тёмная тема. Мы хотели показать, как можно использовать само искусство, чтобы активно участвовать в создании отличного тёмного режима. Для этого мы программно настроили стили фона каждого окна и использовали максимально возможное количество CSS при создании оформления окон. Большинство фонов представляли собой CSS-градиенты, чтобы было проще настраивать их цветовые значения. Затем мы разместили на них изображения.
Другие пасхальные яйца
Личные штрихи
Мы добавили на страницу несколько индивидуальных штрихов, чтобы придать сайту индивидуальность. Во-первых, это набор персонажей, вдохновлённый нашей командой. Мы также добавили курсор в стиле ретро для неактивных дней и поэкспериментировали со стилем фавикона.

Функциональные штрихи
Одним из дополнительных функциональных штрихов является функция «Перейти к сегодняшнему дню» с изображением птицы на крыше здания. Нажатие на эту птицу или Enter перенесёт вас вниз по странице к текущему дню месяца, что позволит быстро ознакомиться с последними новинками.
На сайте Designcember также есть специальная таблица стилей для печати, в которой мы, по сути, предлагаем определенное изображение, которое лучше всего смотрится на бумаге размером 8,5" x 11", чтобы вы могли распечатать календарь самостоятельно и сохранять праздничную атмосферу круглый год.

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