Apprendre JavaScript et le développement front-end en français

Une référence complète pour apprendre HTML, CSS, JavaScript et TypeScript, puis découvrir les principaux frameworks front-end : React, Vue.js et Angular.

Chaque notion est expliquée avec des exemples concrets, des fiches de référence, du code prêt à tester, un éditeur JavaScript et des exercices pour progresser par la pratique.

8
Sections HTML
11
Sections CSS
12
Chapitres JS & TS
3
Frameworks front-end

Une progression complète du HTML aux frameworks modernes

Ce site rassemble les connaissances essentielles pour comprendre le développement front-end moderne, depuis la structure d'une page HTML jusqu'à la création d'interfaces avec React, Vue.js ou Angular.

Si tu débutes, commence par HTML, puis découvre CSS avant de passer aux bases de JavaScript. Tu apprendras ensuite à manipuler le DOM, travailler avec les tableaux, comprendre l'asynchrone, utiliser fetch(), les Promises et async/await, puis écrire du JavaScript moderne.

Une fois les bases maîtrisées, la partie TypeScript permet d'ajouter un système de types à JavaScript. Les chapitres React, Vue.js et Angular montrent ensuite comment ces connaissances sont utilisées dans de véritables applications front-end.

L'objectif n'est pas seulement de mémoriser des propriétés ou des méthodes : chaque fiche cherche à expliquer à quoi sert la notion, comment elle fonctionne et quand l'utiliser.

Structure de base

Les éléments fondamentaux d'un document HTML : déclaration du document, élément racine, métadonnées, contenu visible, conteneurs génériques et attributs globaux.

<!doctype html> doit apparaître au début d'un document HTML.

Ce n'est pas un élément HTML.

Il s'agit d'une déclaration permettant notamment au navigateur d'utiliser son mode de rendu standard plutôt qu'un ancien mode de compatibilité appelé quirks mode.

Avec le HTML moderne, la déclaration est volontairement simple :

<!doctype html>.

Il n'est plus nécessaire d'indiquer un numéro de version HTML.
<!doctype html> <html lang="fr"> <head> ... </head> <body> ... </body> </html>
<html> est l'élément racine du document.

Il contient normalement :

<head> et <body>.

L'attribut lang indique la langue principale du document.

Exemple :

lang="fr".

Cette information aide notamment les technologies d'assistance, les outils de traduction et le traitement linguistique.

L'attribut dir peut contrôler le sens d'écriture :

ltr, rtl ou auto.
<html lang="fr" > <head> ... </head> <body> ... </body> </html> <!-- Exemple d'écriture droite → gauche --> <html lang="ar" dir="rtl" > ... </html>
<head> contient les métadonnées et ressources associées au document.

On y trouve notamment :

<meta>
<title>
<link>
<style>
<script> dans certains cas.

Le contenu du <head> ne constitue pas le contenu principal visible de la page, même si certaines informations comme le <title> peuvent apparaître dans l'interface du navigateur.
<head> <meta charset="UTF-8" > <meta name="viewport" content="width=device-width, initial-scale=1" > <title> Apprendre JavaScript </title> <link rel="stylesheet" href="style.css" > </head>
<body> représente le contenu du document HTML.

On y place notamment :

textes,
images,
formulaires,
tableaux,
navigation,
contenu principal,
médias.

Un document possède un seul élément <body>.

Les scripts JavaScript modernes peuvent souvent être chargés avec defer ou type="module" afin de ne pas bloquer inutilement l'analyse du HTML.
<body> <header> ... </header> <main> <h1> Apprendre JavaScript </h1> <p> Bienvenue dans le cours. </p> </main> <footer> ... </footer> <script src="app.js" defer ></script> </body>
<div> est un conteneur générique sans signification sémantique particulière.

Il est utile lorsque aucun élément HTML plus précis ne correspond au rôle du contenu.

Exemple :

wrapper de layout,
groupe destiné au CSS,
conteneur technique.

Les navigateurs affichent généralement un <div> comme une boîte de type block par défaut, mais ce comportement visuel peut être modifié avec CSS.

Si le contenu possède une vraie signification, préférez par exemple :

<section>, <article>, <nav>, <aside>.
<div class="card-grid" > <article class="card" > ... </article> <article class="card" > ... </article> </div>
<span> est un conteneur générique destiné au contenu de type phrasing content.

Il est utile lorsqu'on souhaite cibler une portion de texte avec CSS ou JavaScript sans lui donner une signification particulière.

Il est affiché comme élément inline par défaut dans les navigateurs, mais CSS peut modifier son comportement visuel.

Si une balise sémantique plus précise correspond au contenu, utilisez-la plutôt que <span>.
<p> Prix : <span class="price" > 29 € </span> / mois </p> <p> Statut : <span class="badge" > Actif </span> </p> /* CSS */ .price { font-weight: 700; }
Les commentaires HTML permettent d'ajouter des indications dans le code source.

Ils ne sont normalement pas affichés dans la page.

Ils sont utiles pour :

identifier une grande section,
documenter une structure,
laisser une note technique.

Attention : un commentaire HTML reste visible dans le code source envoyé au navigateur.

N'y placez donc jamais de mot de passe, clé privée ou information secrète.
<!-- Navigation principale --> <nav> ... </nav> <!-- TODO : ajouter le formulaire de recherche -->
HTML possède des attributs utilisables sur de nombreux éléments.

id : identifiant unique dans le document.

class : permet de regrouper plusieurs éléments.

data-* : stocke des données personnalisées destinées notamment à JavaScript.

hidden : indique qu'un élément n'est actuellement pas pertinent pour l'affichage.

lang : précise la langue du contenu.

title : fournit une information consultative, mais ne doit pas être utilisé comme seul moyen de transmettre une information essentielle.
<article id="article-css-grid" class="article featured" data-category="css" > ... </article> <p lang="en" > Hello world </p> <div hidden> Contenu temporairement masqué. </div>
Un document HTML classique commence avec le doctype, puis l'élément <html>.

Le <head> contient les métadonnées.

Le <body> contient la structure et le contenu de la page.

À l'intérieur du body, préférez les éléments sémantiques lorsqu'ils correspondent réellement au rôle du contenu.
<!doctype html> <html lang="fr"> <head> <meta charset="UTF-8" > <meta name="viewport" content="width=device-width, initial-scale=1" > <title> Ma page </title> <link rel="stylesheet" href="style.css" > </head> <body> <header> ... </header> <main> <h1> Titre principal </h1> <p> Contenu de la page. </p> </main> <footer> ... </footer> <script src="app.js" defer ></script> </body> </html>

Texte & Titres

Les éléments textuels HTML donnent une structure et une signification au contenu : titres, paragraphes, emphase, citations, code, abréviations, modifications et annotations.

HTML fournit six niveaux de titres :

<h1> à <h6>.

<h1> représente le niveau le plus élevé.

En pratique, une page devrait généralement avoir un <h1> principal décrivant son sujet.

Utilisez ensuite <h2>, <h3>... selon la hiérarchie réelle.

Évitez de sauter inutilement des niveaux.

Ne choisissez jamais un niveau de titre uniquement pour sa taille : utilisez CSS pour l'apparence.
<h1> Guide JavaScript </h1> <h2> Les fonctions </h2> <h3> Fonctions classiques </h3> <h3> Fonctions fléchées </h3> <h2> Les tableaux </h2>
<p> représente un paragraphe.

Les navigateurs lui appliquent généralement des marges par défaut, mais ces espacements sont du style CSS navigateur et peuvent être modifiés.

Utilisez un nouveau <p> lorsqu'une nouvelle unité de texte commence.

N'utilisez pas une succession de <br> simplement pour créer de l'espace vertical : utilisez CSS.
<p> JavaScript permet d'ajouter de l'interactivité à une page web. </p> <p> Il peut manipuler le DOM, effectuer des requêtes réseau et gérer des événements. </p>
<strong> indique qu'une partie du contenu possède une importance particulière.

Il peut notamment signaler :

importance,
gravité,
urgence,
avertissement important.

Les navigateurs affichent généralement ce contenu en gras, mais ce n'est pas sa raison d'être.

Si vous souhaitez uniquement obtenir du gras visuellement, utilisez plutôt CSS.
<p> <strong> Attention : </strong> cette opération est irréversible. </p> <p> Vous devez <strong> sauvegarder vos données </strong> avant de continuer. </p>
<em> représente une emphase particulière dans la phrase.

Cette emphase peut modifier le sens ou l'intention de la phrase.

Les navigateurs l'affichent généralement en italique, mais son objectif est sémantique.

Pour un simple effet italique, utilisez CSS.
<p> Je n'ai <em> jamais </em> dit cela. </p> <p> Vous devez <em> vraiment </em> vérifier cette valeur. </p>
Contrairement à une idée courante, <b> et <i> ne sont pas simplement « gras » et « italique ».

<b> attire l'attention sur du contenu sans lui attribuer une importance supplémentaire.

Exemple : mot-clé, nom de produit, terme à repérer.

<i> représente du texte dans une voix ou un registre différent.

Exemples : terme technique, expression étrangère, nom scientifique, pensée, idiome.

Pour une simple apparence gras ou italique, utilisez CSS.
<p> Le terme <b> réactivité </b> est central dans Vue. </p> <p> Le terme latin <i> carpe diem </i> signifie « cueille le jour ». </p>
<u> représente une annotation non textuelle du contenu.

Exemple classique : signaler une faute ou une annotation particulière.

Évitez de souligner du texte ordinaire, car le soulignement est fortement associé aux liens.

<s> représente un contenu qui n'est plus exact ou n'est plus pertinent.

Exemple : ancien prix.

Pour représenter une modification du document, utilisez plutôt <del>.
<p> Orthographe : <u> javascrit </u> </p> <p> Ancien prix : <s> 59 € </s> <strong> 29 € </strong> </p>
<mark> représente un passage particulièrement pertinent dans le contexte actuel.

Exemple typique :

mettre en évidence un terme recherché dans un résultat.

