Frontend

Semplifica la scrittura di styled components con l'attributo 'css'

Andrea Mangano

Scrivere stile attraverso gli styled-components è verboso e dispersivo quando le viste sono complesse. In questo tutorial mostro come riuscire a creare styled component con meno linee di codice e come organizzare il codice css in maniera pratica ed efficiente.

Scopo del tutorial

Creare lo stile di un componente con styled-component diviene un’operazione poco pratica quando la complessità della vista aumenta.

In questo tutorial, mostro come sia possibile creare styled component in maniera meno verbosa e come si possa raggiungere, con qualche piccolo accorgimento, una maggiore organizzazione del codice.

Creazione classica di componenti di stile

Per chi è nuovo all’idea, mi accingo a mostrare le ridondanze presenti nello scrivere styled component nella via consueta.
Creo un file Button.jsx definendo un semplice componente bottone. Ad esso associo lo stile con l’uso classico degli styled-components:

1import React from "react";
2import PropTypes from "prop-types";
3import styled from "styled-components";
4
5const ButtonStyled = styled.button`
6 background-color: #e43;
7 padding: 1rem 1.5rem;
8 font-size: 1rem;
9 color: white;
10 outline: 0;
11 border: 0;
12 cursor: pointer;
13`;
14
15const IconStyled = styled.span`
16 margin-right: 1rem;
17`;
18
19const Button = ({ label, icon }) => (
20 <ButtonStyled>
21 {icon && <IconStyled>{icon}</IconStyled>}
22 {label}
23 </ButtonStyled>
24);
25
26Button.propTypes = {
27 label: PropTypes.string.isRequired,
28 icon: PropTypes.element
29};
30
31export default Button;

Uno styled component di fatto crea un contenitore di “stile” per uno specifico tag, associando ad esso un set di attributi css.

1const ButtonStyled = styled.button`
2 background-color: #e43;
3 padding: 1rem 1.5rem;
4 ...
5`

Lo styled component così definito, viene poi usato all’interno della vista:

1<ButtonStyled>{label}</ButtonStyled>

Nel componente Button sono presenti due styled component: uno agganciato al tag <button>, l’altro al tag <span> (che contiene il componente icon passato come prop).

Seguendo tale approccio, ogni qual volta mi occorra stilare un tag, devo creare un relativo styled component.
Più la vista è complessa, più sono gli styled component impiegati. Il risultato è un file che cresce di dimensioni, poco manutenibile e con variabili che risultano essere ridondanti. Non è pratico, inoltre, avere lo stile definito all’interno del componente.
Infine, sono impossibilitato a leggere in modo esplicito l’insieme di tag che compongono la vista poichè sono mascherati dai nomi dati agli styled component.

1<ButtonStyled>
2 {icon && <IconStyled>{icon}</IconStyled>}
3 {label}
4</ButtonStyled>

Fino all’introduzione dell’attributo css(tra l’altro già presente da tempo in glamor), alcuni di questi problemi non erano risolvibili. Adesso il modo esiste e va quindi sfruttato!

Supporto per l’attributo ‘css’

Prima di addentrarmi nell’uso e mostrarne i vantaggi pratici, installo l’ultima versione del plugin Babel per gli styled component che abilita il supporto alla funzionalità:

1yarn add -D babel-plugin-styled-components@latest

o, in alternativa, con npm:

1npm install --save-dev babel-plugin-styled-components@latest

Infine creo il file .babelrc (qualora non esista) aggiungendo babel-plugin-styled-components tra i plugin:

1{
2 "plugins": [
3 "babel-plugin-styled-components"
4 ]
5}

Questo è tutto ciò che occorre per supportare la funzionalità di cui non resta che scoprirne l’utilizzo!

Uso dell’attributo e dell’utility ‘css’

Piuttosto che creare uno styled component per aggiungere dello stile ad uno specifico tag (come visto in precedenza), utilizzo un attributo chiamato css per definire lo stile sul tag stesso.

Esso permette tutto ciò che un classico styled component supporta, compresi i componenti personalizzati che si riadattano in base alle prop o al tema fornito. Parto da un esempio base:

1<h1 css="color: red">Titolo</h1>

Questo approccio è molto simile alla scrittura del classico stile in linea.
Se volessi sostituire il colore con una variabile color potrei utilizzare un literal template per concatenare la variabile alla stringa:

1<h1 css={`color: ${color};`}>Titolo</h1>

oppure utilizzare l’utility css offerta da styled component (attenzione a non confonderla con l’attributo css stesso!):

1import { css } from "styled-components";
2
3// ...
4
5<h1 css={css`color: ${color};`}>Titolo</h1>

