Привет, Хабр!
В одном из проектов мне нужно было создать сложную админку для крупного клиента. Интерфейс требовал большо количества интерактивностей, а также поддержку различных тем и динамических стилей. Именно тогда я решил обратить свой взгляд на CSS‑in‑JS библиотеки, и это для меня стало большим открытием.
Ведь стилизация компонентов — основа для создания интуитивно понятных и эстетически приятных интерфейсов. И иногда традиционные методы стилизации, такие как CSS‑файлы или препроцессоры, имеют свои ограничения и могут усложнять сам процесс разработки, чего мы точно не хотим.
Здесь вот и приходят на помощь решения CSS‑in‑JS, объединяющие фичи JS и CSS.
CSS‑in‑JS — это подход к стилизации, который позволяет писать стили прямо в JavaScript‑коде. Преимущества такого подхода:
Изоляция стилей: компоненты получают свои собственные стили.
Динамические стили: легко применять стили в зависимости от состояния компонента или пропсов.
Поддержка тем: удобное управление темами и их переключение на лету.
Интеграция с 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 (
<div>
<h1>Welcome to Styled-Components!</h1>
<Button>Click me</Button>
</div>
);
}
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 (
<div>
<Button primary>Primary Button</Button>
<Button>Secondary Button</Button>
</div>
);
}
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 (
<ThemeProvider theme={theme}>
<div>
<Button primary>Primary Button</Button>
<Button>Secondary Button</Button>
</div>
</ThemeProvider>
);
}
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 (
<>
<GlobalStyle />
<div>
<h1>Hello, World!</h1>
</div>
</>
);
}
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 <h1 className={titleStyle}>Hello, Linaria!</h1>;
}
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 (
<div>
<h1>Welcome to Linaria!</h1>
<Button>Click me</Button>
</div>
);
}
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 (
<div>
<h1>Welcome to Themed Linaria!</h1>
<Button>Click me</Button>
</div>
);
}
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 <div css={style}>Hello, Emotion!</div>;
}
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 (
<div>
<Button>Click me</Button>
</div>
);
}
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 (
<div>
<Button primary>Primary Button</Button>
<Button>Secondary Button</Button>
</div>
);
}
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 (
<ThemeProvider theme={theme}>
<div>
<Button>Click me</Button>
</div>
</ThemeProvider>
);
}
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 (
<Container>
<Item>Item 1</Item>
<Item>Item 2</Item>
<Item>Item 3</Item>
</Container>
);
}
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 (
<div>
<h1>Hello, Stitches!</h1>
<Button>Click me</Button>
</div>
);
}
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 (
<div>
<Button color="primary">Primary Button</Button>
<Button color="secondary">Secondary Button</Button>
</div>
);
}
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 (
<div className={isDark ? darkTheme : lightTheme}>
<Container>
<h1>Hello, Stitches!</h1>
<Button onClick={() => setIsDark(!isDark)}>
Toggle Theme
</Button>
</Container>
</div>
);
}
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 (
<Container>
<Item>Item 1</Item>
<Item>Item 2</Item>
<Item>Item 3</Item>
</Container>
);
}
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 <button className={button}>Click me</button>;
};
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 (
<div className={container}>
<div className={item}>Item 1</div>
<div className={item}>Item 2</div>
<div className={item}>Item 3</div>
</div>
);
};
export default App;
Container
меняет направление флекс‑контейнера в зависимости от ширины экрана.
Подробнее — здесь
Финальные слова
Все представленные инструменты в статье хороши по своему и каждый может найти свой по вкусу. Хотелось бы услышать о вашем опыте работы с CSS-in-JS, какие инструменты использовали и какие нюансы могли бы рассказать?
Все актуальные методы и инструменты веб-разработки можно освоить на онлайн-курсах OTUS: в каталоге можно посмотреть список всех программ, а в календаре — записаться на открытые уроки.
Комментарии (11)
taujavarob
20.07.2024 20:47Странные библиотеки из прошлого.
Сейчас это tailwind. Как никак на дворе 2024 год.
nin-jin
Забавно, что все эти css-in-js ничего не знают про, собственно, Cascading Style Sheets, а единственная либа, которая про это знает, в топ не попала: https://mol.hyoo.ru/#!section=docs/=xwq9q5_f966fg
DarthVictor
И styled и linaria прекрасно работают с каскадностью.
nin-jin
Действительно, каскад там есть, но кривой - по именам блоков, но не элементов, в терминах БЭМ.
DarthVictor
Там каскад по имени сгенерированного класса. Примерно как в css-modules.
nin-jin
Ну вот в каунтере, как стилизовать кнопку инкремента так, чтобы не задеть остальные кнопки на любой глубине вложенности?
DarthVictor
Примерно так
nin-jin
Это стилизует все кнопки подряд, а не одну конкретную.
DarthVictor
Это стилизует кнопки внутри компонента. Если вам нужно одну кнопку компонента, то нужно было так и писать. Будет соответственно
nin-jin
Я именно так и написал - надо стилизовать конкретную кнопку, не перелопачивая код сторонних компонент. В вашем же примере нужен их глубокий рефакторинг.