Files
_Assistant_Lead_Tech/10_frontend_patterns_valides.md
MaksTinyWorkshop e61e3d5ea8 Capitalisation complète — app-alexandrie & app-template-resto (23-03-2026)
Intègre ~50 entrées depuis 95_a_capitaliser.md vers les fichiers validés :
- backend risques : +15 (GET sans authz, TOCTOU tenantId, TTL UTC, AdminRoleGuard, P3014...)
- backend patterns : P2002 amendé (create+update) + 10 nouveaux (Decimal, URL safe, EN enforcement...)
- frontend risques : +21 (defaultValue/key, useTransition global, consent state, Tailwind invalide...)
- frontend patterns : +6 (click-to-load, toggle optimiste, Server Action retourne entité...)
- debug/postmortem : export{fn} ne crée pas de binding local

95_a_capitaliser.md remis à l'état initial vide.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 15:02:14 +01:00

29 KiB
Raw Blame History

Patterns front-end validés

Ce fichier contient uniquement des patterns front-end :

  • testés,
  • validés,
  • utilisés dans des projets réels (ou des apps complètes, pas des snippets isolés).

Il sert de mémoire durable pour éviter :

  • de refaire les mêmes erreurs,
  • de redélibérer éternellement sur des sujets déjà tranchés,
  • de propager des “bonnes pratiques” théoriques non éprouvées.

Dernière mise à jour : 23-03-2026


Index


Règle dor

Si ce nest pas confirmé comme fonctionnel et utile,
ça na rien à faire ici.

  • Pas de conseils vagues
  • Pas de patterns “à la mode”
  • Pas de dépendance implicite à un framework ou une version non précisée

Périmètre couvert

  • SPA et webapps
  • UX technique (forms, erreurs, loading, feedback)
  • State management (client / server)
  • Architecture front-end
  • Performance et accessibilité
  • Sécurité front (au niveau applicatif)
  • DX et maintenabilité

Ce fichier traite le front-end comme un logiciel en production,
au même niveau dexigence que le backend.


Format standard dun pattern (obligatoire)

Pattern : <Nom clair et précis>

  • Objectif : ce que le pattern résout
  • Contexte : type dapplication / contraintes
  • Quand lutiliser : cas pertinents
  • Quand léviter : contre-exemples
  • Avantages : bénéfices concrets
  • Limites / vigilance : pièges, dette potentielle
  • Validé le : DD-MM-YYYY
  • Contexte technique : framework + version + tooling principal

Implémentation (exemple minimal)

(contenu volontairement minimal, lisible, non-magique)

Pattern : Gestion explicite des états UI (loading / empty / error)

Synthèse

  • Objectif : éviter les interfaces ambiguës ou incohérentes en rendant explicites tous les états possibles dune vue.
  • Contexte : SPA ou webapp consommant des données asynchrones (API, backend, cache).
  • Quand lutiliser : dès quune vue dépend de données externes ou dun traitement async.
  • Quand léviter : vues purement statiques ou synchrones sans dépendance externe.

Analyse

  • Avantages :
    • UX plus prévisible et compréhensible
    • Debug facilité (état visible = problème identifiable)
    • Base saine pour tests et accessibilité
  • Limites / vigilance :
    • Peut sembler verbeux sur des écrans simples
    • Nécessite une discipline pour ne pas “court-circuiter” les états

Validation

  • Validé le : 25-01-2026
  • Contexte technique : SPA (React / Vue / Svelte agnostique), API HTTP

Implémentation (exemple minimal)

if (loading) {
  afficher un skeleton ou spinner
} else if (error) {
  afficher un message clair + action possible
} else if (data est vide) {
  afficher un état empty explicite
} else {
  afficher la vue nominale
}

Checklist

  • Aucun écran blanc ou silencieux
  • Message derreur compréhensible pour lutilisateur
  • États testables individuellement
  • Accessibilité respectée (focus, lecture écran)
  • Pas de logique métier cachée dans le rendu

Pattern : Séparation claire server state / client state

Synthèse

  • Objectif : éviter le mélange des responsabilités entre données serveur et état local UI.
  • Contexte : SPA ou webapp consommant une API avec interactions utilisateur.
  • Quand lutiliser : dès que lapplication affiche des données distantes modifiables ou synchronisées.
  • Quand léviter : applications très simples ou purement statiques.

Analyse

  • Avantages :
    • Logique plus lisible et testable
    • Réduction des bugs liés aux états incohérents
    • Évolutivité facilitée quand lapp grossit
  • Limites / vigilance :
    • Demande de la rigueur dans le découpage
    • Peut sembler abstrait au début pour des petits projets

