Accueil/Informatique & Développement /Chapitre 10 /Exercices corrigés

Projet CV en ligne — exercices

Le projet refait pas à pas, puis relu comme le ferait un correcteur.

🎯

La méthode

Faites chaque exercice dans un vrai fichier, pas dans votre tête : ouvrez cv.html et style.css, écrivez, rechargez, regardez. Le CSS ne s'apprend pas en lisant — il s'apprend en voyant l'écart entre ce qu'on attendait et ce qui s'affiche.

01Lire l'énoncé

1Transformer le sujet en check-list Facile

Voici un extrait du cahier des charges :

énoncé
2. Section « À propos »
   • Photo de profil (image ou placeholder)
   • Texte de présentation (2–3 lignes)
   • Organisation Flexbox : photo et texte côte à côte

Écrivez la liste des points vérifiables un par un que le correcteur pourra cocher.

✅ Correction
  • ☐ une section existe et porte id="a-propos" (le menu doit pouvoir y pointer) ;
  • ☐ elle contient une balise <img> qui s'affiche réellement ;
  • ☐ elle contient un texte de présentation de 2 à 3 lignes — ni une ligne, ni dix ;
  • ☐ le CSS de cette section contient display: flex ;
  • ☐ à l'écran, la photo et le texte sont bien côte à côte, pas l'un sous l'autre.

Le dernier point est le seul qui compte vraiment : on peut écrire display: flex et obtenir une colonne — il suffit d'un flex-direction: column oublié plus bas. La check-list se vérifie à l'écran, pas dans le code.

2Ce qui manque Moyen

Un camarade vous montre son rendu : les cinq sections sont là, le CSS est joli, tout fonctionne. Il a pourtant perdu des points sur les livrables. Citez deux causes possibles, en relisant la fin de l'énoncé.

✅ Correction

1. Le CSS n'est pas externe. L'énoncé l'écrit noir sur blanc : « Le fichier HTML doit inclure le CSS externe avec la balise <link rel="stylesheet" href="style.css"> ». Du CSS dans un <style> donne le même rendu et ne remplit pas la consigne.

2. Les fichiers ne sont pas nommés comme demandé. L'énoncé exige cv.html et style.css. Un index.html ou un styles.css — avec un « s » — oblige le correcteur à chercher, et casse le <link> si l'un des deux a été renommé sans l'autre.

Une troisième cause, très fréquente : les images ne sont pas jointes. Les deux fichiers demandés sont les livrables de code ; le dossier asset/ reste indispensable pour que la page s'affiche.

02Le header et le menu

3Un menu horizontal en Flexbox Facile

Voici le HTML. Écrivez le CSS pour obtenir un menu horizontal, centré, à fond sombre, avec 30 px entre les liens, sans puces et sans soulignement.

html
<nav>
  <ul class="menu">
    <li><a href="#a-propos">À propos</a></li>
    <li><a href="#competences">Compétences</a></li>
    <li><a href="#experiences">Expériences</a></li>
    <li><a href="#contact">Contact</a></li>
  </ul>
</nav>
✅ Correction
css
.menu {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 30px;                    /* l'espacement demandé */
  height: 50px;
  margin: 0;                    /* <ul> a une marge par défaut */
  padding: 0;                   /* et un padding-left de 40px */
  background: rgba(0, 0, 0, .55);
}
.menu li { list-style: none; }  /* borné au menu, pas « li » tout seul */
.menu a {
  color: #fff;
  text-decoration: none;
  padding: 8px 12px;
}
.menu a:hover { background: rgba(255, 255, 255, .18); border-radius: 5px; }
⚠️ Les deux lignes qu'on oublie toujours

Un <ul> arrive avec margin et padding-left: 40px par défaut. Sans margin: 0; padding: 0, le menu paraît décentré vers la droite — et l'on passe dix minutes à chercher pourquoi justify-content: center « ne marche pas ». Il marche : il centre dans une boîte elle-même décalée.

