Accueil/Informatique & Développement /Chapitre 10 /Projet · TP5

Projet CV en ligne

Le premier projet où l'on ne répond plus à un exercice, mais à un cahier des charges — et où l'on est jugé sur ce qu'on livre.

📌

Résumé flash

Un site personnel en deux fichiers : cv.html pour la structure, style.css pour la mise en forme. Cinq blocs imposés — header, à propos, compétences, expériences, footer — chacun avec sa technique : Flexbox pour aligner, Grid pour les cartes, ancres pour naviguer. Le projet ne teste pas de nouvelle notion : il vérifie que HTML et CSS tiennent ensemble sur une vraie page.

🔑 #cahier-des-charges #CSS-externe #sémantique #ancres #Flexbox #Grid #auto-fit #minmax #formulaire #label-for #media-query #responsive #alt #données-personnelles

01Lire le cahier des charges

L'énoncé du projet n'est pas un texte à lire une fois : c'est la grille de notation, écrite à l'avance. Chaque puce est un point qu'on aura ou qu'on n'aura pas. Le premier geste du projet est donc de le retourner en check-list.

BlocCe qui est exigéTechnique attendue
1. Header nom et prénom · menu vers les 4 sections · image de profil (facultative) Flexbox horizontal, fond et texte contrastés, espacement
2. À propos photo + 2 à 3 lignes de présentation Flexbox : photo et texte côte à côte
3. Compétences 4 à 6 compétences techniques CSS Grid, en cartes
4. Expériences pour chacune : titre · période · description blocs verticaux, Flexbox ou Grid si l'écran le permet
5. Footer email · téléphone · LinkedIn ou GitHub fond proche du header, contenu centré
Livrables cv.html + style.css CSS externe, lié par <link>
🔗 Le cahier des charges, c'est la commande au restaurant

Le client commande « une entrée, un plat, un dessert ». Si le cuisinier envoie un plat magnifique mais oublie l'entrée, la table n'est pas servie — la qualité du plat ne rattrape pas l'oubli. En projet c'est identique : une section absente coûte plus cher qu'une section moyenne. On couvre tout d'abord, on embellit ensuite.

🧰 La phrase à ne pas survoler

« Le fichier HTML doit inclure le CSS externe avec la balise <link rel="stylesheet" href="style.css">. »

C'est une contrainte de notation, pas un conseil. Du CSS écrit dans un <style> ou dans des attributs style="" ne remplit pas la consigne, même si le rendu est identique. Le projet demande deux fichiers : il faut que le correcteur puisse ouvrir style.css et y trouver toutes les règles.

⚠️ Piège d'examen — « optionnel » n'est pas « ignoré »

L'énoncé écrit « Une petite icône ou image de profil (optionnel) ». En BTS, une mention facultative est presque toujours un point bonus, jamais un piège. Le coût est d'une ligne :

html
<img src="asset/pp.jpg" alt="Photo de profil de l'élève" class="avatar">

Une ligne pour un point : on la met.

🧠 Auto-évaluation

L'énoncé demande « 4 à 6 compétences techniques (HTML, CSS, Réseaux, Linux/Windows, etc.) ». Présenter Montage image et Comptabilité répond-il à la consigne ?

Partiellement. Le compte est bon — quatre cartes — mais deux d'entre elles ne sont pas des compétences techniques au sens de la formation. Le mot « techniques » et les exemples donnés (Réseaux, Linux/Windows) orientent vers le référentiel du BTS CIEL.

Le réflexe : garder ces cartes si elles vous distinguent — un CV n'est pas qu'une liste de langages — mais ajouter les compétences attendues, ici Réseaux & systèmes et Linux / Windows Server. On passe à six cartes, la consigne est couverte, et le CV gagne en cohérence avec la filière.

Règle générale : quand l'énoncé donne une fourchette (« 4 à 6 »), viser le haut de la fourchette coûte peu et sécurise la note.

02Poser le squelette

Avant la première règle CSS, on crée l'arborescence. Les chemins d'images sont écrits une fois dans le HTML et doivent rester valides sur la machine du correcteur.

