Files
_Assistant_Lead_Tech/knowledge/frontend/risques/general.md

12 KiB

Frontend — Risques & vigilance : Général

Extrait de la base de connaissance Lead_tech. Voir knowledge/frontend/risques/README.md pour l'index complet.


Accessibilité oubliée (a11y)

Risques

  • App inutilisable au clavier/lecteur d'écran
  • Régressions silencieuses sur focus/labels

Symptômes

  • Modales impossibles à fermer au clavier
  • Inputs sans labels/erreurs non annoncées
  • Focus "perdu"

Bonnes pratiques / mitigations

  • Checklist a11y minimale sur chaque écran clé
  • Gestion de focus (modales, erreurs formulaire)
  • Labels/aria cohérents + tests simples

Regex globale /g en singleton — bug lastIndex stateful

Risques

  • Une regex avec flag /g ou /y définie comme constante au niveau module maintient un état lastIndex entre les appels
  • String.prototype.replace() réinitialise lastIndex, mais .test() ou .exec() ne le font pas → bug stateful difficile à détecter, souvent introduit par un refactor ultérieur

Symptômes

  • .test(str) retourne alternativement true / false sur la même chaîne selon l'ordre d'appel
  • Bug non reproductible en isolation, uniquement en séquence d'appels

Bonnes pratiques / mitigations

// ❌ RISQUÉ — regex globale partagée entre tous les appels
const LINK_PATTERN = /https?:\/\/\S+/gi;
function processLinks(content: string) {
  return content.replace(LINK_PATTERN, ...); // OK today
  // Mais si quelqu'un ajoute LINK_PATTERN.test(x) ailleurs → bug lastIndex
}

// ✅ SÛR — nouvelle instance à chaque appel, aucun état partagé
function makeLinkPattern(): RegExp {
  return /https?:\/\/\S+/gi;
}
function processLinks(content: string) {
  return content.replace(makeLinkPattern(), ...);
}
  • Règle : les regex avec flag /g ou /y utilisées pour transformation de strings → toujours créer via une factory, jamais en singleton de module

  • Contexte technique : TypeScript / React Native — app-alexandrie 24-03-2026


Alert.prompt iOS-only — fonctionnalité silencieusement cassée sur Android

Risques

  • Alert.prompt ne déclenche rien sur Android (retourne undefined silencieusement).
  • Les tests unitaires passent (mock), mais le flux ne s'exécute jamais sur 50 % des devices en production.

Symptômes

  • Flux de saisie utilisateur qui fonctionne sur simulateur iOS mais est inactif sur Android
  • Aucun message d'erreur côté dev ni côté utilisateur

Bonnes pratiques / mitigations

  1. Ne jamais utiliser Alert.prompt dans un projet Expo cross-platform.
  2. Remplacer par une modale custom : Modal + TextInput React Native — portable, accessible, testable.
  3. Wrapper le TextInput dans KeyboardAvoidingView avec behavior={Platform.OS === 'ios' ? 'padding' : 'height'}.
  • Contexte technique : React Native / Expo cross-platform — app-alexandrie 31-03-2026

Primitive UI couplée au contexte parent (layout ou namespace métier)

Risques

  • Une primitive générique (PageShell, ContentCard, SectionWrapper) qui embarque des classes de surface, de largeur ou de namespace métier devient non réutilisable hors de son premier contexte
  • Le couplage reste silencieux au lint et au typecheck, puis force l'ajout progressif de props variant, layout, width ou de classes externes contradictoires

Symptômes

  • La primitive applique directement des classes comme .card, .card--dashboard, .dashboard__item, .profile__card
  • Le parent doit contourner le style natif de la primitive pour l'utiliser dans un autre écran
  • Les classes namespace__element fuitent dans des composants supposés agnostiques du domaine

