Время прочтения — 14 минут

Family Frontend Meetup #1

Обновления CSS и Next.js, принципы доступности веба, динамическое масштабирование шрифтов и эффективная типизация для redux-thunk

Содержание
Стереотипы о том, что разработчики в большинстве своем замкнутые интроверты, порой совсем и не стереотипы. Поэтому, чтобы расшевелить команду и привить обмен знаниями внутри компании, решили регулярно организовывать Family Frontend Meetup.
У СЕО и devrel планы наполеоновские — хотят потом сделать и вовсе публичное мероприятие. Шеринг знаниями и расширение круга знакомств всегда были полезны для роста и развития. Но это все в будущем. А пока, делимся темами и находками, которые обсуждали летом.

Какой компонент выбрать для обработки изображений?

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

Что нового у Next.js?

С момента выпуска последней версии прошло достаточно времени, чтобы потестить ее на новых проектах.
Вот какие отличия заметили:
1
Новый маршрутизатор приложений (Директория app), созданный на основе серверных компонентов React. Поддерживает состояние загрузки, ошибок и многое другое.
2
Новый способ выборки данных. Вместо функций инициализации используется функция вида fetch(API_URL, { cache: 'no-store', next: { revalidate: 10 } }), с помощью которой можно работать с кэшем, выполнять ревалидацию и многие другие вещи
3
Каждый компонент можно сделать серверным или клиентским. По-умолчанию все компоненты — серверные. Компоненты могут рендерится на сервере отдельно друг от друга с собственным состоянием загрузки. Выборку данных можно сделать на уровне компонента.
4
Улучшенная оптимизация компонента <Image /> и шрифтов с помощью @next/font.
Тем не менее тестирование новой версии фреймворка на реальном проекте показало, что несмотря на плюсы и всевозможные улучшения, есть ряд минусов и достаточно серьезных. По крайней мере, мы решили не использовать 13-ю версию в коммерческих проектах и подождать более стабильную версию. Рассказываем, почему.
Что нам не понравилось:
1
Абсолютно новая структура приложения. Требуется много ресурсов для изучения.
2
Изменения в next router. Роутер в привычном его понимании разбили на отдельные хуки, например: useParams, usePathname, useSearchParams. Сам router представляет собой набор методов для работы с навигацией, при том методы частично обрезаны. Все же помнят параметр shallow в router.push? Теперь его нет.
3
Локализация. На своих проектах мы используем популярную библиотеку i18next. В нынешней реализации Next.js интеграция локализации выглядит крайне громоздко. В предыдущих версиях необходимо было создать конфигурационный файл с парой строк, обернуть точку входа приложения в специальную обёртку, импортируемую из библиотеки и создать сами файлы локализации. Теперь же мы вынуждены использовать дополнительную библиотеку, настроить middleware файл, написать дополнительный хук и еще телегу дополнительной логики.
4
CSS-in-JS в настоящее время работает только с клиентскими компонентами, пришлось переписывать многие переиспользуемые компоненты на SCSS.
5
Redux. В предыдущих версиях можно было реализовать кэширование и связать сервер с клиентом через getInitialProps, либо используя гидратацию при использовании getServerSideProps. В текущей версии решения для реализации кэширования найдено не было.
Подробнее можно прочитать здесь:

Обзор новых возможностей CSS

Изучили последние достижения в CSS, включая scroll-snap, @container, @layer и CSS nesting. Посмотрели, как улучшить пользовательский опыт с помощью инновационных возможностей для стилей и техник компоновки.
Подробнее здесь:
scroll-snap — позволяет сделать простенький слайдер в пару строк. Иногда в мобильной адаптации требуется сделать обычный слайдер, но использовать стороннюю библиотеку или писать тяжелую логику для данной задачи не хотелось бы. На помощь приходит новая фича css, которая позволяет реализовать плавное переключение слайдов с помощью overflow свойства.
Родительский блок получает стили:
overflow-x: auto и scroll-snap-type: x mandatory
Дочерние элементы получают стили:
scroll-snap-stop: always и scroll-snap-align: start
На выходе получаем следующий результат:
@container — долгожданная фича, которая недавно стала поддерживаться всеми основными браузерами. Помогает делать адаптацию и не только её в зависимости от параметров определенного блока, а не только опираясь на значения viewport.