arborescence
projet-cv/
├── cv.html          ← la structure
├── style.css        ← toutes les règles de mise en forme
└── asset/           ← les images, dans un sous-dossier
    ├── pp.jpg               (photo de profil)
    ├── background.jpg       (bannière du header)
    └── projet-1.png … projet-6.png
⚠️ Piège — le projet qui s'affiche sans images chez le correcteur

Le HTML écrit src="asset/pp.jpg" : un chemin relatif qui suppose un sous-dossier asset/ à côté de cv.html. Si l'on rend un dossier à plat — les trois fichiers et les images mélangés — ou si l'on n'envoie que cv.html et style.css parce que « ce sont les deux livrables demandés », toutes les images se cassent et la page paraît vide.

Le test : copier le dossier rendu sur le Bureau, sous un autre nom, l'ouvrir, et regarder. Ce que l'on voit là est exactement ce que le correcteur verra.

🔧 Corrigé — espaces et parenthèses dans les noms de fichiers

Les captures d'écran des projets s'appelaient projet (1).png, projet (2).png… Cela fonctionne en local, mais une URL n'accepte ni espace ni parenthèse : le navigateur doit les encoder en projet%20(1).png, et certains serveurs refusent purement et simplement ces caractères. Le jour où le CV est mis en ligne, les images disparaissent.

minuscules · pas d'accent · pas d'espace · tiret comme séparateur

projet (1).png → projet-1.png. C'est la même règle de nommage que pour les dossiers de ce site, et elle vaut pour tout fichier destiné au web.

La structure sémantique

Cinq blocs demandés, cinq conteneurs HTML. On choisit les balises pour ce qu'elles signifient, pas pour leur apparence — c'est ce qui distingue une page structurée d'une pile de <div>.

html — le squelette
<body>
  <header>
    <nav>…le menu…</nav>
    <div class="presentation">…nom et accroche…</div>
  </header>

  <main>
    <section id="a-propos">   …</section>
    <section id="competences">…</section>
    <section id="experiences">…</section>
  </main>

  <footer id="contact">…</footer>
</body>
🔧 Corrigé — le <footer> était à l'intérieur du <main>

Dans la version rendue, la balise fermante </main> arrivait après le footer :

html — ce qui était écrit
<main>
  <section id="a-propos">…</section>
  …
  <footer id="contact">…</footer>   <!-- ✘ enfermé dans main -->
</main>

<main> désigne le contenu principal et unique de la page — ce qui reste quand on retire l'en-tête, le menu et le pied de page. Un pied de page à l'intérieur du contenu principal est une contradiction : un lecteur d'écran qui propose « aller au contenu principal » y embarque le formulaire de contact, et la commande « aller au pied de page » ne trouve plus rien au bon niveau.

La correction tient en un déplacement de la balise fermante : </main> avant <footer>. Aucun pixel ne bouge à l'écran — et c'est justement pour cela que l'erreur passe inaperçue jusqu'à la correction.

💡 Les attributs class="" vides

Le fichier rendu contenait <nav class=""> et <footer class="" id="contact">. Ce sont des restes d'édition : l'attribut existe, ne vaut rien, ne sert à rien. Aucun effet sur le rendu, mais c'est le genre de détail qu'un correcteur remarque — il montre que le fichier n'a pas été relu. Un nettoyage de trente secondes avant de rendre.

04« À propos » en Flexbox

Consigne : « photo et texte côte à côte », en Flexbox. Trois déclarations suffisent.

css
.content-a-propos {
  display: flex;            /* les enfants se placent en ligne */
  align-items: center;      /* alignés sur leur milieu vertical */
  gap: 28px;                /* l'espace entre photo et texte */
}
.content-a-propos img {
  width: 150px;
  height: 150px;
  object-fit: cover;        /* recadre sans déformer */
  border-radius: 50%;       /* photo ronde */
}
🔧 Corrigé — height: 100% sur une image déforme
css — ce qui était écrit
.content-a-propos > div > img { height: 100%; width: 150px; }   /* ✘ */

Donner à la fois une largeur et une hauteur à une image l'étire ou l'écrase, sauf coïncidence : la photo rendue, en 4:3, était comprimée dans un carré de 150 px. Et height: 100% ne s'évalue que si le parent a une hauteur définie — ce qui n'était pas le cas : la règle était donc à la fois nuisible et imprévisible.

