CSS-in-JS интеграция

React Aria предоставляет мощные инструменты для построения доступных пользовательских интерфейсов, концентрируясь на поведении и управлении состоянием компонентов, оставляя стилизацию на усмотрение разработчика. Интеграция CSS-in-JS позволяет создавать динамические стили, тесно связывая их с состоянием компонентов, что особенно полезно при работе с интерактивными элементами, такими как кнопки, вкладки, модальные окна и выпадающие списки.


Выбор CSS-in-JS библиотеки

Для интеграции React Aria с CSS-in-JS чаще всего используют следующие подходы:

  1. Styled Components Позволяет создавать компонентные стили с использованием шаблонных литералов:

    import styled from 'styled-components';
    
    const Button = styled.button`
      background-color: ${props => props.isPressed ? 'blue' : 'gray'};
      color: white;
      padding: 8px 16px;
      border: none;
      border-radius: 4px;
      cursor: pointer;
    
      &:hover {
        background-color: ${props => props.isPressed ? 'darkblue' : 'darkgray'};
      }
    `;
  2. Emotion Предоставляет возможность создавать стили с использованием функций css и styled, а также динамически изменять их на основе пропсов:

    /** @jsxImportSource @emotion/react */
    import { css } from '@emotion/react';
    import { useButton } from '@react-aria/button';
    
    function CustomButton(props) {
      let ref = React.useRef();
      let { buttonProps, isPressed } = useButton(props, ref);
    
      const buttonStyle = css`
        background-color: ${isPressed ? 'blue' : 'gray'};
        color: white;
        padding: 8px 16px;
        border: none;
        border-radius: 4px;
        cursor: pointer;
    
        &:hover {
          background-color: ${isPressed ? 'darkblue' : 'darkgray'};
        }
      `;
    
      return <button {...buttonProps} ref={ref} css={buttonStyle}>{props.children}</button>;
    }
  3. Stitches Предоставляет декларативный синтаксис для создания системных стилей с поддержкой вариаций:

    import { styled } from '@stitches/react';
    import { useButton } from '@react-aria/button';
    
    const Button = styled('button', {
      padding: '8px 16px',
      borderRadius: '4px',
      border: 'none',
      color: 'white',
      variants: {
        pressed: {
          true: { backgroundColor: 'blue' },
          false: { backgroundColor: 'gray' },
        },
      },
      '&:hover': {
        variants: {
          pressed: {
            true: { backgroundColor: 'darkblue' },
            false: { backgroundColor: 'darkgray' },
          },
        },
      },
    });
    
    function CustomButton(props) {
      let ref = React.useRef();
      let { buttonProps, isPressed } = useButton(props, ref);
    
      return <Button {...buttonProps} ref={ref} pressed={isPressed}>{props.children}</Button>;
    }

Связывание состояния React Aria с CSS-in-JS

React Aria предоставляет хуки для управления поведением компонентов без привязки к стилям:

  • useButton — отслеживает состояние нажатия и фокус.
  • useCheckbox — управляет состоянием чекбоксов и aria-атрибутами.
  • useMenu / useMenuItem — реализует доступные контекстные меню.

Интеграция с CSS-in-JS заключается в передаче этих состояний в пропсы стилей:

const buttonStyle = css`
  background-color: ${isPressed ? 'blue' : 'gray'};
  outline: ${isFocusVisible ? '2px solid orange' : 'none'};
`;

Важно учитывать focus-visible и другие состояния доступности, чтобы визуальная индикация соответствовала стандартам WCAG.


Динамическая генерация стилей

CSS-in-JS позволяет создавать стили на лету в зависимости от состояния компонента:

  • Псевдоклассы: hover, active, focus.
  • Варианты состояний: selected, disabled, expanded.
  • Темизация: поддержка light/dark и пользовательских тем.

Пример с табами:

import { useTab, useTabList } from '@react-aria/tabs';
import { styled } from '@stitches/react';

const TabButton = styled('button', {
  padding: '8px 16px',
  border: 'none',
  borderBottom: '2px solid transparent',
  variants: {
    selected: {
      true: { borderBottomColor: 'blue', fontWeight: 'bold' },
      false: { borderBottomColor: 'transparent', fontWeight: 'normal' },
    },
  },
});

function Tab({ item, state }) {
  let ref = React.useRef();
  let { tabProps, isSelected } = useTab({ key: item.key }, state, ref);

  return (
    <TabButton {...tabProps} ref={ref} selected={isSelected}>
      {item.name}
    </TabButton>
  );
}

Стратегии оптимизации

  1. Минимизация ререндеров стилей — использовать memoization для стилей, зависящих от состояния.
  2. Стилизация глобальных состояний — создавать theme-объекты для унификации цветов, шрифтов и отступов.
  3. Совместимость с SSR — библиотеки Emotion и Stitches позволяют безопасно генерировать CSS на сервере.
  4. Комбинирование с utility-классами — возможна интеграция с Tailwind через tw для быстрого прототипирования.

Практические рекомендации

  • Всегда учитывать aria-атрибуты при изменении внешнего вида: фокус, выбранное состояние, выключенные элементы.
  • Использовать variant-подход в CSS-in-JS для управления множественными состояниями.
  • Для сложных компонентов (например, выпадающих списков или модальных окон) отделять стили визуальные от логики состояния React Aria, чтобы облегчить поддержку и тестирование.
  • Интеграция с системами дизайна и theme-провайдерами позволяет легко менять внешний вид компонентов без изменения логики.

CSS-in-JS и React Aria создают мощное сочетание: доступная логика компонентов управляется React Aria, а динамическая, полностью кастомизируемая стилизация — через CSS-in-JS. Это обеспечивает гибкость, удобство поддержки и соответствие современным стандартам веб-разработки.