Avez-vous une question? (216) 97 656 803 hajjriadh@gmail.com
Flutter

TP Les formulaires Bootstrap 5.3 : portail d'inscription FormaPlus

Un cours complet pour maîtriser TP Les formulaires Bootstrap 5.3 : portail d'inscription FormaPlus.

0 chapitres 0 QCM Accès gratuit
Dans cet article

TP Les formulaires Bootstrap 5.3 : portail d'inscription FormaPlus

  1. Objectifs

  • Ce TP de 6 heures clôt le chapitre Les formulaires Bootstrap. En le réalisant, vous allez apprendre à :
    • intégrer Bootstrap 5.3 dans un site de plusieurs pages (CDN, gabarit commun, barre de navigation) ;
    • utiliser tous les contrôles de formulaire: types <input> HTML5, <textarea>, <select>, <datalist>, type="color", type="file" et curseur .form-range;
    • gérer les choix avec les cases à cocher, les boutons radio, les interrupteurs (.form-switch) et les boutons bascules (.btn-check) ;
    • construire un formulaire de base (vertical) : .form-label, .form-text, tailles, <fieldset>, champs readonly / disabled;
    • construire des formulaires en ligne sans .form-inline: grille .row-cols-lg-auto, .col-auto, utilitaires flex ;
    • construire un formulaire horizontal avec la grille : .col-form-label, .offset-*, <legend>;
    • enrichir les champs avec les groupes de saisie (.input-group) et les étiquettes flottantes (.form-floating) ;
    • mettre en place la validation côté navigateur (novalidate, .was-validated, messages) et afficher une validation côté serveur (.is-valid / .is-invalid) ;
    • versionner son travail avec Git, le publier sur GitHub dès le départ et le mettre en ligne avec GitHub Pages: dépôt, commit à chaque étape, push, README.md et version finale étiquetée.
  • Prérequis : HTML5 et CSS de base ; avoir lu les cours Contrôles de formulaire, Formulaires de base, Formulaires en ligne, Formulaires horizontaux et Validation des formulaires ; connaître le système de grille de Bootstrap (.container, .row, .col-*).
  • Outils : un éditeur de code (VS Code conseillé, avec l'extension Live Server), un navigateur récent (Chrome, Firefox ou Edge) et ses outils de développement (F12).
  • Avant la séance : un compte GitHub gratuit et Git installé sur le poste (vérifier avec git --version).
  1. Description

  • Le centre de formation FormaPlus souhaite un petit site web pour présenter ses formations et permettre aux candidats de s'inscrire en ligne, de se connecter, de compléter leur profil et de contacter le centre.
  • Le site final comporte cinq pages qui partagent la même barre de navigation. Chaque page met en œuvre un ou plusieurs types de formulaires Bootstrap :
Page Fichier Type de formulaire et notions du chapitre
Accueil index.html Formulaires en ligne: recherche dans la navbar, barre de filtres, newsletter
Inscription inscription.html Formulaire de base (vertical) : tous les contrôles, cases, radios, interrupteurs, <fieldset>, validation
Connexion connexion.html Étiquettes flottantes, groupes de saisie, afficher / masquer le mot de passe
Mon profil profil.html Formulaire horizontal, validation côté serveur simulée (.is-valid / .is-invalid)
Contact contact.html Validation complète : novalidate, JavaScript, messages, info-bulles
  • Répartition du temps :
Partie Notion(s) travaillée(s) Durée
Partie 00 – Mise en place du projet et du dépôt GitHub Git, GitHub, arborescence 20 min
Partie 01 – Gabarit Bootstrap 5.3 et navigation CDN, navbar, grille 25 min
Partie 02 – Les contrôles de formulaire .form-control, .form-select, .form-range, <datalist> 45 min
Partie 03 – Cases, radios, interrupteurs et boutons bascules .form-check, .form-switch, .btn-check 40 min
Partie 04 – Le formulaire de base (vertical) .form-label, .form-text, tailles, <fieldset>, readonly 40 min
Partie 05 – Les formulaires en ligne .row-cols-lg-auto, .col-auto, .d-flex, .visually-hidden 35 min
Partie 06 – Le formulaire horizontal .col-form-label, .offset-*, <legend> 40 min
Partie 07 – Groupes de saisie et étiquettes flottantes .input-group, .form-floating 35 min
Partie 08 – La validation des formulaires novalidate, .was-validated, .is-invalid, JavaScript 50 min
Partie 09 – Intégration finale, GitHub Pages et version 1.0 mode sombre, README.md, GitHub Pages, git tag 30 min
Total   6 h
  1. Travail à réaliser

  • Partie 00Mise en place du projet et du dépôt GitHub
  • Q0.1. Créer un dossier formaplus et l'ouvrir dans VS Code (Fichier → Ouvrir le dossier).
  • Q0.2. Créer l'arborescence ci-dessous (à gauche). Les fichiers HTML peuvent être vides pour l'instant.
  • Q0.3. Placer dans images/ deux images de votre choix : un logo (logo.png, environ 40 × 40 px) et une photo de profil (avatar.png, environ 150 × 150 px). Utilisez des images libres de droits ou créez-les vous-même.
  • Q0.4. Créer le fichier .gitignore (à droite) pour exclure les fichiers propres à votre poste.
  • Q0.5. Créer le fichier reponses.md avec un titre # Réponses – TP Formulaires Bootstrap et votre nom : vous y noterez les réponses aux questions tout au long du TP.
formaplus/
├── .gitignore
├── README.md            (Partie 09)
├── reponses.md          (au fil du TP)
├── index.html           (Accueil)
├── inscription.html     (Inscription)
├── connexion.html       (Connexion)
├── profil.html          (Mon profil)
├── contact.html         (Contact)
├── css/
│   └── style.css
├── js/
│   ├── main.js          (Parties 02, 07, 09)
│   └── validation.js    (Partie 08)
├── images/
│   ├── logo.png
│   └── avatar.png
└── captures/            (Partie 09)
# .gitignore
# Fichiers système
.DS_Store
Thumbs.db

# Paramètres de l'éditeur
.vscode/

# Fichiers temporaires
*.tmp
*.log
  • Publier le projet sur GitHub dès le départ
  • Q0.6. Sur github.com/new, créer un dépôt public nommé formaplus, sans cocher « Add a README », « .gitignore » ni « license » (le projet existe déjà en local). Le dépôt doit être public pour pouvoir utiliser GitHub Pages gratuitement en partie 09.
  • Q0.7. Configurer Git une seule fois sur le poste avec votre nom et l'adresse e-mail de votre compte GitHub (commandes 1 et 2 ci-dessous).
  • Q0.8. Dans le dossier formaplus, initialiser le dépôt local, faire le premier commit et l'envoyer sur GitHub (commandes 3 à 8). Remplacer VOTRE-COMPTE par votre nom d'utilisateur GitHub.
# 1-2. Identité (une seule fois par poste)
git config --global user.name "Prénom Nom"
git config --global user.email "adresse@exemple.com"

# 3-8. Premier envoi du projet
cd formaplus
git init
git add .
git commit -m "Partie 00 : création du projet FormaPlus"
git branch -M main
git remote add origin https://github.com/VOTRE-COMPTE/formaplus.git
git push -u origin main
# Vérifications utiles
git status          # fichiers modifiés
git log --oneline   # historique des commits
git remote -v       # adresse du dépôt GitHub
  • Q0.9. Actualiser la page du dépôt sur GitHub. Le dossier captures/ est vide : apparaît-il sur GitHub ? Pourquoi ? Ajouter un fichier vide captures/.gitkeep, puis faire un nouveau commit et un push. (Réponse dans reponses.md. )
  • Q0.10. Envoyer le lien du dépôt à l'enseignant avant de commencer la partie 01.
Authentification : lors du premier git push, GitHub n'accepte plus le mot de passe du compte. Connectez-vous par le navigateur quand Git le propose (Git Credential Manager, VS Code ou GitHub Desktop), ou utilisez un Personal Access Token (Settings → Developer settings → Personal access tokens) à la place du mot de passe.
Règle du TP : un commit et un push à la fin de chaque partie.
Chaque partie se termine par un encadré « Git » qui donne le message de commit attendu. L'historique des commits fait partie de la note : un dépôt qui ne contient qu'un seul commit déposé en fin de séance n'obtient aucun point sur le critère Git.
Question de réflexion : pourquoi est-il préférable de faire plusieurs petits commits plutôt qu'un seul gros commit à la fin ?
  • Partie 01Gabarit Bootstrap 5.3 et navigation
  • Objectif : créer une page modèle qui charge Bootstrap 5.3, puis la dupliquer pour obtenir les cinq pages du site.
  • Q1.1. Dans index.html, écrire le gabarit ci-dessous. Il charge la feuille de style de Bootstrap 5.3 et les icônes Bootstrap Icons dans le <head>, et le JavaScript de Bootstrap (bootstrap.bundle.min.js) juste avant </body>.
  • Q1.2. À quoi sert la balise <meta name="viewport">? Supprimez-la temporairement et observez la page en mode mobile dans les outils de développement (F12 puis Ctrl+Maj+M). Remettez-la.
  • Q1.3. Pourquoi charge-t-on bootstrap.bundle.min.js et non bootstrap.min.js? (Indice : Popper. )
<!DOCTYPE html>
<html lang="fr" data-bs-theme="light">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>FormaPlus – Accueil</title>
  <!-- Bootstrap 5.3 -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
  <!-- Bootstrap Icons -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
  <!-- Styles personnels (toujours APRÈS Bootstrap) -->
  <link rel="stylesheet" href="css/style.css">
</head>
<body class="d-flex flex-column min-vh-100">

  <!-- Barre de navigation (Q1.4) -->

  <main class="container my-4 flex-grow-1">
    <h1 class="h3 mb-4">Bienvenue sur FormaPlus</h1>
    <!-- Contenu de la page -->
  </main>

  <footer class="bg-body-tertiary py-3 mt-auto">
    <div class="container text-center small">&copy; 2026 FormaPlus – Prénom Nom</div>
  </footer>

  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
  <script src="js/main.js"></script>
</body>
</html>
  • Q1.4. Ajouter une barre de navigation .navbar.navbar-expand-lg sur fond sombre (.bg-dark + data-bs-theme="dark") contenant :
    • à gauche, la marque (.navbar-brand) : le logo images/logo.png (hauteur 32 px) suivi du texte « FormaPlus » ;
    • un bouton .navbar-toggler qui replie le menu sur mobile (data-bs-toggle="collapse") ;
    • les liens : Accueil, Inscription, Connexion, Mon profil, Contact. Le lien de la page courante porte la classe .active et l'attribut aria-current="page".
  • Q1.5. Réduire la largeur de la fenêtre : à partir de quelle largeur le menu se replie-t-il ? Quel mot de la classe .navbar-expand-lg fixe ce seuil ?
  • Q1.6. Dupliquer index.html pour créer les quatre autres pages. Pour chacune, adapter le <title>, le titre <h1> et le lien .active. Vérifier que la navigation fonctionne entre les cinq pages (avec Live Server).
  • Q1.7. Dans css/style.css, ajouter une règle qui limite la largeur des formulaires à 900 px et les centre :
/* css/style.css */
.form-page {
  max-width: 900px;
  margin-inline: auto;
}
// js/main.js
// Ce fichier sera complété aux parties 02, 07 et 09.
console.log('FormaPlus : main.js chargé');
  • Q1.8. Ouvrir la console du navigateur (F12 → Console) : le message de main.js s'affiche-t-il ? S'il y a une erreur 404, corriger le chemin du fichier.

Résultat attendu de la partie 01 : page d'accueil avec la barre de navigation FormaPlus

Résultat attendu à la fin de la partie 01.

Git – fin de la partie 01 : enregistrer et publier votre travail, puis vérifier le nouveau commit sur GitHub.
git add .
git commit -m "Partie 01 : gabarit Bootstrap 5.3 et navigation des 5 pages"
git push
  • Partie 02Les contrôles de formulaire
  • Objectif : construire la première version du formulaire d'inscription en utilisant tous les types de contrôles pris en charge par Bootstrap 5.3.
  • Q2.1. Dans inscription.html, à l'intérieur du <main>, créer le formulaire suivant. Chaque couple étiquette / champ est placé dans un <div class="mb-3">.
<form id="formInscription" class="form-page" action="#" method="get">
  <h2 class="h5 mb-3"><i class="bi bi-person-vcard"></i> Informations personnelles</h2>

  <div class="mb-3">
    <label for="nom" class="form-label">Nom</label>
    <input type="text" class="form-control" id="nom" name="nom" placeholder="Votre nom">
  </div>

  <!-- Q2.2 : compléter avec les autres champs -->

  <button type="submit" class="btn btn-primary">Envoyer</button>
</form>
  • Q2.2. Compléter la section « Informations personnelles » avec les champs suivants (chacun avec un <label>, un id unique et un attribut name). Choisir à chaque fois le bon type d'<input>:
Champ id / name Type à utiliser Contraintes / remarques
Prénom prenom … placeholder « Votre prénom »
Adresse e-mail email … placeholder « nom@exemple.com »
Mot de passe mdp … —
Téléphone tel … placeholder « 20 123 456 »
Date de naissance naissance … —
Âge age … min="16", max="60"
Site web ou profil LinkedIn site … facultatif
Heure de disponibilité pour un appel heure … —
  • Q2.3. Ouvrir la page sur un téléphone (ou en mode mobile dans le navigateur). Comparer le clavier affiché pour les champs email, tel et number. Pourquoi le choix du type est-il important ? Remplir le tableau de la Q2.2 dans reponses.md.
  • Q2.4. Ajouter une deuxième section « Formation souhaitée » (titre <h2 class="h5"> avec l'icône bi-mortarboard) contenant :
    • une liste déroulante <select class="form-select"> « Domaine » : Développement web, Développement mobile, Réseaux, Bureautique. La première option « Choisir un domaine » porte selected disabled value="";
    • une liste à sélection multiple « Langages déjà pratiqués » (multiple, size="4") : HTML, CSS, JavaScript, PHP, Dart, Python ;
    • une zone de texte <textarea> « Motivation » de 4 lignes.
  • Q2.5. Pourquoi utilise-t-on .form-select et non .form-control sur un <select> en Bootstrap 5 ? Que se passe-t-il visuellement si vous utilisez .form-control?
  • Q2.6. Ajouter un champ « Ville » avec une liste de suggestions <datalist> (Tunis, Nabeul, Sousse, Sfax, Bizerte, Monastir). Quelle est la différence entre un <datalist> et un <select>?
  • Q2.7. Ajouter un champ « Couleur préférée pour votre badge » de type color. Quelles deux classes faut-il lui donner ?
  • Q2.8. Ajouter un champ « CV (PDF) » de type file avec la classe .form-control et l'attribut accept=".pdf". Que change l'attribut accept dans la fenêtre de choix de fichier ?
  • Q2.9. Ajouter un curseur « Niveau en informatique » (type="range", classe .form-range, de 0 à 10, pas de 1, valeur initiale 5). À côté de l'étiquette, afficher la valeur courante dans un <span id="niveauValeur" class="badge text-bg-primary">5</span>, mise à jour par le script suivant (à placer dans js/main.js) :
<div class="mb-3">
  <label for="niveau" class="form-label">
    Niveau en informatique :
    <span id="niveauValeur" class="badge text-bg-primary">5</span>
  </label>
  <input type="range" class="form-range" id="niveau"
         name="niveau" min="0" max="10" step="1" value="5">
</div>
// js/main.js – Q2.9 : afficher la valeur du curseur
const niveau = document.getElementById('niveau');
const niveauValeur = document.getElementById('niveauValeur');

if (niveau && niveauValeur) {   // le curseur n'existe que sur inscription.html
  niveau.addEventListener('input', () => {
    niveauValeur.textContent = niveau.value;
  });
}
  • Q2.10. Pourquoi le script teste-t-il if (niveau && niveauValeur) avant d'ajouter l'écouteur ? (Indice : main.js est chargé sur les cinq pages.)
  • Q2.11 (approfondissement). Ajouter les classes .form-control-lg sur le champ e-mail et .form-select-sm sur la liste « Domaine ». Comparer, puis retirer ces classes.

Résultat attendu de la partie 02 : formulaire d'inscription avec tous les types de contrôles

Résultat attendu à la fin de la partie 02 (extrait).

Question de réflexion :
Bootstrap ne crée pas de nouveaux contrôles : il met en forme les contrôles HTML5 existants. Citez trois avantages d'utiliser les types HTML5 natifs plutôt que des champs type="text" partout.
Git – fin de la partie 02 : enregistrer et publier votre travail, puis vérifier le nouveau commit sur GitHub.
git add .
git commit -m "Partie 02 : contrôles de formulaire de la page Inscription"
git push
  • Partie 03Cases à cocher, boutons radio, interrupteurs et boutons bascules
  • Objectif : permettre au candidat de faire des choix simples ou multiples, avec les différentes présentations proposées par Bootstrap 5.3.
  • Q3.1. Dans inscription.html, ajouter une troisième section « Préférences » (icône bi-sliders). Y placer un groupe de cases à cocher « Centres d'intérêt » : Sites web, Applications mobiles, Jeux vidéo, Intelligence artificielle, Cybersécurité. Respecter la structure :
<p class="form-label">Centres d'intérêt</p>
<div class="form-check">
  <input class="form-check-input" type="checkbox"
         id="interetWeb" name="interets" value="web">
  <label class="form-check-label" for="interetWeb">Sites web</label>
</div>
<!-- … les 4 autres cases -->
  • .form-check: le conteneur ;
  • .form-check-input: la case ;
  • .form-check-label: son étiquette ;
  • chaque id est unique; toutes les cases du groupe partagent le même name.
  • Q3.2. Cocher par défaut « Sites web » (checked) et désactiver « Jeux vidéo » (disabled) avec la mention « (bientôt disponible) ». Pourquoi l'étiquette de la case désactivée apparaît-elle aussi grisée ?
  • Q3.3. Ajouter un groupe de boutons radio « Niveau d'études » : Baccalauréat, BTP, BTS, Licence, Autre. Les afficher sur une seule ligne avec .form-check-inline. « Baccalauréat » est sélectionné par défaut.
  • Q3.4. Donner volontairement un name différent à l'un des boutons radio, puis tester. Que constatez-vous ? Corriger et expliquer dans reponses.md le rôle de l'attribut name pour les boutons radio.
  • Q3.5. Ajouter deux interrupteurs (.form-check.form-switch, avec role="switch") : « Recevoir la newsletter » (activé) et « Recevoir les SMS de rappel » (désactivé). Un interrupteur est-il une case à cocher ou un bouton radio ? Justifier à partir du code.
  • Q3.6. Ajouter le choix « Séance préférée » sous forme de boutons radio bascules: Matin, Après-midi, Soir, Samedi. Utiliser un .btn-group, la classe .btn-check sur chaque <input type="radio"> et .btn.btn-outline-primary sur chaque <label>:
<p class="form-label">Séance préférée</p>
<div class="btn-group mb-3" role="group" aria-label="Séance préférée">
  <input type="radio" class="btn-check" name="seance" id="seanceMatin" value="matin" autocomplete="off" checked>
  <label class="btn btn-outline-primary" for="seanceMatin"><i class="bi bi-sunrise"></i> Matin</label>

  <input type="radio" class="btn-check" name="seance" id="seanceAprem" value="apres-midi" autocomplete="off">
  <label class="btn btn-outline-primary" for="seanceAprem"><i class="bi bi-sun"></i> Après-midi</label>

  <!-- Q3.6 : compléter avec Soir (bi-moon) et Samedi (bi-calendar-week) -->
</div>
  • Q3.7. Ajouter un groupe de cases bascules « Modalités acceptées » : Présentiel, À distance, Hybride (type="checkbox" + .btn-check, labels en .btn-outline-success). Plusieurs choix doivent être possibles en même temps : vérifier.
  • Q3.8. Terminer la section par une case obligatoire « J'accepte le règlement intérieur du centre » (elle sera validée en partie 08).
  • Q3.9. Compléter le tableau suivant dans reponses.md:
Présentation Type d'<input> Classe(s) Bootstrap Nombre de choix possibles Exemple d'usage
Case à cocher … … … …
Bouton radio … … … …
Interrupteur … … … …
Bouton bascule … … … …

Résultat attendu de la partie 03 : cases à cocher, radios en ligne, interrupteurs et boutons bascules

Résultat attendu à la fin de la partie 03 : section « Préférences ».

Git – fin de la partie 03 : enregistrer et publier votre travail, puis vérifier le nouveau commit sur GitHub.
git add .
git commit -m "Partie 03 : cases, radios, interrupteurs et boutons bascules"
git push
  • Partie 04Le formulaire de base (vertical)
  • Objectif : finaliser la mise en forme du formulaire d'inscription : textes d'aide, disposition en colonnes, regroupements, champs en lecture seule et observation des données envoyées.
  • Q4.1. Sous le champ « Mot de passe », ajouter un texte d'aide .form-text: « 8 à 20 caractères, avec au moins un chiffre. » Le relier au champ avec aria-describedby. À quoi sert cet attribut pour un utilisateur malvoyant ?
  • Q4.2. Ajouter de la même façon un texte d'aide sous l'e-mail (« Nous ne partagerons jamais votre adresse. ») et sous le CV (« Format PDF, 2 Mo maximum. »).
  • Q4.3. Disposer les champs sur deux colonnes sur les écrans moyens et grands, et sur une colonne sur mobile :
    • la section « Informations personnelles » devient un <div class="row g-3">;
    • chaque champ est placé dans un <div class="col-md-6"> (Nom / Prénom, E-mail / Téléphone, etc.) ;
    • la classe .mb-3 devient inutile à l'intérieur de la ligne : pourquoi ? (Indice : .g-3. )
<div class="row g-3 mb-4">
  <div class="col-md-6">
    <label for="nom" class="form-label">Nom</label>
    <input type="text" class="form-control" id="nom" name="nom" placeholder="Votre nom">
  </div>
  <div class="col-md-6">
    <label for="prenom" class="form-label">Prénom</label>
    <input type="text" class="form-control" id="prenom" name="prenom" placeholder="Votre prénom">
  </div>
  <div class="col-md-6">
    <label for="mdp" class="form-label">Mot de passe</label>
    <input type="password" class="form-control" id="mdp" name="mdp" aria-describedby="mdpAide">
    <div id="mdpAide" class="form-text">8 à 20 caractères, avec au moins un chiffre.</div>
  </div>
  <!-- … -->
  <div class="col-12">
    <!-- un champ sur toute la largeur, ex. Motivation -->
  </div>
</div>
  • Q4.4. Placer chacune des trois sections dans une carte (.card > .card-header + .card-body) séparées par .mb-4. Le titre <h2> de chaque section passe dans le .card-header.
  • Q4.5. Ajouter en fin de formulaire une quatrième carte « Réservé à l'administration » contenant un <fieldset disabled> avec une <legend class="fs-6"> et deux champs : « Numéro de dossier » et « Décision » (liste : En attente, Accepté, Refusé). Que fait l'attribut disabled placé sur le <fieldset>?
  • Q4.6. Dans la carte « Informations personnelles », ajouter en tête un champ « Session » avec la valeur Session d'octobre 2026:
    • d'abord avec l'attribut readonly;
    • puis avec la classe .form-control-plaintext (à la place de .form-control) et readonly. Quelle différence visuelle observez-vous ? Garder la seconde version.
  • Q4.7. En bas du formulaire, placer deux boutons : « Envoyer ma candidature » (type="submit", .btn-primary.btn-lg) et « Effacer » (type="reset", .btn-outline-secondary.btn-lg), séparés par .me-2. Sur mobile, ils doivent occuper toute la largeur : utiliser <div class="d-grid gap-2 d-md-block">.
  • Observer les données envoyées
  • Q4.8. Le formulaire utilise method="get": remplir quelques champs, cliquer sur « Envoyer » et observer la barre d'adresse. Copier l'URL obtenue dans reponses.md.
  • Q4.9. À partir de cette URL, répondre :
    • un champ sans attribut name est-il envoyé ? (Supprimer temporairement le name du prénom pour vérifier.)
    • le champ « Session » (readonly) est-il envoyé ? Et les champs du <fieldset disabled>?
    • comment apparaissent plusieurs cases « Centres d'intérêt » cochées ? Une case non cochée est-elle envoyée ?
    • pourquoi ne faut-il jamais utiliser method="get" pour un formulaire qui contient un mot de passe ?
  • Q4.10. Remplacer method="get" par method="post". Le formulaire est-il toujours visible dans l'URL ? Où peut-on voir les données envoyées dans les outils de développement (onglet Réseau / Network) ?

Résultat attendu de la partie 04 : formulaire d'inscription sur deux colonnes, en cartes

Résultat attendu à la fin de la partie 04 : version grand écran (à gauche) et mobile (à droite).

Question de réflexion :
Quelle est la différence entre readonly et disabled du point de vue de l'utilisateur et du point de vue des données envoyées ? Donnez un exemple d'usage pour chacun.
Git – fin de la partie 04 : enregistrer et publier votre travail, puis vérifier le nouveau commit sur GitHub.
git add .
git commit -m "Partie 04 : formulaire d'inscription vertical, cartes et fieldset"
git push
  • Partie 05Les formulaires en ligne
  • Objectif : créer sur la page d'accueil trois petits formulaires dont tous les éléments sont alignés sur une ligne, sans la classe .form-inline (supprimée en Bootstrap 5).
  • Formulaire 1 : recherche dans la barre de navigation
  • Q5.1. Dans la navbar de toutes les pages, après la liste des liens, ajouter un formulaire de recherche :
<form class="d-flex" role="search" action="index.html" method="get">
  <input class="form-control me-2" type="search" name="q"
         placeholder="Rechercher une formation" aria-label="Rechercher une formation">
  <button class="btn btn-outline-light" type="submit"><i class="bi bi-search"></i></button>
</form>
  • Q5.2. Ce formulaire n'a pas de <label>: qu'est-ce qui le remplace pour l'accessibilité ? Le bouton ne contient qu'une icône : ajouter un texte <span class="visually-hidden">Rechercher</span> dans le bouton. Pourquoi ?
  • Q5.3. Pour que la recherche soit poussée à droite du menu, ajouter .me-auto à la liste .navbar-nav. Expliquer le fonctionnement de .me-auto dans un conteneur flex.
  • Formulaire 2 : barre de filtres des formations
  • Q5.4. Dans index.html, sous le titre, ajouter une section « Nos formations » avec un formulaire de filtres utilisant la grille (méthode recommandée par Bootstrap) :
    • <form class="row row-cols-lg-auto g-3 align-items-center mb-4">;
    • chaque élément dans un <div class="col-12">;
    • une liste « Domaine », une liste « Niveau » (Débutant, Intermédiaire, Avancé), une case à cocher « Certifiante uniquement » et un bouton « Filtrer » ;
    • les étiquettes des listes sont masquées avec .visually-hidden.
  • Q5.5. Passer en mode mobile (Ctrl+Maj+M) : comment se comporte la barre de filtres ? Remplacer .row-cols-lg-auto par .row-cols-md-auto: à partir de quelle largeur les éléments passent-ils maintenant en ligne ?
  • Q5.6. Sous la barre de filtres, afficher trois formations sous forme de cartes (.row.row-cols-1.row-cols-md-3.g-4) : titre, durée, niveau (.badge) et un bouton « S'inscrire » qui mène vers inscription.html. Ajouter une image en haut de chaque carte (.card-img-top, images de votre choix dans images/).
  • Formulaire 3 : inscription à la newsletter
  • Q5.7. Avant le pied de page, ajouter un bandeau .bg-primary-subtle.rounded.p-4 contenant un formulaire en ligne basé sur .col-auto:
<form class="row g-2 align-items-center justify-content-center">
  <div class="col-auto">
    <label for="newsletterEmail" class="col-form-label fw-semibold">Restez informé :</label>
  </div>
  <div class="col-12 col-sm">
    <input type="email" class="form-control" id="newsletterEmail" name="email" placeholder="Votre e-mail">
  </div>
  <div class="col-auto">
    <!-- Q5.7 : bouton « S'abonner » -->
  </div>
</form>
  • Q5.8. Expliquer la différence entre .col-auto, .col et .col-12 dans cet exemple.
  • Q5.9. Réécrire la barre de filtres de la Q5.4 avec les utilitaires flex (.d-flex.flex-wrap.align-items-center.gap-2) au lieu de la grille. Les listes doivent avoir .w-auto. Garder la version de votre choix et justifier ce choix dans reponses.md.
  • Q5.10. Compléter dans reponses.md: « En Bootstrap 4, on écrivait <form class="form-inline"> et .visually-hidden. En Bootstrap 5.3, on écrit … »

Résultat attendu de la partie 05 : page d'accueil avec recherche, filtres en ligne, cartes et newsletter

Résultat attendu à la fin de la partie 05 : page d'accueil.

Git – fin de la partie 05 : enregistrer et publier votre travail, puis vérifier le nouveau commit sur GitHub.
git add .
git commit -m "Partie 05 : formulaires en ligne – recherche, filtres et newsletter"
git push
  • Partie 06Le formulaire horizontal
  • Objectif : construire la page « Mon profil » avec une disposition horizontale : étiquette à gauche, champ à droite, une ligne par champ.
  • Q6.1. Dans profil.html, créer une mise en page en deux colonnes (.row) : à gauche (.col-lg-3) une carte avec la photo images/avatar.png (classes .img-thumbnail.rounded-circle), le nom du candidat et un champ fichier « Changer de photo » (accept="image/*", .form-control-sm) ; à droite (.col-lg-9) le formulaire horizontal.
  • Q6.2. Créer les lignes « Nom », « Prénom » et « E-mail » selon la structure horizontale. Vérifier que la somme des colonnes vaut 12 sur chaque ligne :
<form id="formProfil" action="#" method="post">
  <div class="row mb-3">
    <label for="profilNom" class="col-sm-3 col-form-label">Nom</label>
    <div class="col-sm-9">
      <input type="text" class="form-control" id="profilNom" name="nom" value="Ben Salah">
    </div>
  </div>
  <!-- Q6.2 : Prénom, E-mail -->
</form>
  • Q6.3. Ajouter une ligne « Identifiant » affichée en texte simple (.form-control-plaintext + readonly, valeur FP-2026-0142).
  • Q6.4. Ajouter une ligne « Genre » sous forme de boutons radio en ligne, avec la structure <fieldset class="row mb-3"> + <legend class="col-form-label col-sm-3 pt-0">. Retirer puis remettre .pt-0: quel est son rôle ?
  • Q6.5. Ajouter une ligne « Adresse » (toute la largeur), puis une ligne qui contient deux champs: « Code postal » (.col-sm-3) et « Ville » (avec sa propre étiquette .col-sm-2 et un champ .col-sm-4). Le total de la ligne fait-il 12 ? Corriger si nécessaire.
  • Q6.6. Ajouter une ligne « Biographie » avec un <textarea> de 3 lignes et un texte d'aide .form-text (« 200 caractères maximum », attribut maxlength="200").
  • Q6.7. Ajouter une ligne sans étiquette contenant l'interrupteur « Profil visible par les formateurs ». Utiliser .offset-sm-3 pour l'aligner avec les champs.
  • Q6.8. Ajouter une section « Paramètres d'affichage » utilisant les étiquettes de taille différente :
    • une ligne « Langue » en petit format : .col-form-label-sm + .form-select-sm;
    • une ligne « Pseudo » en grand format : .col-form-label-lg + .form-control-lg.
    • Que se passe-t-il si on oublie .col-form-label-lg sur l'étiquette du grand champ ?
  • Q6.9. Terminer par les boutons « Enregistrer » et « Annuler » alignés avec les champs (.offset-sm-3).
  • Q6.10. Rendre la disposition plus fine selon la taille de l'écran : les étiquettes occupent .col-md-4.col-lg-3 et les champs .col-md-8.col-lg-9. Tester les trois tailles (mobile, tablette, ordinateur) et décrire le comportement dans reponses.md.

Résultat attendu de la partie 06 : page Mon profil avec photo et formulaire horizontal

Résultat attendu à la fin de la partie 06 : page « Mon profil ».

Question de réflexion :
Le code d'origine de nombreux tutoriels utilise .form-horizontal, .form-group, .control-label et .col-sm-offset-2. À quelle version de Bootstrap appartiennent ces classes ? Par quoi les remplace-t-on en Bootstrap 5.3 ?
Git – fin de la partie 06 : enregistrer et publier votre travail, puis vérifier le nouveau commit sur GitHub.
git add .
git commit -m "Partie 06 : page Mon profil en formulaire horizontal"
git push
  • Partie 07Groupes de saisie et étiquettes flottantes
  • Objectif : créer une page de connexion moderne avec les étiquettes flottantes, et enrichir des champs avec des icônes, du texte et des boutons accolés.
  • Étiquettes flottantes
  • Q7.1. Dans connexion.html, centrer une carte de connexion : <div class="row justify-content-center"> > <div class="col-sm-10 col-md-7 col-lg-5"> > .card.shadow-sm. En haut de la carte, afficher le logo et le titre « Connexion ».
  • Q7.2. Dans la carte, créer les champs « Adresse e-mail » et « Mot de passe » avec des étiquettes flottantes:
<div class="form-floating mb-3">
  <input type="email" class="form-control" id="connexionEmail"
         name="email" placeholder="nom@exemple.com">
  <label for="connexionEmail">Adresse e-mail</label>
</div>
  • l'<input> est placé avant le <label>;
  • l'attribut placeholder est obligatoire, même s'il n'est pas affiché ;
  • le <label> n'a pas de classe.
  • Q7.3. Supprimer le placeholder du champ e-mail et observer le comportement de l'étiquette. Expliquer pourquoi Bootstrap en a besoin (indice : pseudo-classe CSS :placeholder-shown). Remettre le placeholder.
  • Q7.4. Ajouter sous les champs : une case « Se souvenir de moi », un bouton « Se connecter » pleine largeur (.w-100.btn-lg) et un lien « Pas encore de compte ? S'inscrire » vers inscription.html.
  • Q7.5. Dans contact.html, créer un premier jet du formulaire de contact entièrement en étiquettes flottantes : « Nom complet », « E-mail », « Objet » (<select class="form-select">: Renseignements, Inscription, Réclamation, Autre) et « Message » (<textarea> d'une hauteur de 150 px fixée par style="height: 150px"). Pourquoi l'attribut rows ne suffit-il pas pour un <textarea> flottant ?
  • Q7.6. Placer « Nom complet » et « E-mail » côte à côte (.row.g-2 + .col-md), chaque colonne contenant son propre .form-floating.
  • Groupes de saisie
  • Q7.7. Dans connexion.html, remplacer le champ mot de passe par un groupe de saisie qui combine une icône, l'étiquette flottante et un bouton « afficher / masquer » :
<div class="input-group mb-3">
  <span class="input-group-text"><i class="bi bi-lock"></i></span>
  <div class="form-floating">
    <input type="password" class="form-control" id="connexionMdp"
           name="mdp" placeholder="Mot de passe">
    <label for="connexionMdp">Mot de passe</label>
  </div>
  <button class="btn btn-outline-secondary" type="button"
          id="btnVoirMdp" aria-label="Afficher le mot de passe">
    <i class="bi bi-eye"></i>
  </button>
</div>
// js/main.js – Q7.8
const btnVoirMdp = document.getElementById('btnVoirMdp');
const champMdp = document.getElementById('connexionMdp');

if (btnVoirMdp && champMdp) {
  btnVoirMdp.addEventListener('click', () => {
    const masque = champMdp.type === 'password';
    champMdp.type = masque ? 'text' : 'password';
    btnVoirMdp.innerHTML = masque
      ? '<i class="bi bi-eye-slash"></i>'
      : '<i class="bi bi-eye"></i>';
    // Q7.8 : mettre aussi à jour aria-label
  });
}
  • Q7.8. Ajouter le script ci-dessus dans js/main.js et le compléter pour que l'attribut aria-label du bouton devienne « Masquer le mot de passe » quand le mot de passe est visible. Pourquoi le bouton a-t-il type="button" et non type="submit"?
  • Q7.9. Faire de même pour l'e-mail : icône bi-envelope dans un .input-group-text à gauche de l'étiquette flottante.
  • Q7.10. Dans inscription.html, transformer trois champs en groupes de saisie :
    • « Téléphone » : texte +216 à gauche ;
    • « Site web » : texte https:// à gauche ;
    • « Budget de formation » (nouveau champ number) : texte DT à droite et .00 en second suffixe.
  • Q7.11. Sur la page d'accueil, refaire la newsletter de la Q5.7 avec un groupe de saisie (champ e-mail et bouton « S'abonner » accolés), en taille grande (.input-group-lg). Quelle version préférez-vous ? Garder la meilleure.
  • Q7.12 (approfondissement). Ajouter à la barre de recherche de la navbar un menu déroulant (.dropdown-toggle dans le .input-group) permettant de choisir où chercher : « Formations », « Formateurs », « Actualités ». Quel fichier JavaScript de Bootstrap est nécessaire pour que le menu s'ouvre ?

Résultat attendu de la partie 07 : page de connexion avec étiquettes flottantes et groupe de saisie

Résultat attendu à la fin de la partie 07 : page de connexion.

Git – fin de la partie 07 : enregistrer et publier votre travail, puis vérifier le nouveau commit sur GitHub.
git add .
git commit -m "Partie 07 : étiquettes flottantes et groupes de saisie"
git push
  • Partie 08La validation des formulaires
  • Objectif : empêcher l'envoi de formulaires incomplets ou incorrects et guider l'utilisateur avec des messages clairs, côté navigateur, puis simuler l'affichage d'erreurs renvoyées par un serveur.
  • Déclarer les règles en HTML5
  • Q8.1. Dans contact.html, ajouter les règles suivantes au formulaire de contact de la partie 07, puis essayer de l'envoyer vide. Que fait le navigateur sans Bootstrap ?
Champ Règle(s) à ajouter Message d'erreur à afficher (Q8.3)
Nom complet required, minlength="3" Veuillez saisir votre nom (3 caractères minimum).
E-mail required (le type email vérifie déjà le format) Veuillez saisir une adresse e-mail valide.
Objet required + première option value="" Veuillez choisir un objet.
Message required, minlength="20", maxlength="500" Votre message doit contenir entre 20 et 500 caractères.
Case « J'accepte que mes données soient utilisées pour me répondre » required Vous devez accepter avant d'envoyer.
  • Afficher les messages Bootstrap
  • Q8.2. Ajouter au <form> l'attribut novalidate et la classe .needs-validation. Essayer à nouveau d'envoyer le formulaire vide : que se passe-t-il maintenant ? Pourquoi ?
  • Q8.3. Sous chaque champ, ajouter un <div class="invalid-feedback"> avec le message du tableau. Sous « Nom complet » et « E-mail », ajouter aussi un <div class="valid-feedback">Parfait !</div>. Pour la case à cocher, le message se place dans le .form-check mais après le <label>.
  • Q8.4. Créer le fichier js/validation.js avec le script officiel de Bootstrap, l'inclure dans contact.html, inscription.html et connexion.html (après bootstrap.bundle.min.js) :
// js/validation.js – validation Bootstrap 5.3 de tous les formulaires .needs-validation
(() => {
  'use strict';

  const forms = document.querySelectorAll('.needs-validation');

  Array.from(forms).forEach(form => {
    form.addEventListener('submit', event => {
      if (!form.checkValidity()) {
        event.preventDefault();      // bloque l'envoi
        event.stopPropagation();
      }
      form.classList.add('was-validated');   // affiche les couleurs et les messages
    }, false);
  });
})();
  • Q8.5. Tester : envoyer le formulaire vide, puis corriger les champs un par un. Les couleurs changent-elles pendant la saisie ? Expliquer le rôle de checkValidity(), de event.preventDefault() et de la classe .was-validated.
  • Q8.6. Retirer le script et placer directement class="was-validated" sur le <form>. Recharger la page : quel problème d'ergonomie apparaît ? Remettre le script.
  • Q8.7. Pour « Objet », remplacer .invalid-feedback par une info-bulle .invalid-tooltip. Que faut-il ajouter au parent pour qu'elle soit bien placée ? (Avec les étiquettes flottantes, le .form-floating est déjà positionné : vérifier.)
  • Valider le formulaire d'inscription
  • Q8.8. Dans inscription.html, ajouter novalidate et .needs-validation au formulaire, puis les règles et messages suivants :
    • Nom, Prénom, E-mail, Domaine, case « Règlement intérieur » : required;
    • Mot de passe : required, minlength="8", maxlength="20", pattern="(?=.*[0-9]).{8,20}" (au moins un chiffre) ;
    • Téléphone : pattern="[0-9]{8}" (8 chiffres) ;
    • Âge : min="16" et max="60" (déjà présents) ;
    • CV : required.
  • Q8.9. Le téléphone et le site web sont dans des groupes de saisie (Q7.10) : le message d'erreur s'affiche-t-il correctement ? Ajouter la classe .has-validation au .input-group et comparer les coins arrondis.
  • Q8.10. Ajouter un champ « Confirmer le mot de passe » (id="mdp2"). HTML5 ne sait pas comparer deux champs : le faire en JavaScript avec setCustomValidity(), puis afficher le message « Les deux mots de passe ne correspondent pas. » :
// js/validation.js – Q8.10 : confirmation du mot de passe
const mdp = document.getElementById('mdp');
const mdp2 = document.getElementById('mdp2');

function verifierConfirmation() {
  if (mdp2.value !== mdp.value) {
    mdp2.setCustomValidity('Les mots de passe ne correspondent pas.');
  } else {
    mdp2.setCustomValidity('');   // chaîne vide = champ valide
  }
}

if (mdp && mdp2) {
  mdp.addEventListener('input', verifierConfirmation);
  mdp2.addEventListener('input', verifierConfirmation);
}
  • tant que setCustomValidity() reçoit un texte non vide, le champ est considéré comme :invalid;
  • pourquoi faut-il surveiller les deux champs et pas seulement mdp2?
  • ajouter le .invalid-feedback correspondant dans le HTML.
  • Q8.11. Le bouton « Effacer » (type="reset") vide les champs mais les couleurs restent affichées. Ajouter dans validation.js un écouteur sur l'événement reset du formulaire qui retire la classe .was-validated.
  • Simuler un envoi réussi
  • Q8.12. Pour le formulaire de contact, quand le formulaire est valide, bloquer quand même l'envoi (pas de serveur dans ce TP), afficher une alerte Bootstrap de succès au-dessus du formulaire, vider le formulaire et retirer .was-validated:
<!-- contact.html, au-dessus du formulaire -->
<div id="alerteSucces" class="alert alert-success d-none" role="alert">
  <i class="bi bi-check-circle"></i>
  Merci ! Votre message a bien été envoyé.
</div>
// À compléter dans l'écouteur submit, si le formulaire est valide :
// 1. event.preventDefault();
// 2. afficher #alerteSucces (retirer la classe d-none) ;
// 3. form.reset();
// 4. form.classList.remove('was-validated');
// 5. masquer l'alerte au bout de 4 secondes (setTimeout).
  • Afficher une validation côté serveur
  • Q8.13. Dans profil.html, on imagine que le serveur a vérifié le formulaire et renvoie le résultat. Simuler cette réponse en HTML, sans JavaScript :
    • le champ E-mail porte .is-invalid avec le message « Cette adresse est déjà utilisée par un autre compte. » ;
    • les champs Nom et Prénom portent .is-valid;
    • chaque message est relié à son champ par aria-describedby;
    • en haut du formulaire, une alerte .alert-danger résume l'erreur.
  • Q8.14. Pourquoi les classes .is-valid / .is-invalid n'ont-elles pas besoin d'un parent .was-validated? Dans quel langage (PHP, Symfony…) seraient-elles ajoutées dans un vrai projet ?

Résultat attendu de la partie 08 : formulaire de contact avec champs valides en vert et invalides en rouge

Résultat attendu à la fin de la partie 08 : formulaire de contact après une tentative d'envoi.

Questions de réflexion :
1) Un utilisateur peut désactiver JavaScript ou modifier le HTML avec F12 (par exemple supprimer l'attribut required). Que pouvez-vous en conclure sur la validation côté navigateur ?
2) Pourquoi la première option d'un <select required> doit-elle avoir value=""?
3) Donnez deux règles d'un bon message d'erreur.
Git – fin de la partie 08 : enregistrer et publier votre travail, puis vérifier le nouveau commit sur GitHub.
git add .
git commit -m "Partie 08 : validation des formulaires Contact, Inscription et Profil"
git push
  • Partie 09Intégration finale, GitHub Pages et version 1.0
  • Objectif : harmoniser le site, ajouter le mode sombre de Bootstrap 5.3, le mettre en ligne et livrer une version finale propre.
  • Mode sombre
  • Q9.1. Bootstrap 5.3 gère nativement un thème sombre avec l'attribut data-bs-theme. Dans la navbar de toutes les pages, ajouter un bouton id="btnTheme" (icône bi-moon-stars), puis compléter js/main.js:
// js/main.js – Q9.1 : bascule clair / sombre mémorisée
const racine = document.documentElement;          // la balise <html>
const btnTheme = document.getElementById('btnTheme');

let themeEnregistre = null;
try { themeEnregistre = localStorage.getItem('theme'); } catch (e) {}
if (themeEnregistre) {
  racine.setAttribute('data-bs-theme', themeEnregistre);
}

if (btnTheme) {
  btnTheme.addEventListener('click', () => {
    const nouveau = racine.getAttribute('data-bs-theme') === 'dark' ? 'light' : 'dark';
    racine.setAttribute('data-bs-theme', nouveau);
    try { localStorage.setItem('theme', nouveau); } catch (e) {}
    // Q9.2 : changer l'icône (bi-moon-stars / bi-sun)
  });
}
  • Q9.2. Compléter le script pour que l'icône du bouton devienne bi-sun en mode sombre. Changer de page : le thème est-il conservé ? Quel est le rôle de localStorage?
  • Q9.3. Passer en mode sombre et parcourir les cinq pages. Les éléments qui utilisent des couleurs fixes (.bg-light, .text-dark) restent-ils lisibles ? Les remplacer par les classes adaptatives de Bootstrap 5.3 : .bg-body-tertiary, .text-body, .bg-primary-subtle…
  • Vérifications finales
  • Q9.4. Pour chaque page, vérifier avec la liste de contrôle suivante et corriger si nécessaire :
    • le lien de la page courante est .active dans la navbar;
    • chaque champ a une étiquette (visible, .visually-hidden ou aria-label) reliée par for / id;
    • aucun id n'est utilisé deux fois sur une même page ;
    • chaque image a un attribut alt descriptif ;
    • la console du navigateur ne contient aucune erreur.
  • Q9.5. Valider le code HTML de chaque page avec le validateur du W3C (validator.w3.org, onglet Validate by Direct Input). Noter dans reponses.md le nombre d'erreurs trouvées et corrigées.
  • Q9.6. Lancer un audit Lighthouse (F12 → onglet Lighthouse → catégorie Accessibility) sur inscription.html. Noter le score et corriger au moins deux problèmes signalés.
  • Q9.7. Faire une capture d'écran de chaque page (version ordinateur) et des pages Inscription et Contact en version mobile, puis du formulaire de contact en mode sombre. Les enregistrer au format PNG dans captures/: accueil.png, inscription.png, inscription-mobile.png, connexion.png, profil.png, contact.png, contact-mobile.png, contact-sombre.png.
  • Mettre le site en ligne avec GitHub Pages
  • Q9.8. Faire un commit et un push de toutes les modifications. Sur GitHub, ouvrir le dépôt, puis Settings → Pages: dans Build and deployment, choisir Source : Deploy from a branch, Branch : main, dossier / (root), puis Save.
  • Q9.9. Après une à deux minutes, le site est disponible à l'adresse https://VOTRE-COMPTE.github.io/formaplus/. Ouvrir cette adresse sur votre téléphone et tester les cinq pages. Pourquoi la page d'accueil doit-elle obligatoirement s'appeler index.html?
  • Finaliser le dépôt GitHub
  • Q9.10. Créer à la racine du projet un fichier README.md qui présente le site (modèle ci-dessous) : description, lien vers le site en ligne, pages et notions, captures d'écran et auteur.
  • Q9.11. Vérifier que reponses.md est complet et présent à la racine du dépôt.
  • Q9.12. Faire le dernier commit, puis marquer la version finale avec une étiquette v1.0 et l'envoyer sur GitHub (commandes ci-dessous). Vérifier sur GitHub que le README s'affiche avec les captures et que l'étiquette apparaît dans Tags.