Bonnes pratiques / mitigations

  • Une primitive pose le squelette sémantique ; le parent pose la surface visuelle (card, width, background, espacement de contexte)
  • Ne pas injecter de classes de namespace métier sur une primitive générique via class
  • Si une variation réutilisable existe vraiment, l'exprimer via une API explicite et bornée (tone, variant) plutôt que par des classes métier ad hoc
  • Contexte technique : Vue 3 / CSS modulaire — RL799_V2, 02-04-2026

Migration partielle vers un composant standard — classes legacy conservées

Risques

  • La coexistence de classes legacy (.primary, .ghost, .danger) et de classes du nouveau composant (.app-btn--primary, .app-btn--ghost) crée une ambiguïté durable de convention
  • Les nouveaux développements continuent d'utiliser l'ancien système faute de règle claire, ce qui ralentit la standardisation

Symptômes

  • Deux façons de produire la même affordance coexistent dans le même repo
  • Un composant dédié existe, mais des liens ou boutons continuent d'utiliser les anciennes classes globales

Bonnes pratiques / mitigations

  • Lorsqu'un composant standardise une affordance, supprimer en même temps les classes CSS globales équivalentes
  • Si un reliquat legacy doit rester temporairement, documenter explicitement son périmètre et sa date de sortie attendue
  • En review, traiter toute nouvelle utilisation d'une classe legacy équivalente comme une régression de standardisation
  • Contexte technique : Vue 3 / design system léger — RL799_V2, 02-04-2026

ARIA roles sans comportement clavier associé

Risques

  • Poser role="menu" / role="menuitem" sur un composant sans implémenter le pattern clavier donne une fausse impression d'accessibilité
  • Les rôles ARIA trompent les lecteurs d'écran et violent WCAG 2.1 (4.1.2 Name, Role, Value)

Symptômes

  • role="menu" sans fermeture via Escape
  • Pas de navigation ArrowUp / ArrowDown ni de roving tabindex

Bonnes pratiques / mitigations

