Luca Mele
Luca Mele

Architecture

La Rule of Least Power : arrêtez de sur-ingénieriser votre frontend

La Rule of Least Power : arrêtez de sur-ingénieriser votre frontend
Retour aux articles
·9 min de lecture
Écouter le podcast généré par IA
Tous les podcasts →

En 2006, Tim Berners-Lee et Noah Mendelsohn ont publié un document du TAG W3C intitulé « The Rule of Least Power ». L'idée centrale est d'une simplicité trompeuse : lors de la conception d'une solution, choisissez le langage ou la technologie la moins puissante adaptée à la tâche.

Il ne s'agit pas d'être primitif. Il s'agit d'effet de levier. Moins l'outil est puissant, plus la plateforme peut faire pour vous — accessibilité, performance, résilience et portabilité sont intégrées. Dès que vous utilisez JavaScript pour faire ce que HTML fait déjà, vous prenez en charge une responsabilité que le navigateur était prêt à gérer gratuitement.

Après 16 ans de développement frontend, j'ai constaté que ce principe est l'un des guides les plus constamment précieux dans mon travail. C'est la raison pour laquelle je résiste quand les équipes cherchent une bibliothèque de datepicker, un composant modal ou un framework de validation de formulaires — quand le navigateur fournit déjà tout cela.

L'échelle de puissance

Pensez aux technologies frontend comme une échelle de puissance croissante — et de coût croissant :

HTML → CSS → JavaScript → Framework (React, Vue) → Bibliothèque → Framework supplémentaire/méta-framework.

Chaque barreau que vous montez ajoute : taille du bundle, charge de maintenance, fuites d'abstraction, bugs potentiels et choses à apprendre. La règle dit : restez aussi bas sur l'échelle que le problème le permet. Si HTML peut le faire, arrêtez-vous là. Si vous avez besoin de logique de style, CSS. Si vous avez besoin d'interactivité, JS vanilla. N'utilisez React que quand vous avez besoin d'état de composant et de réactivité.

La plupart des équipes montent au sommet de l'échelle par défaut et ne regardent jamais vers le bas. Elles installent une bibliothèque de formulaires avant d'essayer un formulaire natif. Elles ajoutent un composant modal avant de découvrir <dialog>. Elles écrivent du JavaScript pour des choses que CSS gère depuis des années.

Validation de formulaires : le pattern le plus sur-ingénierisé en React

C'est là que la règle paie le plus. J'ai vu d'innombrables codebases React avec des formulaires contrôlés, useState pour chaque champ, des fonctions de validation personnalisées, une gestion d'état d'erreur — des centaines de lignes de code qui répliquent ce que le navigateur fait nativement.

La validation de formulaires HTML est incroyablement puissante : required, minLength, maxLength, pattern (regex complète), type="email", type="url", type="number" avec min/max/step. Le navigateur valide, affiche des messages d'erreur accessibles, empêche la soumission et annonce les erreurs aux lecteurs d'écran. Tout cela gratuitement.

Combinez cela avec le pattern de formulaire non contrôlé de React (useRef + FormData) et vous obtenez le meilleur des deux mondes : le modèle de composants de React avec la validation native du navigateur. Zéro re-render à la saisie, zéro état de validation à gérer, zéro bug de synchronisation état/DOM.

// ✅ The "least power" way — HTML does the work
function ContactForm({ onSubmit }: { onSubmit: (data: FormData) => void }) {
  const formRef = useRef<HTMLFormElement>(null);

  return (
    <form
      ref={formRef}
      onSubmit={(e) => { e.preventDefault(); onSubmit(new FormData(formRef.current!)); }}
    >
      <input name="name" required minLength={2} />
      <input name="email" type="email" required />
      <button type="submit">Send</button>
    </form>
  );
}
// Zero state. Zero re-renders. The browser validates for you.
// Works without JavaScript. Accessible by default.

Comparez avec la « façon React » typique :

// ❌ The "React way" — 40 lines for a validated form
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [errors, setErrors] = useState<Record<string, string>>({});

const validate = () => {
  const errs: Record<string, string> = {};
  if (!name.trim()) errs.name = 'Required';
  if (!/^[^@]+@[^@]+\.[^@]+$/.test(email)) errs.email = 'Invalid';
  setErrors(errs);
  return Object.keys(errs).length === 0;
};

const handleSubmit = (e: FormEvent) => {
  e.preventDefault();
  if (!validate()) return;
  submit({ name, email });
};

// Plus: controlled inputs, error rendering, re-renders on every keystroke…

CSS peut faire plus que vous ne le pensez

Besoin d'afficher un message d'erreur de validation uniquement quand un champ est invalide ? N'écrivez pas un useState + rendu conditionnel. Les pseudo-classes CSS comme :invalid, :valid, :placeholder-shown et :user-invalid peuvent le gérer. Combinez-les avec le sélecteur de frère adjacent (+) et vous avez un feedback de validation sans JavaScript.

Le pattern :not(:placeholder-shown):invalid est particulièrement puissant — il ne montre l'erreur qu'après que l'utilisateur a commencé à taper, évitant le flash de rouge au chargement. C'est une exigence UX courante que les équipes résolvent avec un état JavaScript. CSS le fait en une ligne.

/* Show validation errors with zero JavaScript */
input:not(:placeholder-shown):invalid {
  border-color: #d32f2f;
}

/* Show the error message next to an invalid field */
input:not(:placeholder-shown):invalid + .error-hint {
  display: block;
}