Validation

  • Validé le : 25-01-2026
  • Contexte technique : SPA agnostique (React / Vue / Svelte), API HTTP

Implémentation (exemple minimal)

serverState = données venant du backend (fetch, cache, sync)
clientState = état local UI (filtres, onglets, modales, formulaires)

Ne jamais :
- stocker du state UI dans le cache serveur
- dériver la logique UI directement des réponses API sans adaptation

Checklist

  • Les données serveur peuvent être invalidées / rechargées
  • Létat UI est local et réinitialisable
  • Les responsabilités sont lisibles dans le code
  • Les tests peuvent cibler chaque type détat
  • Pas de dépendance implicite entre UI et API

Pattern : Formulaire robuste avec validation et erreurs explicites

Synthèse

  • Objectif : garantir des formulaires fiables, compréhensibles et maintenables.
  • Contexte : toute interface avec saisie utilisateur et règles métier.
  • Quand lutiliser : dès quun formulaire dépasse un simple champ isolé.
  • Quand léviter : formulaires ultra-simples sans validation réelle.

Analyse

  • Avantages :
    • UX claire (lutilisateur sait quoi corriger)
    • Moins derreurs silencieuses
    • Base saine pour tests et accessibilité
  • Limites / vigilance :
    • Peut sembler verbeux sans discipline
    • Risque de duplication si mal factorisé

Validation

  • Validé le : 25-01-2026
  • Contexte technique : Front-end agnostique, API HTTP

Implémentation (exemple minimal)

- Validation côté client (format, champs requis)
- Validation côté serveur (règles métier)
- Mapping explicite des erreurs serveur → champs UI
- Aucun submit silencieux

Checklist

  • Messages derreur compréhensibles et localisés
  • Validation client + serveur cohérente
  • Focus automatique sur le champ en erreur
  • États loading / disabled gérés
  • Tests sur cas valides et invalides

Pattern : Navigation réactive post-action async (React / Expo Router)

Synthèse

  • Objectif : déclencher la navigation après une action asynchrone (login, register, submit) de façon idiomatique et sans bypasser la réactivité React.
  • Contexte : SPA ou app mobile React avec state management (Zustand, Redux, Context) et router déclaratif (React Router, Expo Router, Next.js App Router).
  • Quand l'utiliser : dès qu'une navigation dépend du résultat d'une action async.
  • Quand l'éviter : navigations synchrones sans état async impliqué.

Analyse

  • Avantages :
    • Respecte le cycle de vie React (pas de lecture de state hors cycle)
    • Re-render automatique si l'état change entre-temps
    • Testable : on peut assert sur l'état, pas sur des effets de bord
  • Limites / vigilance :
    • Ne pas oublier les dépendances du useEffect (ESLint react-hooks/exhaustive-deps)
    • Gérer le cas "composant démonté" si la navigation peut être annulée

Validation

  • Validé le : 07-03-2026
  • Contexte technique : React 18+ / Zustand / Expo Router — pattern applicable sur React Router, Next.js App Router

Implémentation (exemple minimal)

// ❌ Anti-pattern : lecture de state hors cycle React
const handleSubmit = async () => {
  await login(email, password);
  const { accessToken } = useAuthStore.getState(); // bypasse la réactivité
  if (accessToken) router.replace('/(tabs)');
};

// ✅ Pattern correct : useEffect réactif sur le state
const { accessToken, isLoading, error } = useAuthStore();

useEffect(() => {
  if (accessToken && !isLoading && !error) {
    router.replace('/(tabs)');
  }
}, [accessToken, isLoading, error]);

const handleSubmit = async () => {
  await login(email, password);
  // la navigation se déclenche via useEffect quand le store se met à jour
};

Pour les callbacks OAuth (ref nécessaire)

// Quand un callback externe déclenche la navigation
const pendingOAuth = useRef(false);

useEffect(() => {
  if (pendingOAuth.current && accessToken) {
    pendingOAuth.current = false;
    router.replace('/(tabs)');
  }
}, [accessToken]);

const handleOAuth = async () => {
  pendingOAuth.current = true;
  await exchangeWithIdp(token);
};

Checklist

  • Aucun store.getState() utilisé pour lire l'état post-action dans un handler
  • useEffect avec dépendances explicites (state pertinent + isLoading + error)
  • Cas d'erreur géré (ne pas naviguer si error est défini)
  • useRef si le trigger vient d'un callback externe (OAuth, deep link)
  • Convention documentée dans la story foundations / project-context avant les premiers écrans

