Foto Andrea Mangano
Andrea Mangano

Come semplificare la scrittura di styled components con l'attributo 'css'

04 gennaio 2019
7m
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.
React.js
Styled Components
Componenti
Css
Tutorial
Condividi su

Scopo del tutorial

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

In questo tutorial mostro come sia possibile creare, attraverso l’uso dell’attributo css (di recente introduzione), 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

Chi è nuovo alla scrittura di stile attraverso gli styled-components potrebbe non afferrare la motivazione da cui nasce l’idea di questo tutorial.

Faccio quindi un passo indietro per chiarire le perplessità (e scomodità) 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:

import React from "react";
import PropTypes from "prop-types";
import styled from "styled-components";

const ButtonStyled = styled.button`
  background-color: #e43;
  padding: 1rem 1.5rem;
  font-size: 1rem;
  color: white;
  outline: 0;
  border: 0;
  cursor: pointer;
`;

const IconStyled = styled.span`
  margin-right: 1rem;
`;

const Button = ({ label, icon }) => (
  <ButtonStyled>
    {icon && <IconStyled>{icon}</IconStyled>}
    {label}
  </ButtonStyled>
);

Button.propTypes = {
  label: PropTypes.string.isRequired,
  icon: PropTypes.element
};

export default Button;

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

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

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

<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, maggiore saranno 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.

<ButtonStyled>
  {icon && <IconStyled>{icon}</IconStyled>}
  {label}
</ButtonStyled>

Fino all’introduzione dell’attributo css (tra l’altro già presente da tempo in glamor), alcuni di questi problemi non erano risolvibili. Addesso 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à:

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

o, in alternativa, con npm:

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

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

{
  "plugins": [
    "babel-plugin-styled-components"
  ]
}

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:

<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:

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

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

import { css } from "styled-components";

...

<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:

// Verifica la presenza della prop 'danger' sul tag <h1>
// Se presente sovrascrive il colore del testo con il colore della variabile 'colorDanger' 
<h1
  danger
  css={css`
    color: ${colorDefault};
    
    ${props => props.danger && `
      color: ${colorDanger};
    `}
  `}
>
  Titolo
</h1>

Posso inoltre accedere al tema stabilito se presente:

<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:

...

const Button = ({ label, icon }) => (
  <button
    type="button"
    css={css`
      background-color: #e43;
      padding: 1rem 1.5rem;
      font-size: 1rem;
      color: white;
      outline: 0;
      border: 0;
      cursor: pointer;
      display: inline-flex;
      align-items: center;
    `}
  >
    {icon && <span css={css`margin-right: 1rem;`}>{icon}</span>}
    {label}
  </button>
);

...

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.

Variabili per la definizione di stili

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

const basicStyle = css`
  background-color: #e43;
  padding: 1rem 1.5rem;
  font-size: 1rem;
  color: white;
  outline: 0;
  border: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
`;

const iconStyle = css`
  margin-right: 1rem;
`;

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

<button type="button" css={basicStyle}>
  {icon && <span css={iconStyle}>{icon}</span>}
  {label}
</button>

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

import React from "react";
import PropTypes from "prop-types";
import { css } from "styled-components";

const basicStyle = css`
  background-color: #e43;
  padding: 1rem 1.5rem;
  font-size: 1rem;
  color: white;
  outline: 0;
  border: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
`;

const iconStyle = css`
  margin-right: 1rem;
`;

const Button = ({ label, icon }) => (
  <button type="button" css={basicStyle}>
    {icon && <span css={iconStyle}>{icon}</span>}
    {label}
  </button>
);

Button.propTypes = {
  label: PropTypes.string.isRequired,
  icon: PropTypes.element
};

export default Button;

Separazione dello stile dalla definzione 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:

import { css } from "styled-components";

export const basicStyle = css`
  background-color: #e43;
  padding: 1rem 1.5rem;
  font-size: 1rem;
  color: white;
  outline: 0;
  border: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
`;

export const iconStyle = css`
  margin-right: 1rem;
`;

Nota che 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:

import React from "react";
import PropTypes from "prop-types";
import { basicStyle, iconStyle } from "./Button.style";

const Button = ({ label, icon }) => (
  <button type="button" css={basicStyle}>
    {icon && <span css={iconStyle}>{icon}</span>}
    {label}
  </button>
);

Button.propTypes = {
  label: PropTypes.string.isRequired,
  icon: PropTypes.element
};

export default Button;

Il gioco è fatto!

Conclusioni

Ad essere sincero, non ho subito accettato l’idea di scrivere codice Css in JavaScript.

La mia iniziare paura era di non riuscire a mantenere netta la separazione di interessi tra quello che è logica, contenuto e visualizzazione. Un timore concreto agli albori di questa pratica.

Questo tutorial suggerisce una soluzione semplice per manterenere e gestire gli stili quanto più possibile separati dalle logiche della vista, anche utilizzando JavaScript e, nello specifico, gli styled-component.

Non ti resta che dare uno sguardo all’esempio completo su CodeSandbox e misurarne di persona l’utilità nel tuo prossimo progetto!

Edit pp3kn4220m

Alla prossima!

L'autore
Foto di Andrea Mangano

Andrea Mangano

Frontend Developer @ iBuildings

Da più di otto anni, costruisco interfacce utente per siti e applicazioni web commerciali, con constante curiosità ed entusiasmo per le nuove tecnologie.

Professionalità, tenacia e propensione al rischio sono i miei tratti distintivi.

Seguimi su twitter: @andreaman87