/* The HTML — that's it */
/* <input type="email" required placeholder="you@example.com" /> */
/* <span class="error-hint">Please enter a valid email</span> */

.error-hint {
  display: none;
  color: #d32f2f;
  font-size: 0.85rem;
}

L'élément <dialog> : supprimez votre bibliothèque modale

L'élément HTML <dialog> vous offre : modes modal et non-modal, un backdrop intégré (stylisable avec ::backdrop), piégeage automatique du focus, Échap pour fermer, l'attribut inert sur le contenu d'arrière-plan et une valeur de retour via <form method="dialog">.

C'est tout ce que fait votre bibliothèque modale — sauf que c'est zéro octet, zéro dépendance, accessible par défaut et déjà dans le navigateur. Tous les navigateurs modernes le supportent. J'ai remplacé 3 bibliothèques modales différentes par <dialog> dans des codebases de production.

<!-- ❌ npm install some-modal-library -->
<!-- Bundle size: +12KB gzipped, new API to learn, z-index wars -->

<!-- ✅ HTML <dialog> — built in, accessible, zero dependencies -->
<dialog id="confirm">
  <h2>Are you sure?</h2>
  <form method="dialog">
    <button value="cancel">Cancel</button>
    <button value="confirm">Confirm</button>
  </form>
</dialog>

<script>
  // Show it
  document.getElementById('confirm').showModal();
  // Esc to close: free. Focus trap: free. Backdrop: free.
  // method="dialog" closes it and gives you the return value.
</script>

requestSubmit(), pattern et les inputs date natifs

Trois autres exemples de la règle en action :

requestSubmit() est l'API DOM que la plupart des développeurs React ne connaissent pas. Contrairement à form.submit() qui contourne la validation, requestSubmit() déclenche la validation native des contraintes du navigateur. Si le formulaire est invalide, le navigateur affiche son UI d'erreur intégrée. Si valide, il déclenche l'événement submit. Une ligne de code remplace une fonction de validation personnalisée.

L'attribut HTML pattern accepte une regex JavaScript complète. Besoin de valider un format de téléphone ? Un code produit ? Un code postal ? pattern le fait de manière déclarative, avec des messages d'erreur accessibles. Aucune bibliothèque nécessaire.

Les inputs date HTML sont controversés car ils ont un aspect différent selon les navigateurs. Mais c'est en fait un avantage — sur mobile, ils déclenchent le sélecteur de date natif (roue de défilement sur iOS, calendrier sur Android), qui est bien supérieur à n'importe quel datepicker JavaScript. Je discute activement avec les équipes UI/UX pour concevoir autour de l'input natif plutôt que de le remplacer. L'expérience mobile seule le justifie.

<!-- HTML pattern attribute — regex validation, no JS needed -->
<input
  type="text"
  name="phone"
  pattern="[0-9+\s]{7,15}"
  title="Phone number (digits, spaces, +)"
  required
/>

<!-- requestSubmit() — trigger native validation from JS -->
<script>
  // Unlike form.submit(), requestSubmit() runs validation first
  document.getElementById('myForm').requestSubmit();
  // If invalid → shows native error tooltips
  // If valid → fires the submit event
</script>

<!-- HTML date input — no datepicker library needed -->
<input type="date" name="birthday" min="1920-01-01" max="2026-01-01" />
<!-- Native on every browser. Better UX on mobile (native date wheel). -->

Web Components : quand vous avez besoin de réutilisation sans framework

Parfois vous avez besoin d'un composant réutilisable qui fonctionne à travers les frameworks — ou sans framework. C'est là que les Web Components excellent. Chez AXA, j'ai construit une bibliothèque de composants à l'échelle de l'entreprise avec des Web Components parce que les équipes utilisaient des frameworks différents. Les composants fonctionnaient partout : apps React, pages HTML simples, templates CMS.

Les Web Components se positionnent parfaitement sur l'échelle de puissance : plus puissants que HTML/CSS seuls, mais moins puissants (et moins coûteux) qu'une bibliothèque de composants spécifique à un framework. Ils utilisent le modèle de composants natif de la plateforme — Shadow DOM, Custom Elements, slots — plutôt qu'un modèle virtuel de framework.

La Rule of Least Power ne signifie pas « n'utilisez jamais d'outils puissants ». Elle signifie : ne les utilisez pas quand un outil plus simple fait le travail. Les Web Components sont la bonne réponse pour la réutilisation cross-framework. React est la bonne réponse pour la gestion d'état complexe. Une bibliothèque npm est la bonne réponse quand l'API native est vraiment insuffisante. La clé est d'y recourir délibérément, pas par défaut.

Comment appliquer cela dès demain

Avant d'ajouter une dépendance ou d'écrire une solution personnalisée, montez l'échelle : HTML peut-il le faire ? (required, pattern, type, <dialog>, <details>, <datalist>). CSS peut-il le faire ? (:invalid, :has(), scroll-snap, container queries). Une API DOM native peut-elle le faire ? (requestSubmit, FormData, Constraint Validation API, Intersection Observer). Le JS vanilla peut-il le faire sans bibliothèque ?

Ce n'est que lorsque vous avez répondu « non » à chaque barreau que vous devriez passer au niveau supérieur. Cela ne vous ralentit pas — cela vous accélère. Moins de code à écrire, moins à maintenir, moins de bugs, de meilleures performances et une accessibilité intégrée.

Le W3C nous l'a dit il y a vingt ans. Le navigateur devient plus puissant chaque année depuis. La plupart de ce que vous installez via npm aujourd'hui, la plateforme le fournit déjà. Il suffit de regarder en bas avant de grimper.