Pattern : Refresh idempotent sur store de liste paginée

Synthèse

  • Objectif : garantir quun pull-to-refresh recharge une liste paginée sans doublons, sans courses réseau et sans état intermédiaire incohérent.
  • Contexte : app mobile ou SPA avec store de domaine (ex. Zustand) et pagination incrémentale.
  • Quand lutiliser : dès quune même liste supporte à la fois loadMore et refresh.
  • Quand léviter : listes purement statiques ou données entièrement remplacées sans pagination.

Analyse

  • Avantages :
    • évite les doublons lors des refresh concurrents
    • garde une transition atomique entre ancien et nouvel état
    • rend le comportement async testable côté store
  • Limites / vigilance :
    • impose une discipline claire entre refresh et loadMore
    • demande une clé didentité stable pour dédupliquer les items

Validation

  • Validé le : 10-03-2026
  • Contexte technique : React Native / Expo / Zustand / listes paginées

Implémentation (exemple minimal)

- conserver une promesse de refresh partagée tant quun refresh est en vol
- refuser ou réutiliser tout refresh concurrent au lieu den lancer un second
- remplacer atomiquement la liste à la fin du refresh
- dédupliquer les items par identifiant au merge des pages suivantes
- empêcher `loadMore` de fusionner sur un snapshot devenu obsolète

Checklist

  • Une seule promesse de refresh en vol à la fois
  • refresh et loadMore ont des garde-fous explicites
  • La liste est remplacée atomiquement après refresh
  • Les pages suivantes sont dédupliquées par identifiant stable
  • Tests sur refresh concurrent + refresh suivi de pagination

Pattern : UI admin légère sur domaine existant

Synthèse

  • Objectif : ajouter une capacité interne simple sans ouvrir trop tôt un back-office séparé ni dupliquer la logique métier.
  • Contexte : app mobile ou SPA avec un domaine métier déjà structuré et quelques actions internes ponctuelles.
  • Quand lutiliser : publication, activation, modération légère, bascule de statut, action opérateur simple.
  • Quand léviter : permissions complexes, workflows multiples, audit riche ou volume dactions qui justifie un vrai espace dadministration.

Analyse

  • Avantages :
    • réutilise le service et le store métier existants
    • limite le coût de structure pour une capacité admin mince
    • garde les mutations testables et lisibles
  • Limites / vigilance :
    • ne pas laisser cette approche dériver vers un pseudo back-office implicite
    • le refresh après mutation doit être explicite sur les vues impactées

Validation

  • Validé le : 10-03-2026
  • Contexte technique : React Native / Expo Router / store de domaine

Implémentation (exemple minimal)

- ajouter une route dédiée minimale pour laction interne
- réutiliser le service/store métier existant au lieu de créer une couche parallèle
- afficher le statut courant avant action
- bloquer les actions concurrentes avec un flag explicite (`isUpdating*`)
- déclencher un refresh explicite des vues impactées après succès
- éviter les mutations en fire-and-forget

Checklist

  • Route dédiée minimale, pas de mini back-office générique
  • Réutilisation du domaine métier existant
  • Garde-fou explicite contre les doubles actions
  • Refresh explicite après mutation réussie
  • Tests sur succès, erreur et action concurrente


Synthèse

  • Objectif : éviter les parcours concurrents et centraliser les garde-fous UX quand une fonctionnalité publique dépend dun service tiers externe.
  • Contexte : site ou webapp avec CTA publics menant vers un tiers de réservation, paiement, prise de rendez-vous ou formulaire externe.
  • Quand lutiliser : dès quune fonctionnalité externe dispose dune page locale dédiée côté produit (/reservation, /booking, etc.).
  • Quand léviter : si le produit assume volontairement une sortie directe unique vers le tiers, sans page locale intermédiaire ni besoin de contextualisation.

Analyse

  • Avantages :
    • UX plus cohérente entre home, navigation et pages dédiées
    • garde-fous, wording et fallbacks centralisés au même endroit
    • facilite lévolution future vers embed, click-to-load ou variantes de parcours
  • Limites / vigilance :
    • ajoute une étape intermédiaire si la page locale napporte aucune valeur
    • demande de maintenir lalignement entre les CTA internes et le parcours canonique

Validation

  • Validé le : 19-03-2026
  • Contexte technique : site web public / intégration tierce en mode lien externe