une seule dimension  →  l'autre suit
deux dimensions  →  il faut object-fit: cover

object-fit: cover agit sur une <img> comme background-size: cover sur un fond : l'image remplit le cadre imposé, le surplus est rogné, les proportions sont conservées.

🧠 Auto-évaluation

Sur un écran de téléphone, la photo et le texte côte à côte donnent une colonne de texte de 120 px de large — illisible. Quelle est la correction, et pourquoi cette propriété plutôt qu'une media query ?

css
.content-a-propos {
  display: flex;
  flex-wrap: wrap;        /* ← autorise le passage à la ligne */
  gap: 28px;
}
.content-a-propos > div:last-child { flex: 1 1 280px; }  /* base 280px, sinon on passe dessous */

flex-wrap: wrap autorise les éléments à passer à la ligne quand ils ne tiennent plus. Combiné à flex: 1 1 280px, le texte réclame au moins 280 px : en dessous, il descend automatiquement sous la photo.

L'avantage sur une media query : le point de bascule n'est pas une largeur d'écran arbitraire, mais le moment réel où le contenu ne tient plus. Le comportement reste juste si l'on change la taille de la photo, la police, ou si le bloc est un jour placé dans une colonne plus étroite. La media query reste utile — mais pour ce qu'elle sait faire seule : réorganiser une page entière.

05« Compétences » en Grid

Ici l'énoncé impose l'outil : CSS Grid, en cartes. Flexbox aligne une file d'éléments ; Grid dessine un damier de lignes et de colonnes. Pour des cartes de même nature, c'est Grid.

css
.competences {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 20px;
}
.competences .card {
  background: #fff;
  border-radius: 16px;
  padding: 20px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, .12);
  text-align: center;
}
.competences .card h3 { margin-top: 0; color: #2D5A27; }

Pourquoi auto-fit plutôt que repeat(4, 1fr)

🔧 Corrigé — quatre colonnes figées
css — ce qui était écrit
.competences { display: grid; grid-template-columns: repeat(4, 1fr); }   /* ✘ */

« Quatre colonnes » est une instruction que le navigateur applique à toutes les tailles d'écran. Sur un iPhone de 375 px de large, quatre cartes se partagent l'espace : environ 80 px chacune, marges déduites. Les titres se brisent lettre par lettre, les images débordent, la page défile horizontalement.

css — corrigé
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));

Cette ligne se lit ainsi : « fais autant de colonnes que tu peux, chacune d'au moins 220 px, et distribue l'espace restant à parts égales ». Le navigateur calcule le nombre de colonnes à partir de la place disponible :

Largeur disponibleColonnes obtenuesAppareil
1000 px4ordinateur
700 px3tablette paysage
480 px2tablette portrait
375 px1téléphone

Une ligne de CSS, aucune media query, et la grille est responsive. C'est le motif le plus rentable de tout le CSS moderne : retenez-le tel quel.

⚠️ minmax(220px, 1fr) — l'ordre des deux valeurs

minmax(min, max). Écrire minmax(1fr, 220px) est une erreur : le minimum ne peut pas être une fraction de l'espace restant. La règle est alors ignorée et la grille retombe sur une seule colonne — sans message d'erreur.

Et auto-fill n'est pas auto-fit : avec quatre cartes dans un espace qui en contiendrait huit, auto-fill crée huit colonnes dont quatre vides (les cartes restent à 220 px, serrées à gauche), tandis qu'auto-fit replie les colonnes vides et laisse les quatre cartes s'étendre. Pour un CV, auto-fit.

🔧 Corrigé — grid-template-rows: 150px 150px 150px
css — ce qui était écrit
.competences > .card {
  display: grid;
  grid-template-rows: 150px 150px 150px;   /* ✘ titre / image / texte */
}

L'intention était bonne : que les trois cartes s'alignent, titre sous titre, image sous image. Mais imposer 150 px à une rangée de texte suppose que le texte fera toujours cette hauteur. Le paragraphe de la première carte est trois fois plus long que celui de la troisième : il déborde de sa rangée et chevauche le bord de la carte.