# FormaPlus

Site web d'un centre de formation réalisé dans le cadre
du TP « Les formulaires Bootstrap 5.3 ».

**Site en ligne :** https://VOTRE-COMPTE.github.io/formaplus/

## Pages et notions
| Page | Type de formulaire |
|------|--------------------|
| Accueil | Formulaires en ligne (recherche, filtres, newsletter) |
| Inscription | Formulaire de base, tous les contrôles, validation |
| Connexion | Étiquettes flottantes, groupes de saisie |
| Mon profil | Formulaire horizontal, validation serveur simulée |
| Contact | Validation complète avec JavaScript |

## Technologies
- HTML5, CSS3, JavaScript
- Bootstrap 5.3 et Bootstrap Icons

## Captures d'écran
![Accueil](captures/accueil.png)
![Inscription](captures/inscription.png)
![Contact en mode sombre](captures/contact-sombre.png)

## Auteur
Prénom Nom – Groupe
git add .
git commit -m "Partie 09 : mode sombre, README, captures et mise en ligne"
git push

git tag v1.0
git push origin v1.0

Résultat attendu de la partie 09 : site FormaPlus en mode sombre publié sur GitHub Pages

Résultat attendu à la fin de la partie 09 : le site publié sur GitHub Pages, en mode sombre.

  1. Exercices complémentaires (bonus)

  • Indicateur de force du mot de passe : sous le mot de passe de l'inscription, afficher une barre de progression Bootstrap (.progress) qui passe du rouge au vert selon la longueur et la présence de chiffres, majuscules et caractères spéciaux.
  • Compteur de caractères : sous le message du formulaire de contact, afficher « 120 / 500 caractères » mis à jour en temps réel.
  • Formulaire en plusieurs étapes : découper l'inscription en trois étapes avec les onglets Bootstrap (.nav-pills) et des boutons « Suivant » / « Précédent », en validant chaque étape avant de passer à la suivante.
  • Récapitulatif dans une fenêtre modale : avant l'envoi de l'inscription, afficher les données saisies dans une .modal Bootstrap avec les boutons « Modifier » et « Confirmer ».
  • Intégration continue : ajouter un fichier .github/workflows/html.yml qui vérifie le HTML à chaque git push (GitHub Actions, par exemple avec html-validate), et afficher le badge de statut dans le README.md.
  1. Compte rendu

  • À rendre :
    • le lien de votre dépôt GitHub formaplus, envoyé dès la partie 00 : c'est ce dépôt qui est corrigé, dans l'état de l'étiquette v1.0;
    • le lien du site en ligne sur GitHub Pages (https://VOTRE-COMPTE.github.io/formaplus/) ;
    • dans le dépôt, le fichier reponses.md: réponses aux questions Q0.9, Q1.2, Q1.3, Q1.5, Q2.3, Q2.5, Q2.6, Q2.10, Q3.2, Q3.4, Q3.5, Q4.1, Q4.3, Q4.5, Q4.8, Q4.9, Q4.10, Q5.2, Q5.3, Q5.5, Q5.8, Q5.9, Q5.10, Q6.4, Q6.8, Q6.10, Q7.3, Q7.5, Q7.8, Q7.11, Q8.1, Q8.2, Q8.5, Q8.6, Q8.14, Q9.2, Q9.5, Q9.6, Q9.9, aux questions de réflexion et les tableaux des Q2.2 et Q3.9 ;
    • dans le dépôt, le README.md et les captures d'écran du dossier captures/.
  • Barème indicatif (/20) :
Critère Points
Partie 00 – mise en place, arborescence, images et dépôt 1
Partie 01 – gabarit Bootstrap 5.3, navbar responsive, 5 pages reliées 1,5
Partie 02 – contrôles : types HTML5, .form-select, <datalist>, couleur, fichier, curseur 2
Partie 03 – cases, radios en ligne, interrupteurs, boutons bascules 2
Partie 04 – formulaire vertical : .form-text, grille, cartes, <fieldset>, readonly, analyse GET / POST 2
Partie 05 – formulaires en ligne : recherche, filtres, newsletter, .visually-hidden 2
Partie 06 – formulaire horizontal : .col-form-label, <legend>, .offset-*, responsive 2
Partie 07 – étiquettes flottantes, groupes de saisie, afficher / masquer le mot de passe 2
Partie 08 – validation : novalidate, messages, setCustomValidity(), envoi simulé, .is-invalid 2,5
Partie 09 – mode sombre, vérifications (W3C, Lighthouse), site publié sur GitHub Pages 1,5
Git / GitHub – dépôt créé en partie 00, un commit par partie avec un message clair, README.md, étiquette v1.0 1,5
Total 20
Bonus +2 max
Question de réflexion (à rédiger en 5 lignes) :
Vous avez utilisé quatre dispositions de formulaires (vertical, en ligne, horizontal, étiquettes flottantes). Pour chacune, donnez un exemple de situation où elle est la plus adaptée, et expliquez pourquoi un même site peut en combiner plusieurs.