Implémentation (exemple minimal)

- faire pointer les CTA internes (home, nav, landing) vers une page locale dédiée
- faire de cette page locale le point canonique vers le service tiers externe
- centraliser sur cette page le contexte utile, les garde-fous et les fallbacks
- éviter les sorties directes concurrentes vers le tiers depuis plusieurs endroits du site

Checklist

  • Un parcours canonique unique est défini
  • Les CTA internes convergent vers la page locale dédiée
  • Les garde-fous et fallbacks sont centralisés
  • Les sorties directes concurrentes vers le tiers sont évitées ou justifiées

Pattern : Design Tokens natifs TypeScript (Expo / React Native)

Synthèse

  • Objectif : centraliser les tokens de design sans librairie externe (NativeBase, Tamagui), typés et barrel-exportés.
  • Contexte : app Expo / React Native avec un système de design à maintenir.
  • Quand lutiliser : dès le début dun projet mobile, avant les premiers composants.
  • Quand léviter : si une librairie UI opinionée est déjà choisie et gère ses propres tokens.

Analyse

  • Avantages :
    • aucune dépendance externe, zéro configuration magique
    • autocomplétion TypeScript exacte via as const + types dérivés
    • facile à migrer vers un design system plus élaboré ultérieurement
  • Limites / vigilance :
    • les fichiers TTF doivent être présents dans assets/fonts/ — Google Fonts ne peut pas être téléchargé automatiquement, documenter comme pré-requis dans la story
    • ne pas réutiliser les tokens spacing pour les dimensions de composants (voir risques)

Validation

  • Validé le : 19-03-2026
  • Contexte technique : Expo SDK 52+ / React Native / TypeScript — app-alexandrie story 0.1

Implémentation (exemple minimal)

// apps/mobile/src/theme/colors.ts
export const colors = {
  primary: #2563EB,
  error: #DC2626,
  // ...
} as const;
export type ColorToken = keyof typeof colors;

// apps/mobile/src/theme/spacing.ts
export const spacing = { xs: 4, sm: 8, md: 12, base: 16, lg: 24 } as const;
export type SpacingToken = keyof typeof spacing;

// apps/mobile/src/theme/index.ts (barrel export)
export * from ./colors;
export * from ./spacing;
export * from ./typography;
export * from ./shadows;

Checklist

  • Tous les tokens as const pour inférence exacte
  • Pas de Context React — constantes TypeScript pures
  • Types dérivés (ColorToken = keyof typeof colors) pour lautocomplétion
  • useFonts dans _layout.tsx avec guard !fontsLoaded
  • Fichiers TTF présents dans assets/fonts/ et documentés dans la story

Pattern : Tests de styles React Native sans renderer JSX

Synthèse

  • Objectif : tester les tokens et styles de composants React Native dans un environnement Jest testEnvironment: node sans renderer JSX.
  • Contexte : config Jest avec transform: { ^.+\\.ts$: ts-jest } — les .tsx ne sont pas transformés.
  • Quand lutiliser : tokens de thème, logique pure, valeurs de style exportées.
  • Quand léviter : rendu conditionnel (styles dynamiques inline) — nécessite @testing-library/react-native.

Analyse

  • Avantages :
    • teste que le composant utilise les bons tokens, pas seulement que les tokens ont des valeurs
    • détecte les régressions de style sans renderer
    • rapide, aucune config Jest supplémentaire
  • Limites / vigilance :
    • ne teste pas le style calculé au runtime (style conditionnel dynamique)

Validation

  • Validé le : 19-03-2026
  • Contexte technique : React Native / Jest / ts-jest — app-alexandrie story 0.2

Implémentation

// Button.tsx — exporter le StyleSheet avec un nom préfixé
export const buttonStyles = StyleSheet.create({
  base: { borderRadius: 20, height: 57 },
  primary: { backgroundColor: colors.primary },
});
export function Button(...) { ... }

// ui-components.spec.ts — importer et vérifier les tokens
import { buttonStyles } from ./Button;
import { colors } from @/theme;

it(variante primary utilise colors.primary, () => {
  expect(buttonStyles.primary.backgroundColor).toBe(colors.primary);
});

Deux niveaux de tests UI recommandés

  1. .spec.ts (node) : tokens, valeurs, logique pure
  2. .spec.tsx (config séparée avec renderer) : rendu visuel, interactions

Pattern : Export des styles de composant pour réutilisation partielle (React Native)

