Cours Next.js · Module 3: Styles et optimisation visuelle

Gestion des assets statiques dans le répertoire public de Next.js 16

13 min de lecture
Dans cette leçon12

À Metropolis Quantum, chaque quartier possède sa propre base de ressources centrale - un endroit où sont stockés les éléments accessibles à tous les habitants : cartes, brochures d'information, éléments d'identité visuelle et holo-écrans interactifs avec les plans de la ville. Ces ressources sont immuables et publiquement disponibles, garantissant la cohérence et la continuité de l'expérience dans toute la ville.

Dans les applications Next.js, le répertoire public joue le rôle de cette base centrale. C'est là que nous plaçons tous les assets statiques qui doivent être disponibles sans être traités par le système de build de Next.js. Dans ce chapitre, nous verrons comment gérer efficacement ces assets pour que notre application soit à la fois performante et bien organisée.

Qu'est-ce que le répertoire public dans Next.js 16 ?

Le répertoire public est un dossier spécial d'une application Next.js qui remplit les fonctions suivantes :

  1. Il sert les fichiers statiques directement - les fichiers placés dans public sont servis depuis la racine du domaine
  2. Il ne traite pas le contenu - les fichiers ne passent pas par le système de build (contrairement aux ressources importées directement dans les composants)
  3. Il fournit des URL immuables - les noms de fichiers ne changent pas au cours du build (aucun hash n'est ajouté aux noms)

Ce répertoire est idéal pour stocker des ressources comme :

  • Les images de logo et les icônes
  • Les fichiers favicon et manifest.json
  • Les fichiers robots.txt et sitemap.xml
  • Les polices
  • Les documents (PDF, DOCX)
  • Les ressources multimédias (vidéos, sons)
  • Les données statiques (JSON, CSV)

Structure du répertoire public

Un répertoire public bien organisé facilite la gestion des ressources. Voici un exemple de structure :

1public/
2├── favicon.ico                  # Icône de la page
3├── favicon-16x16.png            # Tailles alternatives du favicon
4├── favicon-32x32.png
5├── apple-touch-icon.png         # Icon for Apple devices
6├── manifest.json                # Manifest PWA
7├── robots.txt                   # Instructions for bots
8├── sitemap.xml                  # Plan du site pour les moteurs de recherche
9├── fonts/                       # Répertoire de polices
10│   ├── QuantumSans-Regular.woff2
11│   └── QuantumSans-Bold.woff2
12├── images/                      # Répertoire d’images
13│   ├── logo.png
14│   ├── hero-banner.jpg
15│   ├── icons/                   # Podkatalog z ikonami
16│   │   ├── arrow-right.svg
17│   │   └── search.svg
18│   └── backgrounds/             # Subdirectory with backgrounds
19│       ├── quantum-pattern.svg
20│       └── districts/           # Sous-dossiers supplémentaires pour l'organisation
21│           ├── downtown.jpg
22│           └── quantum-district.jpg
23├── videos/                      # Répertoire de vidéos
24│   └── intro.mp4
25└── data/                        # Répertoire de données statiques
26    ├── districts.json
27    └── services.json

Accéder aux assets du répertoire public

Accéder aux assets du répertoire public est simple et intuitif. Souviens-toi que :

  1. Les chemins sont relatifs à la racine du domaine
  2. Tu dois préfixer les noms de fichiers par une barre oblique (/)

Dans les composants React

1// Example of using an image from the public directory
2export default function Logo() {
3  return (
4    <img
5      src="/images/logo.png"
6      alt="Metropolis Quantum Logo"
7      width={200}
8      height={50}
9    />
10  );
11}

Dans le CSS

1/* Dans le fichier CSS */
2.hero-section {
3  background-image: url('/images/backgrounds/quantum-pattern.svg');
4  background-repeat: repeat;
5}

Dans les métadonnées

1// app/layout.tsx
2import { Metadata } from 'next';
3
4export const metadata: Metadata = {
5  title: 'Metropolis Quantum',
6  description: 'Official portal of the city of the future',
7  icons: {
8    icon: [
9      { url: '/favicon-16x16.png', sizes: '16x16', type: 'image/png' },
10      { url: '/favicon-32x32.png', sizes: '32x32', type: 'image/png' },
11    ],
12    apple: [
13      { url: '/apple-touch-icon.png' },
14    ],
15  },
16};
17
18export default function RootLayout({
19  children,
20}: {
21  children: React.ReactNode;
22}) {
23  return (
24    <html lang="pl">
25      <body>{children}</body>
26    </html>
27  );
28}

Le composant Image et le répertoire public

Bien que Next.js fournisse un composant Image optimisé via le paquet next/image, il nous faut parfois utiliser des images directement depuis le répertoire public. Le composant Image peut être utilisé avec des images du répertoire public tout en conservant les avantages de l'optimisation :

1import Image from 'next/image';
2
3export default function HeroBanner() {
4  return (
5    <div className="hero-banner">
6      <Image
7        src="/images/hero-banner.jpg"
8        alt="Bienvenue à Metropolis Quantum"
9        width={1200}
10        height={600}
11        preload
12      />
13    </div>
14  );
15}

Quand utiliser les images de public plutôt que les imports ?

  1. Quand l'URL de l'image doit être connue au moment du build :

    • Les images dans les métadonnées (Open Graph, Twitter)
    • Les arrière-plans CSS
  2. Quand l'image est très volumineuse et rarement utilisée :

    • De grandes images qui n'apparaissent que dans des contextes spécifiques
  3. Quand tu veux un lien direct vers le fichier :

    • Les ressources téléchargeables
    • Les ressources partagées avec d'autres applications

Gérer différents types de fichiers

SVG

Les fichiers SVG peuvent être placés dans le répertoire public et utilisés comme des images classiques :

1export default function Icon() {
2  return (
3    <img src="/images/icons/search.svg" alt="Szukaj" width={24} height={24} />
4  );
5}

Tu peux aussi importer les SVG directement dans le code (sans passer par le répertoire public), ce qui permet de manipuler leurs propriétés :

1import SearchIcon from '@/public/images/icons/search.svg';
2
3export default function SearchButton() {
4  return (
5    <button className="search-button">
6      <SearchIcon className="icon" width={24} height={24} />
7      Szukaj
8    </button>
9  );
10}

Remarque : pour que l'exemple ci-dessus fonctionne, une configuration supplémentaire est nécessaire, par ex. en utilisant @svgr/webpack.

Polices

Comme nous l'avons vu au chapitre précédent, les fichiers de police peuvent être stockés dans le répertoire public et utilisés avec next/font/local :

1import localFont from 'next/font/local';
2
3export const quantumSans = localFont({
4  src: [
5    {
6      path: '../public/fonts/QuantumSans-Regular.woff2',
7      weight: '400',
8      style: 'normal',
9    },
10    {
11      path: '../public/fonts/QuantumSans-Bold.woff2',
12      weight: '700',
13      style: 'normal',
14    },
15  ],
16});

Vidéos et audio

Les fichiers multimédias peuvent aussi être stockés dans le répertoire public :

1export default function IntroVideo() {
2  return (
3    <video
4      controls
5      width="100%"
6      poster="/images/video-poster.jpg"
7    >
8      <source src="/videos/intro.mp4" type="video/mp4" />
9      Your browser does not support the video player.
10    </video>
11  );
12}

Fichiers JSON

Le JSON stocké dans public peut être chargé à l'aide de fetch :

1// app/districts/page.tsx
2export default async function DistrictsPage() {
3  // Fetch des données depuis le fichier JSON
4  const response = await fetch('http://localhost:3000/data/districts.json');
5  const districts = await response.json();
6
7  return (
8    <div>
9      <h1>Dzielnice Metropolis Quantum</h1>
10      <ul>
11        {districts.map((district) => (
12          <li key={district.id}>{district.name}</li>
13        ))}
14      </ul>
15    </div>
16  );
17}

Astuce : dans les applications réelles, une meilleure solution consiste souvent à importer les données JSON directement dans le code (par ex. import districts from '@/data/districts.json') ou à les stocker dans une base de données.

Organisation et bonnes pratiques

1. Structure des répertoires

Maintiens une structure de répertoires logique qui reflète l'usage des assets :

1public/
2├── branding/       # Brand-related elements
3├── icons/          # Ikony systemowe
4├── images/         # Photos and graphics
5├── videos/         # Fichiers vidéo
6├── fonts/          # Czcionki
7└── data/           # Données statiques (JSON, CSV)

2. Conventions de nommage

Utilise des conventions de nommage cohérentes :

  • Utilise le kebab-case (hero-banner.jpg) au lieu des espaces (hero banner.jpg)
  • Ajoute des suffixes de taille pour les différentes variantes (logo-sm.png, logo-lg.png)
  • Utilise des préfixes pour regrouper (icon-home.svg, icon-user.svg)

3. Optimisation des assets

Avant de placer des assets dans le répertoire public, optimise-les :

  • Images : compresse-les avec des outils comme TinyPNG, ImageOptim
  • SVG : minimise-les avec SVGO
  • Vidéo : compresse-la dans des formats et tailles appropriés
  • Polices : utilise le WOFF2 comme format préféré

4. Mise en cache

Next.js 16 ajoute aux fichiers du dossier public l'en-tête Cache-Control: public, max-age=0, car ces fichiers peuvent changer sans que leur nom change. Seuls les fichiers de /_next/static, dont le nom contient une empreinte du contenu, bénéficient d'un cache long et immuable. Si tu sais que tes ressources ne changent pas, définis tes propres en-têtes dans le fichier next.config.js :

1// next.config.js
2module.exports = {
3  async headers() {
4    return [
5      {
6        source: '/fonts/:path*',
7        headers: [
8          {
9            key: 'Cache-Control',
10            value: 'public, max-age=31536000, immutable',
11          },
12        ],
13      },
14      {
15        source: '/images/:path*',
16        headers: [
17          {
18            key: 'Cache-Control',
19            value: 'public, max-age=86400, stale-while-revalidate=31536000',
20          },
21        ],
22      },
23    ];
24  },
25};

Problèmes et solutions

Problème 1 : répertoire public trop volumineux

Si le répertoire public devient trop volumineux, cela peut affecter le temps de déploiement et la gestion des ressources.

Solution :

  • Déplace les gros assets rarement utilisés vers des services CDN (Content Delivery Network)
  • Applique des stratégies de lazy loading aux assets
  • Optimise toutes les ressources avant de les ajouter au répertoire

Problème 2 : problèmes d'URL d'image

Solution :

  • Utilise toujours une barre oblique au début des chemins (/images/logo.png, et non images/logo.png)
  • Évite les espaces et les caractères spéciaux dans les noms de fichiers
  • Vérifie la casse des noms de fichiers (les systèmes Linux sont sensibles à la casse)

Problème 3 : incohérence dans la gestion des assets

Solution :

  • Crée et applique des conventions de nommage pour toute l'équipe
  • Documente la structure du répertoire public
  • Envisage de créer des composants abstraits pour les assets fréquemment utilisés

Exemples pratiques

Exemple 1 : système d'icônes depuis le répertoire public

1// components/Icon.tsx
2type IconName = 'home' | 'search' | 'user' | 'settings' | 'menu';
3
4interface IconProps {
5  name: IconName;
6  size?: 'sm' | 'md' | 'lg';
7  color?: string;
8  className?: string;
9}
10
11const sizeMap = {
12  sm: { width: 16, height: 16 },
13  md: { width: 24, height: 24 },
14  lg: { width: 32, height: 32 },
15};
16
17export function Icon({
18  name,
19  size = 'md',
20  color = 'currentColor',
21  className = '',
22}: IconProps) {
23  const dimensions = sizeMap[size];
24
25  return (
26    <img
27      src={`/icons/${name}.svg`}
28      alt={`${name} icon`}
29      className={`icon icon-${name} ${className}`}
30      {...dimensions}
31      style={{ filter: `${color !== 'currentColor' ? `color(${color})` : ''}` }}
32    />
33  );
34}

Utilisation :

1import { Icon } from '@/components/Icon';
2
3export default function Navbar() {
4  return (
5    <nav className="navbar">
6      <Icon name="home" size="sm" />
7      <Icon name="search" color="#3a86ff" />
8      <Icon name="user" className="user-icon" />
9    </nav>
10  );
11}

Exemple 2 : système de chargement dynamique de photos depuis le répertoire public

1// components/DistrictImage.tsx
2import Image from 'next/image';
3
4interface DistrictImageProps {
5  districtId: string;
6  alt: string;
7  className?: string;
8}
9
10export function DistrictImage({ districtId, alt, className = '' }: DistrictImageProps) {
11  return (
12    <div className={`district-image-container ${className}`}>
13      <Image
14        src={`/images/backgrounds/districts/${districtId}.jpg`}
15        alt={alt}
16        fill
17        className="district-image"
18        sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
19      />
20    </div>
21  );
22}

Utilisation :

1import { DistrictImage } from '@/components/DistrictImage';
2
3export default function DistrictCard({ district }) {
4  return (
5    <div className="district-card">
6      <DistrictImage
7        districtId={district.id}
8        alt={`Dzielnica ${district.name}`}
9      />
10      <h3>{district.name}</h3>
11      <p>{district.description}</p>
12    </div>
13  );
14}

Exemple 3 : configuration SEO avec des assets de public

1// app/districts/[id]/page.tsx
2import { Metadata } from 'next';
3
4interface Props {
5  params: Promise<{ id: string }>;
6}
7
8export async function generateMetadata({ params }: Props): Promise<Metadata> {
9  const districtId = (await params).id;
10  const district = await fetchDistrictData(districtId);
11
12  return {
13    title: `${district.name} | Metropolis Quantum`,
14    description: district.description,
15    openGraph: {
16      images: [{
17        url: `/images/backgrounds/districts/${districtId}.jpg`,
18        width: 1200,
19        height: 630,
20        alt: district.name,
21      }],
22      type: 'website',
23    },
24    twitter: {
25      card: 'summary_large_image',
26      images: [`/images/backgrounds/districts/${districtId}.jpg`],
27    },
28  };
29}
30
31export default async function DistrictPage({ params }: Props) {
32  const district = await fetchDistrictData((await params).id);
33
34  return (
35    <div className="district-page">
36      <h1>{district.name}</h1>
37      {/* Rest of page content */}
38    </div>
39  );
40}
41
42// Function fetching district data
43async function fetchDistrictData(id: string) {
44  // Dans une vraie application : récupération des données depuis une API ou une base
45  const response = await fetch(`http://localhost:3000/data/districts/${id}.json`);
46  return response.json();
47}

Intégration avec un CDN

Pour les applications plus grandes, il vaut la peine d'envisager de déplacer les assets du répertoire public vers un CDN externe (Content Delivery Network), ce qui garantira un chargement plus rapide dans le monde entier.

Configuration du base path dans Next.js

1// next.config.js
2module.exports = {
3  assetPrefix: process.env.NODE_ENV === 'production' ? 'https://cdn.twojadomena.com' : '',
4};

Composant pour gérer les chemins des assets

1// utils/assetPath.ts
2export function getAssetPath(path: string): string {
3  const cdnUrl = process.env.NEXT_PUBLIC_CDN_URL || '';
4  return `${cdnUrl}${path}`;
5}

Utilisation :

1import { getAssetPath } from '@/utils/assetPath';
2
3export default function Logo() {
4  return (
5    <img
6      src={getAssetPath('/images/logo.png')}
7      alt="Metropolis Quantum Logo"
8      width={200}
9      height={50}
10    />
11  );
12}

Techniques avancées

1. Imports dynamiques d'assets

Tu peux importer dynamiquement des assets depuis le répertoire public pendant le build :

1// utils/getDistrictData.ts
2import fs from 'fs';
3import path from 'path';
4
5export function getDistrictData() {
6  const filePath = path.join(process.cwd(), 'public', 'data', 'districts.json');
7  const fileContents = fs.readFileSync(filePath, 'utf8');
8  return JSON.parse(fileContents);
9}

2. Générer des assets pendant le build

Tu peux générer des assets statiques pendant le build de l'application :

1// scripts/generate-sitemap.js
2const fs = require('fs');
3const path = require('path');
4
5// Function generating sitemap.xml
6function generateSitemap(routes) {
7  const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
8  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
9    ${routes.map(route => `
10      <url>
11        <loc>https://metropolisquantum.pl${route}</loc>
12        <lastmod>${new Date().toISOString()}</lastmod>
13      </url>
14    `).join('')}
15  </urlset>
16  `;
17
18  fs.writeFileSync(
19    path.join(process.cwd(), 'public', 'sitemap.xml'),
20    sitemap
21  );
22}
23
24// Liste des routes de l’application
25const routes = [
26  '/',
27  '/districts',
28  '/about',
29  '/contact',
30  // inne trasy
31];
32
33generateSitemap(routes);

Tu peux ensuite ajouter ce script au processus de build dans package.json :

1{
2  "scripts": {
3    "build": "node scripts/generate-sitemap.js && next build"
4  }
5}

Sécurité et remarques sur les assets publics

1. Évite de stocker des informations confidentielles

Le répertoire public est accessible publiquement, tu ne dois donc jamais y stocker :

  • De clés API
  • De données confidentielles
  • De fichiers de configuration contenant des informations sensibles

2. Protection contre le hotlinking

Tu peux protéger tes assets contre le hotlinking (leur utilisation sur d'autres sites) à l'aide d'en-têtes HTTP :

1// next.config.js
2module.exports = {
3  async headers() {
4    return [
5      {
6        source: '/images/:path*',
7        headers: [
8          {
9            key: 'Referrer-Policy',
10            value: 'same-origin',
11          },
12        ],
13      },
14    ];
15  },
16};

3. Content Security Policy (CSP)

Il vaut la peine de configurer une CSP pour protéger ton application :

1// next.config.js
2module.exports = {
3  async headers() {
4    return [
5      {
6        source: '/:path*',
7        headers: [
8          {
9            key: 'Content-Security-Policy',
10            value: "default-src 'self'; img-src 'self' https://cdn.twojadomena.com; font-src 'self' https://fonts.gstatic.com",
11          },
12        ],
13      },
14    ];
15  },
16};

Résumé

Le répertoire public dans Next.js 16 est un outil puissant pour gérer les assets statiques de l'application. Une bonne organisation, des conventions de nommage et l'optimisation des assets peuvent considérablement améliorer la performance et la maintenabilité du projet.

Points clés :

  1. Structure et organisation - maintiens une structure de répertoires logique et des conventions de nommage cohérentes
  2. Optimisation - compresse et optimise les assets avant de les placer dans le répertoire
  3. Abstraction - crée des composants abstraits pour gérer l'accès aux assets
  4. Sécurité - souviens-toi que le contenu du répertoire public est accessible publiquement
  5. Mise à l'échelle - pour les applications plus grandes, envisage d'utiliser un CDN

Tout comme dans la base de ressources centrale de Metropolis Quantum, un répertoire public bien géré garantit un accès efficace à tous les éléments nécessaires, en maintenant l'ordre et la performance de l'ensemble du système.

Dans le prochain chapitre, nous étudierons les icônes, le favicon et les métadonnées visuelles, qui sont des éléments clés de l'identité visuelle de ton application Next.js 16.

Code de cette leçon : app/page.tsx
1// CSS Performance Optimization - Cyberpunk Edition
2'use client';
3
4export default function PerformanceDemo() {
5  return (
6    <div style={{
7      minHeight: '100vh',
8      background: 'linear-gradient(135deg, #0f0f23, #1a1a2e)',
9      color: '#ffffff',
10      padding: '3rem 2rem'
11    }}>
12      <h1 style={{
13        fontSize: '3rem',
14        textAlign: 'center',
15        background: 'linear-gradient(45deg, #64ffda, #7c4dff)',
16        WebkitBackgroundClip: 'text',
17        WebkitTextFillColor: 'transparent',
18        marginBottom: '3rem'
19      }}>
20        CSS Performance Optimization
21      </h1>
22
23      {/* Will-change for animated elements */}
24      <section style={{
25        maxWidth: '1200px',
26        margin: '0 auto 3rem',
27        background: 'rgba(255,255,255,0.05)',
28        padding: '2rem',
29        borderRadius: '1rem',
30        border: '1px solid rgba(100,255,218,0.2)'
31      }}>
32        <h2 style={{color: '#64ffda', marginBottom: '1.5rem'}}>1. will-change Property</h2>
33        <div style={{
34          width: '200px',
35          height: '200px',
36          background: 'linear-gradient(135deg, #64ffda, #7c4dff)',
37          borderRadius: '1rem',
38          willChange: 'transform',
39          animation: 'float 3s ease-in-out infinite'
40        }}></div>
41        <p style={{color: '#b0bec5', marginTop: '1rem'}}>
42          This element uses <code style={{background: 'rgba(0,0,0,0.5)', padding: '0.25rem 0.5rem', borderRadius: '0.25rem', color: '#64ffda'}}>will-change: transform</code> for better animation performance
43        </p>
44      </section>
45
46      {/* Transform au lieu de left/top */}
47      <section style={{
48        maxWidth: '1200px',
49        margin: '0 auto 3rem',
50        background: 'rgba(255,255,255,0.05)',
51        padding: '2rem',
52        borderRadius: '1rem',
53        border: '1px solid rgba(100,255,218,0.2)'
54      }}>
55        <h2 style={{color: '#64ffda', marginBottom: '1.5rem'}}>2. Transform vs Position</h2>
56        <div style={{display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '2rem'}}>
57          <div>
58            <h3 style={{color: '#7c4dff', marginBottom: '1rem'}}>Lent (position)</h3>
59            <div style={{
60              position: 'relative',
61              width: '150px',
62              height: '150px',
63              background: '#ff0080',
64              borderRadius: '0.75rem'
65            }}>
66              <div style={{fontSize: '0.875rem', padding: '1rem', color: '#fff'}}>
67                Uses position: relative; top/left
68              </div>
69            </div>
70          </div>
71
72          <div>
73            <h3 style={{color: '#7c4dff', marginBottom: '1rem'}}>Rapide (transform)</h3>
74            <div style={{
75              width: '150px',
76              height: '150px',
77              background: '#00ff7f',
78              borderRadius: '0.75rem',
79              transform: 'translateZ(0)'
80            }}>
81              <div style={{fontSize: '0.875rem', padding: '1rem', color: '#0f0f23', fontWeight: 600}}>
82                Uses transform: translate()
83              </div>
84            </div>
85          </div>
86        </div>
87      </section>
88
89      {/* Contain property */}
90      <section style={{
91        maxWidth: '1200px',
92        margin: '0 auto 3rem',
93        background: 'rgba(255,255,255,0.05)',
94        padding: '2rem',
95        borderRadius: '1rem',
96        border: '1px solid rgba(100,255,218,0.2)'
97      }}>
98        <h2 style={{color: '#64ffda', marginBottom: '1.5rem'}}>3. CSS Containment</h2>
99        <div style={{
100          contain: 'layout style paint',
101          background: 'rgba(124,77,255,0.2)',
102          padding: '1.5rem',
103          borderRadius: '0.75rem',
104          border: '1px solid rgba(124,77,255,0.5)'
105        }}>
106          <p style={{color: '#b0bec5', lineHeight: 1.6}}>
107            This container uses <code style={{background: 'rgba(0,0,0,0.5)', padding: '0.25rem 0.5rem', borderRadius: '0.25rem', color: '#7c4dff'}}>contain: layout style paint</code>
108            <br/><br/>
109            Isolates layout changes to this container only, improving rendering performance.
110          </p>
111        </div>
112      </section>
113
114      {/* Content-visibility */}
115      <section style={{
116        maxWidth: '1200px',
117        margin: '0 auto 3rem',
118        background: 'rgba(255,255,255,0.05)',
119        padding: '2rem',
120        borderRadius: '1rem',
121        border: '1px solid rgba(100,255,218,0.2)'
122      }}>
123        <h2 style={{color: '#64ffda', marginBottom: '1.5rem'}}>4. content-visibility</h2>
124        {[1, 2, 3, 4, 5].map(i => (
125          <div key={i} style={{
126            contentVisibility: 'auto',
127            containIntrinsicSize: '0 200px',
128            marginBottom: '1.5rem',
129            background: 'rgba(100,255,218,0.1)',
130            padding: '1.5rem',
131            borderRadius: '0.75rem',
132            border: '1px solid rgba(100,255,218,0.3)'
133          }}>
134            <h3 style={{color: '#64ffda', marginBottom: '0.75rem'}}>Section {i}</h3>
135            <p style={{color: '#b0bec5', lineHeight: 1.6}}>
136              This section uses content-visibility: auto. The browser renders it only when in the viewport,
137              which significantly improves performance with large content lists.
138            </p>
139          </div>
140        ))}
141      </section>
142
143      {/* Critical CSS */}
144      <section style={{
145        maxWidth: '1200px',
146        margin: '0 auto 3rem',
147        background: 'rgba(255,255,255,0.05)',
148        padding: '2rem',
149        borderRadius: '1rem',
150        border: '1px solid rgba(100,255,218,0.2)'
151      }}>
152        <h2 style={{color: '#64ffda', marginBottom: '1.5rem'}}>5. Critical CSS Best Practices</h2>
153        <ul style={{color: '#b0bec5', lineHeight: 2, paddingLeft: '1.5rem'}}>
154          <li>Styles critiques en ligne dans le &lt;head&gt;</li>
155          <li>Lazy-load non-critical CSS</li>
156          <li>Use CSS-in-JS for components</li>
157          <li>Minifie et compresse le CSS</li>
158          <li>Remove unused styles (PurgeCSS)</li>
159          <li>Use CDN for faster delivery</li>
160        </ul>
161      </section>
162
163      <style jsx global>{`
164        @keyframes float {
165          0%, 100% { transform: translateY(0); }
166          50% { transform: translateY(-20px); }
167        }
168      `}</style>
169    </div>
170  );
171}

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Les Custom Properties CSS (variables) se définissent avec :

  2. 2. SASS/SCSS est :

Voici 2 des 3 questions de cette leçon. Réponds aux autres dans le jeu.

Tâches pratiques dans le jeu

  • Éditeur de code

    Crée du CSS pensé pour l'accessibilité : styles de focus, prefers-reduced-motion, contraste élevé

  • Clique dans l'ordre

    Reconstitue la déclaration d'un template CSS Grid :

  • Tri vertical

    Classe dans le bon ordre les étapes de l'optimisation des images par le composant next/image :

Articles utiles