Рубріки: Теория

Styled Components — стилизация React-приложений

Сергій Бондаренко

Сегодня мы рассмотрим удобную библиотеку для React – Styled Components. Она предназначена для реализации стилей в JavaScript файлах на основании входных данных React-компонентов — пропсов (props).

Особенности Styled Components

React — это отличный способ писать большие и быстрые JavaScript-приложения. При разработке динамических приложений c этим инструментом часто приходится стилизовать контент. По ряду причин использование стандартных средств CSS для этого не совсем удобно. Библиотека Styled Components дает возможность выполнить стилизацию React-приложения, упростив и ускорив написание кода. Styled Components позволяет работать со стилями прямо в JavaScript — это компонент, который в стиль подсовывает функцию от каких-то аргументов. В Styled Components вы обращаетесь к функции и она, по сути, может делать что угодно, возвращая любое строковое значение для стиля этого компонента. 

Преимущества и недостатки Styled Components в React-приложениях

Ранее, до появления Styled Components, если нужно было сделать динамические стили, их нужно было выносить в inline или писать много className. Но с этой библиотекой вы больше не стилизуете элементы HTML или компоненты на основании их класса или HTML-элемента. Отпадает необходимость в тернарных операторах, нет надобности прибегать к className. Вместо этого используются пропсы внутри компонент, с указанием стилей, а классы генерируются автоматически (к слову, проблема коллизии имен отсутствует как таковая). Обращение к стилям CSS происходит прямо в JavaScript, благодаря чему им легко управлять, он понятен, нет необходимости учить какой-то дополнительный синтаксис.

Стили легко отслеживать. Предположим, вы написали какой-то компонент, а затем его убрали, а стили остались – от них нужно избавиться, чтобы они лишний раз не грузились на клиент. Такая проблема со Styled Components решается автоматически — удалили компонент, удалились стили. Также к преимуществам Styled Components можно отнести наличие серверного рендеринга и модульных тестов. 

Очевидный недостаток Styled Components – привязка к React. Кроме того, зарезервированные названия (height, width, background-color и пр.) мы не должны использовать как входные данные React-компонентов. Еще один минус данной библиотеки – стили не сохраняются в кеше, стиль появляется только тогда, когда исполняется JavaScript. Это несколько влияет на производительность.

Установка Styled Components

Для начала разберемся с установкой Styled Components. Ее можно выполнить одной командой в командной строке:

# менеджер пакетов npm

npm install --save styled-components

Альтернативный вариант с yarn:

# через yarn

yarn add styled-components 

Стилизация компонентов происходит следующим образом. Вместо того, чтобы описывать стили HTML на основе класса:

<button className="btn">Knopka</button>

И

button.btn { background-color: #1dcde0;

    color: white;

    font-size: 22px;

    margin: 11px;

    padding: 5px 20px;

    border: 4px solid black;

    border-radius: 8px;}

Указываются компоненты, которые содержат собственные инкапсулированные наборы стилей, вот так:

const Knopka = styled.button`

   background-color: #1dcde0;

    color: white;

    font-size: 22px;

    margin: 11px;

    padding: 5px 20px;

    border: 4px solid black;

    border-radius: 8px;`;

С помощью утилиты create-react-app создадим новое приложение, удалим все лишнее, оставив приложение полностью пустым. После установки пакета переходим в компоненту App и импортируем этот модуль styled-component. Создадим стилизованный компонент – обертку приложения, грубо говоря, корневой блок div. 

Внутри шаблонных литералов пишем стили, которые собираемся использовать: ширина на 100 процентов, минимальная высота – на всю высоту окна браузера, padding в два rem (размер относительно шрифта) и черный цвет фона приложения. 

Чтобы увидеть стили, теперь обернем наше приложение в обертку:

 const App = () => {

    return (

      <AppWrapper>

         Some text

        </AppWrapper>

  );

};

Управление глобальными стилями