Synthèse

  • Objectif : partager les dimensions et formes dun composant UI vers des éléments custom qui en dérivent, sans dupliquer les valeurs.
  • Contexte : app React Native où des screens construisent des éléments qui doivent être "au gabarit" dun composant existant.
  • Quand lutiliser : bouton custom OAuth, container calqué sur un composant de base, etc.
  • Quand léviter : si lécart visuel est intentionnel — dans ce cas, une constante locale est plus claire.

Analyse

  • Avantages :
    • zéro drift silencieux : si les dimensions du composant changent, tous les éléments dérivés suivent
    • tests de styles possibles en dehors du composant
  • Limites / vigilance :
    • à nutiliser que pour des éléments vraiment dérivés, pas comme contournement de design system

Validation

  • Validé le : 19-03-2026
  • Contexte technique : React Native / StyleSheet — app-alexandrie story 0.3

Implémentation

// Button.tsx
export const buttonStyles = StyleSheet.create({
  base: { borderRadius: 20, height: 57 },
  primary: { backgroundColor: colors.primary },
});
export function Button(...) { ... }

// login.tsx — bouton OAuth au gabarit du Button
import { buttonStyles } from @/components/ui/Button;
<TouchableOpacity style={[buttonStyles.base, styles.facebookButton]} />

Pattern : Token typography par usage sémantique (React Native)

Synthèse

  • Objectif : éviter les mauvais usages de tokens typography visuellement proches mais sémantiquement distincts.
  • Contexte : fichier typography.ts dans un design system React Native.
  • Quand lutiliser : dès que deux tokens partagent la même taille mais un poids différent.
  • Quand léviter : jamais — les tokens typography doivent toujours refléter lusage, pas lapparence.

Analyse

  • Avantages :
    • prévient les "approximations" de tokens en code review
    • changement de style dusage spécifique sans régression globale
  • Limites / vigilance :
    • en review : chercher les usages sans fontWeight explicite — cest souvent le signe que le mauvais token a été choisi

Validation

  • Validé le : 19-03-2026
  • Contexte technique : React Native / TypeScript — app-alexandrie story 0.4

Implémentation

// Bon : nommé par usage sémantique
listItemTitle: { fontSize: 12, fontWeight: 600 },  // titre dun item de liste
caption: { fontSize: 12, fontWeight: 500 },         // info secondaire, hints

// Mauvais : nommé par apparence
mediumText12: { fontSize: 12, fontWeight: 500 },    // ambigu, réutilisé à tort

Règle : caption (Medium) ≠ listItemTitle (SemiBold) même si la taille est identique. Ne jamais piocher un token "par approximation".


Principes transverses

  • Un pattern = une responsabilité claire
  • On privilégie la simplicité locale avant la généricité globale
  • Le code doit rester compréhensible 6 mois plus tard
  • Si un pattern devient central → il mérite une décision darchitecture dédiée

Notes importantes

  • 3 bons patterns > 30 moyens
  • Si un pattern évolue :
    • on met à jour la date
    • on précise le nouveau contexte
    • En cas de doute → le pattern nentre pas encore ici

Pattern : Click-to-load strict pour les embeds tiers (iframe/widget)

Synthèse

  • Objectif : ne charger aucun service tiers sans action explicite de lutilisateur (performance + consentement implicite).
  • Contexte : site/webapp avec modules de réservation, map, chat ou tout embed iframe à la demande.
  • Quand lutiliser : dès quun embed tiers est chargé à la demande (pas au premier rendu).
  • Quand léviter : si lembed est central à la page et doit être visible immédiatement.

Analyse

  • Avantages :
    • LCP non pollué par des tiers (performance-first)
    • Aucun tiers ne reçoit de données utilisateur sans action volontaire (consentement implicite)
    • Fallback toujours disponible en cas derreur iframe
  • Limites / vigilance :
    • Le fallback (lien externe + tel:) doit être actionnable même si lembed échoue

Validation

  • Validé le : 21-03-2026
  • Contexte technique : React / Next.js — app-template-resto

Implémentation

const [loaded, setLoaded] = useState(false);
const [errored, setErrored] = useState(false);

if (errored) return <a href={url}>Ouvrir {label}</a>;

return (
  <>
    {!loaded && <button onClick={() => setLoaded(true)}>Charger {label}</button>}
    {loaded && <iframe src={url} onError={() => setErrored(true)} />}
  </>
);

Pattern : Toggle optimiste avec rollback (React Server Action)

