Fermer

juillet 27, 2021

Style global vs style local dans Next.js —


Résumé rapide ↬

Next.js a des opinions bien arrêtées sur la façon d'organiser JavaScript mais pas CSS. Comment pouvons-nous développer des modèles qui encouragent les meilleures pratiques CSS tout en suivant la logique du framework ? La réponse est étonnamment simple : écrire du CSS bien structuré qui équilibre les préoccupations de style globales et locales.

J'ai eu une grande expérience avec Next.js pour gérer des projets frontaux complexes. Next.js a des opinions sur la façon d'organiser le code JavaScript, mais il n'a pas d'opinions intégrées sur la façon d'organiser CSS.

Après avoir travaillé dans le cadre, j'ai trouvé une série de modèles d'organisation que je pense tous deux conformes aux philosophies directrices de Next.js et appliquer les meilleures pratiques CSS. Dans cet article, nous allons créer ensemble un site Web (un salon de thé !) Pour illustrer ces modèles.

Remarque : Vous n'aurez probablement pas besoin d'une expérience préalable de Next.js, bien que ce soit le cas. être bon d'avoir une compréhension de base de React et d'être ouvert à l'apprentissage de nouvelles techniques CSS. de la bibliothèque CSS-in-JS. Bien qu'il puisse y avoir des avantages en fonction du projet, CSS-in-JS introduit de nombreuses considérations techniques. Cela nécessite l'utilisation d'une nouvelle bibliothèque externe, ce qui augmente la taille de l'ensemble. CSS-in-JS peut également avoir un impact sur les performances en provoquant des rendus et des dépendances supplémentaires sur l'état global.

Lecture recommandée : « Les coûts de performances invisibles des bibliothèques CSS-in-JS modernes Dans React Apps)” par Aggelos Arvanitakis

En outre, l'intérêt d'utiliser une bibliothèque comme Next.js est de rendre statiquement les ressources chaque fois que cela est possible, donc cela n'a pas tellement de sens d'écrire du JS qui doit être exécuté dans le navigateur pour générer du CSS.

Il y a quelques questions que nous devons prendre en compte lors de l'organisation du style dans Next.js :

Comment pouvons-nous nous adapter aux conventions/meilleures pratiques du framework ?

Comment pouvons-nous équilibrer les problèmes de style « globaux » (polices, couleurs, mises en page principales, etc.) avec les « locaux » (styles concernant les composants individuels) ?

La réponse que j'ai trouvée pour la première question consiste à simplement écrire du bon vieux CSS . Non seulement Next.js prend en charge cette opération sans configuration supplémentaire ; il donne également des résultats performants et statiques.

Pour résoudre le deuxième problème, j'adopte une approche qui peut être résumée en quatre éléments :

  1. Design tokens
  2. Global styles
  3. Utility classes
  4. Component styles

Je suis redevable à l'idée d'Andy Bell de CUBE CSS ("Composition, Utility, Block, Exception") ici. Si vous n'avez jamais entendu parler de ce principe d'organisation auparavant, je vous recommande de consulter son site officiel ou fonctionnalité sur le Smashing Podcast. L'un des principes que nous retiendrons de CUBE CSS est l'idée que nous devrions embrasser plutôt que craindre la cascade CSS. Apprenons ces techniques en les appliquant à un projet de site Web.

Prise en main

Nous allons construire un magasin de thé parce que, eh bien, le thé est savoureux. Nous allons commencer par exécuter yarn create next-app pour créer un nouveau projet Next.js. Ensuite, nous supprimerons tout dans le répertoire styles/ (ce sont tous des exemples de code).

Remarque : Si vous souhaitez suivre le projet terminé, vous pouvez le vérifier ici.

Design Tokens

Dans pratiquement toutes les configurations CSS, il y a un avantage évident à stocker toutes les valeurs partagées globalement dans des variables. Si un client demande qu'une couleur change, la mise en œuvre du changement est une simple ligne plutôt qu'un énorme gâchis de recherche et de remplacement. Par conséquent, une partie clé de notre configuration CSS Next.js stockera toutes les valeurs à l'échelle du site en tant que design tokens.