Le second défaut est structurel : la carte « Testeur logiciel » enveloppait son paragraphe dans un <div> supplémentaire, contrairement aux trois autres. Or grid-template-rows distribue les enfants directs : la structure devait être identique d'une carte à l'autre pour que l'alignement fonctionne.

css — corrigé
.competences { align-items: stretch; }   /* les cartes prennent la hauteur de la plus haute */

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

auto laisse chaque rangée prendre la hauteur de son contenu ; 1fr donne tout l'espace restant à la dernière. Les cartes restent alignées, et aucun texte n'est coupé.

🔧 Corrigé — trois images chargées depuis des sites tiers

Les cartes de compétences affichaient des illustrations pointant vers fiverr-res.cloudinary.com, imgs.search.brave.com, static-cse.canva.com et i.ytimg.com. Quatre problèmes superposés :

  1. Le CV ne fonctionne pas hors connexion. Ouvert sans réseau — en salle d'examen, dans le train — il montre quatre cadres vides.
  2. Les liens meurent. Ces URL sont des adresses internes de services tiers (une annonce Fiverr, un cache de moteur de recherche). Elles changent sans préavis. Un CV qui affiche des images cassées six mois plus tard est pire qu'un CV sans images.
  3. Ce sont les images de quelqu'un d'autre. Les afficher depuis son site, sans licence, est une contrefaçon — et charger l'image depuis le serveur d'autrui lui fait payer la bande passante. Le terme consacré est hotlinking.
  4. Ça sort du livrable. L'énoncé demande cv.html et style.css : le projet doit être autonome.

La correction : télécharger des visuels libres de droits (Unsplash, Pexels, icônes Feather ou Bootstrap Icons), les placer dans asset/, et pointer dessus en relatif.

html — corrigé
<img src="asset/icone-web.svg" alt="" width="72" height="72">

Un SVG est ici idéal : quelques kilo-octets, net à toutes les tailles et sur les écrans Retina. Et l'alt vide est correct dans ce cas précis : l'image est décorative, le titre de la carte porte déjà l'information — la répéter encombrerait le lecteur d'écran. Voir la section 9.

💡 « Tersteur logiciel : ISTQB Certified »

Faute de frappe dans un titre de carte — Testeur. Un correcteur la voit en une seconde, un recruteur aussi, et sur un CV dont l'objet est justement la qualité logicielle elle tombe particulièrement mal. Même remarque pour « Site Mamma clazon » (calzone) et « projets web interactives » (interactifs : accord avec projets).

Le réflexe : copier le texte du CV dans un traitement de texte, laisser le correcteur orthographique passer, recopier. Deux minutes, et cela vaut plus qu'une heure de CSS.

06« Projets » et les images

Chaque expérience doit porter trois informations : titre, période, description. La balise <article> convient mieux qu'un <div> : chaque projet est un contenu autonome, qui garderait son sens sorti de la page.

html
<article class="card">
  <h3>SpeedrunHub</h3>
  <time datetime="2023-11">Novembre 2023</time>
  <img src="asset/projet-2.png" alt="Page d'accueil de SpeedrunHub" loading="lazy">
  <p>Plateforme communautaire dédiée au speedrunning : partage de performances,
     forums, profils de jeux et gestion des utilisateurs.</p>
</article>
🧰 <time> — la date lisible par la machine

La version rendue écrivait <p>Date : Janvier 2021</p>. Correct, mais c'est du texte : rien n'indique qu'il s'agit d'une date.

html
<time datetime="2021-01">Janvier 2021</time>

L'attribut datetime porte la date au format normalisé AAAA-MM-JJ ; le texte visible reste libre. Les moteurs de recherche et les robots d'analyse de CV savent alors trier les expériences par date.

🔧 Corrigé — height: 405px; width: 650px sur les captures
css — ce qui était écrit
.card-image-projets > img { height: 405px; width: 650px; }   /* ✘ */

650 px imposés à une image placée dans une carte de 375 px : elle déborde, et la page entière défile horizontalement — le défaut le plus visible d'un site non responsive. Par ailleurs, les six captures n'ont pas toutes le même format : les forcer en 650 × 405 en déforme certaines.