Synthèse

  • Objectif : masquer la latence serveur sur un toggle boolean en mettant à jour lUI immédiatement, avec rollback en cas derreur.
  • Contexte : toggles boolean (visibilité, disponibilité, settings) où la latence doit être masquée.
  • Quand lutiliser : toggles sans besoin de re-fetcher lentité entière après mutation.
  • Quand léviter : mutations qui retournent des données complexes → préférer le pattern "Server Action retournant lentité".

Validation

  • Validé le : 21-03-2026
  • Contexte technique : React / Next.js App Router — app-template-resto

Implémentation

const [optimistic, setOptimistic] = useState(initialValue);

async function handleToggle() {
  const prev = optimistic;
  setOptimistic(!prev);          // update immédiat
  try {
    await toggleAction(!prev);
    router.refresh();            // synchronise le Server Component parent
  } catch {
    setOptimistic(prev);         // rollback si erreur
  }
}

Pattern : Server Action retournant lentité — élimination de router.refresh() sur create/edit

Synthèse

  • Objectif : mettre à jour létat local directement avec les données réelles retournées par le serveur, sans round-trip SSR supplémentaire.
  • Contexte : liste ditems managée côté client (useState) avec création et modification via Server Actions.
  • Quand lutiliser : create et edit dentités dans une liste. Plus performant que toggle optimiste + router.refresh().
  • Quand léviter : simples toggles boolean → le pattern optimiste avec rollback suffit.

Analyse

  • Avantages vs toggle optimiste + router.refresh() :
    • Zéro aller-retour SSR supplémentaire (~500ms2s économisés sur mobile)
    • État local garanti cohérent avec la DB (données réelles, pas calculées localement)
    • Pas de flash de rechargement
  • Limites / vigilance :
    • revalidatePath reste nécessaire pour invalider le cache des pages publiques

Validation

  • Validé le : 22-03-2026
  • Contexte technique : React / Next.js App Router — app-template-resto story 3.8

Implémentation

// Repository — retourne lentité complète
export async function createItem(tenantId: string, data: Input): Promise<ItemRow> {
  return prisma.item.create({ data: { tenantId, ...data }, select: { ...fullSelect } });
}

// Action — retourne la donnée au client
export async function createItemAction(formData: FormData): Promise<ItemRow> {
  const actor = await requireOwner();
  const item = await createItem(actor.tenantId, input);
  revalidatePath("/dashboard/..."); // invalider cache pages publiques
  return item; // ← clé : retourner lentité
}

// Client — mise à jour locale sans round-trip SSR
const created = await createItemAction(formData);
setItems((prev) => [...prev, created]); // pas de router.refresh()

Pour les entités avec relations : utiliser un helper findItemById(tenantId, id) appelé après la mutation pour retourner la forme complète avec les relations résolues.


Pattern : ESLint flat config avec presets Next.js (eslint.config.mjs)

Synthèse

  • Objectif : éviter les bugs de compatibilité de lancien .eslintrc avec Next.js récent.
  • Contexte : projet Next.js récent utilisant déjà le flat config ESLint.
  • Quand lutiliser : nouveau projet Next.js ou migration ESLint.
  • Quand léviter : si le projet doit rester compatible avec des outils legacy ESLint.

Validation

  • Validé le : 16-03-2026
  • Contexte technique : Next.js 16+ / ESLint flat config — app-template-resto

Implémentation

// eslint.config.mjs
import nextPlugin from "@next/eslint-plugin-next";

export default [
  ...nextPlugin.configs["core-web-vitals"],
  ...nextPlugin.configs["typescript"],
  {
    rules: {
      // overrides ciblés ici
    },
  },
];

Pattern : Grilles 2 colonnes FR/EN — mobile-first

Synthèse

  • Objectif : afficher les champs FR + EN côte à côte sur desktop, en colonne unique sur mobile.
  • Contexte : formulaires dashboard avec champs bilingues FR/EN côte à côte.
  • Quand lutiliser : tout formulaire avec colonnes parallèles sur un projet mobile-first.
  • Quand léviter : si les champs sont indépendants et nont pas de relation visuelle FR/EN.

Validation

  • Validé le : 22-03-2026
  • Contexte technique : Tailwind CSS / React — app-template-resto

Implémentation

<!-- ✅ Mobile-first — colonne unique sur < 640px, 2 colonnes sur ≥ 640px -->
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
  <input placeholder="Nom (FR)" />
  <input placeholder="Name (EN)" />
</div>

<!-- ❌ À éviter — 2 colonnes trop étroites sur mobile -->
<div class="grid grid-cols-2 gap-4">...</div>