# Fichier: python_cheats/cheatsheets/html.txt
# Cheatsheet HTML - Guide Complet pour Débutants


[OK] INTRODUCTION À HTML

HTML (HyperText Markup Language) est le langage de balisage standard pour créer des pages web.
Il structure le contenu d'une page web en utilisant des balises (tags).

# Concepts de base:
# - HTML n'est PAS un langage de programmation, c'est un langage de balisage
# - Les balises définissent la structure et le sens du contenu
# - CSS gère l'apparence, JavaScript gère l'interactivité
# - HTML5 est la version actuelle (2014+)


[OK] STRUCTURE DE BASE D'UN DOCUMENT HTML

<!-- Structure minimale valide -->
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Titre de la page</title>
</head>
<body>
    <h1>Contenu de la page</h1>
</body>
</html>

# Explication ligne par ligne:
# <!DOCTYPE html>           - Déclare que c'est un document HTML5
# <html lang="fr">          - Élément racine, lang="fr" pour français
# <head>                    - Contient métadonnées (non visibles)
# <meta charset="UTF-8">    - Encodage des caractères (accents, etc.)
# <meta name="viewport">    - Responsive design (mobile)
# <title>                   - Titre dans l'onglet du navigateur
# <body>                    - Contenu visible de la page


[OK] SYNTAXE DES BALISES HTML

# === BALISE OUVRANTE ET FERMANTE ===
<p>Ceci est un paragraphe</p>
<h1>Ceci est un titre</h1>
<div>Ceci est une division</div>

# Syntaxe: <nomBalise>contenu</nomBalise>
# [OK] Toujours fermer les balises ouvertes
# [OK] Respecter l'ordre d'imbrication

# === BALISES AUTO-FERMANTES ===
<img src="image.jpg" alt="Description">
<br>
<hr>
<input type="text">
<meta charset="UTF-8">

# Syntaxe: <nomBalise attribut="valeur">
# Pas de balise fermante nécessaire

# === ATTRIBUTS ===
<a href="https://example.com" target="_blank" title="Infobulle">Lien</a>
<img src="photo.jpg" alt="Photo" width="300" height="200">
<div id="conteneur" class="principal actif">Contenu</div>

# Syntaxe: nomAttribut="valeur"
# Attributs communs: id, class, style, title, data-*

# === IMBRICATION CORRECTE ===
[OK] CORRECT:
<div>
    <p>Texte <strong>gras</strong></p>
</div>

[X] INCORRECT:
<div>
    <p>Texte <strong>gras</p></strong>
</div>

# === COMMENTAIRES ===
<!-- Ceci est un commentaire -->
<!-- 
    Commentaire
    sur plusieurs
    lignes
-->

# Raccourci: Ctrl+/ (VS Code, la plupart des éditeurs)


[OK] BALISES DE STRUCTURE SÉMANTIQUE (HTML5)

<!-- Structure moderne d'une page -->
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <title>Mon Site</title>
</head>
<body>
    <!-- En-tête principal -->
    <header>
        <nav>
            <ul>
                <li><a href="#accueil">Accueil</a></li>
                <li><a href="#apropos">À propos</a></li>
                <li><a href="#contact">Contact</a></li>
            </ul>
        </nav>
    </header>

    <!-- Contenu principal -->
    <main>
        <!-- Section de contenu -->
        <section id="accueil">
            <h1>Bienvenue</h1>
            <article>
                <h2>Premier article</h2>
                <p>Contenu de l'article...</p>
            </article>
        </section>

        <!-- Contenu annexe -->
        <aside>
            <h3>Publicité</h3>
            <p>Contenu latéral...</p>
        </aside>
    </main>

    <!-- Pied de page -->
    <footer>
        <p>&copy; 2024 Mon Site</p>
    </footer>
</body>
</html>

# === BALISES SÉMANTIQUES EXPLIQUÉES ===

<header>        # En-tête de page ou de section
<nav>           # Menu de navigation
<main>          # Contenu principal (unique par page)
<section>       # Section thématique de contenu
<article>       # Contenu autonome (article de blog, post)
<aside>         # Contenu annexe (sidebar, publicité)
<footer>        # Pied de page ou de section
<figure>        # Contenu illustratif (image + légende)
<figcaption>    # Légende d'une figure
<time>          # Date/heure
<mark>          # Texte surligné
<details>       # Contenu pliable/dépliable
<summary>       # Titre d'un élément <details>

# Avantages des balises sémantiques:
# [OK] Meilleur SEO (référencement Google)
# [OK] Accessibilité (lecteurs d'écran)
# [OK] Code plus lisible et maintenable


[OK] TITRES ET PARAGRAPHES

# === TITRES (6 NIVEAUX) ===
<h1>Titre de niveau 1 (le plus important)</h1>
<h2>Titre de niveau 2</h2>
<h3>Titre de niveau 3</h3>
<h4>Titre de niveau 4</h4>
<h5>Titre de niveau 5</h5>
<h6>Titre de niveau 6 (le moins important)</h6>

# Bonnes pratiques:
# [OK] Un seul <h1> par page (titre principal)
# [OK] Respecter la hiérarchie (h1 > h2 > h3...)
# [X] Ne pas sauter de niveaux (h1 > h3 sans h2)
# [X] Ne pas utiliser les titres juste pour la taille

# === PARAGRAPHES ===
<p>Ceci est un paragraphe de texte.</p>
<p>Ceci est un autre paragraphe.</p>

# Paragraphe avec retour à la ligne
<p>
    Première ligne<br>
    Deuxième ligne
</p>

# === SÉPARATEUR HORIZONTAL ===
<hr>    <!-- Ligne horizontale de séparation -->

# === PRÉFORMATÉ ===
<pre>
    Texte préformaté
    Les    espaces    sont    respectés
    Les retours à la ligne aussi
</pre>

# === CITATION ===
<blockquote cite="https://source.com">
    Longue citation en bloc
</blockquote>

<q>Citation courte en ligne</q>

<cite>Source de la citation</cite>


[OK] MISE EN FORME DU TEXTE

# === EMPHASE ET IMPORTANCE ===
<strong>Texte important (gras)</strong>
<em>Texte emphatique (italique)</em>
<b>Texte en gras (sans importance sémantique)</b>
<i>Texte en italique (sans emphase)</i>
<u>Texte souligné</u>
<s>Texte barré</s>

# strong vs b: strong = importance sémantique, b = style visuel
# em vs i: em = emphase sémantique, i = style visuel
# Préférer <strong> et <em> pour le SEO

# === INDICES ET EXPOSANTS ===
<p>H<sub>2</sub>O (indice)</p>
<p>X<sup>2</sup> (exposant)</p>

# === TEXTE SURLIGNÉ ===
<mark>Texte surligné</mark>

# === PETIT TEXTE ===
<small>Texte en petits caractères</small>

# === CODE ET TECHNIQUE ===
<code>console.log('Hello')</code>       <!-- Code inline -->
<kbd>Ctrl + C</kbd>                     <!-- Touche clavier -->
<samp>Sortie programme</samp>           <!-- Exemple de sortie -->
<var>x = 10</var>                       <!-- Variable -->

# Bloc de code
<pre><code>
function hello() {
    console.log('Hello World');
}
</code></pre>

# === DÉFINITIONS ET ABRÉVIATIONS ===
<abbr title="HyperText Markup Language">HTML</abbr>
<dfn>HTML</dfn> est un langage de balisage

# === INSERTION ET SUPPRESSION ===
<ins>Texte inséré</ins>     <!-- Souligné -->
<del>Texte supprimé</del>   <!-- Barré -->


[OK] LISTES

# === LISTE NON ORDONNÉE (puces) ===
<ul>
    <li>Premier élément</li>
    <li>Deuxième élément</li>
    <li>Troisième élément</li>
</ul>

# === LISTE ORDONNÉE (numérotée) ===
<ol>
    <li>Première étape</li>
    <li>Deuxième étape</li>
    <li>Troisième étape</li>
</ol>

# Liste ordonnée avec attributs
<ol start="5">              <!-- Commence à 5 -->
<ol type="A">               <!-- A, B, C... -->
<ol type="a">               <!-- a, b, c... -->
<ol type="I">               <!-- I, II, III... -->
<ol type="i">               <!-- i, ii, iii... -->
<ol reversed>               <!-- Ordre décroissant -->

# === LISTE DE DÉFINITIONS ===
<dl>
    <dt>HTML</dt>
    <dd>Langage de balisage pour le web</dd>
    
    <dt>CSS</dt>
    <dd>Langage de style pour le web</dd>
    
    <dt>JavaScript</dt>
    <dd>Langage de programmation pour le web</dd>
</dl>

# === LISTES IMBRIQUÉES ===
<ul>
    <li>Élément 1</li>
    <li>Élément 2
        <ul>
            <li>Sous-élément 2.1</li>
            <li>Sous-élément 2.2</li>
        </ul>
    </li>
    <li>Élément 3</li>
</ul>


[OK] LIENS (ANCRES)

# === LIEN EXTERNE ===
<a href="https://www.google.com">Aller sur Google</a>

# === LIEN VERS AUTRE PAGE DU SITE ===
<a href="contact.html">Page de contact</a>
<a href="pages/about.html">Page à propos</a>
<a href="../index.html">Retour à l'accueil</a>

# Chemins:
# contact.html          - Même dossier
# pages/contact.html    - Sous-dossier
# ../index.html         - Dossier parent
# /index.html           - Racine du site

# === LIEN VERS ANCRE (section de page) ===
<a href="#section1">Aller à la section 1</a>

<!-- Plus loin dans la page -->
<section id="section1">
    <h2>Section 1</h2>
</section>

# === LIEN VERS AUTRE PAGE + ANCRE ===
<a href="page2.html#section3">Section 3 de page 2</a>

# === ATTRIBUTS IMPORTANTS ===
<a href="url" target="_blank">Ouvrir dans nouvel onglet</a>
<a href="url" target="_self">Ouvrir dans même fenêtre (défaut)</a>
<a href="url" title="Info au survol">Lien avec infobulle</a>
<a href="url" download>Télécharger le fichier</a>
<a href="url" rel="nofollow">Lien non suivi par Google</a>

# === LIENS SPÉCIAUX ===
<a href="mailto:contact@example.com">Envoyer un email</a>
<a href="mailto:contact@example.com?subject=Sujet&body=Message">Email pré-rempli</a>
<a href="tel:+33123456789">Appeler</a>
<a href="sms:+33123456789">Envoyer SMS</a>

# === LIEN BOUTON ===
<a href="page.html" class="button">Bouton stylisé en CSS</a>


[OK] IMAGES

# === IMAGE SIMPLE ===
<img src="image.jpg" alt="Description de l'image">

# Attribut src: chemin vers l'image
# Attribut alt: texte alternatif (obligatoire pour accessibilité)

# === IMAGES AVEC DIMENSIONS ===
<img src="photo.jpg" alt="Photo" width="600" height="400">
<img src="photo.jpg" alt="Photo" style="width: 100%; max-width: 600px;">

# === CHEMINS D'IMAGES ===
<img src="photo.jpg">                    <!-- Même dossier -->
<img src="images/photo.jpg">             <!-- Sous-dossier -->
<img src="../images/photo.jpg">          <!-- Dossier parent -->
<img src="/images/photo.jpg">            <!-- Racine du site -->
<img src="https://site.com/photo.jpg">   <!-- URL externe -->

# === FORMATS D'IMAGES ===
# JPG/JPEG  - Photos (compression avec perte)
# PNG       - Images avec transparence
# GIF       - Animations simples
# SVG       - Images vectorielles (logos, icônes)
# WebP      - Format moderne (meilleure compression)

# === IMAGE CLIQUABLE (LIEN) ===
<a href="page.html">
    <img src="banniere.jpg" alt="Bannière">
</a>

# === FIGURE AVEC LÉGENDE ===
<figure>
    <img src="photo.jpg" alt="Photo de paysage">
    <figcaption>Belle vue sur les montagnes</figcaption>
</figure>

