Мой ТОП-5 лучших CSS-in-JS решений

a12cba62805793eace886d09e8f80df4.png

Привет, Хабр!

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

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

Здесь вот и приходят на помощь решения CSS-in-JS, объединяющие фичи JS и CSS.

CSS-in-JS — это подход к стилизации, который позволяет писать стили прямо в JavaScript-коде. Преимущества такого подхода:

  1. Изоляция стилей: компоненты получают свои собственные стили.

  2. Динамические стили: легко применять стили в зависимости от состояния компонента или пропсов.

  3. Поддержка тем: удобное управление темами и их переключение на лету.

  4. Интеграция с JavaScript.

В этой статье я хотел бы представить свой ТОП-5 лучших решений CSS-in-JS, которые я использовал.

Styled Components

Styled Components — это библиотека для стилизации React-компонентов с использованием ES6 и шаблонных литералов. Она позволяет писать CSS в JavaScript, создавая стилизованные компоненты, которые инкапсулируют свои стили. Так стилизация становится более модульной и управляемой.

Styled Components использует теги шаблонных литералов для написания CSS внутри самого JS.

Для начала установим библиотеку с помощью npm или yarn:

npm install styled-components
# или
yarn add styled-components

После установки можно импортировать библиотеку.

Пример базового использования:

import styled from 'styled-components';

const Button = styled.button`
  background-color: #007bff;
  color: white;
  border: none;
  padding: 10px 20px;
  border-radius: 4px;
  cursor: pointer;

  &:hover {
    background-color: #0056b3;
  }
`;

function App() {
  return (
    

Welcome to Styled-Components!

); } export default App;

Создали компонент Button, который можно использовать как обычный React-компонент с предопределенными стилями. Стили можно изменять на основе пропсов, используя функцию внутри шаблонных литералов:

const Button = styled.button`
  background-color: ${props => props.primary ? '#007bff' : 'white'};
  color: ${props => props.primary ? 'white' : 'black'};
  /* ... другие стили ... */
`;

function App() {
  return (
    
); }

Styled Components поддерживает темизацию. Для этого можно использовать ThemeProvider:

import { ThemeProvider } from 'styled-components';

const theme = {
  primaryColor: '#007bff',
  secondaryColor: '#6c757d',
};

const Button = styled.button`
  background-color: ${props => props.primary ? props.theme.primaryColor : props.theme.secondaryColor};
  /* ... другие стили ... */
`;

function App() {
  return (
    
      
); } export default App;

ЗдесьButton использует свойства темы для применения стилей.

Styled Components также позволяет создавать глобальные стили с помощью createGlobalStyle:

import { createGlobalStyle } from 'styled-components';

const GlobalStyle = createGlobalStyle`
  body {
    font-family: 'Arial', sans-serif;
    background-color: #f0f0f0;
  }
`;

function App() {
  return (
    <>
      
      

Hello, World!

); } export default App;

Styled Components — очень мощный инструмент. Если вы еще не пробовали его, настоятельно рекомендую!

А подробнее с библиотекой можно ознакомиться здесь.

Linaria

Linaria — это zero-runtime CSS-in-JS библиотека, которая преобразует стили, написанные в JS, в отдельные CSS файлы на этапе сборки. Все это для снижения затрат на выполнение стилей во время работы приложения.

Linaria использует шаблонные литералы для определения стилей, которые затем компилируются в чистые CSS файлы. Она поддерживает фичи современного CSS: переменные, медиа-запросы, интеграцию с популярными инструментами сборки, такими как Webpack и Rollup.

Для начала использования Linaria, установим необходимые пакеты:

npm install @linaria/core @linaria/react @linaria/babel-preset

Затем настроим Babel, добавив @linaria в Babel конфиг:

{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react",
    "@linaria/babel-preset"
  ]
}

Linaria позволяет создавать стили с помощью функции css и компоненты с помощью styled:

import { css } from '@linaria/core';

const titleStyle = css`
  font-size: 24px;
  color: #333;
  text-align: center;
`;

function Title() {
  return 

Hello, Linaria!

; } export default Title;

Создали класс titleStyle и применяем его к компоненту Title.

Linaria также поддерживает создание стилизованных компонентов с функцией styled, аналогично Styled Components:

import { styled } from '@linaria/react';

const Button = styled.button`
  background-color: #007bff;
  color: white;
  border: none;
  padding: 10px 20px;
  border-radius: 4px;
  cursor: pointer;

  &:hover {
    background-color: #0056b3;
  }
`;

function App() {
  return (
    

Welcome to Linaria!

); } export default App;

Пример использования Linaria для создания темы в React-приложении:

// theme.js
export const theme = {
  colors: {
    primary: '#007bff',
    secondary: '#6c757d',
  },
};

// Button.js
import { styled } from '@linaria/react';
import { theme } from './theme';