css — corrigé
.card-image-projets img {
  width: 100%;              /* jamais plus large que sa carte */
  aspect-ratio: 16 / 10;    /* toutes les vignettes au même format */
  object-fit: cover;        /* recadrées, pas déformées */
  border-radius: 10px;
  display: block;           /* supprime l'espace fantôme sous l'image */
}
Sur le web, une image se dimensionne en %,
pas en pixels.
⚠️ Piège — une bannière de 8 Mo

background.jpg pèse 8 Mo. C'est la photo sortie de l'appareil, glissée telle quelle dans le dossier. Conséquences : plusieurs secondes d'écran vide en 4G, et le quota de données du visiteur entamé pour une seule image de fond.

UsageLargeur utilePoids visé
Bannière pleine largeur1920 px< 300 Ko
Capture de projet800 px< 150 Ko
Photo de profil400 px< 60 Ko
IcôneSVG< 10 Ko

Redimensionner puis exporter en WebP (ou en JPEG de qualité 80) fait tomber une photo de 8 Mo sous les 200 Ko sans différence visible à l'écran. Et loading="lazy" sur les images de projets diffère leur chargement jusqu'à ce qu'on défile jusqu'à elles.

💡 Trois projets datés du même mois

« Plus ou Moins », « Application Météo » et « Janken de luxe » portent tous Janvier 2021. C'est probablement exact — trois petits projets menés dans la même période d'apprentissage — mais un lecteur y voit une date recopiée par facilité.