Nous utiliserons les propriétés CSS personnalisées intégrées pour stocker ces jetons. (Si vous n'êtes pas familier avec cette syntaxe, vous pouvez consulter « Un guide de stratégie pour les propriétés personnalisées CSS ».) Je dois mentionner que (dans certains projets) j'ai choisi d'utiliser SASS/SCSS variables à cet effet. Je n'ai trouvé aucun avantage réel, donc je n'inclus généralement SASS dans un projet que si j'ai besoin d'autres fonctionnalités SASS (mix-ins, itération, importation de fichiers, etc.). Les propriétés personnalisées CSS, en revanche, fonctionnent également avec la cascade et peuvent être modifiées au fil du temps plutôt que d'être compilées de manière statique. Donc, pour aujourd'hui, restons-en au CSS.

Dans notre répertoire styles/créons un nouveau fichier design_tokens.css :

 :racine {
  --vert : #3FE79E ;
  --dark: #0F0235;
  --blanc cassé : #F5F5F3 ;

  --space-sm: 0.5rem;
  --space-md : 1rem ;
  --space-lg : 1,5 rem ;

  --font-size-sm: 0.5rem;
  --font-size-md : 1rem ;
  --font-size-lg: 2rem;
}

Bien sûr, cette liste peut et va s'allonger avec le temps. Une fois que nous avons ajouté ce fichier, nous devons accéder à notre fichier pages/_app.jsxqui est la mise en page principale de toutes nos pages, et ajouter :

import '../styles/design_tokens .css'

J'aime considérer les jetons de conception comme le ciment qui maintient la cohérence tout au long du projet. Nous allons référencer ces variables à l'échelle mondiale, ainsi qu'au sein de composants individuels, garantissant un langage de conception unifié.

Plus après le saut ! Continuez à lire ci-dessous ↓

Global Styles

Ensuite, ajoutons une page à notre site Web ! Sautons dans le fichier pages/index.jsx (c'est notre page d'accueil). Nous allons supprimer tout le passe-partout et ajouter quelque chose comme :

export default function Home() {
  retour 

Thés apaisants

Bienvenue dans notre merveilleux salon de thé.

Nous sommes ouverts depuis 1987 et servons nos clients avec des thés oolong triés sur le volet.

}

Malheureusement, il aura l'air assez simple, définissons donc des styles globaux pour les éléments de basepar exemple

balises. (J'aime considérer ces styles comme des « valeurs par défaut globales raisonnables ».) Nous pouvons les remplacer dans des cas spécifiques, mais ils sont une bonne idée de ce que nous voulons si nous ne le faisons pas.

Je vais mettre ceci dans le fichier styles/globals.css (qui vient par défaut de Next.js):

*,
*::avant,
*::après {
  dimensionnement de la boîte : border-box ;
}

corps {
  couleur: var(--blanc cassé);
  couleur d'arrière-plan : var(--dark);
}

h1 {
  couleur : var(--vert);
  taille de police : var(--taille de police-lg);
}

p {
  taille de police : var(--taille de police-md);
}

p, article, section {
  hauteur de ligne : 1,5 ;
}

:concentrer {
  contour : 0.15rem var en pointillés (--blanc cassé) ;
  décalage de contour : 0,25 rem ;
}
objectif principal {
  contour : aucun ;
}

img {
  largeur maximale : 100 % ;
}

Bien sûr, cette version est assez basique, mais mon fichier globals.css n'a généralement pas besoin d'être trop volumineux. Ici, je stylise les éléments HTML de base (titres, corps, liens, etc.). Il n'est pas nécessaire d'encapsuler ces éléments dans des composants React ou d'ajouter constamment des classes uniquement pour fournir un style de base.

J'inclus également les réinitialisations des styles de navigateur par défaut. Parfois, j'aurai un style de mise en page à l'échelle du site pour fournir un "pied de page collant", par exemple, mais ils n'appartiennent ici que si toutes les pages partagent la même mise en page. Sinon, il devra être défini dans des composants individuels.

J'inclus toujours une sorte de style :focus pour clairement indiquer des éléments interactifs pour les utilisateurs de clavier lorsqu'ils sont concentrés. Il est préférable d'en faire une partie intégrante de l'ADN de conception du site !

Maintenant, notre site Web commence à prendre forme :

Photo du site Web en cours de réalisation. L'arrière-plan de la page est maintenant de couleur bleu foncé et le titre « Thés apaisants » est vert. Le site Web n'a pas de mise en page/d'espacement et s'étend donc complètement à la largeur de la fenêtre du navigateur.

Image du site Web en cours de réalisation. L'arrière-plan de la page est maintenant de couleur bleu foncé et le titre « Thés apaisants » est vert. Le site Web n'a pas de mise en page/d'espacement et s'étend donc complètement à la largeur de la fenêtre du navigateur. ( Grand aperçu)

Classes utilitaires

Un domaine dans lequel notre page d'accueil pourrait certainement s'améliorer est que le texte s'étend actuellement toujours sur les côtés de l'écran, limitons donc sa largeur. Nous avons besoin de cette mise en page sur cette page, mais j'imagine que nous pourrions également en avoir besoin sur d'autres pages. C'est un excellent cas d'utilisation pour une classe utilitaire !

J'essaie d'utiliser les classes utilitaires avec parcimonie plutôt que de remplacer simplement l'écriture de CSS. Mes critères personnels lorsqu'il est judicieux d'en ajouter un à un projet sont :

  1. J'en ai besoin à plusieurs reprises ;
  2. Il fait une chose bien ;
  3. Il s'applique à une gamme de composants ou de pages différents.

Je pense que ce cas répond aux trois critères, alors créons un nouveau fichier CSS styles/utilities.css et ajoutons :

.lockup {
  largeur maximale : 90 ch ;
  marge : 0 automatique ;
}

Ensuite, ajoutons import '../styles/utilities.css' à nos pages/_app.jsx. Enfin, changeons la balise

dans nos pages/index.jsx en
.

Maintenant, notre page se rassemble encore plus. Étant donné que nous avons utilisé la propriété max-widthnous n'avons besoin d'aucune requête multimédia pour rendre notre mise en page mobile réactive. Et, parce que nous avons utilisé l'unité de mesure ch – qui équivaut à environ la largeur d'un caractère – notre dimensionnement est dynamique en fonction de la taille de la police du navigateur de l'utilisateur.

 Le même site Web qu'avant, mais maintenant le texte se coince au milieu et ne devient pas trop large

Le même site Web qu'avant, mais maintenant le texte se coince au milieu et ne devient pas trop large. ( Grand aperçu)

Au fur et à mesure que notre site Web se développe, nous pouvons continuer à ajouter plus de classes utilitaires. J'adopte ici une approche assez utilitaire : si je travaille et que j'ai besoin d'une autre classe pour une couleur ou quelque chose, je l'ajoute. Je n'ajoute pas toutes les classes possibles sous le soleil – cela augmenterait la taille du fichier CSS et rendrait mon code confus. Parfois, dans des projets plus importants, j'aime diviser les choses dans un répertoire styles/utilities/ avec quelques fichiers différents ; cela dépend des besoins du projet.

Nous pouvons considérer les classes utilitaires comme notre boîte à outils de commandes de style communes et répétées qui sont partagées globalement. Ils nous aident à éviter de réécrire constamment le même CSS entre différents composants.

Styles de composants

Nous avons terminé notre page d'accueil pour le moment, mais nous devons encore construire une partie de notre site Web : la boutique en ligne. Notre objectif ici sera d'afficher une grille de cartes de tous les thés que nous voulons vendrenous devrons donc ajouter quelques composants à notre site.

Commençons par ajouter une nouvelle page à l'adresse pages/shop.jsx:

exporter la fonction par défaut Shop() {
  retour 

Achetez nos thés

}

Ensuite, nous aurons besoin de thés à afficher. Nous inclurons un nom, une description et une image (dans le répertoire public/) pour chaque thé :

const teas = [
  { name: "Oolong", description: "A partially fermented tea.", image: "/oolong.jpg" },
  // ...
]

Remarque : Ce n'est pas un article sur la récupération de donnéesnous avons donc choisi la voie facile et défini un tableau au début du fichier.

Ensuite, nous devrons définir un composant pour afficher nos thés. Commençons par créer un répertoire components/ (Next.js ne le fait pas par défaut). Ajoutons ensuite un répertoire components/TeaList. Pour tout composant nécessitant plusieurs fichiers, je place généralement tous les fichiers associés dans un dossier. Cela évite que notre dossier components/ ne soit plus navigable.

Maintenant, ajoutons notre fichier components/TeaList/TeaList.jsx :

 import TeaListItem depuis './TeaList '

const TeaList = (accessoires) => {
  const { thés } = accessoires

  retour 
    {thés.map(thé => )}
} export default TeaList

Le but de ce composant est d'itérer sur nos thés et d'afficher un élément de liste pour chacun, définissons donc maintenant notre composant components/TeaList/TeaListItem.jsx :

 importer l'image de 'suivant/image'

const TeaListItem = (accessoires) => {
  const { thé } = accessoires

  retour 
  • {tea.name}

    {tea.description}

  • } exportez le TeaListItem par défaut

    Notez que nous utilisons le composant d'image intégré de Next.js . J'ai défini l'attribut alt sur une chaîne vide car les images sont purement décoratives dans ce cas ; nous voulons éviter d'enliser les utilisateurs de lecteurs d'écran avec de longues descriptions d'images ici.

    Enfin, créons un fichier components/TeaList/index.jsafin que nos composants soient faciles à importer en externe :[19659028] importer TeaList de './TeaList'
    importer TeaListItem de './TeaListItem'

    export { TeaListItem }

    exportez la TeaList par défaut

    Et puis, rassemblons le tout en ajoutant import TeaList de ../components/TeaList et un élément à notre page Boutique. Maintenant, nos thés apparaîtront dans une liste, mais ce ne sera pas si joli.

    Colocation de style avec des composants via des modules CSS

    Commençons par styliser nos cartes (le composant TeaListLitem ). Maintenant, pour la première fois dans notre projet, nous allons vouloir ajouter un style spécifique à un seul composant. Créons un nouveau fichier components/TeaList/TeaListItem.module.css.

    Vous vous posez peut-être des questions sur le module dans l'extension de fichier. Il s'agit d'un Module CSS . Next.js prend en charge les modules CSS et inclut une bonne documentation sur eux. Lorsque nous écrivons un nom de classe à partir d'un module CSS tel que .TeaListItemil sera automatiquement transformé en quelque chose comme . TeaListItem_TeaListItem__TFOk_ avec un tas de caractères supplémentaires ajoutés. Par conséquent, nous pouvons utiliser n'importe quel nom de classe que nous voulons sans craindre qu'il n'entre en conflit avec d'autres noms de classe ailleurs sur notre site.

    Un autre avantage des modules CSS est la performance. Next.js inclut une fonctionnalité d'importation dynamique. next/dynamic nous permet de charger les composants paresseux afin que leur code ne soit chargé qu'en cas de besoin, plutôt que d'ajouter à la taille totale du bundle. Si nous importons les styles locaux nécessaires dans des composants individuels, les utilisateurs peuvent également charger paresseux le CSS pour les composants importés dynamiquement . Pour les grands projets, nous pouvons choisir de charger paresseux des morceaux importants de notre code et de ne charger en amont que le JS/CSS le plus nécessaire. En conséquence, je finis généralement par créer un nouveau fichier de module CSS pour chaque nouveau composant nécessitant un style local.

    Commençons par ajouter quelques styles initiaux à notre fichier :

    .TeaListItem {
      affichage : flexible ;
      flex-direction : colonne ;
      écart : var(--space-sm);
      background-color: var(--color, var(--off-white));
      couleur : var(--foncé);
      rayon de bordure : 3px ;
      box-shadow : 1px 1px 1px rgba (0, 0, 0, 0,1);
    }
    

    Ensuite, nous pouvons importer le style de ./TeaListItem.module.css dans notre composant TeaListitem. La variable de style entre comme un objet JavaScript, nous pouvons donc accéder à ce style.TeaListItem de type classe.

    Remarque : Notre nom de classe n'a pas besoin de être en majuscule. J'ai trouvé qu'une convention de noms de classes en majuscules à l'intérieur des modules (et en minuscules à l'extérieur) différencie visuellement les noms de classes locales et globales.

    Alors, prenons notre nouvelle classe locale et affectons-la au [19659096]dans notre composant TeaListItem :

  • Vous vous posez peut-être des questions sur la ligne de couleur d'arrière-plan (c'est-à-dire var(--color, var(--off-white));). Ce que cet extrait signifie, c'est que par défaut l'arrière-plan sera notre valeur --blanc cassé. Mais, si nous définissons une propriété personnalisée --color sur une carte, elle remplacera et choisira cette valeur à la place.

    Au début, nous voudrons que toutes nos cartes soient -- blanc cassémais il se peut que nous souhaitions modifier la valeur des cartes individuelles plus tard. Cela fonctionne de manière très similaire aux accessoires dans React. Nous pouvons définir une valeur par défaut mais créer un emplacement où nous pouvons choisir d'autres valeurs dans des circonstances spécifiques. Donc, je nous encourage à penser aux propriétés CSS personnalisées comme la version CSS de props.

    Le style n'aura toujours pas fière allure car nous voulons nous assurer que les images restent dans leurs conteneurs. Le composant Image de Next.js avec la prop layout="fill" obtient position:absolu; à partir du framework, nous pouvons donc limiter la taille en mettant dans un conteneur avec la position:relative; .

    Ajoutons une nouvelle classe à notre TeaListItem.module.css:

    .ImageContainer {
      position : relative ;
      largeur : 100 % ;
      hauteur : 10em ;
      débordement caché;
    }

    Et puis ajoutons className={styles.ImageContainer} sur le

    qui contient notre . J'utilise des noms relativement "simples" tels que ImageContainer parce que nous sommes à l'intérieur d'un module CSS, nous n'avons donc pas à nous soucier d'un conflit avec le style extérieur.

    Enfin, nous voulons ajoutons un peu de remplissage sur les côtés du texte, ajoutons donc une dernière classe et comptons sur les variables d'espacement que nous avons configurées en tant que jetons de conception :

    .Title {
      remplissage-gauche: var(--space-sm);
      remplissage-droit : var(--space-sm);
    }

    Nous pouvons ajouter cette classe au

    qui contient notre nom et notre description. Maintenant, nos cartes n'ont pas l'air si mal :

     Les cartes s'affichent pour 3 thés différents qui ont été ajoutés en tant que données de graines. Ils ont des images, des noms et des descriptions. Ils s'affichent actuellement dans une liste verticale sans espace entre eux.

    Les cartes s'affichent pour 3 thés différents qui ont été ajoutés en tant que données de graines. Ils ont des images, des noms et des descriptions. Ils apparaissent actuellement dans une liste verticale sans espace entre eux. ( Grand aperçu)

    Combinaison de style global et local

    Ensuite, nous voulons que nos cartes s'affichent sous forme de grille. Dans ce cas, nous sommes juste à la frontière entre les styles locaux et globaux. Nous pourrions certainement coder notre mise en page directement sur le composant TeaList. Mais, je pourrais aussi imaginer qu'avoir une classe utilitaire qui transforme une liste en une disposition de grille pourrait être utile à plusieurs autres endroits.

    Prenons l'approche globale ici et ajoutons une nouvelle classe utilitaire dans notre styles/utilities.css:

    .grid {
      style de liste : aucun ;
      affichage : grille ;
      grid-template-columns: repeat(auto-fill, minmax(var(--min-item-width, 30ch), 1fr));
      écart : var(--space-md);
    }

    Maintenant, nous pouvons ajouter la classe .grid sur n'importe quelle liste, et nous obtiendrons une disposition de grille automatiquement réactive. Nous pouvons également modifier la propriété personnalisée --min-item-width (par défaut 30ch) pour modifier la largeur minimale de chaque élément.

    Remarque : N'oubliez pas de penser aux propriétés personnalisées comme les accessoires ! Si cette syntaxe ne vous semble pas familière, vous pouvez consulter "Grille CSS intrinsèquement réactive avec minmax() And min()" par Chris Coyier.

    As nous avons écrit ce style globalement, il ne nécessite aucune fantaisie pour ajouter className="grid" sur notre composant TeaList. Mais, disons que nous voulons coupler ce style global avec un magasin local supplémentaire. Par exemple, nous voulons apporter un peu plus de « l'esthétique du thé » et faire en sorte que toutes les autres cartes aient un fond vert. Tout ce que nous avons à faire est de créer un nouveau fichier components/TeaList/TeaList.module.css :

    .TeaList > :nth-child(even) {
      --color: var(--green);
    }

    Vous vous souvenez comment nous avons créé une propriété --color custom sur notre composant TeaListItem ? Eh bien, maintenant nous pouvons le définir dans des circonstances spécifiques. Notez que nous pouvons toujours utiliser des sélecteurs enfants dans les modules CSS, et peu importe que nous sélectionnions un élément stylisé dans un module différent. Ainsi, nous pouvons également utiliser nos styles de composants locaux pour affecter les composants enfants. Il s'agit d'une fonctionnalité plutôt que d'un bug, car il nous permet de profiter de la cascade CSS ! Si nous essayions de reproduire cet effet d'une autre manière, nous nous retrouverions probablement avec une sorte de soupe JavaScript plutôt que trois lignes de CSS.

    Alors, comment pouvons-nous conserver la classe globale .grid sur notre composant TeaList tout en ajoutant également la classe locale .TeaList ? C'est là que la syntaxe peut devenir un peu géniale car nous devons accéder à notre classe .TeaList depuis le module CSS en faisant quelque chose comme style.TeaList.

    Une option serait être d'utiliser l'interpolation de chaîne pour obtenir quelque chose comme :

      Dans ce petit cas, cela pourrait suffire. Si nous mélangeons et associons plus de classes, je trouve que cette syntaxe fait un peu exploser mon cerveau, alors je choisirai parfois d'utiliser la bibliothèque de noms de classes. Dans ce cas, nous nous retrouvons avec une liste d'apparence plus raisonnable :

        Maintenant, nous avons terminé notre page Boutique, et nous avons fait en sorte que notre composant TeaList tire parti des global et local styles.

        Nos cartes de thé s'affichent désormais dans une grille. Les entiers pairs sont colorés en vert, tandis que les entrées impaires sont en blanc.

        Nos cartes de thé s'affichent désormais dans une grille. Les entiers pairs sont colorés en vert, tandis que les entrées impaires sont en blanc. ( Grand aperçu)

        Un acte d'équilibrage

        Nous avons maintenant construit notre salon de thé en utilisant uniquement du CSS simple pour gérer le style. Vous avez peut-être remarqué que nous n'avons pas eu à passer des heures à gérer des configurations Webpack personnalisées, à installer des bibliothèques externes, etc. C'est à cause des modèles que nous avons utilisés avec Next.js prêts à l'emploi. En outre, ils encouragent les meilleures pratiques CSS et s'intègrent naturellement dans l'architecture du framework Next.js.

        Notre organisation CSS se composait de quatre éléments clés :

        1. Jetons de conception,
        2. Styles globaux, [19659015]Classes utilitaires,
        3. Styles de composants.

        Au fur et à mesure que nous poursuivons la construction de notre site, notre liste de jetons de conception et de classes utilitaires s'allongera. Tout style qui n'a pas de sens à ajouter en tant que classe utilitaire, nous pouvons l'ajouter aux styles de composants à l'aide de modules CSS. En conséquence, nous pouvons trouver un équilibre continu entre les préoccupations de style locales et mondiales. Nous pouvons également générer un code CSS performant et intuitif qui se développe naturellement avec notre site Next.js.

        Smashing Editorial" width="35" height="46" loading="lazy" decoding="async (vf, yk, il)




        Source link

        juillet 27, 2021