const Button = styled.button`
  background-color: ${theme.colors.primary};
  color: white;
  border: none;
  padding: 10px 20px;
  border-radius: 4px;
  cursor: pointer;

  &:hover {
    background-color: ${theme.colors.secondary};
  }
`;

export default Button;

// App.js
import React from 'react';
import Button from './Button';

function App() {
  return (
    

Welcome to Themed Linaria!

); } export default App;

Создаем тему в отдельном файле и используем ее в стилизованном компоненте Button. Стили кнопки изменяются на основе свойств темы.

Подробнее с Linaria можно ознакомиться здесь.

Emotion

Emotion — это высокопроизводительная библиотека для CSS-in-JS, поддерживающая как стильные компоненты, так и базовые CSS стили.

Emotion предлагает два основных способа написания стилей: через styled-компоненты и через css-утилиту.

Для использования Emotion, установим необходимые пакеты:

npm install @emotion/react @emotion/styled

Создание стилей с помощью css:
css — это утилита, которая позволяет определять стили как объекты или строки.

/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react';

const style = css`
  color: hotpink;
`;

function App() {
  return 
Hello, Emotion!
; } export default App;

Здесь юзаем css для определения стиля и применяем его к элементу через атрибут css.

Создание стилизованных компонентов с помощью styled:
styled позволяет создавать React-компоненты с инкапсулированными стилями.

import styled from '@emotion/styled';

const Button = styled.button`
  background-color: #007bff;
  color: white;
  border: none;
  padding: 10px 20px;
  border-radius: 4px;
  cursor: pointer;

  &:hover {
    background-color: #0056b3;
  }
`;

function App() {
  return (
    
); } export default App;

Динамические стили:
Emotion позволяет легко применять динамические стили на основе пропсов.

const Button = styled.button`
  background-color: ${props => props.primary ? '#007bff' : 'white'};
  color: ${props => props.primary ? 'white' : '#007bff'};
  border: 2px solid #007bff;
  padding: 10px 20px;
  border-radius: 4px;
  cursor: pointer;

  &:hover {
    background-color: ${props => props.primary ? '#0056b3' : '#e0e0e0'};
  }
`;

function App() {
  return (
    
); } export default App;

Темизация:
Emotion поддерживает темизацию через контекст и ThemeProvider.

import { ThemeProvider } from '@emotion/react';

const theme = {
  colors: {
    primary: '#007bff',
    secondary: '#6c757d',
  },
};

const Button = styled.button`
  background-color: ${props => props.theme.colors.primary};
  color: white;
  border: none;
  padding: 10px 20px;
  border-radius: 4px;
  cursor: pointer;

  &:hover {
    background-color: ${props => props.theme.colors.secondary};
  }
`;

function App() {
  return (
    
      
); } export default App;

ЗдесьButton использует свойства темы для применения стилей, с помощью чего можно легко управлять цветовой схемой приложения.

Пример использования Emotion для создания адаптивного интерфейса:

import styled from '@emotion/styled';

const Container = styled.div`
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 20px;

  @media (min-width: 600px) {
    flex-direction: row;
  }
`;

const Item = styled.div`
  background-color: #f0f0f0;
  margin: 10px;
  padding: 20px;
  border-radius: 4px;
`;

function App() {
  return (
    
      Item 1
      Item 2
      Item 3
    
  );
}

export default App;

В этом кодеContainer меняет направление флекс-контейнера в зависимости от ширины экрана.

Документацию к Emotion можно посмотреть здесь.

Stitches

Stitches предлагает удобный API, поддержку тем и динамических стилей, а также интеграцию с фреймворками и инструментами сборки

Установим библиотеку через npm или yarn:

npm install @stitches/react
# или
yarn add @stitches/react

Функция createStitches позволяет определить конфигурацию для стилей и создавать styled-компоненты:

import { createStitches } from '@stitches/react';

const { styled, css, globalCss, theme } = createStitches({
  theme: {
    colors: {
      primary: '#007bff',
      secondary: '#6c757d',
    },
    fontSizes: {
      body: '16px',
      heading: '24px',
    },
  },
});

const Button = styled('button', {
  backgroundColor: '$primary',
  color: 'white',
  padding: '10px 20px',
  borderRadius: '4px',
  border: 'none',
  cursor: 'pointer',

  '&:hover': {
    backgroundColor: '$secondary',
  },
});

Создали кнопку с использованием тем и стилей, определенных в конфигурации createStitches.

Stitches позволяет задавать глобальные стили через функцию globalCss:

const globalStyles = globalCss({
  body: {
    margin: 0,
    fontFamily: 'Arial, sans-serif',
  },
});

function App() {
  globalStyles();

  return (
    

Hello, Stitches!

); } export default App;

Stitches позволяет легко применять динамические стили на основе пропсов:

const Button = styled('button', {
  variants: {
    color: {
      primary: {
        backgroundColor: '$primary',
        color: 'white',
      },
      secondary: {
        backgroundColor: '$secondary',
        color: 'white',
      },
    },
  },
  defaultVariants: {
    color: 'primary',
  },
});

function App() {
  return (
    
); } export default App;

Stitches поддерживает создание и использование тем:

const lightTheme = theme({
  colors: {
    background: 'white',
    text: 'black',
  },
});

const darkTheme = theme({
  colors: {
    background: 'black',
    text: 'white',
  },
});

const Container = styled('div', {
  backgroundColor: '$background',
  color: '$text',
  padding: '20px',
  borderRadius: '8px',
});

function App() {
  const [isDark, setIsDark] = React.useState(false);

  return (
    

Hello, Stitches!

); } export default App;

Пример кода Stitches для создания адаптивного интерфейса с динамическими стилями:

import { createStitches } from '@stitches/react';

const { styled, globalCss } = createStitches({
  theme: {
    colors: {
      primary: '#007bff',
      secondary: '#6c757d',
    },
  },
});

const globalStyles = globalCss({
  body: {
    margin: 0,
    fontFamily: 'Arial, sans-serif',
  },
});

const Container = styled('div', {
  display: 'flex',
  flexDirection: 'column',
  alignItems: 'center',
  padding: '20px',

  '@media(min-width: 600px)': {
    flexDirection: 'row',
  },
});

const Item = styled('div', {
  backgroundColor: '#f0f0f0',
  margin: '10px',
  padding: '20px',
  borderRadius: '4px',
});

function App() {
  globalStyles();

  return (
    
      Item 1
      Item 2
      Item 3
    
  );
}

export default App;

Здесь Container будет менять направление флекс-контейнера в зависимости от ширины экрана.

Как можно заметить, строчек здесь уже намного больше, чем в аналогичном примере у Emotion.

Подробнее со Stiches можно ознакомиться здесь.

Vanilla-Extract

Vanilla-Extract — это zero-runtime CSS-in-JS библиотека, которая позволяет писать стили в TypeScript или JavaScript и компилировать их в статические CSS файлы на этапе сборки.

Vanilla-Extract использует функции для определения стилей и тем, которые затем компилируются в отдельные CSS файлы.

Установим:

npm install @vanilla-extract/css

Затем настроим сборщик, например Webpack, для работы с Vanilla-Extract. Для этого нужно добавить плагин в конфигурацию Webpack.

Пример настройки Webpack:

// webpack.config.js
const { VanillaExtractPlugin } = require('@vanilla-extract/webpack-plugin');

module.exports = {
  // ... другие настройки
  plugins: [
    new VanillaExtractPlugin(),
  ],
  module: {
    rules: [
      {
        test: /\.css\.ts$/,
        use: [
          'vanilla-extract-loader',
          'ts-loader',
        ],
      },
    ],
  },
};

Vanilla-Extract позволяет создавать стили с помощью функции style и тем через функцию createTheme:

// styles.css.ts
import { style } from '@vanilla-extract/css';

export const button = style({
  backgroundColor: 'blue',
  color: 'white',
  padding: '10px 20px',
  borderRadius: '5px',
  border: 'none',
  cursor: 'pointer',
  ':hover': {
    backgroundColor: 'darkblue',
  },
});

Стили, определенные с помощью Vanilla-Extract, можно использовать в компонентах React или других фреймворках:

// Button.tsx
import React from 'react';
import { button } from './styles.css.ts';

const Button: React.FC = () => {
  return ;
};

export default Button;

Пример использования Vanilla-Extract для создания адаптивного интерфейса с динамическими стилями и темами:

// responsive.css.ts
import { style } from '@vanilla-extract/css';

export const container = style({
  display: 'flex',
  flexDirection: 'column',
  alignItems: 'center',
  padding: '20px',

  '@media': {
    '(min-width: 600px)': {
      flexDirection: 'row',
    },
  },
});

export const item = style({
  backgroundColor: '#f0f0f0',
  margin: '10px',
  padding: '20px',
  borderRadius: '5px',
});
// App.tsx
import React from 'react';
import { container, item } from './responsive.css.ts';

const App: React.FC = () => {
  return (
    
Item 1
Item 2
Item 3
); }; export default App;

Container меняет направление флекс-контейнера в зависимости от ширины экрана.

Подробнее — здесь.

Финальные слова

Все представленные инструменты в статье хороши по своему и каждый может найти свой по вкусу. Хотелось бы услышать о вашем опыте работы с CSS-in-JS, какие инструменты использовали и какие нюансы могли бы рассказать?

Все актуальные методы и инструменты веб-разработки можно освоить на онлайн-курсах OTUS: в каталоге можно посмотреть список всех программ, а в календаре — записаться на открытые уроки.

© Habrahabr.ru