Poser role="menu" / role="menuitem" implique obligatoirement :

  • Fermeture via Escape
  • Navigation via ArrowUp / ArrowDown
  • Roving tabindex (tabindex="0" sur l'item actif, -1 sur les autres)
  • Focus automatique du premier item à l'ouverture

Règle : ne jamais poser un role ARIA de widget interactif sans implémenter le pattern clavier correspondant (cf. WAI-ARIA Authoring Practices)

  • Contexte technique : Vue 3 / accessibilité — RL799_V2 03-04-2026

Duplication de logique métier dans les composants UI (monorepo)

Risques

  • Dans un monorepo avec un package partagé (shared), les fonctions utilitaires métier (ex: conversion grade → rang) sont redéfinies localement dans les composants ou pages frontend
  • Ce type de duplication silencieuse provoque des divergences à terme

Symptômes

  • Fonction switch/case ou mapping identique à une fonction déjà exportée par shared
  • Même signature et même logique dans plusieurs fichiers de couches différentes (composant, page, service)

Bonnes pratiques / mitigations

  • Les fonctions utilitaires métier ne doivent jamais être redéfinies localement dans les composants ou pages frontend

  • Importer systématiquement depuis le package partagé (@monrepo/shared ou équivalent) plutôt que de copier-coller la logique

  • Signal review : grep des fonctions utilitaires existantes dans shared avant de valider un nouveau switch/case

  • Contexte technique : Vue 3 / monorepo — RL799_V2 06-04-2026


Event listeners globaux pour interactions modales

Risques

  • window.addEventListener('keydown') pour capturer Escape dans une modale crée un listener global qui peut confliter avec d'autres modales
  • Le listener fuit si le composant est mal démonté

Symptômes

  • window.addEventListener('keydown', handler) dans un composant modale
  • Cleanup dans onBeforeUnmount mais risque de fuite si le démontage échoue

Bonnes pratiques / mitigations

  • Utiliser @keydown.escape directement sur l'élément dialog avec tabindex="-1" + focus automatique à l'ouverture

  • Élimine le besoin de cleanup et scope l'interaction au composant

  • Signal review : dans tout composant modale, vérifier que les listeners clavier sont sur l'élément, pas sur window

  • Contexte technique : Vue 3 / modales — RL799_V2 06-04-2026


Boutons imbriqués dans les listes interactives

Risques

  • Un <button> ou <a> contenant un autre élément interactif (bouton, lien) est du HTML invalide
  • Casse l'accessibilité et produit un comportement imprévisible selon les navigateurs

Symptômes

  • <button> conteneur avec un <button> enfant (ex: étoile favori dans une carte cliquable)
  • Comportement de clic imprévisible, événements qui ne remontent pas correctement

Bonnes pratiques / mitigations

  • Utiliser un <div> conteneur avec des boutons séparés côte à côte

  • Si toute la ligne doit être cliquable, séparer la zone de clic principale (bouton content) de l'action secondaire (bouton étoile/action)

  • Signal review : dans tout composant liste avec actions inline, vérifier qu'aucun élément interactif n'est imbriqué dans un autre

  • Contexte technique : HTML / accessibilité — RL799_V2 06-04-2026


Fire-and-forget sans feedback sur actions non-critiques

Risques

  • Une action asynchrone non-critique (cache IndexedDB, analytics, sync) lancée en fire-and-forget sans feedback masque les échecs
  • L'utilisateur croit que l'action est faite (ex: document disponible hors-ligne) alors qu'elle a échoué

Symptômes

  • .then(...).catch(() => {}) sur une action secondaire
  • catch { /* ignore */ } sans log ni feedback visuel

Bonnes pratiques / mitigations

  • Même si l'action est non-bloquante, afficher un feedback discret en cas d'échec (toast, badge absent)

  • L'utilisateur doit pouvoir distinguer "fait" de "échoué silencieusement"

  • Signal review : tout .catch(() => {}) ou catch { /* ignore */ } mérite au minimum un log ou un feedback visuel

  • Contexte technique : frontend / actions async — RL799_V2 07-04-2026


Monorepo ESM — shim runtime .js désynchronisé de l'index TypeScript

Risques

  • Le typecheck passe mais le runtime navigateur casse (named export not found).

Symptômes

  • Erreur Vite/browser sur export absent alors que index.ts est correct.

Bonnes pratiques / mitigations

  • Si un shim .js est maintenu, imposer une mise à jour miroir à chaque nouvel export.

  • Ajouter un test/guard de cohérence exports TS vs JS shim.

  • Contexte technique : monorepo / ESM shim runtime — RL799_V2 15-04-2026


ESLint flat config TypeScript sans tsconfigRootDir

Risques

  • Erreurs de parsing massives en IDE/monorepo selon CWD d'exécution.

Symptômes

  • No TsConfigRootDir / Cannot read tsconfig.json alors que le build TS passe.

Bonnes pratiques / mitigations

  • Toujours définir tsconfigRootDir: import.meta.dirname quand parserOptions.project est utilisé.

  • Redémarrer le serveur ESLint après correction.

  • Contexte technique : tooling / ESLint flat config — RL799_V2 17-04-2026


Risques

  • Réponses sensibles servies depuis cache offline.
  • Comportement d'auth incohérent entre réseau/cached.

Symptômes

  • Session/app state divergents après activation SW ou reprise réseau.

Bonnes pratiques / mitigations

  • Exclure explicitement les routes authentifiées sensibles du cache persistant.

  • Définir une stratégie stricte par classe de route (auth, API privée, assets publics).

  • Contexte technique : PWA / service worker / auth cookie — RL799_V2 18-04-2026


PWA install prompt — capture tardive de beforeinstallprompt

Risques

  • Événement perdu au cold boot, prompt jamais proposé.

Symptômes

  • Implémentation correcte en apparence mais aucun déclenchement sur Android.

Bonnes pratiques / mitigations

  • Installer l'écouteur le plus tôt possible dans le cycle d'initialisation.

  • Ne pas baser la détection iOS uniquement sur l'UA (cas iPad en mode desktop).

  • Contexte technique : PWA / install prompt — RL799_V2 18-04-2026