Le padding se met sur le <a>, pas sur le <li> : c'est le lien qui doit être cliquable sur toute la surface survolée.

4Trois erreurs dans une bannière Moyen

Repérez trois défauts dans ce CSS de bannière, et corrigez-les.

css
header {
  background-image: url("asset/background.jpg");
  height: 700px;
}
.menu { margin-bottom: 350px; }
.presentation { justify-self: center; }
✅ Correction

1. background-image sans background-size. L'image s'affiche à sa taille réelle et se répète en mosaïque si le bloc est plus grand qu'elle.

2. height: 700px et margin-bottom: 350px. Une hauteur figée plus une marge réglée à l'œil pour la compenser. Sur un écran étroit, l'accroche déborde du header.

3. justify-self sur un élément qui n'est pas dans une grille. La propriété est purement ignorée : du code mort.

css — corrigé
header {
  min-height: 70vh;               /* une contrainte, pas un carcan */
  display: flex;
  flex-direction: column;
  justify-content: space-between; /* menu en haut, accroche en bas */

  background-image: url("asset/background.jpg");
  background-size: cover;
  background-position: center;
}
.menu { margin: 0; }
.presentation { margin-inline: auto; }   /* le vrai centrage d'un bloc */

Le test : réduisez la fenêtre à 375 px de large. Avec la version corrigée, la bannière s'adapte ; avec l'originale, l'accroche sort du cadre.

5Lien ou bouton ? Facile

Pour chacun de ces éléments, dites s'il faut un <a> ou un <button> :

  1. « Voir mes projets », qui fait défiler la page jusqu'à la section Projets
  2. « Envoyer », qui soumet le formulaire de contact
  3. « Télécharger mon CV en PDF »
  4. « ☰ », qui ouvre et ferme le menu sur téléphone
✅ Correction
ÉlémentBalisePourquoi
Voir mes projets<a href="#experiences"> on va quelque part, même dans la même page
Envoyer<button type="submit"> on déclenche la soumission
Télécharger le PDF<a href="cv.pdf" download> on va vers une ressource — un fichier est une adresse
☰ menu<button> on agit sur la page, on ne navigue pas
<a> = une destination  ·  <button> = une action

Et dans tous les cas : jamais l'un dans l'autre. Un <a> peut être habillé en bouton avec display: inline-block et du padding — l'apparence relève du CSS, la balise relève du sens.

03Photo et texte côte à côte

6La section « À propos » Facile

Écrivez le CSS demandé : photo de 150 px et texte alignés horizontalement, centrés verticalement l'un par rapport à l'autre, avec un espace confortable entre les deux. La photo doit rester ronde et non déformée.

✅ Correction
css
.content-a-propos {
  display: flex;
  align-items: center;    /* centrage vertical mutuel */
  gap: 28px;
}
.content-a-propos img {
  width: 150px;
  height: 150px;
  object-fit: cover;      /* recadre au lieu de déformer */
  border-radius: 50%;
  flex-shrink: 0;         /* la photo ne se comprime jamais */
}

flex-shrink: 0 est la subtilité : par défaut, un élément flex accepte de rétrécir si la place manque. Sans cette ligne, la photo passe de 150 px à 90 px sur un écran étroit — et devient ovale.

7Pourquoi la photo est-elle écrasée ? Moyen

css
.content-a-propos > div > img { height: 100%; width: 150px; }

La photo d'origine fait 800 × 600 px. Elle s'affiche écrasée. Expliquez, puis corrigez.

✅ Correction

Deux dimensions imposées à la fois. La photo est en 4:3 ; on lui impose 150 px de large et une hauteur calculée sur le parent. Sauf coïncidence arithmétique, le rapport ne tombe pas juste et l'image est étirée.

Et height: 100 % de quoi ? Un pourcentage de hauteur ne s'évalue que si le parent a lui-même une hauteur définie. Le <div> parent n'en avait pas : la déclaration retombe sur auto, et le résultat dépend du contexte — donc change d'un navigateur ou d'un écran à l'autre.