Поскольку по умолчанию в браузере всегда есть внешние-внутренние отступы (из-за чего появилась полоса прокрутки), их следует убрать глобальными стилями. Для этого в коде index.js из модуля styled-components импортируем функцию GlobalStyle и, по аналогии с предыдущим примером, создаем компонент, вызываем эту функцию, указывая шаблонных литералах стили. Используя универсальный селектор для каждого элемента на странице по умолчанию уберем внешние и внутренние отступы, параметр box-sizing (определяет как вычисляется общая ширина и высота элемента) поставим как border-box. Теперь этот компонент можно поместить в любой участок кода и эти стили будут функционировать. Обычно, их помещают в корень приложения, либо в компоненту app. Обернем все в react-фрагмент и добавим наше приложение App-компонент и глобальные стили:

import React from 'react';

import ReactDOM from 'react-dom';

import App from './App';

import styled, {createGlobalStyle} from "styled-components";

const Global = createGlobalStyle`

* {

       margin:0;

       padding:0;

       box-sizing:border-box;

       }`

ReactDOM.render(

    <>

        <Global/>

        <App />

    </>,

  document.getElementById( 'root')

);

Отступы пропали, значит, глобальные стили работают. В стилях подключим шрифт.

font-family: consolas;

Работа с пропсами

Чтобы упорядочить компоненты, создадим отдельную директорию, назовем ее components. Создадим там первый компонент – Title.js. Быстро делаем React-компонент (можно сниппетом, набрав rsc) и сделаем стилизованный заголовок, прописав в шаблонных литералах стили. 

import React from 'react';

import styled from "styled-components";

const StyledTitle = styled.h1`

color: white;

`

const Title = () => {

    return <StyledTitle> 

 

        </StyledTitle>

};

 

export default Title;

Вернемся в App.js и добавим его на страницу. Вместо Some text добавляем 

<Title>Highload.today </Title>

Если обновить страницу в браузере мы увидим черный цвет.

Чтобы React понял, что текст нужно добавить внутрь стилизованного заголовка, используем пропс, который называется {children}. Добавляем этот пропс и помещаем его в середину. 

import React from 'react';

import styled from "styled-components";

const StyledTitle = styled.h1`

color: white;

`

const Title = ({children}) => {

    return <StyledTitle>

            {children}

    </StyledTitle>

};

export default Title;

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

color: ${props => props.color};

Вернемся в компоненту App и добавим нашему заголовку пропс color, добавив, скажем, синий цвет:

<Title color={"blue"}>Highload.today </Title>

Если сейчас мы откроем браузер, то ничего не увидим, поскольку передали пропс не в StyledTitle, а просто в Title. Поэтому в Title.js этот пропс нужно принять и передать в StyledTitle:

const Title = ({children, color}) => {

    return <StyledTitle color={color}>

Открываем браузер и видим, что цвет изменился – таким образом можно передавая разные пропсы менять цвет заголовка. 

Однако, такая запись является достаточно неудобной. Ведь, если у нас будет с десяток различных пропсов, вручную их перечислять, конечно, неудобно. Есть более лаконичный синтаксис, позволяющий в StyledTitle развернуть все пропсы:

import React from 'react';

import styled from "styled-components";

const StyledTitle = styled.h1`

color: ${props => props.color};

`

const Title = (props) => {

    return <StyledTitle {...props}/>

};

export default Title;

Все, что мы будем передавать в Title, автоматически будет передаваться и в StyledTitle.

Создадим еще один компонент, назовем его Flex.js. Это будет тоже своего рода обертка, которая будет принимать направление Flex-контейнера, align-items и justify-content, чтобы выравнивать относительно контейнера все внутренние элементы.

import React from 'react';

import styled from 'styled-components'

const StyledFlex = styled.div`

display: flex;

flex-direction: ${props => props.direction || 'row'};

align-items: ${props => props.align || 'stretch'};

justify-content: ${props => props.justify || 'stretch'};

margin: ${({margin}) => margin || '0'};

`

const Flex = (props) => {

    return <StyledFlex {...props}/>

};

export default Flex;

Каждое из перечисленных свойств будет принимать значение, в зависимости от того, какой пропс мы прокинули. Так, если мы передали props.direction, то он будет присвоен  свойству flex-direction. В обратном случае будет присвоено значение ‘row’.

Возвращаемся в App.js – теперь можем пользоваться компонентом Flex и поместить внутрь него наш Title, центруя его посередине. 

import Title from "./components/Title";

import Flex from "./components/Flex";

const AppWrapper = styled.div`

width: 100% ;

min-height: 100vh;

padding: 2rem;

background: black;

`

const App = () => {

    return (

      <AppWrapper>

          <Flex justify = "center">

          <Title color={"blue"}>Highload.today </Title>

          </Flex>

        </AppWrapper>

  );

};

export default App;

Чтобы разобраться в данной теме, сделаем что-то вроде симулятора окна командной строки. Перейдем к верстке окна терминала – создаем компонент Console.js. Поле textarea растянем на всю ширину, высоту – 80% от высоты окна браузера, прокинем в компонент пропсы и возвратим из этого компонента нашу стилизованную консоль.

import React from 'react';

import styled from "styled-components";

const StyledConsole = styled.textarea`

width:100%;

height:80vh;

`

const Console = (props) => {

    return <StyledConsole {...props}/>

};

export default Console;

В компоненте Flex.js добавим эту консоль

const App = () => {

    return (

      <AppWrapper>

          <Flex justify = "center">

          <Title color={"blue"}>Highload.today </Title>

          </Flex>

          <Console/>

        </AppWrapper>

Настроим внешний вид консоли

color: ${({color}) => color || "red"

Псевдоклассы

На примере фокуса рассмотрим как работать с псевдоклассами.  Используем знак &, который заменяет селектор текущего элемента и указываем, чтобы рамка поля при выделении не рисовалась:

&:focus{ 

    outline: none;

}

Добавим кнопку и на примере этого элемента рассмотрим как можно делать разные стили в зависимости от пропса. Создаем и разворачиваем новый компонент Button.js:

import React from 'react';

import styled from "styled-components";

const StyledButton = styled.button`

border:none;

padding:10px 15px;

font-size:26px;

cursor: pointer;

& focus {

    outline:none;

    }

`

const Button = (props) => {

    return <StyledButton{...props}/>

};

export default Button;

Возвращаемся в App.js и добавляем под консоль эту кнопку:

.. <AppWrapper>

          <Flex justify = "center">

          <Title color={"blue"}>Highload.today </Title>

          </Flex>

          <Console/>

          <Button>Publish</Button>

        </AppWrapper>

Переместим кнопку вправо. Консоль и кнопку обернем во <Flex> и добавим пропс для того, чтобы сместить кнопку вправо:

     <AppWrapper>

          <Flex justify = "center">

          <Title color={"blue"}>Highload.today </Title>

          </Flex >

          <Flex direction="column">

              <Console/>

              <Button align="flex-end">Publish</Button>

          </Flex>

        </AppWrapper>

В Button.js добавляем свойство align-self, которое позволяет выровнять какой-то элемент внутри флекс-контейнера. Значение будем получать из пропсов, а если такового нет, по умолчанию установим stretch. 

  align-self: ${props => props.align || 'stretch'}

Группировка стилей на основании пропсов

Теперь займемся стилями. Чтобы сгруппировать несколько свойств, в зависимости от пропсов, используется следующая конструкция. Она состоит из логического “И” (&&) и указанного в шаблонных литералах стилей, которые применяются, если мы передали в компонент этот пропс. Помещаем код в Button.js

import React from 'react';

import styled from "styled-components";

const StyledButton = styled.button`

border:none;

padding:10px 15px;

font-size:26px;

cursor: pointer;

& focus {

    outline:none;

    } 

    align-self: ${props => props.align || 'stretch'};

${props => props.primary && `

color: ${props => props.color ||'white'};

background: ${props => props.background||'white'};

`}

`

const Button = (props) => {

    return <StyledButton{...props}/>

};

export default Button;

Перейдем в компоненту App и передадим пропс primary.

Button primary background={‘green’} color={'red'} align="flex-end">Publish</Button>

Меняем color={'green'}  и видим, что цвет кнопки стал красным на зеленом фоне.

Теперь сделаем стиль кнопки с обводкой

${props => props.outlined && css`

    color: ${props => props.color || 'white'};

    border: 1px solid ${props => props.color || "white"};

    background: transparent;

`}

Возвращаемся в App.js и передаем пропсам outlined

<Button outlined align="flex-end">Publish</Button>

Создадим кнопку, которая будет иметь все те же стили, что и Styled button, но больше в размерах. Создадим кнопку, что будет иметь все те же стили, что и наша Styled button. Вызываем Styled, параметром передаем компонент, от которого хотим унаследоваться,  увеличиваем размер шрифта.

const LargeButton = styled(StyledButton)`

    font-size: 32px;

Теперь кнопка стала больше, но она сохранила все стили, которые есть у StyledButton. Грубо говоря – LargeButton наследуется от StyledButton. Таким образом, стили можно расширять на основе уже существующих компонентов. 

Анимация

Принцип анимации тот же самый, что и в CSS. Чтобы добавить анимацию, необходимо использовать функцию keyframes. Для подключения используем псевдокласс hover.

import React from 'react';

import styled, {css, keyframes} from 'styled-components'

 

const rotateAnimation = keyframes`

0% {

    transform: rotateZ(0deg);

}

100% {

    transform: rotateZ(360deg);

}

`const StyledButton = styled.button.attrs(props => ({

    outlined: true,

}))`

border: none;

padding: 10px 15px;

font-size: 18px;

cursor: pointer;

&:focus {

    outline: none;

}

&: hover {

    animation: ${rotateAnimation} 1s infinite linear;

}

align-self: ${props => props.align || 'stretch'};

${props => props.primary && css`

    color: ${props => props.color || 'white'};

    background: ${props => props.background || 'white'};

`}

${props => props.outlined && css`

    color: ${props => props.color || 'white'};

    border: 1px solid ${props => props.color || "white"};

    background: transparent;

`}

`const LargeButton = styled(StyledButton)`

    font-size: 32px;

`const Button = (props) => {

    return <StyledButton {...props}/>

};

export default Button;

Заключение

Мы разобрались с библиотекой Styled Components и рассмотрели основные приемы работы с ней. Для более детального изучения данной темы рекомендуем вам посмотреть видео, где автор используя Styled Components проведет мастер класс по созданию SPA-приложения с выбором темы REST API запросами, поиском, фильтрацией SPA-проект с темизацией на React и styled-components.

Останні статті

Обучение Power BI – какие онлайн курсы аналитики выбрать

Сегодня мы поговорим о том, как выбрать лучшие курсы Power BI в Украине, особенно для…

13.01.2024

Work.ua назвал самые конкурентные вакансии в IТ за 2023 год

В 2023 году во всех крупнейших регионах конкуренция за вакансию выросла на 5–12%. Не исключением…

08.12.2023

Украинская IT-рекрутерка создала бесплатный трекер поиска работы

Unicorn Hunter/Talent Manager Лина Калиш создала бесплатный трекер поиска работы в Notion, систематизирующий все этапы…

07.12.2023

Mate academy отправит работников в 10-дневный оплачиваемый отпуск

Edtech-стартап Mate academy принял решение отправить своих работников в десятидневный отпуск – с 25 декабря…

07.12.2023

Переписки, фото, история браузера: киевский программист зарабатывал на шпионаже

Служба безопасности Украины задержала в Киеве 46-летнего программиста, который за деньги устанавливал шпионские программы и…

07.12.2023

Как вырасти до сеньйора? Девелопер создал популярную подборку на Github

IT-специалист Джордан Катлер создал и выложил на Github подборку разнообразных ресурсов, которые помогут достичь уровня…

07.12.2023