L'essentiel en une minute
Ce cours HTML CSS débutant part de zéro et va jusqu'à une page publiée : structure, mise en forme, Flexbox, Grid, responsive, accessibilité et validation, puis huit exercices corrigés et un plan de quatre semaines.
HTML et CSS restent un passage obligé. Dans l'enquête 2026 de Stack Overflow, 58,2 % des 14 099 répondants disent avoir beaucoup développé avec HTML/CSS au cours de l'année écoulée, juste derrière JavaScript (62,0 %) et SQL (58,4 %), et devant Python (58,0 %) et TypeScript (43,8 %) (Stack Overflow Developer Survey 2026, consulté le 5 novembre 2026).
Points clés
- HTML décrit le contenu, CSS son apparence. Quelques jours de HTML sémantique d'abord, puis les deux ensemble.
- Deux outils gratuits suffisent : Visual Studio Code et les DevTools du navigateur.
- Flexbox aligne sur un axe, Grid sur deux. Une media query adapte la page aux petits écrans.
:has(), l'imbrication CSS et les container queries sont « Baseline Widely available » selon MDN.- Le mini-projet est passé sans erreur aux validateurs HTML et CSS du W3C le 5 novembre 2026.
HTML et CSS : qui fait quoi
HTML structure le contenu, CSS le met en forme. Le parcours de MDN, la documentation web de Mozilla, les enseigne dans cet ordre : « Structurer le contenu avec HTML », puis « Bases de la mise en forme CSS » (MDN, modules fondamentaux, consulté le 5 novembre 2026).
| HTML | CSS | |
|---|---|---|
| Rôle | Dire ce qu'est chaque contenu : titre, paragraphe, lien, formulaire | Dire à quoi il ressemble : couleurs, tailles, espacements, disposition |
| Unité de base | La balise, par exemple <h1> ou <a> | La règle : un sélecteur et des déclarations |
| Fichier | index.html | style.css, relié par <link> |
JavaScript ajoute ensuite le comportement ; c'est la suite logique, détaillée dans notre guide pour apprendre JavaScript.
Les outils : VS Code et les DevTools
Un éditeur de code et un navigateur suffisent. Visual Studio Code est « gratuit pour un usage privé ou commercial », selon sa FAQ, qui précise qu'il est « construit » sur le code open source Code - OSS (licence MIT) mais distribué sous licence Microsoft (notre traduction) (FAQ de VS Code, consultée le 5 novembre 2026). Trois réglages utiles :
- Le raccourci Emmet. Tapez
!puis Tab dans un fichier.htmlvide : VS Code écrit le squelette de la page. - Une extension d'aperçu. Un serveur local recharge la page à chaque sauvegarde ; notre sélection d'extensions VS Code en compare plusieurs.
- Le formatage automatique. « Format On Save » garde une indentation propre.
Les DevTools sont l'autre moitié de l'outillage. Dans Chrome, Ctrl + Maj + C sous Windows et Linux, ou Cmd + Option + C sur Mac, ouvre le panneau Elements en mode inspection ; un clic droit puis « Inspecter » fait la même chose (Chrome for Developers, consulté le 5 novembre 2026). Vous y voyez le HTML réel, les règles appliquées, celles qui sont barrées et le schéma du modèle de boîte.
La structure d'une page HTML
Toute page commence par le même squelette. Voici la version minimale que nous avons fait passer au validateur du W3C :
<!DOCTYPE html> <html lang="fr"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Ma première page</title> <link rel="stylesheet" href="style.css"> </head> <body> <header> <nav aria-label="Navigation principale"> <a href="#apropos">À propos</a> </nav> </header> <main> <section id="apropos"> <h1>Bonjour</h1> <p>Ma première page en HTML et CSS.</p> </section> </main> <footer> <p>Contact : moi@example.com</p> </footer> </body> </html> <!DOCTYPE html> active le mode standard, lang="fr" annonce la langue aux lecteurs d'écran, charset évite les accents cassés, et la balise viewport cale la page sur la largeur de l'écran. Sans elle, un mobile peut rendre la page dans une fenêtre virtuelle plus large, 980 pixels dans l'exemple de MDN, puis la réduire : vos media queries pour petit écran ne se déclenchent pas.
Les balises sémantiques décrivent le rôle de chaque zone : <header> (en-tête), <nav> (navigation), <main> (contenu principal, un seul par page), <section> (partie thématique avec titre), <article> (contenu autonome) et <footer> (pied de page). Un <div> ne dit rien de son contenu : gardez-le pour la mise en forme.
Texte, liens, images, listes, tableaux et formulaires
Les titres et le texte. <h1> à <h6> forment un plan, sans sauter de niveau. <p> crée un paragraphe, <strong> marque une importance.
Les liens. <a href="#contact"> pointe vers une ancre de la page, une adresse complète vers une autre page. Le texte du lien doit avoir un sens seul : jamais « cliquez ici ».
Les images. alt décrit l'image pour qui ne la voit pas ; une image décorative reçoit alt="". width et height évitent que la page saute au chargement.
Les listes. <ul> à puces, <ol> ordonnée, chaque élément dans un <li>.
Les tableaux. Pour des données, jamais pour la mise en page. <caption> donne le titre, <th scope="col"> les en-têtes.
Les formulaires. Chaque champ a un <label> relié par for et id. Le bon type (email, tel) affiche le bon clavier sur mobile ; required bloque un envoi vide.
<form action="/inscription" method="post"> <label for="courriel">E-mail (obligatoire)</label> <input id="courriel" name="courriel" type="email" autocomplete="email" required> <button type="submit">S'inscrire</button> </form> Les bases de CSS : sélecteurs, cascade et spécificité
Une règle CSS associe un sélecteur à des déclarations. Le sélecteur choisit les éléments, chaque déclaration fixe une propriété :
p { color: #1f2937; line-height: 1.6; } Les sélecteurs de base : la balise (p), la classe (.carte), l'identifiant (#contact), le descendant (nav a) et les pseudo-classes comme :hover. Préférez les classes : elles se réutilisent.
Quand deux règles visent le même élément, la cascade tranche. MDN la résume en trois critères, du moins fort au plus fort : l'ordre dans le code (à égalité, la dernière règle gagne), la spécificité, puis !important (MDN, gestion des conflits, page modifiée le 6 octobre 2026, consultée le 5 novembre 2026).
La spécificité se lit en trois colonnes : identifiants, classes (avec attributs et pseudo-classes), éléments. Les colonnes se comparent de gauche à droite, et un seul identifiant bat n'importe quel nombre de classes, d'après la même page.
| Sélecteur | Identifiants | Classes | Éléments | Score |
|---|---|---|---|---|
a | 0 | 0 | 1 | 0-0-1 |
.menu a:hover | 0 | 2 | 1 | 0-2-1 |
#menu a | 1 | 0 | 1 | 1-0-1 |
Le panneau Styles des DevTools barre les règles perdantes : c'est le moyen le plus rapide de comprendre pourquoi une couleur ne s'applique pas. Évitez !important.
Modèle de boîte, unités, couleurs et polices
Chaque élément est une boîte : contenu, padding (marge intérieure), border, puis margin (marge extérieure). Par défaut, width ne compte que le contenu ; avec box-sizing: border-box, elle inclut padding et bordure, ce qui rend les calculs prévisibles.
Les unités changent le comportement de la page :
| Unité | Relative à | Usage typique |
|---|---|---|
px | Rien, valeur fixe | Bordures fines |
rem | Taille de police de la racine (html) | Tailles de texte, espacements |
% | Dimension du parent | Largeurs fluides |
vw, vh | 1 % de la largeur, de la hauteur de la fenêtre | Titres fluides, toujours combinés à rem |
Utilisez rem pour le texte : si le lecteur agrandit sa police, la page suit. N'écrivez jamais une taille de texte en vw seul : elle ne grossit pas avec le zoom, alors que le critère WCAG 1.4.4 demande un texte agrandissable jusqu'à 200 %. clamp(1.75rem, 4vw, 2.5rem) combine un minimum en rem, une valeur fluide et un maximum.
Pour les couleurs, l'hexadécimal (#1d4ed8) suffit ; rangez-les dans des variables (--accent dans :root, puis var(--accent)). Pour les polices, font-family: system-ui, sans-serif utilise la police du système, sans téléchargement.
Flexbox et Grid : la mise en page
Flexbox aligne des éléments sur un axe, Grid les place sur deux. Une barre de navigation est un cas typique de Flexbox :
.site-header { display: flex; justify-content: space-between; align-items: center; gap: 1rem; } justify-content répartit sur l'axe principal, align-items aligne sur l'axe secondaire, gap fixe l'espacement. flex-wrap: wrap autorise le retour à la ligne.
Une grille de cartes est un cas typique de Grid :
.cartes { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; } L'unité fr partage l'espace libre, mais une colonne 1fr ne descend pas sous la largeur de son contenu le plus long : écrivez repeat(3, minmax(0, 1fr)) si un mot ou une image trop large déforme la grille. Retenez : Flexbox pour une rangée de liens, Grid pour une galerie ou une page.
Le responsive design avec les media queries
Une page responsive s'adapte à la largeur de l'écran. Partez du mobile : une colonne par défaut, plus de colonnes quand la place le permet.
.cartes { display: grid; grid-template-columns: 1fr; gap: 1.5rem; } @media (min-width: 48rem) { .cartes { grid-template-columns: repeat(3, 1fr); } } Sous 48rem, soit 768 pixels avec la taille de police par défaut, les cartes s'empilent ; au-delà, elles passent sur trois colonnes. Nous avons vérifié ce comportement sur le mini-projet : une colonne à 375 pixels de large, trois à 1 280 pixels, et aucun défilement horizontal dans les deux cas.
Complétez avec max-width: 100%; height: auto; sur les images : la première déclaration empêche l'image de déborder, la seconde recalcule la hauteur pour garder ses proportions malgré les attributs width et height. Testez ensuite plusieurs largeurs avec le mode appareil des DevTools.
Le CSS moderne et son statut Baseline
Baseline est l'étiquette commune de web.dev et MDN pour savoir si une fonction CSS est utilisable partout. Une fonction est « Newly available » quand elle marche dans tous les navigateurs du socle : Chrome, Edge, Firefox et Safari, sur ordinateur et sur mobile. Elle devient « Widely available » 30 mois plus tard (web.dev, Baseline, consulté le 5 novembre 2026).
Trois fonctions modernes ont ce statut, d'après les bandeaux de MDN relevés le 5 novembre 2026 :
| Fonction | À quoi elle sert | Statut sur MDN | Disponible partout depuis |
|---|---|---|---|
@container | Adapter un composant à son conteneur | Widely available | Février 2023 |
:has() | Cibler un élément selon son contenu | Widely available | Décembre 2023 |
Imbrication & | Imbriquer des règles sans outil | Widely available | Décembre 2023 |
MDN précise pour @container et l'imbrication que certaines parties peuvent avoir un support variable. Le mini-projet utilise l'imbrication pour ranger les styles de la navigation dans l'en-tête :
.site-header { display: flex; a { color: var(--accent); } } Et :has() change la couleur du bouton tant qu'un champ du formulaire est invalide, sans une ligne de JavaScript :
form:has(:invalid) button { background: #4b5563; } Le bouton reste gris tant que le nom est vide ou que l'e-mail est invalide, et devient bleu quand les deux champs obligatoires sont valides : nous l'avons vérifié avec un test Playwright décrit plus bas. Ce gris n'est qu'un indice : le bouton reste cliquable, et c'est la validation native du navigateur qui bloque l'envoi (vérifié : la page ne change pas et le champ passe en invalide). La couleur ne porte pas seule l'information (critère WCAG 1.4.1) : les labels indiquent « (obligatoire) ».
Accessibilité : les bases dès le premier jour
Une page accessible se lit au clavier, au lecteur d'écran et avec une vue faible. La référence est WCAG 2.2, recommandation du W3C depuis le 5 octobre 2023, dont la version en vigueur date du 12 décembre 2024 (historique des publications du W3C et texte de WCAG 2.2, consultés le 5 novembre 2026). Quatre règles de base :
- Le contraste. Le critère 1.4.3 demande un rapport d'au moins 4,5:1 pour le texte normal et 3:1 pour le grand texte, d'après la même norme.
- Les labels. Chaque champ a un
<label>visible et relié, qui signale aussi les champs obligatoires, par exemple « Nom (obligatoire) » (critère 3.3.2). - Le focus visible. Ne supprimez jamais
outlinesans le remplacer : le critère 2.4.7 exige un indicateur de focus visible au clavier, et le 2.4.11, nouveau en 2.2, interdit que l'élément qui a le focus soit entièrement caché par un contenu de la page, comme un bandeau fixe. Le critère 2.5.8 fixe aussi une cible d'au moins 24 x 24 pixels CSS, sauf exceptions. - Les titres et les images. Des titres sans saut de niveau, un
altsur chaque image utile.
Le mini-projet applique la règle suivante, et notre test au clavier confirme un contour bleu de 3 pixels sur le premier lien atteint avec Tab :
a:focus-visible, input:focus-visible, textarea:focus-visible, button:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; } Nos calculs avec la formule du W3C pour le mini-projet : texte #1f2937 sur blanc, 14,68:1 ; liens #1d4ed8 sur #f3f4f6, 6,09:1 ; blanc sur le bouton bleu, 6,7:1, et sur le gris, 7,56:1. Notre première version grisait le bouton avec opacity, ce qui pâlissait le texte : une couleur de fond sombre règle le problème.
Le mini-projet, validé par le W3C
Le projet final est une page de présentation de développeur junior : navigation, trois cartes, un tableau et un formulaire. Nous l'avons écrit puis vérifié le 5 novembre 2026 :
- Validateur HTML du W3C (Nu Html Checker 26.11.5) : zéro erreur, zéro avertissement.
- Validateur CSS du W3C (profil CSS3 + SVG) : zéro erreur, imbrication comprise.
- Rendu avec Playwright dans Chrome 156, Edge 156 et Chromium 120, à 1 280 et 375 pixels : trois colonnes puis une, sans défilement horizontal ni erreur de console.
- Test du formulaire, dans les trois mêmes navigateurs : bouton gris (
#4b5563) avec les champs vides puis avec l'e-mail « lea », bleu (#1d4ed8) avec un e-mail valide ; clic sur « Envoyer » avec le nom vidé : la page ne change pas et le champ est invalide. Une pression sur Tab donne au premier lien un contour plein de 3 pixels décalé de 2 pixels.
Le fichier index.html :
<!DOCTYPE html> <html lang="fr"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Léa Martin, développeuse web junior</title> <meta name="description" content="Page de présentation de Léa Martin, développeuse web junior."> <link rel="stylesheet" href="style.css"> </head> <body> <header class="site-header"> <p class="logo">Léa Martin</p> <nav aria-label="Navigation principale"> <ul> <li><a href="#projets">Projets</a></li> <li><a href="#competences">Compétences</a></li> <li><a href="#contact">Contact</a></li> </ul> </nav> </header> <main> <section class="intro"> <h1>Développeuse web junior</h1> <p>J'apprends HTML et CSS depuis quatre semaines. Voici mes premiers projets.</p> </section> <section id="projets"> <h2>Projets</h2> <div class="cartes"> <article class="carte"> <img src="img/recette.jpg" alt="Aperçu de la page de recette" width="600" height="400"> <h3>Page de recette</h3> <p>Titres, listes et image avec texte alternatif.</p> </article> <article class="carte"> <img src="img/horaires.jpg" alt="Aperçu du tableau d'horaires" width="600" height="400"> <h3>Tableau d'horaires</h3> <p>Un tableau avec en-têtes et légende.</p> </article> <article class="carte"> <img src="img/formulaire.jpg" alt="Aperçu du formulaire d'inscription" width="600" height="400"> <h3>Formulaire d'inscription</h3> <p>Chaque champ a son label.</p> </article> </div> </section> <section id="competences"> <h2>Compétences</h2> <table> <caption>Ce que je sais faire après un mois</caption> <thead> <tr><th scope="col">Domaine</th><th scope="col">Niveau</th></tr> </thead> <tbody> <tr><td>HTML sémantique</td><td>À l'aise</td></tr> <tr><td>Flexbox et Grid</td><td>En progrès</td></tr> </tbody> </table> </section> <section id="contact"> <h2>Contact</h2> <form action="https://example.com/contact" method="post"> <label for="nom">Nom (obligatoire)</label> <input id="nom" name="nom" type="text" autocomplete="name" required> <label for="email">E-mail (obligatoire)</label> <input id="email" name="email" type="email" autocomplete="email" required> <label for="message">Message</label> <textarea id="message" name="message" rows="4"></textarea> <button type="submit">Envoyer</button> </form> </section> </main> <footer> <p>© 2026 Léa Martin</p> </footer> </body> </html> Le fichier style.css :
:root { --texte: #1f2937; --fond: #ffffff; --accent: #1d4ed8; } *, *::before, *::after { box-sizing: border-box; } body { margin: 0; font-family: system-ui, sans-serif; font-size: 1rem; line-height: 1.6; color: var(--texte); background: var(--fond); } .site-header { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 1rem; padding: 1rem 5%; background: #f3f4f6; ul { display: flex; gap: 1rem; list-style: none; margin: 0; padding: 0; } a { color: var(--accent); } } .logo { margin: 0; font-weight: 700; } main { max-width: 60rem; margin: 0 auto; padding: 0 5%; } .intro h1 { font-size: clamp(1.75rem, 4vw, 2.5rem); } .cartes { display: grid; grid-template-columns: 1fr; gap: 1.5rem; } .carte { border: 1px solid #d1d5db; border-radius: 0.5rem; padding: 1rem; } .carte img { max-width: 100%; height: auto; border-radius: 0.25rem; } table { width: 100%; border-collapse: collapse; } th, td { border: 1px solid #d1d5db; padding: 0.5rem; text-align: left; } form { display: grid; gap: 0.5rem; max-width: 30rem; } input, textarea { font: inherit; padding: 0.5rem; border: 1px solid #6b7280; border-radius: 0.25rem; } a:focus-visible, input:focus-visible, textarea:focus-visible, button:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; } form:has(:invalid) button { background: #4b5563; } button { justify-self: start; padding: 0.5rem 1.25rem; font: inherit; color: #ffffff; background: var(--accent); border: 0; border-radius: 0.25rem; cursor: pointer; } footer { margin-top: 3rem; padding: 1rem 5%; text-align: center; background: #f3f4f6; } @media (min-width: 48rem) { .cartes { grid-template-columns: repeat(3, 1fr); } } Un détail de cascade à remarquer : la règle form:has(:invalid) button est placée avant button, mais elle gagne quand même. :has() prend la spécificité de son argument le plus spécifique, ici :invalid : la règle vaut 0-1-2 et dépasse button (0-0-1). Pour valider votre propre page, collez-la sur validator.w3.org/nu ; la page de présentation du validateur rappelle que l'outil est un vérificateur, pas une certification.
Publier sa première page
GitHub Pages publie gratuitement un site statique depuis un dépôt public. La documentation de GitHub décrit la marche à suivre : créer un dépôt nommé <votre-identifiant-github>.github.io, y placer index.html à la racine, choisir la branche de publication dans Settings, puis Pages, et attendre la construction, qui peut prendre jusqu'à dix minutes (GitHub Docs, consulté le 5 novembre 2026).
Avec l'offre GitHub Free, le dépôt doit être public, selon la même page : n'y mettez aucune donnée personnelle. Si Git vous est encore étranger, notre guide Git et GitHub pour débutant va de la première commande au premier dépôt en ligne. Une page publiée devient ensuite la première brique d'un portfolio de développeur.
Exercices HTML CSS débutant corrigés
Faites chaque exercice avant de lire la correction. Le 5 novembre 2026, tous les blocs de code de l'article sont passés aux validateurs HTML et CSS du W3C sans erreur, les extraits HTML étant insérés dans une page complète.
Exercice 1 : le squelette
Écrivez une page minimale en français, avec un titre d'onglet « Exercice 1 », la balise viewport et un <h1>.
Correction.
<!DOCTYPE html> <html lang="fr"> <head> Source:https://dev.to/neurau_tech/html-css-debutant-le-cours-pas-a-pas-un-mini-projet-et-8-exercices-corriges-10a Related posts
No more Cybertrucks; Musk moves on from the angular metallic flop
Float to Int Conversion: Why Large Numbers Break
A 100% coverage threshold that CI never ran
¿Por qué git checkout no puede deshacer lo que cambió un agente de IA en tu código?
Desktops have SQLite. Servers have Postgres. Now, the browser finally has WebDB — Introducing WebDB