Для чего это может понадобиться?

Например, у вас есть одна карточка, которая в двух разных местах должна выглядеть по-разному, первое что приходит на ум: передать определенный ключ через props, по которому можно ориентироваться и изменять компонент. Имеет место быть, но теперь существует более элегантное решение с помощью нативного css.
Делаем родительский блок контейнером:

container: cart-popup-container / inline-size;
Адаптируем элемент внутри контейнера:

@container cart-popup-container (width < 475px) {
	color: black;
	font-size: 14;
}
Пример:
Как видите, в двух разных местах используется одна и та же карточка, с помощью @container мы изменили её под наши нужды в попапе сверху справа.
Подробнее можно прочитать здесь:
Сss layers — дополнительный способ контролировать каскадность css. Любые стили, объявленные вне слоя, будут переопределять стили, объявленные в слое, независимо от их специфики.
Подробнее можно прочитать здесь:
CSS Nesting — разработчики из Google уже много лет работают над реализацией нестинга в нативном CSS, как замена препроцессорам. В данный момент он уже поддерживается последними версиями браузеров Chrome, Safari и Firefox, что сподвигло нас обсудить перспективы данной фичи и возможное внедрение в наши проекты.

Главные тезисы

1
Всегда нужно использовать & (амперсанд), нельзя просто вложить внутрь селектор.
2
Возможность стилизации состояний элемента, его псевдоэлементов и дочерних элементов, с использованием атрибута style.
3
Отсутствие многих возможностей и функций, имеющихся в scss.
4
Теряем необходимость в дополнительных манипуляциях в виде установки и настройки препроцессора в вашем рабочем окружении.
5
Слишком сырой, чтобы использовать на реальных проектах.
Подробнее можно прочитать здесь:

Как реализовать динамическое масштабирование шрифтов?

Разобрали интересную технику адаптации шрифтов для разных экранов с использованием динамического масштабирования. Этот подход помогает улучшить опыт пользователей на разных устройствах при минимальных трудозатратах разработчика.
Данный подход для адаптации шрифтов в веб-приложениях использует CSS-правила и медиа-запросы для динамической настройки размера шрифта в зависимости от ширины окна просмотра (viewport width). Он позволяет создать адаптивное поведение для шрифтов, чтобы они оптимально масштабировались и оставались читаемыми на разных устройствах и экранах.
Формула выглядит так:

calc([min font-size] + ([max font-size] - [min font-size]) * ((curr viewport width - [min viewport width]) / ([max viewport width] - [min viewport width])));
Пример использования в реальном проекте:

html {
  	
  font-size: 10px;

  @media only screen and (min-width: 1113px) and (max-width: 1440px) {
    font-size: calc(6.5px + 3.5 * ((100vw - 1113px) / (1440 - 1113)));
  }

  @media only screen and (min-width: 835px) and (max-width: 1112px) {
    font-size: calc(7.5px + 2.5 * ((100vw - 835px) / (1112 - 835)));
  }
  @media only screen and (max-width: 834px) {
    font-size: calc(10px + 1.5 * ((100vw - 375px) / (834 - 375)));
  }
}

body {
  font-size: 1.6rem;
}

Что такое «Принципы Доступности Веба»?