# === IMAGES RESPONSIVE ===
<img src="small.jpg" 
     srcset="small.jpg 300w, medium.jpg 600w, large.jpg 1200w"
     sizes="(max-width: 600px) 300px, (max-width: 1200px) 600px, 1200px"
     alt="Image responsive">

# === LAZY LOADING (chargement différé) ===
<img src="image.jpg" alt="Photo" loading="lazy">

# === IMAGE DE FOND (CSS) ===
<div style="background-image: url('fond.jpg'); height: 300px;"></div>


[OK] TABLEAUX

# === TABLEAU SIMPLE ===
<table>
    <tr>
        <th>Nom</th>
        <th>Âge</th>
        <th>Ville</th>
    </tr>
    <tr>
        <td>Alice</td>
        <td>25</td>
        <td>Paris</td>
    </tr>
    <tr>
        <td>Bob</td>
        <td>30</td>
        <td>Lyon</td>
    </tr>
</table>

# Balises:
# <table>   - Conteneur du tableau
# <tr>      - Ligne (table row)
# <th>      - Cellule d'en-tête (table header)
# <td>      - Cellule de données (table data)

# === TABLEAU AVEC STRUCTURE COMPLÈTE ===
<table>
    <caption>Liste des employés</caption>
    <thead>
        <tr>
            <th>Nom</th>
            <th>Poste</th>
            <th>Salaire</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Alice</td>
            <td>Développeur</td>
            <td>50000€</td>
        </tr>
        <tr>
            <td>Bob</td>
            <td>Designer</td>
            <td>45000€</td>
        </tr>
    </tbody>
    <tfoot>
        <tr>
            <td colspan="2">Total</td>
            <td>95000€</td>
        </tr>
    </tfoot>
</table>

# <caption>  - Titre du tableau
# <thead>    - En-tête du tableau
# <tbody>    - Corps du tableau
# <tfoot>    - Pied du tableau

# === FUSION DE CELLULES ===
<table>
    <tr>
        <td colspan="2">Cellule fusionnée sur 2 colonnes</td>
    </tr>
    <tr>
        <td rowspan="2">Cellule fusionnée sur 2 lignes</td>
        <td>Cellule normale</td>
    </tr>
    <tr>
        <td>Cellule normale</td>
    </tr>
</table>

# colspan - Fusion horizontale (colonnes)
# rowspan - Fusion verticale (lignes)

# === ATTRIBUTS DE TABLEAU ===
<table border="1">                  <!-- Bordure (obsolète, utiliser CSS) -->
<table style="width: 100%;">        <!-- Largeur -->
<th scope="col">En-tête colonne</th>
<th scope="row">En-tête ligne</th>

# === TABLEAU RESPONSIVE (envelopper dans div) ===
<div style="overflow-x: auto;">
    <table>
        <!-- Contenu du tableau -->
    </table>
</div>


[OK] FORMULAIRES

# === FORMULAIRE SIMPLE ===
<form action="traitement.php" method="post">
    <label for="nom">Nom :</label>
    <input type="text" id="nom" name="nom" required>
    
    <label for="email">Email :</label>
    <input type="email" id="email" name="email" required>
    
    <button type="submit">Envoyer</button>
</form>

# Attributs du formulaire:
# action  - URL de traitement (script serveur)
# method  - get (URL) ou post (corps de requête)
# enctype - Type d'encodage (multipart/form-data pour fichiers)

# === TYPES D'INPUT ===
<input type="text" placeholder="Texte simple">
<input type="password" placeholder="Mot de passe">
<input type="email" placeholder="email@exemple.com">
<input type="number" min="0" max="100" step="5">
<input type="tel" placeholder="+33 6 12 34 56 78">
<input type="url" placeholder="https://example.com">
<input type="date">
<input type="time">
<input type="datetime-local">
<input type="month">
<input type="week">
<input type="color">
<input type="range" min="0" max="100">
<input type="search" placeholder="Rechercher...">
<input type="file">
<input type="file" multiple>                    <!-- Plusieurs fichiers -->
<input type="file" accept="image/*">            <!-- Seulement images -->
<input type="file" accept=".pdf,.doc,.docx">    <!-- Types spécifiques -->
<input type="hidden" name="id" value="123">     <!-- Champ caché -->

# === CASES À COCHER ===
<input type="checkbox" id="newsletter" name="newsletter" value="oui">
<label for="newsletter">S'abonner à la newsletter</label>

# Groupe de cases à cocher
<fieldset>
    <legend>Choisissez vos intérêts :</legend>
    <input type="checkbox" id="sport" name="interets" value="sport">
    <label for="sport">Sport</label>
    
    <input type="checkbox" id="musique" name="interets" value="musique">
    <label for="musique">Musique</label>
    
    <input type="checkbox" id="cinema" name="interets" value="cinema">
    <label for="cinema">Cinéma</label>
</fieldset>

# === BOUTONS RADIO (choix unique) ===
<fieldset>
    <legend>Votre sexe :</legend>
    <input type="radio" id="homme" name="sexe" value="homme">
    <label for="homme">Homme</label>
    
    <input type="radio" id="femme" name="sexe" value="femme">
    <label for="femme">Femme</label>
    
    <input type="radio" id="autre" name="sexe" value="autre">
    <label for="autre">Autre</label>
</fieldset>

# === LISTE DÉROULANTE (SELECT) ===
<label for="pays">Pays :</label>
<select id="pays" name="pays">
    <option value="">-- Choisissez --</option>
    <option value="fr">France</option>
    <option value="be">Belgique</option>
    <option value="ch">Suisse</option>
    <option value="ca">Canada</option>
</select>

# Liste avec groupes
<select name="voiture">
    <optgroup label="Française">
        <option value="renault">Renault</option>
        <option value="peugeot">Peugeot</option>
    </optgroup>
    <optgroup label="Allemande">
        <option value="bmw">BMW</option>
        <option value="mercedes">Mercedes</option>
    </optgroup>
</select>

# Sélection multiple
<select name="langues" multiple size="5">
    <option value="fr">Français</option>
    <option value="en">Anglais</option>
    <option value="es">Espagnol</option>
    <option value="de">Allemand</option>
</select>

# === ZONE DE TEXTE (TEXTAREA) ===
<label for="message">Message :</label>
<textarea id="message" name="message" rows="5" cols="50" placeholder="Votre message..."></textarea>

# === LISTE DE SUGGESTIONS (DATALIST) ===
<label for="navigateur">Navigateur :</label>
<input list="navigateurs" id="navigateur" name="navigateur">
<datalist id="navigateurs">
    <option value="Chrome">
    <option value="Firefox">
    <option value="Safari">
    <option value="Edge">
</datalist>

# === BOUTONS ===
<button type="submit">Envoyer</button>          <!-- Soumet le formulaire -->
<button type="reset">Réinitialiser</button>     <!-- Réinitialise le formulaire -->
<button type="button">Bouton simple</button>    <!-- Ne fait rien par défaut -->

# Ancien style (toujours valide)
<input type="submit" value="Envoyer">
<input type="reset" value="Réinitialiser">
<input type="button" value="Cliquez-moi">

# === ATTRIBUTS DE VALIDATION ===
<input type="text" required>                    <!-- Champ obligatoire -->
<input type="text" minlength="3" maxlength="20"> <!-- Longueur -->
<input type="number" min="0" max="100">         <!-- Valeur min/max -->
<input type="text" pattern="[0-9]{5}">          <!-- Expression régulière -->
<input type="email" placeholder="email@exemple.com">
<textarea maxlength="500"></textarea>

# === CHAMP DÉSACTIVÉ ===
<input type="text" disabled>
<input type="text" readonly value="Non modifiable">

# === FOCUS AUTOMATIQUE ===
<input type="text" autofocus>

# === AUTOCOMPLÉTION ===
<input type="text" autocomplete="on">
<input type="text" autocomplete="off">
<input type="text" autocomplete="name">
<input type="email" autocomplete="email">

# === FORMULAIRE COMPLET EXEMPLE ===
<form action="inscription.php" method="post" enctype="multipart/form-data">
    <fieldset>
        <legend>Informations personnelles</legend>
        
        <label for="prenom">Prénom * :</label>
        <input type="text" id="prenom" name="prenom" required>
        
        <label for="nom">Nom * :</label>
        <input type="text" id="nom" name="nom" required>
        
        <label for="email">Email * :</label>
        <input type="email" id="email" name="email" required>
        
        <label for="tel">Téléphone :</label>
        <input type="tel" id="tel" name="tel">
        
        <label for="naissance">Date de naissance :</label>
        <input type="date" id="naissance" name="naissance">
    </fieldset>
    
    <fieldset>
        <legend>Compte</legend>
        
        <label for="password">Mot de passe * :</label>
        <input type="password" id="password" name="password" required minlength="8">
        
        <label for="confirm">Confirmer mot de passe * :</label>
        <input type="password" id="confirm" name="confirm" required>
    </fieldset>
    
    <fieldset>
        <legend>Préférences</legend>
        
        <input type="checkbox" id="newsletter" name="newsletter" value="oui">
        <label for="newsletter">Recevoir la newsletter</label>
        
        <label for="bio">Biographie :</label>
        <textarea id="bio" name="bio" rows="4" maxlength="500"></textarea>
    </fieldset>
    
    <button type="submit">S'inscrire</button>
    <button type="reset">Réinitialiser</button>
</form>


[OK] CONTENEURS ET MISE EN PAGE

# === DIV (division générique) ===
<div>
    Conteneur générique sans signification sémantique
</div>

<div class="container">
    <div class="header">En-tête</div>
    <div class="content">Contenu</div>
    <div class="footer">Pied de page</div>
</div>

# === SPAN (conteneur inline) ===
<p>
    Texte normal <span style="color: red;">texte rouge</span> texte normal
</p>

# === DIFFÉRENCE DIV vs SPAN ===
# <div>  - Élément block (prend toute la largeur, nouvelle ligne)
# <span> - Élément inline (ne prend que la place nécessaire)

# === LAYOUTS MODERNES ===

<!-- Layout avec Flexbox -->
<div style="display: flex;">
    <div style="flex: 1;">Colonne 1</div>
    <div style="flex: 1;">Colonne 2</div>
    <div style="flex: 1;">Colonne 3</div>
</div>

<!-- Layout avec Grid -->
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr;">
    <div>Cellule 1</div>
    <div>Cellule 2</div>
    <div>Cellule 3</div>
</div>


[OK] MULTIMÉDIA

# === AUDIO ===
<audio controls>
    <source src="audio.mp3" type="audio/mpeg">
    <source src="audio.ogg" type="audio/ogg">
    Votre navigateur ne supporte pas l'audio HTML5.
</audio>

# Attributs audio:
<audio controls>        <!-- Affiche les contrôles -->
<audio autoplay>        <!-- Lecture automatique (éviter) -->
<audio loop>            <!-- Lecture en boucle -->
<audio muted>           <!-- Son coupé -->
<audio preload="auto">  <!-- Précharge le fichier -->

# === VIDÉO ===
<video controls width="640" height="360">
    <source src="video.mp4" type="video/mp4">
    <source src="video.webm" type="video/webm">
    Votre navigateur ne supporte pas la vidéo HTML5.
</video>

# Attributs vidéo:
<video controls>                    <!-- Contrôles -->
<video autoplay>                    <!-- Lecture auto (éviter) -->
<video loop>                        <!-- Boucle -->
<video muted>                       <!-- Son coupé -->
<video poster="vignette.jpg">       <!-- Image avant lecture -->
<video preload="metadata">          <!-- Précharge métadonnées -->

# === VIDÉO YOUTUBE (IFRAME) ===
<iframe width="560" height="315" 
        src="https://www.youtube.com/embed/VIDEO_ID" 
        frameborder="0" 
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" 
        allowfullscreen>
</iframe>

# === IFRAME (intégration de page) ===
<iframe src="page.html" width="600" height="400"></iframe>
<iframe src="https://www.google.com/maps/embed?..." width="600" height="450"></iframe>

