BackRetour au blog

Styliser Next.js avec Styled JSX

Styled JSX est une bibliothèque CSS-in-JS qui permet d'écrire du CSS encapsulé et scopé pour styliser vos composants. Cet article vous aidera à commencer à utiliser Styled JSX dans Next.js.

Styled JSX est une bibliothèque CSS-in-JS qui permet d'écrire du CSS encapsulé et scopé pour styliser vos composants. Les styles que vous appliquez à un composant n'affecteront pas les autres composants, vous permettant ainsi d'ajouter, modifier et supprimer des styles sans craindre des effets secondaires indésirables.

Premiers pas

Next.js inclut Styled JSX par défaut, donc commencer est aussi simple qu'ajouter une balise <style jsx> dans un élément React existant et y écrire du CSS :

pages/index.js
function Home() {
  return (
    <div className="container">
      <h1>Hello Next.js</h1>
      <p>Explorons différentes manières de styliser les applications Next.js</p>
      <style jsx>{`
        .container {
          margin: 50px;
        }
        p {
          color: blue;
        }
      `}</style>
    </div>
  );
}
 
export default Home;

Dans cet exemple, nous incluons des styles pour l'élément conteneur du composant et un paragraphe. Même si nous utilisons des sélecteurs génériques, les styles n'affectent pas les éléments avec le nom de classe container ou les balises <p> dans d'autres composants. C'est parce que Styled JSX garantit que les styles sont limités à ce composant uniquement (en appliquant des noms de classe uniques supplémentaires aux éléments stylisés).

En ajoutant simplement un seul attribut jsx à un élément <style>, vous pouvez écrire du CSS standard qui est automatiquement préfixé et scopé au composant. Les éléments <style jsx> doivent être placés à l'intérieur de l'élément racine du composant.

Ajout de styles globaux

La plupart des projets nécessitent des styles globaux pour styliser l'élément body ou fournir des réinitialisations CSS. Styled JSX permet d'ajouter des styles globaux en utilisant <style jsx global>. Par exemple :

pages/index.js
function Home() {
  return (
    <div className="container">
      <h1>Hello Next.js</h1>
      <p>Explorons différentes manières de styliser les applications Next.js</p>
      <style jsx>{`
        .container {
          margin: 50px;
        }
        p {
          color: blue;
        }
      `}</style>
      <style jsx global>{`
        p {
          font-size: 20px;
        }
      `}</style>
    </div>
  );
}
 
export default Home;

Ceci applique une taille de police de 20px à toutes les balises <p> de cette page spécifique.

Pour appliquer des styles globaux à toutes les pages de notre application, une bonne approche consiste d'abord à créer un composant de mise en page (layout) avec les styles globaux, puis à envelopper toutes les pages avec celui-ci.

L'utilisation d'un composant de mise en page offre la flexibilité d'appliquer un certain ensemble de styles à certaines pages tout en permettant un style différent pour d'autres :

components/Layout.js
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;

Dans Next.js, nous pouvons charger la mise en page une fois pour toutes les pages en créant un composant App personnalisé dans pages/_app.js, en important le composant Layout, puis en l'ajoutant à la méthode render comme suit :

pages/_app.js
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;

Écrire des styles dans des fichiers externes

Nous pouvons également écrire des styles dans des fichiers externes en dehors du composant.

Par exemple, nous pouvons déplacer nos styles globaux du composant Layout vers un fichier séparé comme suit :

styles/global.js
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;
  }
`;

Nous pouvons ensuite importer les styles dans le composant Layout :

components/Layout.js
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;

Sélecteurs globaux ponctuels

Les styles que nous ajoutons à un composant en utilisant <style jsx> n'affectent que les éléments à l'intérieur de ce composant, mais pas les composants enfants.

Parfois, nous pouvons avoir besoin de remplacer un certain style d'un composant enfant. Pour ce faire, Styled JSX fournit :global(), donnant accès à des sélecteurs globaux ponctuels.

Par exemple, supposons que nous ayons un composant <Widget> qui contient un bouton avec le nom de classe btn. Si nous voulons changer les couleurs de ce bouton uniquement lorsque le widget est importé sur la page d'accueil, nous pouvons le faire comme ceci :

pages/index.js
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;

Styles dynamiques

Comparé à d'autres solutions, la capacité d'adapter le style d'un composant en fonction de ses props est un grand avantage des bibliothèques CSS-in-JS.

Avec Styled JSX, nous pouvons le faire comme ceci :

components/Alert.js
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;

Si le composant Alert reçoit une prop type avec la valeur warning comme :

<Alert type="warning">Ceci est un message important</Alert>

le composant aura un fond orange. Sans spécifier la prop type, le fond reviendra à la couleur grise par défaut.

Notez que nous avons placé le style dynamique dans une balise <style jsx> séparée. Ce n'est pas obligatoire, mais il est recommandé de séparer les styles statiques et dynamiques afin que seules les parties dynamiques soient recalculées lorsque les props changent.

Une approche alternative pour adapter les styles en fonction des props consiste à appliquer différents noms de classe en fonction de la valeur de la prop, comme illustré ci-dessous :

components/Alert.js
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;

Création d'un thème de site

Un thème peut être un simple objet où nous incluons les variables les plus courantes dont nous pourrions avoir besoin dans notre application :

styles/theme.js
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;

Nous importons ensuite ce fichier de thème dans nos composants et remplaçons les valeurs en dur par des variables :

components/Layout.js
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;
components/Alert.js
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;

Dans cet article, nous avons couvert comment commencer avec Styled JSX. Pour en savoir plus sur les fonctionnalités supplémentaires, assurez-vous de consulter le dépôt GitHub.