Styled JSX — это библиотека CSS-in-JS, которая позволяет писать инкапсулированные и изолированные стили для компонентов. Стили, определённые для одного компонента, не влияют на другие компоненты, что позволяет добавлять, изменять и удалять стили без риска возникновения непредвиденных побочных эффектов.
Начало работы
Next.js включает Styled JSX по умолчанию, поэтому для начала работы достаточно добавить тег <style jsx>
в существующий React-элемент и написать внутри него CSS:
function Home() {
return (
<div className="container">
<h1>Hello Next.js</h1>
<p>Let's explore different ways to style Next.js apps</p>
<style jsx>{`
.container {
margin: 50px;
}
p {
color: blue;
}
`}</style>
</div>
);
}
export default Home;
В этом примере мы добавляем стили для контейнера компонента и абзаца. Несмотря на использование общих селекторов, эти стили не влияют на элементы с классом container
или теги <p>
в других компонентах. Это происходит потому, что Styled JSX обеспечивает изоляцию стилей только для данного компонента (добавляя уникальные классы к стилизованным элементам).
Добавив всего один атрибут jsx
к элементу <style>
, вы можете писать стандартный CSS, который автоматически префиксируется и изолируется для компонента. Элементы <style jsx>
должны располагаться внутри корневого элемента компонента.
Добавление глобальных стилей
Большинству проектов требуются глобальные стили для оформления элемента body
или сброса CSS. Styled JSX позволяет добавлять глобальные стили с помощью <style jsx global>
. Например:
function Home() {
return (
<div className="container">
<h1>Hello Next.js</h1>
<p>Let's explore different ways to style Next.js apps</p>
<style jsx>{`
.container {
margin: 50px;
}
p {
color: blue;
}
`}</style>
<style jsx global>{`
p {
font-size: 20px;
}
`}</style>
</div>
);
}
export default Home;
Это применит размер шрифта 20px ко всем тегам <p>
на этой странице.
Чтобы применить глобальные стили ко всем страницам приложения, хорошей практикой является создание компонента макета (Layout) с глобальными стилями и оборачивание всех страниц в него.
Использование компонента макета обеспечивает гибкость: можно применять определённые стили к некоторым страницам, сохраняя возможность использовать другие стили для остальных:
function Layout(props) {
return (
<div className="page-layout">
{props.children}
<style jsx global>{`
body {
margin: 0;
padding: 0;
font-size: 18px;
font-weight: 400;
line-height: 1.8;
color: #333;
font-family: sans-serif;
}
h1 {
font-weight: 700;
}
p {
margin-bottom: 10px;
}
`}</style>
</div>
);
}
export default Layout;
В Next.js мы можем загрузить макет один раз для всех страниц, создав кастомный компонент App
в pages/_app.js
, импортировав компонент Layout
и добавив его в метод render следующим образом:
import React from 'react';
import App, { Container } from 'next/app';
import Layout from '../components/Layout';
class MyApp extends App {
render() {
const { Component, pageProps } = this.props;
return (
<Container>
<Layout>
<Component {...pageProps} />
</Layout>
</Container>
);
}
}
export default MyApp;
Вынесение стилей в отдельные файлы
Мы также можем выносить стили в отдельные файлы вне компонента.
Например, мы можем перенести глобальные стили из компонента Layout
в отдельный файл:
import css from 'styled-jsx/css';
export default css.global`
body {
margin: 0;
padding: 0;
font-size: 18px;
font-weight: 400;
line-height: 1.8;
color: #333;
font-family: sans-serif;
}
h1 {
font-weight: 700;
}
p {
margin-bottom: 10px;
}
`;
Затем мы можем импортировать эти стили обратно в компонент Layout
:
import globalStyles from '../styles/global.js';
function Layout(props) {
return (
<div className="page-layout">
{props.children}
<style jsx global>
{globalStyles}
</style>
</div>
);
}
export default Layout;
Единичные глобальные селекторы
Стили, добавленные к компоненту через <style jsx>
, влияют только на элементы внутри этого компонента, но не на дочерние компоненты.
Иногда может потребоваться переопределить определённый стиль дочернего компонента. Для этого Styled JSX предоставляет :global()
, дающий доступ к единичным глобальным селекторам.
Например, предположим, что у нас есть компонент <Widget>
, содержащий кнопку с классом btn
. Если мы хотим изменить цвет этой кнопки только при импорте виджета на главной странице, мы можем сделать это так:
import Widget from '../components/Widget';
function Home() {
return (
<div className="container">
<h1>Hello Next.js</h1>
<Widget />
<style jsx>{`
.container {
margin: 50px;
}
.container :global(.btn) {
background: #000;
color: #fff;
}
`}</style>
</div>
);
}
export default Home;
Динамические стили
По сравнению с другими решениями, возможность адаптации стилей компонента на основе его пропсов — это большое преимущество библиотек CSS-in-JS
.
С Styled JSX мы можем сделать это следующим образом:
function Alert(props) {
return (
<div className="alert">
{props.children}
<style jsx>{`
.alert {
display: inline-block;
padding: 20px;
border-radius: 8px;
}
`}</style>
<style jsx>{`
.alert {
background: ${props.type == 'warning' ? '#fff3cd' : '#eee'};
}
`}</style>
</div>
);
}
export default Alert;
Если компоненту Alert
передаётся проп type
со значением warning
, например:
<Alert type="warning">This is an important message</Alert>
то компонент получит оранжевый фон. Без указания пропа type
фон останется серым по умолчанию.
Обратите внимание, что мы поместили динамический стиль в отдельный тег <style jsx>
. Это не обязательно, но рекомендуется разделять статические и динамические стили, чтобы при изменении пропсов пересчитывались только динамические части.
Альтернативный подход к адаптации стилей на основе пропсов — применение разных классов в зависимости от значения пропа:
function Alert(props) {
return (
<div className={props.type == 'warning' ? 'alert warning' : 'alert'}>
{props.children}
<style jsx>{`
.alert {
display: inline-block;
padding: 20px;
border-radius: 8px;
background: #eee;
}
.alert.warning {
background: #fff3cd;
}
`}</style>
</div>
);
}
export default Alert;
Создание темы сайта
Тема может быть простым объектом, содержащим наиболее часто используемые переменные в приложении:
const theme = {
fontFamily: {
sansSerif: '-apple-system, "Helvetica Neue", Arial, sans-serif',
mono: 'Menlo, Monaco, monospace',
},
colors: {
text: '#333',
background: '#fff',
link: '#1eaaf1',
linkHover: '#0d8ecf',
border: '#ddd',
warning: '#fff3cd',
success: '#d4edda',
},
};
export default theme;
Затем мы импортируем этот файл темы в наши компоненты и заменяем жёстко заданные значения переменными:
import theme from '../styles/theme';
function Layout(props) {
return (
<div className="page-wrapper">
{props.children}
<style jsx global>{`
body {
background: ${theme.colors.background};
color: ${theme.colors.text};
font-family: ${theme.fontFamily.sansSerif};
}
`}</style>
<style jsx global>{`
body {
margin: 0;
padding: 0;
font-size: 18px;
font-weight: 400;
line-height: 1.8;
}
h1 {
font-weight: 700;
}
p {
margin-bottom: 10px;
}
`}</style>
</div>
);
}
export default Layout;
import theme from '../styles/theme';
function Alert(props) {
return (
<div className="alert">
{props.children}
<style jsx>{`
.alert {
display: inline-block;
padding: 20px;
border-radius: 8px;
}
`}</style>
<style jsx>{`
.alert {
background: ${props.type == 'warning'
? theme.colors.warning
: theme.colors.light};
}
`}</style>
</div>
);
}
export default Alert;
В этой статье мы рассмотрели, как начать работать со Styled JSX. Чтобы узнать больше о дополнительных возможностях, обязательно ознакомьтесь с документацией на GitHub.