Il reale vantaggio di usare l’utility css è che posso accedere facilmente a tutti gli attributi presenti sul tag per creare uno stile adattivo:

1// Verifica la presenza della prop 'danger' sul tag <h1>
2// Se presente sovrascrive il colore del testo con il colore della variabile 'colorDanger'
3<h1
4 danger
5 css={css`
6 color: ${colorDefault};
7
8 ${props => props.danger && `
9 color: ${colorDanger};
10 `}
11 `}
12>
13 Titolo
14</h1>

Posso inoltre accedere al tema stabilito se presente:

1<h1 css={css`color: ${props => props.theme.colors.text};`}>Titolo</h1>

Per maggiori informazioni sull’impostazione di un tema, dai uno sguardo alla documentazione ufficiale.

Creazione di stile tramite l’attributo ‘css’

Ora che sono più chiari i concetti e l’uso pratico, riscrivo il componente Button usando l’approccio appena visto:

1// ...
2
3const Button = ({ label, icon }) => (
4 <button
5 type="button"
6 css={css`
7 background-color: #e43;
8 padding: 1rem 1.5rem;
9 font-size: 1rem;
10 color: white;
11 outline: 0;
12 border: 0;
13 cursor: pointer;
14 display: inline-flex;
15 align-items: center;
16 `}
17 >
18 {icon && <span css={css`margin-right: 1rem;`}>{icon}</span>}
19 {label}
20 </button>
21);
22
23// ...

Ho subito un riscontro positivo se considero il numero di linee di codice necessarie, ma nel contempo noto che la definizione dello stile è strettamente legata alla vista.

Uso di variabili per la definizione degli stili

Per evitare di sporcare la vista rendendola meno leggibile e agevole in modifica, creo una variabile per ogni definizione di stile:

1const buttonStyle = css`
2 background-color: #e43;
3 padding: 1rem 1.5rem;
4 font-size: 1rem;
5 color: white;
6 outline: 0;
7 border: 0;
8 cursor: pointer;
9 display: inline-flex;
10 align-items: center;
11`;
12
13const iconStyle = css`
14 margin-right: 1rem;
15`;

Quindi associo ogni variabili all’attributo css compattando la vista:

1<button type="button" css={buttonStyle}>
2 {icon && <span css={iconStyle}>{icon}</span>}
3 {label}
4</button>

Questo è l’intero codice ottenuto nel file Button.jsx:

1import React from "react";
2import PropTypes from "prop-types";
3import { css } from "styled-components";
4
5const buttonStyle = css`
6 background-color: #e43;
7 padding: 1rem 1.5rem;
8 font-size: 1rem;
9 color: white;
10 outline: 0;
11 border: 0;
12 cursor: pointer;
13 display: inline-flex;
14 align-items: center;
15`;
16
17const iconStyle = css`
18 margin-right: 1rem;
19`;
20
21const Button = ({ label, icon }) => (
22 <button type="button" css={buttonStyle}>
23 {icon && <span css={iconStyle}>{icon}</span>}
24 {label}
25 </button>
26);
27
28Button.propTypes = {
29 label: PropTypes.string.isRequired,
30 icon: PropTypes.element
31};
32
33export default Button;

Separazione dello stile dalla definizione del componente

Avendo separato lo stile dalla vista nel passo precedente, è immediato separare stile e componente in file differenti.
Creo quindi un file con nome Button.style.js e sposto al suo interno le variabili di stile, eliminando il suffisso Style ormai inutile:

1import { css } from "styled-components";
2
3export const button = css`
4 background-color: #e43;
5 padding: 1rem 1.5rem;
6 font-size: 1rem;
7 color: white;
8 outline: 0;
9 border: 0;
10 cursor: pointer;
11 display: inline-flex;
12 align-items: center;
13`;
14
15export const style = css`
16 margin-right: 1rem;
17`;

Ho aggiunto alle variabili la keyword export per permetterne l’utilizzo dall’esterno.
Quindi importo le definizioni di stile dentro il file Button.jsx da cui le ho precedentemente rimosse ed il gioco è fatto!

1import React from "react";
2import PropTypes from "prop-types";
3import * as styles from "./Button.style";
4
5const Button = ({ label, icon }) => (
6 <button type="button" css={styles.button}>
7 {icon && <span css={styles.icon}>{icon}</span>}
8 {label}
9 </button>
10);
11
12Button.propTypes = {
13 label: PropTypes.string.isRequired,
14 icon: PropTypes.element
15};
16
17export default Button;
Styled components
CSS
Componenti
React.js
← Precedente

Come creare un set di Icone in React.js

Successivo →

Recupero dati remoti tramite gli hook di React.js