# === SVG INLINE ===
<svg width="100" height="100">
    <circle cx="50" cy="50" r="40" fill="blue" />
</svg>


[OK] ÉLÉMENTS INTERACTIFS HTML5

# === DETAILS ET SUMMARY (accordéon) ===
<details>
    <summary>Cliquez pour voir plus</summary>
    <p>Contenu caché par défaut, visible au clic.</p>
</details>

<details open>  <!-- Ouvert par défaut -->
    <summary>Déjà ouvert</summary>
    <p>Contenu visible.</p>
</details>

# === DIALOG (boîte de dialogue) ===
<dialog id="monDialog">
    <p>Ceci est une boîte de dialogue</p>
    <button onclick="document.getElementById('monDialog').close()">Fermer</button>
</dialog>

<button onclick="document.getElementById('monDialog').showModal()">
    Ouvrir dialogue
</button>



[OK] META TAGS ET SEO

# === META TAGS ESSENTIELS ===
<head>
    <!-- Encodage des caractères (OBLIGATOIRE) -->
    <meta charset="UTF-8">
    
    <!-- Responsive design (OBLIGATOIRE pour mobile) -->
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
    <!-- Description de la page (SEO) -->
    <meta name="description" content="Description de votre page (150-160 caractères max)">
    
    <!-- Mots-clés (moins important aujourd'hui) -->
    <meta name="keywords" content="html, css, tutoriel, web">
    
    <!-- Auteur -->
    <meta name="author" content="Votre Nom">
    
    <!-- Robot d'indexation -->
    <meta name="robots" content="index, follow">
    <meta name="robots" content="noindex, nofollow">    <!-- Ne pas indexer -->
    
    <!-- Langue de la page -->
    <meta http-equiv="content-language" content="fr">
    
    <!-- Redirection automatique (après 5 secondes) -->
    <meta http-equiv="refresh" content="5;url=https://example.com">
    
    <!-- Compatibilité Internet Explorer -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
</head>

# === META TAGS OPEN GRAPH (Facebook, LinkedIn) ===
<head>
    <!-- Titre pour partage social -->
    <meta property="og:title" content="Titre de la page">
    
    <!-- Description pour partage social -->
    <meta property="og:description" content="Description de la page">
    
    <!-- Image de prévisualisation (1200x630px recommandé) -->
    <meta property="og:image" content="https://example.com/image.jpg">
    
    <!-- URL de la page -->
    <meta property="og:url" content="https://example.com/page.html">
    
    <!-- Type de contenu -->
    <meta property="og:type" content="website">
    <meta property="og:type" content="article">
    
    <!-- Nom du site -->
    <meta property="og:site_name" content="Nom du Site">
    
    <!-- Langue -->
    <meta property="og:locale" content="fr_FR">
</head>

# === META TAGS TWITTER CARD ===
<head>
    <!-- Type de carte Twitter -->
    <meta name="twitter:card" content="summary_large_image">
    <meta name="twitter:card" content="summary">
    
    <!-- Compte Twitter -->
    <meta name="twitter:site" content="@votrecompte">
    <meta name="twitter:creator" content="@auteur">
    
    <!-- Titre -->
    <meta name="twitter:title" content="Titre de la page">
    
    <!-- Description -->
    <meta name="twitter:description" content="Description de la page">
    
    <!-- Image -->
    <meta name="twitter:image" content="https://example.com/image.jpg">
</head>

# === FAVICON ===
<head>
    <!-- Favicon standard -->
    <link rel="icon" type="image/x-icon" href="/favicon.ico">
    <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
    <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
    
    <!-- Apple Touch Icon (iOS) -->
    <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
    
    <!-- Android Chrome -->
    <link rel="manifest" href="/site.webmanifest">
    
    <!-- Safari sur Mac -->
    <link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
    
    <!-- Couleur thème (barre d'adresse mobile) -->
    <meta name="theme-color" content="#ffffff">
    <meta name="msapplication-TileColor" content="#da532c">
</head>

# Générer favicons: https://realfavicongenerator.net/

# === LIENS CANONIQUES ===
<head>
    <!-- URL canonique (évite contenu dupliqué) -->
    <link rel="canonical" href="https://example.com/page.html">
    
    <!-- Versions alternatives de la page -->
    <link rel="alternate" hreflang="en" href="https://example.com/en/page.html">
    <link rel="alternate" hreflang="fr" href="https://example.com/fr/page.html">
    <link rel="alternate" hreflang="es" href="https://example.com/es/page.html">
</head>


[OK] INTÉGRATION CSS

# === STYLES INLINE (à éviter) ===
<p style="color: red; font-size: 16px;">Texte rouge</p>
<div style="background-color: blue; padding: 20px;">Contenu</div>

# Inconvénients:
# [X] Difficile à maintenir
# [X] Code moins lisible
# [X] Pas de réutilisation

# === STYLES INTERNES (dans <head>) ===
<head>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f0f0f0;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
        }
        
        h1 {
            color: #333;
            font-size: 2em;
        }
        
        .button {
            background-color: #007bff;
            color: white;
            padding: 10px 20px;
            border: none;
            border-radius: 5px;
            cursor: pointer;
        }
        
        .button:hover {
            background-color: #0056b3;
        }
    </style>
</head>

# Avantages:
# [OK] Pas de requête HTTP supplémentaire
# [OK] Bon pour pages uniques
# Inconvénients:
# [X] Styles non réutilisables entre pages

# === STYLES EXTERNES (RECOMMANDÉ) ===
<head>
    <!-- Fichier CSS externe -->
    <link rel="stylesheet" href="styles.css">
    <link rel="stylesheet" href="css/main.css">
    <link rel="stylesheet" href="/assets/css/theme.css">
    
    <!-- Plusieurs fichiers CSS -->
    <link rel="stylesheet" href="css/reset.css">
    <link rel="stylesheet" href="css/base.css">
    <link rel="stylesheet" href="css/layout.css">
    <link rel="stylesheet" href="css/components.css">
    
    <!-- CSS pour impression -->
    <link rel="stylesheet" href="print.css" media="print">
    
    <!-- CSS pour écran -->
    <link rel="stylesheet" href="screen.css" media="screen">
    
    <!-- CSS responsive -->
    <link rel="stylesheet" href="mobile.css" media="screen and (max-width: 768px)">
</head>

# Avantages:
# [OK] Réutilisable sur toutes les pages
# [OK] Cache navigateur
# [OK] Code organisé et maintenable
# [OK] Séparation des préoccupations

# === FRAMEWORKS CSS ===
<head>
    <!-- Bootstrap -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" 
          rel="stylesheet">
    
    <!-- Tailwind CSS -->
    <script src="https://cdn.tailwindcss.com"></script>
    
    <!-- Bulma -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
    
    <!-- Foundation -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/foundation-sites@6.7.5/dist/css/foundation.min.css">
    
    <!-- Materialize -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
</head>

# === POLICES WEB ===
<head>
    <!-- Google Fonts -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap" 
          rel="stylesheet">
    
    <!-- Font Awesome (icônes) -->
    <link rel="stylesheet" 
          href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    
    <!-- Police locale -->
    <style>
        @font-face {
            font-family: 'MaPolice';
            src: url('fonts/mapolice.woff2') format('woff2'),
                 url('fonts/mapolice.woff') format('woff');
            font-weight: normal;
            font-style: normal;
        }
    </style>
</head>


[OK] INTÉGRATION JAVASCRIPT

# === SCRIPT INLINE (dans HTML) ===
<button onclick="alert('Bonjour!')">Cliquez-moi</button>

<script>
    console.log('Script inline');
</script>

# À éviter pour le même raison que CSS inline

# === SCRIPT INTERNE (dans <head> ou <body>) ===
<head>
    <script>
        function saluer() {
            alert('Bonjour!');
        }
        
        document.addEventListener('DOMContentLoaded', function() {
            console.log('Page chargée');
        });
    </script>
</head>

# === SCRIPT EXTERNE (RECOMMANDÉ) ===
<head>
    <!-- Script dans <head> -->
    <script src="script.js"></script>
    <script src="js/main.js"></script>
    <script src="/assets/js/app.js"></script>
</head>

<body>
    <!-- Contenu -->
    
    <!-- Scripts en fin de <body> (MEILLEURE PRATIQUE) -->
    <script src="js/script.js"></script>
</body>

# Pourquoi en fin de <body>?
# [OK] Page s'affiche avant chargement des scripts
# [OK] Meilleure performance perçue
# [OK] DOM déjà disponible

# === ATTRIBUTS ASYNC ET DEFER ===

<!-- DEFER: Exécute après parsing HTML (ordre maintenu) -->
<script src="script.js" defer></script>

<!-- ASYNC: Exécute dès disponible (pas d'ordre garanti) -->
<script src="analytics.js" async></script>

# Comparaison:
# Sans attribut  - Bloque parsing HTML
# defer          - Parse HTML en parallèle, exécute après
# async          - Parse HTML en parallèle, exécute dès prêt

# Ordre de préférence:
# 1. <script defer> - Pour scripts qui dépendent du DOM
# 2. <script async> - Pour scripts indépendants (analytics)
# 3. Fin de <body> - Si defer/async non supporté

# === BIBLIOTHÈQUES JAVASCRIPT ===
<body>
    <!-- jQuery -->
    <script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
    
    <!-- React -->
    <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
    
    <!-- Vue.js -->
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    
    <!-- Alpine.js -->
    <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
    
    <!-- Chart.js -->
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    
    <!-- Moment.js -->
    <script src="https://cdn.jsdelivr.net/npm/moment@2.29.4/moment.min.js"></script>
    
    <!-- Lodash -->
    <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
    
    <!-- Axios -->
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</body>

# === MODULE JAVASCRIPT (ES6) ===
<script type="module">
    import { fonction } from './module.js';
    fonction();
</script>

<script type="module" src="app.js"></script>

# === SCRIPT POUR ANALYTICS ===
<head>
    <!-- Google Analytics -->
    <script async src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"></script>
    <script>
        window.dataLayer = window.dataLayer || [];
        function gtag(){dataLayer.push(arguments);}
        gtag('js', new Date());
        gtag('config', 'GA_MEASUREMENT_ID');
    </script>
</head>


[OK] ATTRIBUTS GLOBAUX (utilisables sur TOUTES les balises)

# === ID (identifiant unique) ===
<div id="header">En-tête</div>
<p id="intro">Premier paragraphe</p>

# Règles:
# [OK] Unique par page
# [OK] Pas d'espaces
# [OK] Commence par lettre
# [OK] Sensible à la casse (id="Header" ≠ id="header")

# Utilisations:
# - CSS: #header { ... }
# - JavaScript: document.getElementById('header')
# - Ancres: <a href="#header">Aller à l'en-tête</a>

# === CLASS (classes CSS) ===
<div class="container">Contenu</div>
<p class="text-primary bold">Texte</p>
<button class="btn btn-primary btn-lg">Bouton</button>

# Règles:
# [OK] Réutilisable (plusieurs éléments)
# [OK] Plusieurs classes séparées par espace
# [OK] Pas d'espaces dans les noms

# Utilisations:
# - CSS: .container { ... }
# - JavaScript: document.getElementsByClassName('container')
# - JavaScript: document.querySelectorAll('.container')

# === STYLE (CSS inline) ===
<p style="color: red;">Texte rouge</p>
<div style="background: blue; padding: 20px; margin: 10px;">Contenu</div>

# Format: propriété: valeur; propriété2: valeur2;

# === TITLE (infobulle au survol) ===
<a href="#" title="Ceci est une infobulle">Survolez-moi</a>
<img src="image.jpg" alt="Image" title="Description détaillée">
<button title="Cliquez pour enregistrer">[SAUVEGARDE]</button>

# === DATA-* (attributs personnalisés) ===
<div data-user-id="123" data-role="admin" data-status="active">
    Utilisateur
</div>

<button data-action="delete" data-confirm="Êtes-vous sûr ?">
    Supprimer
</button>

# JavaScript:
<script>
    const div = document.querySelector('div');
    console.log(div.dataset.userId);     // "123"
    console.log(div.dataset.role);       // "admin"
    console.log(div.dataset.status);     // "active"
</script>

# Règles:
# [OK] Commence par "data-"
# [OK] Minuscules uniquement
# [OK] Pas d'espaces
# [OK] Utiliser tirets pour plusieurs mots: data-user-name

# === HIDDEN (cacher élément) ===
<div hidden>Contenu caché</div>
<p hidden>Ce paragraphe n'est pas visible</p>

# Équivalent à: display: none;

# === CONTENTEDITABLE (éditable) ===
<div contenteditable="true">
    Cliquez pour éditer ce texte
</div>

<p contenteditable="false">Non éditable</p>

# === DRAGGABLE (glisser-déposer) ===
<div draggable="true">
    Glissez-moi!
</div>

<img src="image.jpg" draggable="false" alt="Non déplaçable">

# === TABINDEX (ordre de tabulation) ===
<input type="text" tabindex="1">
<input type="text" tabindex="2">
<input type="text" tabindex="3">
<button tabindex="-1">Non accessible par Tab</button>
<div tabindex="0">Focusable mais ordre naturel</div>

# tabindex="n"  - Ordre spécifique (n > 0)
# tabindex="0"  - Ordre naturel
# tabindex="-1" - Non accessible par Tab

# === LANG (langue du contenu) ===
<html lang="fr">
<p lang="en">This is English text</p>
<span lang="es">Hola mundo</span>

# Codes courants:
# fr - Français
# en - Anglais
# es - Espagnol
# de - Allemand
# it - Italien
# pt - Portugais
# ar - Arabe
# zh - Chinois
# ja - Japonais

# === DIR (direction du texte) ===
<p dir="ltr">Texte de gauche à droite (défaut)</p>
<p dir="rtl">نص من اليمين إلى اليسار</p>
<p dir="auto">Direction automatique</p>

# === SPELLCHECK (vérification orthographique) ===
<textarea spellcheck="true">Texte vérifié</textarea>
<input type="text" spellcheck="false">

# === TRANSLATE (autoriser traduction) ===
<p translate="yes">Traduire ce texte</p>
<p translate="no" class="notranslate">Ne pas traduire: API_KEY_12345</p>

# === ACCESSKEY (raccourci clavier) ===
<button accesskey="s">Enregistrer (Alt+S)</button>
<a href="#" accesskey="h">Accueil (Alt+H)</a>

# Activation:
# Windows: Alt + touche
# Mac: Control + Option + touche

# === ROLE (accessibilité ARIA) ===
<div role="navigation">Menu</div>
<div role="main">Contenu principal</div>
<div role="banner">Bannière</div>
<div role="contentinfo">Pied de page</div>
<button role="button">Bouton</button>

# === ARIA-* (attributs d'accessibilité) ===
<button aria-label="Fermer">×</button>
<input type="text" aria-required="true" aria-invalid="false">
<div aria-live="polite">Mise à jour en direct</div>
<nav aria-label="Menu principal">...</nav>


[OK] ENTITÉS HTML (caractères spéciaux)

# === CARACTÈRES RÉSERVÉS ===
&lt;        <!-- < (inférieur à) -->
&gt;        <!-- > (supérieur à) -->
&amp;       <!-- & (esperluette) -->
&quot;      <!-- " (guillemet double) -->
&apos;      <!-- ' (guillemet simple) -->

# Exemple:
<p>Pour écrire &lt;p&gt;, utilisez les entités HTML</p>
<!-- Affiche: Pour écrire <p>, utilisez les entités HTML -->

# === ESPACES ===
&nbsp;      <!-- Espace insécable -->
&ensp;      <!-- Espace moyenne -->
&emsp;      <!-- Espace large -->
&thinsp;    <!-- Espace fine -->

<p>100&nbsp;000&nbsp;€</p>
<!-- 100 000 € (ne se coupe pas sur plusieurs lignes) -->

# === SYMBOLES MONÉTAIRES ===
&euro;      <!-- € -->
&dollar;    <!-- $ -->
&pound;     <!-- £ -->
&yen;       <!-- ¥ -->
&cent;      <!-- ¢ -->

# === SYMBOLES MATHÉMATIQUES ===
&times;     <!-- × (multiplication) -->
&divide;    <!-- ÷ (division) -->
&plusmn;    <!-- ± (plus ou moins) -->
&minus;     <!-- − (moins) -->
&sup2;      <!-- ² (carré) -->
&sup3;      <!-- ³ (cube) -->
&frac12;    <!-- ½ -->
&frac14;    <!-- ¼ -->
&frac34;    <!-- ¾ -->
&infin;     <!-- ∞ (infini) -->

# === FLÈCHES ===
&larr;      <!-- <- -->
&uarr;      <!-- ^ -->
&rarr;      <!-- -> -->
&darr;      <!-- v -->
&harr;      <!-- <-> -->

# === SYMBOLES COURANTS ===
&copy;      <!-- © (copyright) -->
&reg;       <!-- ® (marque déposée) -->
&trade;     <!-- ™ (trademark) -->
&deg;       <!-- ° (degré) -->
&para;      <!-- ¶ (paragraphe) -->
&sect;      <!-- § (section) -->

# Exemple footer:
<footer>
    <p>&copy; 2024 Mon Site. Tous droits réservés.</p>
</footer>

# === ACCENTS (si charset UTF-8, pas nécessaire) ===
&agrave;    <!-- à -->
&eacute;    <!-- é -->
&egrave;    <!-- è -->
&ecirc;     <!-- ê -->
&iuml;      <!-- ï -->
&ccedil;    <!-- ç -->

# Avec UTF-8 (recommandé):
<meta charset="UTF-8">
<p>Café, crème, château</p>

# === EMOJIS (Unicode) ===
<!-- Directement dans le texte -->
<p>Hello [WAVING_HAND_SIGN] World [MONDE]</p>

<!-- Ou avec code Unicode -->
<p>&#128512;</p>  <!-- [GRINNING_FACE] -->
<p>&#9829;</p>    <!-- [BLACK_HEART_SUIT] -->

# === CODES NUMÉRIQUES ===
&#60;       <!-- < -->
&#62;       <!-- > -->
&#169;      <!-- © -->

# Format hexadécimal:
&#x003C;    <!-- < -->
&#x003E;    <!-- > -->
&#x00A9;    <!-- © -->


[OK] COMMENTAIRES CONDITIONNELS (Internet Explorer)

# === SYNTAXE ===
<!--[if IE]>
    <p>Vous utilisez Internet Explorer</p>
<![endif]-->

<!--[if IE 9]>
    <p>Vous utilisez IE 9</p>
<![endif]-->

<!--[if lt IE 9]>
    <script src="html5shiv.js"></script>
    <script src="respond.js"></script>
<![endif]-->

<!--[if gte IE 9]>
    <p>IE 9 ou supérieur</p>
<![endif]-->

# Opérateurs:
# lt  - Less than (inférieur à)
# lte - Less than or equal (inférieur ou égal)
# gt  - Greater than (supérieur à)
# gte - Greater than or equal (supérieur ou égal)
# !   - NOT (négation)

# === EXEMPLES ===
<!--[if !IE]><!-->
    <p>Tous navigateurs SAUF Internet Explorer</p>
<!--<![endif]-->

# Note: Obsolète avec Edge (Chromium)
# Internet Explorer n'est plus supporté


[OK] MICRODATA (données structurées)

# === SCHÉMA ORGANISATION ===
<div itemscope itemtype="http://schema.org/Organization">
    <span itemprop="name">Mon Entreprise</span>
    <div itemprop="address" itemscope itemtype="http://schema.org/PostalAddress">
        <span itemprop="streetAddress">123 Rue Example</span>,
        <span itemprop="addressLocality">Paris</span>,
        <span itemprop="postalCode">75001</span>
    </div>
    Téléphone: <span itemprop="telephone">+33 1 23 45 67 89</span>
    Email: <span itemprop="email">contact@example.com</span>
</div>

# === SCHÉMA PERSONNE ===
<div itemscope itemtype="http://schema.org/Person">
    <span itemprop="name">Jean Dupont</span>
    <span itemprop="jobTitle">Développeur Web</span>
    <img src="photo.jpg" itemprop="image" alt="Jean">
    <a href="https://example.com" itemprop="url">Site web</a>
</div>

# === SCHÉMA ARTICLE ===
<article itemscope itemtype="http://schema.org/Article">
    <h1 itemprop="headline">Titre de l'article</h1>
    <p itemprop="description">Description courte</p>
    <p>Par <span itemprop="author">Jean Dupont</span></p>
    <time itemprop="datePublished" datetime="2024-01-15">15 janvier 2024</time>
    <div itemprop="articleBody">
        <p>Contenu de l'article...</p>
    </div>
</article>

# === SCHÉMA PRODUIT ===
<div itemscope itemtype="http://schema.org/Product">
    <h1 itemprop="name">Nom du produit</h1>
    <img src="produit.jpg" itemprop="image" alt="Produit">
    <p itemprop="description">Description du produit</p>
    <span itemprop="brand">Marque</span>
    <div itemprop="offers" itemscope itemtype="http://schema.org/Offer">
        Prix: <span itemprop="price">99.99</span>
        <span itemprop="priceCurrency">EUR</span>
    </div>
</div>

# === SCHÉMA ÉVÉNEMENT ===
<div itemscope itemtype="http://schema.org/Event">
    <h1 itemprop="name">Nom de l'événement</h1>
    <time itemprop="startDate" datetime="2024-06-15T19:00">15 juin 2024 à 19h</time>
    <div itemprop="location" itemscope itemtype="http://schema.org/Place">
        <span itemprop="name">Lieu</span>
        <div itemprop="address">Adresse</div>
    </div>
</div>

# Avantages:
# [OK] Meilleur référencement (rich snippets Google)
# [OK] Affichage enrichi dans résultats recherche
# [OK] Google comprend mieux votre contenu

# Tester: https://search.google.com/test/rich-results


[OK] CANVAS (dessin 2D)

# === CANVAS DE BASE ===
<canvas id="monCanvas" width="400" height="300">
    Votre navigateur ne supporte pas canvas.
</canvas>

<script>
    const canvas = document.getElementById('monCanvas');
    const ctx = canvas.getContext('2d');
    
    // Dessiner rectangle
    ctx.fillStyle = 'blue';
    ctx.fillRect(50, 50, 100, 80);
    
    // Dessiner cercle
    ctx.beginPath();
    ctx.arc(200, 100, 50, 0, Math.PI * 2);
    ctx.fillStyle = 'red';
    ctx.fill();
    
    // Dessiner ligne
    ctx.beginPath();
    ctx.moveTo(50, 200);
    ctx.lineTo(350, 200);
    ctx.strokeStyle = 'green';
    ctx.lineWidth = 3;
    ctx.stroke();
    
    // Écrire texte
    ctx.font = '20px Arial';
    ctx.fillStyle = 'black';
    ctx.fillText('Hello Canvas!', 150, 250);
</script>

# === FORMES AVANCÉES ===
<script>
    // Rectangle avec contour
    ctx.strokeStyle = 'black';
    ctx.strokeRect(10, 10, 100, 50);
    
    // Effacer zone
    ctx.clearRect(20, 20, 30, 20);
    
    // Triangle
    ctx.beginPath();
    ctx.moveTo(75, 50);
    ctx.lineTo(100, 100);
    ctx.lineTo(50, 100);
    ctx.closePath();
    ctx.fill();
    
    // Arc
    ctx.beginPath();
    ctx.arc(100, 75, 50, 0, Math.PI, false);
    ctx.stroke();
    
    // Courbe de Bézier
    ctx.beginPath();
    ctx.moveTo(20, 20);
    ctx.bezierCurveTo(20, 100, 200, 100, 200, 20);
    ctx.stroke();
</script>

# === IMAGES ===
<script>
    const img = new Image();
    img.onload = function() {
        ctx.drawImage(img, 0, 0);
        // Avec dimensions
        ctx.drawImage(img, 0, 0, 100, 100);
        // Avec crop et position
        ctx.drawImage(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
    };
    img.src = 'image.jpg';
</script>

# === GRADIENTS ===
<script>
    // Gradient linéaire
    const gradient = ctx.createLinearGradient(0, 0, 200, 0);
    gradient.addColorStop(0, 'red');
    gradient.addColorStop(0.5, 'yellow');
    gradient.addColorStop(1, 'green');
    ctx.fillSty
    const canvas = document.getElementById('monCanvas');
    const ctx = canvas.getContext('2d');
    
    // Gradient radial
    const radialGradient = ctx.createRadialGradient(100, 100, 20, 100, 100, 70);
    radialGradient.addColorStop(0, 'white');
    radialGradient.addColorStop(0.5, 'yellow');
    radialGradient.addColorStop(1, 'orange');
    ctx.fillStyle = radialGradient;
    ctx.fillRect(0, 0, 200, 200);
</script>

# === PATTERNS (MOTIFS) ===
<script>
    // Créer pattern depuis image
    const img = new Image();
    img.onload = function() {
        const pattern = ctx.createPattern(img, 'repeat');
        // Options: 'repeat', 'repeat-x', 'repeat-y', 'no-repeat'
        ctx.fillStyle = pattern;
        ctx.fillRect(0, 0, 400, 300);
    };
    img.src = 'texture.png';
    
    // Pattern depuis canvas
    const patternCanvas = document.createElement('canvas');
    patternCanvas.width = 20;
    patternCanvas.height = 20;
    const pCtx = patternCanvas.getContext('2d');
    pCtx.fillStyle = 'red';
    pCtx.fillRect(0, 0, 10, 10);
    pCtx.fillStyle = 'blue';
    pCtx.fillRect(10, 10, 10, 10);
    
    const pattern = ctx.createPattern(patternCanvas, 'repeat');
    ctx.fillStyle = pattern;
    ctx.fillRect(0, 0, 400, 300);
</script>

# === OMBRES ===
<script>
    ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';
    ctx.shadowBlur = 10;
    ctx.shadowOffsetX = 5;
    ctx.shadowOffsetY = 5;
    
    ctx.fillStyle = 'blue';
    ctx.fillRect(50, 50, 100, 80);
    
    // Désactiver ombre
    ctx.shadowColor = 'transparent';
</script>

# === TRANSFORMATIONS ===
<script>
    // Translation (déplacement)
    ctx.translate(100, 100);
    ctx.fillRect(0, 0, 50, 50);
    
    // Rotation (en radians)
    ctx.rotate(Math.PI / 4);  // 45 degrés
    ctx.fillRect(0, 0, 50, 50);
    
    // Échelle (zoom)
    ctx.scale(2, 2);  // Double la taille
    ctx.fillRect(0, 0, 50, 50);
    
    // Sauvegarder/Restaurer état
    ctx.save();        // Sauvegarde état actuel
    ctx.translate(100, 100);
    ctx.rotate(Math.PI / 4);
    ctx.fillRect(0, 0, 50, 50);
    ctx.restore();     // Restaure état sauvegardé
    
    // Réinitialiser transformations
    ctx.setTransform(1, 0, 0, 1, 0, 0);
</script>

# === COMPOSITION ===
<script>
    // globalAlpha - Transparence globale
    ctx.globalAlpha = 0.5;
    ctx.fillRect(50, 50, 100, 100);
    
    // globalCompositeOperation - Mode de fusion
    ctx.globalCompositeOperation = 'source-over';    // Défaut
    ctx.globalCompositeOperation = 'source-atop';
    ctx.globalCompositeOperation = 'destination-over';
    ctx.globalCompositeOperation = 'destination-atop';
    ctx.globalCompositeOperation = 'lighter';        // Addition
    ctx.globalCompositeOperation = 'multiply';       // Multiplication
    ctx.globalCompositeOperation = 'screen';
    ctx.globalCompositeOperation = 'overlay';
    ctx.globalCompositeOperation = 'xor';
    
    // Exemple
    ctx.fillStyle = 'red';
    ctx.fillRect(50, 50, 100, 100);
    ctx.globalCompositeOperation = 'multiply';
    ctx.fillStyle = 'blue';
    ctx.fillRect(100, 100, 100, 100);
</script>

# === MANIPULATION PIXELS ===
<script>
    // Obtenir données pixels
    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    const data = imageData.data;  // Uint8ClampedArray [R, G, B, A, R, G, B, A, ...]
    
    // Modifier pixels (convertir en niveaux de gris)
    for (let i = 0; i < data.length; i += 4) {
        const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
        data[i] = avg;      // Rouge
        data[i + 1] = avg;  // Vert
        data[i + 2] = avg;  // Bleu
        // data[i + 3] est alpha (transparence)
    }
    
    // Remettre pixels modifiés
    ctx.putImageData(imageData, 0, 0);
    
    // Créer ImageData vide
    const newImageData = ctx.createImageData(200, 200);
    
    // Inverser couleurs
    for (let i = 0; i < data.length; i += 4) {
        data[i] = 255 - data[i];
        data[i + 1] = 255 - data[i + 1];
        data[i + 2] = 255 - data[i + 2];
    }
    ctx.putImageData(imageData, 0, 0);
</script>

# === ANIMATIONS CANVAS ===
<script>
    let x = 0;
    let y = 100;
    let dx = 2;  // Vitesse
    
    function animate() {
        // Effacer canvas
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        
        // Dessiner
        ctx.fillStyle = 'blue';
        ctx.fillRect(x, y, 50, 50);
        
        // Mettre à jour position
        x += dx;
        
        // Rebondir sur les bords
        if (x + 50 > canvas.width || x < 0) {
            dx = -dx;
        }
        
        // Boucle d'animation
        requestAnimationFrame(animate);
    }
    
    // Démarrer animation
    animate();
    
    // Contrôler FPS
    let lastTime = 0;
    const fps = 60;
    const interval = 1000 / fps;
    
    function animateFPS(currentTime) {
        requestAnimationFrame(animateFPS);
        
        const deltaTime = currentTime - lastTime;
        if (deltaTime < interval) return;
        
        lastTime = currentTime - (deltaTime % interval);
        
        // Votre code d'animation ici
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        // ...
    }
    
    requestAnimationFrame(animateFPS);
</script>

# === ÉVÉNEMENTS CANVAS ===
<script>
    // Obtenir coordonnées souris
    canvas.addEventListener('click', function(e) {
        const rect = canvas.getBoundingClientRect();
        const x = e.clientX - rect.left;
        const y = e.clientY - rect.top;
        
        console.log('Clic à:', x, y);
        
        // Dessiner au clic
        ctx.fillStyle = 'red';
        ctx.fillRect(x - 5, y - 5, 10, 10);
    });
    
    // Dessiner en glissant
    let isDrawing = false;
    
    canvas.addEventListener('mousedown', function(e) {
        isDrawing = true;
        const rect = canvas.getBoundingClientRect();
        const x = e.clientX - rect.left;
        const y = e.clientY - rect.top;
        
        ctx.beginPath();
        ctx.moveTo(x, y);
    });
    
    canvas.addEventListener('mousemove', function(e) {
        if (!isDrawing) return;
        
        const rect = canvas.getBoundingClientRect();
        const x = e.clientX - rect.left;
        const y = e.clientY - rect.top;
        
        ctx.lineTo(x, y);
        ctx.stroke();
    });
    
    canvas.addEventListener('mouseup', function() {
        isDrawing = false;
    });
    
    canvas.addEventListener('mouseleave', function() {
        isDrawing = false;
    });
</script>

# === SAUVEGARDER CANVAS ===
<script>
    // Convertir en image base64
    const dataURL = canvas.toDataURL('image/png');
    const dataURL = canvas.toDataURL('image/jpeg', 0.9);  // 0.9 = qualité
    
    // Télécharger image
    function downloadCanvas() {
        const link = document.createElement('a');
        link.download = 'mon-dessin.png';
        link.href = canvas.toDataURL();
        link.click();
    }
    
    // Créer blob
    canvas.toBlob(function(blob) {
        const url = URL.createObjectURL(blob);
        // Utiliser URL...
        URL.revokeObjectURL(url);  // Nettoyer après usage
    });
</script>


[OK] WEB STORAGE API

# === LOCALSTORAGE (stockage permanent) ===
<script>
    // Enregistrer donnée
    localStorage.setItem('nom', 'Jean');
    localStorage.setItem('age', '25');
    localStorage.nom = 'Jean';  // Alternative (moins recommandé)
    
    // Lire donnée
    const nom = localStorage.getItem('nom');
    const age = localStorage.getItem('age');
    console.log(nom);  // "Jean"
    
    // Supprimer donnée
    localStorage.removeItem('nom');
    
    // Tout supprimer
    localStorage.clear();
    
    // Vérifier existence
    if (localStorage.getItem('nom')) {
        console.log('Nom existe');
    }
    
    // Nombre d'éléments
    console.log(localStorage.length);
    
    // Parcourir toutes les clés
    for (let i = 0; i < localStorage.length; i++) {
        const key = localStorage.key(i);
        const value = localStorage.getItem(key);
        console.log(key, value);
    }
    
    // Stocker objets (JSON)
    const user = {
        nom: 'Jean',
        age: 25,
        ville: 'Paris'
    };
    localStorage.setItem('user', JSON.stringify(user));
    
    // Récupérer objet
    const userStr = localStorage.getItem('user');
    const user = JSON.parse(userStr);
    console.log(user.nom);  // "Jean"
    
    // Stocker tableau
    const fruits = ['pomme', 'banane', 'orange'];
    localStorage.setItem('fruits', JSON.stringify(fruits));
    
    const fruitsStr = localStorage.getItem('fruits');
    const fruits = JSON.parse(fruitsStr);
</script>

# === SESSIONSTORAGE (durée de session) ===
<script>
    // Même API que localStorage
    sessionStorage.setItem('token', 'abc123');
    const token = sessionStorage.getItem('token');
    sessionStorage.removeItem('token');
    sessionStorage.clear();
    
    // Différence:
    // - localStorage: persiste même après fermeture navigateur
    // - sessionStorage: supprimé à fermeture onglet/fenêtre
</script>

# === ÉVÉNEMENT STORAGE ===
<script>
    // Détecter changements localStorage (dans autres onglets)
    window.addEventListener('storage', function(e) {
        console.log('Clé modifiée:', e.key);
        console.log('Ancienne valeur:', e.oldValue);
        console.log('Nouvelle valeur:', e.newValue);
        console.log('URL:', e.url);
    });
</script>

# === LIMITES ET BONNES PRATIQUES ===
# Limite: ~5-10 MB par origine (varie selon navigateur)
# [OK] Utiliser pour: préférences utilisateur, cache, données non sensibles
# [X] Ne pas stocker: mots de passe, tokens sensibles, grandes quantités
# [OK] Toujours utiliser JSON.stringify/parse pour objets
# [OK] Toujours vérifier disponibilité

<script>
    // Vérifier si localStorage disponible
    function storageAvailable(type) {
        try {
            const storage = window[type];
            const x = '__storage_test__';
            storage.setItem(x, x);
            storage.removeItem(x);
            return true;
        } catch(e) {
            return false;
        }
    }
    
    if (storageAvailable('localStorage')) {
        // Utiliser localStorage
    } else {
        // Fallback
    }
</script>

# === EXEMPLES PRATIQUES ===

<!-- Sauvegarder thème sombre -->
<script>
    const themeToggle = document.getElementById('theme-toggle');
    
    // Charger thème sauvegardé
    const savedTheme = localStorage.getItem('theme') || 'light';
    document.body.className = savedTheme;
    
    // Sauvegarder nouveau thème
    themeToggle.addEventListener('click', function() {
        const currentTheme = document.body.className;
        const newTheme = currentTheme === 'light' ? 'dark' : 'light';
        document.body.className = newTheme;
        localStorage.setItem('theme', newTheme);
    });
</script>

<!-- Panier d'achat -->
<script>
    function addToCart(product) {
        let cart = JSON.parse(localStorage.getItem('cart') || '[]');
        cart.push(product);
        localStorage.setItem('cart', JSON.stringify(cart));
    }
    
    function getCart() {
        return JSON.parse(localStorage.getItem('cart') || '[]');
    }
    
    function removeFromCart(index) {
        let cart = getCart();
        cart.splice(index, 1);
        localStorage.setItem('cart', JSON.stringify(cart));
    }
    
    function clearCart() {
        localStorage.removeItem('cart');
    }
</script>

<!-- Formulaire avec sauvegarde auto -->
<script>
    const form = document.getElementById('myForm');
    const inputs = form.querySelectorAll('input, textarea');
    
    // Charger valeurs sauvegardées
    inputs.forEach(input => {
        const savedValue = localStorage.getItem('form_' + input.name);
        if (savedValue) {
            input.value = savedValue;
        }
    });
    
    // Sauvegarder à chaque changement
    inputs.forEach(input => {
        input.addEventListener('input', function() {
            localStorage.setItem('form_' + input.name, input.value);
        });
    });
    
    // Effacer après soumission
    form.addEventListener('submit', function() {
        inputs.forEach(input => {
            localStorage.removeItem('form_' + input.name);
        });
    });
</script>


[OK] GEOLOCATION API

# === OBTENIR POSITION ===
<script>
    // Vérifier support
    if ('geolocation' in navigator) {
        console.log('Géolocalisation disponible');
    }
    
    // Obtenir position actuelle
    navigator.geolocation.getCurrentPosition(
        function(position) {
            // Succès
            const latitude = position.coords.latitude;
            const longitude = position.coords.longitude;
            const accuracy = position.coords.accuracy;  // Précision en mètres
            
            console.log('Latitude:', latitude);
            console.log('Longitude:', longitude);
            console.log('Précision:', accuracy, 'mètres');
            
            // Autres propriétés disponibles:
            // position.coords.altitude
            // position.coords.altitudeAccuracy
            // position.coords.heading (direction, 0-360°)
            // position.coords.speed (vitesse en m/s)
            // position.timestamp
        },
        function(error) {
            // Erreur
            switch(error.code) {
                case error.PERMISSION_DENIED:
                    console.log('Permission refusée');
                    break;
                case error.POSITION_UNAVAILABLE:
                    console.log('Position indisponible');
                    break;
                case error.TIMEOUT:
                    console.log('Délai dépassé');
                    break;
            }
        },
        {
            // Options
            enableHighAccuracy: true,  // Haute précision (GPS)
            timeout: 5000,             // Timeout en ms
            maximumAge: 0              // Âge max du cache (0 = toujours frais)
        }
    );
</script>

# === SUIVRE POSITION (GPS) ===
<script>
    let watchId;
    
    function startTracking() {
        watchId = navigator.geolocation.watchPosition(
            function(position) {
                const lat = position.coords.latitude;
                const lon = position.coords.longitude;
                console.log('Position mise à jour:', lat, lon);
                
                // Mettre à jour carte, etc.
            },
            function(error) {
                console.error('Erreur:', error.message);
            },
            {
                enableHighAccuracy: true,
                timeout: 10000,
                maximumAge: 0
            }
        );
    }
    
    function stopTracking() {
        if (watchId) {
            navigator.geolocation.clearWatch(watchId);
            watchId = null;
        }
    }
</script>

# === EXEMPLE COMPLET ===
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <title>Géolocalisation</title>
</head>
<body>
    <button id="getLocation">Obtenir ma position</button>
    <div id="result"></div>
    
    <script>
        document.getElementById('getLocation').addEventListener('click', function() {
            const resultDiv = document.getElementById('result');
            resultDiv.innerHTML = 'Chargement...';
            
            if (!navigator.geolocation) {
                resultDiv.innerHTML = 'Géolocalisation non supportée';
                return;
            }
            
            navigator.geolocation.getCurrentPosition(
                function(position) {
                    const lat = position.coords.latitude;
                    const lon = position.coords.longitude;
                    
                    resultDiv.innerHTML = `
                        <p><strong>Latitude:</strong> ${lat}</p>
                        <p><strong>Longitude:</strong> ${lon}</p>
                        <p><a href="https://www.google.com/maps?q=${lat},${lon}" 
                              target="_blank">Voir sur Google Maps</a></p>
                    `;
                },
                function(error) {
                    resultDiv.innerHTML = `Erreur: ${error.message}`;
                }
            );
        });
    </script>
</body>
</html>

# === CALCULER DISTANCE ENTRE DEUX POINTS ===
<script>
    function calculateDistance(lat1, lon1, lat2, lon2) {
        const R = 6371;  // Rayon de la Terre en km
        const dLat = (lat2 - lat1) * Math.PI / 180;
        const dLon = (lon2 - lon1) * Math.PI / 180;
        
        const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
                  Math.cos(lat1 * Math.PI / 180) * 
                  Math.cos(lat2 * Math.PI / 180) *
                  Math.sin(dLon/2) * Math.sin(dLon/2);
        
        const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
        const distance = R * c;
        
        return distance;  // Distance en km
    }
    
    // Exemple: Distance Paris - Lyon
    const distanceKm = calculateDistance(48.8566, 2.3522, 45.7640, 4.8357);
    console.log('Distance:', distanceKm.toFixed(2), 'km');
</script>


[OK] DRAG AND DROP API

# === ÉLÉMENT DÉPLAÇABLE ===
<div draggable="true" id="item">
    Glissez-moi!
</div>

<script>
    const item = document.getElementById('item');
    
    // Événements sur élément source (draggable)
    item.addEventListener('dragstart', function(e) {
        console.log('Début du glissement');
        
        // Stocker données à transférer
        e.dataTransfer.setData('text/plain', 'Données à transférer');
        e.dataTransfer.setData('text/html', '<strong>HTML</strong>');
        
        // Effet visuel
        e.dataTransfer.effectAllowed = 'move';  // 'copy', 'link', 'move'
        
        // Image de glissement personnalisée
        const img = new Image();
        img.src = 'drag-icon.png';
        e.dataTransfer.setDragImage(img, 0, 0);
        
        // Ajouter classe CSS pendant glissement
        this.classList.add('dragging');
    });
    
    item.addEventListener('drag', function(e) {
        // Pendant le glissement (se déclenche souvent)
    });
    
    item.addEventListener('dragend', function(e) {
        console.log('Fin du glissement');
        this.classList.remove('dragging');
    });
</script>

# === ZONE DE DÉPÔT ===
<div id="dropzone" style="width: 300px; height: 200px; border: 2px dashed #ccc;">
    Déposez ici
</div>

<script>
    const dropzone = document.getElementById('dropzone');
    
    // Événements sur zone de dépôt
    dropzone.addEventListener('dragenter', function(e) {
        e.preventDefault();  // IMPORTANT: autoriser le dépôt
        console.log('Entrée dans la zone');
        this.style.backgroundColor = '#e0e0e0';
    });
    
    dropzone.addEventListener('dragover', function(e) {
        e.preventDefault();  // IMPORTANT: autoriser le dépôt
        e.dataTransfer.dropEffect = 'move';  // Curseur
    });
    
    dropzone.addEventListener('dragleave', function(e) {
        console.log('Sortie de la zone');
        this.style.backgroundColor = '';
    });
    
    dropzone.addEventListener('drop', function(e) {
        e.preventDefault();  // Empêcher comportement par défaut
        console.log('Dépôt!');
        
        // Récupérer données
        const data = e.dataTransfer.getData('text/plain');
        console.log('Données reçues:', data);
        
        this.style.backgroundColor = '';
        this.innerHTML = 'Reçu: ' + data;
    });
</script>

# === EXEMPLE COMPLET: RÉORGANISER LISTE ===
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <title>Drag & Drop</title>
    <style>
        .list-item {
            padding: 10px;
            margin: 5px;
            background: #f0f0f0;
            border: 1px solid #ccc;
            cursor: move;
        }
        
        .list-item.dragging {
            opacity: 0.5;
        }
        
        .list-item.drag-over {
            border-top: 3px solid blue;
        }
    </style>
</head>
<body>
    <ul id="sortable-list">
        <li class="list-item" draggable="true">Élément 1</li>
        <li class="list-item" draggable="true">Élément 2</li>
        <li class="list-item" draggable="true">Élément 3</li>
        <li class="list-item" draggable="true">Élément 4</li>
    </ul>
    
    <script>
        const items = document.querySelectorAll('.list-item');
        let draggedItem = null;
        
        items.forEach(item => {
            item.addEventListener('dragstart', function() {
                draggedItem = this;
                this.classList.add('dragging');
            });
            
            item.addEventListener('dragend', function() {
                this.classList.remove('dragging');
                items.forEach(i => i.classList.remove('drag-over'));
            });
            
            item.addEventListener('dragover', function(e) {
                e.preventDefault();
                
                if (this === draggedItem) return;
                
                this.classList.add('drag-over');
            });
            
            item.addEventListener('dragleave', function() {
                this.classList.remove('drag-over');
            });
            
            item.addEventListener('drop', function(e) {
                e.preventDefault();
                
                if (this === draggedItem) return;
                
                // Insérer avant cet élément
                const list = this.parentNode;
                list.insertBefore(draggedItem, this);
            });
        });
    </script>
</body>
</html>

# === GLISSER-DÉPOSER FICHIERS ===
<div id="file-dropzone" style="width: 400px; height: 200px; border: 2px dashed #ccc;">
    Glissez fichiers ici
</div>
<div id="file-list"></div>

<script>
    const fileDropzone = document.getElementById('file-dropzone');
    const fileList = document.getElementById('file-list');
    
    fileDropzone.addEventListener('dragover', function(e) {
        e.preventDefault();
        this.style.backgroundColor = '#e8f4f8';
    });
    
    fileDropzone.addEventListener('dragleave', function(e) {
        this.style.backgroundColor = '';
    });
    
    fileDropzone.addEventListener('drop', function(e) {
        e.preventDefault();
        this.style.backgroundColor = '';
        
        const files = e.dataTransfer.files;
        console.log('Fichiers déposés:', files.length);
        
        fileList.innerHTML = '';
        
        for (let i = 0; i < files.length; i++) {
            const file = files[i];
            
            const fileInfo = document.createElement('div');
            fileInfo.textContent = `${file.name} (${file.size} octets, ${file.type})`;
            fileList.appendChild(fileInfo);
            
            // Lire fichier si image
            if (file.type.startsWith('image/')) {
                const reader = new FileReader();
                reader.onload = function(e) {
                    const img = document.createElement('img');
                    img.src = e.target.result;
                    img.style.maxWidth = '200px';
                    fileList.appendChild(img);
                };
                reader.readAsDataURL(file);
            }
        }
    });
</script>


[OK] FILE API

# === INPUT FILE ===
<input type="file" id="file-input">
<input type="file" id="multiple-files" multiple>
<input type="file" id="image-only" accept="image/*">
<input type="file" id="pdf-only" accept=".pdf,.doc,.docx">

<script>
    const fileInput = document.getElementById('file-input');
    
    fileInput.addEventListener('change', function(e) {
        const files = e.target.files;
        
        for (let i = 0; i < files.length; i++) {
            const file = files[i];
            
            console.log('Nom:', file.name);
            console.log('Taille:', file.size, 'octets');
            console.log('Type:', file.type);
            console.log('Dernière modif:', file.lastModified);
            console.log('Date:', new Date(file.lastModified));
        }
    });
</script>

# === FILEREADER - LIRE FICHIERS ===
<script>
    const reader = new FileReader();
    
    // Événements
    reader.onload = function(e) {
        console.log('Chargé!');
        console.log('Résultat:', e.target.result);
    };
    
    reader.onerror = function(e) {
        console.error('Erreur:', e.target.error);
    };
    
    reader.onprogress = function(e) {
        if (e.lengthComputable) {
            const percentLoaded = (e.loaded / e.total) * 100;
            console.log('Progression:', percentLoaded.toFixed(2), '%');
        }
    };
    
    // Méthodes de lecture
    reader.readAsText(file);           // Texte
    reader.readAsDataURL(file);        // Base64 (pour images)
    reader.readAsArrayBuffer(file);    // Binaire
    reader.readAsBinaryString(file);   // Chaîne binaire (obsolète)
    
    // Annuler lecture
    reader.abort();
</script>

# === EXEMPLE: PRÉVISUALISER IMAGE ===
<input type="file" id="image-input" accept="image/*">
<img id="preview" style="max-width: 400px;">

<script>
    document.getElementById('image-input').addEventListener('change', function(e) {
        const file = e.target.files[0];
        
        if (!file.type.startsWith('image/')) {
            alert('Veuillez sélectionner une image');
            return;
        }
        
        const reader = new FileReader();
        
        reader.onloa


# Fichier: python_cheats/cheatsheets/html_suite.txt
# Cheatsheet HTML (Suite) - Guide Complet pour Débutants


[OK] FILE API (SUITE)

# === EXEMPLE: LIRE FICHIER TEXTE ===
<input type="file" id="text-file" accept=".txt">
<pre id="text-content"></pre>

<script>
    document.getElementById('text-file').addEventListener('change', function(e) {
        const file = e.target.files[0];
        
        if (!file) return;
        
        const reader = new FileReader();
        
        reader.onload = function(e) {
            document.getElementById('text-content').textContent = e.target.result;
        };
        
        reader.onerror = function() {
            alert('Erreur de lecture du fichier');
        };
        
        reader.readAsText(file, 'UTF-8');  // Encodage
    });
</script>

# === EXEMPLE: UPLOADER FICHIER (AJAX) ===
<input type="file" id="upload-file">
<button id="upload-btn">Upload</button>
<div id="upload-progress"></div>

<script>
    document.getElementById('upload-btn').addEventListener('click', function() {
        const fileInput = document.getElementById('upload-file');
        const file = fileInput.files[0];
        
        if (!file) {
            alert('Sélectionnez un fichier');
            return;
        }
        
        const formData = new FormData();
        formData.append('file', file);
        formData.append('description', 'Mon fichier');
        
        const xhr = new XMLHttpRequest();
        
        // Progression upload
        xhr.upload.addEventListener('progress', function(e) {
            if (e.lengthComputable) {
                const percentComplete = (e.loaded / e.total) * 100;
                document.getElementById('upload-progress').textContent = 
                    'Upload: ' + percentComplete.toFixed(2) + '%';
            }
        });
        
        // Fin upload
        xhr.addEventListener('load', function() {
            if (xhr.status === 200) {
                alert('Upload réussi!');
                console.log('Réponse:', xhr.responseText);
            } else {
                alert('Erreur upload');
            }
        });
        
        xhr.addEventListener('error', function() {
            alert('Erreur réseau');
        });
        
        xhr.open('POST', 'upload.php');
        xhr.send(formData);
    });
</script>

# === EXEMPLE: LIRE FICHIER CSV ===
<input type="file" id="csv-file" accept=".csv">
<table id="csv-table" border="1"></table>

<script>
    document.getElementById('csv-file').addEventListener('change', function(e) {
        const file = e.target.files[0];
        const reader = new FileReader();
        
        reader.onload = function(e) {
            const csv = e.target.result;
            const lines = csv.split('\n');
            
            let tableHTML = '';
            
            lines.forEach((line, index) => {
                const cells = line.split(',');
                const tag = index === 0 ? 'th' : 'td';
                
                tableHTML += '<tr>';
                cells.forEach(cell => {
                    tableHTML += `<${tag}>${cell.trim()}</${tag}>`;
                });
                tableHTML += '</tr>';
            });
            
            document.getElementById('csv-table').innerHTML = tableHTML;
        };
        
        reader.readAsText(file);
    });
</script>

# === EXEMPLE: REDIMENSIONNER IMAGE ===
<input type="file" id="image-resize" accept="image/*">
<canvas id="canvas"></canvas>

<script>
    document.getElementById('image-resize').addEventListener('change', function(e) {
        const file = e.target.files[0];
        const reader = new FileReader();
        
        reader.onload = function(e) {
            const img = new Image();
            
            img.onload = function() {
                const canvas = document.getElementById('canvas');
                const ctx = canvas.getContext('2d');
                
                // Nouvelles dimensions
                const maxWidth = 400;
                const maxHeight = 300;
                
                let width = img.width;
                let height = img.height;
                
                // Calculer ratio
                if (width > height) {
                    if (width > maxWidth) {
                        height *= maxWidth / width;
                        width = maxWidth;
                    }
                } else {
                    if (height > maxHeight) {
                        width *= maxHeight / height;
                        height = maxHeight;
                    }
                }
                
                canvas.width = width;
                canvas.height = height;
                
                // Dessiner image redimensionnée
                ctx.drawImage(img, 0, 0, width, height);
                
                // Obtenir image redimensionnée
                canvas.toBlob(function(blob) {
                    console.log('Taille originale:', file.size);
                    console.log('Nouvelle taille:', blob.size);
                    
                    // Télécharger
                    const url = URL.createObjectURL(blob);
                    const a = document.createElement('a');
                    a.href = url;
                    a.download = 'resized_' + file.name;
                    a.click();
                    URL.revokeObjectURL(url);
                }, 'image/jpeg', 0.9);
            };
            
            img.src = e.target.result;
        };
        
        reader.readAsDataURL(file);
    });
</script>

# === LIMITES ET VALIDATION ===
<script>
    function validateFile(file) {
        // Taille max: 5 MB
        const maxSize = 5 * 1024 * 1024;
        if (file.size > maxSize) {
            alert('Fichier trop volumineux (max 5 MB)');
            return false;
        }
        
        // Types autorisés
        const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
        if (!allowedTypes.includes(file.type)) {
            alert('Type de fichier non autorisé');
            return false;
        }
        
        return true;
    }
    
    fileInput.addEventListener('change', function(e) {
        const file = e.target.files[0];
        if (file && validateFile(file)) {
            // Traiter fichier
        }
    });
</script>


[OK] WEB WORKERS (MULTITHREADING)

# === CRÉER WEB WORKER ===

<!-- Fichier: index.html -->
<button id="start-worker">Démarrer calcul</button>
<div id="result"></div>

<script>
    // Vérifier support
    if (window.Worker) {
        // Créer worker
        const worker = new Worker('worker.js');
        
        // Recevoir messages du worker
        worker.onmessage = function(e) {
            console.log('Message du worker:', e.data);
            document.getElementById('result').textContent = e.data;
        };
        
        // Gérer erreurs
        worker.onerror = function(e) {
            console.error('Erreur worker:', e.message);
        };
        
        // Envoyer message au worker
        document.getElementById('start-worker').addEventListener('click', function() {
            worker.postMessage({ action: 'calculate', number: 1000000 });
        });
        
        // Terminer worker
        // worker.terminate();
    } else {
        console.log('Web Workers non supportés');
    }
</script>

<!-- Fichier: worker.js -->
<script>
    // Dans le worker (pas d'accès au DOM!)
    
    // Recevoir messages
    self.onmessage = function(e) {
        console.log('Message reçu:', e.data);
        
        if (e.data.action === 'calculate') {
            const number = e.data.number;
            
            // Calcul lourd
            let sum = 0;
            for (let i = 0; i < number; i++) {
                sum += i;
            }
            
            // Envoyer résultat
            self.postMessage('Résultat: ' + sum);
        }
    };
    
    // Envoyer progression
    self.onmessage = function(e) {
        const total = e.data.number;
        let sum = 0;
        
        for (let i = 0; i < total; i++) {
            sum += i;
            
            // Envoyer progression tous les 10%
            if (i % (total / 10) === 0) {
                self.postMessage({
                    type: 'progress',
                    percent: (i / total) * 100
                });
            }
        }
        
        self.postMessage({
            type: 'complete',
            result: sum
        });
    };
</script>

# === EXEMPLE: CALCUL FACTORIELLE ===

<!-- index.html -->
<script>
    const worker = new Worker('factorial-worker.js');
    
    worker.onmessage = function(e) {
        console.log('Factorielle:', e.data);
    };
    
    worker.postMessage(10);  // Calculer 10!
</script>

<!-- factorial-worker.js -->
<script>
    function factorial(n) {
        if (n === 0 || n === 1) return 1;
        return n * factorial(n - 1);
    }
    
    self.onmessage = function(e) {
        const n = e.data;
        const result = factorial(n);
        self.postMessage(result);
    };
</script>

# === WORKER INLINE ===
<script>
    // Créer worker depuis string
    const workerCode = `
        self.onmessage = function(e) {
            self.postMessage('Bonjour ' + e.data);
        };
    `;
    
    const blob = new Blob([workerCode], { type: 'application/javascript' });
    const workerUrl = URL.createObjectURL(blob);
    const worker = new Worker(workerUrl);
    
    worker.onmessage = function(e) {
        console.log(e.data);
    };
    
    worker.postMessage('Monde');
    
    // Nettoyer
    // URL.revokeObjectURL(workerUrl);
</script>

# === SHARED WORKERS (partagés entre onglets) ===
<script>
    // Créer shared worker
    const sharedWorker = new SharedWorker('shared-worker.js');
    
    // Communiquer via port
    sharedWorker.port.start();
    
    sharedWorker.port.onmessage = function(e) {
        console.log('Message:', e.data);
    };
    
    sharedWorker.port.postMessage('Hello');
</script>

<!-- shared-worker.js -->
<script>
    let connections = 0;
    
    self.onconnect = function(e) {
        const port = e.ports[0];
        connections++;
        
        port.onmessage = function(e) {
            port.postMessage('Connectés: ' + connections);
        };
        
        port.start();
    };
</script>

# === LIMITATIONS WEB WORKERS ===
# [OK] Calculs lourds sans bloquer UI
# [OK] Peut utiliser: XMLHttpRequest, WebSockets, IndexedDB
# [X] Pas d'accès au DOM
# [X] Pas d'accès à window, document, parent
# [X] Accès limité à navigator, location


[OK] SERVICE WORKERS (PWA)

# === ENREGISTRER SERVICE WORKER ===
<script>
    // Vérifier support
    if ('serviceWorker' in navigator) {
        // Enregistrer après chargement page
        window.addEventListener('load', function() {
            navigator.serviceWorker.register('/service-worker.js')
                .then(function(registration) {
                    console.log('Service Worker enregistré:', registration.scope);
                })
                .catch(function(error) {
                    console.error('Erreur enregistrement:', error);
                });
        });
    }
</script>

# === SERVICE WORKER BASIQUE ===
<!-- service-worker.js -->
<script>
    const CACHE_NAME = 'mon-app-v1';
    const urlsToCache = [
        '/',
        '/styles.css',
        '/script.js',
        '/index.html',
        '/offline.html'
    ];
    
    // Installation
    self.addEventListener('install', function(event) {
        console.log('Service Worker: Installation...');
        
        event.waitUntil(
            caches.open(CACHE_NAME)
                .then(function(cache) {
                    console.log('Cache ouvert');
                    return cache.addAll(urlsToCache);
                })
        );
    });
    
    // Activation
    self.addEventListener('activate', function(event) {
        console.log('Service Worker: Activation...');
        
        // Nettoyer anciens caches
        event.waitUntil(
            caches.keys().then(function(cacheNames) {
                return Promise.all(
                    cacheNames.map(function(cacheName) {
                        if (cacheName !== CACHE_NAME) {
                            console.log('Suppression ancien cache:', cacheName);
                            return caches.delete(cacheName);
                        }
                    })
                );
            })
        );
    });
    
    // Interception requêtes
    self.addEventListener('fetch', function(event) {
        event.respondWith(
            caches.match(event.request)
                .then(function(response) {
                    // Retourner cache si disponible
                    if (response) {
                        return response;
                    }
                    
                    // Sinon, faire requête réseau
                    return fetch(event.request);
                })
                .catch(function() {
                    // Si hors ligne, retourner page offline
                    return caches.match('/offline.html');
                })
        );
    });
</script>

# === STRATÉGIES DE CACHE ===

<!-- Cache First (cache prioritaire) -->
<script>
    self.addEventListener('fetch', function(event) {
        event.respondWith(
            caches.match(event.request)
                .then(function(response) {
                    return response || fetch(event.request);
                })
        );
    });
</script>

<!-- Network First (réseau prioritaire) -->
<script>
    self.addEventListener('fetch', function(event) {
        event.respondWith(
            fetch(event.request)
                .catch(function() {
                    return caches.match(event.request);
                })
        );
    });
</script>

<!-- Stale While Revalidate (cache + mise à jour) -->
<script>
    self.addEventListener('fetch', function(event) {
        event.respondWith(
            caches.open(CACHE_NAME).then(function(cache) {
                return cache.match(event.request).then(function(response) {
                    const fetchPromise = fetch(event.request).then(function(networkResponse) {
                        cache.put(event.request, networkResponse.clone());
                        return networkResponse;
                    });
                    
                    return response || fetchPromise;
                });
            })
        );
    });
</script>

# === NOTIFICATIONS PUSH ===
<script>
    // Demander permission
    Notification.requestPermission().then(function(permission) {
        if (permission === 'granted') {
            console.log('Permission accordée');
        }
    });
    
    // Dans service worker
    self.addEventListener('push', function(event) {
        const data = event.data.json();
        
        const options = {
            body: data.body,
            icon: '/icon.png',
            badge: '/badge.png',
            vibrate: [200, 100, 200]
        };
        
        event.waitUntil(
            self.registration.showNotification(data.title, options)
        );
    });
    
    // Clic sur notification
    self.addEventListener('notificationclick', function(event) {
        event.notification.close();
        
        event.waitUntil(
            clients.openWindow('https://example.com')
        );
    });
</script>

# === SYNCHRONISATION EN ARRIÈRE-PLAN ===
<script>
    // Enregistrer sync
    navigator.serviceWorker.ready.then(function(registration) {
        return registration.sync.register('sync-data');
    });
    
    // Dans service worker
    self.addEventListener('sync', function(event) {
        if (event.tag === 'sync-data') {
            event.waitUntil(
                // Synchroniser données
                syncData()
            );
        }
    });
    
    function syncData() {
        return fetch('/api/sync', {
            method: 'POST',
            body: JSON.stringify(data)
        });
    }
</script>

# === METTRE À JOUR SERVICE WORKER ===
<script>
    // Détecter nouvelle version
    navigator.serviceWorker.register('/service-worker.js').then(function(reg) {
        reg.onupdatefound = function() {
            const installingWorker = reg.installing;
            
            installingWorker.onstatechange = function() {
                if (installingWorker.state === 'installed') {
                    if (navigator.serviceWorker.controller) {
                        // Nouvelle version disponible
                        console.log('Nouvelle version disponible!');
                        
                        // Afficher notification utilisateur
                        if (confirm('Nouvelle version disponible. Recharger?')) {
                            window.location.reload();
                        }
                    }
                }
            };
        };
    });
    
    // Forcer activation nouvelle version
    self.addEventListener('install', function(event) {
        self.skipWaiting();
    });
    
    self.addEventListener('activate', function(event) {
        event.waitUntil(clients.claim());
    });
</script>


[OK] WEB MANIFEST (PWA)

# === MANIFEST.JSON ===
{
  "name": "Mon Application PWA",
  "short_name": "MonApp",
  "description": "Description de mon application",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#2196F3",
  "orientation": "portrait",
  "icons": [
    {
      "src": "/icons/icon-72x72.png",
      "sizes": "72x72",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-96x96.png",
      "sizes": "96x96",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-128x128.png",
      "sizes": "128x128",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-144x144.png",
      "sizes": "144x144",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-152x152.png",
      "sizes": "152x152",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-384x384.png",
      "sizes": "384x384",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}

# Propriétés:
# name          - Nom complet de l'app
# short_name    - Nom court (écran d'accueil)
# description   - Description de l'app
# start_url     - URL de démarrage
# display       - Mode d'affichage
#   - standalone: comme app native (recommandé)
#   - fullscreen: plein écran
#   - minimal-ui: barre minimale
#   - browser: navigateur normal
# background_color - Couleur de fond (écran de démarrage)
# theme_color      - Couleur de thème (barre d'adresse)
# orientation      - Orientation de l'écran
#   - portrait, landscape, any
# icons         - Icônes de l'app (plusieurs tailles)

# === LIER MANIFEST ===
<head>
    <link rel="manifest" href="/manifest.json">
    
    <!-- Meta tags supplémentaires -->
    <meta name="theme-color" content="#2196F3">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <meta name="apple-mobile-web-app-title" content="MonApp">
    
    <!-- Icônes Apple -->
    <link rel="apple-touch-icon" href="/icons/icon-152x152.png">
</head>

# === MANIFEST AVANCÉ ===
{
  "name": "Application Complète",
  "short_name": "App",
  "start_url": "/?utm_source=homescreen",
  "scope": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#2196F3",
  "orientation": "portrait-primary",
  "categories": ["productivity", "business"],
  "lang": "fr-FR",
  "dir": "ltr",
  "icons": [...],
  "screenshots": [
    {
      "src": "/screenshots/desktop.png",
      "sizes": "1280x720",
      "type": "image/png"
    },
    {
      "src": "/screenshots/mobile.png",
      "sizes": "750x1334",
      "type": "image/png"
    }
  ],
  "shortcuts": [
    {
      "name": "Nouvelle tâche",
      "short_name": "Nouvelle",
      "description": "Créer une nouvelle tâche",
      "url": "/new-task",
      "icons": [{ "src": "/icons/new.png", "sizes": "96x96" }]
    }
  ],
  "related_applications": [
    {
      "platform": "play",
      "url": "https://play.google.com/store/apps/details?id=com.example.app",
      "id": "com.example.app"
    }
  ],
  "prefer_related_applications": false
}

# === DÉTECTER SI INSTALLÉ ===
<script>
    // Vérifier si en mode standalone
    if (window.matchMedia('(display-mode: standalone)').matches) {
        console.log('App installée!');
    }
    
    // Ou
    if (window.navigator.standalone) {
        console.log('App installée (iOS)!');
    }
    
    // Bouton d'installation
    let deferredPrompt;
    
    window.addEventListener('beforeinstallprompt', function(e) {
        // Empêcher prompt automatique
        e.preventDefault();
        
        // Sauvegarder événement
        deferredPrompt = e;
        
        // Afficher bouton d'installation
        const installBtn = document.getElementById('install-btn');
        installBtn.style.display = 'block';
        
        installBtn.addEventListener('click', function() {
            // Afficher prompt
            deferredPrompt.prompt();
            
            // Attendre réponse utilisateur
            deferredPrompt.userChoice.then(function(choiceResult) {
                if (choiceResult.outcome === 'accepted') {
                    console.log('App installée');
                } else {
                    console.log('Installation annulée');
                }
                
                deferredPrompt = null;
            });
        });
    });
    
    // Détecter installation réussie
    window.addEventListener('appinstalled', function() {
        console.log('App installée avec succès!');
        
        // Cacher bouton installation
        document.getElementById('install-btn').style.display = 'none';
    });
</script>


[OK] NOTIFICATIONS API

# === DEMANDER PERMISSION ===
<button id="request-permission">Autoriser notifications</button>

<script>
    document.getElementById('request-permission').addEventListener('click', function() {
        if ('Notification' in window) {
            Notification.requestPermission().then(function(permission) {
                console.log('Permission:', permission);
                // 'granted', 'denied', ou 'default'
                
                if (permission === 'granted') {
                    console.log('Permission accordée!');
                } else if (permission === 'denied') {
                    console.log('Permission refusée');
                } else {
                    console.log('Permission en attente');
                }
            });
        } else {
            alert('Notifications non supportées');
        }
    });
</script>

# === AFFICHER NOTIFICATION ===
<script>
    // Notification simple
    if (Notification.permission === 'granted') {
        new Notification('Titre de la notification');
    }
    
    // Notification avec options
    const options = {
        body: 'Corps de la notification',
        icon: '/icon.png',
        badge: '/badge.png',
        image: '/image.jpg',
        vibrate: [200, 100, 200],  // Pattern de vibration (ms)
        sound: '/notification.mp3',
        tag: 'notification-id',     // ID unique (évite doublons)
        requireInteraction: false,  // Ne se ferme pas auto
        silent: false,              // Silencieux
        timestamp: Date.now(),
        data: { userId: 123 },      // Données personnalisées
        actions: [                  // Boutons d'action
            { action: 'open', title: 'Ouvrir' },
            { action: 'close', title: 'Fermer' }
        ],
        dir: 'auto',               // Direction texte (ltr/rtl/auto)
        lang: 'fr-FR',
        renotify: false            // Alerter même si tag existe
    };
    
    const notification = new Notification('Titre', options);
</script>

# === ÉVÉNEMENTS NOTIFICATION ===
<script>
    const notification = new Notification('Titre', options);
    
    // Clic sur notification
    notification.onclick = function(event) {
        console.log('Notification cliquée');
        event.preventDefault();  // Empêcher comportement par défaut
        
        // Ouvrir fenêtre
        window.open('https://example.com', '_blank');
        
        // Fermer notification
        notification.close();
    };
    
    // Notification affichée
    notification.onshow = function() {
        console.log('Notification affichée');
    };
    
    // Notification fermée
    notification.onclose = function() {
        console.log('Notification fermée');
    };
    
    // Erreur
    notification.onerror = function() {
        console.error('Erreur notification');
    };
</script>

# === EXEMPLE COMPLET ===
<button id="notify-btn">Envoyer notification</button>

<script>
    function sendNotification() {
        // Vérifier support
        if (!('Notification' in window)) {
            alert('Notifications non supportées');
            return;
        }
        
        // Vérifier permission
        if (Notification.permission === 'granted') {
            showNotification();
        } else if (Notification.permission !== 'denied') {
            // Demander permission
            Notification.requestPermission().then(function(permission) {
                if (permission === 'granted') {
                    showNotification();
                }
            });
        }
    }
    
    function showNotification() {
        const options = {
            body: 'Vous avez un nouveau message',
            icon: '/icon.png',
            badge: '/badge.png',
            vibrate: [200, 100, 200],
            tag: 'message-notification',
            data: { url: '/messages' }
        };
        
        const notification = new Notification('Nouveau message', options);
        
        notification.onclick = function() {
            window.focus();
            window.location.href = this.data.url;
            this.close();
        };
        
        // Fermer automatiquement après 5 secondes
        setTimeout(function() {
            notification.close();
        }, 5000);
    }
    
    document.getElementById('notify-btn').addEventListener('click', sendNotification);
</script>

# === NOTIFICATIONS AVEC SERVICE WORKER ===
<script>
    // Dans la page
    navigator.serviceWorker.ready.then(function(registration) {
        registration.showNotification('Titre', {
            body: 'Corps de la notification',
            icon: '/icon.png',
            vibrate: [200, 100, 200],
            actions: [
                { action: 'open', title: 'Ouvrir', icon: '/open.png' },
                { action: 'close', title: 'Fermer', icon: '/close.png' }
            ]
        });
    });
    
    // Dans service-worker.js
    self.addEventListener('notificationclick', function(event) {
        event.notification.close();
        
        if (event.action === 'open') {
            clients.openWindow('/');
        } else if (event.action === 'close') {
            // Ne rien faire, juste fermer
        } else {
            // Clic sur notification (pas sur bouton)
            clients.openWindow('/');
        }
    });
</script>


[OK] CLIPBOARD API (PRESSE-PAPIERS)

# === COPIER TEXTE ===
<button id="copy-btn">Copier</button>
<input type="text" id="text-input" value="Texte à copier">

<script>
    document.getElementById('copy-btn').addEventListener('click', async function() {
        const text = document.getEleme