Le navigateur utilise souvent un fond jaune par défaut, mais son apparence peut être personnalisée avec CSS.
<p> Résultats pour <mark> JavaScript </mark> </p> /* CSS */ mark { background: #fde047; color: #111; }
<small> représente des commentaires secondaires ou du « petit texte ».

Exemples :

mentions légales,
copyright,
conditions,
avertissements secondaires.

Son sens ne signifie pas simplement « mets ce texte plus petit ».

Pour modifier uniquement la taille d'un texte, utilisez CSS.
<p> 29 € / mois <small> TVA incluse, sans engagement. </small> </p> <footer> <small> © 2026 ExampleSite </small> </footer>
<code> représente un fragment de code informatique.

Il peut être utilisé directement dans une phrase.

<pre> représente du texte préformaté.

Les espaces et retours à la ligne présents dans le code source sont conservés.

Pour un bloc de code, on combine souvent :

<pre><code>.

La coloration syntaxique est une fonctionnalité supplémentaire, généralement apportée par CSS ou JavaScript.
<!-- Code dans une phrase --> <p> Utilisez <code> console.log() </code> pour afficher une valeur. </p> <!-- Bloc de code --> <pre><code>const message = "Hello"; console.log(message);</code></pre>
<kbd> représente une entrée utilisateur.

Exemple : touche de clavier.

<samp> représente une sortie produite par un programme.

<var> représente une variable, notamment en programmation ou en mathématiques.
<p> Appuyez sur <kbd> Ctrl </kbd> + <kbd> S </kbd> pour enregistrer. </p> <p> Le terminal affiche : <samp> Server started </samp> </p> <p> Soit <var>x</var> la largeur. </p>
<blockquote> représente une citation relativement longue, généralement présentée comme un bloc.

<q> représente une citation courte intégrée dans une phrase.

L'attribut cite peut contenir l'URL de la source.

Cette URL n'est cependant pas automatiquement présentée à l'utilisateur.

Si la source doit être visible, affichez-la explicitement dans le contenu.
<figure> <blockquote cite="https://example.com/article" > <p> La simplicité est une propriété importante d'une bonne interface. </p> </blockquote> <figcaption> Source : <cite> Guide de conception Example </cite> </figcaption> </figure> <p> Alice a répondu : <q> Cette solution fonctionne. </q> </p>
<cite> représente le titre d'une œuvre créative.

Exemples :

livre,
article,
film,
chanson,
peinture,
site ou œuvre citée.

Il ne sert pas simplement à mettre un nom de personne en italique.

Pour afficher l'auteur, utilisez du texte normal ou une structure adaptée.
<p> J'ai relu <cite> Le Petit Prince </cite> d'Antoine de Saint-Exupéry. </p> <p> Source : <cite> Guide JavaScript moderne </cite> </p>
<abbr> représente une abréviation ou un acronyme.

L'attribut title peut contenir son développement.

Cependant, ne comptez pas uniquement sur le tooltip de title pour transmettre une information essentielle.

Il est souvent préférable d'écrire la forme complète lors de la première occurrence.

<dfn> indique le terme actuellement défini.
<p> Le <dfn> <abbr title="Document Object Model" > DOM </abbr> </dfn> (Document Object Model) représente la structure d'un document sous forme d'objets. </p>
<br> force un retour à la ligne.

Il est adapté lorsque le retour lui-même fait partie du contenu :

adresse,
poème,
paroles ou texte formaté.

Il ne doit pas servir à créer des marges.

<hr> représente une rupture thématique entre deux parties du contenu.

Son apparence de ligne horizontale est seulement son rendu habituel et peut être modifiée avec CSS.
<address> Example Company<br> 12 rue du Web<br> 75000 Paris </address> <p> Première partie du récit. </p> <hr> <p> Le sujet change ici. </p>
<sub> représente un texte en indice.

Exemple : formule chimique.

<sup> représente un texte en exposant.

Exemples :

puissances,
ordinaux,
références typographiques.

Ces éléments doivent être utilisés lorsque la position possède une signification, et non simplement pour déplacer visuellement un texte.
<!-- Chimie --> <p> H<sub>2</sub>O </p> <!-- Mathématiques --> <p> x<sup>2</sup> + y<sup>2</sup> = z<sup>2</sup> </p> <!-- Ordinal --> <p> 1<sup>er</sup> septembre </p>
<ins> représente du contenu ajouté au document.

<del> représente du contenu supprimé.

Ils sont utiles lorsqu'on veut indiquer explicitement une modification.

Les attributs datetime et cite peuvent fournir des informations supplémentaires sur cette modification.
<p> La version nécessite <del> Node 18 </del> <ins> Node 22 </ins>. </p> <del datetime="2026-09-10" > Ancienne instruction </del>
<bdi> signifie Bidirectional Isolation.

Il isole une portion de texte dont la direction n'est pas connue à l'avance.

C'est utile par exemple pour des noms d'utilisateurs provenant de langues différentes.

<bdo> permet au contraire de forcer explicitement une direction avec dir="ltr" ou dir="rtl".
<p> Utilisateur : <bdi> علي </bdi> — 12 messages </p> <bdo dir="rtl" > ABC 123 </bdo>
<wbr> signifie Word Break Opportunity.

Il indique un emplacement où le navigateur peut effectuer un retour à la ligne si nécessaire.

Contrairement à <br>, le saut n'est pas forcé.

C'est utile pour certains identifiants, chemins ou longues chaînes difficiles à couper naturellement.
<p> superLongIdentifier<wbr>ForJavaScript<wbr>Application </p> <p> /documentation/<wbr>javascript/<wbr>async-await </p>
Utilisez les titres pour représenter la hiérarchie réelle du contenu.

Utilisez généralement un titre principal <h1>, puis des niveaux cohérents.

Utilisez :

<strong> pour l'importance,

<em> pour l'emphase,

<b> pour attirer l'attention sans importance supplémentaire,

<i> pour une voix ou un registre différent.

Ne choisissez pas une balise uniquement parce que son style par défaut vous plaît.

HTML décrit le sens. CSS contrôle l'apparence.
<!-- ✅ Sémantique --> <h2> Suppression du compte </h2> <p> <strong> Attention : </strong> cette action est irréversible. </p> <!-- ✅ Présentation avec CSS --> <span class="large-text" > Texte purement décoratif </span> /* CSS */ .large-text { font-size: 2rem; font-weight: 700; }

Listes

Les listes HTML permettent d'organiser des éléments liés entre eux : listes sans ordre particulier, étapes ordonnées, sous-listes et associations terme / description.

<ul> représente une liste dont l'ordre des éléments n'est pas important.

Chaque élément est représenté par un <li>.

Exemples :

• fonctionnalités
• ingrédients
• technologies utilisées
• liens de navigation
• caractéristiques

La forme des puces est un choix visuel et peut être modifiée avec CSS.
<ul> <li> HTML — Structure </li> <li> CSS — Mise en forme </li> <li> JavaScript — Interactivité </li> </ul> /* CSS */ ul { list-style-type: square; }
<ol> représente une liste ordonnée.

Elle convient lorsque la position des éléments possède une signification.

Exemples :

• étapes d'un tutoriel
• classement
• procédure
• chronologie

Attributs utiles :

start définit la valeur de départ.

reversed inverse la numérotation.

type contrôle le type de marqueur : 1, A, a, I, i.
<ol> <li> Ouvrir le terminal </li> <li> Exécuter <code>npm install</code> </li> <li> Démarrer le serveur </li> </ol> <!-- Commencer à 5 --> <ol start="5"> <li>Étape 5</li> <li>Étape 6</li> </ol> <!-- Compte à rebours --> <ol reversed> <li>Troisième</li> <li>Deuxième</li> <li>Premier</li> </ol>
<li> représente un élément d'une liste.

Il peut contenir :

texte,
liens,
paragraphes,
images,
boutons,
autres listes.

Dans une liste ordonnée, l'attribut value permet de définir la valeur numérique d'un item particulier.
<ol> <li> Première étape </li> <li value="10"> Cette étape porte le numéro 10 </li> <li> Cette étape continue à 11 </li> </ol>
Une liste peut contenir une autre liste.

La sous-liste doit être placée à l'intérieur du <li> correspondant.

Cette structure est utile pour :

• menus hiérarchiques
• plans de cours
• catégories
• arbres de contenu
<ul> <li> Front-end <ul> <li> HTML </li> <li> CSS </li> <li> JavaScript </li> </ul> </li> <li> Back-end <ul> <li> PHP </li> <li> Node.js </li> </ul> </li> </ul>
<dl> représente une liste de groupes nom / valeur ou terme / description.

<dt> représente le terme ou le nom.

<dd> représente sa description ou sa valeur.

Usages :

• glossaire
• métadonnées
• caractéristiques techniques
• questions / réponses simples
• informations produit

Un terme peut avoir plusieurs descriptions, et plusieurs termes peuvent partager une même description.
<dl> <dt> HTML </dt> <dd> Langage de balisage utilisé pour structurer une page web. </dd> <dt> CSS </dt> <dd> Langage permettant de définir la présentation. </dd> </dl> <!-- Métadonnées --> <dl> <dt>Auteur</dt> <dd>John Doe</dd> <dt>Date</dt> <dd>14 septembre 2026</dd> </dl>
CSS permet de modifier l'apparence des marqueurs.

Propriétés :

list-style-type
list-style-position
list-style-image

Le raccourci list-style peut regrouper plusieurs valeurs.

Pour des interfaces personnalisées, on utilise également souvent list-style: none, par exemple pour une navigation, à condition de conserver une structure HTML logique.
.features { list-style-type: square; } .roman-list { list-style-type: upper-roman; } .navigation-list { list-style: none; padding: 0; margin: 0; }
Le pseudo-élément ::marker cible directement le marqueur d'un élément de liste.

Il peut notamment modifier :

couleur,
taille de police,
graisse,
certains contenus.

Il est généralement préférable à une fausse puce créée uniquement avec ::before lorsque l'objectif est simplement de styliser le marqueur natif.
.features li::marker { color: #00d2ff; font-size: 1.2em; } .steps li::marker { font-weight: 700; color: royalblue; }
Utilisez <ul> lorsque l'ordre n'a pas de sens.

Utilisez <ol> lorsque la position des éléments est importante.

Utilisez <dl> pour des associations terme / description ou nom / valeur.

Ne remplacez pas une véritable liste par une série de <div> uniquement pour faciliter le CSS.

La structure HTML doit décrire le contenu ; CSS gère son apparence.
<!-- ✅ Une vraie liste --> <ul> <li>Vue.js</li> <li>Angular</li> <li>React</li> </ul> <!-- ❌ Moins sémantique --> <div>Vue.js</div> <div>Angular</div> <div>React</div>

Tableaux

Les tableaux HTML servent à présenter des données organisées en lignes et colonnes. Une bonne structure avec caption, th, scope, thead, tbody et tfoot améliore la compréhension et l'accessibilité.

<table> représente des données ayant une relation logique entre lignes et colonnes.

Utilisez-le pour :

statistiques,
résultats,
tarifs comparatifs,
horaires,
données financières.

Un tableau HTML ne doit pas servir à construire la mise en page générale.

Pour le layout, utilisez plutôt CSS Grid ou Flexbox.
<table> <caption> Chiffre d'affaires 2026 </caption> <thead> <tr> <th scope="col"> Mois </th> <th scope="col"> CA </th> </tr> </thead> <tbody> <tr> <th scope="row"> Janvier </th> <td>12 000 €</td> </tr> </tbody> </table>
<caption> fournit un titre ou une description au tableau.

Il doit être placé comme premier enfant de <table>.

Un caption clair aide l'utilisateur à comprendre immédiatement la nature des données.

Il est particulièrement utile pour l'accessibilité.
<table> <caption> Comparatif des forfaits mensuels </caption> <thead> ... </thead> <tbody> ... </tbody> </table>
<thead> regroupe les lignes d'en-tête.

<tbody> contient les lignes principales de données.

<tfoot> contient généralement les totaux, moyennes ou informations de synthèse.

Ces éléments améliorent la structure du document.

Pour rendre un grand tableau scrollable, il est souvent préférable de placer le tableau dans un conteneur avec overflow plutôt que de transformer directement <tbody>.
<table> <thead> <tr> <th>Produit</th> <th>Prix</th> </tr> </thead> <tbody> <tr> <td>Clavier</td> <td>90 €</td> </tr> </tbody> <tfoot> <tr> <th scope="row"> Total </th> <td>90 €</td> </tr> </tfoot> </table>
<tr> signifie table row.

Il représente une ligne du tableau.

Chaque ligne contient ensuite des cellules :

<th> pour les en-têtes,

ou <td> pour les données.
<tr> <th scope="row"> Février </th> <td> 14 500 € </td> <td> +8 % </td> </tr>
<th> représente une cellule d'en-tête.

<td> représente une cellule de données.

L'apparence visuelle par défaut de <th> n'est pas sa fonction principale : sa valeur est surtout sémantique.

L'attribut scope permet de préciser ce que décrit l'en-tête.
<thead> <tr> <th scope="col"> Nom </th> <th scope="col"> Score </th> </tr> </thead> <tbody> <tr> <th scope="row"> John </th> <td> 92 </td> </tr> </tbody>
scope indique les cellules auxquelles un <th> est associé.

Valeurs utiles :

col → en-tête de colonne.

row → en-tête de ligne.

colgroup et rowgroup existent pour des tableaux plus complexes.
<tr> <th scope="col" > Produit </th> <th scope="col" > Prix </th> </tr> <tr> <th scope="row" > Souris </th> <td> 49 € </td> </tr>
colspan permet à une cellule d'occuper plusieurs colonnes.

rowspan permet d'occuper plusieurs lignes.

Ces attributs sont utiles pour des tableaux structurés, mais une utilisation excessive peut rendre les relations entre données plus difficiles à comprendre.
<tr> <th colspan="3" > Résultats du trimestre </th> </tr> <tr> <th rowspan="2" > Groupe A </th> <td>John</td> <td>92</td> </tr>
<colgroup> permet de regrouper les colonnes d'un tableau.

<col> représente une ou plusieurs colonnes.

L'attribut span peut appliquer un même groupe à plusieurs colonnes.

Les propriétés CSS applicables directement aux colonnes sont cependant plus limitées que sur des éléments HTML classiques.
<table> <colgroup> <col class="product-column" > <col span="2" class="number-column" > </colgroup> ... </table> /* CSS */ .number-column { background: rgb(0 0 0 / 3%); }
Un tableau large peut dépasser sur un petit écran.

Une solution robuste consiste à conserver la structure native du tableau et à placer celui-ci dans un conteneur défilable horizontalement.

Cela évite de casser inutilement la sémantique du tableau.
<div class="table-wrapper" > <table> ... </table> </div> /* CSS */ .table-wrapper { max-width: 100%; overflow-x: auto; } table { width: 100%; border-collapse: collapse; }
table-layout contrôle la méthode utilisée pour calculer les largeurs des colonnes.

auto : le contenu influence les dimensions.

fixed : les dimensions peuvent être calculées plus directement à partir de la largeur du tableau et des colonnes définies.

fixed peut être pratique pour obtenir des colonnes plus prévisibles.
table { width: 100%; table-layout: fixed; } th, td { padding: 12px; overflow-wrap: anywhere; }
HTML ne trie pas automatiquement les lignes d'un tableau.

Une interface de tri nécessite généralement JavaScript.

Le contrôle de tri peut être un véritable <button> placé dans le <th>.

aria-sort peut indiquer l'état de tri de la colonne concernée.
<th scope="col" aria-sort="ascending" > <button type="button"> Nom </button> </th> <!-- JavaScript nécessaire pour effectuer le tri -->
Utilisez un tableau uniquement pour de véritables données tabulaires.

Ajoutez un <caption> lorsque le contexte n'est pas évident.

Utilisez <th> et scope pour les en-têtes.

Évitez les fusions de cellules inutilement complexes.

Gardez un ordre de lecture logique.

Sur mobile, préférez généralement un wrapper scrollable plutôt que de transformer complètement la structure du tableau.
<div class="table-wrapper" > <table> <caption> Résultats du trimestre </caption> <thead> ... </thead> <tbody> ... </tbody> </table> </div>

Formulaires

Les formulaires permettent de collecter des données utilisateur. HTML fournit les champs, labels, groupes, contraintes de validation et comportements natifs nécessaires à une interface accessible.

<form> regroupe des contrôles permettant de saisir puis d'envoyer des données.

action : URL recevant les données.

method="get" : encode généralement les données dans l'URL.

method="post" : transmet les données dans le corps de la requête.

POST ne rend pas les données automatiquement secrètes ou sécurisées : HTTPS et la sécurité côté serveur restent indispensables.

enctype="multipart/form-data" : nécessaire pour l'envoi classique de fichiers avec un formulaire POST.
<form action="/register" method="post" > <label for="email"> Email </label> <input id="email" name="email" type="email" required > <button type="submit"> Créer le compte </button> </form>
L'attribut name est essentiel lors de la soumission.

Il devient la clé associée à la valeur saisie.

Un champ sans name n'est généralement pas inclus dans les données du formulaire.

Pour certains contrôles comme radio ou checkbox, l'attribut value définit la valeur envoyée.
<input type="text" name="username" value="John" > <!-- Données possibles : --> username=John <input type="checkbox" name="newsletter" value="yes" >
<input> possède de nombreux types.

Parmi les plus courants :

text
email
password
number
tel
url
search
date
time
datetime-local
file
checkbox
radio
range
color
hidden.

Utiliser le type approprié améliore souvent clavier mobile, validation native et ergonomie.
<input type="email" name="email" > <input type="date" name="birthdate" > <input type="range" name="volume" min="0" max="100" step="5" > <input type="color" name="theme" value="#2563eb" >
<label> fournit le libellé d'un champ.

Le clic sur le label peut également activer le contrôle associé.

L'association classique utilise :

for sur le label

et le même id sur le champ.

Un placeholder ne doit pas remplacer un vrai label : il disparaît lorsque l'utilisateur saisit.
<label for="user-email" > Adresse email </label> <input id="user-email" name="email" type="email" placeholder="john@example.com" > <!-- Label englobant --> <label> Recevoir les nouveautés <input type="checkbox" name="newsletter" > </label>
autocomplete aide le navigateur à comprendre la nature d'une information.

Exemples :

name
email
username
current-password
new-password
postal-code.

inputmode donne une indication sur le clavier virtuel souhaité.

Exemples : numeric, decimal, tel, email.
<input type="email" name="email" autocomplete="email" > <input type="password" name="password" autocomplete="current-password" > <input type="text" name="verification-code" inputmode="numeric" >
HTML possède un système de validation native.

Attributs courants :

required
min
max
step
minlength
maxlength
pattern.

Le type de champ peut également participer à la validation.

Cette validation améliore l'expérience utilisateur mais ne remplace jamais la validation côté serveur.
<input type="text" name="username" required minlength="3" maxlength="30" > <input type="number" name="age" min="18" max="120" > <input type="text" name="code" pattern="[A-Z]{3}-[0-9]{4}" >
<textarea> permet la saisie de texte sur plusieurs lignes.

Contrairement à <input>, il possède une balise fermante.

Une valeur initiale peut être placée entre les deux balises.

Attention : les espaces et retours placés entre les balises peuvent faire partie de la valeur initiale.

Le redimensionnement visuel peut être contrôlé avec CSS.
<label for="message"> Votre message </label> <textarea id="message" name="message" rows="6" maxlength="500" ></textarea> /* CSS */ textarea { resize: vertical; }
<select> présente une liste d'options.

Chaque <option> possède un texte visible et éventuellement une value différente.

<optgroup> permet de regrouper les options.

multiple autorise plusieurs sélections, mais l'interaction exacte dépend de l'appareil et de l'interface du navigateur.
<label for="country"> Pays </label> <select id="country" name="country" required > <option value=""> Choisir un pays </option> <optgroup label="Europe"> <option value="fr"> France </option> <option value="it"> Italie </option> </optgroup> </select>
Un groupe de radio permet généralement de choisir une seule valeur.

Les boutons radio du même groupe utilisent le même name.

Une checkbox représente généralement une option indépendante pouvant être activée ou non.

Pour un groupe logique, utilisez idéalement <fieldset> et <legend>.
<fieldset> <legend> Mode de livraison </legend> <label> <input type="radio" name="delivery" value="standard" checked > Standard </label> <label> <input type="radio" name="delivery" value="express" > Express </label> </fieldset>
<button> crée un bouton interactif.

Types :

submit → soumet le formulaire.

button → bouton générique, souvent utilisé avec JavaScript.

reset → réinitialise les contrôles à leurs valeurs initiales.

Dans un formulaire, spécifiez explicitement type="button" pour un bouton qui ne doit pas soumettre le formulaire.
<button type="submit" > Envoyer </button> <button type="button" id="open-menu" > Ouvrir le menu </button> <button type="reset" > Réinitialiser </button>
<fieldset> regroupe des contrôles ayant une relation logique.

<legend> fournit le nom du groupe.

Ce pattern est particulièrement utile pour :

radios,
checkboxes,
informations personnelles,
groupes d'options.

La bordure visible par défaut est seulement un style navigateur : ce n'est pas la raison sémantique d'utiliser fieldset.
<fieldset> <legend> Informations personnelles </legend> <label for="first-name"> Prénom </label> <input id="first-name" name="firstName" type="text" autocomplete="given-name" > <label for="last-name"> Nom </label> <input id="last-name" name="lastName" type="text" autocomplete="family-name" > </fieldset>
input type="file" permet de sélectionner un fichier local.

multiple autorise plusieurs fichiers.

accept fournit une indication sur les types de fichiers attendus.

Il ne s'agit pas d'une protection de sécurité : le serveur doit toujours vérifier réellement le fichier reçu.

Pour une soumission HTML classique contenant des fichiers, utilisez généralement multipart/form-data.
<form action="/upload" method="post" enctype="multipart/form-data" > <label for="photos"> Photos </label> <input id="photos" name="photos" type="file" accept="image/*" multiple > </form>
<datalist> fournit des suggestions à un champ.

Le lien se fait avec :

list="..." sur l'input,

et un id correspondant sur le datalist.

Contrairement à un <select>, l'utilisateur peut généralement saisir une valeur différente de celles proposées.
<label for="framework"> Framework </label> <input id="framework" name="framework" list="framework-list" > <datalist id="framework-list" > <option value="Vue.js"> <option value="Angular"> <option value="React"> </datalist>
<output> représente le résultat d'un calcul ou d'une action.

Il peut être associé à d'autres contrôles grâce à l'attribut for.

Il est particulièrement adapté aux formulaires comportant des valeurs calculées.
<form oninput="result.value = Number(a.value) + Number(b.value)" > <input id="a" name="a" type="number" value="10" > + <input id="b" name="b" type="number" value="5" > = <output name="result" for="a b" > 15 </output> </form>
<progress> représente la progression d'une tâche.

Exemple : téléchargement à 70 %.

Sans attribut value, la progression est indéterminée.

<meter> représente une valeur à l'intérieur d'un intervalle connu.

Exemple : espace disque, niveau, score.

Son apparence exacte dépend du navigateur et du système : ne dépendez pas uniquement de sa couleur pour transmettre une information importante.
<label for="upload"> Téléchargement : 70 % </label> <progress id="upload" value="70" max="100" > 70 % </progress> <label for="disk"> Espace utilisé : 7 Go sur 10 </label> <meter id="disk" value="7" min="0" max="10" low="3" high="8" optimum="5" > 7 / 10 </meter>
disabled désactive un contrôle.

Un contrôle désactivé n'est généralement pas modifiable, ne reçoit pas le focus et n'est pas envoyé avec le formulaire.

readonly rend certains contrôles textuels non modifiables tout en conservant leur valeur dans le formulaire.

Les deux attributs ne sont donc pas interchangeables.
<input type="text" name="accountId" value="A-1032" readonly > <input type="text" name="internalCode" value="LOCKED" disabled >
Certains contrôles de formulaire peuvent être associés explicitement à un formulaire grâce à l'attribut form.

Le contrôle n'a alors pas besoin d'être physiquement imbriqué dans le <form>.

La valeur correspond à l'id du formulaire.
<form id="profile-form" action="/profile" method="post" > ... </form> <!-- En dehors du form --> <button type="submit" form="profile-form" > Enregistrer </button>
Un champ peut être associé à une aide ou un message d'erreur grâce à aria-describedby.

Lorsqu'une erreur est détectée, aria-invalid="true" peut indiquer que la valeur est considérée comme invalide.

Un message d'erreur doit être compréhensible et expliquer comment corriger le problème.

Évitez de signaler une erreur uniquement par la couleur.
<label for="password"> Mot de passe </label> <input id="password" name="password" type="password" aria-describedby="password-help password-error" aria-invalid="true" > <p id="password-help"> 12 caractères minimum. </p> <p id="password-error"> Le mot de passe est trop court. </p>
Les contraintes HTML améliorent surtout l'expérience utilisateur.

Elles peuvent être contournées.

Le serveur doit toujours :

valider les données,
vérifier les autorisations,
traiter les fichiers avec prudence,
protéger les données sensibles,
gérer les erreurs.

Utilisez HTTPS pour protéger les données pendant leur transport.

N'envoyez pas des mots de passe ou autres données sensibles dans une URL GET.
<!-- Validation HTML utile... --> <input type="email" name="email" required > <!-- ...mais le serveur doit encore vérifier que la donnée reçue est réellement valide. -->
Un formulaire propre combine notamment :

labels explicites,
attributs name,
types de champs adaptés,
autocomplete,
contraintes natives,
groupes fieldset/legend,
bouton avec type explicite.

Le placeholder reste facultatif et ne remplace pas le label.
<form action="/account" method="post" > <fieldset> <legend> Créer un compte </legend> <label for="account-name"> Nom </label> <input id="account-name" name="name" type="text" autocomplete="name" required > <label for="account-email"> Email </label> <input id="account-email" name="email" type="email" autocomplete="email" required > <label for="account-password"> Mot de passe </label> <input id="account-password" name="password" type="password" autocomplete="new-password" minlength="12" required > </fieldset> <button type="submit" > Créer le compte </button> </form>
Utilisez un label visible pour chaque champ lorsque c'est pertinent.

Utilisez le bon type de champ.

Ajoutez autocomplete lorsque cela facilite la saisie.

Groupez les choix liés avec fieldset et legend.

Indiquez clairement les erreurs.

Ne dépendez jamais uniquement de la couleur.

Et surtout : toute donnée reçue doit être considérée comme non fiable jusqu'à validation côté serveur.
<!-- ✅ Label visible --> <label for="contact-email"> Adresse email </label> <input id="contact-email" name="email" type="email" autocomplete="email" required > <!-- ❌ Le placeholder seul n'est pas un label --> <input type="email" placeholder="Email" >

Éléments Sémantiques

Les éléments sémantiques décrivent le rôle du contenu plutôt que son apparence. Ils rendent la structure du document plus claire pour les navigateurs, les technologies d'assistance et les moteurs de recherche.

Une balise sémantique décrit la fonction du contenu.

Par exemple :

<nav> indique une navigation.

<article> indique un contenu autonome.

<main> indique le contenu principal.

<div> reste utile lorsqu'aucune signification particulière n'est nécessaire, notamment pour un groupement purement visuel.
<!-- Peu descriptif --> <div class="navigation"> ... </div> <!-- Plus sémantique --> <nav> ... </nav> <!-- Groupement purement stylistique --> <div class="card-grid"> ... </div>
<header> représente du contenu introductif.

Il peut contenir :

logo,
titre,
navigation,
recherche,
informations d'auteur.

Une page peut contenir plusieurs <header>.

Un <article> peut par exemple posséder son propre header.
<header class="site-header"> <a href="/"> ExampleSite </a> <nav aria-label="Navigation principale" > ... </nav> </header> <article> <header> <h2>Comprendre Flexbox</h2> <p>Par John Doe</p> </header> ... </article>
<nav> représente une zone dont le rôle principal est la navigation.

Exemples :

menu principal,
sommaire,
pagination,
fil d'Ariane important.

Il n'est pas nécessaire d'utiliser <nav> autour de chaque petit groupe de liens.

Lorsque plusieurs navigations existent, un aria-label clair permet de les distinguer.
<nav aria-label="Navigation principale" > <ul> <li> <a href="/"> Accueil </a> </li> <li> <a href="/cours"> Cours </a> </li> </ul> </nav> <nav aria-label="Pagination" > ... </nav>
<main> représente le contenu dominant du document.

Son contenu doit être directement lié au sujet principal ou à la fonctionnalité principale de la page.

Dans une page classique, utilisez un seul <main> visible.

Techniquement, un document ne doit pas avoir plus d'un <main> sans attribut hidden.

Les éléments répétés entre pages comme le footer global ou la navigation principale ne font généralement pas partie de <main>.
<body> <header> ... </header> <main id="main-content"> <h1> Apprendre JavaScript </h1> <p> Contenu principal de la page. </p> </main> <footer> ... </footer> </body>
<section> représente une section générique mais autonome du document.

Elle est utile lorsqu'aucun élément plus spécifique comme <article>, <nav> ou <aside> ne convient.

Une section devrait généralement posséder un titre.

Pour un simple conteneur visuel sans signification particulière, préférez <div>.
<main> <h1> Formation JavaScript </h1> <section> <h2> Les bases </h2> <p> Variables, fonctions et conditions. </p> </section> <section> <h2> Le DOM </h2> ... </section> </main>
<article> représente une composition autonome.

Exemples fréquents :

article de blog,
actualité,
commentaire,
publication de forum,
fiche éditoriale autonome.

Une bonne question à se poser :

« Ce contenu conserve-t-il son sens lorsqu'il est présenté indépendamment du reste de la page ? »
<article> <header> <h2> Comprendre les Promises </h2> <p> Par John Doe </p> </header> <p> Une Promise représente... </p> <footer> <time datetime="2026-09-14" > 14 septembre 2026 </time> </footer> </article>
<aside> représente un contenu indirectement lié au contenu principal.

Il peut s'agir de :

ressources liées,
biographie d'auteur,
encadré complémentaire,
publicité,
sidebar.

L'élément n'est donc pas réservé aux colonnes latérales visuelles.
<article> <h2> CSS Grid </h2> <p> ... </p> <aside> <h3> À lire aussi </h3> <a href="/flexbox"> Guide Flexbox </a> </aside> </article>
<footer> représente le pied de la section ou du document auquel il appartient.

Il peut contenir :

auteur,
copyright,
liens légaux,
liens connexes,
coordonnées.

Comme <header>, il peut être utilisé plusieurs fois dans un document.
<footer class="site-footer"> <p> <small> © 2026 ExampleSite </small> </p> <a href="/privacy"> Confidentialité </a> <a href="/contact"> Contact </a> </footer>
Les éléments <h1> à <h6> représentent six niveaux de titres.

<h1> est le niveau le plus élevé.

Les niveaux doivent refléter la structure logique du contenu, et non simplement la taille visuelle désirée.

Utilisez CSS pour changer l'apparence des titres.
<main> <h1> Guide JavaScript </h1> <section> <h2> Fonctions </h2> <section> <h3> Fonctions fléchées </h3> </section> </section> </main>
<figure> représente un contenu autonome référencé depuis le texte.

Il peut contenir :

image,
diagramme,
capture d'écran,
exemple de code,
illustration.

<figcaption> fournit la légende associée.
<figure> <img src="grid-layout.webp" alt="Schéma d'une grille CSS à trois colonnes" > <figcaption> Exemple d'organisation avec CSS Grid. </figcaption> </figure>
<time> représente une date, une heure ou une durée.

L'attribut datetime fournit une valeur normalisée.

Le texte visible peut rester naturel pour l'utilisateur.
<p> Publié le <time datetime="2026-09-14" > 14 septembre 2026 </time> </p> <p> Durée : <time datetime="PT2H30M" > 2 h 30 </time> </p>
<details> crée une zone qui peut être ouverte ou fermée.

<summary> fournit son libellé interactif.

Il doit être le premier enfant de <details> pour servir de résumé.

L'attribut open permet d'afficher le contenu dès le chargement.
<details> <summary> Qu'est-ce que CSS Grid ? </summary> <p> CSS Grid est un système de mise en page bidimensionnel. </p> </details> <details open> <summary> Exemple ouvert </summary> <p> Visible immédiatement. </p> </details>
<search> représente une partie du document consacrée à une recherche ou à une opération de filtrage.

Il peut contenir :

formulaire de recherche,
filtres,
contrôles associés.

Il décrit directement la fonction de cette zone.
<search> <form action="/recherche" method="get" > <label for="q"> Rechercher </label> <input id="q" name="q" type="search" > <button type="submit"> Rechercher </button> </form> </search>
<address> représente les informations de contact liées à la personne ou organisation responsable du contenu concerné.

Il peut contenir :

nom,
email,
téléphone,
lien vers un profil,
adresse pertinente.

Il ne doit pas être utilisé simplement parce qu'un texte contient une adresse postale quelconque.
<article> <h2> Introduction à TypeScript </h2> ... <footer> <address> Écrit par <a href="mailto:john@example.com" > John Doe </a> </address> </footer> </article>
Choisissez une balise pour sa signification, pas pour son apparence par défaut.

Utilisez CSS pour le design.

Préférez une balise native lorsque celle-ci exprime déjà correctement le rôle du contenu.

N'ajoutez pas des rôles ARIA inutiles à des éléments possédant déjà la bonne sémantique.

Gardez également une hiérarchie de titres logique et compréhensible.
<!-- ✅ Bouton réel --> <button type="button"> Ouvrir </button> <!-- ✅ Lien pour naviguer --> <a href="/cours"> Voir les cours </a> <!-- ✅ Div si aucune sémantique spécifique --> <div class="visual-wrapper"> ... </div>

Head & Meta

Le <head> contient les métadonnées du document : encodage, viewport, titre, description, ressources, informations SEO, partage social et données structurées.

<head> contient les informations qui décrivent ou configurent le document.

On y trouve généralement :

encodage,
viewport,
titre,
description,
CSS,
favicon,
métadonnées sociales,
données structurées.

La majorité de ces informations n'est pas affichée directement dans le contenu de la page.
<!doctype html> <html lang="fr"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1" > <title> Guide JavaScript | ExampleSite </title> </head> <body> ... </body> </html>
<meta charset="UTF-8"> déclare l'encodage du document.

UTF-8 permet de représenter les caractères utilisés dans pratiquement toutes les langues, ainsi que de nombreux symboles.

Placez cette déclaration très tôt dans le <head> afin que le navigateur connaisse rapidement l'encodage.
<head> <meta charset="UTF-8" > ... </head>
La métadonnée viewport contrôle la zone d'affichage utilisée sur les appareils mobiles.

La configuration classique est :

width=device-width

avec :

initial-scale=1.

Évitez de désactiver le zoom utilisateur avec user-scalable=no ou des limites de zoom trop strictes : le zoom est important pour l'accessibilité.
<meta name="viewport" content="width=device-width, initial-scale=1" >
<title> définit le titre du document.

Il peut notamment apparaître :

dans l'onglet,
dans les favoris,
dans l'historique,
comme source possible d'un titre de résultat de recherche.

Chaque page devrait avoir un titre descriptif et distinct.

Il n'existe pas de nombre de caractères garantissant exactement l'affichage d'un titre : les moteurs peuvent le tronquer ou générer une présentation différente.
<title> Guide CSS Grid pour débutants | ExampleSite </title> <!-- Autre page --> <title> Comprendre les Promises JavaScript | ExampleSite </title>
meta name="description" fournit un résumé de la page.

Un moteur de recherche peut utiliser cette description pour un snippet, mais ce n'est pas garanti.

Le contenu visible de la page peut également être utilisé pour générer l'extrait.

Rédigez donc une description :

spécifique,
naturelle,
utile,
cohérente avec le contenu réel.
<meta name="description" content="Apprenez CSS Grid avec des exemples pratiques : colonnes, lignes, minmax(), auto-fit, zones nommées et layouts responsives." >
Une URL canonique indique la version préférée d'une page lorsqu'un contenu est accessible via plusieurs URL.

On utilise :

rel="canonical".

Utilisez généralement une URL absolue correcte.

Le canonical constitue un signal important, mais un moteur de recherche peut sélectionner une autre URL si d'autres signaux sont contradictoires.
<link rel="canonical" href="https://example.com/css/grid" >
La balise robots peut donner des directives aux moteurs compatibles.

Valeurs fréquentes :

index → autorise l'indexation.

noindex → demande de ne pas indexer.

follow → permet de suivre les liens.

noindex doit être utilisé avec précaution : une erreur sur cette balise peut retirer une page des résultats de recherche.
<!-- Page indexable --> <meta name="robots" content="index, follow" > <!-- Page à ne pas indexer --> <meta name="robots" content="noindex" >
Open Graph fournit des métadonnées utilisées par de nombreuses plateformes pour construire un aperçu de lien.

Les propriétés principales sont :

og:title
og:description
og:image
og:url
og:type

Utilisez des URL absolues pour les ressources importantes comme l'image.
<meta property="og:title" content="Guide CSS Grid" > <meta property="og:description" content="Comprendre CSS Grid avec des exemples pratiques." > <meta property="og:image" content="https://example.com/images/css-grid-og.webp" > <meta property="og:url" content="https://example.com/css/grid" > <meta property="og:type" content="article" >
Les métadonnées historiquement appelées Twitter Cards permettent de contrôler certains aperçus sur X.

Une valeur fréquente est :

summary_large_image.

Open Graph reste également utile pour mutualiser une partie des informations de partage.
<meta name="twitter:card" content="summary_large_image" > <meta name="twitter:title" content="Guide CSS Grid" > <meta name="twitter:description" content="Apprenez Grid avec des exemples pratiques." > <meta name="twitter:image" content="https://example.com/images/css-grid-og.webp" >
<link> associe le document à une ressource externe ou à une relation particulière.

Valeurs courantes de rel :

stylesheet
icon
preload
preconnect
canonical
manifest
alternate

Ne préchargez pas inutilement de nombreuses ressources : preload doit rester ciblé.
<!-- CSS --> <link rel="stylesheet" href="/css/style.css" > <!-- Favicon --> <link rel="icon" href="/favicon.svg" type="image/svg+xml" > <!-- Préconnexion --> <link rel="preconnect" href="https://fonts.example.com" > <!-- Ressource prioritaire --> <link rel="preload" href="/fonts/example.woff2" as="font" type="font/woff2" crossorigin >
Pour un site proposant plusieurs versions linguistiques ou régionales, hreflang permet d'indiquer les relations entre ces pages.

Chaque version doit pointer vers les variantes appropriées.

x-default peut représenter une page par défaut lorsqu'aucune variante linguistique ne convient.
<link rel="alternate" hreflang="fr" href="https://example.com/fr/css-grid" > <link rel="alternate" hreflang="en" href="https://example.com/en/css-grid" > <link rel="alternate" hreflang="x-default" href="https://example.com/css-grid" >
theme-color permet à certains navigateurs d'adapter des parties de leur interface à la couleur du site.

Il est possible d'utiliser des variantes associées aux préférences clair/sombre.

color-scheme peut également indiquer les thèmes pris en charge par le document.
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)" > <meta name="theme-color" content="#0a0a12" media="(prefers-color-scheme: dark)" > <meta name="color-scheme" content="light dark" >
Un Web App Manifest décrit certaines informations d'une application web installable.

On le relie au document avec :

rel="manifest".

Le fichier peut contenir :

nom,
icônes,
couleurs,
URL de départ,
mode d'affichage.
<!-- Dans le head --> <link rel="manifest" href="/manifest.webmanifest" > /* manifest.webmanifest */ { "name": "Example App", "short_name": "Example", "start_url": "/", "display": "standalone", "theme_color": "#0a0a12" }
Les données structurées fournissent des informations explicites sur le contenu d'une page.

JSON-LD est couramment utilisé à l'intérieur de :

<script type="application/ld+json">.

Types possibles selon la page :

Article,
BreadcrumbList,
Organization,
Product,
Recipe,
Event...

Le balisage doit correspondre au contenu réellement présent.

L'ajout de données structurées ne garantit pas l'affichage d'un résultat enrichi.
<script type="application/ld+json" > { "@context": "https://schema.org", "@type": "Article", "headline": "Comprendre CSS Grid", "datePublished": "2026-09-14", "author": { "@type": "Person", "name": "John Doe" } } </script>
Toutes les pages n'ont pas besoin de toutes les métadonnées possibles.

Une page publique classique peut néanmoins disposer d'une base comprenant :

charset,
viewport,
title,
description,
canonical,
favicon,
CSS,
Open Graph.

Ajoutez ensuite uniquement les métadonnées pertinentes pour le projet.
<head> <meta charset="UTF-8" > <meta name="viewport" content="width=device-width, initial-scale=1" > <title> Guide CSS Grid | ExampleSite </title> <meta name="description" content="Apprenez CSS Grid avec des exemples pratiques et des explications claires." > <link rel="canonical" href="https://example.com/css/grid" > <link rel="icon" href="/favicon.svg" > <link rel="stylesheet" href="/css/style.css" > <meta property="og:title" content="Guide CSS Grid" > <meta property="og:description" content="Apprenez CSS Grid avec des exemples pratiques." > <meta property="og:image" content="https://example.com/images/css-grid-og.webp" > <meta property="og:url" content="https://example.com/css/grid" > <meta property="og:type" content="article" > <meta name="twitter:card" content="summary_large_image" > <meta name="theme-color" content="#0a0a12" > </head>
Écrivez un <title> réellement descriptif pour chaque page.

Utilisez une description spécifique à son contenu.

Vérifiez que le canonical pointe vers la bonne URL.

N'utilisez jamais noindex par erreur sur une page destinée à apparaître dans les moteurs de recherche.

Les métadonnées sociales doivent correspondre au contenu réel.

Les données structurées ne remplacent pas le contenu visible de qualité.

Enfin, n'ajoutez pas des dizaines de métadonnées simplement parce qu'elles existent : gardez celles qui ont un rôle réel dans le projet.
<!-- Une base cohérente --> <title> Les Promises JavaScript | ExampleSite </title> <meta name="description" content="Comprenez les Promises JavaScript, then(), catch(), async/await et la gestion des erreurs." > <link rel="canonical" href="https://example.com/javascript/promises" > <meta property="og:title" content="Les Promises JavaScript" >

Box Model

Chaque élément CSS est représenté comme une boîte composée du contenu, du padding, de la bordure et des marges. Comprendre ce modèle est indispensable pour maîtriser les dimensions, les espacements et les débordements.

Une boîte CSS est composée de plusieurs zones.

Content : contenu réel de l'élément.

Padding : espace entre le contenu et la bordure.

Border : bordure autour du padding et du contenu.

Margin : espace extérieur séparant la boîte des éléments voisins.

La taille finale dépend ensuite de la valeur de box-sizing.
.card { width: 300px; padding: 20px; border: 2px solid #ddd; margin: 24px; } /* contenu ↓ padding ↓ border ↓ margin */
Par défaut, CSS utilise généralement :

box-sizing: content-box.

Dans ce mode, width et height représentent uniquement la zone de contenu. Padding et bordure s'ajoutent ensuite.

Avec :

box-sizing: border-box

la largeur et la hauteur déclarées incluent le contenu, le padding et la bordure.

C'est souvent plus simple pour construire des interfaces prévisibles.
/* Base fréquente */ *, *::before, *::after { box-sizing: border-box; } .card { width: 300px; padding: 20px; border: 2px solid #ddd; } /* Avec border-box : la boîte reste large de 300px. */
width définit la largeur.

height définit la hauteur.

Elles peuvent utiliser :

px
%
rem
vw / vh
dvh
auto

Pour beaucoup de contenus, il est préférable de laisser la hauteur en auto plutôt que de forcer une valeur fixe.
.card { width: 300px; } .container { width: 90%; } .hero { min-height: 100dvh; } .content { height: auto; }
Les propriétés min-width, max-width, min-height et max-height permettent de poser des limites.

Elles sont particulièrement utiles pour créer des interfaces fluides.

Un conteneur peut ainsi prendre toute la place disponible sans devenir excessivement large.
.container { width: 100%; max-width: 1200px; margin-inline: auto; } .dialog { width: min(500px, 90%); max-height: 80dvh; }
CSS peut calculer une taille directement à partir du contenu.

min-content : taille minimale intrinsèque.

max-content : taille préférée sans retour à la ligne lorsque cela est possible.

fit-content : taille adaptée au contenu tout en respectant les contraintes disponibles.

Ces valeurs sont particulièrement utiles avec Grid et les composants réutilisables.
.badge { width: fit-content; } .label { width: max-content; } .grid { display: grid; grid-template-columns: max-content 1fr; }
Les propriétés logiques permettent de raisonner selon le sens d'écriture.

inline-size correspond généralement à la largeur dans un texte occidental horizontal.

block-size correspond généralement à la hauteur.

On trouve également :

min-inline-size
max-inline-size
min-block-size
max-block-size.
.article { max-inline-size: 70ch; margin-inline: auto; } .panel { min-block-size: 200px; }
padding crée de l'espace à l'intérieur de la boîte.

Il sépare le contenu de la bordure.

Le padding n'a pas sa propre couleur : le background de l'élément est généralement peint derrière cette zone.

Raccourcis :

padding: 16px → quatre côtés.

padding: 12px 24px → vertical / horizontal.

Les propriétés logiques padding-inline et padding-block sont souvent très pratiques.
.card { padding: 24px; } .button { padding: 10px 18px; } .section { padding-inline: clamp(16px, 4vw, 48px); padding-block: 48px; }
margin crée de l'espace à l'extérieur de la bordure.

Contrairement au padding, la marge ne reçoit pas le background de l'élément.

margin-inline: auto est très utile pour centrer un bloc dont la largeur est inférieure à l'espace disponible.

Les marges automatiques ont également des comportements très utiles avec Flexbox.
.container { width: min(1200px, 100% - 32px); margin-inline: auto; } .section { margin-block: 48px; } .toolbar-actions { margin-inline-start: auto; }
Dans certaines situations du flux normal, les marges verticales de blocs peuvent fusionner.

Deux marges adjacentes ne sont donc pas forcément additionnées.

Ce comportement concerne surtout les blocs dans le flux normal.

Les marges des items Flexbox ou Grid ne fusionnent pas de la même manière.

Lorsque l'objectif est simplement d'espacer une série d'éléments dans Flexbox ou Grid, gap est souvent préférable.
h2 { margin-bottom: 30px; } p { margin-top: 20px; } /* Dans certaines situations, ces marges peuvent fusionner au lieu de produire 50px. */ .stack { display: flex; flex-direction: column; gap: 24px; }
border est un raccourci pour :

border-width
border-style
border-color

Styles fréquents :

solid
dashed
dotted
double
none

Une bordure participe aux dimensions de la boîte selon le modèle box-sizing choisi.
.card { border: 1px solid rgb(255 255 255 / 12%); } .separator { border-bottom: 2px solid royalblue; } .warning { border-inline-start: 4px solid orange; }
border-radius arrondit les coins de la boîte.

Une valeur élevée peut créer un bouton en forme de pilule.

50% permet notamment de produire une forme circulaire lorsque largeur et hauteur sont égales.

On peut aussi définir chaque coin séparément.
.card { border-radius: 16px; } .badge { border-radius: 999px; } .avatar { width: 80px; aspect-ratio: 1; border-radius: 50%; } .asymmetric { border-radius: 20px 4px 20px 4px; }
outline ressemble visuellement à une bordure, mais ne participe généralement pas aux dimensions de la boîte.

Il est particulièrement utile pour les indicateurs de focus.

outline-offset permet d'éloigner le contour de l'élément.

Évitez de supprimer un focus visible sans fournir une alternative accessible.
button:focus-visible { outline: 3px solid royalblue; outline-offset: 3px; } .debug { outline: 1px solid red; }
La syntaxe générale est :

x y blur spread color.

inset crée une ombre intérieure.

Plusieurs ombres peuvent être séparées par des virgules.

Les ombres trop fortes ou trop nombreuses peuvent rendre l'interface visuellement chargée.
.card { box-shadow: 0 8px 30px rgb(0 0 0 / 20%); } .input { box-shadow: inset 0 2px 4px rgb(0 0 0 / 15%); } .glow { box-shadow: 0 0 12px rgb(0 210 255 / 35%), 0 0 30px rgb(0 210 255 / 20%); }
overflow contrôle le contenu dépassant les dimensions d'une boîte.

visible → débordement visible.

hidden → contenu coupé.

auto → défilement lorsque nécessaire.

scroll → conteneur défilable.

clip → contenu coupé sans créer le même comportement de scrolling qu'un overflow classique.

Les variantes overflow-x et overflow-y contrôlent chaque axe.
.code { overflow-x: auto; } .modal { max-height: 80dvh; overflow-y: auto; } .image-wrapper { overflow: hidden; border-radius: 16px; }
scrollbar-gutter permet de réserver un espace destiné à une scrollbar classique.

Cela peut éviter certains déplacements de contenu lorsqu'une scrollbar apparaît ou disparaît.

Une valeur courante est :

stable.
html { scrollbar-gutter: stable; } .scroll-panel { overflow-y: auto; scrollbar-gutter: stable; }
Pour la majorité des interfaces, border-box simplifie le calcul des dimensions.

Évitez les hauteurs fixes lorsqu'un contenu peut évoluer.

Préférez max-width, min(), max() et clamp() pour les dimensions adaptatives.

Utilisez gap pour espacer les éléments de Flexbox et Grid.

Enfin, évitez overflow: hidden uniquement pour masquer un problème de layout : cherchez d'abord la cause du débordement.
*, *::before, *::after { box-sizing: border-box; } .container { width: min( 1200px, 100% - 32px ); margin-inline: auto; } .card { padding: clamp( 16px, 3vw, 32px ); }

Typographie CSS

CSS contrôle la famille de police, la taille, la graisse, la hauteur de ligne, l'espacement, l'alignement, les retours à la ligne et de nombreux détails typographiques.

font-family définit la police utilisée.

Il est recommandé de fournir plusieurs polices de secours.

Si la première police n'est pas disponible, le navigateur teste la suivante.

Familles génériques :

serif
sans-serif
monospace
cursive
fantasy
system-ui
body { font-family: system-ui, -apple-system, "Segoe UI", sans-serif; } code { font-family: "JetBrains Mono", Consolas, monospace; }
@font-face permet d'enregistrer une police utilisable dans le CSS.

font-family lui donne un nom local.

src indique le fichier de police.

Le format woff2 est couramment utilisé sur le web.

font-display contrôle le comportement d'affichage pendant le chargement.
@font-face { font-family: "Example Sans"; src: url("/fonts/example.woff2") format("woff2"); font-weight: 100 900; font-style: normal; font-display: swap; } body { font-family: "Example Sans", sans-serif; }
font-size définit la taille du texte.

rem : relatif à la taille de police de l'élément racine.

Sur de nombreux navigateurs, la valeur initiale habituelle correspond à environ 16 px, mais elle peut dépendre des réglages utilisateur.

em : valeur relative au contexte typographique.

clamp() permet de créer une taille fluide avec minimum et maximum.
body { font-size: 1rem; } h1 { font-size: clamp( 2rem, 6vw, 5rem ); } .subtitle { font-size: 1.25rem; }
font-weight contrôle l'épaisseur des caractères.

Valeurs courantes :

400 → normal
500 → medium
600 → semi-bold
700 → bold

Les polices variables peuvent proposer des valeurs intermédiaires beaucoup plus fines qu'une simple série de poids fixes.

La valeur réellement disponible dépend de la police chargée.
body { font-weight: 400; } .label { font-weight: 600; } h1 { font-weight: 750; } /* 750 est surtout intéressant si la police supporte cette variation. */
font-style permet notamment d'activer une variante italique.

Valeurs courantes :

normal
italic
oblique

Lorsqu'une véritable fonte italique existe, le navigateur peut utiliser les glyphes spécifiquement dessinés pour cette variante.
em { font-style: italic; } .caption { font-style: oblique; }
line-height détermine la hauteur de chaque ligne de texte.

Une valeur sans unité agit comme un multiplicateur de la taille de police.

Exemple :

line-height: 1.6.

Cette forme est souvent intéressante car elle s'adapte naturellement lorsque la taille du texte change.
body { font-size: 1rem; line-height: 1.6; } h1 { line-height: 1.05; } .small-label { line-height: 1.2; }
letter-spacing modifie l'espacement entre les caractères.

word-spacing modifie l'espacement entre les mots.

Une valeur positive espace davantage, tandis qu'une valeur négative resserre.

Les modifications importantes doivent être utilisées avec prudence afin de préserver la lisibilité.
.badge { text-transform: uppercase; letter-spacing: 0.08em; } h1 { letter-spacing: -0.025em; } .special-text { word-spacing: 0.15em; }
text-align contrôle l'alignement du contenu inline.

Valeurs courantes :

start
center
end
justify

start et end sont souvent préférables à gauche/droite lorsque plusieurs directions d'écriture doivent être supportées.
.article { text-align: start; } .hero { text-align: center; } .legal-text { text-align: justify; }
text-decoration peut contrôler plusieurs aspects d'une décoration.

Propriétés utiles :

text-decoration-line
text-decoration-color
text-decoration-style
text-decoration-thickness
text-underline-offset

Ces propriétés sont notamment pratiques pour personnaliser les liens.
a { text-decoration-line: underline; text-decoration-color: currentColor; text-decoration-thickness: 2px; text-underline-offset: 3px; } .old-price { text-decoration: line-through; }
text-transform modifie la présentation de la casse sans changer le texte source dans le HTML.

Valeurs courantes :

uppercase
lowercase
capitalize
none.
.badge { text-transform: uppercase; letter-spacing: 0.08em; } .username { text-transform: lowercase; }
text-wrap contrôle la manière dont le navigateur répartit le texte sur plusieurs lignes.

wrap : comportement normal.

nowrap : empêche les retours automatiques.

balance : tente d'équilibrer les lignes. Très intéressant pour les titres.

pretty : privilégie une composition typographique plus agréable, notamment pour des blocs de texte plus longs.

Le navigateur peut limiter le nombre de lignes traité par balance pour des raisons de coût.
h1, h2 { text-wrap: balance; } .article-intro { text-wrap: pretty; } .single-line { text-wrap: nowrap; }
white-space contrôle la façon dont les espaces et retours à la ligne du texte source sont traités.

Valeurs utiles :

normal
nowrap
pre
pre-wrap
pre-line

pre-wrap est notamment pratique lorsqu'on veut conserver les retours du texte source tout en autorisant le wrapping.
pre { white-space: pre; } .message { white-space: pre-wrap; } .nowrap { white-space: nowrap; }
Une URL ou un mot très long peut dépasser son conteneur.

overflow-wrap autorise le navigateur à casser un mot lorsque cela devient nécessaire.

word-break contrôle plus directement les possibilités de rupture.

Pour beaucoup de textes classiques :

overflow-wrap: anywhere est une solution pratique contre certains débordements.
.comment { overflow-wrap: anywhere; } .url { overflow-wrap: break-word; } .special-text { word-break: break-all; }
hyphens contrôle l'utilisation de traits d'union lors des retours à la ligne.

Valeurs :

none
manual
auto

La langue du document, définie notamment avec l'attribut HTML lang, peut être importante pour permettre au navigateur d'appliquer une césure correcte.
/* HTML */ <article lang="fr"> Texte... </article> /* CSS */ article { hyphens: auto; }
Pour produire le pattern classique avec points de suspension, plusieurs propriétés sont généralement combinées.

overflow: hidden cache le surplus.

white-space: nowrap conserve une seule ligne.

text-overflow: ellipsis affiche les points de suspension.
.card-title { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
line-clamp permet de limiter un bloc à un nombre donné de lignes.

La propriété standard existe, mais sa compatibilité n'est pas encore uniforme sur tous les navigateurs.

Le pattern historique utilisant :

-webkit-line-clamp
-webkit-box
-webkit-box-orient

reste donc utile lorsque la compatibilité est importante.
.excerpt { overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; } /* La propriété standard : line-clamp: 3; existe également, mais vérifier la compatibilité ciblée. */
font-variant-numeric contrôle certaines variantes typographiques des chiffres.

tabular-nums donne aux chiffres une largeur régulière, lorsque la police le supporte.

C'est particulièrement pratique pour :

tableaux,
prix,
compteurs,
statistiques.
.price, .stat, .timer { font-variant-numeric: tabular-nums; } .fraction { font-variant-numeric: diagonal-fractions; }
Une police variable peut contenir plusieurs variations dans une seule ressource.

Les axes fréquents comprennent :

poids,
largeur,
inclinaison,
taille optique.

Pour les axes courants, utilisez de préférence les propriétés CSS habituelles comme font-weight.

font-variation-settings permet un contrôle plus bas niveau lorsque cela est nécessaire.
.title { font-weight: 725; } .special-title { font-variation-settings: "wght" 700, "wdth" 90; }
Certaines polices variables disposent d'un axe optique.

font-optical-sizing: auto permet alors au navigateur d'utiliser une variante adaptée à la taille d'affichage.

Le résultat dépend entièrement des capacités de la police choisie.
body { font-optical-sizing: auto; } h1 { font-size: 4rem; font-optical-sizing: auto; }
text-shadow ajoute une ou plusieurs ombres au texte.

Syntaxe :

x y blur color.

Contrairement à box-shadow, il n'existe pas de spread ou d'ombre inset.

Plusieurs ombres peuvent être séparées par des virgules.
.hero-title { color: cyan; text-shadow: 0 0 10px rgb(0 210 255 / 50%), 0 0 30px rgb(0 210 255 / 25%); }
Une bonne typographie ne consiste pas uniquement à choisir une jolie police.

Gardez une longueur de ligne confortable pour les textes longs.

Utilisez une hauteur de ligne suffisante pour le corps du texte.

Évitez les tailles fixes excessivement petites.

Limitez les effets décoratifs qui réduisent la lisibilité.

Pour les grands titres, text-wrap: balance peut améliorer la composition.

Pour le corps du texte, une largeur exprimée en ch peut être utile pour limiter la longueur des lignes.
body { font-family: system-ui, sans-serif; font-size: 1rem; line-height: 1.6; } article { max-inline-size: 70ch; } h1, h2 { line-height: 1.1; text-wrap: balance; }

Layout & Position

Les propriétés de layout et de positionnement contrôlent la façon dont les éléments participent au flux du document, se superposent, débordent ou restent attachés à une zone de l'écran.

display détermine la façon dont un élément participe à la mise en page.

block : crée généralement une boîte occupant la largeur disponible.

inline : reste dans le flux du texte.

inline-block : reste en ligne tout en acceptant largeur et hauteur.

flex : active Flexbox.

grid : active CSS Grid.

none : retire l'élément du rendu et de la mise en page.
.block { display: block; } .tag { display: inline-block; } .navigation { display: flex; } .cards { display: grid; } .hidden { display: none; }
Ces trois propriétés ne produisent pas le même résultat.

display: none : l'élément ne participe plus à la mise en page.

visibility: hidden : l'élément devient invisible, mais son espace est conservé.

opacity: 0 : l'élément devient transparent, mais reste présent dans le layout et peut encore participer aux interactions.

Ne choisissez donc pas l'une de ces propriétés uniquement parce qu'elles semblent toutes « cacher » quelque chose.
.removed { display: none; } .invisible { visibility: hidden; } .transparent { opacity: 0; } /* opacity: 0 ne signifie pas automatiquement "désactivé". */
static : positionnement normal par défaut.

relative : reste dans le flux, mais peut être décalé. Il peut aussi servir de repère à un descendant positionné.

absolute : est retiré du flux normal et positionné relativement à son containing block approprié.

fixed : reste généralement attaché au viewport pendant le défilement.

sticky : se comporte normalement jusqu'à atteindre un seuil, puis reste collé dans sa zone de défilement.
/* Parent de référence */ .card { position: relative; } /* Badge */ .badge { position: absolute; top: -8px; right: -8px; } /* Bouton fixe */ .help-button { position: fixed; right: 24px; bottom: 24px; } /* Barre collante */ .topbar { position: sticky; top: 0; }
inset est un raccourci pour :

top, right, bottom, left.

On trouve également les propriétés logiques :

inset-inline
inset-block
inset-inline-start
inset-inline-end

Elles sont pratiques pour créer des interfaces indépendantes du sens d'écriture.
/* Ancienne écriture */ .overlay-a { position: absolute; top: 0; right: 0; bottom: 0; left: 0; } /* Écriture courte */ .overlay-b { position: absolute; inset: 0; } .side-panel { position: fixed; inset-block: 0; inset-inline-end: 0; }
Pour fonctionner, sticky a généralement besoin d'un seuil comme :

top: 0.

Son comportement dépend aussi du conteneur de défilement.

Un parent avec certaines valeurs de overflow peut modifier le comportement attendu.

L'élément sticky reste également limité par les limites de son conteneur.
.article-layout { display: grid; grid-template-columns: 220px 1fr; gap: 32px; } .article-nav { position: sticky; top: 24px; align-self: start; }
z-index contrôle l'ordre d'empilement dans le contexte approprié.

Une valeur plus grande ne garantit pas toujours qu'un élément passera devant tout le reste.

La raison : les éléments peuvent appartenir à différents stacking contexts.

Un nouveau contexte d'empilement peut notamment être créé par certaines propriétés telles que :

position + z-index, opacity, transform, filter, isolation...

Les items Flex et Grid peuvent également utiliser z-index sans nécessairement être positionnés.
:root { --z-base: 1; --z-dropdown: 10; --z-sticky: 20; --z-overlay: 30; } .topbar { position: sticky; top: 0; z-index: var(--z-sticky); } .dropdown { position: absolute; z-index: var(--z-dropdown); }
isolation: isolate crée explicitement un nouveau contexte d'empilement.

Cela peut être utile pour un composant comportant plusieurs couches visuelles.

Ses enfants peuvent gérer leur z-index sans interférer avec les couches extérieures au composant.
.hero { position: relative; isolation: isolate; } .hero::before { content: ""; position: absolute; inset: 0; z-index: -1; background: linear-gradient( royalblue, purple ); }
overflow définit ce qui se passe lorsqu'un contenu dépasse les dimensions de son conteneur.

visible : débordement visible.

hidden : contenu visuellement coupé.

auto : permet le défilement lorsque nécessaire.

scroll : crée un conteneur de défilement.

clip : coupe le contenu sans fournir le même comportement de scrolling que hidden.

On peut aussi utiliser : overflow-x et overflow-y.
.code-block { overflow-x: auto; } .modal-content { max-height: 80dvh; overflow-y: auto; } .cropped { overflow: hidden; border-radius: 16px; }
object-fit contrôle la façon dont un contenu remplacé, comme une image ou une vidéo, remplit sa boîte.

cover : remplit la zone, quitte à recadrer.

contain : affiche tout le média sans le recadrer.

fill : remplit les dimensions même si cela déforme le média.

object-position détermine la zone privilégiée lors du recadrage.
.avatar { width: 80px; height: 80px; object-fit: cover; object-position: center top; border-radius: 50%; } .product-image { width: 100%; aspect-ratio: 4 / 3; object-fit: contain; }
Avant Flexbox et Grid, float était souvent détourné pour créer des layouts complets.

Aujourd'hui, Grid et Flexbox sont généralement préférables pour cela.

float reste néanmoins utile pour faire circuler du texte autour d'une image.

display: flow-root crée un nouveau contexte de formatage permettant notamment de contenir proprement des éléments flottants.
.article-image { float: left; width: 220px; margin: 0 20px 12px 0; } .article { display: flow-root; }
Certains éléments peuvent être placés dans une couche spéciale du navigateur, appelée top layer.

C'est notamment le cas de certains :

<dialog>
popovers
éléments en plein écran.

Cette couche se situe au-dessus des contextes d'empilement classiques.

Il ne sert donc à rien de tenter de battre un véritable top layer avec un z-index: 999999.
dialog { border: 0; border-radius: 16px; padding: 24px; } dialog::backdrop { background: rgb(0 0 0 / 60%); }
Pour une mise en page robuste, laissez autant que possible les éléments participer au flux normal.

Utilisez Flexbox et Grid pour organiser les composants.

Réservez absolute aux éléments qui doivent réellement être superposés ou détachés du flux.

Évitez les valeurs de z-index arbitrairement énormes.

Vérifiez également les débordements avant de masquer systématiquement le problème avec overflow: hidden.
/* ✅ Layout principal */ .page { display: grid; grid-template-columns: 240px minmax(0, 1fr); } /* ✅ Organisation locale */ .toolbar { display: flex; align-items: center; gap: 12px; } /* ✅ Positionnement ponctuel */ .card { position: relative; } .card-badge { position: absolute; inset: 8px 8px auto auto; }

Flexbox

Flexbox organise et aligne des éléments principalement sur un axe. Il est particulièrement utile pour les navigations, barres d'outils, boutons, cartes, centrages et composants responsives.

display: flex transforme un élément en flex container.

Ses enfants directs deviennent des flex items.

Par défaut :

flex-direction: row

Les éléments sont donc placés le long de l'axe principal, qui suit alors l'axe inline.
.container { display: flex; gap: 16px; } /* HTML */ <div class="container"> <div>Item 1</div> <div>Item 2</div> <div>Item 3</div> </div>
flex-direction définit l'axe principal.

row : axe principal horizontal dans un document occidental classique.

row-reverse : inverse cet axe.

column : axe principal vertical.

column-reverse : axe vertical inversé.

Lorsque la direction change, le rôle de justify-content change avec l'axe principal.
.navigation { display: flex; flex-direction: row; } .sidebar-menu { display: flex; flex-direction: column; gap: 8px; }
justify-content distribue les flex items sur l'axe principal.

Valeurs courantes :

flex-start
center
flex-end
space-between
space-around
space-evenly

Son orientation dépend de flex-direction.
.navbar { display: flex; justify-content: space-between; } .actions { display: flex; justify-content: center; gap: 12px; }
align-items aligne les items sur l'axe croisé.

Valeurs fréquentes :

stretch
flex-start
center
flex-end
baseline

Avec flex-direction: row, cet axe est généralement vertical.

Avec column, il devient généralement horizontal.
.toolbar { display: flex; align-items: center; gap: 12px; } /* Centrage sur les deux axes */ .centered { display: flex; justify-content: center; align-items: center; min-height: 100dvh; }
Par défaut, Flexbox tente de conserver les items sur une seule ligne.

flex-wrap: wrap autorise plusieurs lignes.

Valeurs :

nowrap
wrap
wrap-reverse

flex-flow est un raccourci combinant flex-direction et flex-wrap.
.cards { display: flex; flex-wrap: wrap; gap: 20px; } /* Raccourci */ .tags { display: flex; flex-flow: row wrap; }
align-content ne doit pas être confondu avec align-items.

Il distribue les lignes flex elles-mêmes sur l'axe croisé.

Il est donc surtout utile lorsque :

le conteneur utilise flex-wrap: wrap,

plusieurs lignes existent,

et le conteneur possède de l'espace disponible sur l'axe croisé.
.gallery { display: flex; flex-wrap: wrap; align-content: space-between; height: 500px; gap: 16px; }
gap crée de l'espace entre les flex items.

Il ne crée pas de marge sur les bords extérieurs.

On peut aussi utiliser :

row-gap et column-gap.

C'est généralement plus simple que d'ajouter des marges individuelles à chaque item.
.navigation { display: flex; gap: 16px; } .cards { display: flex; flex-wrap: wrap; row-gap: 24px; column-gap: 16px; }
flex-grow : définit la capacité d'un item à recevoir une part de l'espace libre.

flex-shrink : contrôle sa capacité à rétrécir lorsqu'il manque de place.

flex-basis : définit sa taille de base sur l'axe principal, avant la distribution de l'espace disponible.

Ces trois propriétés expliquent réellement le comportement de Flexbox.
.sidebar { flex-grow: 0; flex-shrink: 0; flex-basis: 260px; } main { flex-grow: 1; flex-shrink: 1; flex-basis: 0; }
La propriété flex est un raccourci pour :

flex-grow, flex-shrink, flex-basis.

Exemple :

flex: 1 1 300px

signifie qu'un item : peut grandir, peut rétrécir, et part d'une base de 300 px.

flex: 1 signifie généralement qu'il participe fortement au partage de l'espace disponible, avec une base calculée très petite, plutôt que simplement « prendre toute la largeur ».
.cards { display: flex; flex-wrap: wrap; gap: 20px; } .card { flex: 1 1 280px; } .sidebar { flex: 0 0 260px; } main { flex: 1; }
align-items agit sur tous les items.

align-self permet à un seul item d'utiliser un alignement différent.

Valeurs courantes :

auto
stretch
flex-start
center
flex-end

Il agit sur l'axe croisé.
.toolbar { display: flex; align-items: center; } .special-button { align-self: flex-end; }
Les marges automatiques sont particulièrement puissantes avec Flexbox.

Une marge automatique peut absorber l'espace libre.

C'est très pratique pour pousser un bouton ou un groupe vers l'autre extrémité du conteneur.

Avec les propriétés logiques, margin-inline-start: auto évite de dépendre explicitement de gauche ou droite.
.navbar { display: flex; align-items: center; gap: 16px; } .login-button { margin-inline-start: auto; } /* Logo | liens ........ bouton */
order peut changer l'ordre visuel des flex items.

Par défaut :

order: 0.

Une valeur plus petite est placée plus tôt.

Attention : cela ne réécrit pas nécessairement l'ordre logique du document.

Pour l'accessibilité, l'ordre HTML doit rester cohérent avec la lecture et les interactions.
.item-a { order: 2; } .item-b { order: 1; } .item-c { order: 3; } /* Visuellement : B → A → C Ne pas utiliser ceci pour corriger un mauvais ordre HTML. */
Un flex item peut conserver une taille minimale liée à son contenu.

Une longue URL, un bloc de code, ou du texte non cassable peut alors forcer le conteneur à déborder.

min-width: 0 permet souvent au flex item de réellement rétrécir.

C'est un correctif important dans de nombreux layouts Flexbox.
.layout { display: flex; } .sidebar { flex: 0 0 260px; } main { flex: 1; min-width: 0; } pre { overflow-x: auto; }
La combinaison :

flex-wrap: wrap

avec :

flex: 1 1 280px

permet de créer rapidement des composants flexibles.

Chaque carte possède une taille de base, peut grandir, peut rétrécir et passe sur une nouvelle ligne lorsqu'il n'y a plus assez d'espace.
.cards { display: flex; flex-wrap: wrap; gap: 24px; } .card { flex: 1 1 280px; } /* Les cartes changent naturellement de ligne quand l'espace diminue. */
Flexbox est particulièrement adapté lorsque l'organisation principale suit un axe.

Exemples :

navigation, barre d'actions, groupe de boutons, contenu d'une carte, alignement d'icônes.

Grid est souvent préférable lorsqu'il faut contrôler simultanément lignes et colonnes.

Les deux systèmes peuvent parfaitement être imbriqués.
/* Grid pour les cartes */ .cards { display: grid; grid-template-columns: repeat( auto-fit, minmax(260px, 1fr) ); } /* Flexbox dans chaque carte */ .card { display: flex; flex-direction: column; } .card-actions { display: flex; gap: 8px; margin-top: auto; }
Commencez toujours par identifier l'axe principal du composant.

Utilisez gap pour gérer les espacements.

Utilisez les marges automatiques pour repousser un item lorsque c'est plus simple qu'une distribution globale.

Comprenez grow, shrink et basis plutôt que d'utiliser flex: 1 partout sans raison.

Gardez également l'ordre HTML logique, même si Flexbox peut changer l'ordre visuel.
.toolbar { display: flex; align-items: center; gap: 12px; } .toolbar-actions { margin-inline-start: auto; display: flex; gap: 8px; }

CSS Grid

CSS Grid est un système de mise en page bidimensionnel permettant d'organiser les éléments en lignes et colonnes. Il est particulièrement adapté aux layouts de pages, dashboards, galeries et grilles de cartes.

display: grid transforme un élément en conteneur Grid.

Ses enfants directs deviennent des grid items.

Une grille est composée de :

• lignes de grille
• colonnes
• rangées
• cellules
• pistes (tracks)
• zones (areas)

Contrairement à Flexbox, principalement orienté sur un axe, Grid permet de travailler simultanément sur les lignes et les colonnes.
.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; } /* HTML */ <div class="grid"> <article> Card 1 </article> <article> Card 2 </article> <article> Card 3 </article> </div>
grid-template-columns définit les colonnes de la grille.

grid-template-rows définit les lignes.

Les pistes peuvent utiliser :

px
%
fr
auto
min-content
max-content
fit-content()

L'unité fr représente une fraction de l'espace disponible dans la grille.
/* Trois colonnes égales */ .grid-a { display: grid; grid-template-columns: 1fr 1fr 1fr; } /* Sidebar + contenu */ .layout { display: grid; grid-template-columns: 240px 1fr; } /* Colonnes et lignes */ .dashboard { display: grid; grid-template-columns: 200px 1fr 300px; grid-template-rows: auto 1fr auto; }
repeat() évite de répéter plusieurs fois la même définition.

repeat(3, 1fr) crée trois colonnes égales.

minmax() définit une taille minimale et une taille maximale pour une piste.

Exemple :

minmax(250px, 1fr)

signifie : la colonne ne doit pas devenir plus petite que 250 px mais peut ensuite s'étendre.
.grid { display: grid; grid-template-columns: repeat(3, 1fr); } .flexible-grid { display: grid; grid-template-columns: repeat( 3, minmax(200px, 1fr) ); }
auto-fit et auto-fill permettent de créer automatiquement autant de colonnes que l'espace disponible le permet.

auto-fit : les pistes vides peuvent se replier, laissant les éléments existants occuper l'espace disponible.

auto-fill : conserve les pistes potentielles, même si certaines sont vides.

Pour une grille de cartes responsive, auto-fit est très souvent pratique.
/* Responsive automatique */ .cards { display: grid; grid-template-columns: repeat( auto-fit, minmax( min(260px, 100%), 1fr ) ); gap: 24px; } /* Conserve les pistes vides possibles */ .cards-fill { grid-template-columns: repeat( auto-fill, minmax(200px, 1fr) ); }
gap définit l'espace entre les lignes et les colonnes.

On peut également utiliser :

row-gap pour les lignes.

column-gap pour les colonnes.

Contrairement aux marges, gap ajoute uniquement l'espace entre les éléments, sans créer d'espace supplémentaire sur les bords externes de la grille.
.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; } .custom-gap { row-gap: 32px; column-gap: 16px; } /* Deux valeurs avec gap */ .another-grid { gap: 32px 16px; }
Grid numérote les lignes séparant les pistes.

Ainsi :

grid-column: 1 / 3 signifie : commencer sur la ligne 1 et terminer sur la ligne 3.

L'élément occupe donc deux colonnes.

span 2 signifie : occuper deux pistes.

1 / -1 permet souvent d'occuper toute la grille explicite.
.hero { grid-column: 1 / -1; } .featured { grid-column: span 2; grid-row: span 2; } .widget { grid-column: 2 / 4; grid-row: 1 / 3; }
Grid possède plusieurs propriétés d'alignement.

justify-items : alignement sur l'axe inline.

align-items : alignement sur l'axe block.

place-items : raccourci pour les deux.

Pour déplacer l'ensemble des pistes dans le conteneur :

justify-content
align-content
place-content

Pour un seul item :

justify-self, align-self et place-self.
.grid { display: grid; grid-template-columns: repeat(3, 150px); place-items: center; } /* Positionner toute la grille */ .dashboard { justify-content: center; align-content: start; } /* Un seul élément */ .special-card { place-self: center; }
Les pistes définies avec grid-template-* constituent la grille explicite.

Si davantage d'éléments doivent être placés, CSS peut créer automatiquement de nouvelles pistes : c'est la grille implicite.

On peut contrôler leur taille avec :

grid-auto-rows et grid-auto-columns.
.grid { display: grid; grid-template-columns: repeat(3, 1fr); grid-auto-rows: minmax(120px, auto); gap: 16px; } /* Les nouvelles lignes créées automatiquement auront au moins 120px. */
grid-auto-flow contrôle la façon dont Grid place automatiquement les éléments.

Valeurs fréquentes :

row → remplit par lignes.

column → remplit par colonnes.

dense tente de remplir les trous disponibles.

Attention : dense peut produire un ordre visuel différent de l'ordre du document.

Il faut donc éviter de l'utiliser lorsque l'ordre de lecture est important.
.grid { display: grid; grid-template-columns: repeat(4, 1fr); grid-auto-flow: row; } .gallery { display: grid; grid-template-columns: repeat(4, 1fr); grid-auto-flow: dense; }
grid-template-areas permet de représenter visuellement la structure d'une page.

Chaque chaîne représente une ligne.

Le même nom répété représente une zone occupant plusieurs cellules.

Un point . représente une cellule non nommée.

Les éléments sont ensuite associés aux zones avec grid-area.
.app-layout { display: grid; grid-template-areas: "header header header" "sidebar main aside" "footer footer footer"; grid-template-columns: 220px 1fr 280px; grid-template-rows: auto 1fr auto; gap: 20px; } header { grid-area: header; } .sidebar { grid-area: sidebar; } main { grid-area: main; } aside { grid-area: aside; } footer { grid-area: footer; }
Les zones nommées sont particulièrement pratiques pour créer une mise en page responsive.

On peut définir une structure simple sur mobile, puis changer uniquement grid-template-areas et les colonnes sur un écran plus large.

Le HTML reste identique.
/* Mobile */ .layout { display: grid; grid-template-areas: "header" "main" "sidebar" "footer"; grid-template-columns: 1fr; } @media (min-width: 900px) { .layout { grid-template-areas: "header header" "sidebar main" "footer footer"; grid-template-columns: 240px 1fr; } }
Les lignes d'une grille peuvent recevoir des noms personnalisés.

Les noms sont placés entre crochets :

[sidebar-start]

Ils rendent les placements complexes plus faciles à comprendre qu'une série de numéros comme 2 / 5.
.layout { display: grid; grid-template-columns: [page-start] 240px [content-start] 1fr [content-end] 300px [page-end]; } main { grid-column: content-start / content-end; }
Une grille imbriquée possède normalement ses propres pistes indépendantes.

Avec subgrid, une grille enfant peut reprendre les pistes définies par sa grille parente.

Cela permet par exemple d'aligner parfaitement le titre, le contenu et le bouton de plusieurs cartes, même lorsque leur contenu possède des hauteurs différentes.

subgrid peut être utilisé sur les colonnes, les lignes ou les deux.
.cards { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: auto auto auto; gap: 20px; } .card { display: grid; grid-row: span 3; grid-template-rows: subgrid; }
Un item Grid possède parfois une taille minimale liée à son contenu.

Une longue URL, un bloc de code ou un contenu non cassable peut alors provoquer un débordement.

Deux solutions fréquentes :

min-width: 0 sur le grid item.

ou :

minmax(0, 1fr) dans la définition de la piste.

Cette technique est particulièrement utile pour les layouts avec sidebar.
.layout { display: grid; grid-template-columns: 240px minmax(0, 1fr); } main { min-width: 0; } pre { overflow-x: auto; }
Grid et Flexbox ne sont pas concurrents.

Grid est particulièrement efficace lorsque la structure doit être organisée sur deux dimensions.

Exemples :

dashboards,
layouts de pages,
galeries,
grilles de cartes.

Flexbox est souvent pratique pour organiser les éléments d'un composant sur un axe.

Exemple classique : Grid pour disposer les cartes, Flexbox à l'intérieur de chaque carte.
/* Layout principal avec Grid */ .cards { display: grid; grid-template-columns: repeat( auto-fit, minmax(250px, 1fr) ); gap: 24px; } /* Contenu d'une carte avec Flexbox */ .card { display: flex; flex-direction: column; } .card-actions { display: flex; gap: 8px; margin-top: auto; }
1. Utiliser gap pour les espacements entre items.

2. Utiliser auto-fit et minmax() lorsque la grille peut être naturellement responsive.

3. Préserver autant que possible un ordre HTML logique.

4. Ne pas réorganiser visuellement des informations importantes d'une façon différente de l'ordre de lecture.

5. Utiliser les zones nommées pour les grandes structures lisibles.

6. Utiliser subgrid lorsque plusieurs composants imbriqués doivent partager les mêmes pistes.

7. Ne pas ajouter des breakpoints lorsqu'une grille flexible suffit déjà.
/* Grille responsive simple */ .cards { display: grid; grid-template-columns: repeat( auto-fit, minmax( min(280px, 100%), 1fr ) ); gap: clamp(16px, 3vw, 32px); } /* Pas de breakpoint nécessaire pour cette grille de cartes. */

Effets visuels & Animations

CSS permet de déplacer, transformer, filtrer et animer les éléments sans modifier leur structure HTML. Transitions, transformations, keyframes, filtres, masques et animations liées au scroll permettent de créer des interfaces dynamiques tout en respectant l'accessibilité.

transform modifie visuellement la géométrie d'un élément.

Fonctions courantes :

translate()
translateX()
translateY()
rotate()
scale()
skew()

Une transformation ne provoque généralement pas le déplacement des autres éléments dans le layout.

Les transformations sont donc particulièrement adaptées aux interactions et animations visuelles.
/* Déplacement */ .card:hover { transform: translateY(-6px); } /* Rotation */ .icon:hover { transform: rotate(15deg); } /* Agrandissement */ .button:active { transform: scale(0.96); } /* Plusieurs transformations */ .photo:hover { transform: translateY(-4px) rotate(2deg) scale(1.03); }
CSS propose aussi des propriétés séparées :

translate
rotate
scale

Elles évitent d'avoir à réécrire toute la propriété transform pour modifier une seule transformation.

Elles sont très pratiques pour les transitions et les composants interactifs.
.card { translate: 0 0; scale: 1; transition: translate 200ms ease, scale 200ms ease; } .card:hover { translate: 0 -6px; scale: 1.02; } .icon:hover { rotate: 15deg; }
transform-origin définit le point autour duquel une transformation est effectuée.

Par défaut :

50% 50%

c'est-à-dire le centre.

On peut utiliser :

center
top left
bottom center
des pourcentages ou des longueurs.
.door { transform-origin: left center; transition: transform 400ms ease; } .door:hover { transform: rotateY(45deg); } .needle { transform-origin: bottom center; }
CSS permet également des transformations dans l'espace 3D.

Fonctions utiles :

rotateX()
rotateY()
rotateZ()
translateZ()

perspective donne une sensation de profondeur.

transform-style: preserve-3d permet aux descendants de conserver leur positionnement tridimensionnel.
.scene { perspective: 1000px; } .card-3d { transform-style: preserve-3d; transition: transform 500ms ease; } .card-3d:hover { transform: rotateY(15deg) rotateX(5deg); }
transition anime automatiquement le passage entre deux valeurs d'une propriété CSS.

Elle est particulièrement adaptée aux changements :

:hover
:focus
:active
ajout ou retrait de classe

Syntaxe :

propriété durée timing délai.

Il est généralement préférable de préciser les propriétés à animer plutôt que d'utiliser systématiquement transition: all.
.button { background: royalblue; transform: translateY(0); transition: background-color 200ms ease, transform 150ms ease, box-shadow 200ms ease; } .button:hover { background: #1d4ed8; transform: translateY(-2px); box-shadow: 0 8px 20px rgb(37 99 235 / 35%); }
Une timing function contrôle la vitesse d'évolution de l'animation.

Valeurs courantes :

linear
ease
ease-in
ease-out
ease-in-out

cubic-bezier() permet de créer une courbe personnalisée.

steps() permet au contraire une animation par étapes discrètes.
.panel { transition: transform 400ms cubic-bezier(0.22, 1, 0.36, 1); } .loader { animation: spin 1s linear infinite; } .sprite { animation: walk 800ms steps(8) infinite; }
@keyframes décrit les étapes d'une animation.

On peut utiliser :

from / to

ou des pourcentages :

0%, 50%, 100%.

La propriété animation relie ensuite les keyframes à l'élément.
@keyframes fade-in-up { 0% { opacity: 0; transform: translateY(20px); } 100% { opacity: 1; transform: translateY(0); } } .message { animation: fade-in-up 600ms ease-out; }
Une animation possède plusieurs propriétés.

animation-name
animation-duration
animation-delay
animation-iteration-count
animation-direction
animation-fill-mode
animation-play-state

Le raccourci animation permet de regrouper ces valeurs.

forwards conserve notamment les valeurs finales définies par les keyframes.
.loader { animation-name: spin; animation-duration: 1s; animation-timing-function: linear; animation-iteration-count: infinite; } .pulse { animation: pulse 800ms ease-in-out infinite alternate; } .paused { animation-play-state: paused; }
filter applique un traitement visuel à l'élément lui-même.

Fonctions courantes :

blur()
brightness()
contrast()
grayscale()
saturate()
hue-rotate()
invert()
drop-shadow()

Plusieurs filtres peuvent être combinés.
.photo { filter: grayscale(100%); transition: filter 300ms ease; } .photo:hover { filter: grayscale(0%); } .icon { filter: drop-shadow( 0 0 8px cyan ); } .image { filter: brightness(0.8) contrast(1.1) saturate(1.2); }
backdrop-filter applique un filtre à ce qui se trouve derrière l'élément.

Il est souvent combiné à un fond semi-transparent.

C'est la base de nombreux effets de type glassmorphism.

Évitez cependant les valeurs de flou excessives sur de grandes zones si l'effet n'apporte rien à l'interface.
.glass-card { background: rgb(255 255 255 / 8%); backdrop-filter: blur(16px) saturate(150%); border: 1px solid rgb(255 255 255 / 12%); } .topbar { background: rgb(10 10 18 / 80%); backdrop-filter: blur(10px); }
box-shadow ajoute une ombre autour de la boîte d'un élément.

text-shadow ajoute une ombre au texte.

Une ombre peut être :

externe,
interne avec inset,
douce,
colorée,
utilisée comme effet lumineux.

Plusieurs ombres peuvent être séparées par des virgules.
.card { box-shadow: 0 12px 30px rgb(0 0 0 / 20%); } .input { box-shadow: inset 0 1px 4px rgb(0 0 0 / 15%); } .neon-title { color: cyan; text-shadow: 0 0 8px cyan, 0 0 20px rgb(0 210 255 / 60%); }
clip-path découpe une partie de l'élément.

Formes fréquentes :

circle()
ellipse()
inset()
polygon()

Les propriétés de masque permettent des effets plus avancés avec des images ou gradients servant de masque.
/* Cercle */ .avatar { clip-path: circle(50%); } /* Hexagone */ .hexagon { clip-path: polygon( 50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25% ); } /* Masque avec gradient */ .fade-image { mask-image: linear-gradient( to bottom, black 70%, transparent ); }
mix-blend-mode contrôle la façon dont un élément se mélange avec ce qui se trouve derrière lui.

Exemples :

multiply
screen
overlay
difference

Ces effets peuvent être intéressants pour des créations graphiques, mais doivent rester lisibles.
.artwork-title { color: white; mix-blend-mode: difference; } .overlay-shape { background: royalblue; mix-blend-mode: multiply; }
@starting-style permet de définir les valeurs initiales utilisées lorsqu'un élément apparaît pour la première fois.

C'est particulièrement utile pour :

popovers,
dialogs,
éléments nouvellement affichés.

Il permet d'obtenir certaines transitions d'entrée sans devoir créer une animation @keyframes complète.
[popover]:popover-open { opacity: 1; transform: scale(1); transition: opacity 200ms ease, transform 200ms ease; } @starting-style { [popover]:popover-open { opacity: 0; transform: scale(0.95); } }
CSS permet désormais de relier la progression de certaines animations au défilement plutôt qu'au temps.

animation-timeline: view() peut faire progresser l'animation selon la position de l'élément dans la zone visible.

scroll() permet de baser la progression sur le défilement d'un conteneur.

Cette partie est plus avancée : vérifiez la compatibilité navigateur avant de l'utiliser comme fonctionnalité indispensable.
@keyframes reveal { from { opacity: 0; transform: translateY(40px); } to { opacity: 1; transform: translateY(0); } } .reveal { animation-name: reveal; animation-timing-function: linear; animation-timeline: view(); }
Certains utilisateurs demandent à leur système de réduire les mouvements non essentiels.

CSS peut détecter cette préférence avec :

prefers-reduced-motion: reduce.

Les grands déplacements, rotations ou zooms peuvent alors être réduits ou remplacés.

Une transition légère de couleur peut parfois être conservée.
.card { transition: transform 300ms ease, opacity 300ms ease; } .card:hover { transform: translateY(-8px) scale(1.03); } @media (prefers-reduced-motion: reduce) { .card { transition: opacity 150ms ease; } .card:hover { transform: none; } }
Toutes les propriétés CSS n'ont pas le même coût de rendu.

Pour beaucoup d'animations d'interface, transform et opacity sont de bons candidats.

Évitez d'animer inutilement des propriétés influençant fortement la mise en page.

will-change peut indiquer au navigateur qu'une propriété va bientôt changer.

Mais il ne doit pas être ajouté partout : utilisez-le uniquement lorsque cela répond à un véritable besoin.
/* Préférable pour un déplacement visuel */ .panel { transform: translateX(0); transition: transform 300ms ease; } .panel.closed { transform: translateX(-100%); } /* À utiliser avec modération */ .complex-animation { will-change: transform; }
Les animations doivent aider à comprendre l'interface plutôt que simplement attirer l'attention.

1. Utiliser des durées relativement courtes pour les interactions simples.

2. Animer uniquement les propriétés nécessaires.

3. Éviter les mouvements excessifs.

4. Respecter prefers-reduced-motion.

5. Éviter de multiplier les gros flous, filtres et effets graphiques sans nécessité.

6. Une animation doit idéalement expliquer un changement d'état, attirer l'attention sur une information utile ou améliorer la continuité visuelle.
/* Interaction simple */ .button { transition: transform 150ms ease, background-color 200ms ease; } .button:hover { transform: translateY(-2px); } .button:active { transform: scale(0.97); } @media (prefers-reduced-motion: reduce) { .button { transition: background-color 150ms ease; } .button:hover, .button:active { transform: none; } }

Arrière-plans & Couleurs

CSS permet de créer des couleurs, images de fond, dégradés, superpositions et effets visuels complexes. Les propriétés background contrôlent la taille, la position, la répétition, les couches et la zone d'affichage du fond.

CSS accepte plusieurs formats pour représenter une couleur.

HEX : #2563eb

RGB : rgb(37 99 235)

HSL : hsl(221 83% 53%)

Une transparence peut être ajoutée avec une barre oblique :

rgb(37 99 235 / 50%)

CSS propose aussi des espaces colorimétriques modernes comme oklch(), utiles notamment pour construire des palettes plus régulières.
/* HEX */ .box-a { background-color: #2563eb; } /* RGB moderne */ .box-b { background-color: rgb(37 99 235); } /* RGB avec transparence */ .box-c { background-color: rgb(37 99 235 / 40%); } /* HSL */ .box-d { background-color: hsl(221 83% 53%); } /* Couleur moderne */ .box-e { background-color: oklch(62% 0.2 255); }
background-color définit la couleur de fond d'un élément.

La valeur par défaut est transparent.

Une couleur de fond peut rester visible derrière une image possédant des zones transparentes.

Il est souvent préférable d'utiliser une couleur avec alpha plutôt que de modifier opacity, car opacity affecte aussi le contenu et les enfants.
body { background-color: #0a0a12; } .card { background-color: rgb(255 255 255 / 5%); } .transparent-panel { background-color: transparent; } /* Attention : affecte tout l'élément */ .faded { opacity: 0.5; }
background-image permet d'afficher une image derrière le contenu.

Une image de fond est généralement décorative.

Pour une image importante pour le contenu, une véritable balise <img> est souvent préférable.

background-image accepte également les gradients CSS.
.hero { background-image: url("hero.jpg"); } .texture { background-image: url("pattern.png"); } .gradient { background-image: linear-gradient( 135deg, #2563eb, #7c3aed ); }
background-size contrôle la taille de l'image de fond.

cover : remplit entièrement la zone. Une partie de l'image peut être coupée.

contain : affiche toute l'image, mais peut laisser de l'espace libre.

On peut également définir une taille précise ou utiliser des pourcentages.
/* Remplit toute la zone */ .hero { background-size: cover; } /* Image entière visible */ .logo-background { background-size: contain; } /* Pattern */ .pattern { background-size: 120px 120px; } /* Largeur fixe, hauteur automatique */ .illustration { background-size: 300px auto; }
background-position détermine la position de l'image dans son conteneur.

Valeurs courantes :

center
center top
right bottom
50% 50%

Avec background-size: cover, cette propriété devient importante, car certaines zones de l'image peuvent être coupées.
.hero { background-size: cover; background-position: center; } .portrait { background-position: center top; } .custom-position { background-position: 80% 20%; }
background-repeat contrôle la répétition d'une image.

repeat répète dans les deux directions.

no-repeat empêche toute répétition.

repeat-x répète horizontalement.

repeat-y répète verticalement.

CSS propose aussi space et round pour répartir les motifs plus proprement.
.hero { background-repeat: no-repeat; } .horizontal-pattern { background-repeat: repeat-x; } .pattern { background-repeat: repeat; } .distributed-pattern { background-repeat: space; }
Les gradients sont générés directement par CSS et sont considérés comme des images.

linear-gradient() : dégradé suivant une direction.

radial-gradient() : dégradé depuis un point central.

conic-gradient() : dégradé tournant autour d'un centre.

Plusieurs étapes de couleur peuvent être définies.
/* Linéaire */ .gradient-linear { background: linear-gradient( 135deg, #00d2ff, #8b5cf6 ); } /* Radial */ .gradient-radial { background: radial-gradient( circle at center, rgb(0 210 255 / 35%), transparent 70% ); } /* Conique */ .gradient-conic { background: conic-gradient( red, yellow, lime, cyan, blue, magenta, red ); }
CSS peut afficher plusieurs backgrounds sur le même élément.

Les différentes couches sont séparées par des virgules.

La première couche déclarée se trouve visuellement au-dessus des suivantes.

Cette technique permet notamment de créer un overlay sombre par-dessus une photographie.
.hero { background-image: linear-gradient( rgb(0 0 0 / 35%), rgb(0 0 0 / 75%) ), url("hero.jpg"); background-size: cover; background-position: center; } /* Plusieurs halos */ .page { background-image: radial-gradient( circle at top left, rgb(0 210 255 / 25%), transparent 35% ), radial-gradient( circle at bottom right, rgb(139 92 246 / 20%), transparent 40% ); }
La propriété background permet de définir plusieurs propriétés en une seule déclaration.

Elle peut inclure notamment :

couleur,
image,
position,
taille,
répétition,
attachement.

Dans la syntaxe raccourcie, la taille est placée après la position avec une barre oblique :

center / cover.
/* Version détaillée */ .hero-a { background-image: url("hero.jpg"); background-position: center; background-size: cover; background-repeat: no-repeat; } /* Version raccourcie */ .hero-b { background: url("hero.jpg") center / cover no-repeat; }
background-attachment détermine comment le fond réagit au défilement.

scroll : comportement normal.

fixed : le fond reste lié au viewport.

local : le fond suit le défilement interne de l'élément.

Les effets avec fixed doivent être utilisés avec prudence, notamment sur mobile.
.hero { background-image: url("mountains.jpg"); background-size: cover; background-position: center; background-attachment: fixed; } .scroll-box { overflow: auto; background-attachment: local; }
background-origin définit la zone utilisée comme référence pour positionner une image de fond.

background-clip détermine jusqu'où le background est peint.

Valeurs courantes :

border-box
padding-box
content-box

Ces propriétés sont particulièrement utiles lorsque l'élément possède bordures et padding.
.box { padding: 30px; border: 10px solid rgb(0 0 0 / 20%); background-image: url("texture.png"); background-origin: content-box; background-clip: padding-box; }
background-clip: text permet d'utiliser la forme du texte comme masque du background.

La couleur du texte doit devenir transparente pour laisser apparaître le background.

Cette technique est souvent utilisée pour créer des titres avec dégradé.
.gradient-title { background: linear-gradient( 90deg, #00d2ff, #8b5cf6 ); background-clip: text; color: transparent; }
background-blend-mode contrôle la façon dont plusieurs couches de background se mélangent.

Quelques modes :

multiply
screen
overlay
darken
lighten

Cette propriété peut créer des effets graphiques sans modifier directement l'image source.
.hero { background-image: linear-gradient( royalblue, purple ), url("city.jpg"); background-size: cover; background-position: center; background-blend-mode: multiply; }
currentColor représente la valeur actuelle de la propriété color.

Elle peut être utilisée dans :

bordures,
ombres,
SVG,
backgrounds,
décorations.

Cela permet de garder plusieurs éléments synchronisés avec une seule couleur principale.
.button { color: royalblue; border: 2px solid currentColor; } .button:hover { color: purple; } /* La bordure devient également violette */
color-mix() permet de mélanger deux couleurs directement en CSS.

C'est utile pour générer :

• variantes plus claires
• variantes plus sombres
• transparences
• états hover

Cette fonction fonctionne très bien avec les variables CSS.
:root { --primary: #2563eb; } .button { background: var(--primary); } .button:hover { background: color-mix( in srgb, var(--primary) 80%, black ); } .button-soft { background: color-mix( in srgb, var(--primary) 15%, transparent ); }
Quelques règles permettent d'éviter les problèmes fréquents.

1. Utiliser background-image surtout pour les images décoratives.

2. Pour une image importante pour le contenu, préférer une balise <img> avec un texte alternatif adapté.

3. Vérifier le contraste entre texte et arrière-plan.

4. Optimiser les images lourdes.

5. Éviter d'utiliser opacity uniquement pour rendre un background transparent, car les enfants deviennent eux aussi transparents.

6. Préférer une couleur avec alpha lorsque seul le fond doit être translucide.
/* ❌ Tout devient transparent */ .card-bad { background: white; opacity: 0.5; } /* ✅ Seul le fond est transparent */ .card-good { background: rgb(255 255 255 / 50%); color: #111827; } /* Image décorative */ .hero { background: linear-gradient( rgb(0 0 0 / 40%), rgb(0 0 0 / 65%) ), url("hero.webp") center / cover no-repeat; }

Pseudo-classes & Pseudo-éléments

Les pseudo-classes ciblent un élément selon son état, sa position ou sa relation avec d'autres éléments. Les pseudo-éléments permettent de styliser une partie particulière d'un élément ou de générer du contenu décoratif sans ajouter de balise HTML.

Une pseudo-classe utilise généralement un seul deux-points :

:hover
:focus
:first-child

Elle sélectionne un élément selon un état ou une condition.

Un pseudo-élément utilise généralement deux deux-points :

::before
::after
::first-letter

Il représente une partie virtuelle ou spécifique d'un élément.
/* Pseudo-classe */ button:hover { background: royalblue; } /* Pseudo-élément */ .title::after { content: ""; display: block; width: 50px; height: 3px; background: cyan; }
:hover cible un élément lorsqu'un pointeur le survole.

:active cible l'élément pendant son activation, par exemple pendant un clic.

:focus cible un élément ayant reçu le focus.

:focus-visible permet d'afficher un indicateur de focus lorsque le navigateur estime qu'il est utile, notamment lors de la navigation au clavier.

Il est important de conserver un focus visible pour l'accessibilité.
.button:hover { transform: translateY(-2px); } .button:active { transform: scale(0.98); } .button:focus-visible { outline: 3px solid dodgerblue; outline-offset: 3px; }
Les formulaires possèdent de nombreuses pseudo-classes utiles.

:checked → checkbox ou radio sélectionné.

:disabled → champ désactivé.

:enabled → champ actif.

:required → champ obligatoire.

:optional → champ facultatif.

:valid et :invalid permettent de réagir à la validation native du navigateur.
input:checked { accent-color: seagreen; } input:disabled { opacity: 0.5; cursor: not-allowed; } input:required { border-left: 3px solid orange; } input:valid { border-color: seagreen; } input:invalid { border-color: crimson; }
:first-child cible le premier enfant.

:last-child cible le dernier.

:only-child cible un élément qui est l'unique enfant de son parent.

:empty cible un élément ne contenant aucun enfant ni texte.

Ces pseudo-classes permettent souvent d'éviter des classes HTML supplémentaires.
.menu-item:first-child { border-radius: 12px 12px 0 0; } .menu-item:last-child { border-bottom: none; } .message:only-child { margin-block: 24px; } .notification:empty { display: none; }
:nth-child() sélectionne un élément selon sa position parmi tous les enfants du parent.

Exemples :

:nth-child(3) → troisième enfant.

:nth-child(odd) → positions impaires.

:nth-child(even) → positions paires.

:nth-child(3n) → tous les trois éléments.

:nth-child(n + 4) → à partir du quatrième.
/* Lignes paires */ tr:nth-child(even) { background: rgba(0, 0, 0, 0.04); } /* Troisième carte */ .card:nth-child(3) { border-color: gold; } /* Tous les 3 éléments */ .item:nth-child(3n) { font-weight: bold; } /* À partir du quatrième */ .item:nth-child(n + 4) { opacity: 0.7; }
:nth-of-type() ressemble à :nth-child(), mais il tient compte du type d'élément.

Par exemple :

p:nth-of-type(2) cible le deuxième paragraphe parmi les paragraphes frères.

On trouve également :

:first-of-type
:last-of-type
:only-of-type.
/* Deuxième paragraphe */ article p:nth-of-type(2) { font-weight: bold; } /* Premier h2 */ section h2:first-of-type { margin-top: 0; } /* Dernière image */ .gallery img:last-of-type { border: 2px solid royalblue; }
:not() exclut certains éléments.

:is() permet de regrouper plusieurs sélecteurs dans une expression plus concise.

:where() fonctionne de manière similaire à :is(), mais n'ajoute aucune spécificité propre.

Cela le rend particulièrement intéressant pour les styles de base facilement surchargeables.
/* Tous sauf .primary */ button:not(.primary) { opacity: 0.85; } /* Regrouper plusieurs titres */ article :is(h2, h3, h4) { line-height: 1.2; } /* Faible spécificité */ :where(.card, .panel, .box) { border-radius: 16px; }
:has() permet de sélectionner un élément en fonction d'une relation avec d'autres éléments.

Il peut notamment cibler un parent selon son contenu.

Exemple :

.card:has(img) sélectionne une carte contenant une image.

Il peut aussi réagir à l'état d'un enfant, ce qui permet certains comportements autrefois difficiles à obtenir sans JavaScript.
/* Carte contenant une image */ .card:has(img) { padding-top: 0; } /* Formulaire contenant une erreur */ form:has(input:invalid) { border-color: crimson; } /* Label d'une checkbox cochée */ label:has(input:checked) { font-weight: bold; } /* Section contenant .featured */ section:has(.featured) { background: #fff8dc; }
::before et ::after créent des pseudo-éléments associés à un élément.

Ils sont très utilisés pour :

• décorations
• lignes
• badges
• overlays
• icônes visuelles

La propriété content doit généralement être définie, même avec une chaîne vide.

Évitez d'y placer du contenu essentiel à la compréhension de la page : le HTML reste préférable pour le contenu important.
.title::after { content: ""; display: block; width: 60px; height: 3px; margin-top: 8px; background: cyan; } .external-link::after { content: " ↗"; }
::first-letter permet de styliser la première lettre d'un bloc de texte.

::first-line cible la première ligne affichée.

La première ligne dépend de la largeur disponible : son contenu peut donc changer avec le responsive.
.article-intro::first-letter { font-size: 3rem; font-weight: bold; float: left; margin-right: 8px; } .article-intro::first-line { font-weight: 600; }
::selection permet de styliser le texte sélectionné par l'utilisateur.

::placeholder cible le texte indicatif affiché dans certains champs de formulaire.

Pour un placeholder, veillez à conserver un contraste suffisant.

Un placeholder ne doit pas remplacer un véritable label de formulaire.
::selection { background: rgba(0, 210, 255, 0.35); color: #ffffff; } input::placeholder { color: #777777; } textarea::placeholder { font-style: italic; }
::marker cible le marqueur d'un élément de liste.

Il peut être utilisé avec les listes <ul> et <ol>.

Cela permet de personnaliser la couleur, la taille ou certains aspects des puces et numéros sans recréer toute la liste manuellement.
li::marker { color: royalblue; font-size: 1.2em; } ol li::marker { font-weight: bold; }
::file-selector-button permet de styliser le bouton natif d'un champ :

<input type="file">.

Il évite certaines techniques anciennes consistant à masquer complètement le contrôle natif.
input[type="file"]::file-selector-button { border: 0; padding: 10px 16px; border-radius: 8px; background: royalblue; color: white; cursor: pointer; } input[type="file"]::file-selector-button:hover { background: #1d4ed8; }
::backdrop représente la couche située derrière certains éléments affichés au-dessus de la page.

Il est notamment utilisé avec :

<dialog> affiché avec showModal().

Il permet de créer facilement un voile sombre ou un effet de flou.
dialog { border: 0; border-radius: 16px; padding: 24px; } dialog::backdrop { background: rgba(0, 0, 0, 0.65); backdrop-filter: blur(4px); }
Les pseudo-classes et pseudo-éléments peuvent réduire fortement le nombre de classes et de balises nécessaires.

Quelques principes :

1. Ne pas dépendre uniquement de :hover, car les écrans tactiles n'ont pas toujours de véritable état de survol.

2. Conserver un focus clavier visible.

3. Ne pas placer des informations essentielles uniquement dans ::before ou ::after.

4. Utiliser :has(), :is() et :where() pour simplifier les sélecteurs plutôt que les rendre excessivement complexes.

5. Préférer le HTML sémantique lorsque l'information possède un véritable sens dans le document.
/* ❌ Ne pas cacher tout focus */ button:focus { /* outline: none; */ } /* ✅ Focus clavier visible */ button:focus-visible { outline: 3px solid royalblue; outline-offset: 3px; } /* Décoration : très bien en pseudo-élément */ .heading::after { content: ""; display: block; width: 50px; height: 3px; background: royalblue; }

Responsive & Media Queries

Le responsive design permet d'adapter une interface à l'espace disponible, aux capacités de l'appareil et aux préférences de l'utilisateur. Media queries, tailles fluides, Grid, Container Queries et unités modernes permettent aujourd'hui de créer des interfaces beaucoup plus adaptatives.

Une media query applique des règles CSS uniquement lorsque certaines conditions sont remplies.

Elles peuvent tester :

• la largeur
• la hauteur
• l'orientation
• le type de pointeur
• la présence du survol
• les préférences utilisateur

Le responsive ne se limite donc pas à « mobile / tablette / ordinateur ».
/* Style de base */ .grid { display: grid; grid-template-columns: 1fr; gap: 16px; } /* Écrans plus larges */ @media (min-width: 768px) { .grid { grid-template-columns: repeat(2, 1fr); } } @media (min-width: 1200px) { .grid { grid-template-columns: repeat(4, 1fr); } }
L'approche mobile first consiste à écrire les styles de base pour les petits écrans, puis à ajouter des adaptations lorsque davantage d'espace devient disponible.

On utilise généralement :

min-width

Les breakpoints ne devraient pas être choisis uniquement parce qu'ils correspondent à un modèle d'appareil.

Il est préférable de les ajouter lorsque le contenu ou la mise en page en a réellement besoin.
/* Base : petit écran */ .navigation { display: flex; flex-direction: column; gap: 12px; } /* Quand il y a assez de place */ @media (min-width: 700px) { .navigation { flex-direction: row; align-items: center; } }
Les media queries modernes peuvent utiliser une syntaxe de comparaison plus naturelle.

Au lieu de :

(min-width: 768px)

on peut écrire :

(width >= 768px).

On peut également définir une plage :

600px <= width < 1000px.
/* Syntaxe classique */ @media (min-width: 768px) { .sidebar { display: block; } } /* Syntaxe moderne équivalente */ @media (width >= 768px) { .sidebar { display: block; } } /* Entre 600px et 1000px */ @media (600px <= width < 1000px) { .layout { grid-template-columns: 1fr 1fr; } }
Une mise en page responsive ne nécessite pas toujours une media query.

width, max-width et les marges automatiques suffisent souvent pour créer un conteneur fluide.

Une technique moderne consiste à utiliser min() afin de limiter automatiquement la largeur.
/* Technique classique */ .container { width: 100%; max-width: 1200px; margin-inline: auto; padding-inline: 16px; } /* Variante moderne */ .container-modern { width: min(1200px, 100% - 32px); margin-inline: auto; } /* Largeur de lecture confortable */ .article { max-width: 70ch; margin-inline: auto; }
clamp() définit une valeur entre un minimum et un maximum.

Syntaxe :

clamp(minimum, valeur idéale, maximum).

min() choisit la plus petite valeur.

max() choisit la plus grande.

Ces fonctions permettent de créer des tailles naturellement fluides.
/* Titre fluide */ h1 { font-size: clamp(2rem, 5vw, 4.5rem); } /* Espacement fluide */ section { padding-block: clamp(32px, 8vw, 120px); } /* Largeur limitée */ .panel { width: min(600px, 100%); } /* Minimum garanti */ .button { min-height: max(44px, 3rem); }
CSS Grid peut adapter automatiquement le nombre de colonnes disponibles.

minmax() : définit une taille minimale et maximale.

auto-fit : crée autant de colonnes que possible et étire les colonnes existantes.

Cela permet parfois de supprimer complètement plusieurs media queries.
.cards { display: grid; grid-template-columns: repeat( auto-fit, minmax( min(250px, 100%), 1fr ) ); gap: 24px; }
Une media query observe généralement la fenêtre du navigateur.

Une Container Query permet au contraire à un composant de réagir à la taille de son propre conteneur.

C'est particulièrement utile pour les composants réutilisables placés dans différentes zones : sidebar, contenu principal, dashboard...

Le parent doit généralement déclarer :

container-type: inline-size.
.card-container { container-type: inline-size; } .product-card { display: grid; gap: 16px; } @container (width > 500px) { .product-card { grid-template-columns: 180px 1fr; } }
Les Container Queries disposent également de leurs propres unités.

cqw correspond à 1 % de la largeur du conteneur.

cqh correspond à 1 % de sa hauteur.

On trouve également :

cqi, cqb, cqmin, cqmax.

Elles permettent de rendre un composant fluide selon son espace disponible plutôt que selon le viewport.
.card-container { container-type: inline-size; } .card-title { font-size: clamp( 1.2rem, 6cqw, 2.5rem ); }
aspect-ratio définit une proportion entre largeur et hauteur.

Il est très utile pour :

• vidéos
• images
• cartes
• galeries
• placeholders

Associé à object-fit, il permet de conserver un cadrage propre des images.
.video { aspect-ratio: 16 / 9; width: 100%; } .avatar { aspect-ratio: 1 / 1; width: 120px; object-fit: cover; border-radius: 50%; } .product-image { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
vw et vh représentent un pourcentage du viewport.

Sur mobile, les barres du navigateur peuvent modifier l'espace réellement disponible.

CSS propose donc plusieurs unités modernes :

svh : petit viewport.

lvh : grand viewport.

dvh : viewport dynamique.

dvh est particulièrement pratique pour les interfaces devant suivre la hauteur réellement disponible.
/* Hauteur classique */ .hero { min-height: 100vh; } /* Hauteur dynamique sur mobile */ .app { min-height: 100dvh; } /* Petit viewport garanti */ .modal { max-height: 90svh; overflow-y: auto; }
Une media query peut détecter l'orientation de la zone d'affichage.

Valeurs :

portrait
landscape

Cette condition peut être utile pour certaines interfaces multimédias ou mises en page très visuelles.

Elle ne doit cependant pas remplacer une mise en page naturellement flexible.
@media (orientation: landscape) { .gallery { grid-template-columns: repeat(3, 1fr); } } @media (orientation: portrait) { .gallery { grid-template-columns: 1fr; } }
Les media queries peuvent tester les capacités d'interaction plutôt que la taille de l'écran.

hover : indique si le périphérique principal permet facilement le survol.

pointer : indique la précision du périphérique de pointage.

Valeurs courantes :

fine → pointeur précis
coarse → pointeur moins précis, souvent tactile

C'est plus fiable que de supposer « petit écran = tactile ».
/* Appareil capable de survol */ @media (hover: hover) { .card:hover { transform: translateY(-4px); } } /* Pointeur tactile / moins précis */ @media (pointer: coarse) { button { min-width: 44px; min-height: 44px; } }
Certains utilisateurs demandent au système de réduire les animations.

La media query prefers-reduced-motion permet de respecter cette préférence.

Cela améliore l'accessibilité, notamment pour certaines personnes sensibles aux mouvements importants.

Il n'est pas toujours nécessaire de supprimer absolument toutes les animations : on peut simplement réduire celles qui sont décoratives ou très prononcées.
.card { transition: transform 300ms ease; } .card:hover { transform: translateY(-8px); } @media (prefers-reduced-motion: reduce) { .card { transition: none; } .card:hover { transform: none; } }
Les propriétés logiques permettent de raisonner selon le sens du contenu plutôt qu'avec gauche / droite / haut / bas.

Exemples :

margin-inline
margin-block
padding-inline
padding-block
inline-size
block-size

Elles facilitent les interfaces adaptatives et les langues utilisant d'autres directions d'écriture.
.container { max-inline-size: 1200px; margin-inline: auto; padding-inline: clamp(16px, 4vw, 40px); padding-block: 32px; }
Un bon responsive design ne consiste pas à créer une version différente pour chaque appareil.

1. Commencer par une mise en page naturellement fluide.

2. Utiliser Flexbox et Grid avant d'ajouter de nombreuses media queries.

3. Ajouter un breakpoint lorsque le contenu en a réellement besoin.

4. Utiliser clamp(), min() et max() pour les valeurs fluides.

5. Utiliser les Container Queries pour les composants autonomes.

6. Tester les petites et grandes tailles intermédiaires, pas seulement quelques appareils connus.

7. Respecter les préférences d'accessibilité et les capacités réelles du périphérique.
/* Exemple naturellement responsive */ .page { width: min(1200px, 100% - 32px); margin-inline: auto; } .cards { display: grid; grid-template-columns: repeat( auto-fit, minmax( min(260px, 100%), 1fr ) ); gap: clamp(16px, 3vw, 32px); } h1 { font-size: clamp(2rem, 6vw, 5rem); }

Variables CSS

Les variables CSS, aussi appelées custom properties, permettent de centraliser des couleurs, espacements, tailles, thèmes et valeurs réutilisables tout en profitant de la cascade CSS.

Une variable CSS est appelée custom property.

Son nom commence obligatoirement par :

--

Par exemple :

--color-primary
--space-md
--radius-card

Les variables peuvent contenir de nombreuses sortes de valeurs : couleurs, tailles, ombres, espacements, transitions, gradients ou même plusieurs valeurs CSS.
:root { --color-bg: #0a0a12; --color-primary: #00d2ff; --color-text: #f8fafc; --space-md: 16px; --radius-card: 16px; --shadow-soft: 0 12px 40px rgba(0, 0, 0, 0.25); --transition-fast: 200ms ease; }
La fonction var() permet d'utiliser une custom property.

Syntaxe :

var(--nom)

Lorsque la variable change, tous les éléments qui l'utilisent sont automatiquement recalculés par le navigateur.

Cela permet de centraliser le design au lieu de répéter les mêmes valeurs partout.
body { background: var(--color-bg); color: var(--color-text); } .card { padding: var(--space-md); border-radius: var(--radius-card); box-shadow: var(--shadow-soft); } .button { background: var(--color-primary); }
var() accepte une valeur de secours.

Syntaxe :

var(--variable, fallback)

Le fallback est utilisé si la custom property demandée n'est pas définie ou n'est pas exploitable.

Il est également possible d'imbriquer plusieurs variables de secours.
.button { background: var(--btn-bg, #00d2ff); color: var(--btn-color, #000); border-radius: var(--btn-radius, 8px); } /* Fallback imbriqué */ .title { color: var( --title-color, var(--color-text, #ffffff) ); }
Les custom properties participent à la cascade CSS.

Une variable déclarée dans :root est généralement disponible dans toute la page.

Une variable déclarée sur un composant est disponible sur cet élément et peut être héritée par ses descendants.

Un descendant peut également redéfinir la même variable.

Cela permet de modifier localement le comportement ou l'apparence d'un composant.
/* Globale */ :root { --card-bg: #1e293b; } .card { background: var(--card-bg); } /* Surcharge locale */ .card.warning { --card-bg: #7c2d12; } /* Les descendants héritent de la valeur */ .card .content { background: var(--card-bg); }
Une custom property peut être redéfinie comme n'importe quelle autre propriété CSS.

Cela permet de créer un composant générique configurable avec quelques variables.

On évite ainsi de recopier tout le CSS simplement pour changer une couleur ou une taille.
.button { --button-bg: royalblue; --button-text: white; background: var(--button-bg); color: var(--button-text); } .button-danger { --button-bg: crimson; } .button-success { --button-bg: seagreen; }
Une variable CSS peut être utilisée dans calc().

Cela permet de construire un système cohérent d'espacements ou de tailles.

Une variable peut également contenir uniquement un nombre et être combinée avec une unité dans certains calculs.

Les fonctions modernes comme min(), max() et clamp() peuvent elles aussi utiliser des variables.
:root { --space: 8px; --content-width: 1200px; } .card { padding: calc(var(--space) * 3); gap: calc(var(--space) * 2); } main { width: min( 100% - 32px, var(--content-width) ); margin-inline: auto; } h1 { font-size: clamp( 2rem, 5vw, 4rem ); }
Les variables CSS peuvent être redéfinies dans une media query.

Plutôt que de modifier de nombreuses propriétés, on peut simplement changer quelques variables.

Le reste du CSS continue d'utiliser var() sans changement.
:root { --page-padding: 16px; --card-gap: 12px; } .container { padding: var(--page-padding); gap: var(--card-gap); } @media (min-width: 768px) { :root { --page-padding: 32px; --card-gap: 24px; } }
Les variables CSS sont particulièrement adaptées à la gestion des thèmes.

On définit une palette par défaut, puis on surcharge seulement les variables nécessaires.

Un attribut comme :

data-theme="dark"

permet de séparer clairement le thème du reste des classes CSS.
:root { --bg: #ffffff; --text: #111827; --surface: #f1f5f9; } [data-theme="dark"] { --bg: #0a0a12; --text: #f8fafc; --surface: #1e293b; } body { background: var(--bg); color: var(--text); } .card { background: var(--surface); }
La media query prefers-color-scheme permet de connaître la préférence clair/sombre configurée dans le système de l'utilisateur.

Les variables CSS permettent ensuite de remplacer toute la palette avec quelques lignes.

La propriété color-scheme peut également informer le navigateur des thèmes pris en charge.
:root { color-scheme: light dark; --bg: #ffffff; --text: #111827; } @media (prefers-color-scheme: dark) { :root { --bg: #0a0a12; --text: #f8fafc; } }
JavaScript peut lire ou modifier une variable CSS.

setProperty() : définit une custom property.

getPropertyValue() : permet de lire sa valeur.

Cette technique est utile pour : thèmes, sliders, couleurs personnalisables, tailles dynamiques ou composants interactifs.
// Modifier une variable globale document.documentElement .style .setProperty( "--color-primary", "#ff3e8a" ); // Lire la valeur calculée const styles = getComputedStyle( document.documentElement ); const primaryColor = styles .getPropertyValue( "--color-primary" ) .trim(); console.log( primaryColor );
@property permet de déclarer plus précisément une custom property.

On peut définir :

syntax → type de valeur attendu.

inherits → indique si la propriété doit être héritée.

initial-value → valeur initiale.

Cela est notamment utile pour créer des animations plus fiables avec des variables CSS.
@property --rotation { syntax: "<angle>"; inherits: false; initial-value: 0deg; } .icon { --rotation: 0deg; transform: rotate(var(--rotation)); transition: --rotation 300ms ease; } .icon:hover { --rotation: 180deg; }
Dans un projet important, les variables CSS peuvent devenir un véritable système de design.

Il est préférable d'utiliser des noms décrivant le rôle de la valeur.

Exemples :

--color-primary
--color-danger
--space-sm
--space-md
--radius-card
--font-size-title

Une organisation cohérente facilite les changements globaux et la maintenance.
:root { /* Couleurs */ --color-primary: #2563eb; --color-success: #16a34a; --color-danger: #dc2626; /* Espacements */ --space-xs: 4px; --space-sm: 8px; --space-md: 16px; --space-lg: 32px; /* Bordures */ --radius-sm: 6px; --radius-card: 16px; /* Typographie */ --font-size-body: 1rem; --font-size-title: 2rem; }
Les variables CSS sont particulièrement utiles pour les valeurs réellement réutilisées.

Évitez de créer une variable pour chaque propriété simplement pour tout transformer en variable.

Préférez des noms décrivant le rôle :

--color-primary plutôt que --blue.

Utilisez des variables globales pour les valeurs communes au design.

Utilisez des variables locales pour rendre les composants configurables.

N'oubliez pas que les custom properties suivent la cascade et l'héritage CSS : cela fait partie de leur puissance.
/* Moins flexible */ :root { --blue: #2563eb; } /* Plus explicite */ :root { --color-primary: #2563eb; --color-primary-hover: #1d4ed8; } .button-primary { background: var(--color-primary); } .button-primary:hover { background: var(--color-primary-hover); }

Sélecteurs CSS

Les sélecteurs CSS permettent de cibler précisément les éléments HTML auxquels appliquer des styles : éléments, classes, identifiants, relations, attributs, états, positions et sélecteurs modernes.

Sélecteur d'élément : cible toutes les balises d'un type donné.

p cible tous les paragraphes.

Classe : commence par un point.

.card cible tous les éléments ayant la classe card.

ID : commence par #.

#hero cible l'élément ayant l'identifiant hero.

Pour les styles réutilisables, les classes sont généralement préférables aux IDs.
/* Toutes les balises p */ p { line-height: 1.6; } /* Tous les éléments .card */ .card { padding: 24px; border-radius: 12px; } /* Élément unique #hero */ #hero { min-height: 100vh; }
* : sélecteur universel. Il peut cibler tous les éléments.

Il est souvent utilisé dans les règles globales comme le réglage de box-sizing.

, : permet de grouper plusieurs sélecteurs partageant les mêmes règles CSS.

Cela évite de répéter le même bloc de propriétés.
/* Tous les éléments */ * { box-sizing: border-box; } /* Plusieurs sélecteurs */ h1, h2, h3 { font-family: sans-serif; } /* Plusieurs classes */ .success, .warning, .error { padding: 12px; }
Un espace entre deux sélecteurs signifie :

« un élément situé quelque part à l'intérieur d'un autre ».

Par exemple :

.card p cible tous les paragraphes contenus dans .card.

Il n'est pas nécessaire que le paragraphe soit un enfant direct.
.card p { color: #d8d8d8; } nav a { text-decoration: none; } .profile .avatar img { border-radius: 50%; }
> : sélectionne uniquement les enfants directs.

+ : sélectionne le premier frère situé immédiatement après.

~ : sélectionne les frères suivants correspondant au sélecteur.

Ces opérateurs sont appelés combinateurs CSS.
/* Enfant direct */ .menu > li { list-style: none; } /* Premier p après un h2 */ h2 + p { margin-top: 8px; } /* Tous les p suivants */ .alert ~ p { opacity: 0.8; }
Les sélecteurs d'attribut permettent de cibler les éléments selon leurs attributs HTML.

[disabled] → attribut présent.

[type="email"] → valeur exacte.

[href^="https"] → commence par.

[href$=".pdf"] → se termine par.

[class*="card"] → contient une chaîne.

[class~="active"] → contient le mot complet.

Ils sont très utiles avec les formulaires, les liens et les attributs data-*.
input[type="email"] { border-color: cyan; } a[href^="https"] { text-decoration: underline; } a[href$=".pdf"] { font-weight: bold; } button[disabled] { opacity: 0.5; cursor: not-allowed; } [data-role="admin"] { border: 2px solid gold; }
Une pseudo-classe commence par :.

Elle permet de cibler un élément selon son état.

Exemples courants :

:hover → survol souris
:focus → élément ayant le focus
:focus-visible → focus visuellement pertinent
:active → activation
:checked → checkbox/radio sélectionné
:disabled → champ désactivé
:required → champ obligatoire
:valid / :invalid → validation formulaire
button:hover { transform: translateY(-2px); } input:focus { border-color: dodgerblue; } button:focus-visible { outline: 3px solid orange; } input:checked { accent-color: green; } input:invalid { border-color: red; }
Les pseudo-classes structurelles ciblent un élément selon sa position parmi ses frères.

:first-child → premier enfant.

:last-child → dernier enfant.

:only-child → seul enfant.

:nth-child() permet des sélections avancées.

Exemples :

:nth-child(2)
:nth-child(odd)
:nth-child(even)
:nth-child(3n)

:nth-of-type() fonctionne selon le type de balise.
li:first-child { font-weight: bold; } li:last-child { border-bottom: none; } /* Lignes impaires */ tr:nth-child(odd) { background: #f5f5f5; } /* Une ligne sur trois */ li:nth-child(3n) { color: tomato; } /* Deuxième paragraphe */ p:nth-of-type(2) { font-weight: bold; }
:not() : sélectionne les éléments ne correspondant pas au sélecteur indiqué.

:is() : permet de regrouper plusieurs alternatives dans un sélecteur plus compact.

:where() : fonctionne de manière similaire à :is(), mais sa propre spécificité est toujours nulle.

:where() est donc très pratique pour définir des styles faciles à surcharger.
/* Tous les boutons sauf .secondary */ button:not(.secondary) { background: royalblue; } /* Plus compact */ article :is(h2, h3, h4) { margin-bottom: 12px; } /* Même logique avec faible spécificité */ :where(header, main, footer) a { color: inherit; }
:has() permet de sélectionner un élément en fonction de ce qu'il contient ou de sa relation avec d'autres éléments.

Par exemple :

.card:has(img) cible une carte contenant une image.

form:has(input:invalid) cible un formulaire contenant un champ invalide.

Cela permet de résoudre certains cas qui nécessitaient autrefois JavaScript.
/* Carte contenant une image */ .card:has(img) { padding-top: 0; } /* Formulaire avec une erreur */ form:has(input:invalid) { border-color: red; } /* Article contenant un élément .featured */ article:has(.featured) { background: #fff8dc; } /* Label contenant une checkbox cochée */ label:has(input:checked) { font-weight: bold; }
Les pseudo-éléments utilisent généralement deux deux-points : ::.

Ils permettent de cibler une partie particulière d'un élément ou de créer un élément visuel virtuel.

Exemples :

::before
::after
::first-letter
::first-line
::selection
::placeholder

Avec ::before et ::after, la propriété content est généralement nécessaire.
.required::after { content: " *"; color: red; } .link::before { content: "→ "; } p::first-letter { font-size: 2rem; } ::selection { background: gold; color: black; } input::placeholder { color: #888; }
Lorsque plusieurs sélecteurs sont écrits sans espace, ils doivent tous correspondre au même élément.

.card.active signifie :

« un élément ayant à la fois les classes card et active ».

On peut combiner :

élément + classe,
plusieurs classes,
attribut + pseudo-classe,
etc.
/* div ayant la classe card */ div.card { padding: 20px; } /* Deux classes sur le même élément */ .card.active { border-color: limegreen; } /* Input email obligatoire */ input[type="email"][required] { background: #fafafa; } /* Bouton primaire au survol */ button.primary:hover { transform: scale(1.03); }
Lorsque plusieurs règles ciblent le même élément, le navigateur utilise notamment la spécificité pour déterminer laquelle l'emporte.

De manière simplifiée :

ID est plus spécifique qu'une classe.

Une classe, pseudo-classe ou attribut est plus spécifique qu'un sélecteur d'élément.

Exemple :

#profile est plus spécifique que .profile, qui est plus spécifique que section.

Si deux règles ont la même spécificité, celle déclarée plus tard gagne généralement.

Il vaut mieux éviter d'augmenter inutilement la spécificité des sélecteurs.
/* Faible spécificité */ p { color: black; } /* Plus spécifique */ .message { color: blue; } /* Encore plus spécifique */ #important-message { color: red; } /* Préférer souvent des classes simples */ .button-danger { background: crimson; }
Quelques principes facilitent la maintenance du CSS.

1. Préférer les classes pour les composants réutilisables.

2. Éviter les sélecteurs excessivement longs.

3. Éviter de dépendre trop fortement de la structure HTML.

4. Garder une spécificité raisonnable.

5. Utiliser des noms de classes décrivant le rôle plutôt que l'apparence.

Par exemple :

.user-card est souvent préférable à .blue-box.
/* ❌ Très dépendant du HTML */ main section div article div p span { color: red; } /* ✅ Plus simple */ .error-message { color: red; } /* ✅ Composant identifiable */ .user-card { padding: 20px; } .user-card__title { font-weight: bold; }

Bases & Syntaxe

Les fondamentaux de JavaScript : syntaxe, variables, portée, opérateurs, expressions, chaînes modernes, commentaires et outils de débogage.

Un programme JavaScript est composé d'instructions et d'expressions.

Instruction : demande à JavaScript d'effectuer une action.

Expression : produit une valeur.

Les accolades { } permettent de créer des blocs de code.

Les points-virgules ; sont souvent facultatifs grâce à l'insertion automatique de points-virgules de JavaScript, mais utiliser une convention cohérente évite les ambiguïtés.

JavaScript est sensible à la casse : user et User sont deux identifiants différents.
// Une instruction const score = 10; // Une expression 10 + 5; // produit 15 // Bloc de code if (score > 5) { console.log( "Score supérieur à 5" ); } // Sensible à la casse const user = "Julien"; const User = "Marie"; // Deux variables différentes
Les commentaires ne sont pas exécutés par JavaScript.

// crée un commentaire sur une seule ligne.

/* ... */ permet d'écrire un commentaire sur plusieurs lignes.

Les commentaires sont utiles pour expliquer pourquoi une décision a été prise.

Évitez cependant de commenter chaque ligne lorsque le code est déjà évident.
// Commentaire sur une ligne const price = 19.99; /* Commentaire sur plusieurs lignes */ const tax = 0.20; // Explique la raison du calcul const total = price * (1 + tax);
Une variable associe un nom à une valeur.

const : choix recommandé lorsqu'une variable ne doit pas être réassignée.

let : utilisé lorsque la variable doit changer.

var : ancien système de déclaration. Il possède une portée de fonction plutôt qu'une portée de bloc et son comportement de hoisting peut être source de confusion.

Dans du JavaScript moderne :

utilisez const par défaut, puis let lorsque la réassignation est nécessaire.

Attention : const empêche la réassignation de la variable, mais ne rend pas automatiquement les objets ou tableaux immuables.
const name = "Julien"; // ❌ Impossible de réassigner // name = "Paul"; let counter = 0; counter = 1; // OK // Objet const : contenu modifiable const user = { name: "Julien" }; user.name = "Julie"; // Tableau const : contenu modifiable const numbers = [1, 2]; numbers.push( 3 ); // [1, 2, 3]
La portée détermine les endroits où une variable peut être utilisée.

Portée globale : variable accessible depuis une grande partie du programme.

Portée de fonction : variable définie dans une fonction.

Portée de bloc : variable définie entre accolades avec let ou const.

var ne respecte pas la portée de bloc.

Hoisting : certaines déclarations sont traitées avant l'exécution du code.

let et const existent avant leur ligne de déclaration, mais ne peuvent pas être utilisées avant celle-ci : cette zone est appelée Temporal Dead Zone.
const globalValue = 10; function example() { const localValue = 20; if (true) { let blockValue = 30; console.log( blockValue ); } // ❌ blockValue inaccessible ici } // ❌ localValue inaccessible ici
JavaScript fournit les opérateurs mathématiques classiques.

+ addition
- soustraction
* multiplication
/ division
% reste d'une division
** puissance

Les parenthèses permettent de contrôler l'ordre des opérations.

Attention : + sert également à concaténer des chaînes de caractères.
const a = 10; const b = 3; a + b; // 13 a - b; // 7 a * b; // 30 a / b; // 3.333... a % b; // 1 a ** b; // 1000 // Tester pair / impair const isEven = a % 2 === 0; // Priorité contrôlée const result = (10 + 5) * 2; // 30
L'opérateur = affecte une valeur à une variable.

Les opérateurs composés permettent de modifier une variable plus rapidement.

+=
-=
*=
/=
%=
**=

Par exemple :

x += 5 équivaut à : x = x + 5.
let score = 10; score += 5; // 15 score -= 3; // 12 score *= 2; // 24 score /= 4; // 6 score %= 4; // 2
++ : ajoute 1.

-- : retire 1.

Il existe une différence entre :

counter++ et ++counter.

En utilisation seule, le résultat final est identique.

Dans une expression :

le postfixe retourne d'abord l'ancienne valeur, alors que le préfixe retourne la nouvelle valeur.
let counter = 0; counter++; // 1 counter--; // 0 let a = 5; const oldValue = a++; // oldValue = 5 // a = 6 let b = 5; const newValue = ++b; // newValue = 6 // b = 6
Les template literals utilisent des backticks : ` `.

Ils permettent d'insérer une variable ou une expression directement avec :

${expression}

Ils peuvent également contenir plusieurs lignes.

Ils sont généralement plus lisibles que la concaténation de nombreuses chaînes avec l'opérateur +.
const name = "Julien"; const age = 39; // Ancienne concaténation const oldMessage = "Bonjour " + name + ", tu as " + age + " ans"; // Template literal const message = `Bonjour ${name}, tu as ${age} ans`; // Expression const nextYear = `L'année prochaine : ${age + 1} ans`; // Multiligne const html = ` <article class="card"> <h2>${name}</h2> <p>${age} ans</p> </article> `;
La console du navigateur est un outil essentiel pour développer en JavaScript.

Elle est généralement accessible depuis les outils de développement avec F12.

Méthodes utiles :

console.log()
console.warn()
console.error()
console.table()
console.group()
console.time()
console.timeEnd()

console.table() est particulièrement pratique pour inspecter des tableaux d'objets.
console.log( "Bonjour" ); console.warn( "Attention" ); console.error( "Une erreur est survenue" ); const users = [ { name: "Julien", age: 39 }, { name: "Marie", age: 32 } ]; console.table( users ); // Mesurer une opération console.time( "calcul" ); for ( let i = 0; i < 100000; i++ ) { // calcul } console.timeEnd( "calcul" );
Les noms de variables doivent expliquer leur rôle.

En JavaScript, la convention la plus utilisée pour les variables et fonctions est :

camelCase

Exemple : userName, totalPrice, isLoggedIn.

Les classes utilisent généralement : PascalCase.

Exemple : UserService.

Les constantes réellement fixes et globales peuvent utiliser UPPER_SNAKE_CASE.

Exemple : MAX_USERS.

Un nom ne peut pas commencer par un chiffre et ne peut pas utiliser un mot réservé comme class ou return.
// ✅ Bons noms const userName = "Julien"; const totalPrice = 99.99; const isLoggedIn = true; // Constante fixe const MAX_USERS = 100; // Classe class UserService { } // ❌ Peu lisible const x = 99; // ✅ Plus explicite const productPrice = 99;

Types de Données

JavaScript possède 8 grandes catégories de types : 7 types primitifs (string, number, bigint, boolean, undefined, symbol et null) ainsi que le type object. Comprendre les types est essentiel pour manipuler correctement les valeurs, les objets, les comparaisons et les conversions.

string : représente du texte.

Une chaîne peut être écrite avec :

"double quotes"
'simple quotes'
`template literals`

Les chaînes JavaScript sont immuables : une méthode ne modifie pas directement la chaîne originale.

Méthodes fréquentes :

toUpperCase(), toLowerCase(), trim(), includes(), startsWith(), endsWith(), slice(), split(), replace(), replaceAll().
const name = "Julien"; const message = `Bonjour ${name}`; name.length; // 6 name.toUpperCase(); // "JULIEN" " texte ".trim(); // "texte" name.includes( "ul" ); // true "a,b,c".split( "," ); // ["a", "b", "c"] name.slice( 0, 3 ); // "Jul" name.slice( -1 ); // "n"
number : représente les nombres entiers et décimaux.

JavaScript n'a pas de type séparé int ou float.

Valeurs numériques particulières :

Infinity
-Infinity
NaN

Méthodes utiles :

Number.isNaN()
Number.isFinite()
Number.isInteger()
Number.parseInt()
Number.parseFloat()

Pour les très grands entiers, utilisez BigInt.
const price = 29.99; const score = 100; Number( "42" ); // 42 Number.parseInt( "3.7" ); // 3 Number.parseFloat( "3.7" ); // 3.7 Number.isNaN( NaN ); // true Number.isInteger( 42 ); // true Math.round( 29.5 ); // 30 Math.floor( 3.9 ); // 3 Math.ceil( 3.1 ); // 4 Math.abs( -10 ); // 10 const dice = Math.floor( Math.random() * 6 ) + 1; // entier entre 1 et 6
BigInt : représente des entiers de taille arbitrairement grande.

On ajoute la lettre n après le nombre.

Number possède une limite de précision pour les grands entiers.

Cette limite est disponible avec :

Number.MAX_SAFE_INTEGER.

Attention : un BigInt et un Number ne peuvent pas être mélangés directement dans une opération arithmétique.
Number.MAX_SAFE_INTEGER; // 9007199254740991 const huge = 900719925474099123456789n; const result = huge + 10n; // ❌ TypeError // huge + 10 // Conversion explicite const value = huge + BigInt(10);
boolean : représente une valeur logique : true ou false.

Les booléens sont utilisés dans :

conditions, comparaisons, validations, états d'interface, permissions...

Boolean() permet de convertir explicitement une valeur.

Les principales valeurs falsy sont : false, 0, -0, 0n, "", null, undefined et NaN.
const active = true; const disabled = false; Boolean(0); // false Boolean(""); // false Boolean("JavaScript"); // true Boolean([]); // true Boolean({}); // true if (active) { console.log( "Compte actif" ); }
undefined : signifie généralement qu'aucune valeur n'a encore été définie.

Exemples :

variable non initialisée, propriété inexistante, fonction sans return.

null : représente généralement une absence de valeur définie volontairement par le développeur.

null et undefined sont deux types distincts.

null === undefined retourne false.
let user; console.log( user ); // undefined const object = {}; object.name; // undefined let token = null; // absence volontaire de token null === undefined; // false null == undefined; // true // Tester les deux en même temps if (user == null) { // null OU undefined }
Symbol : crée une valeur primitive unique.

Même deux Symbols créés avec la même description sont différents.

Ils peuvent être utilisés comme clés de propriétés d'un objet sans risque de collision avec une clé texte.

JavaScript possède également des Symbols intégrés, comme Symbol.iterator.
const id1 = Symbol( "id" ); const id2 = Symbol( "id" ); id1 === id2; // false const user = { name: "Julien", [id1]: 42 }; user[id1]; // 42
Un tableau permet de stocker plusieurs valeurs dans un ordre précis.

L'index commence à 0.

Les tableaux JavaScript sont des objets spécialisés.

Ainsi :

typeof [] retourne "object".

Pour savoir si une valeur est réellement un tableau, utilisez :

Array.isArray().

Un tableau peut techniquement contenir différents types, même si des données homogènes sont souvent plus simples à manipuler.
const users = [ "Alice", "Bob", "Julien" ]; users[0]; // "Alice" users.length; // 3 users.at(-1); // "Julien" typeof users; // "object" Array.isArray( users ); // true const mixed = [ 1, "hello", true, null ];
Un objet permet de regrouper plusieurs données sous forme de propriétés.

Les clés de propriétés sont des chaînes ou des Symbols.

Les valeurs peuvent contenir n'importe quel type : nombres, textes, tableaux, objets ou fonctions.

Une fonction stockée dans un objet est souvent appelée méthode.

Les objets sont des valeurs mutables. Une affectation d'objet vers une autre variable copie la référence vers le même objet, pas l'objet lui-même.
const user = { name: "Julien", age: 39, active: true, address: { city: "Aix-en-Provence" }, greet() { return `Bonjour ${this.name}`; } }; user.name; // "Julien" user["age"]; // 39 user.greet(); // "Bonjour Julien" const other = user; other.active = false; user.active; // false
typeof : retourne une chaîne décrivant le type d'une valeur.

Exemples :

"string"
"number"
"bigint"
"boolean"
"undefined"
"symbol"
"object"
"function"

Attention à deux particularités historiques :

typeof null retourne "object".

typeof [] retourne également "object".

Pour un tableau, utilisez donc Array.isArray().
typeof "Bonjour"; // "string" typeof 42; // "number" typeof 42n; // "bigint" typeof true; // "boolean" typeof undefined; // "undefined" typeof Symbol(); // "symbol" typeof {}; // "object" typeof []; // "object" typeof null; // "object" ← particularité historique typeof (() => {}); // "function"
JavaScript peut effectuer certaines conversions automatiquement, mais il est souvent préférable d'être explicite.

String() : convertit vers une chaîne.

Number() : convertit vers un nombre.

Boolean() : convertit vers un booléen.

Les conversions implicites peuvent parfois produire des résultats surprenants.

Pour les comparaisons, utilisez généralement === plutôt que ==.
String( 42 ); // "42" Number( "42" ); // 42 Number( "bonjour" ); // NaN Boolean( 1 ); // true Boolean( 0 ); // false // Conversion implicite "5" + 2; // "52" "5" - 2; // 3 5 == "5"; // true 5 === "5"; // false
Les types primitifs sont :

string, number, bigint, boolean, undefined, symbol et null.

Lorsqu'une valeur primitive est affectée à une autre variable, sa valeur est copiée.

Pour les objets, la valeur copiée est une référence vers le même objet.

Deux variables peuvent donc pointer vers le même objet.

Une copie superficielle peut être créée avec le spread { ...objet }.

Pour certaines structures compatibles, structuredClone() permet d'effectuer une copie profonde.
// Primitif let a = 10; let b = a; b = 20; console.log( a ); // 10 // Objet const user1 = { name: "Julien" }; const user2 = user1; user2.name = "Paul"; console.log( user1.name ); // "Paul" // Copie superficielle const copy = { ...user1 }; // Copie profonde compatible const deepCopy = structuredClone( user1 );

Conditions

Les conditions permettent de contrôler le flux d'exécution d'un programme. JavaScript propose if/else, l'opérateur ternaire, switch et les opérateurs logiques pour décider quel code doit être exécuté.

if : exécute un bloc lorsque la condition est évaluée comme vraie.

else if : teste une nouvelle condition lorsque les précédentes sont fausses.

else : s'exécute lorsqu'aucune des conditions précédentes n'est vraie.

Une condition n'est pas obligée de retourner explicitement true ou false : JavaScript convertit la valeur en booléen.
const score = 85; if (score >= 90) { console.log( "Excellent" ); } else if (score >= 70) { console.log( "Bien" ); } else { console.log( "À améliorer" ); }
Dans une condition, JavaScript convertit automatiquement certaines valeurs en booléen.

Les principales valeurs falsy sont :

false
0
-0
0n
""
null
undefined
NaN

La plupart des autres valeurs sont truthy.

Attention : un tableau vide [] et un objet vide {} sont truthy.

Boolean() permet de voir explicitement la conversion.
Boolean(0); // false Boolean(""); // false Boolean(null); // false Boolean([]); // true Boolean({}); // true Boolean("Bonjour"); // true const username = "Julien"; if (username) { console.log( "Un nom existe" ); }
Les conditions utilisent très souvent des opérateurs de comparaison.

=== : égalité stricte. Compare la valeur et le type.

!== : différence stricte.

>, <, >=, <= comparent des valeurs.

Il est généralement préférable d'utiliser === et !== plutôt que == et !=, car ces derniers effectuent des conversions implicites.
5 === 5; // true 5 === "5"; // false // == effectue une conversion implicite 5 == "5"; // true const age = 20; if (age >= 18) { console.log( "Majeur" ); } const role = "admin"; if (role !== "guest") { console.log( "Accès autorisé" ); }
&& — AND : les deux conditions doivent être truthy.

|| — OR : au moins une condition doit être truthy.

! — NOT : inverse la valeur booléenne.

Ces opérateurs utilisent également le court-circuit : JavaScript arrête l'évaluation dès que le résultat est connu.

Ils ne retournent pas forcément true ou false : ils peuvent retourner directement une des valeurs testées.
const age = 25; const hasAccount = true; // AND if ( age >= 18 && hasAccount ) { console.log( "Accès autorisé" ); } // OR const role = "admin"; if ( role === "admin" || role === "moderator" ) { console.log( "Administration autorisée" ); } // NOT const isOffline = !navigator.onLine; // Conversion explicite en booléen const hasName = !!user.name;
Les opérateurs logiques peuvent être utilisés autrement que dans un if.

Avec &&, la deuxième expression n'est évaluée que si la première est truthy.

Avec ||, la deuxième expression n'est évaluée que si la première est falsy.

Cela permet parfois d'écrire des expressions concises, mais il ne faut pas sacrifier la lisibilité.

Pour une valeur par défaut qui doit uniquement remplacer null ou undefined, ?? est souvent préférable à ||.
const isAdmin = true; // Appelle la fonction seulement si isAdmin est truthy isAdmin && showAdminPanel(); // Valeur par défaut avec || const username = user.name || "Visiteur"; // Attention : 0 est falsy const score = 0; const displayScore = score || 100; // 100 // ?? conserve 0 const correctScore = score ?? 100; // 0
L'opérateur ternaire est une expression conditionnelle.

Syntaxe :

condition ? valeurSiVrai : valeurSiFaux

Contrairement à un bloc if, le ternaire produit directement une valeur.

Il est idéal pour une condition simple.

Évitez d'empiler plusieurs ternaires imbriqués lorsque cela réduit la lisibilité.
const age = 20; const label = age >= 18 ? "Adulte" : "Mineur"; // Autre exemple const status = user.active ? "Actif" : "Inactif"; // Fonction concise const getAccess = (isLoggedIn) => isLoggedIn ? "Autorisé" : "Refusé";
switch : compare une valeur avec plusieurs case.

Il est pratique lorsqu'une même variable peut prendre plusieurs valeurs précises.

break : arrête l'exécution du switch. Sans break, JavaScript continue dans le case suivant : c'est le fall-through.

default : est utilisé lorsqu'aucun case ne correspond.

Les comparaisons de switch suivent une égalité stricte.
const role = "admin"; switch (role) { case "admin": console.log( "Accès complet" ); break; case "editor": console.log( "Modification autorisée" ); break; case "user": console.log( "Lecture uniquement" ); break; default: console.log( "Rôle inconnu" ); }
L'absence de break peut parfois être utilisée volontairement.

Plusieurs case peuvent ainsi partager le même bloc.

C'est pratique lorsque plusieurs valeurs doivent produire exactement le même comportement.

En revanche, un fall-through accidentel peut provoquer des bugs : il est donc préférable que l'intention soit claire.
const day = "samedi"; switch (day) { case "samedi": case "dimanche": console.log( "Week-end" ); break; default: console.log( "Jour de semaine" ); }
Une guard clause vérifie rapidement qu'une condition nécessaire est respectée.

Si ce n'est pas le cas, la fonction se termine immédiatement avec return.

Cela permet souvent d'éviter de nombreux niveaux de if imbriqués.

Le code principal devient généralement plus lisible, car les cas d'erreur sont traités en premier.
// Version très imbriquée function openDashboard(user) { if (user) { if (user.active) { if (user.role === "admin") { showDashboard(); } } } } // Version avec guard clauses function openDashboardBetter(user) { if (!user) { return; } if (!user.active) { return; } if (user.role !== "admin") { return; } showDashboard(); }
Une condition très longue devient rapidement difficile à comprendre.

Il est souvent préférable de donner un nom aux différentes parties de la condition.

Des variables booléennes bien nommées rendent le code beaucoup plus explicite.

Utilisez des parenthèses lorsque plusieurs opérateurs && et || sont combinés.

Pour des règles métier complexes, préférez plusieurs étapes lisibles plutôt qu'une seule expression gigantesque.
// Difficile à lire if ( user && user.active && (user.role === "admin" || user.role === "editor") && user.age >= 18 ) { showPanel(); } // Plus lisible const isActive = user?.active === true; const hasAllowedRole = user?.role === "admin" || user?.role === "editor"; const isAdult = user?.age >= 18; if ( isActive && hasAllowedRole && isAdult ) { showPanel(); }

JavaScript Asynchrone

JavaScript peut gérer des opérations qui prennent du temps sans bloquer inutilement l'application : timers, requêtes HTTP, Promises, async/await, concurrence et annulation.

Event Loop : mécanisme qui permet à JavaScript de gérer les opérations asynchrones sans bloquer en permanence l'exécution.

Call Stack : contient le code JavaScript actuellement exécuté.

Task Queue : contient notamment les événements et les callbacks de setTimeout().

Microtask Queue : contient notamment les callbacks des Promises.

Les microtasks sont exécutées avant de passer à la tâche suivante.
console.log("1"); setTimeout(() => { console.log("4"); }, 0); Promise.resolve() .then(() => { console.log("3"); }); console.log("2"); // Résultat : // 1 // 2 // 3 // 4
setTimeout() : planifie une fonction après un délai minimum.

setInterval() : répète une fonction régulièrement.

clearTimeout() et clearInterval() permettent d'annuler les timers.

Le délai indiqué n'est pas garanti exactement : si JavaScript est occupé, l'exécution peut avoir lieu plus tard.
const timeoutId = setTimeout(() => { console.log( "2 secondes" ); }, 2000); clearTimeout( timeoutId ); const intervalId = setInterval(() => { updateClock(); }, 1000); clearInterval( intervalId );
Une Promise représente le résultat futur d'une opération asynchrone.

Elle possède trois états :

pending → en attente
fulfilled → réussie
rejected → échouée

then() : traite un succès.
catch() : traite une erreur.
finally() : s'exécute dans tous les cas.
const promise = new Promise( (resolve, reject) => { setTimeout(() => { const success = Math.random() > 0.5; if (success) { resolve( "Succès" ); } else { reject( new Error( "Erreur" ) ); } }, 1000); } ); promise .then((result) => { console.log(result); }) .catch((error) => { console.error(error); }) .finally(() => { console.log("Terminé"); });
async : une fonction async retourne toujours une Promise.

await : attend le résultat d'une Promise à l'intérieur d'une fonction asynchrone.

Cela ne bloque pas l'ensemble du navigateur.

try/catch permet de gérer les erreurs de manière lisible.
async function loadUser(id) { try { const response = await fetch( `/api/users/${id}` ); if (!response.ok) { throw new Error( `HTTP ${response.status}` ); } const user = await response.json(); return user; } catch (error) { console.error( error ); throw error; } }
fetch() : API native permettant d'envoyer des requêtes HTTP.

Elle retourne une Promise contenant un objet Response.

Important : une réponse HTTP 404 ou 500 ne rejette pas automatiquement la Promise.

Il faut donc vérifier : response.ok.
// GET async function getUsers() { const response = await fetch( "/api/users" ); if (!response.ok) { throw new Error( `HTTP ${response.status}` ); } return await response.json(); }
// POST JSON async function createUser(user) { const response = await fetch( "/api/users", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify( user ) } ); if (!response.ok) { throw new Error( `HTTP ${response.status}` ); } return await response.json(); }
Une Promise n'est pas directement annulable.

Certaines API comme fetch() acceptent cependant un AbortSignal.

AbortController permet alors d'interrompre la requête.

Cette technique est utile lors d'une navigation, d'une recherche instantanée ou lorsqu'une requête précédente n'est plus nécessaire.
const controller = new AbortController(); async function loadData() { try { const response = await fetch( "/api/data", { signal: controller.signal } ); return await response.json(); } catch (error) { if ( error.name === "AbortError" ) { console.log( "Requête annulée" ); return; } throw error; } } loadData(); // Annulation controller.abort();
En mode séquentiel, on attend qu'une opération soit terminée avant de lancer la suivante.

Lorsque plusieurs opérations sont indépendantes, elles peuvent être lancées ensemble puis attendues avec Promise.all().

Cela réduit souvent le temps total d'attente.

Il s'agit de concurrence asynchrone : cela ne signifie pas automatiquement que plusieurs morceaux de JavaScript sont exécutés simultanément sur plusieurs CPU.
// Séquentiel const users = await getUsers(); const posts = await getPosts(); // Concurrent const usersPromise = getUsers(); const postsPromise = getPosts(); const [ users2, posts2 ] = await Promise.all([ usersPromise, postsPromise ]);
Promise.all() : attend que toutes réussissent. Une seule erreur rejette le résultat global.

Promise.allSettled() : attend toutes les Promises, même celles qui échouent.

Promise.any() : retourne la première Promise qui réussit.

Promise.race() : retourne la première Promise terminée, qu'elle réussisse ou échoue.
// Toutes doivent réussir const [ users, posts, stats ] = await Promise.all([ getUsers(), getPosts(), getStats() ]); // Toutes les réponses, succès ou erreur const results = await Promise.allSettled([ getUsers(), getPosts() ]); // Première réussite const firstSuccess = await Promise.any([ serverA(), serverB() ]); // Première terminée const firstResult = await Promise.race([ requestA(), requestB() ]);
Promise.withResolvers() retourne directement trois valeurs :

promise
resolve
reject

Cette API est surtout utile lorsque la résolution de la Promise doit être déclenchée ailleurs dans le programme.

Elle peut notamment servir à adapter des événements ou des API basées sur des callbacks.
const { promise, resolve, reject } = Promise.withResolvers(); button.addEventListener( "click", () => { resolve( "Bouton cliqué" ); }, { once: true } ); promise.then( (result) => { console.log( result ); } );
Un Async Iterable produit des valeurs progressivement de manière asynchrone.

for await...of permet de parcourir ces valeurs avec une syntaxe simple.

Cette technique est notamment utile pour : streams, générateurs asynchrones, données paginées ou résultats arrivant progressivement.
async function* counter() { for ( let i = 1; i <= 3; i++ ) { await new Promise( (resolve) => { setTimeout( resolve, 500 ); } ); yield i; } } for await ( const value of counter() ) { console.log( value ); }
1. Toujours gérer les erreurs importantes.

2. Toujours vérifier response.ok avec fetch().

3. Éviter les await séquentiels lorsque les opérations sont indépendantes.

4. Annuler les requêtes devenues inutiles quand c'est possible.

5. Attention : forEach() n'attend pas automatiquement les callbacks async.
// ⚠️ forEach n'attend pas users.forEach( async (user) => { await saveUser(user); } ); // Séquentiel for ( const user of users ) { await saveUser(user); } // Concurrent await Promise.all( users.map( (user) => saveUser(user) ) );

ES6+ JavaScript Moderne

ES6, publié en 2015, a profondément modernisé JavaScript. Depuis, ECMAScript continue d'évoluer chaque année. Cette section présente les fonctionnalités modernes essentielles : portée des variables, fonctions fléchées, template literals, déstructuration, spread/rest, optional chaining, objets modernes, Map, Set, classes, modules et fonctionnalités ECMAScript récentes.

const : déclare une variable qui ne peut pas être réassignée. C'est généralement le choix par défaut.

let : permet de réassigner la variable.

Contrairement à var, let et const possèdent une portée de bloc.

Un objet déclaré avec const peut toujours être modifié : c'est la variable elle-même qui ne peut pas être réassignée.
const name = "Julien"; // ❌ Réassignation impossible // name = "Paul"; let score = 10; score = 20; // const ne rend pas l'objet immuable const user = { name: "Julien", score: 0 }; user.score = 10; // Portée de bloc if (true) { const message = "Bonjour"; } // message n'existe pas ici
Les arrow functions offrent une syntaxe plus courte pour écrire des fonctions.

Elles sont particulièrement utilisées dans : callbacks, méthodes de tableaux, événements, Promises et frameworks.

Si une seule expression est retournée, le mot-clé return peut être omis.

Attention : une fonction fléchée ne possède pas son propre this. Elle utilise le this de son contexte extérieur.
// Fonction classique function double(value) { return value * 2; } // Arrow function const doubleArrow = (value) => { return value * 2; }; // Retour implicite const triple = (value) => value * 3; // Très courant avec map() const numbers = [1, 2, 3]; const doubled = numbers.map( (number) => number * 2 ); // [2, 4, 6] // Retourner directement un objet const createUser = (name) => ({ name, active: true });
Les template literals utilisent des accents graves `.

Ils permettent d'insérer directement des expressions avec :

${expression}

Ils peuvent également contenir plusieurs lignes.

Les paramètres de fonctions peuvent recevoir une valeur par défaut utilisée lorsque l'argument vaut undefined.
const name = "Julien"; const score = 42; const message = `Bonjour ${name}, score : ${score}`; // Expression JavaScript const result = `Double : ${score * 2}`; // Plusieurs lignes const html = ` <article> <h2>${name}</h2> <p>Score : ${score}</p> </article> `; // Paramètres par défaut function greet( name = "Visiteur", role = "user" ) { return `${name} (${role})`; } greet(); // "Visiteur (user)"
La déstructuration permet d'extraire rapidement des données d'un objet ou d'un tableau.

Elle permet également :

de renommer une variable, définir une valeur par défaut, ignorer certains éléments, récupérer les valeurs restantes.

Elle est très utilisée dans les paramètres de fonctions ainsi que dans React, Vue et Node.js.
const user = { name: "Julien", age: 39, role: "developer" }; // Objet const { name, age } = user; // Renommer const { name: username } = user; // Valeur par défaut const { country = "France" } = user; // Tableau const [ first, second, ...others ] = [10, 20, 30, 40]; // Paramètre de fonction function displayUser({ name, role = "user" }) { return `${name} — ${role}`; }
Les deux syntaxes utilisent ... mais leur rôle est opposé.

Spread : étale des valeurs.

Il permet notamment de copier ou fusionner tableaux et objets.

Rest : collecte plusieurs valeurs restantes dans un tableau ou un objet.

Attention : les copies réalisées avec spread sont superficielles. Les objets imbriqués restent partagés.
// SPREAD — tableau const a = [1, 2]; const b = [ ...a, 3, 4 ]; // SPREAD — objet const user = { name: "Julien", role: "user" }; const updatedUser = { ...user, role: "admin" }; // Spread dans un appel const numbers = [10, 50, 20]; Math.max( ...numbers ); // REST — paramètres function sum(...numbers) { return numbers.reduce( (total, number) => total + number, 0 ); } // REST — objet const { role, ...publicUser } = user;
?. : arrête l'accès si une valeur intermédiaire vaut null ou undefined.

Le résultat devient alors undefined plutôt que de provoquer une erreur.

?? : utilise la valeur de droite uniquement si celle de gauche vaut null ou undefined.

C'est différent de ||, qui considère également 0, "" et false comme des valeurs falsy.
const city = user?.address?.city; // Tableau const first = users?.[0]; // Fonction optionnelle user?.onLogin?.(); // Valeur par défaut const avatar = user?.profile?.avatar ?? "default.png"; const score = 0; // || considère 0 comme falsy const a = score || 100; // a = 100 // ?? conserve 0 const b = score ?? 100; // b = 0
Les opérateurs d'affectation logique combinent une condition et une affectation.

??= : affecte seulement si la valeur vaut null ou undefined.

||= : affecte si la valeur est falsy.

&&= : affecte si la valeur est truthy.
const settings = { theme: null, notifications: true }; // Seulement null ou undefined settings.theme ??= "dark"; // Si falsy settings.language ||= "fr"; // Si truthy settings.notifications &&= "enabled";
JavaScript fournit plusieurs méthodes modernes très pratiques pour travailler avec les objets.

Object.keys() : retourne les clés.

Object.values() : retourne les valeurs.

Object.entries() : retourne des couples [clé, valeur].

Object.fromEntries() : effectue l'opération inverse.

Object.hasOwn() : vérifie qu'une propriété appartient directement à l'objet.

Object.groupBy() : permet de regrouper des valeurs selon une fonction.
const user = { name: "Julien", role: "developer", active: true }; Object.keys(user); // ["name", "role", "active"] Object.values(user); Object.entries(user); // Transformer puis reconstruire const entries = Object.entries(user); const copy = Object.fromEntries( entries ); // Vérifier une propriété propre Object.hasOwn( user, "role" ); // true // Raccourci de propriétés const name = "Julien"; const age = 39; const profile = { name, age };
Map : collection clé-valeur.

Contrairement aux propriétés ordinaires d'un objet, les clés peuvent être de n'importe quel type : objet, fonction, nombre, chaîne...

Set : collection de valeurs uniques.

Il est particulièrement pratique pour supprimer les doublons.

JavaScript moderne fournit également des opérations d'ensembles comme : union(), intersection() et difference().
// MAP const cache = new Map(); cache.set( "user:1", { name: "Julien" } ); cache.get( "user:1" ); cache.has( "user:1" ); cache.size; cache.delete( "user:1" ); // SET const tags = new Set([ "js", "vue", "js" ]); // Set { "js", "vue" } // Dédupliquer const values = [1, 2, 2, 3]; const unique = [ ...new Set(values) ]; // [1, 2, 3] // Opérations modernes sur les Set const frontend = new Set([ "JS", "CSS" ]); const backend = new Set([ "JS", "SQL" ]); const all = frontend.union(backend); const common = frontend.intersection(backend); const onlyFrontend = frontend.difference(backend);
La syntaxe class permet de créer des objets à partir d'un modèle.

constructor : initialise l'instance.

extends : permet l'héritage.

super : appelle le constructeur parent.

#propriete : crée un champ privé réellement inaccessible depuis l'extérieur.

static : crée une propriété ou méthode appartenant à la classe plutôt qu'aux instances.
class User { // Champ privé #password; static count = 0; constructor( name, password ) { this.name = name; this.#password = password; User.count++; } greet() { return `Bonjour ${this.name}`; } checkPassword(password) { return this.#password === password; } } class Admin extends User { constructor( name, password ) { super( name, password ); this.role = "admin"; } }
Les modules permettent de séparer une application en plusieurs fichiers.

export nommé : plusieurs exports sont possibles dans un fichier.

export default : un seul export par défaut par module.

import() : permet de charger dynamiquement un module, seulement lorsqu'il devient nécessaire.

Dans le navigateur, un module peut être chargé avec :

<script type="module">.

Le top-level await permet également d'utiliser await directement au niveau supérieur d'un module.
// utils.js export const PI = 3.14159; export function double(value) { return value * 2; } // user.js export default class User { } // app.js import { PI, double } from "./utils.js"; import User from "./user.js"; // Renommer un import import { double as multiplyByTwo } from "./utils.js"; // Import dynamique const module = await import( "./charts.js" ); module.drawChart();
JavaScript utilise habituellement le type Number.

Celui-ci ne peut représenter exactement que les entiers situés dans une certaine plage.

BigInt : permet de travailler avec des entiers arbitrairement grands.

Un BigInt se termine par n.

Séparateurs numériques : le caractère _ peut améliorer la lisibilité des grands nombres.
const population = 8_000_000_000; const budget = 1_500_000.50; // BigInt const hugeNumber = 900719925474099123456789n; const result = hugeNumber + 10n; // Number et BigInt ne se mélangent // ❌ hugeNumber + 10 // ✅ hugeNumber + BigInt(10);
for...of : parcourt les valeurs d'un objet itérable : Array, String, Map, Set...

Generator : fonction spéciale utilisant function*.

Elle peut suspendre son exécution avec yield puis reprendre plus tard.

Les générateurs permettent notamment de créer des séquences de valeurs à la demande.
const technologies = [ "JavaScript", "Vue", "Angular" ]; for ( const technology of technologies ) { console.log( technology ); } // Générateur function* counter() { yield 1; yield 2; yield 3; } const iterator = counter(); iterator.next(); // { value: 1, done: false } iterator.next(); // { value: 2, done: false }
JavaScript continue d'évoluer chaque année.

Iterator Helpers : permettent d'utiliser des méthodes comme map(), filter() ou take() directement sur des itérateurs.

RegExp.escape() : échappe automatiquement une chaîne avant de l'intégrer dans une expression régulière.

Array.fromAsync() : construit un tableau à partir d'une source asynchrone.

Math.sumPrecise() : additionne une collection de nombres en réduisant les erreurs de précision liées aux nombres flottants.

Error.isError() : permet de vérifier proprement qu'une valeur représente une erreur.

Pour ces fonctionnalités très récentes, il est recommandé de vérifier la compatibilité de l'environnement ciblé avant utilisation en production.
// Iterator Helpers const values = Iterator.from( [1, 2, 3, 4, 5] ) .filter( (value) => value % 2 === 0 ) .map( (value) => value * 10 ) .toArray(); // [20, 40] // Sécuriser une chaîne utilisée dans une RegExp const search = "JavaScript (ES6+)"; const pattern = new RegExp( RegExp.escape( search ) ); // ES2026 : source asynchrone vers tableau const items = await Array.fromAsync( asyncIterable ); // Somme numérique plus précise const total = Math.sumPrecise( [0.1, 0.2, 0.3] ); // Vérifier une erreur if ( Error.isError(value) ) { console.error( value.message ); }

Méthodes des Tableaux

Les tableaux sont omniprésents en JavaScript. Leurs méthodes permettent de parcourir, transformer, filtrer, rechercher, trier, regrouper et modifier des collections de données. Une distinction essentielle existe entre les méthodes qui modifient le tableau original et celles qui retournent un nouveau tableau.

forEach() : exécute une fonction pour chaque élément du tableau.

Contrairement à map(), forEach() ne construit pas un nouveau tableau. Sa valeur de retour est undefined.

Il est adapté lorsque l'on veut produire un effet : afficher une valeur, modifier le DOM, appeler une fonction, etc.

Le callback peut recevoir :

(valeur, index, tableau)
const users = [ "Julien", "Marie", "Paul" ]; users.forEach((user, index) => { console.log( index, user ); }); // 0 Julien // 1 Marie // 2 Paul // Exemple DOM users.forEach((user) => { const li = document.createElement("li"); li.textContent = user; list.append(li); });
map() : applique une fonction à chaque élément et retourne un nouveau tableau.

Le tableau retourné possède la même longueur que le tableau original.

map() ne modifie pas directement le tableau source.

Très utilisé pour transformer des données provenant d'une API ou préparer des données pour une interface.
const prices = [10, 25, 50]; const withTax = prices.map((price) => price * 1.2 ); // [12, 30, 60] // Transformer des objets const users = [ { id: 1, name: "Julien" }, { id: 2, name: "Marie" } ]; const names = users.map((user) => user.name ); // ["Julien", "Marie"]
filter() : retourne un nouveau tableau contenant seulement les éléments pour lesquels le callback retourne true.

Le tableau original n'est pas modifié.

Le chaînage :

filter().map()

est extrêmement courant : on sélectionne d'abord les données souhaitées, puis on les transforme.
const users = [ { name: "Julien", active: true }, { name: "Paul", active: false }, { name: "Marie", active: true } ]; const activeUsers = users.filter( (user) => user.active ); // Filtrer puis transformer const activeNames = users .filter( (user) => user.active ) .map( (user) => user.name ); // ["Julien", "Marie"] // Recherche texte const results = users.filter( (user) => user.name .toLowerCase() .includes( query.toLowerCase() ) );
find() : retourne le premier élément correspondant à une condition. Sinon, retourne undefined.

findIndex() : retourne l'index du premier élément correspondant. Sinon, retourne -1.

findLast() : recherche depuis la fin et retourne la dernière valeur correspondante.

findLastIndex() : retourne l'index de la dernière correspondance.

includes() : vérifie si une valeur existe dans le tableau.

indexOf() : retourne la position d'une valeur ou -1.
const users = [ { id: 1, name: "Julien" }, { id: 42, name: "Marie" } ]; // Trouver un objet const user = users.find( (user) => user.id === 42 ); // { id: 42, name: "Marie" } // Trouver son index const index = users.findIndex( (user) => user.id === 42 ); // 1 const roles = ["user", "admin", "editor"]; roles.includes("admin"); // true roles.indexOf("editor"); // 2
some() : retourne true si au moins un élément respecte la condition.

every() : retourne true uniquement si tous les éléments respectent la condition.

Ces méthodes s'arrêtent dès que le résultat peut être déterminé.

Elles sont très utiles pour les validations, permissions, paniers ou formulaires.
const users = [ { name: "Julien", role: "user", active: true }, { name: "Marie", role: "admin", active: true } ]; const hasAdmin = users.some( (user) => user.role === "admin" ); // true const allActive = users.every( (user) => user.active ); // true
reduce() : parcourt un tableau en conservant un accumulateur.

Cet accumulateur peut être :

• un nombre
• une chaîne
• un objet
• un tableau
• n'importe quelle autre valeur

Syntaxe :

array.reduce( (accumulateur, valeur) => résultat, valeurInitiale )

Il est conseillé de fournir explicitement une valeur initiale.
// Somme const total = [10, 20, 30] .reduce( (sum, value) => sum + value, 0 ); // 60 // Total d'un panier const cart = [ { price: 20, quantity: 2 }, { price: 10, quantity: 3 } ]; const cartTotal = cart.reduce( (total, item) => { return total + item.price * item.quantity; }, 0 ); // 70 // Grouper par catégorie const grouped = items.reduce( (groups, item) => { const category = item.category; groups[category] ??= []; groups[category] .push(item); return groups; }, {} );
Ces quatre méthodes modifient directement le tableau original.

push() : ajoute un ou plusieurs éléments à la fin.

pop() : retire et retourne le dernier élément.

unshift() : ajoute au début.

shift() : retire et retourne le premier élément.

Pour créer un nouveau tableau sans modifier l'ancien, on peut utiliser le spread : [...tableau, valeur].
const fruits = ["Pomme", "Banane"]; // Ajouter à la fin fruits.push("Orange"); // ["Pomme", "Banane", "Orange"] // Retirer à la fin const last = fruits.pop(); // Ajouter au début fruits.unshift("Fraise"); // Retirer au début const first = fruits.shift(); // Variante sans mutation const newFruits = [ ...fruits, "Kiwi" ];
slice(début, fin) : retourne une portion du tableau sans modifier l'original.

L'index de fin n'est pas inclus.

splice(index, quantité, ...éléments) : peut supprimer, remplacer ou insérer des éléments.

Attention : splice() modifie le tableau original.

toSpliced() : fonctionne sur le même principe mais retourne un nouveau tableau sans modifier l'ancien.
const items = ["A", "B", "C", "D"]; // Extraire B et C const part = items.slice(1, 3); // ["B", "C"] // Les 2 derniers const lastTwo = items.slice(-2); // ["C", "D"] // splice() modifie items items.splice( 1, 1, "X" ); // ["A", "X", "C", "D"] // Même opération sans mutation const updated = items.toSpliced( 1, 1, "Y" );
sort() : trie le tableau en modifiant l'original.

Sans fonction de comparaison, les valeurs sont comparées comme des chaînes.

Pour des nombres : (a, b) => a - b.

toSorted() : trie mais retourne un nouveau tableau. L'original reste intact.

reverse() : inverse le tableau original.

toReversed() : retourne une copie inversée sans mutation.

Pour du texte destiné aux utilisateurs, localeCompare() est souvent préférable.
const numbers = [50, 2, 100, 10]; // ATTENTION : sort() modifie numbers numbers.sort( (a, b) => a - b ); // Sans modifier l'original const sorted = numbers.toSorted( (a, b) => a - b ); const users = [ { name: "Zoé" }, { name: "Alain" }, { name: "Éric" } ]; const alphabetical = users.toSorted( (a, b) => a.name.localeCompare( b.name, "fr" ) ); // Inverser sans modifier const reversed = numbers.toReversed();
at(index) : retourne un élément à une position donnée.

Son avantage est de permettre les indices négatifs.

array.at(-1) retourne donc le dernier élément.

with(index, valeur) : crée une copie du tableau dans laquelle un élément est remplacé.

Contrairement à : array[index] = valeur, la méthode with() ne modifie pas le tableau original.
const colors = [ "rouge", "vert", "bleu" ]; colors.at(0); // "rouge" colors.at(-1); // "bleu" const newColors = colors.with( 1, "jaune" ); // ["rouge", "jaune", "bleu"] // colors reste inchangé // ["rouge", "vert", "bleu"]
flat() : transforme un tableau contenant d'autres tableaux en une structure plus plate.

Par défaut, un seul niveau est aplati.

flat(2) aplatit deux niveaux.

flat(Infinity) aplatit tous les niveaux.

flatMap() : correspond approximativement à :

map().flat(1)

Il permet donc de transformer puis d'aplatir en une seule opération.
const values = [ [1, 2], [3, 4] ]; const flatValues = values.flat(); // [1, 2, 3, 4] const users = [ { name: "Julien", roles: [ "user", "editor" ] }, { name: "Marie", roles: [ "admin" ] } ]; const roles = users.flatMap( (user) => user.roles ); // ["user", "editor", "admin"]
concat() : fusionne plusieurs tableaux et retourne un nouveau tableau.

Il ne modifie pas les tableaux originaux.

L'opérateur spread ... permet également de copier ou fusionner des tableaux.

Attention : ces copies sont superficielles. Les objets présents à l'intérieur restent partagés.
const a = [1, 2]; const b = [3, 4]; // concat() const merged = a.concat(b); // [1, 2, 3, 4] // Spread const merged2 = [ ...a, ...b ]; // Copier un tableau const copy = [ ...a ]; // Ajouter sans mutation const next = [ ...a, 5 ];
join() : transforme les éléments d'un tableau en une chaîne de caractères.

Le séparateur est fourni en argument.

Sans argument, une virgule est utilisée.

Cette méthode est très pratique pour construire des textes, listes, URLs ou affichages.
const technologies = [ "JavaScript", "Vue", "Angular" ]; const text = technologies.join(" • "); // "JavaScript • Vue • Angular" const path = ["users", "42", "profile"] .join("/"); // "users/42/profile"
Array.from() : crée un véritable tableau à partir d'une structure itérable ou ressemblant à un tableau.

Exemple : NodeList, chaîne de caractères, Set...

Il accepte également une fonction de transformation.

Array.isArray() : vérifie si une valeur est réellement un tableau.

C'est préférable à : typeof value, car un tableau possède également le type "object" avec typeof.
// NodeList vers Array const nodes = document.querySelectorAll(".card"); const cards = Array.from(nodes); // Chaîne vers tableau Array.from("JS"); // ["J", "S"] // Créer [0, 1, 2, 3, 4] const numbers = Array.from( { length: 5 }, (_, index) => index ); Array.isArray(numbers); // true Array.isArray({}); // false
Il est essentiel de savoir si une méthode modifie directement le tableau original.

Modifient l'original :

push()
pop()
shift()
unshift()
splice()
sort()
reverse()

Retournent une nouvelle valeur ou un nouveau tableau :

map()
filter()
slice()
concat()
flat()
toSorted()
toReversed()
toSpliced()
with()

Les méthodes non mutantes sont particulièrement utiles dans des bibliothèques et frameworks comme React, Vue ou Angular lorsque l'on veut garder des transformations prévisibles.
const original = [3, 1, 2]; // MUTATION const sameArray = original.sort( (a, b) => a - b ); // original est maintenant [1, 2, 3] const numbers = [3, 1, 2]; // PAS DE MUTATION const sorted = numbers.toSorted( (a, b) => a - b ); // numbers : [3, 1, 2] // sorted : [1, 2, 3]

Manipulation du DOM

Manipuler une page HTML avec JavaScript : sélectionner et parcourir les éléments, modifier leur contenu, créer ou supprimer des nœuds, gérer les attributs et les classes, écouter les événements et utiliser la délégation d'événements.

querySelector() : retourne le premier élément correspondant à un sélecteur CSS.

querySelectorAll() : retourne tous les éléments correspondants sous forme de NodeList.

getElementById() : récupère directement un élément grâce à son identifiant.

Les sélecteurs utilisés sont les mêmes qu'en CSS : #id, .classe, tag, [attribut], etc.

Une NodeList retournée par querySelectorAll() peut directement être parcourue avec forEach().
// Par identifiant const title = document.getElementById("title"); // Premier élément correspondant const button = document.querySelector("#submit-btn"); const navbar = document.querySelector(".navbar"); // Tous les éléments correspondants const cards = document.querySelectorAll(".card"); cards.forEach((card) => { console.log(card); }); // Conversion en véritable Array const cardsArray = [...cards];
Une fois un élément sélectionné, JavaScript permet de naviguer autour de lui dans l'arbre DOM.

parentElement : retourne l'élément parent.

children : retourne les éléments enfants.

firstElementChild / lastElementChild : premier et dernier enfant.

nextElementSibling : élément HTML suivant au même niveau.

previousElementSibling : élément précédent.

closest() : remonte dans le DOM jusqu'au premier élément correspondant au sélecteur.

matches() : vérifie si un élément correspond à un sélecteur CSS.
const item = document.querySelector(".item"); // Parent console.log( item.parentElement ); // Enfants console.log( item.children ); // Premier enfant console.log( item.firstElementChild ); // Élément voisin suivant console.log( item.nextElementSibling ); // Chercher un parent correspondant const card = item.closest(".card"); // Tester un sélecteur if (item.matches(".active")) { console.log("Élément actif"); }
textContent : lit ou modifie du texte brut. Le contenu HTML n'est pas interprété.

C'est généralement le meilleur choix lorsque le contenu provient d'un utilisateur.

innerHTML : lit ou remplace le HTML interne d'un élément.

Il ne faut pas injecter directement dans innerHTML des données provenant d'un utilisateur sans les contrôler, car cela peut provoquer une faille XSS.

insertAdjacentHTML() : permet d'insérer du HTML sans remplacer tout le contenu.

Positions possibles : beforebegin, afterbegin, beforeend, afterend.
const app = document.querySelector("#app"); // Texte brut app.textContent = "Bonjour Julien"; // HTML contrôlé par le développeur app.innerHTML = "<strong>Bonjour</strong>"; // Ajouter du HTML à la fin app.insertAdjacentHTML( "beforeend", "<p>Nouveau contenu</p>" ); // Donnée utilisateur : préférer textContent const message = document.createElement("p"); message.textContent = userInput; app.append(message);
createElement() : crée un nouvel élément HTML en mémoire.

Il faut ensuite l'insérer dans le DOM.

append() : ajoute à la fin.

prepend() : ajoute au début.

before() / after() : insèrent un élément avant ou après un autre.

remove() : supprime directement un élément.

replaceWith() : remplace un élément par un autre.

cloneNode(true) : crée une copie d'un élément et de ses descendants.
const list = document.querySelector("#todo-list"); // Créer un élément const item = document.createElement("li"); item.textContent = "Apprendre le DOM"; item.classList.add("todo"); // Ajouter à la fin list.append(item); // Ajouter au début list.prepend(anotherItem); // Insérer autour d'un élément item.before(previousItem); item.after(nextItem); // Supprimer item.remove(); // Remplacer oldElement.replaceWith(newElement); // Cloner avec tous les enfants const copy = newElement.cloneNode(true);
getAttribute() : lit un attribut HTML.

setAttribute() : ajoute ou modifie un attribut.

removeAttribute() : supprime un attribut.

hasAttribute() : vérifie sa présence.

Beaucoup d'éléments possèdent également des propriétés JavaScript accessibles directement : input.value, checkbox.checked, button.disabled.

dataset : permet d'accéder aux attributs HTML data-*.

Par exemple : data-user-id="42" devient element.dataset.userId.
const link = document.querySelector("a"); // Lire un attribut const href = link.getAttribute("href"); // Modifier link.setAttribute( "title", "Voir la documentation" ); // Vérifier if (link.hasAttribute("target")) { console.log("Target présent"); } // Supprimer link.removeAttribute("target"); // Propriétés JavaScript const input = document.querySelector("#name"); console.log(input.value); const checkbox = document.querySelector("#accept"); console.log(checkbox.checked); // data-user-id="42" const user = document.querySelector("[data-user-id]"); console.log( user.dataset.userId );
classList : permet de travailler facilement avec les classes CSS.

• add() : ajouter
• remove() : supprimer
• toggle() : basculer
• contains() : vérifier
• replace() : remplacer

element.style : permet de modifier directement un style inline.

Pour la majorité des changements visuels, il est généralement préférable d'ajouter ou supprimer une classe CSS plutôt que d'écrire de nombreux styles directement en JavaScript.

getComputedStyle() : permet de lire le style final calculé par le navigateur.
const menu = document.querySelector(".menu"); menu.classList.add("open"); menu.classList.remove("hidden"); menu.classList.toggle("active"); if ( menu.classList.contains("open") ) { menu.classList.replace( "open", "closed" ); } // Style inline menu.style.display = "block"; menu.style.backgroundColor = "#222"; // Variable CSS menu.style.setProperty( "--menu-width", "300px" ); // Style réellement appliqué const styles = getComputedStyle(menu); console.log( styles.display );
addEventListener() : exécute une fonction lorsqu'un événement se produit.

Événements fréquents :

click, input, change, submit, keydown, keyup, focus, blur, scroll, resize.

event : objet contenant des informations sur l'événement.

preventDefault() : empêche le comportement par défaut du navigateur.

removeEventListener() : retire un écouteur. Pour cela, il faut conserver une référence vers la fonction utilisée.
const button = document.querySelector("#button"); function handleClick(event) { console.log( "Clic" ); console.log( event.target ); } button.addEventListener( "click", handleClick ); // Supprimer l'écouteur button.removeEventListener( "click", handleClick ); // Formulaire form.addEventListener( "submit", (event) => { event.preventDefault(); console.log( "Formulaire intercepté" ); } ); // Exécuter une seule fois button.addEventListener( "click", () => { console.log("Une seule fois"); }, { once: true } );
Les événements peuvent se propager dans l'arbre DOM.

Le comportement le plus courant est le bubbling : l'événement part de l'élément cible puis remonte vers ses parents.

event.target : élément qui a réellement déclenché l'événement.

event.currentTarget : élément sur lequel l'écouteur addEventListener() est installé.

stopPropagation() : empêche l'événement de continuer sa propagation.

Il faut cependant éviter de l'utiliser sans raison : le bubbling est justement très utile pour la délégation d'événements.
const card = document.querySelector(".card"); card.addEventListener( "click", (event) => { // Élément réellement cliqué console.log( event.target ); // Élément qui possède l'écouteur console.log( event.currentTarget ); } ); // Arrêter la propagation button.addEventListener( "click", (event) => { event.stopPropagation(); } );
La délégation d'événements consiste à placer un seul écouteur sur un élément parent plutôt qu'un écouteur sur chacun de ses enfants.

Grâce au bubbling, le parent reçoit également les événements déclenchés par ses descendants.

Cette technique est particulièrement utile pour :

• les grandes listes
• les tableaux
• les menus
• les éléments ajoutés dynamiquement

closest() permet de déterminer quel bouton ou élément a été cliqué.
const list = document.querySelector("#todo-list"); list.addEventListener( "click", (event) => { const deleteButton = event.target.closest( "[data-action='delete']" ); // Ce n'était pas un bouton supprimer if (!deleteButton) { return; } // Vérifie que le bouton appartient bien à la liste if (!list.contains(deleteButton)) { return; } const item = deleteButton.closest("li"); item?.remove(); } );
Lorsque de nombreux éléments doivent être créés, on peut préparer la structure avant de l'ajouter au DOM.

DocumentFragment : conteneur temporaire léger permettant de regrouper plusieurs éléments.

Une fois terminé, le fragment est ajouté au DOM en une seule opération.

replaceChildren() : permet de remplacer tous les enfants d'un élément.

Bonnes pratiques :

• éviter les manipulations DOM inutiles
• regrouper les modifications
• conserver les éléments fréquemment utilisés dans des variables
• préférer les classes CSS pour les changements visuels
const list = document.querySelector("#users"); const fragment = document.createDocumentFragment(); users.forEach((user) => { const item = document.createElement("li"); item.textContent = user.name; fragment.append(item); }); // Une seule insertion dans le DOM list.append(fragment); // Remplacer tout le contenu enfant container.replaceChildren( newElement );

🔒 Closures & Scope avancé

JS Avancé

Comprendre les closures, le lexical scope, le hoisting et le mot-clé this — fondamentaux pour écrire du JavaScript professionnel.

JS Closures Fonctions qui capturent leur environnement ▼
Définition : Une closure est une fonction qui mémorise les variables de sa portée parente, même après que cette portée a été fermée. C'est le concept le plus important de JS.

Comment ça marche : Quand une fonction est créée, elle capture une référence vers son environnement lexical (les variables disponibles au moment de sa création).

Cas d'usage :
• Encapsulation de données (modules)
• Fonctions factory (créer des variantes)
• Callbacks et event handlers
• Mémoïsation / cache

Piège courant : Les closures capturent la référence à la variable, pas sa valeur au moment de la capture.
// Closure basique — compteur privé
function createCounter(initial = 0) {
  let count = initial; // Variable "enfermée"
  
  return {
    increment: () => ++count,
    decrement: () => --count,
    getValue: () => count,
    reset: () => { count = initial; }
  };
}

const counter = createCounter(10);
counter.increment(); // 11
counter.increment(); // 12
counter.getValue();  // 12
// count est inaccessible de l'extérieur ✅

// Factory function — créer des variantes
function createMultiplier(factor) {
  return (number) => number * factor;
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
double(5);  // 10
triple(5);  // 15

// ⚠️ Piège classique avec var dans une boucle
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// Affiche : 3, 3, 3 (pas 0, 1, 2)

// ✅ Solution avec let (block scope)
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// Affiche : 0, 1, 2
JS Lexical Scope & Hoisting Portée statique et remontée des déclarations ▼
Lexical Scope : La portée d'une variable est déterminée par où elle est écrite dans le code, pas où elle est appelée. JS recherche les variables de la portée la plus interne vers la plus externe (scope chain).

Hoisting : JS "remonte" les déclarations en haut de leur portée lors de la compilation :
• var → déclaration remontée, valeur = undefined
• let/const → remontées mais dans la Temporal Dead Zone (erreur si accédées avant déclaration)
• function déclaration → entièrement remontée (corps inclus)
• function expression → comme var/let

Portées : Globale → Fonction → Bloc (let/const)
// ─── Lexical Scope (scope chain) ───
const app = 'NovaDash';

function outer() {
  const version = '2.0';
  
  function inner() {
    const module = 'crypto';
    // Accès : module ✅, version ✅, app ✅
    console.log(`${app} v${version} — ${module}`);
  }
  
  inner();
  // Accès : version ✅, app ✅, module ❌
}

// ─── Hoisting — var vs let ───
console.log(a); // undefined (var est remonté)
console.log(b); // ❌ ReferenceError (TDZ)

var a = 1;
let b = 2;

// ─── Hoisting — fonctions ───
// Déclaration → entièrement remontée
greet(); // ✅ "Hello" — fonctionne !
function greet() { console.log('Hello'); }

// Expression → pas remontée
sayBye(); // ❌ TypeError
const sayBye = () => console.log('Bye');

// ─── Block scope ───
if (true) {
  var x = 1;   // Fuit hors du bloc
  let y = 2;   // Reste dans le bloc
  const z = 3; // Reste dans le bloc
}
console.log(x); // 1
console.log(y); // ❌ ReferenceError
JS this, call, apply, bind Le contexte d'exécution et comment le contrôler ▼
this : Référence l'objet qui appelle la fonction. Sa valeur dépend du comment la fonction est appelée, pas où elle est écrite.

Règles de résolution :
1. new → this = nouvel objet
2. call/apply/bind → this = objet passé
3. Méthode d'objet → this = l'objet
4. Fonction libre → this = window (ou undefined en strict)
5. Arrow function → this = celui du scope parent (lexical)

call vs apply vs bind :
• call(obj, arg1, arg2) → appel immédiat, args séparés
• apply(obj, [args]) → appel immédiat, args en tableau
• bind(obj) → retourne une nouvelle fonction (pas d'appel)
// ─── this selon le contexte ───
const user = {
  name: 'Julien',
  greet() {
    console.log(this.name); // 'Julien'
  },
  greetLater() {
    // ❌ this perdu dans setTimeout
    setTimeout(function() {
      console.log(this.name); // undefined
    }, 100);
    
    // ✅ Arrow function → this lexical
    setTimeout(() => {
      console.log(this.name); // 'Julien'
    }, 100);
  }
};

// ─── call / apply / bind ───
function introduce(greeting, emoji) {
  console.log(`${greeting} ${this.name} ${emoji}`);
}

const dev = { name: 'Julien' };

// call — args séparés
introduce.call(dev, 'Salut', '👋');

// apply — args en tableau
introduce.apply(dev, ['Salut', '👋']);

// bind — retourne une nouvelle fonction
const boundIntro = introduce.bind(dev);
boundIntro('Hello', '🚀'); // Hello Julien 🚀

// ─── Cas pratique : emprunt de méthode ───
const logger = {
  prefix: '[LOG]',
  log(msg) { console.log(`${this.prefix} ${msg}`); }
};

const errorLogger = { prefix: '[ERROR]' };
logger.log.call(errorLogger, 'Crash !');
// [ERROR] Crash !
JS IIFE & Module Pattern Encapsulation et état privé avant ES Modules ▼
IIFE : Immediately Invoked Function Expression — une fonction qui s'exécute immédiatement. Crée une portée isolée pour éviter la pollution globale.

Module Pattern : Combine IIFE + closures pour créer des modules avec état privé et API publique. C'était LE pattern avant ES6 modules.

Revealing Module Pattern : Variante plus lisible qui déclare tout en privé puis "révèle" les méthodes publiques dans le return.

Aujourd'hui : Préférer les ES Modules (import/export), mais le module pattern reste utile pour comprendre les closures et encapsuler du code dans un seul fichier.
// ─── IIFE — Portée isolée ───
(function() {
  const secret = 'abc123';
  console.log('Initialisé');
  // secret n'existe pas dehors
})();

// Avec paramètres
(function(win, doc) {
  // Alias locaux pour window et document
})(window, document);

// ─── Module Pattern ───
const CryptoWallet = (function() {
  // 🔒 État privé
  let balance = 0;
  const transactions = [];
  
  // Fonction privée
  function log(type, amount) {
    transactions.push({ type, amount, date: new Date() });
  }
  
  // 🔓 API publique
  return {
    deposit(amount) {
      balance += amount;
      log('deposit', amount);
    },
    withdraw(amount) {
      if (amount > balance) throw new Error('Fonds insuffisants');
      balance -= amount;
      log('withdraw', amount);
    },
    getBalance: () => balance,
    getHistory: () => [...transactions] // copie
  };
})();

CryptoWallet.deposit(1000);
CryptoWallet.withdraw(250);
CryptoWallet.getBalance();   // 750
CryptoWallet.getHistory();   // [{...}, {...}]
// CryptoWallet.balance       // undefined 🔒
// CryptoWallet.transactions  // undefined 🔒

🧬 Prototypes & Classes avancées

JS Avancé

Le système d'héritage prototype de JavaScript et les classes ES6+ qui l'encapsulent. Comprendre les prototypes est essentiel pour maîtriser le langage.

JS Prototype Chain L'héritage natif de JavaScript ▼
Prototype : Chaque objet JS possède un lien interne [[Prototype]] vers un autre objet. Quand on accède à une propriété absente, JS remonte la chaîne de prototypes jusqu'à null.

__proto__ vs prototype :
• __proto__ → le prototype de l'instance (utiliser Object.getPrototypeOf())
• .prototype → propriété des fonctions constructeurs, assignée aux instances créées avec new

Chaîne : instance → Constructor.prototype → Object.prototype → null

Performance : Les méthodes sur le prototype sont partagées entre toutes les instances (économie mémoire), contrairement aux méthodes définies dans le constructeur.
// ─── Constructeur + prototype ───
function Crypto(name, symbol) {
  this.name = name;     // Propre à chaque instance
  this.symbol = symbol;
}

// Méthode partagée via prototype
Crypto.prototype.display = function() {
  return `${this.name} (${this.symbol})`;
};

const btc = new Crypto('Bitcoin', 'BTC');
const eth = new Crypto('Ethereum', 'ETH');

btc.display(); // "Bitcoin (BTC)"
eth.display(); // "Ethereum (ETH)"

// Même fonction en mémoire
btc.display === eth.display; // true ✅

// ─── Chaîne de prototypes ───
console.log(
  Object.getPrototypeOf(btc) === Crypto.prototype, // true
  Object.getPrototypeOf(Crypto.prototype) === Object.prototype, // true
  Object.getPrototypeOf(Object.prototype) === null  // true — fin
);

// ─── Vérifications ───
btc instanceof Crypto;           // true
btc.hasOwnProperty('name');      // true (propre)
btc.hasOwnProperty('display');   // false (hérité)
'display' in btc;                // true (remonte la chaîne)

// ─── Object.create — héritage sans new ───
const baseConfig = { theme: 'dark', lang: 'fr' };
const userConfig = Object.create(baseConfig);
userConfig.fontSize = 16;
userConfig.theme;    // 'dark' (hérité)
userConfig.fontSize; // 16 (propre)
JS Classes ES6+ & Héritage Sucre syntaxique sur les prototypes ▼
Rôle : Les classes ES6 sont du sucre syntaxique sur le système de prototypes. Même mécanique sous le capot, syntaxe plus claire.

Éléments :
• constructor() → initialisation
• Méthodes → automatiquement sur le prototype
• static → méthode de classe (pas d'instance)
• get/set → propriétés calculées
• extends → héritage
• super() → appel du parent

Champs privés (ES2022) : Préfixe # pour les propriétés/méthodes vraiment privées (pas juste une convention).
class Portfolio {
  // Champ privé (ES2022)
  #holdings = new Map();
  #owner;

  constructor(owner) {
    this.#owner = owner;
  }

  // Méthode publique
  buy(symbol, amount) {
    const current = this.#holdings.get(symbol) || 0;
    this.#holdings.set(symbol, current + amount);
  }

  // Getter
  get totalAssets() {
    return this.#holdings.size;
  }

  // Méthode statique
  static compare(a, b) {
    return a.totalAssets - b.totalAssets;
  }

  // Méthode privée
  #validate(amount) {
    if (amount <= 0) throw new Error('Montant invalide');
  }
}

// ─── Héritage avec extends ───
class PremiumPortfolio extends Portfolio {
  #leverage;

  constructor(owner, leverage = 2) {
    super(owner); // Appel du constructeur parent
    this.#leverage = leverage;
  }

  // Override de méthode
  buy(symbol, amount) {
    super.buy(symbol, amount * this.#leverage);
  }
}

const p = new PremiumPortfolio('Julien', 3);
p.buy('BTC', 1);  // Achète 3 BTC (levier x3)
p.totalAssets;     // 1

// p.#holdings      // ❌ SyntaxError — privé
JS Symbol, Iterators & Generators Protocoles d'itération et valeurs uniques ▼
Symbol : Type primitif qui crée des identifiants uniques et immuables. Utilisé comme clés de propriétés pour éviter les collisions et pour les protocoles internes (iterators, toString...).

Iterator Protocol : Un objet est itérable s'il implémente [Symbol.iterator]() qui retourne un objet avec une méthode next(). Permet l'utilisation de for...of, spread, destructuring.

Generators : Fonctions avec function* qui peuvent suspendre leur exécution avec yield. Retournent un iterator. Parfait pour les séquences paresseuses, la pagination, et les flux asynchrones.
// ─── Symbol ───
const id = Symbol('userId');
const user = { [id]: 42, name: 'Julien' };
user[id];        // 42
Object.keys(user); // ['name'] — Symbol non listé

// Symbols globaux partagés
const KEY = Symbol.for('app.key');
Symbol.for('app.key') === KEY; // true

// ─── Iterator personnalisé ───
class CryptoList {
  #coins;
  constructor(...coins) { this.#coins = coins; }

  [Symbol.iterator]() {
    let i = 0;
    const coins = this.#coins;
    return {
      next() {
        return i < coins.length
          ? { value: coins[i++], done: false }
          : { done: true };
      }
    };
  }
}

const list = new CryptoList('BTC', 'ETH', 'SOL');
for (const coin of list) console.log(coin);
const arr = [...list]; // ['BTC', 'ETH', 'SOL']

// ─── Generator ───
function* fibonacci() {
  let [a, b] = [0, 1];
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

const fib = fibonacci();
fib.next().value; // 0
fib.next().value; // 1
fib.next().value; // 1
fib.next().value; // 2

// Generator pour pagination API
async function* fetchPages(url) {
  let page = 1;
  while (true) {
    const res = await fetch(`${url}?page=${page}`);
    const data = await res.json();
    if (data.length === 0) return;
    yield data;
    page++;
  }
}
JS Proxy & Reflect Intercepter et personnaliser les opérations sur les objets ▼
Proxy : Crée un "piège" autour d'un objet pour intercepter les opérations (lecture, écriture, suppression, appel de fonction...). C'est la base de la réactivité dans Vue.js 3.

Traps courants :
• get(target, prop) → lecture de propriété
• set(target, prop, value) → écriture
• has(target, prop) → opérateur in
• deleteProperty(target, prop) → delete
• apply(target, thisArg, args) → appel de fonction

Reflect : API miroir qui fournit les comportements par défaut des traps. Utilisé dans les handlers pour déléguer au comportement original.
// ─── Proxy — Validation automatique ───
function createValidated(schema) {
  return new Proxy({}, {
    set(target, prop, value) {
      if (schema[prop]) {
        const { type, min, max } = schema[prop];
        if (typeof value !== type)
          throw new TypeError(`${prop} doit être ${type}`);
        if (min !== undefined && value < min)
          throw new RangeError(`${prop} min: ${min}`);
        if (max !== undefined && value > max)
          throw new RangeError(`${prop} max: ${max}`);
      }
      return Reflect.set(target, prop, value);
    },
    get(target, prop) {
      if (!(prop in target))
        console.warn(`Propriété "${prop}" non définie`);
      return Reflect.get(target, prop);
    }
  });
}

const trade = createValidated({
  amount: { type: 'number', min: 0 },
  symbol: { type: 'string' },
  leverage: { type: 'number', min: 1, max: 100 }
});

trade.symbol = 'BTC';     // ✅
trade.amount = 1000;       // ✅
trade.leverage = 50;       // ✅
// trade.amount = -5;      // ❌ RangeError
// trade.symbol = 123;     // ❌ TypeError

// ─── Proxy — Observable (style Vue.js) ───
function reactive(obj, onChange) {
  return new Proxy(obj, {
    set(target, prop, value) {
      const old = target[prop];
      const result = Reflect.set(target, prop, value);
      if (old !== value) onChange(prop, value, old);
      return result;
    }
  });
}

const state = reactive({ count: 0 }, (prop, val) => {
  console.log(`${prop} changed to ${val}`);
});
state.count = 5; // "count changed to 5"

🔄 Event Loop & Concurrence

JS Avancé

Comment JavaScript gère l'asynchrone avec un seul thread. Comprendre l'event loop, les microtasks et macrotasks, et les patterns de concurrence avancés.

JS Event Loop en détail Call stack, microtasks, macrotasks ▼
JavaScript est single-threaded — un seul fil d'exécution. L'event loop permet le multitâche non-bloquant en déléguant les opérations lentes (I/O, timers, réseau) aux APIs du navigateur.

Ordre d'exécution :
1. Call Stack — code synchrone en cours
2. Microtask Queue — Promises (.then), queueMicrotask, MutationObserver
3. Macrotask Queue — setTimeout, setInterval, I/O, events DOM

Règle clé : Les microtasks sont TOUJOURS exécutées avant les macrotasks. Après chaque macrotask, TOUTES les microtasks en attente sont vidées.

requestAnimationFrame : Exécuté avant le prochain repaint du navigateur (~60fps). Idéal pour les animations.
// ─── Quiz : quel ordre d'affichage ? ───
console.log('1 — Sync');

setTimeout(() => console.log('2 — Timeout'), 0);

Promise.resolve().then(() => console.log('3 — Promise'));

queueMicrotask(() => console.log('4 — Microtask'));

console.log('5 — Sync');

// Résultat :
// 1 — Sync        (call stack)
// 5 — Sync        (call stack)
// 3 — Promise     (microtask)
// 4 — Microtask   (microtask)
// 2 — Timeout     (macrotask)

// ─── Pourquoi c'est important ───
// Les microtasks peuvent bloquer le rendu !
Promise.resolve().then(function loop() {
  // ⚠️ Boucle infinie de microtasks
  // Le navigateur ne peut JAMAIS peindre
  Promise.resolve().then(loop);
});

// ─── requestAnimationFrame ───
function animate(element) {
  let start = null;
  
  function step(timestamp) {
    if (!start) start = timestamp;
    const progress = (timestamp - start) / 1000;
    
    element.style.transform = 
      `translateX(${progress * 200}px)`;
    
    if (progress < 2) {
      requestAnimationFrame(step); // ~60fps
    }
  }
  
  requestAnimationFrame(step);
}
JS Promises avancées all, allSettled, race, any et chaînage ▼
Combinateurs de Promises :
• Promise.all([...]) → attend TOUTES. Échoue si UNE échoue
• Promise.allSettled([...]) → attend TOUTES, jamais d'échec. Retourne le statut de chaque promise
• Promise.race([...]) → retourne la PREMIÈRE (résolue OU rejetée)
• Promise.any([...]) → retourne la PREMIÈRE résolue. Échoue seulement si TOUTES échouent

Cas d'usage SaaS :
• all → charger plusieurs API en parallèle
• allSettled → dashboard multi-sources (certaines peuvent échouer)
• race → timeout sur une requête
• any → fallback entre plusieurs serveurs
// ─── Promise.all — tout ou rien ───
const [user, trades, prices] = await Promise.all([
  fetch('/api/user').then(r => r.json()),
  fetch('/api/trades').then(r => r.json()),
  fetch('/api/prices').then(r => r.json())
]);
// Si UNE échoue → tout échoue

// ─── Promise.allSettled — résultats partiels ───
const results = await Promise.allSettled([
  fetch('/api/binance'),
  fetch('/api/kraken'),
  fetch('/api/coinbase')
]);

results.forEach(r => {
  if (r.status === 'fulfilled') 
    console.log('OK:', r.value);
  else 
    console.log('Erreur:', r.reason);
});

// ─── Promise.race — timeout pattern ───
function fetchWithTimeout(url, ms = 5000) {
  return Promise.race([
    fetch(url),
    new Promise((_, reject) =>
      setTimeout(() => reject(new Error('Timeout')), ms)
    )
  ]);
}

// ─── Promise.any — premier succès ───
const fastest = await Promise.any([
  fetch('https://api1.example.com/price'),
  fetch('https://api2.example.com/price'),
  fetch('https://api3.example.com/price')
]);
// Retourne la première réponse réussie

// ─── Chaînage avancé ───
const portfolio = await fetch('/api/portfolio')
  .then(r => { if (!r.ok) throw r; return r.json(); })
  .then(data => data.filter(c => c.value > 100))
  .then(filtered => filtered.sort((a, b) => b.value - a.value))
  .catch(err => { console.error(err); return []; })
  .finally(() => console.log('Requête terminée'));
JS Web Workers Calculs lourds sans bloquer l'UI ▼
Problème : JS est single-threaded — un calcul lourd (analyse de données, ML, crypto) bloque l'interface utilisateur.

Web Workers : Exécutent du JS dans un thread séparé. Communication par messages (postMessage / onmessage). Pas d'accès au DOM.

Types :
• Dedicated Worker — un seul script propriétaire
• Shared Worker — partagé entre plusieurs onglets
• Service Worker — proxy réseau (cache, offline, push notifications)

Transferables : Pour les gros tableaux, utilisez postMessage(data, [buffer]) pour transférer la mémoire (pas copier) — beaucoup plus rapide.
// ─── Main thread (app.js) ───
const worker = new Worker('analyzer.js');

// Envoyer des données au worker
worker.postMessage({
  type: 'ANALYZE',
  prices: [95000, 95200, 94800, 95500, 96000]
});

// Recevoir les résultats
worker.onmessage = (event) => {
  const { sma, rsi, signal } = event.data;
  document.getElementById('signal').textContent = signal;
};

worker.onerror = (err) => console.error('Worker:', err);

// Terminer le worker
// worker.terminate();

// ─── Worker (analyzer.js) ───
self.onmessage = function(event) {
  const { type, prices } = event.data;
  
  if (type === 'ANALYZE') {
    // Calculs lourds sans bloquer l'UI
    const sma = prices.reduce((a, b) => a + b) 
                / prices.length;
    const rsi = calculateRSI(prices);
    const signal = rsi > 70 ? 'SELL' : 
                   rsi < 30 ? 'BUY' : 'HOLD';
    
    // Retourner les résultats
    self.postMessage({ sma, rsi, signal });
  }
};

function calculateRSI(prices) {
  // ... calcul intensif du RSI
  return 65;
}

// ─── Inline Worker (sans fichier séparé) ───
const blob = new Blob([`
  self.onmessage = (e) => {
    const result = e.data * 2;
    self.postMessage(result);
  };
`], { type: 'application/javascript' });

const inlineWorker = new Worker(URL.createObjectURL(blob));
JS AbortController & Patterns async Annuler des requêtes, debounce, retry ▼
AbortController : Permet d'annuler des requêtes fetch, des streams, ou toute opération asynchrone. Essentiel pour les recherches en temps réel, les changements de page et les composants démontés.

Debounce : Attend que l'utilisateur arrête de taper pendant X ms avant d'exécuter. Idéal pour les recherches.

Throttle : Exécute au maximum une fois toutes les X ms. Idéal pour le scroll, le resize.

Retry avec backoff : Relance une requête échouée avec un délai croissant (exponentiel). Standard pour les API instables.
// ─── AbortController — annuler des requêtes ───
let controller = null;

async function searchCrypto(query) {
  // Annuler la requête précédente
  if (controller) controller.abort();
  controller = new AbortController();
  
  try {
    const res = await fetch(`/api/search?q=${query}`, {
      signal: controller.signal
    });
    return await res.json();
  } catch (err) {
    if (err.name === 'AbortError') return; // Ignoré
    throw err;
  }
}

// ─── Debounce ───
function debounce(fn, delay = 300) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), delay);
  };
}

const debouncedSearch = debounce(searchCrypto, 400);
input.addEventListener('input', e => {
  debouncedSearch(e.target.value);
});

// ─── Throttle ───
function throttle(fn, limit = 100) {
  let waiting = false;
  return (...args) => {
    if (waiting) return;
    fn(...args);
    waiting = true;
    setTimeout(() => { waiting = false; }, limit);
  };
}

// ─── Retry avec exponential backoff ───
async function fetchWithRetry(url, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      const res = await fetch(url);
      if (res.ok) return await res.json();
      throw new Error(`HTTP ${res.status}`);
    } catch (err) {
      if (i === retries - 1) throw err;
      const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
      await new Promise(r => setTimeout(r, delay));
    }
  }
}

🏗️ Design Patterns

JS Avancé

Patterns architecturaux essentiels pour structurer des applications JavaScript maintenables et scalables.

JS Singleton & Factory Créer des instances contrôlées ▼
Singleton : Garantit qu'une classe n'a qu'une seule instance. Utile pour les connexions DB, les configs globales, les stores. En JS moderne, un simple module ES6 est déjà un singleton (mis en cache après le premier import).

Factory : Crée des objets sans exposer la logique de création. Le code appelant ne sait pas quelle classe concrète est instanciée — il demande un type et reçoit l'objet.

Quand utiliser :
• Singleton → config, logger, connexion API, cache
• Factory → création d'éléments UI, parsers, notifications
// ─── Singleton avec classe ───
class ApiClient {
  static #instance = null;
  #baseUrl;
  #token;

  constructor(baseUrl) {
    if (ApiClient.#instance) return ApiClient.#instance;
    this.#baseUrl = baseUrl;
    this.#token = null;
    ApiClient.#instance = this;
  }

  setToken(token) { this.#token = token; }

  async get(path) {
    return fetch(`${this.#baseUrl}${path}`, {
      headers: { Authorization: `Bearer ${this.#token}` }
    }).then(r => r.json());
  }
}

const api1 = new ApiClient('https://api.novadash.fr');
const api2 = new ApiClient('https://other.url');
api1 === api2; // true — même instance

// ─── Singleton avec module ES6 (plus simple) ───
// api-client.js
// export default new ApiClient('https://...');
// → Même instance partout grâce au cache du module

// ─── Factory Pattern ───
class Notification {
  static create(type, message) {
    switch (type) {
      case 'success': return new SuccessNotif(message);
      case 'error':   return new ErrorNotif(message);
      case 'warning': return new WarningNotif(message);
      default: throw new Error(`Type inconnu: ${type}`);
    }
  }
}

class SuccessNotif {
  constructor(msg) { this.msg = msg; this.color = '#0f0'; }
  show() { /* ... */ }
}
class ErrorNotif {
  constructor(msg) { this.msg = msg; this.color = '#f00'; }
  show() { /* ... */ }
}

const n = Notification.create('success', 'Trade exécuté');
n.show();
JS Observer & Pub/Sub Communication événementielle entre modules ▼
Observer : Un objet (le sujet) maintient une liste d'observateurs et les notifie automatiquement en cas de changement d'état. Base de la réactivité dans les frameworks.

Pub/Sub (EventEmitter) : Version découplée — les publishers émettent des événements sur un bus central, les subscribers écoutent les événements qui les intéressent. Aucune connaissance directe entre émetteur et récepteur.

Cas d'usage :
• Observer → binding UI, formulaires réactifs
• Pub/Sub → communication entre modules indépendants, microservices frontend, logging centralisé
// ─── Observer Pattern ───
class Observable {
  #observers = new Set();

  subscribe(fn) {
    this.#observers.add(fn);
    return () => this.#observers.delete(fn); // unsubscribe
  }

  notify(data) {
    this.#observers.forEach(fn => fn(data));
  }
}

// Utilisation
const priceStream = new Observable();

const unsub = priceStream.subscribe(price => {
  document.getElementById('btc').textContent = price;
});

priceStream.subscribe(price => {
  if (price > 100000) alert('BTC > 100K !');
});

priceStream.notify(95000); // Tous les observers notifiés
unsub(); // Désabonner le premier observer

// ─── EventEmitter (Pub/Sub) ───
class EventBus {
  #events = new Map();

  on(event, handler) {
    if (!this.#events.has(event))
      this.#events.set(event, new Set());
    this.#events.get(event).add(handler);
    return () => this.#events.get(event).delete(handler);
  }

  emit(event, ...args) {
    this.#events.get(event)?.forEach(fn => fn(...args));
  }

  once(event, handler) {
    const unsub = this.on(event, (...args) => {
      handler(...args);
      unsub();
    });
  }
}

// Bus global de l'app
const bus = new EventBus();

// Module A — émet
bus.emit('trade:executed', { symbol: 'BTC', amount: 0.5 });

// Module B — écoute (ne connaît pas A)
bus.on('trade:executed', (trade) => {
  console.log(`Trade: ${trade.symbol}`);
});
JS Strategy & Command Comportements interchangeables et actions réversibles ▼
Strategy : Encapsule des algorithmes interchangeables. Le contexte délègue le travail à la stratégie active. Élimine les gros blocs if/else ou switch.

Command : Encapsule une action en objet, permettant de la stocker, passer en paramètre, annuler (undo), ou rejouer (redo).

En JS idiomatique : Ces patterns se simplifient souvent en objets littéraux + fonctions de premier ordre (pas besoin de classes complexes comme en Java).
// ─── Strategy — Algorithmes interchangeables ───
const sortStrategies = {
  price:  (a, b) => b.price - a.price,
  name:   (a, b) => a.name.localeCompare(b.name),
  volume: (a, b) => b.volume - a.volume,
  change: (a, b) => b.change24h - a.change24h
};

function sortCryptos(cryptos, strategy = 'price') {
  return [...cryptos].sort(sortStrategies[strategy]);
}

const coins = [
  { name: 'Bitcoin', price: 95000, volume: 50e9, change24h: 2.1 },
  { name: 'Ethereum', price: 3200, volume: 20e9, change24h: -1.3 },
];

sortCryptos(coins, 'price');   // Par prix
sortCryptos(coins, 'name');    // Alphabétique
sortCryptos(coins, 'change');  // Par variation

// ─── Command — Undo/Redo ───
class CommandManager {
  #history = [];
  #position = -1;

  execute(command) {
    // Supprimer le futur si on a fait undo
    this.#history.splice(this.#position + 1);
    command.execute();
    this.#history.push(command);
    this.#position++;
  }

  undo() {
    if (this.#position < 0) return;
    this.#history[this.#position].undo();
    this.#position--;
  }

  redo() {
    if (this.#position >= this.#history.length - 1) return;
    this.#position++;
    this.#history[this.#position].execute();
  }
}

// Commande concrète
const addTrade = (portfolio, trade) => ({
  execute: () => portfolio.push(trade),
  undo: () => portfolio.pop()
});

const mgr = new CommandManager();
const portfolio = [];
mgr.execute(addTrade(portfolio, { symbol: 'BTC' }));
mgr.execute(addTrade(portfolio, { symbol: 'ETH' }));
mgr.undo();  // Retire ETH
mgr.redo();  // Remet ETH
JS Memoization & Currying Optimisation fonctionnelle et composition ▼
Memoization : Cache le résultat d'une fonction pour les mêmes arguments. Évite les recalculs coûteux. Fonctionne uniquement avec les fonctions pures (même entrée → même sortie).

Currying : Transforme une fonction de N arguments en N fonctions d'un argument. Permet la partial application — pré-remplir certains paramètres pour créer des variantes spécialisées.

Composition : Combiner des fonctions simples pour construire des transformations complexes. pipe(f, g, h)(x) = h(g(f(x))).
// ─── Memoization ───
function memoize(fn) {
  const cache = new Map();
  return (...args) => {
    const key = JSON.stringify(args);
    if (cache.has(key)) return cache.get(key);
    const result = fn(...args);
    cache.set(key, result);
    return result;
  };
}

const expensiveCalc = memoize((prices) => {
  console.log('Calcul RSI...');
  return prices.reduce((a, b) => a + b) / prices.length;
});

expensiveCalc([95, 96, 94]); // "Calcul RSI..." → 95
expensiveCalc([95, 96, 94]); // Cache hit — pas de recalcul

// ─── Currying ───
const curry = (fn) => {
  const arity = fn.length;
  return function curried(...args) {
    if (args.length >= arity) return fn(...args);
    return (...more) => curried(...args, ...more);
  };
};

const formatPrice = curry((currency, decimals, amount) => 
  `${amount.toFixed(decimals)} ${currency}`
);

// Créer des variantes spécialisées
const formatUSD = formatPrice('$', 2);
const formatBTC = formatPrice('BTC', 8);

formatUSD(95000);     // "95000.00 $"
formatBTC(0.00045);   // "0.00045000 BTC"

// ─── Composition (pipe) ───
const pipe = (...fns) => (x) => 
  fns.reduce((acc, fn) => fn(acc), x);

const processPrice = pipe(
  (p) => p * 1.2,           // Ajouter 20% de marge
  (p) => Math.round(p),     // Arrondir
  (p) => formatUSD(p)       // Formater
);

processPrice(100); // "120.00 $"
JS Error Handling avancé Erreurs custom, boundaries, logging global ▼
Erreurs personnalisées : Créez des classes d'erreurs spécifiques qui étendent Error. Permet de différencier les types d'erreurs dans les catch et de transporter des données contextuelles (code HTTP, champ invalide...).

Gestion globale :
• window.onerror → erreurs non capturées
• window.onunhandledrejection → Promises non capturées
• try/catch → erreurs synchrones et async/await

Pattern Result : Au lieu de throw, retourner { ok, data, error }. Inspiré de Rust/Go — rend la gestion d'erreur explicite sans exceptions.
// ─── Erreurs personnalisées ───
class AppError extends Error {
  constructor(message, code, details = {}) {
    super(message);
    this.name = 'AppError';
    this.code = code;
    this.details = details;
    this.timestamp = new Date().toISOString();
  }
}

class ValidationError extends AppError {
  constructor(field, message) {
    super(message, 'VALIDATION_ERROR', { field });
    this.name = 'ValidationError';
  }
}

class ApiError extends AppError {
  constructor(status, message) {
    super(message, 'API_ERROR', { status });
    this.name = 'ApiError';
  }
}

// Utilisation
async function createTrade(data) {
  if (!data.symbol) 
    throw new ValidationError('symbol', 'Symbole requis');
  
  const res = await fetch('/api/trade', { method: 'POST' });
  if (!res.ok) 
    throw new ApiError(res.status, 'Trade échoué');
  
  return res.json();
}

try {
  await createTrade({});
} catch (err) {
  if (err instanceof ValidationError) {
    showFieldError(err.details.field, err.message);
  } else if (err instanceof ApiError) {
    showToast(`Erreur serveur (${err.details.status})`);
  } else {
    throw err; // Erreur inattendue → remonter
  }
}

// ─── Pattern Result (sans exceptions) ───
async function safeFetch(url) {
  try {
    const res = await fetch(url);
    const data = await res.json();
    return { ok: true, data };
  } catch (error) {
    return { ok: false, error };
  }
}

const result = await safeFetch('/api/price/BTC');
if (result.ok) console.log(result.data);
else console.error(result.error);

// ─── Gestion globale ───
window.addEventListener('unhandledrejection', (e) => {
  console.error('Promise non gérée:', e.reason);
  // Envoyer au service de monitoring
});

🌐 Web APIs avancées

JS Avancé

APIs navigateur puissantes pour le stockage, les communications temps réel, les notifications et l'observation du DOM.

JS Storage: localStorage, sessionStorage, IndexedDB Persister des données côté client ▼
localStorage : Stockage clé/valeur persistant (survit à la fermeture du navigateur). ~5-10 Mo par domaine. Synchrone.

sessionStorage : Idem mais effacé à la fermeture de l'onglet. Utile pour les données temporaires de session.

IndexedDB : Base de données objet côté client. Asynchrone, transactionnelle, supporte les index et les requêtes. Pas de limite de taille pratique. Pour les apps offline, le cache de données volumineuses.

Cookies vs Storage : Les cookies sont envoyés à chaque requête HTTP (overhead réseau). localStorage/sessionStorage ne sont jamais envoyés au serveur.
// ─── localStorage ───
// Stocker (toujours en string)
localStorage.setItem('theme', 'dark');
localStorage.setItem('user', JSON.stringify({
  name: 'Julien', plan: 'premium'
}));

// Lire
const theme = localStorage.getItem('theme');
const user = JSON.parse(localStorage.getItem('user'));

// Supprimer
localStorage.removeItem('theme');
localStorage.clear(); // Tout supprimer

// Écouter les changements (entre onglets !)
window.addEventListener('storage', (e) => {
  console.log(`${e.key}: ${e.oldValue} → ${e.newValue}`);
});

// ─── Wrapper typé avec expiration ───
const store = {
  set(key, value, ttlMinutes = null) {
    const item = { value, created: Date.now() };
    if (ttlMinutes) item.expires = Date.now() + ttlMinutes * 60000;
    localStorage.setItem(key, JSON.stringify(item));
  },
  get(key) {
    const raw = localStorage.getItem(key);
    if (!raw) return null;
    const item = JSON.parse(raw);
    if (item.expires && Date.now() > item.expires) {
      localStorage.removeItem(key);
      return null; // Expiré
    }
    return item.value;
  }
};

store.set('prices', { BTC: 95000 }, 5); // Expire dans 5min
store.get('prices'); // { BTC: 95000 } ou null

// ─── IndexedDB (simplifié) ───
async function openDB(name, version = 1) {
  return new Promise((resolve, reject) => {
    const req = indexedDB.open(name, version);
    req.onupgradeneeded = (e) => {
      const db = e.target.result;
      db.createObjectStore('trades', { keyPath: 'id' });
    };
    req.onsuccess = () => resolve(req.result);
    req.onerror = () => reject(req.error);
  });
}

const db = await openDB('NovaDash');
const tx = db.transaction('trades', 'readwrite');
tx.objectStore('trades').add({ id: 1, symbol: 'BTC' });
JS WebSocket & Server-Sent Events Communication temps réel bidirectionnelle ▼
WebSocket : Connexion bidirectionnelle persistante entre client et serveur. Pas de polling — les données arrivent instantanément. Idéal pour le trading live, les chats, les jeux.

Server-Sent Events (SSE) : Flux unidirectionnel du serveur vers le client. Plus simple que WebSocket, reconnexion automatique, fonctionne avec HTTP standard. Idéal pour les notifications, les feeds en direct.

Quand choisir :
• WebSocket → bidirectionnel (chat, trading, jeu)
• SSE → serveur → client uniquement (notifications, prix live)
• Polling → fallback si WS/SSE impossibles
// ─── WebSocket ───
class CryptoSocket {
  #ws; #url; #reconnectDelay = 1000;

  constructor(url) { this.#url = url; this.connect(); }

  connect() {
    this.#ws = new WebSocket(this.#url);
    
    this.#ws.onopen = () => {
      console.log('Connecté');
      this.#ws.send(JSON.stringify({
        action: 'subscribe',
        channels: ['BTC-USD', 'ETH-USD']
      }));
    };

    this.#ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
      this.updateUI(data);
    };

    this.#ws.onclose = () => {
      console.log('Déconnecté — reconnexion...');
      setTimeout(() => this.connect(), this.#reconnectDelay);
      this.#reconnectDelay = Math.min(
        this.#reconnectDelay * 2, 30000
      );
    };
  }

  send(data) {
    if (this.#ws.readyState === WebSocket.OPEN) {
      this.#ws.send(JSON.stringify(data));
    }
  }

  updateUI(data) {
    document.querySelector(`#${data.symbol}`)
      .textContent = data.price;
  }
}

const ws = new CryptoSocket('wss://stream.exchange.com');

// ─── Server-Sent Events ───
const source = new EventSource('/api/stream/prices');

source.onmessage = (event) => {
  const price = JSON.parse(event.data);
  console.log(`${price.symbol}: ${price.value}`);
};

// Événements nommés
source.addEventListener('alert', (e) => {
  const alert = JSON.parse(e.data);
  showNotification(alert.message);
});

source.onerror = () => console.log('SSE reconnexion...');
// source.close(); // Fermer
JS Observers : Intersection, Resize, Mutation Observer le DOM de manière performante ▼
IntersectionObserver : Détecte quand un élément entre/sort du viewport. Utilisé pour le lazy loading, les animations au scroll, le tracking de visibilité, le infinite scroll.

ResizeObserver : Détecte les changements de taille d'un élément. Plus précis que window.resize — observe un élément spécifique, pas toute la fenêtre.

MutationObserver : Observe les changements du DOM (ajout/suppression d'éléments, attributs, texte). Utile pour les composants tiers, les extensions, le monitoring de changements dynamiques.

Avantage : Tous les observers sont asynchrones et performants — ils ne bloquent pas le thread principal contrairement aux événements scroll/resize classiques.
// ─── IntersectionObserver — Lazy loading ───
const lazyObserver = new IntersectionObserver(
  (entries) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const img = entry.target;
        img.src = img.dataset.src;
        img.classList.add('loaded');
        lazyObserver.unobserve(img); // Stop
      }
    });
  },
  { rootMargin: '200px' } // Précharger 200px avant
);

document.querySelectorAll('img[data-src]')
  .forEach(img => lazyObserver.observe(img));

// ─── Animate on scroll ───
const animObserver = new IntersectionObserver(
  (entries) => {
    entries.forEach(entry => {
      entry.target.classList.toggle(
        'visible', entry.isIntersecting
      );
    });
  },
  { threshold: 0.2 } // 20% visible
);

document.querySelectorAll('.el-card')
  .forEach(el => animObserver.observe(el));

// ─── ResizeObserver ───
const resizer = new ResizeObserver((entries) => {
  for (const entry of entries) {
    const { width } = entry.contentRect;
    entry.target.classList.toggle('compact', width < 400);
  }
});
resizer.observe(document.querySelector('.dashboard'));

// ─── MutationObserver ───
const mutObserver = new MutationObserver((mutations) => {
  mutations.forEach(m => {
    if (m.type === 'childList') {
      console.log('Nœuds ajoutés:', m.addedNodes.length);
    }
  });
});
mutObserver.observe(document.body, {
  childList: true, subtree: true
});
JS Clipboard, Notifications & Fullscreen APIs système du navigateur ▼
Clipboard API : Lire et écrire dans le presse-papiers. Requiert HTTPS. writeText() pour copier, readText() pour coller (demande permission).

Notifications API : Affiche des notifications système (même quand l'onglet est inactif). Requiert la permission de l'utilisateur. Combinée avec les Service Workers pour les push notifications.

Fullscreen API : Passe un élément en plein écran. Utile pour les dashboards, les graphiques, les jeux et les présentations.

Vibration API : Fait vibrer l'appareil mobile. Utile pour les alertes de prix.
// ─── Clipboard API ───
// Copier du texte
async function copyToClipboard(text) {
  try {
    await navigator.clipboard.writeText(text);
    showToast('Copié !');
  } catch (err) {
    // Fallback pour HTTP
    const ta = document.createElement('textarea');
    ta.value = text;
    document.body.appendChild(ta);
    ta.select();
    document.execCommand('copy');
    ta.remove();
  }
}

// Bouton "copier l'adresse wallet"
copyBtn.onclick = () => copyToClipboard('0xABC...DEF');

// ─── Notifications API ───
async function sendNotification(title, body) {
  if (Notification.permission === 'default') {
    await Notification.requestPermission();
  }
  if (Notification.permission === 'granted') {
    new Notification(title, {
      body,
      icon: '/img/logo.png',
      badge: '/img/badge.png',
      tag: 'price-alert', // Remplace la précédente
      requireInteraction: true
    });
  }
}

sendNotification('BTC Alert', 'Bitcoin > 100K $ !');

// ─── Fullscreen API ───
const chart = document.getElementById('chart');

function toggleFullscreen(element) {
  if (!document.fullscreenElement) {
    element.requestFullscreen();
  } else {
    document.exitFullscreen();
  }
}

document.addEventListener('fullscreenchange', () => {
  chart.classList.toggle('fullscreen', 
    !!document.fullscreenElement);
});

// ─── Vibration (mobile) ───
navigator.vibrate?.(200); // Vibrer 200ms
navigator.vibrate?.([100, 50, 100]); // Pattern
JS Drag & Drop API Glisser-déposer natif HTML5 ▼
Drag & Drop : API native HTML5 pour le glisser-déposer. Pas besoin de librairie pour les cas simples.

Événements source (élément glissé) :
• dragstart → début du drag
• drag → pendant le mouvement
• dragend → fin (drop ou annulation)

Événements cible (zone de dépôt) :
• dragenter → entre dans la zone
• dragover → au-dessus (preventDefault obligatoire !)
• dragleave → sort de la zone
• drop → élément relâché

DataTransfer : Objet qui transporte les données entre la source et la cible. Supporte du texte, du HTML, des fichiers.
// ─── Drag & Drop — Liste réordonneable ───

// Rendre les éléments draggables
document.querySelectorAll('.trade-item').forEach(item => {
  item.draggable = true;
  
  item.addEventListener('dragstart', (e) => {
    e.dataTransfer.setData('text/plain', item.id);
    item.classList.add('dragging');
    e.dataTransfer.effectAllowed = 'move';
  });
  
  item.addEventListener('dragend', () => {
    item.classList.remove('dragging');
  });
});

// Zone de dépôt
const dropzone = document.querySelector('.portfolio');

dropzone.addEventListener('dragover', (e) => {
  e.preventDefault(); // OBLIGATOIRE pour autoriser le drop
  e.dataTransfer.dropEffect = 'move';
  
  // Trouver l'élément le plus proche pour insérer
  const afterEl = getDragAfterElement(dropzone, e.clientY);
  const dragging = document.querySelector('.dragging');
  if (afterEl) {
    dropzone.insertBefore(dragging, afterEl);
  } else {
    dropzone.appendChild(dragging);
  }
});

dropzone.addEventListener('drop', (e) => {
  e.preventDefault();
  const id = e.dataTransfer.getData('text/plain');
  console.log(`Déposé : ${id}`);
});

function getDragAfterElement(container, y) {
  const elements = [...container.querySelectorAll(
    '.trade-item:not(.dragging)')
  ];
  return elements.reduce((closest, child) => {
    const box = child.getBoundingClientRect();
    const offset = y - box.top - box.height / 2;
    if (offset < 0 && offset > closest.offset) {
      return { offset, element: child };
    }
    return closest;
  }, { offset: Number.NEGATIVE_INFINITY }).element;
}

⚡ Performance & Sécurité

JS Avancé

Optimisations de performance, bonnes pratiques de sécurité et outils de mesure pour des applications JavaScript de qualité production.

JS Performance & Optimisation Lazy loading, virtual scroll, Web Vitals ▼
Performance API : Mesurer précisément les temps d'exécution avec performance.now(), performance.mark() et performance.measure().

Lazy Loading : Charger les ressources à la demande. Images avec loading="lazy", modules avec import() dynamique, composants au scroll.

DOM Batching : Minimiser les accès au DOM. Regrouper les lectures puis les écritures. Utiliser DocumentFragment pour les insertions multiples.

Web Vitals :
• LCP (Largest Contentful Paint) < 2.5s
• FID (First Input Delay) < 100ms
• CLS (Cumulative Layout Shift) < 0.1
// ─── Performance API ───
performance.mark('start-fetch');
const data = await fetch('/api/data').then(r => r.json());
performance.mark('end-fetch');
performance.measure('API Call', 'start-fetch', 'end-fetch');

const [measure] = performance.getEntriesByName('API Call');
console.log(`API: ${measure.duration.toFixed(2)}ms`);

// ─── Import dynamique (code splitting) ───
button.onclick = async () => {
  const { ChartModule } = await import('./chart.js');
  ChartModule.render(data);
};

// ─── DOM Batching avec DocumentFragment ───
function renderList(items) {
  const fragment = document.createDocumentFragment();
  
  items.forEach(item => {
    const li = document.createElement('li');
    li.textContent = `${item.name}: ${item.price}$`;
    fragment.appendChild(li);
  });
  
  // UN SEUL accès DOM au lieu de N
  document.getElementById('list').appendChild(fragment);
}

// ─── Virtual Scroll (principe) ───
function virtualScroll(container, items, itemHeight) {
  const visibleCount = Math.ceil(
    container.clientHeight / itemHeight
  ) + 2; // Buffer
  
  container.onscroll = () => {
    const scrollTop = container.scrollTop;
    const startIdx = Math.floor(scrollTop / itemHeight);
    const visibleItems = items.slice(
      startIdx, startIdx + visibleCount
    );
    
    renderList(visibleItems);
    // Spacer pour le scroll
    container.style.paddingTop = 
      `${startIdx * itemHeight}px`;
  };
}

// ─── requestIdleCallback ───
requestIdleCallback((deadline) => {
  while (deadline.timeRemaining() > 0) {
    // Tâches non urgentes (analytics, préchargement)
    doBackgroundWork();
  }
});
JS Sécurité JavaScript XSS, CSRF, sanitization, CSP ▼
XSS (Cross-Site Scripting) : Injection de code malveillant via des données utilisateur. Règle d'or : ne JAMAIS utiliser innerHTML avec des données non fiables. Utiliser textContent ou sanitizer.

CSRF : Requête forgée par un site tiers. Protection : tokens CSRF, cookie SameSite, vérification Origin/Referer.

Content Security Policy (CSP) : En-tête HTTP qui restreint les sources de scripts, styles, images. Bloque les injections même si XSS réussit.

Bonnes pratiques :
• textContent au lieu de innerHTML
• Valider côté client ET serveur
• HttpOnly + Secure sur les cookies sensibles
• CORS restrictif
// ─── XSS — Le problème ───
const userInput = '<img onerror="alert(1)" src=x>';

// ❌ DANGEREUX — exécute le script
element.innerHTML = userInput;

// ✅ SAFE — échappe automatiquement
element.textContent = userInput;

// ─── Sanitization manuelle ───
function sanitizeHTML(str) {
  const div = document.createElement('div');
  div.textContent = str;
  return div.innerHTML;
  // <script> → &lt;script&gt;
}

// ─── Sanitizer API (moderne) ───
// const sanitizer = new Sanitizer();
// element.setHTML(userInput, { sanitizer });

// ─── Échapper pour les URL ───
const query = encodeURIComponent(userInput);
const url = `/search?q=${query}`;
// Empêche l'injection dans l'URL

// ─── Protection CSRF ───
async function secureFetch(url, options = {}) {
  const csrfToken = document.querySelector(
    'meta[name="csrf-token"]'
  )?.content;
  
  return fetch(url, {
    ...options,
    headers: {
      ...options.headers,
      'X-CSRF-Token': csrfToken,
      'Content-Type': 'application/json'
    },
    credentials: 'same-origin' // Cookies same-origin
  });
}

// ─── Validation d'entrée ───
function validateEmail(email) {
  const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  if (!re.test(email)) throw new Error('Email invalide');
  if (email.length > 254) throw new Error('Email trop long');
  return email.toLowerCase().trim();
}

// ─── Stockage sécurisé ───
// ❌ Ne JAMAIS stocker en localStorage :
// - Tokens JWT sensibles
// - Mots de passe
// - Données personnelles en clair
// → Utiliser des cookies HttpOnly + Secure
JS WeakMap, WeakRef & Gestion mémoire Références faibles et garbage collection ▼
Garbage Collector (GC) : JS libère automatiquement la mémoire des objets sans référence. Mais les closures, les event listeners oubliés et les caches peuvent créer des memory leaks.

WeakMap : Map dont les clés sont des références faibles. Si l'objet clé n'a plus d'autre référence, il peut être GC. Pas énumérable, pas de .size. Idéal pour associer des métadonnées à des objets DOM.

WeakSet : Comme Set mais avec des références faibles. Utile pour marquer des objets (visité, traité...).

WeakRef (ES2021) : Référence faible vers un objet. .deref() retourne l'objet ou undefined s'il a été GC. Utile pour les caches intelligents.
// ─── WeakMap — métadonnées sans memory leak ───
const metadata = new WeakMap();

function trackElement(element) {
  metadata.set(element, {
    clickCount: 0,
    createdAt: Date.now()
  });
  
  element.addEventListener('click', () => {
    const data = metadata.get(element);
    data.clickCount++;
  });
}

// Si l'élément est supprimé du DOM,
// les métadonnées sont automatiquement GC ✅

// ─── WeakMap — données privées de classe ───
const _private = new WeakMap();

class Wallet {
  constructor(balance) {
    _private.set(this, { balance, pin: null });
  }
  
  get balance() { return _private.get(this).balance; }
  
  setPin(pin) { _private.get(this).pin = pin; }
}

const w = new Wallet(1000);
w.balance;       // 1000
_private.get(w); // { balance: 1000, pin: null }
// Inaccessible sans la référence _private

// ─── WeakRef — Cache intelligent ───
class SmartCache {
  #cache = new Map();

  set(key, value) {
    this.#cache.set(key, new WeakRef(value));
  }

  get(key) {
    const ref = this.#cache.get(key);
    if (!ref) return undefined;
    
    const obj = ref.deref();
    if (!obj) {
      this.#cache.delete(key); // GC a nettoyé
      return undefined;
    }
    return obj;
  }
}

// ─── Éviter les memory leaks ───
// ❌ Event listener jamais supprimé
// element.addEventListener('click', handler);

// ✅ Nettoyer à la destruction
const controller = new AbortController();
element.addEventListener('click', handler, {
  signal: controller.signal
});
// Plus tard : controller.abort(); // Supprime le listener
JS structuredClone, JSON avancé & Intl Clonage profond, sérialisation et internationalisation ▼
structuredClone() : Clone profond natif d'objets (ES2022). Gère les objets imbriqués, Map, Set, Date, RegExp, ArrayBuffer. Remplace JSON.parse(JSON.stringify()) qui ne gère ni les dates, ni les undefined, ni les références circulaires.

JSON avancé : JSON.stringify accepte un replacer (filtre) et un spacer. JSON.parse accepte un reviver pour transformer les valeurs à la lecture.

Intl (Internationalisation) : API native pour formater les nombres, monnaies, dates, listes et messages selon la locale. Plus besoin de librairies pour le formatage basique.
// ─── structuredClone ───
const original = {
  user: { name: 'Julien' },
  trades: [{ symbol: 'BTC', date: new Date() }],
  meta: new Map([['version', 2]])
};

// Clone profond — indépendant de l'original
const clone = structuredClone(original);
clone.user.name = 'Alice';
original.user.name; // 'Julien' — non affecté ✅
clone.trades[0].date instanceof Date; // true ✅

// ─── JSON avancé ───
// Replacer — filtrer/transformer
const json = JSON.stringify(original, (key, value) => {
  if (key === 'pin') return undefined; // Exclure
  if (value instanceof Date) return value.toISOString();
  return value;
}, 2); // 2 = indentation

// Reviver — transformer à la lecture
const parsed = JSON.parse(json, (key, value) => {
  // Reconvertir les dates ISO en objets Date
  if (typeof value === 'string' && 
      /\d{4}-\d{2}-\d{2}T/.test(value)) {
    return new Date(value);
  }
  return value;
});

// ─── Intl — Formatage localisé ───
// Monnaie
new Intl.NumberFormat('fr-FR', {
  style: 'currency', currency: 'EUR'
}).format(95000); // "95 000,00 €"

new Intl.NumberFormat('en-US', {
  style: 'currency', currency: 'USD',
  notation: 'compact'
}).format(95000); // "$95K"

// Date
new Intl.DateTimeFormat('fr-FR', {
  dateStyle: 'full', timeStyle: 'short'
}).format(new Date()); // "vendredi 28 février 2026 à 14:30"

// Relatif
new Intl.RelativeTimeFormat('fr', { numeric: 'auto' })
  .format(-1, 'day'); // "hier"

// Liste
new Intl.ListFormat('fr', { type: 'conjunction' })
  .format(['BTC', 'ETH', 'SOL']); // "BTC, ETH et SOL"

Introduction à TypeScript

TypeScript est un surensemble typé de JavaScript. Il compile vers du JavaScript standard.

TS vs JSPourquoi TypeScript ?Avantages du typage statique▼
TypeScript = JavaScript + Types. Tout code JS valide est aussi du code TS valide.

Avantages :
• Détection des bugs avant l'exécution
• Autocomplétion IDE et documentation intégrée
• Refactoring sécurisé sur de gros projets
• Standard industrie SaaS (Angular, Next.js, Nest.js)
// JavaScript — bug silencieux function add(a, b) { return a + b; } add("5", 3); // "53" bug! // TypeScript — erreur à la compilation function add(a: number, b: number): number { return a + b; } add("5", 3); // Erreur TS!
: typeAnnotation de typevariable : Type — la syntaxe fondamentale▼
Syntaxe : Ajouter : Type après le nom d'une variable, paramètre ou propriété.

Inférence : TypeScript peut souvent deviner le type automatiquement. Annotez surtout les paramètres de fonctions — les variables locales initiálisées n'en ont souvent pas besoin.
// Annotation explicite let nom: string = "Julien"; let age: number = 28; let actif: boolean = true; // Inférence (pas besoin d'annoter) let ville = "Paris"; // TS infère: string let score = 100; // TS infère: number

Interfaces TypeScript

Les interfaces définissent la forme des objets. Elles sont le pilier de la structuration de données en TypeScript.

interfaceDéclaration d'interfaceDéfinit le contrat d'un objet▼
Rôle : Définit un contrat que les objets doivent respecter — toutes les propriétés requises avec les bons types.

Interface vs type : interface pour les objets (extensible avec extends, declaration merging). type pour les unions, intersections et types complexes.
interface User { id: number; nom: string; email: string; avatar?: string; // optionnel readonly createdAt: Date; } const user: User = { id: 1, nom: "Julien", email: "j@jlqdev.fr", createdAt: new Date() };
extendsHéritage d'interfacesComposer des interfaces par héritage▼
Rôle : extends permet à une interface d'hériter des propriétés d'une ou plusieurs autres interfaces.

Héritage multiple : Contrairement aux classes, une interface peut étendre plusieurs interfaces simultanément.
interface BaseEntity { id: number; createdAt: Date; } interface User extends BaseEntity { nom: string; email: string; } // Héritage multiple interface Admin extends User, Permissions { superAdmin: boolean; }

Types Avancés

Union, literal types, keyof, typeof et discriminated unions.

Union | Type Union La valeur peut être l'un OU l'autre type ▼
Rôle : Définit qu'une valeur peut être de l'un ou l'autre type. Le type le plus utilisé en TypeScript après les types de base.

Narrowing : TypeScript rétrécit automatiquement le type dans les branches conditionnelles après une vérification.
type ID = string | number;
type Status = "active" | "pending" | "deleted";

function processId(id: string | number) {
  if (typeof id === "string") {
    id.toUpperCase(); // ✅ string ici
  } else {
    id.toFixed(2); // ✅ number ici
  }
}
Literal Types Types littéraux Valeurs exactes comme types ▼
Rôle : Restreindre un type à des valeurs exactes. Plus précis que string ou number — TypeScript vérifie que seules ces valeurs sont utilisées.

Combiné avec union : Créer des types d'état finis, très utiles pour les SaaS.
// Type littéral simple
type Direction = "up" | "down" | "left" | "right";

// Type d'état SaaS
type OrderStatus =
  | "cart"
  | "checkout"
  | "paid"
  | "shipped"
  | "delivered";

function move(dir: Direction) { }
move("up"); // ✅
move("jump"); // ❌ Erreur TS
keyof / typeof keyof et typeof Extraire les types de clés et de variables ▼
keyof : Produit un union type de toutes les clés d'un type objet. Permet des accès sûrs aux propriétés.

typeof : Extrait le type TypeScript d'une valeur ou variable. Utile pour typer à partir de valeurs existantes sans redéfinir.
// keyof
interface User { nom: string; age: number; }
type UserKey = keyof User; // "nom" | "age"

function getField<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

// typeof
const config = { port: 3000, host: "localhost" };
type Config = typeof config;
// { port: number; host: string; }
Discriminated Union Union discriminante Union avec discriminant pour différencier les types ▼
Rôle : Pattern où chaque membre de l'union a une propriété commune (le discriminant) avec une valeur littérale unique. TypeScript peut rétrécir le type automatiquement selon cette propriété.

C'est le pattern de base pour les state machines TypeScript.
type AsyncState<T> =
  | { status: "idle" }
  | { status: "loading" }
  | { status: "success"; data: T }
  | { status: "error"; error: string };

function render(state: AsyncState<User>) {
  switch (state.status) {
    case "success": return state.data; // ✅ data typée
    case "error": return state.error; // ✅ error typée
  }
}

Generics

Les Generics permettent de créer des composants réutilisables tout en conservant la sécurité du typage.

<T>Fonction génériqueFonctions qui acceptent n'importe quel type▼
Rôle : <T> est un paramètre de type — un placeholder remplacé par le type réel à l'utilisation.

Avantage vs any : Contrairement à any, les generics conservent la relation entre les types. TypeScript peut vérifier l'utilisation des données.
// Sans generic — perd le type function first(arr: any[]): any { ... } // Avec generic — conserve le type! function first<T>(arr: T[]): T { return arr[0]; } first([1, 2, 3]); // type: number first(["a", "b"]); // type: string
ApiResponse<T>Interface génériqueTypes réutilisables pour les API▼
Rôle : Les interfaces et classes peuvent être génériques — on les paramètre avec un type à l'utilisation.

Pattern SaaS : Un type ApiResponse<T> générique réutilisé pour toutes les réponses API garantit la cohérence.
interface ApiResponse<T> { data: T; status: number; message: string; } type UserRes = ApiResponse<User>; type ListRes = ApiResponse<User[]>; class Store<T> { items: T[] = []; add(item: T) { this.items.push(item); } }

Utility Types

Types utilitaires intégrés pour transformer les types existants.

Partial<T> Partial — Tout optionnel Rend toutes les propriétés optionnelles ▼
Rôle : Crée un type où toutes les propriétés de T deviennent optionnelles. Indispensable pour les fonctions de mise à jour partielle (PATCH).

Implémentation interne : { [P in keyof T]?: T[P] }
interface User {
  nom: string;
  email: string;
  role: string;
}

// Mise à jour partielle
function updateUser(id: string, data: Partial<User>) {
  // data peut avoir 0, 1 ou 3 propriétés
}

updateUser("1", { email: "new@mail.com" }); // ✅
Pick<T, K> / Omit<T, K> Pick et Omit — Sélection de propriétés Sélectionner ou exclure des propriétés d'un type ▼
Pick<T, K> : Sélectionne uniquement les propriétés listées dans K. Crée un sous-type.

Omit<T, K> : Exclut les propriétés listées dans K. L'inverse de Pick. Très utilisé pour créer des DTOs.
interface User {
  id: number; nom: string; email: string;
  password: string; createdAt: Date;
}

// Aperçu public
type UserCard = Pick<User, "id" | "nom">;

// Création (sans id et createdAt auto)
type CreateUserDTO = Omit<User, "id" | "createdAt">;

// DTO public (sans mot de passe)
type PublicUser = Omit<User, "password">;
Record<K, V> Record — Type dictionnaire Crée un type objet avec clés et valeurs typées ▼
Rôle : Crée un type objet dont les clés sont de type K et les valeurs de type V. Alternative typée aux { [key: string]: value }.

Particulièrement utile : Pour des maps, caches, tables de correspondance.
// Dictionnaire
type Scores = Record<string, number>;
const scores: Scores = { alice: 95, bob: 87 };

// Clés typées (union)
type Status = "active" | "pending" | "deleted";
type StatusConfig = Record<Status, { label: string; color: string }>;

const config: StatusConfig = {
  active: { label: "Actif", color: "green" },
  pending: { label: "En attente", color: "yellow" },
  deleted: { label: "Supprimé", color: "red" },
};
ReturnType / Parameters ReturnType et Parameters Extraire les types d'une fonction ▼
ReturnType<T> : Extrait le type de retour d'une fonction. Utile pour typer à partir d'une implémentation existante.

Parameters<T> : Extrait les types des paramètres en tuple.

Awaited<T> : Extrait le type résolu d'une Promise.
async function fetchUser(id: string) {
  return { id, nom: "Julien", role: "admin" };
}

// Extraire le type de retour
type FetchResult = ReturnType<typeof fetchUser>;
// Promise<{ id: string; nom: string; role: string }>

// Résoudre la Promise
type User = Awaited<FetchResult>;
// { id: string; nom: string; role: string }

⚛️ React — Fondamentaux

Frameworks JS

React est une bibliothèque JavaScript permettant de construire des interfaces utilisateur déclaratives à partir de composants. React moderne utilise principalement les composants fonctionnels, les Hooks et une gestion déclarative de l'état. Écosystème : Vite, Next.js, React Router, TanStack Query, Redux Toolkit et Zustand.

React Composants, JSX & Props Construire une interface avec des composants ▼
Composant : fonction JavaScript qui retourne une interface décrite avec du JSX.

JSX : syntaxe permettant d'écrire une structure proche du HTML directement dans JavaScript.

Props : données transmises d'un composant parent vers un composant enfant.

Règles JSX :
• les composants commencent par une majuscule
• utiliser className à la place de class
• utiliser htmlFor à la place de for
• les expressions JavaScript sont placées dans { }
• plusieurs éléments peuvent être regroupés avec <> ... </>
function UserCard({
  name,
  role,
  avatar
}) {

  return (

    <article className="card">

      <img
        src={avatar}
        alt={`Avatar de ${name}`}
      />

      <h3>
        {name}
      </h3>

      <p>
        {role}
      </p>

    </article>

  );

}


// Utilisation

<UserCard
  name="Julien"
  role="Développeur React"
  avatar="/images/avatar.png"
/>
// Fragment : pas de div supplémentaire

function Stats() {

  return (

    <>

      <h2>
        Statistiques
      </h2>

      <p>
        Total : 42
      </p>

    </>

  );

}
React useState & événements Créer et modifier un état local ▼
useState() : Hook permettant à un composant de mémoriser une valeur.

Modifier l'état avec sa fonction setter demande à React d'effectuer un nouveau rendu.

État précédent : lorsqu'une nouvelle valeur dépend de l'ancienne, il est préférable d'utiliser une fonction : setCount(count => count + 1).

Les objets et tableaux présents dans l'état doivent être remplacés plutôt que modifiés directement.

Les événements React utilisent une syntaxe comme onClick, onChange, onSubmit ou onMouseEnter.
import {
  useState
} from 'react';


function Counter() {

  const [
    count,
    setCount
  ] = useState(0);


  const [
    user,
    setUser
  ] = useState({

    name: 'Julien',
    score: 0

  });


  function increment() {

    setCount(
      previous => previous + 1
    );

  }


  function addScore() {

    setUser(previous => ({

      ...previous,

      score:
        previous.score + 10

    }));

  }


  return (

    <div>

      <p>
        Compteur : {count}
      </p>

      <button
        onClick={increment}
      >

        +1

      </button>


      <p>

        {user.name} :
        {user.score} points

      </p>


      <button
        onClick={addScore}
      >

        +10 points

      </button>

    </div>

  );

}
React Formulaires contrôlés input, value, onChange et onSubmit ▼
Un formulaire React peut être contrôlé par l'état du composant.

La propriété value reçoit la valeur de l'état et onChange met cet état à jour.

Cela crée une circulation :

état React → input → événement → état React

onSubmit permet d'intercepter l'envoi du formulaire.
import {
  useState
} from 'react';


function LoginForm() {

  const [
    email,
    setEmail
  ] = useState('');


  const [
    password,
    setPassword
  ] = useState('');


  function handleSubmit(event) {

    event.preventDefault();


    console.log({

      email,
      password

    });

  }


  return (

    <form
      onSubmit={handleSubmit}
    >


      <input

        type="email"

        value={email}

        onChange={
          event =>
            setEmail(
              event.target.value
            )
        }

        placeholder="Email"

      />


      <input

        type="password"

        value={password}

        onChange={
          event =>
            setPassword(
              event.target.value
            )
        }

        placeholder="Mot de passe"

      />


      <button
        type="submit"
      >

        Connexion

      </button>


    </form>

  );

}
React Conditions & listes if, ternaire, && et map() ▼
React utilise simplement JavaScript pour décider quels éléments afficher.

Un if peut être utilisé avant le return.

À l'intérieur du JSX, on utilise souvent :

• l'opérateur ternaire ? :
• && pour un affichage conditionnel
• .map() pour afficher une liste

Chaque élément d'une liste doit posséder une key stable et unique.
function Dashboard({
  user,
  transactions
}) {

  if (!user) {

    return (
      <p>
        Connectez-vous
      </p>
    );

  }


  return (

    <div>


      <h2>
        Bonjour {user.name}
      </h2>


      {user.isAdmin && (

        <button>
          Administration
        </button>

      )}


      {transactions.length > 0 ? (

        <ul>

          {transactions.map(
            transaction => (

              <li
                key={transaction.id}
              >

                {transaction.label}

                {' — '}

                {transaction.amount} €

              </li>

            )
          )}

        </ul>

      ) : (

        <p>
          Aucune transaction
        </p>

      )}


    </div>

  );

}
React useEffect Synchroniser avec un système externe ▼
useEffect() : permet de synchroniser un composant React avec quelque chose d'extérieur à React.

Exemples : API, timer, WebSocket, événements du navigateur ou bibliothèque externe.

Dépendances :
• pas de tableau → après chaque rendu concerné
• [value] → lorsque cette valeur change
• [] → aucune dépendance réactive

En mode développement avec Strict Mode, React peut volontairement effectuer un cycle setup + cleanup supplémentaire pour détecter les effets mal nettoyés.

Si aucune synchronisation externe n'est nécessaire, un Effect n'est souvent pas nécessaire.
import {

  useEffect,
  useState

} from 'react';


function CryptoPrice({
  symbol
}) {

  const [
    price,
    setPrice
  ] = useState(null);


  useEffect(() => {

    const controller =
      new AbortController();


    async function loadPrice() {

      try {

        const response =
          await fetch(

            `/api/crypto/${symbol}`,

            {
              signal:
                controller.signal
            }

          );


        const data =
          await response.json();


        setPrice(
          data.price
        );

      }

      catch (error) {

        if (
          error.name !==
          'AbortError'
        ) {

          console.error(error);

        }

      }

    }


    loadPrice();


    return () => {

      controller.abort();

    };


  }, [symbol]);


  return (

    <p>

      {symbol} :
      {price ?? 'Chargement...'}

    </p>

  );

}
// Exemple avec un timer

useEffect(() => {

  const id =
    setInterval(() => {

      console.log(
        'Actualisation'
      );

    }, 5000);


  return () => {

    clearInterval(id);

  };

}, []);
React useRef DOM et valeurs persistantes ▼
useRef() : crée un objet persistant entre les rendus.

Sa propriété est accessible avec ref.current.

Modifier une ref ne déclenche pas de nouveau rendu.

Deux usages courants :

• accéder directement à un élément du DOM
• mémoriser une information qui ne doit pas être affichée dans l'interface, comme un identifiant de timer.
import {
  useRef
} from 'react';


function SearchBar() {

  const inputRef =
    useRef(null);


  function focusInput() {

    inputRef.current?.focus();

  }


  return (

    <div>

      <input

        ref={inputRef}

        placeholder="Rechercher..."

      />


      <button
        onClick={focusInput}
      >

        Focus

      </button>

    </div>

  );

}
React useReducer & Context État complexe et partage de données ▼
useReducer() : alternative à useState() lorsque la logique de modification de l'état devient plus complexe.

Un reducer reçoit :

state + action → nouvel état

Context : permet de fournir une valeur à plusieurs composants descendants sans transmettre manuellement la même prop à chaque niveau.

Depuis React 19, le contexte lui-même peut être utilisé directement comme Provider : <ThemeContext value={...}>.

Context est particulièrement utile pour le thème, la langue, l'utilisateur connecté ou certaines dépendances partagées.
import {

  createContext,
  useContext,
  useReducer

} from 'react';


const CounterContext =
  createContext(null);


function reducer(
  state,
  action
) {

  switch (action.type) {

    case 'increment':

      return {
        count:
          state.count + 1
      };


    case 'reset':

      return {
        count: 0
      };


    default:

      return state;

  }

}


function CounterProvider({
  children
}) {

  const [
    state,
    dispatch
  ] = useReducer(

    reducer,

    {
      count: 0
    }

  );


  return (

    <CounterContext
      value={{
        state,
        dispatch
      }}
    >

      {children}

    </CounterContext>

  );

}


function Counter() {

  const {
    state,
    dispatch
  } = useContext(
    CounterContext
  );


  return (

    <button

      onClick={() =>
        dispatch({
          type: 'increment'
        })
      }

    >

      {state.count}

    </button>

  );

}
React Custom Hooks Réutiliser de la logique React ▼
Un Custom Hook est une fonction JavaScript dont le nom commence par use.

Il permet d'extraire et de réutiliser une logique utilisant d'autres Hooks React.

Exemples : useFetch, useLocalStorage, useWindowSize, useAuth.

Un Custom Hook partage la logique, mais chaque composant qui l'utilise possède son propre état.
import {

  useEffect,
  useState

} from 'react';


function useFetch(url) {

  const [
    data,
    setData
  ] = useState(null);


  const [
    loading,
    setLoading
  ] = useState(true);


  const [
    error,
    setError
  ] = useState(null);


  useEffect(() => {

    const controller =
      new AbortController();


    async function loadData() {

      setLoading(true);

      setError(null);


      try {

        const response =
          await fetch(
            url,
            {
              signal:
                controller.signal
            }
          );


        if (!response.ok) {

          throw new Error(
            'Erreur HTTP'
          );

        }


        const result =
          await response.json();


        setData(result);

      }

      catch (error) {

        if (
          error.name !==
          'AbortError'
        ) {

          setError(error);

        }

      }

      finally {

        if (
          !controller.signal.aborted
        ) {

          setLoading(false);

        }

      }

    }


    loadData();


    return () => {

      controller.abort();

    };


  }, [url]);


  return {

    data,
    loading,
    error

  };

}
function CryptoList() {

  const {
    data,
    loading,
    error
  } = useFetch(
    '/api/cryptos'
  );


  if (loading) {

    return (
      <p>
        Chargement...
      </p>
    );

  }


  if (error) {

    return (
      <p>
        Une erreur est survenue
      </p>
    );

  }


  return (

    <ul>

      {data.map(
        crypto => (

          <li
            key={crypto.id}
          >

            {crypto.name}

          </li>

        )
      )}

    </ul>

  );

}
React Suspense & use() Attendre une ressource de façon déclarative ▼
Suspense : permet d'afficher une interface temporaire pendant qu'une partie de l'interface attend une ressource.

use() : API React permettant notamment de lire une Promise compatible avec Suspense ou une valeur provenant d'un Context.

Si la Promise n'est pas encore terminée, React suspend le composant et affiche le fallback du Suspense le plus proche.

Contrairement aux Hooks classiques, use() possède des règles particulières et peut notamment être utilisé dans certaines conditions.
import {

  Suspense,
  use

} from 'react';


function User({
  userPromise
}) {

  const user =
    use(userPromise);


  return (

    <h3>
      {user.name}
    </h3>

  );

}


function App({
  userPromise
}) {

  return (

    <Suspense
      fallback={
        <p>
          Chargement...
        </p>
      }
    >

      <User
        userPromise={userPromise}
      />

    </Suspense>

  );

}
React 19 Actions & formulaires modernes useActionState, useFormStatus et useOptimistic ▼
React moderne permet de passer une fonction directement à la propriété action d'un formulaire.

Ces fonctions asynchrones sont appelées Actions.

useActionState() : conserve l'état associé au résultat d'une Action et fournit également un état isPending.

useFormStatus() : permet à un composant situé à l'intérieur d'un formulaire de connaître son état de soumission.

useOptimistic() : permet de modifier temporairement l'interface immédiatement, avant même que le serveur ait confirmé l'opération.
import {
  useActionState
} from 'react';


async function saveName(
  previousState,
  formData
) {

  const name =
    formData.get('name');


  if (!name) {

    return {
      error:
        'Nom obligatoire'
    };

  }


  await fetch(
    '/api/user',
    {

      method: 'POST',

      body:
        JSON.stringify({
          name
        })

    }
  );


  return {
    success: true
  };

}


function ProfileForm() {

  const [

    state,
    formAction,
    isPending

  ] = useActionState(

    saveName,

    null

  );


  return (

    <form
      action={formAction}
    >


      <input
        name="name"
        placeholder="Nom"
      />


      <button
        disabled={isPending}
      >

        {isPending
          ? 'Enregistrement...'
          : 'Enregistrer'
        }

      </button>


      {state?.error && (

        <p>
          {state.error}
        </p>

      )}


      {state?.success && (

        <p>
          Enregistré !
        </p>

      )}


    </form>

  );

}
import {
  useFormStatus
} from 'react-dom';


function SubmitButton() {

  const {
    pending
  } = useFormStatus();


  return (

    <button
      type="submit"
      disabled={pending}
    >

      {pending
        ? 'Envoi...'
        : 'Envoyer'
      }

    </button>

  );

}
React React Router Navigation dans une SPA ▼
React Router est une bibliothèque externe permettant de gérer la navigation d'une application React.

BrowserRouter : utilise l'historique du navigateur.

Routes : contient les différentes routes.

Route : associe une URL à un composant.

Link : permet de naviguer sans recharger complètement la page.

Les paramètres dynamiques utilisent une syntaxe comme /crypto/:id.
import {

  BrowserRouter,
  Routes,
  Route,
  Link

} from 'react-router';


function App() {

  return (

    <BrowserRouter>


      <nav>

        <Link to="/">
          Accueil
        </Link>

        <Link to="/dashboard">
          Dashboard
        </Link>

      </nav>


      <Routes>

        <Route
          path="/"
          element={
            <Home />
          }
        />


        <Route
          path="/dashboard"
          element={
            <Dashboard />
          }
        />


        <Route
          path="/crypto/:id"
          element={
            <CryptoDetail />
          }
        />


        <Route
          path="*"
          element={
            <NotFound />
          }
        />


      </Routes>


    </BrowserRouter>

  );

}
React React Compiler Optimisation automatique des composants ▼
React Compiler est un compilateur d'optimisation pour React.

Il analyse le code au moment du build et peut effectuer automatiquement de nombreuses optimisations de mémorisation.

Cela réduit le besoin d'ajouter manuellement useMemo(), useCallback() ou React.memo() uniquement pour éviter des recalculs ou rendus inutiles.

Ces API existent toujours et restent utiles lorsque le développeur a besoin d'un contrôle précis.

Le plus important reste d'écrire des composants purs et de respecter les règles des Hooks.
// Code React classique

function ProductList({
  products,
  search
}) {

  const filteredProducts =
    products.filter(
      product =>

        product.name
          .toLowerCase()
          .includes(
            search.toLowerCase()
          )

    );


  return (

    <ul>

      {filteredProducts.map(
        product => (

          <li
            key={product.id}
          >

            {product.name}

          </li>

        )
      )}

    </ul>

  );

}
// Avec React Compiler activé,
// le compilateur peut automatiquement
// mémoriser certaines valeurs et
// certains calculs lorsque cela
// est bénéfique.

// useMemo() et useCallback()
// ne disparaissent pas :
// ils restent disponibles lorsque
// l'on souhaite contrôler précisément
// la mémorisation.

💚 Vue.js — Fondamentaux

Frameworks JS

Vue.js est un framework JavaScript progressif permettant de créer des interfaces utilisateur réactives et des applications web modernes. Avec Vue 3, on utilise principalement la Composition API et la syntaxe <script setup>. Écosystème : Vue Router, Pinia, Vite et Nuxt.

Vue Composant SFC & <script setup> Structure moderne d'un composant Vue 3 ▼
SFC (Single File Component) : un composant Vue est généralement écrit dans un fichier .vue. Il peut contenir trois parties : <script>, <template> et <style>.

<script setup> : syntaxe recommandée avec la Composition API. Les variables, fonctions, refs, computed et composants importés sont directement utilisables dans le template.

Template : contient le HTML du composant. Vue peut y utiliser les variables JavaScript grâce aux doubles accolades {{ }}.

Style scoped : avec scoped, le CSS est limité au composant.
<!-- UserCard.vue -->

<script setup>

const props = defineProps({
  name: {
    type: String,
    required: true
  },

  role: {
    type: String,
    default: 'Développeur'
  },

  avatar: {
    type: String,
    default: ''
  }
});

</script>


<template>

  <article class="card">

    <img
      v-if="props.avatar"
      :src="props.avatar"
      :alt="`Avatar de ${props.name}`"
    />

    <h3>
      {{ props.name }}
    </h3>

    <span class="badge">
      {{ props.role }}
    </span>

  </article>

</template>


<style scoped>

.card {
  padding: 16px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.05);
}

</style>


<!-- Utilisation depuis un composant parent -->

<UserCard
  name="Julien"
  role="Développeur Vue.js"
/>
Vue ref(), reactive() & computed() Créer et calculer un état réactif ▼
ref() : crée une valeur réactive. Dans le JavaScript, sa valeur est accessible avec .value. Dans le template, Vue retire automatiquement .value.

reactive() : transforme un objet en objet réactif. Ses propriétés peuvent être modifiées directement.

computed() : crée une valeur calculée à partir d'autres données réactives. Vue recalcule automatiquement cette valeur lorsque ses dépendances changent.

En pratique, utiliser ref() pour la majorité des états est une approche simple et très courante.
<script setup>

import {
  ref,
  reactive,
  computed
} from 'vue';


const count = ref(0);

const name = ref('Julien');


const user = reactive({
  name: 'Julien',
  score: 0
});


const doubled = computed(() => {
  return count.value * 2;
});


function increment() {

  count.value++;

  user.score += 10;

}

</script>


<template>

  <p>
    Compteur : {{ count }}
  </p>

  <p>
    Double : {{ doubled }}
  </p>

  <p>
    {{ user.name }} :
    {{ user.score }} points
  </p>

  <button @click="increment">
    Ajouter
  </button>

</template>
Vue Directives Vue v-if, v-for, v-model, v-bind et v-on ▼
v-if : affiche ou supprime un élément selon une condition.

v-show : conserve l'élément dans le DOM mais le masque avec CSS. Il est pratique pour les éléments souvent affichés ou masqués.

v-for : permet de parcourir un tableau. On ajoute généralement une :key unique.

v-model : crée une liaison entre une valeur Vue et un champ de formulaire.

v-bind : lie dynamiquement un attribut HTML. Son raccourci est :.

v-on : écoute un événement. Son raccourci est @.
<script setup>

import {
  ref,
  computed
} from 'vue';


const search = ref('');

const isLoggedIn = ref(true);


const cryptos = ref([

  {
    id: 1,
    name: 'Bitcoin',
    price: 95000
  },

  {
    id: 2,
    name: 'Ethereum',
    price: 3200
  },

  {
    id: 3,
    name: 'Solana',
    price: 180
  }

]);


const filteredCryptos = computed(() => {

  return cryptos.value.filter((crypto) =>

    crypto.name
      .toLowerCase()
      .includes(search.value.toLowerCase())

  );

});

</script>


<template>

  <input
    v-model="search"
    type="text"
    placeholder="Rechercher une crypto"
  />


  <div v-if="isLoggedIn">

    <h3>
      Dashboard
    </h3>

  </div>


  <p v-else>
    Connectez-vous
  </p>


  <ul>

    <li
      v-for="crypto in filteredCryptos"
      :key="crypto.id"
    >

      {{ crypto.name }}
      —
      {{ crypto.price }} €

    </li>

  </ul>


  <button
    :class="{ active: isLoggedIn }"
    @click="isLoggedIn = !isLoggedIn"
  >

    Changer l'état

  </button>

</template>
Vue Props & Emits Communication entre composants ▼
Les composants Vue communiquent principalement avec props et emits.

Props : permettent au composant parent d'envoyer des données à un composant enfant.

Le flux est descendant :
Parent → Enfant

Emits : permettent à l'enfant d'envoyer un événement vers son parent.

Le flux devient :
Enfant → événement → Parent

C'est un principe essentiel pour créer des composants indépendants et réutilisables.
<!-- ExerciseCounter.vue -->

<script setup>

const props = defineProps({

  count: {
    type: Number,
    default: 0
  }

});


const emit = defineEmits([
  'increment'
]);


function addSeries() {

  emit('increment');

}

</script>


<template>

  <div>

    <p>
      Séries : {{ props.count }}
    </p>

    <button @click="addSeries">
      Ajouter une série
    </button>

  </div>

</template>


<!-- Parent.vue -->

<script setup>

import { ref } from 'vue';

import ExerciseCounter
  from './ExerciseCounter.vue';


const series = ref(3);

</script>


<template>

  <ExerciseCounter

    :count="series"

    @increment="series++"

  />

</template>
Vue watch() & watchEffect() Réagir aux changements d'état ▼
watch() : observe explicitement une ou plusieurs valeurs réactives. La fonction est exécutée lorsque la valeur observée change.

Il est particulièrement utile pour : appels API, sauvegarde locale, synchronisation ou effets secondaires.

watchEffect() : détecte automatiquement les dépendances réactives utilisées dans sa fonction et s'exécute immédiatement.

À l'inverse de computed(), un watcher sert principalement à produire un effet secondaire, et non simplement à calculer une valeur.
<script setup>

import {
  ref,
  watch,
  watchEffect
} from 'vue';


const symbol = ref('BTC');

const price = ref(null);


watch(symbol, async (newSymbol, oldSymbol) => {

  console.log(
    `${oldSymbol} → ${newSymbol}`
  );


  const response = await fetch(
    `/api/price/${newSymbol}`
  );


  price.value =
    await response.json();

});


watchEffect(() => {

  console.log(
    'Symbole actuel :',
    symbol.value
  );

});

</script>
Vue Cycle de vie onMounted, onUpdated et onUnmounted ▼
Un composant Vue passe par plusieurs étapes entre sa création et sa destruction.

onMounted() : exécuté lorsque le composant est affiché dans le DOM.

Exemple : démarrer un timer, charger des données ou initialiser une bibliothèque externe.

onUpdated() : exécuté après une mise à jour du DOM du composant.

onUnmounted() : exécuté lorsque le composant est retiré. Il sert notamment à nettoyer les timers ou événements.
<script setup>

import {
  ref,
  onMounted,
  onUpdated,
  onUnmounted
} from 'vue';


const seconds = ref(0);

let intervalId = null;


onMounted(() => {

  console.log(
    'Composant monté'
  );


  intervalId = setInterval(() => {

    seconds.value++;

  }, 1000);

});


onUpdated(() => {

  console.log(
    'DOM mis à jour'
  );

});


onUnmounted(() => {

  clearInterval(intervalId);

  console.log(
    'Timer supprimé'
  );

});

</script>


<template>

  <p>
    Temps :
    {{ seconds }} secondes
  </p>

</template>
Vue Vue Router Navigation entre les pages d'une application Vue ▼
Vue Router est le routeur officiel de Vue.

Il permet de créer plusieurs pages ou vues dans une application sans recharger complètement le navigateur.

<RouterLink> permet de créer les liens de navigation.

<RouterView> indique l'endroit où Vue doit afficher le composant correspondant à la route.

Les routes peuvent également contenir des paramètres, par exemple : /crypto/:id.
// router/index.js

import {
  createRouter,
  createWebHistory
} from 'vue-router';


const router = createRouter({

  history: createWebHistory(),

  routes: [

    {
      path: '/',
      name: 'home',

      component: () =>
        import('../views/HomeView.vue')
    },

    {
      path: '/crypto/:id',
      name: 'crypto',

      component: () =>
        import('../views/CryptoView.vue')
    },

    {
      path: '/:pathMatch(.*)*',

      component: () =>
        import('../views/NotFoundView.vue')
    }

  ]

});


export default router;


// Dans un composant

import {
  useRoute,
  useRouter
} from 'vue-router';


const route = useRoute();

const router = useRouter();


console.log(
  route.params.id
);


router.push('/');
Vue Pinia Partager un état entre plusieurs composants ▼
Pinia est la solution officielle de gestion d'état pour Vue.

Un store permet de partager des données entre plusieurs composants sans devoir transmettre continuellement des props à travers toute l'application.

Un store peut contenir :

• state → les données
• getters → les valeurs calculées
• actions → les fonctions qui travaillent sur l'état

Pinia devient surtout utile lorsque plusieurs composants éloignés ont besoin des mêmes données.
// stores/crypto.js

import {
  defineStore
} from 'pinia';


export const useCryptoStore =
defineStore('crypto', {

  state: () => ({

    coins: [],

    loading: false

  }),


  getters: {

    topCoins: (state) => {

      return state.coins.slice(0, 10);

    }

  },


  actions: {

    async fetchCoins() {

      this.loading = true;

      try {

        const response =
          await fetch('/api/coins');


        this.coins =
          await response.json();

      }

      finally {

        this.loading = false;

      }

    }

  }

});


// Dans un composant Vue

<script setup>

import {
  onMounted
} from 'vue';

import {
  useCryptoStore
} from '@/stores/crypto';


const store =
  useCryptoStore();


onMounted(() => {

  store.fetchCoins();

});

</script>


<template>

  <p v-if="store.loading">
    Chargement...
  </p>


  <ul v-else>

    <li
      v-for="coin in store.topCoins"
      :key="coin.id"
    >

      {{ coin.name }}

    </li>

  </ul>

</template>

🅰️ Angular — Fondamentaux

Frameworks JS

Angular est un framework web complet basé sur TypeScript. Il propose une architecture orientée composants, un système de réactivité avec les Signals, l'injection de dépendances, un routeur, des formulaires, un client HTTP et une intégration avec RxJS. Écosystème : Angular CLI, Signals, RxJS, Angular Router et Angular Material.

Angular Composants Angular Classe TypeScript, template et styles ▼
Composant : élément fondamental d'une application Angular. Un composant associe une classe TypeScript, un template HTML et éventuellement des styles CSS.

@Component : décorateur qui indique à Angular que la classe représente un composant.

selector : définit la balise HTML permettant d'utiliser le composant.

Les composants Angular modernes sont standalone par défaut. Il n'est donc généralement plus nécessaire de créer des NgModules pour organiser une application moderne.
// user-card.component.ts

import {
  Component
} from '@angular/core';


@Component({

  selector: 'app-user-card',

  template: `
    <article class="card">

      <h3>
        {{ name }}
      </h3>

      <p>
        {{ role }}
      </p>

      <button (click)="sayHello()">
        Dire bonjour
      </button>

    </article>
  `,

  styles: [`
    .card {
      padding: 16px;
      border-radius: 12px;
    }
  `]

})


export class UserCardComponent {

  name = 'Julien';

  role = 'Développeur';


  sayHello() {

    console.log(
      `Bonjour ${this.name}`
    );

  }

}
Angular input(), output() & model() Communication entre composants ▼
Les composants doivent pouvoir communiquer entre eux.

input() : permet au composant parent d'envoyer une donnée vers un composant enfant.

output() : permet au composant enfant d'émettre un événement vers son parent.

model() : permet une communication bidirectionnelle. Le parent transmet une valeur et l'enfant peut la modifier.

Le principe est comparable aux props et emits de Vue.
// counter.component.ts

import {
  Component,
  input,
  output,
  model
} from '@angular/core';


@Component({

  selector: 'app-counter',

  template: `

    <h3>
      {{ title() }}
    </h3>

    <p>
      Valeur : {{ count() }}
    </p>

    <button (click)="increment()">
      +1
    </button>

  `

})


export class CounterComponent {

  // Parent → enfant
  title = input('Compteur');


  // Valeur modifiable dans les deux sens
  count = model(0);


  // Enfant → parent
  changed = output<number>();


  increment() {

    this.count.update(
      value => value + 1
    );

    this.changed.emit(
      this.count()
    );

  }

}
<!-- Utilisation dans le parent -->

<app-counter

  title="Séries"

  [(count)]="series"

  (changed)="onSeriesChanged($event)"

/>
Angular Signals & état réactif signal(), computed() et effect() ▼
signal() : crée une donnée réactive. Sa valeur se lit en appelant le signal comme une fonction : count().

.set() : remplace directement la valeur.

.update() : calcule une nouvelle valeur à partir de l'ancienne.

computed() : crée une valeur calculée automatiquement à partir d'autres Signals.

effect() : exécute un effet secondaire lorsqu'une dépendance réactive utilisée dans l'effet change.
import {

  Component,
  signal,
  computed,
  effect

} from '@angular/core';


@Component({

  selector: 'app-counter',

  template: `

    <p>
      Compteur : {{ count() }}
    </p>

    <p>
      Double : {{ doubled() }}
    </p>

    <button (click)="increment()">
      +1
    </button>

    <button (click)="reset()">
      Reset
    </button>

  `

})


export class CounterComponent {

  count = signal(0);


  doubled = computed(() =>

    this.count() * 2

  );


  constructor() {

    effect(() => {

      console.log(
        'Compteur :',
        this.count()
      );

    });

  }


  increment() {

    this.count.update(
      value => value + 1
    );

  }


  reset() {

    this.count.set(0);

  }

}
Angular Templates & Control Flow @if, @for, bindings et pipes ▼
Angular dispose d'une syntaxe de template permettant d'utiliser directement les données du composant.

{{ expression }} : interpolation d'une valeur dans le HTML.

[property] : lie une propriété HTML à une valeur TypeScript.

(event) : écoute un événement comme un clic.

@if : permet un affichage conditionnel.

@for : permet de parcourir une liste. track permet à Angular d'identifier chaque élément efficacement.

Pipes : transforment une valeur directement dans le template, par exemple currency, date ou uppercase.
import {

  Component,
  signal

} from '@angular/core';

import {

  CurrencyPipe

} from '@angular/common';


@Component({

  selector: 'app-dashboard',

  imports: [
    CurrencyPipe
  ],

  template: `

    @if (isLoggedIn()) {

      <h3>
        Bienvenue {{ userName() }}
      </h3>

    }

    @else {

      <p>
        Connectez-vous
      </p>

    }


    @for (
      crypto of cryptos();
      track crypto.id
    ) {

      <div class="crypto-row">

        <span>
          {{ crypto.name }}
        </span>

        <span>
          {{ crypto.price | currency:'EUR' }}
        </span>

        <button
          [disabled]="crypto.price === 0"
          (click)="selectCrypto(crypto)"
        >

          Détails

        </button>

      </div>

    }

    @empty {

      <p>
        Aucune crypto
      </p>

    }

  `

})


export class DashboardComponent {

  isLoggedIn =
    signal(true);


  userName =
    signal('Julien');


  cryptos = signal([

    {
      id: 1,
      name: 'Bitcoin',
      price: 95000
    },

    {
      id: 2,
      name: 'Ethereum',
      price: 3200
    }

  ]);


  selectCrypto(crypto: any) {

    console.log(crypto);

  }

}
Angular Cycle de vie ngOnInit, ngOnDestroy et DestroyRef ▼
Un composant Angular possède un cycle de vie.

ngOnInit() : exécuté après l'initialisation du composant. Il est souvent utilisé pour charger des données.

ngOnChanges() : exécuté lorsque certaines valeurs d'entrée changent.

ngOnDestroy() : exécuté juste avant la destruction du composant. Il permet notamment de nettoyer des timers.

DestroyRef : API moderne permettant d'enregistrer directement une fonction de nettoyage.
import {

  Component,
  OnInit,
  inject,
  DestroyRef

} from '@angular/core';


@Component({

  selector: 'app-timer',

  template: `

    <p>
      Temps :
      {{ seconds }}
      secondes
    </p>

  `

})


export class TimerComponent
implements OnInit {

  seconds = 0;

  private intervalId:
    ReturnType<typeof setInterval> | null = null;


  private destroyRef =
    inject(DestroyRef);


  ngOnInit() {

    this.intervalId =
      setInterval(() => {

        this.seconds++;

      }, 1000);


    this.destroyRef.onDestroy(() => {

      if (this.intervalId) {

        clearInterval(
          this.intervalId
        );

      }

    });

  }

}
Angular Services & Dependency Injection Partager la logique et les données ▼
Service : classe utilisée pour regrouper une logique qui ne doit pas appartenir directement à un composant.

Un service peut contenir : appels API, logique métier, stockage ou état partagé.

@Injectable : indique qu'une classe peut participer au système d'injection de dépendances Angular.

providedIn: 'root' : crée généralement une seule instance du service pour toute l'application.

inject() : permet de récupérer simplement une dépendance.
// counter.service.ts

import {

  Injectable,
  signal

} from '@angular/core';


@Injectable({
  providedIn: 'root'
})


export class CounterService {

  count = signal(0);


  increment() {

    this.count.update(
      value => value + 1
    );

  }

}
// counter.component.ts

import {

  Component,
  inject

} from '@angular/core';

import {

  CounterService

} from './counter.service';


@Component({

  selector: 'app-counter',

  template: `

    <p>
      {{ counter.count() }}
    </p>

    <button
      (click)="counter.increment()"
    >

      +1

    </button>

  `

})


export class CounterComponent {

  counter =
    inject(CounterService);

}
Angular Formulaires Signal Forms, Reactive Forms et validation ▼
Angular propose plusieurs façons de créer des formulaires.

Template-driven Forms : approche simple utilisant principalement le template et ngModel.

Reactive Forms : approche basée sur FormControl et FormGroup. Elle reste très utilisée dans les applications existantes.

Signal Forms : approche moderne construite autour des Signals. Le modèle du formulaire est stocké dans un signal et Angular synchronise automatiquement les champs.

Signal Forms permet également de gérer validation, erreurs, état touched, dirty et disabled.
import {

  Component,
  signal

} from '@angular/core';


import {

  form,
  FormField,
  required,
  email,
  submit

} from '@angular/forms/signals';


@Component({

  selector: 'app-login',

  imports: [
    FormField
  ],

  template: `

    <form
      (submit)="onSubmit($event)"
    >


      <label>

        Email

        <input
          type="email"
          [formField]="loginForm.email"
        />

      </label>


      <label>

        Mot de passe

        <input
          type="password"
          [formField]="loginForm.password"
        />

      </label>


      <button
        type="submit"
        [disabled]="loginForm().invalid()"
      >

        Connexion

      </button>


    </form>

  `

})


export class LoginComponent {

  loginModel = signal({

    email: '',

    password: ''

  });


  loginForm =
    form(
      this.loginModel,

      path => {

        required(
          path.email
        );

        email(
          path.email
        );

        required(
          path.password
        );

      }
    );


  onSubmit(event: Event) {

    event.preventDefault();


    submit(
      this.loginForm,

      async () => {

        console.log(
          this.loginModel()
        );

      }
    );

  }

}
Angular HTTP & RxJS HttpClient, Observables et httpResource() ▼
HttpClient : service Angular permettant d'effectuer des requêtes HTTP vers une API.

Observable : flux de données utilisé intensivement par Angular et RxJS. Une requête HttpClient retourne généralement un Observable.

subscribe() : permet de recevoir les valeurs émises par l'Observable.

RxJS : fournit de nombreux opérateurs comme map, filter, switchMap ou debounceTime.

httpResource() : API moderne adaptée aux requêtes GET réactives. Elle expose directement les données, l'état de chargement et les erreurs sous forme réactive.
import {

  Component,
  signal

} from '@angular/core';


import {

  httpResource

} from '@angular/common/http';


interface User {

  id: number;

  name: string;

}


@Component({

  selector: 'app-user',

  template: `

    @if (user.isLoading()) {

      <p>
        Chargement...
      </p>

    }


    @else if (user.error()) {

      <p>
        Erreur de chargement
      </p>

    }


    @else if (user.hasValue()) {

      <h3>
        {{ user.value().name }}
      </h3>

    }

  `

})


export class UserComponent {

  userId =
    signal(1);


  user =
    httpResource<User>(
      () =>
        `/api/users/${this.userId()}`
    );

}
// Pour POST, PUT ou DELETE,
// on utilise généralement HttpClient.

import {
  HttpClient
} from '@angular/common/http';

import {
  inject
} from '@angular/core';


private http =
  inject(HttpClient);


saveUser(user: User) {

  this.http
    .post('/api/users', user)
    .subscribe({

      next: data => {

        console.log(data);

      },

      error: error => {

        console.error(error);

      }

    });

}
Angular RxJS & Signals toSignal() et takeUntilDestroyed() ▼
Angular permet de faire communiquer facilement les Observables RxJS et les Signals.

toSignal() : transforme un Observable en Signal.

Cela permet d'utiliser une donnée RxJS dans le template avec la syntaxe habituelle des Signals.

takeUntilDestroyed() : arrête automatiquement une souscription RxJS lorsque le composant est détruit.

Cela évite de nombreuses fuites mémoire liées aux abonnements manuels.
import {

  Component

} from '@angular/core';


import {

  interval

} from 'rxjs';


import {

  toSignal

} from '@angular/core/rxjs-interop';


@Component({

  selector: 'app-clock',

  template: `

    <p>
      Compteur :
      {{ counter() }}
    </p>

  `

})


export class ClockComponent {

  counter$ =
    interval(1000);


  counter =
    toSignal(
      this.counter$,
      {
        initialValue: 0
      }
    );

}
Angular Angular Router Navigation, paramètres, lazy loading et guards ▼
Angular Router : gère la navigation dans une application Angular.

routerLink : crée un lien interne sans recharger complètement la page.

router-outlet : représente l'emplacement où Angular affiche le composant de la route active.

Lazy loading : permet de charger un composant seulement lorsqu'il est nécessaire.

Guards : contrôlent l'accès à une route. Ils sont souvent utilisés pour l'authentification.

Les routes dynamiques comme /user/:id permettent de transmettre un paramètre dans l'URL.
// app.routes.ts

import {

  Routes

} from '@angular/router';


export const routes: Routes = [

  {

    path: '',

    loadComponent: () =>

      import(
        './home/home.component'
      )

      .then(
        module =>
          module.HomeComponent
      )

  },


  {

    path: 'dashboard',

    loadComponent: () =>

      import(
        './dashboard/dashboard.component'
      )

      .then(
        module =>
          module.DashboardComponent
      ),

    canActivate: [
      authGuard
    ]

  },


  {

    path: 'user/:id',

    loadComponent: () =>

      import(
        './user/user.component'
      )

      .then(
        module =>
          module.UserComponent
      )

  },


  {

    path: '**',

    loadComponent: () =>

      import(
        './not-found/not-found.component'
      )

      .then(
        module =>
          module.NotFoundComponent
      )

  }

];
// auth.guard.ts

import {

  inject

} from '@angular/core';


import {

  Router

} from '@angular/router';


export const authGuard = () => {

  const auth =
    inject(AuthService);

  const router =
    inject(Router);


  if (
    auth.isAuthenticated()
  ) {

    return true;

  }


  return router.parseUrl(
    '/login'
  );

};
<!-- Navigation Angular -->

<a
  routerLink="/dashboard"
  routerLinkActive="active"
>

  Dashboard

</a>


<router-outlet />