Aller au contenu
PROJET WEB DYNAMIQUE

Formulaire

Chapitre 3 : Formulaires — Ne perdez pas vos utilisateurs en route 📝

On entre dans le dur. Les formulaires.

C'est le moment où vous demandez à l'utilisateur de bosser pour vous (s'inscrire, payer, poster).

Si votre formulaire est une galère à utiliser, ils partiront. Point barre.

Pour un utilisateur de lecteur d'écran ou un utilisateur au clavier, un mauvais formulaire ressemble à un labyrinthe dans le noir. 🔦

Slides

https://gamma.app/docs/Chapitre-3-Formulaires-Accessibles--76qo8zcxto1dw4r

1. La Sainte Trinité du Formulaire 🔱

Pour qu'un champ soit accessible, il lui faut 3 choses :

  1. Une étiquette (Label) : "C'est quoi ce champ ?"

  2. Un type/état : "C'est du texte ? C'est obligatoire ? C'est en erreur ?"

  3. Une description (si besoin) : "Ça doit faire 8 caractères ?" ou "Pourquoi c'est rouge ?"

Le Label : L'attachement vital 🔗

Visuellement, on voit que le texte "Email" est à côté de la case blanche.

Pour un robot, s'ils ne sont pas liés par le code, ils sont des étrangers.

  • Mauvais :

    HTML

    <div>Email</div>
    <input type="text" />
    
  • Bon (Avec Shadcn/HTML natif) :

    HTML

    <Label for="email">Email</Label>
    <Input id="email" name="email" />
    

    L'attribut for du Label doit matcher l'attribut id de l'Input. C'est le lien magique. Quand on clique sur le label, ça active l'input.

2. Gestion des Erreurs : Ne soyez pas juste "Rouge" 🔴

Imaginez : vous êtes daltonien. Le champ devient rouge. Vous ne le voyez pas.

Imaginez : vous êtes aveugle. Un texte d'erreur apparaît sous le champ. Votre lecteur d'écran est focalisé sur le champ, pas sur le texte en dessous. Vous ne savez pas qu'il y a une erreur.

La solution technique : aria-describedby et aria-invalid.

3. Exemple Concret : Refactoring de "Créer une Maison" 🏠

Reprenons ton code Inertia/Vue pour la création immobilière. On va le transformer en Forteresse de l'Accessibilité.

Ce qu'on va ajouter :

  • aria-required="true" : Pour dire au lecteur "Hé, faut remplir ça".

  • aria-invalid="true" : Si Inertia renvoie une erreur.

  • aria-describedby : Pour lier l'input à son message d'erreur.

  • autocomplete : Pour aider le navigateur à pré-remplir (ça aide énormément les troubles moteurs/cognitifs).

Le Code (Version a11y) ✨

Extrait de code

<script setup>
import { Form } from '@inertiajs/vue3'; // Le composant magique
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';

// ... (imports et props inchangés)
</script>

<template>
  <Form :action="store()" #default="{ errors, processing }" class="space-y-6">
    
    <div class="space-y-2">
      <Label for="title">
        Titre de l'annonce
        <span class="text-destructive" aria-hidden="true">*</span>
      </Label>
      
      <Input
        id="title"
        type="text"
        name="title"
        placeholder="Ex: Belle maison à Paris"
        
        aria-required="true" 
        
        :aria-invalid="errors.title ? 'true' : 'false'"
        
        :aria-describedby="errors.title ? 'title-error' : null"
      />

      <p
        v-if="errors.title"
        id="title-error" 
        class="text-sm text-destructive"
        role="alert" 
      >
        {{ errors.title }}
      </p>
    </div>

    <div class="space-y-2">
      <Label for="price">Prix (€) <span aria-hidden="true">*</span></Label>
      <Input
        id="price"
        type="number"
        name="price"
        
        autocomplete="transaction-amount"
        
        aria-required="true"
        :aria-invalid="errors.price ? 'true' : 'false'"
        :aria-describedby="errors.price ? 'price-error' : null"
      />
      <p v-if="errors.price" id="price-error" class="text-sm text-destructive" role="alert">
        {{ errors.price }}
      </p>
    </div>

    <div class="flex gap-4">
      <Button type="submit" :disabled="processing">
        <span v-if="processing" class="mr-2">⏳</span>
        {{ processing ? 'Enregistrement en cours...' : 'Créer la maison' }}
      </Button>
    </div>

  </Form>
</template>

4. Pourquoi c'est mieux ? 🤔

  1. role="alert" sur l'erreur : Dès que le message apparaît, le lecteur d'écran l'interrompt pour le lire : "Alerte : Le titre est obligatoire".

  2. Connexion ID/For : Si je clique sur le label "Prix", le curseur va dans le champ Prix (super pour les tremblements ou gros doigts sur mobile).

  3. aria-invalid : Le lecteur d'écran annonce "Titre, invalide, édition de texte". L'utilisateur sait immédiatement qu'il doit corriger.

💡 Astuce Shadcn : Les composants Shadcn propagent souvent les attributs (...props). Donc si vous mettez aria-required sur le composant <Input>, il atterrira bien sur la balise HTML <input> finale.


gemini_generated_image_l8p73zl8p73zl8p7(1)

Synthèse


On passe à la suite ? Le prochain chapitre concerne les trucs qui bougent et qui poppent (Modales, Toasts) ! 🍿