css — deux corrections possibles
/* A. une seule dimension : l'autre suit, le rapport est conservé */
.content-a-propos img { width: 150px; height: auto; }

/* B. un cadre imposé : on recadre au lieu de déformer */
.content-a-propos img { width: 150px; height: 150px; object-fit: cover; }

A quand on veut voir l'image entière ; B quand on veut un cadre régulier — des vignettes alignées, un avatar rond. Jamais deux dimensions fixes sans object-fit.

04La grille de compétences

8Quatre colonnes qui deviennent une Moyen

Le CSS rendu écrivait :

css
.competences { display: grid; grid-template-columns: repeat(4, 1fr); }

Réécrivez cette ligne pour que la grille passe d'elle-même à 3, 2 puis 1 colonne quand l'écran rétrécit — sans écrire de media query. Chaque carte doit faire au moins 220 px.

✅ Correction
css
.competences {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 20px;
}

Se lit : « autant de colonnes que tu peux, d'au moins 220 px, et partage le reste équitablement ».

MorceauRôle
repeat(…)répéter un motif de colonne
auto-fitle navigateur décide du nombre
minmax(220px, 1fr)jamais moins de 220 px, sinon une part égale du reste

C'est le motif le plus utile du CSS moderne. Apprenez-le par cœur : il résout à lui seul 80 % des grilles responsives.

9auto-fit ou auto-fill ? Difficile

Un conteneur de 1000 px contient 3 cartes, avec minmax(220px, 1fr) et gap: 20px. Décrivez le rendu obtenu avec auto-fit, puis avec auto-fill.

✅ Correction

Combien de colonnes tiennent ? Avec n colonnes de 220 px et (n−1) espaces de 20 px : 4 colonnes demandent 4 × 220 + 3 × 20 = 940 px, ce qui tient dans 1000. Une 5e demanderait 1180 px. Le navigateur crée donc 4 pistes.

Pistes videsRésultat à l'écran
auto-fillconservées 3 cartes à 235 px, serrées à gauche, un vide à droite
auto-fitrepliées (largeur 0) 3 cartes qui s'étalent à 320 px chacune

Autrement dit : auto-fill réserve la place des cartes futures, auto-fit donne toute la place aux cartes présentes.

Pour un CV — un nombre de compétences fixe et connu — auto-fit. auto-fill a du sens sur une galerie qui se remplit au fil du temps, où l'on veut que l'alignement reste stable.

10Le texte qui déborde de sa carte Moyen

css
.competences > .card {
  display: grid;
  grid-template-rows: 150px 150px 150px;   /* titre / image / texte */
}

Les cartes contiennent des descriptions de longueurs très différentes. Le texte le plus long déborde du cadre blanc. Corrigez sans perdre l'alignement des titres et des images entre cartes.

✅ Correction
css
.competences { align-items: stretch; }   /* c'est déjà le défaut : on l'assume */

.competences .card {
  display: grid;
  grid-template-rows: auto auto 1fr;     /* titre / image : leur taille
                                            texte : tout le reste */
}

auto = la rangée prend la hauteur de son contenu, sans le couper. 1fr = la dernière rangée absorbe l'espace restant, ce qui aligne les cartes sur la plus haute.

⚠️ Le piège caché : des cartes de structure différente

grid-template-rows distribue les enfants directs. Si une carte enveloppe son paragraphe dans un <div> de plus, elle n'a plus le même nombre d'enfants que les autres : son texte atterrit dans la rangée de l'image, et l'alignement saute pour cette carte seulement.

Des cartes alignées supposent des cartes de structure identique. Le HTML se relit avant le CSS.

05Le formulaire de contact

11Cinq erreurs dans un formulaire Difficile

Ce formulaire s'affiche parfaitement. Il contient pourtant cinq défauts. Trouvez-les.