Принципы доступности в вебе позволяют обеспечивать инклюзивность для всех пользователей. Их популярность с каждым годом растет, поэтому на митапе обсудили самые важные моменты.
Мы выделили для своих проектов 7 основных подходов:
1
Все, что пользователь может сделать с помощью клика мышью, можно сделать и с помощью клавиатуры. Добавляется псевдокласс :focus-visible для обработки фокуса с клавиатуры.
2
Каждый input должен быть связан с тегом label, что позволяет скринридерам читать назначение инпута.
3
Атрибут role + атрибут tabIndex для создания интерактивных элементов с не интерактивными html тегами. Возникают ситуации, когда нужно сделать какой либо компонент кликабельным, например карточку товара. Вместо того, чтобы заворачивать её в тег <button/> можно добавить описанные выше атрибуты и получить интерактивный кликабельный семантически верный элемент.
4
aria-label и aria-hidden для добавления описания элемента для скринридеров и скрытия элемента. Живой пример использования — ссылка на социальную сеть в виде svg-иконки. Скринридер автоматически читает содержимое тега <a>, однако если содержимое отсутствует, то и читать нечего. В этой ситуации можно использовать aria-label, чтобы различные роботы понимали, куда ведёт ссылка, и сообщали об этом пользователю. Атрибут aria-hidden можно использовать для скрытия неинтерактивного контента от API специальных возможностей. Например, внутри такой ссылки у нас есть изображение, которое робот попытается прочесть. Чтобы избежать подобной ситуации, используем данный атрибут.
5
alt атрибут должен быть в каждом изображении. Тут и объяснять нечего. Просто аксиома, которую необходимо запомнить. Данный атрибут индексируется и читается всеми роботами, при его отсутствии изображение становится невидимым.
6
role=«alert» или role=«status». Используется для оповещения пользователей в случае появления уведомлений и ошибок. При использовании «alert», скринридер прерывает чтение любой другой информации и в срочном порядке зачитывает появившийся контент, в случае использования «status», контент зачитывается после выполнения текущих задач.
7
<dialog/> в качестве модальных окон. Если заменить div на dialog, можно из коробки получить возможность открытия/закрытия модального окна, в том числе с помощью клавиатуры. Например, для закрытия можно нажать на esc.

Эффективная типизация для redux-thunk и использование condition

При типизации redux-thunk проще всего использовать any, но мы изучили, как можно правильно составить типизацию и лучше организовать код, чтобы он был безопасным и понятным, когда вы работаете с асинхронными действиями при использовании redux-thunk.
Это может пригодиться, например, для сценариев, где выполнение действия или создание полезной нагрузки требуется только при определенных условиях.
Ниже пример типизированного Thunk.
C помощью дженерика, передаваемого в createAsyncThunk, можно описать возвращаемое значение, передаваемые параметры и третий необязательный параметр, который можно использовать для настройки поведения вашего Thunk.
Этот параметр позволяет вам прикрепить дополнительные данные или конфигурации к функции создания полезных данных и повлиять на то, как обрабатывается ваша асинхронная операция.

 interface ServiceParams {
  	sessionToken: CookieValueTypes;
  }

  export type AsyncThunkApi = {
rejectValue: T;
state: AppState;
  };

  export type AsyncThunkCachedResponse = {
data: T;
key: string;
  };

  export const fetchNews = createAsyncThunk<
   AsyncThunkCachedResponse,
   ServiceParams,
   AsyncThunkApi
  >(
   'catalog/fetchNews',
 	async (thunkParams, thunkApi) => {
const { rejectWithValue, dispatch, getState } = thunkApi;

try {
// your logic…
} catch (e) {
return rejectWithValue(String(e));
}
  },
  {
   condition: (thunkParams, thunkApi) => {
const { getState } = thunkApi;
const key = JSON.stringify(thunkParams);
const news = getNewsByKey(getState().news, key);

if (news) {
    return false;
}
    },
  }
);
condition — это опция, позволяющая отменить выполнение Thunk перед тем, как создатель payload будет вызван. Это может пригодиться, например, для отмены запроса, который уже находится в процессе выполнения или если запрос уже был выполнен заранее и результат уже имеется в хранилище.
— Подробнее читайте тут
Если интересны такие обзоры или статьи по разработке, подписывайтесь на нас
в твиттере и следите за обновлениями.