Deux options, aussi honnêtes l'une que l'autre : dater précisément chaque projet, ou les regrouper sous un intitulé commun (« Projets d'initiation — JavaScript, janvier 2021 ») qui explique la simultanéité au lieu de la subir.

07Le formulaire de contact

L'énoncé ne demandait qu'un bloc d'informations de contact. Le projet rendu va plus loin : il ajoute un formulaire. C'est une bonne initiative — et c'est aussi l'endroit du projet qui concentre le plus d'erreurs, parce qu'un formulaire s'affiche correctement même quand il est faux.

html — le formulaire 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>
🔧 Corrigé — <form action="POST">

C'est l'erreur la plus fréquente du chapitre formulaires, et elle mérite qu'on s'y arrête : les deux attributs répondent à deux questions différentes.

AttributQuestionValeur
actionOù j'envoie ? une URL : contact.php
methodComment j'envoie ? get ou post

Écrire action="POST" revient à donner le mot « POST » comme adresse de destination. Le navigateur cherche un fichier nommé POST à côté de cv.html, ne le trouve pas — et comme method est absent, il applique la valeur par défaut, get : les champs partent dans la barre d'adresse. Rien ne signale l'erreur, sinon une page 404 après le clic.

action = l'adresse  ·  method = le verbe

Le rappel complet sur GET et POST est au chapitre PHP — sessions.

🔧 Corrigé — deux <label> qui ne pointaient sur rien
html — ce qui était écrit
<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>

Le for d'un label doit reprendre exactement l'id du champ. Ici for="first-name" — vestige d'un copier-coller — ne correspond à aucun id de la page, et il est répété deux fois. Les deux labels sont donc orphelins :

  • cliquer sur le mot « Email » ne place plus le curseur dans le champ ;
  • un lecteur d'écran annonce « zone de saisie, sans intitulé » — l'utilisateur ne sait pas quoi taper ;
  • la zone cliquable, précieuse au doigt sur un téléphone, se réduit au champ lui-même.
html — corrigé
<label for="email">Email</label>
<input type="email" id="email" name="email" required>

Le test en trois secondes, sans aucun outil : cliquer sur chaque intitulé. Si le curseur ne saute pas dans le champ, la liaison est cassée.

🔧 Corrigé — type="number" pour un numéro de téléphone

Un numéro de téléphone n'est pas un nombre : c'est une suite de chiffres. La différence est loin d'être théorique.

Avec type="number"Avec type="tel"
le 0 initial de 07 22… peut être perdu la saisie est conservée telle quelle
espaces, +33 et points refusés tous les formats acceptés
des flèches d'incrément s'affichent aucun contrôle parasite
la molette modifie le numéro par accident la molette fait défiler la page
clavier numérique avec virgule et signe moins clavier téléphone sur mobile
html — corrigé
<input type="tel" id="telephone" name="telephone"
       placeholder="06 12 34 56 78"
       pattern="[0-9 +().-]{6,20}">

type="number" est réservé aux valeurs sur lesquelles une addition a du sens : une quantité, un âge, un prix. Jamais un téléphone, un code postal, un numéro de sécurité sociale ni un numéro de carte.

⚠️ Le <textarea> sans intitulé

La zone de message n'avait ni <label> ni placeholder : un grand rectangle vide, sans indication de ce qu'on attend. Et required y était absent alors qu'il figurait sur tous les autres champs — on pouvait donc envoyer un message vide, ce qui est précisément l'inverse du besoin.

Un placeholder ne remplace jamais un <label> : il disparaît dès la première frappe, son contraste est faible par défaut, et il n'est pas lu comme un intitulé par les technologies d'assistance.

💡 Pourquoi ce formulaire n'envoie encore rien

Même corrigé, ce formulaire ne fait rien : HTML et CSS s'exécutent chez le visiteur, et rien chez le visiteur ne peut envoyer un courriel. Il faut un script côté serveur — le fichier contact.php désigné par action — qui reçoive $_POST, valide les champs et appelle mail(). C'est exactement le mécanisme du chapitre PHP.

Sur un hébergement purement statique (GitHub Pages, par exemple), PHP n'existe pas. La solution honnête est alors d'assumer un lien direct :

html
<a href="mailto:prenom.nom@exemple.fr">Écrivez-moi</a>

Un formulaire qui ne part nulle part est pire que pas de formulaire du tout : le visiteur croit vous avoir écrit.

🧠 Auto-évaluation

Le bouton du formulaire portait le texte « Envoyé ». Pourquoi « Envoyer » est-il la bonne étiquette ?

Parce qu'un bouton nomme l'action qu'il va déclencher, pas un état atteint. « Envoyé » est un participe passé : il annonce que le message est déjà parti, alors qu'on n'a pas encore cliqué. C'est une promesse fausse, et au lecteur d'écran cela s'annonce « bouton — Envoyé », ce qui est franchement trompeur.

Règle d'ergonomie : un bouton se nomme à l'infinitif — Envoyer, Valider, Supprimer, Télécharger. Le participe passé (« Envoyé ✓ ») ne s'affiche qu'après, dans le message de confirmation.

08Rendre le site responsive

Le projet rendu ne contient aucune media query. Sur un téléphone, le menu déborde, les quatre cartes de compétences font 80 px de large et les captures de projets forcent un défilement horizontal. Or un CV en ligne est très souvent ouvert depuis un téléphone.

🧰 Les trois conditions du responsive
  1. La balise viewport — sans elle, rien d'autre ne sert à rien :
    html
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    Elle était bien présente dans le projet. Sans elle, un téléphone fait semblant de mesurer 980 px et dézoome la page entière.
  2. Des dimensions souples — %, fr, minmax(), max-width plutôt que des pixels figés.
  3. Des media queries pour ce que le contenu ne peut pas régler seul : réorganiser la page.
css — le minimum vital
@media (max-width: 768px) {
  .menu { flex-wrap: wrap; height: auto; padding: 10px 0; gap: 10px; }

  header { min-height: 55vh; }
  .fiche-tete h1 { font-size: 1.8rem; }

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

  fieldset { padding: 20px; }        /* 60px de padding sur 375px : il ne reste rien */
}
⚠️ Piège d'examen — le padding: 60px du formulaire

Sur un écran de 375 px, un padding: 60px à gauche et à droite consomme 120 px : il reste 255 px pour les champs. Combiné à box-sizing non réglé, la boîte déborde du parent et la page défile horizontalement.

Le réflexe universel, à placer en tête de toute feuille de style :

css
*, *::before, *::after { box-sizing: border-box; }

Padding et bordure sont alors compris dans la largeur annoncée : un bloc en width: 100% reste large de 100 %, quel que soit son padding.

🧰 Tester sans téléphone

Dans le navigateur : F12 (ou ⌥⌘I sur Mac), puis l'icône de téléphone — Toggle device toolbar. Choisir iPhone SE (375 px), la largeur la plus contraignante encore courante.

Un seul critère : aucune barre de défilement horizontale.

Si elle apparaît, un élément est trop large. Pour le trouver en une ligne, dans la console :

js
document.querySelectorAll('*').forEach(e => {
  if (e.getBoundingClientRect().right > document.documentElement.clientWidth)
    console.log(e);        // affiche le ou les coupables
});

09Accessibilité et vie privée

Les attributs alt

Toutes les images du projet portaient alt="". Un alt vide n'est pas une erreur en soi — c'est même la bonne réponse pour une image décorative — mais il doit être un choix, pas un oubli.

Imagealt attenduPourquoi
Photo de profilalt="l'élève" elle porte une information : qui est la personne
Capture d'un projetalt="Page d'accueil de SpeedrunHub" elle montre le travail décrit à côté
Icône de compétencealt="" décorative : le titre de la carte dit déjà tout
Fond de bannièreen CSS purement décoratif : jamais dans le HTML
⚠️ Le texte blanc sur photo

L'accroche est en blanc sur rgba(0,0,0,.685), posé sur une photographie. Le contraste dépend alors de la zone de l'image qui se trouve derrière — il est excellent sur une partie sombre, insuffisant sur un ciel clair.

Le critère WCAG AA demande un rapport de 4,5:1 pour le texte courant. Pour le garantir quelle que soit la photo, on superpose un voile uniforme :

css
header {
  background-image:
    linear-gradient(rgba(0,0,0,.55), rgba(0,0,0,.55)),   /* le voile, au-dessus */
    url("asset/background.jpg");                          /* la photo, en dessous */
  background-size: cover;
}

L'ordre compte : dans background-image, la première couche déclarée est celle du dessus.

Ce qu'on met — et ne met pas — sur un CV public

⚠️ Un CV en ligne est une page publique, indexée

Le bloc « Mes renseignements » affichait un numéro de téléphone et une adresse postale complète. Mis en ligne, ce bloc est aspiré par les moteurs de recherche et par les robots collecteurs d'adresses, et reste consultable des années dans les caches — bien après la mise à jour de la page.

C'est exactement le matériau de l'OSINT étudié au chapitre cybersécurité : nom, téléphone, adresse, employeur et photo réunis sur une seule page suffisent à monter une usurpation d'identité ou un hameçonnage ciblé crédible.

DonnéeSur un CV publicSur un CV envoyé en PDF
Nom et prénom✔ oui✔ oui
Email✔ oui — une adresse dédiée✔ oui
LinkedIn / GitHub✔ oui✔ oui
Ville✔ oui — « Trappes (78) » suffit✔ oui
Adresse postale✘ non✔ si demandé
Téléphone✘ non — ou via le formulaire✔ oui
Date de naissance✘ nonfacultatif

Le principe du RGPD dit minimisation : ne publier que ce qui est nécessaire à la finalité. La finalité d'un CV en ligne est qu'un recruteur puisse vous joindre — un email y suffit.

💡 Les fausses coordonnées du projet rendu

Le projet affichait une adresse email et une adresse postale manifestement fantaisistes. Pour un TP c'est le bon réflexe : le fichier circule, il est corrigé, parfois republié — mieux vaut qu'il ne contienne aucune donnée réelle.

À une réserve près : le lien LinkedIn, lui, était bien réel. Un fichier ne peut pas être à moitié anonyme. Sur un rendu de TP, ou bien toutes les coordonnées sont fictives, ou bien ce sont celles qu'on assume de publier — pas un mélange des deux.

10Relire et livrer

Le projet est écrit. Reste le geste qui distingue un rendu correct d'un bon rendu : la relecture. Elle se fait dans cet ordre, sans en sauter.

🧰 La check-list de rendu
  1. Le cahier des charges, ligne à ligne. Reprendre le tableau de la section 1 et cocher. Les cinq blocs sont-ils là ? Le CSS est-il bien externe ?
  2. Le dossier copié ailleurs. Dupliquer le projet sur le Bureau, ouvrir la copie : toutes les images s'affichent-elles ?
  3. Le validateur W3C. validator.w3.org, onglet Validate by File Upload. Il détecte en une seconde le <a> dans un <button>, les balises mal fermées et les id dupliqués.
  4. La console du navigateur (F12). Zéro erreur rouge. Une image manquante y apparaît en 404 même quand la page semble correcte.
  5. La largeur 375 px. Aucune barre de défilement horizontale.
  6. Chaque intitulé de formulaire cliqué. Le curseur saute-t-il dans le bon champ ?
  7. L'orthographe. Le texte relu dans un correcteur, titres compris.
⚠️ Ce qu'un correcteur regarde en premier

Un enseignant qui corrige trente projets ouvre la page, regarde dix secondes, puis ouvre le code. Dans ces dix secondes se joue l'essentiel :

  • la page s'affiche — pas de cadres vides ni d'images cassées ;
  • les cinq sections demandées sont visibles ;
  • les liens du menu fonctionnent ;
  • elle tient sur un téléphone.

Puis, dans le code : le CSS est-il bien externe, les balises sont-elles sémantiques, les classes nommées lisiblement. C'est dans cet ordre qu'il faut relire soi-même.

💡 Ce projet n'est pas un exercice noté de plus

C'est la première brique du dossier E4. Un CV en ligne qui fonctionne vraiment — hébergé, responsive, à jour — est une adresse à donner en entretien d'alternance, et la démonstration la plus directe qu'on sait livrer quelque chose de fini.

Le pas suivant coûte une heure : déposer le dossier sur un dépôt GitHub et activer GitHub Pages. Le site devient public à une vraie adresse, gratuitement, et l'historique du dépôt montre le travail — ce qu'un fichier joint à un courriel ne montrera jamais.

🧠 Auto-évaluation

Le CV s'affiche parfaitement sur votre ordinateur. Chez le correcteur, les images des projets ne s'affichent pas, mais les icônes de compétences si. Quelle est la cause la plus probable ?

Les images locales n'ont pas été rendues, contrairement aux images distantes. Les icônes de compétences venaient d'URL en https:// : elles se chargent depuis Internet, quel que soit le dossier. Les captures de projets, elles, étaient en chemin relatif asset/projet-1.png — si le dossier asset/ n'est pas dans le rendu, elles sont introuvables.

Deuxième cause possible, plus sournoise : la casse. macOS et Windows ne distinguent pas majuscules et minuscules dans les noms de fichiers ; Linux — et donc la quasi-totalité des serveurs web — les distingue. Un src="asset/PP.jpg" qui désigne un fichier pp.jpg fonctionne chez vous et nulle part ailleurs.

Les deux causes se préviennent de la même façon : tout en minuscules, sans espace ni accent, et un test sur une copie du dossier avant de rendre.

⚡ Récap 2 minutes

  • Le cahier des charges est la grille de notation : le retourner en check-list avant d'écrire une ligne.
  • Sémantique : header / nav (les liens seulement) / main / section / footer — le footer hors du main.
  • Ancres : href="#nom" pointe vers id="nom". Plus scroll-margin-top pour ne pas passer sous le menu.
  • <a> pour naviguer, <button> pour agir — jamais l'un dans l'autre.
  • Flexbox pour aligner une file (menu, photo + texte) · Grid pour un damier de cartes.
  • La ligne à retenir : repeat(auto-fit, minmax(220px, 1fr)) — responsive sans media query.
  • Images : width: 100%, jamais deux dimensions fixes, object-fit: cover pour recadrer, et un poids sous 300 Ko.
  • Formulaire : action = l'adresse, method = le verbe · for = l'id du champ · type="tel" pour un téléphone · bouton à l'infinitif.
  • Responsive : viewport + box-sizing: border-box + dimensions souples. Test à 375 px, zéro défilement horizontal.
  • Vie privée : sur un CV public, email et LinkedIn — ni adresse postale ni téléphone.
  • Avant de rendre : copier le dossier ailleurs et l'ouvrir. Ce qu'on voit est ce que le correcteur verra.

Sources exploitées

  • Sujet « Projet Développement WEB — CV en ligne » (le professeur, BTS CIEL 2025-2026), 2 pages
  • Compte rendu TP5 de l'élève — document-sans-titre.docx
  • Projet rendu : cv.html (310 lignes) et style.css (185 lignes)
  • Ressources du projet : background.jpg, pp.jpg, projet-1.png à projet-6.png
  • Prérequis : chapitres HTML et CSS