html
<form action="POST">
  <fieldset>
    <legend>Contactez-moi :</legend>

    <label for="nom">Nom :</label>
    <input type="text" id="nom" name="nom" required />

    <label for="first-name">Email :</label>
    <input type="email" id="email" name="email" required />

    <label for="first-name">Téléphone :</label>
    <input type="number" id="number" name="number" required />

    <textarea name="commentaires" id="commentaires" rows="5"></textarea>

    <button type="submit">Envoyé</button>
  </fieldset>
</form>
✅ Correction
#ErreurConséquence réelle
1action="POST" action attend une URL. Le navigateur cherche un fichier « POST », et comme method manque il envoie en GET : les champs finissent dans la barre d'adresse.
2for="first-name", deux fois aucun champ ne porte cet id. Les deux labels ne désignent rien : cliquer sur « Email » ne fait rien, et un lecteur d'écran annonce un champ sans intitulé.
3type="number" pour un téléphone le 0 initial saute, les espaces sont refusés, la molette modifie le numéro par accident.
4<textarea> sans <label> un grand rectangle vide sans indication. Et required y manque alors qu'il est partout ailleurs : on peut envoyer un message vide.
5bouton « Envoyé » un bouton nomme l'action à venir, pas un état atteint : Envoyer.
html — corrigé
<form method="post" action="contact.php">
  <fieldset>
    <legend>Me contacter</legend>

    <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="telephone">Téléphone</label>
    <input type="tel" id="telephone" name="telephone">

    <label for="message">Votre message</label>
    <textarea id="message" name="message" rows="5" required></textarea>

    <button type="submit">Envoyer</button>
  </fieldset>
</form>

Le téléphone perd son required : sur un CV, l'email suffit à répondre. Un champ obligatoire qui ne l'est pas vraiment fait fuir des visiteurs.

Le contrôle en trois secondes, sans outil : cliquez sur chaque intitulé. Si le curseur ne saute pas dans le champ correspondant, le for est faux.

12Le bon type Facile

Quel type d'<input> pour chacun de ces champs ?

  1. un numéro de téléphone
  2. une adresse email
  3. un code postal
  4. un nombre d'années d'expérience
  5. une adresse de portfolio
✅ Correction
ChamptypePourquoi
Téléphonetel suite de chiffres, pas un nombre : les zéros et les espaces comptent
Emailemail validation intégrée + clavier avec @ sur mobile
Code postaltext + inputmode="numeric" « 06000 » perdrait son zéro en type="number"
Années d'expériencenumber là, c'est vraiment un nombre : on peut l'additionner
Portfoliourl vérifie la présence de https://
On peut l'additionner ?  →  number
Sinon  →  text, tel, email, url

06Passer au responsive

13Trouver ce qui déborde Moyen

À 375 px de large, la page défile horizontalement. Trois règles du projet en sont responsables. Lesquelles, et pourquoi ?

css
.competences            { grid-template-columns: repeat(4, 1fr); }
.card-image-projets > img { height: 405px; width: 650px; }
fieldset                { display: flex; flex-direction: column; padding: 60px; }
✅ Correction

1. Les images de projets — la plus visible. 650 px imposés dans un écran de 375 px : 275 px de débordement à elles seules.

2. La grille à 4 colonnes. Elle ne déborde pas forcément, mais les mots longs (« Développement ») ne tiennent pas dans 80 px et sortent de leur carte.

3. Le padding: 60px du fieldset. 120 px consommés à gauche et à droite ; sans box-sizing: border-box, la boîte dépasse son parent.

css — corrigé
*, *::before, *::after { box-sizing: border-box; }   /* en tête de fichier */

.competences { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }

.card-image-projets img {
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  display: block;
}

@media (max-width: 768px) {
  fieldset { padding: 20px; }
}
🧰 Trouver le coupable en une ligne

Dans la console du navigateur (F12) :

js
document.querySelectorAll('*').forEach(e => {
  if (e.getBoundingClientRect().right > document.documentElement.clientWidth)
    console.log(e);
});

Chaque élément affiché dépasse le bord droit. On remonte ensuite à la règle CSS qui le concerne.

