Guide contact: relecture form, redimensionnement borne textarea (min/max), centrage margin auto

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-18 11:41:16 +02:00
parent 965efdb0cb
commit 075d626ebb
+419
View File
@@ -0,0 +1,419 @@
# 📬 Page Contact — relecture & prochaine étape
Salut Mélissa ! 👋
Ta page contact prend forme : le tableau de l'équipe est sympa, le formulaire est en place, et le bouton « contact » qui reste enfoncé pour montrer la page courante (`.instyled`) est une **très bonne idée** d'ergonomie. Bravo ! 🎉
Ce guide a trois objectifs :
1. Corriger **4 bugs** (avec l'explication, pas juste la solution).
2. T'apprendre à **définir et limiter une `<textarea>`** proprement.
3. Te lancer sur ta **première mission JavaScript** 🚀 (dans le thème FleetZen).
---
## 🚨 Bug 1 — L'accolade `}` oubliée (le bug le plus important)
Tu te souviens de la leçon « chaque `{` a son `}` » ? Elle te concerne en plein ! 😄 Regarde le tout début de `contact.css` :
```css
body {
width: 100%;
height: 100%;
background-color: lightblue;
margin: 0;
padding-bottom: 60px;
/* ❌ il manque le } ICI pour fermer body */
#nav_barre { /* du coup le navigateur croit que tout ça est ENCORE dans body */
display: flex;
...
```
Tu as **ouvert** `body {` mais tu ne l'as **jamais refermé**. Résultat : le navigateur pense que `#nav_barre`, `.styled`, `.table`… sont tous **imbriqués dans `body`**, il se perd, et **une grande partie de ton CSS est ignorée**. C'est pour ça que ta page ne ressemble pas à ce que tu attends.
### ✅ Correction : ferme `body` avec un `}`
```css
body {
width: 100%;
height: 100%;
background-color: lightblue;
margin: 0;
padding-bottom: 60px;
} /* ✅ on ferme body ici */
#nav_barre {
display: flex;
...
}
```
> 💡 **À retenir :** à chaque `{` correspond **un** `}`. Une accolade oubliée et **tout le reste de la feuille de style casse**. Astuce : la plupart des éditeurs colorent ou « replient » les blocs — si la coloration paraît bizarre à partir d'un endroit, c'est souvent une accolade manquante juste avant.
---
## 🐛 Bug 2 — Un lien de navigation cassé
Ta page `contact.html` est **déjà dans le dossier `pages/`**. Or ton lien vers services pointe vers :
```html
<a href="pages/services.html"> <!-- ❌ cherche pages/pages/services.html → n'existe pas -->
```
Comme tu es déjà dans `pages/`, tu redemandes d'entrer dans `pages/` → le chemin n'existe pas.
### ✅ Correction : le fichier est **juste à côté**, dans le même dossier
```html
<a href="services.html"> <!-- ✅ même dossier, on met juste le nom du fichier -->
```
> 💡 **À retenir :** les chemins sont **relatifs au fichier courant**.
> - `services.html` → dans le **même dossier**
> - `../index.html` → **remonter** d'un dossier (le `..`), puis prendre `index.html`
> Ton lien « retour » (`../index.html`) est d'ailleurs **correct**, bravo : c'est la même logique.
---
## 🐛 Bug 3 — Le champ e-mail n'est pas un vrai champ e-mail
```html
<input type="text" name="e-mail" id="e-mail" ...> <!-- ❌ type="text" -->
```
En `type="text"`, le navigateur accepte **n'importe quoi**. Pour un e-mail, il existe un type dédié qui **vérifie tout seul** qu'il y a bien un `@` :
```html
<input type="email" name="email" id="email" ...> <!---->
```
> 💡 **À retenir :** le **bon `type`** d'`<input>` fait travailler le navigateur à ta place : `email`, `tel`, `number`, `date`… Chacun ajoute sa petite vérification automatique.
>
> ⚠️ Petit détail : essaie d'éviter le tiret dans les `id`/`name` (`e-mail`). Préfère `email` ou `e_mail` — c'est plus simple à réutiliser ensuite, surtout en JavaScript.
---
## 🎯 Bug 4 — L'icône SVG n'est pas centrée dans le bouton « retour »
Ton bouton « retour » mélange une **image** (le SVG) et du **texte** côte à côte :
```html
<button class="styled"><svg ...></svg>retour</button>
```
Le souci : par défaut, une icône SVG se pose **sur la ligne du texte** (sur la « ligne de base », comme une lettre). Du coup elle paraît trop haute ou trop basse, et ton `line-height: 2.5` accentue le décalage. L'icône et le mot « retour » ne sont pas alignés sur leur **milieu**.
### ✅ La solution : transformer le bouton en boîte flex
```css
.styled {
display: inline-flex; /* le bouton range ses enfants (icône + texte) */
align-items: center; /* ↕ les centre verticalement, sur le même milieu */
justify-content: center; /* ↔ les centre horizontalement */
gap: 6px; /* un petit espace entre l'icône et le mot */
line-height: normal; /* on enlève le 2.5 qui déréglait l'alignement */
/* ...garde le reste de tes styles (couleur, border-radius, etc.) */
}
```
`align-items: center`, c'est **exactement** le `flex` que tu connais déjà (tu l'utilises sur `#buttons`). Ici on l'applique **au bouton lui-même**, parce que c'est lui le parent qui contient l'icône **et** le texte.
> 💡 **À retenir :** dès qu'un bouton (ou n'importe quelle boîte) contient **une icône + du texte** à aligner, passe-le en `display: inline-flex; align-items: center;`. C'est le réflexe pro pour centrer proprement une icône avec du texte.
>
> 🔎 Comme tous tes boutons partagent la classe `.styled`, cette correction les rend **tous** propres d'un coup — même ceux sans icône (le texte y reste bien centré).
---
## 📐 La leçon du jour : définir et **limiter** une `<textarea>`
Ta zone de message est « brute » :
```html
<textarea name="contacter" id="contacter"></textarea>
```
Par défaut elle est petite, et l'utilisateur peut écrire **sans aucune limite**. On va la **cadrer**.
### 1️⃣ Lui donner une taille de départ : `rows` et `cols`
```html
<textarea id="contacter" name="contacter"
rows="6" cols="40"></textarea>
```
- `rows` = nombre de **lignes** visibles (la hauteur).
- `cols` = largeur en **caractères**.
> 🔎 Ce ne sont **pas** des pixels : c'est « combien de lignes/caractères je vois » au départ.
### 2️⃣ Limiter le nombre de caractères : `maxlength`
```html
<textarea id="contacter" name="contacter"
rows="6" cols="40"
maxlength="500"></textarea>
```
`maxlength="500"` = impossible de taper plus de 500 caractères. Le navigateur bloque **tout seul**, sans JavaScript. 👍
### 3️⃣ Cadrer le redimensionnement : `resize` en CSS
La petite poignée en bas à droite permet à l'utilisateur d'étirer la zone et de **casser ta mise en page**. On la contrôle en CSS :
```css
#contacter {
resize: vertical; /* étirement en hauteur seulement (recommandé) */
/* resize: none; pour l'interdire complètement */
width: 100%;
min-height: 120px;
}
```
> 💡 **À retenir :**
> - `rows` / `cols` → **taille de départ**.
> - `maxlength` → **limite** le nombre de caractères (le navigateur bloque tout seul).
> - `resize` (CSS) → contrôle la **poignée** d'étirement (`vertical`, `horizontal`, `none`, `both`).
### 4️⃣ 👉 Votre question : laisser l'utilisateur redimensionner, mais **entre un min et un max**
Tu as écrit pour l'instant :
```css
textarea {
resize: initial; /* "initial" = la valeur par défaut = "both" : étirable dans TOUS les sens, sans limite */
width: 527px;
height: 80px;
}
```
`resize: initial` remet la valeur **par défaut**, c'est-à-dire `both` : l'utilisateur peut tout étirer, **dans toutes les directions et sans aucune borne**. Il peut donc rendre la zone énorme et **casser ta mise en page**. Vous m'avez justement demandé comment **autoriser** l'agrandissement **sans qu'il parte trop grand (ni trop petit)**. La recette tient en 2 idées qui se combinent :
1. **`resize`** dit *dans quelle direction* on peut étirer.
2. **`min-*` / `max-*`** disent *jusqu'où* on peut aller.
```css
#contacter {
resize: vertical; /* on autorise l'étirement, mais en HAUTEUR seulement */
min-height: 80px; /* impossible de la réduire en dessous de 80px */
max-height: 300px; /* impossible de l'agrandir au-delà de 300px */
width: 100%; /* largeur pilotée en CSS, pas par l'utilisateur */
}
```
Avec ça : l'utilisateur attrape la poignée, tire vers le bas pour avoir plus de place… mais **la zone refuse de dépasser 300px** et **de descendre sous 80px**. Ta mise en page reste protégée. 🎉
#### Et si on veut autoriser les **deux** sens (vertical **et** horizontal) ?
Même principe, mais avec **4 bornes** : 2 pour la hauteur, 2 pour la largeur. On passe `resize` à `both` (c'est d'ailleurs ce que faisait ton `resize: initial`, mais cette fois **borné**) :
```css
#contacter {
resize: both; /* la poignée étire en hauteur ET en largeur */
min-height: 80px; /* bornes verticales */
max-height: 300px;
min-width: 200px; /* bornes horizontales */
max-width: 100%; /* ne dépasse jamais la largeur du parent → pas de scroll */
}
```
Chaque direction a sa paire `min`/`max` :
- **hauteur** → `min-height` / `max-height`
- **largeur** → `min-width` / `max-width`
L'utilisateur peut alors étirer dans tous les sens, mais **jamais au-delà** de ces 4 limites.
> ⚠️ Le piège classique en horizontal : mets `max-width: 100%` (et pas un grand `527px` fixe), sinon l'utilisateur peut élargir la zone **plus que l'écran** et faire apparaître une barre de défilement horizontale.
Récap des valeurs de `resize` :
| `resize` | Ce que l'utilisateur peut étirer |
|---|---|
| `none` | rien (verrouillé) |
| `vertical` | la hauteur seulement (le plus confortable pour un message) |
| `horizontal` | la largeur seulement |
| `both` | les deux — c'est ce que donne `initial` par défaut |
> 🎯 Petit conseil de ciblage : vise plutôt `#contacter` (l'id de **ta** textarea) que `textarea` tout court. Le sélecteur `textarea` toucherait **toutes** les zones de texte du site, même celles que Guillaume ajoutera sur sa page.
> 💡 **À retenir :** `resize` = **la direction** autorisée. `min-height`/`max-height` (et `min-width`/`max-width`) = **les bornes**. Les deux ensemble = un redimensionnement libre **mais encadré**.
---
## 🧭 Petit plus — centrer un bloc sans le « pousser » avec `margin-left`
J'ai vu que tu centres ta table **et** ton nouveau `.form` avec `margin-left: 600px` / `620px`. Ça « marche » sur ton écran… mais sur un écran plus petit, le bloc est **poussé hors de la fenêtre** → barre de défilement horizontale. Pour **vraiment centrer** un bloc de largeur fixe :
```css
.form {
width: 35%;
margin-left: auto; /* "auto" à gauche ET à droite = */
margin-right: auto; /* le navigateur répartit l'espace → bloc centré */
/* on enlève le margin-left: 620px */
}
```
> 💡 **À retenir :** `margin: auto` à gauche **et** à droite centre un bloc qui a une largeur. C'est l'outil correct ; `margin-left: 620px` ne fait que le décaler et casse l'affichage sur petit écran.
---
## 🚀 Ta première mission JavaScript : le **compteur de caractères**
Jusqu'ici tu as fait du HTML (la **structure**) et du CSS (l'**apparence**). Le JavaScript, c'est la **3ᵉ couche** : le **comportement**, ce qui réagit en direct. 🧠
On va le faire **en mode tuto** : tu construis petit bout par petit bout, et **tu testes à chaque étape**. Ne copie pas tout d'un coup — avance palier par palier, c'est comme ça qu'on comprend. 🙂
**Objectif final :** `maxlength` empêche de dépasser 500 caractères… mais l'utilisateur ne **voit pas** combien il lui en reste. On va afficher un **compteur en direct** : « Il vous reste 437 caractères ». C'est exactement ce que font Twitter/X, les SMS, etc.
---
### 🪜 Étape 0 — Découvrir la console (ton meilleur ami)
Ouvre ta page dans le navigateur, puis appuie sur **F12** → onglet **Console**. C'est là que le JS « parle ».
Crée un fichier `contact.js` avec une seule ligne :
```js
console.log("Mon fichier JS est bien chargé !");
```
Et branche-le en bas du `<body>`, **juste avant** `</body>` :
```html
<script src="contact.js"></script>
```
> 🔎 Pourquoi à la fin du body ? Parce que le JS s'exécute de haut en bas. S'il se lance **avant** que le HTML existe, il ne trouve rien à manipuler. À la fin, tout le HTML est déjà là. 👍
**✅ Teste maintenant :** recharge la page. Tu dois voir ton message dans la Console. Si oui, ton JS est branché. Sinon, vérifie le chemin du `src`.
---
### 🪜 Étape 1 — Ajouter l'affichage du compteur
Sous ta textarea, ajoute la ligne qui affichera le décompte :
```html
<textarea id="contacter" name="contacter" rows="6" maxlength="500"></textarea>
<p>Il vous reste <span id="restant">500</span> caractères.</p>
```
**✅ Teste maintenant :** tu dois lire « Il vous reste 500 caractères ». Pour l'instant ce 500 est figé, on va le rendre vivant.
> 🔎 Le `<span id="restant">` est juste une petite « boîte » dans laquelle le JS viendra écrire le nombre. Son `id` est l'étiquette par laquelle on va l'attraper.
---
### 🪜 Étape 2 — Attraper les éléments depuis le JS
Dans `contact.js`, remplace ton `console.log` de test par :
```js
const zoneMessage = document.getElementById("contacter"); // ta textarea
const compteur = document.getElementById("restant"); // le <span>
console.log(zoneMessage); // pour vérifier qu'on l'a bien trouvée
```
**✅ Teste maintenant :** dans la Console tu dois voir s'afficher ta `<textarea>`. Si tu vois `null`, l'id ne correspond pas → vérifie l'orthographe (`contacter`).
> 🧩 **Idée clé n°1 — SÉLECTIONNER.** `document.getElementById("contacter")` veut dire : « va me chercher l'élément dont l'id est *contacter* ». On le range dans une variable pour s'en servir après.
---
### 🪜 Étape 3 — Réagir quand on tape
Ajoute en dessous :
```js
zoneMessage.addEventListener("input", function () {
console.log("Tu viens de taper quelque chose !");
});
```
**✅ Teste maintenant :** clique dans la zone et tape quelques lettres. À **chaque** touche, un message apparaît dans la Console. 🎉
> 🧩 **Idée clé n°2 — RÉAGIR.** `addEventListener("input", … )` se lit : « écoute les saisies dans cette zone, et à chaque frappe, exécute cette fonction ». (`"input"` = « le contenu a changé », parfait pour une textarea.)
---
### 🪜 Étape 4 — Calculer et afficher ce qu'il reste
Maintenant on remplace le `console.log` par le vrai calcul. Voici le fichier complet :
```js
// 1. On attrape la textarea et la "boîte" où afficher le compteur
const zoneMessage = document.getElementById("contacter");
const compteur = document.getElementById("restant");
const MAX = 500;
// 2. À chaque frappe, on recalcule ce qu'il reste
zoneMessage.addEventListener("input", function () {
const dejaTape = zoneMessage.value.length; // .value = le texte tapé, .length = sa longueur
compteur.textContent = MAX - dejaTape; // on écrit le résultat dans le <span>
});
```
> 🧩 **Idée clé n°3 — MODIFIER.** `compteur.textContent = …` réécrit le texte affiché **en direct**, sans recharger la page. C'est ça, la magie du JS.
>
> 🔎 `zoneMessage.value`, c'est le **contenu tapé** par l'utilisateur ; `.length` en donne le **nombre de caractères**. `500 ce nombre` = ce qu'il reste.
**✅ Teste maintenant :** tape du texte → le nombre doit **descendre** à chaque lettre (499, 498, …) et **s'arrêter à 0** quand tu atteins la limite. Si oui : bravo, tu viens d'écrire ta première vraie logique en JavaScript ! 🎉
> 🎯 **Défis bonus** (dans l'ordre de difficulté) :
> 1. Passe le compteur en **rouge** quand il reste moins de 50 caractères :
> ```js
> compteur.style.color = (MAX - dejaTape) < 50 ? "red" : "black";
> ```
> 2. Affiche le texte « caractères » au singulier quand il reste 1 ou 0.
> 3. Désactive le bouton « Envoyer » tant que le message est vide.
---
## ♻️ Bonus — La notion de **composant réutilisable** (la navbar)
Regarde : la barre du haut (`#nav_barre`) est **recopiée à l'identique** dans `index.html`, `services.html` et `contact.html`. Le jour où on change un lien, il faut le modifier **dans 3 fichiers** → source d'erreurs (c'est d'ailleurs comme ça que le Bug 2 se glisse 😉).
C'est **le** problème que résolvent les **composants** : on écrit la navbar **une seule fois**, et on la réutilise partout.
Première approche très simple en JavaScript : une fonction qui **génère** la navbar.
```js
function creerNavbar() {
return `
<div id="nav_barre" class="shadow">
<h1>FleetZen</h1>
<div id="buttons">
<button class="instyled">contact</button>
<a href="services.html"><button class="styled">services</button></a>
<a href="../index.html"><button class="styled">retour</button></a>
</div>
</div>`;
}
// On l'injecte tout en haut du body :
document.body.insertAdjacentHTML("afterbegin", creerNavbar());
```
**✅ Teste :** enlève la navbar écrite « en dur » dans le HTML, garde ce JS, recharge → la navbar réapparaît, générée par la fonction. Maintenant le jour où tu changes un lien, tu le changes **à un seul endroit** pour **toutes** les pages (fini les liens cassés du Bug 2 😉).
> 💡 **À retenir :** un **composant** = un bout d'interface écrit **une fois** et **réutilisé** partout. C'est l'idée centrale derrière React, Vue, etc. — mais ça commence par une simple fonction comme celle-ci.
---
## ✅ Ta checklist
- [ ] Fermer `body` avec un `}` dans le CSS (Bug 1 — le plus urgent)
- [ ] Réparer le lien vers `services.html` (Bug 2)
- [ ] Passer le champ e-mail en `type="email"` (Bug 3)
- [ ] Centrer l'icône SVG du bouton « retour » avec `inline-flex` (Bug 4)
- [ ] Ajouter `rows`, `maxlength` + redimensionnement borné (`resize: vertical` + `min/max-height`)
- [ ] Centrer table et `.form` avec `margin: auto` (au lieu de `margin-left: 600px`)
- [ ] Faire marcher le **compteur de caractères** en JavaScript
- [ ] (Bonus) Transformer la navbar en composant réutilisable
- [ ] Penser à fermer la balise `</html>` en fin de fichier + ajouter un `<footer>`
- [ ] Commits petits et clairs, puis `git push`
Pose-moi toutes tes questions. Tu gères ! 📬✨