# 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 ```typescript // ❌ 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 `