14La media query du projet Moyen

Écrivez la media query qui, sous 768 px : empile la photo et le texte de « À propos », laisse le menu passer à la ligne, réduit le padding du formulaire et diminue la hauteur de la bannière.

✅ Correction
css
@media (max-width: 768px) {
  .menu { flex-wrap: wrap; height: auto; padding: 10px 0; gap: 10px; }

  header { min-height: 55vh; }

  .content-a-propos { flex-direction: column; text-align: center; }

  fieldset { padding: 20px; }
}
⚠️ Les media queries se placent à la fin du fichier

En CSS, à spécificité égale, la dernière règle l'emporte. Une media query écrite en haut du fichier est écrasée par les règles générales qui la suivent — et l'on conclut à tort qu'« elle ne marche pas ». Elle marchait : elle a été recouverte.

Remarquez que .competences n'apparaît pas ici : avec auto-fit et minmax, la grille s'adapte seule. Une media query ne sert que pour ce que le contenu ne peut pas régler lui-même.

07Audit de code

15Relire comme un correcteur Difficile

Voici un extrait réel du projet rendu. Relevez tout ce qui est à corriger — structure, sémantique, accessibilité.

html
<main>
  <section id="competences">
    <h2>Compétences</h2>
    <div class="competences">
      <div class="card">
        <h3>Tersteur logiciel : ISTQB Certified</h3>
        <img src="https://imgs.search.brave.com/lTIp_MlC810..." alt="" />
        <div><p>En tant que testeur certifié…</p></div>
      </div>
    </div>
  </section>

  <footer class="" id="contact">…</footer>
</main>
✅ Correction
LigneDéfautCorrection
<footer> à l'intérieur de <main> fermer </main> avant le footer
class=""attribut vide, reste d'édition le supprimer
Tersteurfaute dans un titre visible Testeur
src="https://imgs.search.brave…" image hébergée chez un tiers : lien fragile, droits non acquis, page inutilisable hors connexion télécharger un visuel libre dans asset/
<div><p>…</p></div> <div> superflu : cette carte n'a plus la même structure que ses voisines, l'alignement en grille saute garder le <p> seul
<div class="card"> une carte de compétence est un contenu autonome <article class="card">
alt=""ici c'est correct — l'image est décorative, le titre porte l'information le garder, mais délibérément

Six corrections, dont une seule est visible à l'écran (la faute d'orthographe). C'est tout l'objet d'une relecture de code : la plupart des défauts ne se voient pas, ils se lisent.

16Les dix minutes avant de rendre Facile

Vous avez fini. Il vous reste dix minutes. Que faites-vous, dans quel ordre ?

✅ Correction
  1. Copier le dossier ailleurs et l'ouvrir (1 min) — c'est le test qui attrape le plus de points perdus : images manquantes, chemins faux, casse.
  2. Relire le cahier des charges (2 min) — les cinq blocs, le CSS externe, les noms de fichiers.
  3. Passer la page au validateur W3C (2 min) — balises mal fermées, id dupliqués, imbrications interdites.
  4. Ouvrir la console, F12 (1 min) — zéro erreur rouge, zéro 404.
  5. Tester à 375 px (1 min) — aucune barre de défilement horizontale.
  6. Cliquer chaque lien du menu et chaque intitulé du formulaire (1 min).
  7. Relire l'orthographe (2 min) — titres compris, ce sont eux qu'on lit en premier.

Aucune de ces étapes n'écrit une ligne de code. Elles rapportent pourtant plus de points que dix minutes de CSS supplémentaires — parce qu'elles corrigent ce qui coûte des points, au lieu d'ajouter ce qui n'en rapporte plus.

Sources exploitées

  • Sujet « Projet Développement WEB — CV en ligne » (le professeur, BTS CIEL 2025-2026)
  • Compte rendu TP5 de l'élève et projet rendu (cv.html, style.css)
  • Le cours du chapitre : Projet CV en ligne