---
title: Tap simple, double tap et tap long
description: Comment les smartphones distinguent tap, double tap et appui long — l'histoire du geste depuis la souris jusqu'au code du jeu w3hc.org/clic.
date: 2026-09-12
lang: fr-FR
author: Julien Béranger
model: Claude Sonnet 5
source: https://julienberanger.com/tap-double-tap-tap-long
---

# Tap simple, double tap et tap long

*Comment les smartphones font la différence — et comment un jeu web la reconstruit à la main*

🎮 **Jeu d'entraînement : [w3hc.org/clic](https://www.w3hc.org/clic)** — [code source](https://github.com/w3hc/w3hc-web/blob/main/src/app/clic/page.tsx)

---

## En trois phrases

L'écran de ton téléphone ne sait faire qu'une chose : dire « un doigt vient de se poser ici », « il a bougé », « il est parti ». Rien d'autre. C'est le logiciel qui, en chronométrant le temps entre le poser et le lever, décide après coup s'il s'agissait d'un tap ou d'un appui long — la frontière est à une demi-seconde, et elle a été choisie par des ingénieurs, pas par une norme.

Conséquence pratique : un appui long se déclenche **pendant** que le doigt est encore posé (d'où la petite vibration qui prévient), alors qu'un tap simple n'est confirmé qu'**au relâchement**. Et si le doigt glisse un peu trop pendant l'appui, tout est annulé : le système considère que tu voulais faire défiler, pas appuyer.

---

## Table des matières

- [1. Au niveau matériel : aucune différence](#1-au-niveau-matériel--aucune-différence)
- [2. Les deux critères qui séparent les gestes](#2-les-deux-critères-qui-séparent-les-gestes)
- [3. Valeurs concrètes par plateforme](#3-valeurs-concrètes-par-plateforme)
- [4. Standardisé ou non ?](#4-standardisé-ou-non-)
- [5. Localisation](#5-localisation)
- [6. Un peu d'histoire](#6-un-peu-dhistoire)
- [Étude de cas : le jeu w3hc.org/clic](#étude-de-cas--le-jeu-w3hcorgclic)
- [Références](#références)

---

## 1. Au niveau matériel : aucune différence

Le [digitizer capacitif](https://en.wikipedia.org/wiki/Capacitive_sensing) ne connaît pas la notion de « tap ». Le contrôleur tactile remonte uniquement des événements bruts, à une fréquence de scan de 60 à 240 Hz :

- `DOWN` — contact détecté
- `MOVE` — coordonnées x/y, éventuellement surface de contact et pression
- `UP` — relâchement

Sous Android, ça correspond aux constantes [`MotionEvent.ACTION_DOWN`](https://developer.android.com/reference/android/view/MotionEvent#ACTION_DOWN) / `ACTION_MOVE` / `ACTION_UP` ; sur le web, aux événements [`pointerdown`](https://developer.mozilla.org/fr/docs/Web/API/Element/pointerdown_event) / [`pointermove`](https://developer.mozilla.org/fr/docs/Web/API/Element/pointermove_event) / [`pointerup`](https://developer.mozilla.org/fr/docs/Web/API/Element/pointerup_event).

Toute la distinction tap / long tap / drag / double tap est faite **en logiciel**, dans la couche d'input de l'OS ou du framework UI.

## 2. Les deux critères qui séparent les gestes

**La durée entre `DOWN` et `UP`.** Seuil quasi universel : 500 ms.

**La tolérance de déplacement (*touch slop*).** Si le doigt bouge au-delà d'un rayon donné pendant l'appui, le geste est reclassé en scroll/drag et le timer d'appui long est annulé. C'est ce qui évite qu'un long press se déclenche quand on commence à faire défiler une liste lentement.

Différence comportementale importante :

| | Moment de résolution | Signal utilisateur |
|---|---|---|
| Tap | au relâchement (`UP`) | état visuel « pressé » |
| Long press | pendant l'appui, à l'expiration du timer | retour haptique |

Le long press vibre parce que c'est le seul moyen d'indiquer que le seuil vient d'être franchi, alors que rien n'a encore bougé à l'écran. Sur le web, l'équivalent est l'[API Vibration](https://developer.mozilla.org/fr/docs/Web/API/Vibration_API) (`navigator.vibrate()`), supportée sur Android mais [pas sur iOS Safari](https://caniuse.com/vibration).

## 3. Valeurs concrètes par plateforme

| Plateforme | Seuil long press | Touch slop | Constante |
|---|---|---|---|
| Android (AOSP) | 500 ms | ~8 dp | [`ViewConfiguration.getLongPressTimeout()`](https://developer.android.com/reference/android/view/ViewConfiguration#getLongPressTimeout()) / [`getScaledTouchSlop()`](https://developer.android.com/reference/android/view/ViewConfiguration#getScaledTouchSlop()) |
| iOS / UIKit | 0,5 s | 10 pt | [`minimumPressDuration`](https://developer.apple.com/documentation/uikit/uilongpressgesturerecognizer/minimumpressduration) / [`allowableMovement`](https://developer.apple.com/documentation/uikit/uilongpressgesturerecognizer/allowablemovement) |
| SwiftUI | 0,5 s | 10 pt | [`.onLongPressGesture(minimumDuration:)`](https://developer.apple.com/documentation/swiftui/view/onlongpressgesture(minimumduration:maximumdistance:perform:onpressingchanged:)) |
| Flutter | 500 ms | 18 px logiques | [`kLongPressTimeout`](https://api.flutter.dev/flutter/gestures/kLongPressTimeout-constant.html) / [`kTouchSlop`](https://api.flutter.dev/flutter/gestures/kTouchSlop-constant.html) |
| React Native | 500 ms | — | [`delayLongPress`](https://reactnative.dev/docs/pressable#delaylongpress) |
| Jetpack Compose | 500 ms | — | [`detectTapGestures(onLongPress:)`](https://developer.android.com/reference/kotlin/androidx/compose/foundation/gestures/package-summary#(androidx.compose.ui.input.pointer.PointerInputScope).detectTapGestures(kotlin.Function1,kotlin.Function1,kotlin.Function1,kotlin.Function1)) |
| Web | non natif | — | à implémenter soi-même ; [`contextmenu`](https://developer.mozilla.org/fr/docs/Web/API/Element/contextmenu_event) sert souvent de proxy |

Autres constantes Android utiles pour situer (voir le [fichier source AOSP `ViewConfiguration.java`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/view/ViewConfiguration.java)) :

- [`TAP_TIMEOUT`](https://developer.android.com/reference/android/view/ViewConfiguration#getTapTimeout()) = 100 ms — délai avant d'afficher l'état « pressé », pour ne pas faire clignoter le bouton pendant un scroll
- [`DOUBLE_TAP_TIMEOUT`](https://developer.android.com/reference/android/view/ViewConfiguration#getDoubleTapTimeout()) = 300 ms
- [`PRESSED_STATE_DURATION`](https://developer.android.com/reference/android/view/ViewConfiguration#getPressedStateDuration()) = 64 ms

Côté Flutter, toutes les constantes sont regroupées dans un seul fichier très lisible : [`gestures/constants.dart`](https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/gestures/constants.dart).

## 4. Standardisé ou non ?

Il n'existe **aucune norme formelle**. Ni le W3C — la spec [Pointer Events Level 3](https://www.w3.org/TR/pointerevents3/) s'arrête aux événements bas niveau et ne définit aucun geste — ni l'ISO, ni aucun organisme ne fixe ces durées. Les 500 ms sont une convergence de fait : Android l'a fixé dans AOSP, Apple a retenu la même valeur, les frameworks tiers ont suivi pour la cohérence perçue.

Les surcouches constructeurs ([One UI](https://www.samsung.com/fr/apps/one-ui/), [MIUI/HyperOS](https://hyperos.mi.com/), ColorOS…) conservent en général la valeur AOSP : la modifier casserait le comportement des applications tierces qui lisent `ViewConfiguration`.

Les vraies variations viennent de **l'accessibilité**. Android expose un réglage [« Délai d'appui prolongé »](https://support.google.com/accessibility/android/answer/6006989) (court / moyen / long, de l'ordre de 400 ms à 1,5 s) qui modifie la valeur système globale. Une app codée en dur avec son propre `postDelayed(500)` ignorera ce réglage — bug d'accessibilité classique. iOS a un réglage équivalent dans [Toucher › Durée du contact](https://support.apple.com/fr-fr/guide/iphone/iph77bcdd132/ios).

**Cas particulier historique :** le [3D Touch](https://en.wikipedia.org/wiki/3D_Touch) (iPhone 6s à XS) distinguait les deux gestes par la **pression** mesurée et non par la durée, via un capteur capacitif sous l'écran. Apple l'a abandonné à partir de l'iPhone 11 au profit du [Haptic Touch](https://support.apple.com/fr-fr/guide/iphone/iph77bcdd132/ios), purement temporel.

## 5. Localisation

Aucune variation régionale. Ces seuils ne font pas partie des ressources localisables — ils sont en dur dans le framework, pas dans les fichiers de traduction. La locale n'influence que la direction des gestes directionnels (le [RTL](https://developer.android.com/training/basics/supporting-devices/languages#SupportLayoutMirroring) inverse la sémantique des swipes, pas celle des taps) et, marginalement, certains profils haptiques.

Côté accessibilité, le long press pose problème pour les utilisateurs avec tremblements ou mobilité réduite. Le [WCAG 2.1](https://www.w3.org/Translations/WCAG21-fr/) recommande :

- **[2.5.1 Gestes du pointeur](https://www.w3.org/WAI/WCAG21/Understanding/pointer-gestures.html)** — aucune fonction ne doit être accessible *uniquement* par long press
- **[2.5.2 Annulation du pointeur](https://www.w3.org/WAI/WCAG21/Understanding/pointer-cancellation.html)** — une action ne doit pas se déclencher sur le `DOWN` seul

---

## 6. Un peu d'histoire

### 1968–1973 — Le clic naît avec la souris

La [démo d'Engelbart de 1968](https://www.youtube.com/watch?v=yJDv-zdhzMY) (« [The Mother of All Demos](https://en.wikipedia.org/wiki/The_Mother_of_All_Demos) ») introduit la souris et l'idée d'un pointeur qui « clique ». À ce stade, un clic est un événement atomique : l'interrupteur est fermé, point. Aucune notion de durée.

Le [Xerox Alto](https://computerhistory.org/blog/the-xerox-alto-and-the-birth-of-the-modern-gui/) (1973) au [PARC](https://en.wikipedia.org/wiki/PARC_(company)) pose les bases du GUI moderne — fenêtres, icônes, menus — avec une souris **à trois boutons**. Chaque bouton a sa sémantique propre : pas besoin de surcharger le geste avec le temps.

### 1974–1975 — Le double-clic, première désambiguïsation temporelle

Dans l'éditeur [Gypsy](https://en.wikipedia.org/wiki/Gypsy_(software)), [Larry Tesler](https://en.wikipedia.org/wiki/Larry_Tesler) et Tim Mott introduisent le double-clic pour sélectionner un mot. C'est **la première fois qu'un logiciel mesure l'intervalle entre deux clics pour en déduire une intention différente**. Tout le modèle actuel découle de là : un geste pointeur n'est plus un événement, c'est une séquence à interpréter.

Tesler rejoindra Apple en 1980 ; le lien direct entre le PARC et les interfaces d'aujourd'hui passe par lui. (Il est aussi l'inventeur du [copier/coller](https://www.computerworld.com/article/1615267/rip-larry-tesler-inventor-of-copy-paste.html), qui réapparaîtra plus loin dans cette histoire.)

### 1983–1984 — Lisa, Macintosh, et le bouton unique

L'[Apple Lisa](https://computerhistory.org/blog/the-lisa-apples-most-influential-failure/) (1983) puis le Macintosh (1984) généralisent le double-clic à l'ouverture de fichiers et d'applications — [Bill Atkinson](https://en.wikipedia.org/wiki/Bill_Atkinson) en revendique la paternité pour cet usage.

Décision lourde de conséquences : Apple choisit une souris **à un seul bouton**. Ce qui était réparti sur trois boutons chez Xerox doit désormais tenir sur un seul, donc être encodé autrement — par le nombre de clics, par la durée, ou par un modificateur clavier (le `Cmd`-clic, puis le `Ctrl`-clic pour le menu contextuel). **La contrainte matérielle crée le besoin de désambiguïsation temporelle.** C'est exactement le même mécanisme qui produira le long press sur mobile.

La même année, le [HP-150](https://en.wikipedia.org/wiki/HP-150) devient l'un des premiers PC à écran tactile du marché (grille infrarouge). Le doigt est trop imprécis, le bras se fatigue (« [gorilla arm](https://en.wikipedia.org/wiki/Touchscreen#%22Gorilla_arm%22) ») : l'idée est abandonnée pendant vingt ans.

### 1985–1995 — Le clic droit s'impose sur PC

Côté Microsoft, la souris à deux boutons devient la norme et [Windows 95](https://en.wikipedia.org/wiki/Context_menu) généralise le **menu contextuel au clic droit**. Le monde PC n'a donc pas besoin d'appui long : il a un second bouton. Cette divergence Mac/PC explique en partie pourquoi les deux écosystèmes mobiles aborderont le problème différemment.

### 1993–2005 — Les PDA à stylet inventent le tap-and-hold

Les assistants personnels à écran [résistif](https://en.wikipedia.org/wiki/Resistive_touchscreen) — [Apple Newton](https://en.wikipedia.org/wiki/Apple_Newton) (1993), [Palm Pilot](https://en.wikipedia.org/wiki/PalmPilot) (1996), [Pocket PC](https://en.wikipedia.org/wiki/Pocket_PC) (2000) — se manipulent au stylet. Un stylet n'a pas de bouton droit.

Microsoft formalise alors le **tap-and-hold** dans les guidelines Windows Mobile, explicitement présenté comme le substitut du clic droit. La documentation de l'époque est sans ambiguïté : [« un Pocket PC n'a pas de souris, l'utilisateur ne peut pas faire de clic droit ; il doit taper sur le contrôle puis maintenir le stylet en place jusqu'à l'apparition du menu contextuel »](https://learn.microsoft.com/en-us/previous-versions/ms839437(v=msdn.10)). On y trouve même déjà le pattern d'implémentation moderne : armer un timer au `MouseDown`, l'annuler si le geste se transforme.

**C'est l'ancêtre direct du long press.** Il naît d'une contrainte matérielle (pas de second bouton) et d'un besoin fonctionnel (les menus contextuels), pas d'une intuition ergonomique.

### 2007 — L'iPhone remet tout à plat

L'iPhone abandonne le stylet pour le [multitouch capacitif](https://en.wikipedia.org/wiki/Multi-touch) (technologie issue du rachat de [FingerWorks](https://en.wikipedia.org/wiki/FingerWorks) en 2005). Trois affordances du desktop disparaissent d'un coup :

- pas de clic droit
- pas de survol (`:hover`) — impossible de prévisualiser une action
- une précision de pointage bien pire (le doigt fait ~10 mm, le curseur 1 px)

Le tap-and-hold hérité des PDA devient donc essentiel. Sur iPhone OS 1.0 il sert surtout à faire apparaître la **loupe** pour placer le curseur dans un texte. Le copier/coller — et donc le menu contextuel au long press — n'arrive qu'avec [iPhone OS 3.0 en 2009](https://en.wikipedia.org/wiki/IPhone_OS_3).

[Android 1.0](https://en.wikipedia.org/wiki/Android_version_history) (2008) va plus loin et fait du long press **le** geste de menu contextuel système, disponible partout, avec vibration systématique. Les 500 ms d'AOSP datent de cette période et n'ont jamais bougé depuis.

### 2007–2016 — Le web paie l'addition : les 300 ms

Le navigateur mobile hérite du problème sous sa forme la plus visible. Comme le double-tap sert à zoomer, les navigateurs mobiles ont pendant des années appliqué un délai de 300 à 350 ms entre `touchend` et `click`, le temps de vérifier si un second tap allait suivre.

Résultat : toutes les applications web paraissent molles face aux applications natives. Des bibliothèques comme [FastClick](https://github.com/ftlabs/fastclick) (Financial Times Labs) contournent le problème en synthétisant le clic depuis `touchend`.

La sortie de crise se fait par étapes : Chrome 32 supprime le délai en 2014 pour les sites déclarant un viewport adapté, sans désactiver le pinch-to-zoom ; Firefox et IE/Edge suivent peu après, et un correctif similaire arrive dans iOS 9.3 en mars 2016. La propriété CSS [`touch-action: manipulation`](https://developer.mozilla.org/fr/docs/Web/CSS/touch-action) devient la solution propre et normalisée.

Cet épisode est la démonstration la plus nette du principe exposé plus haut : **désambiguïser un geste coûte forcément de la latence sur le geste le plus simple.**

### 2015–2019 — La parenthèse de la pression

Apple tente une autre voie : mesurer la **force** plutôt que la durée. [Force Touch](https://en.wikipedia.org/wiki/Force_Touch) arrive sur l'Apple Watch et les trackpads MacBook début 2015, puis [3D Touch sur l'iPhone 6s](https://www.techtarget.com/enterprise-software/definition/Apple-3D-Touch) en septembre 2015, couplé au [Taptic Engine](https://en.wikipedia.org/wiki/Taptic_Engine) qui simule un clic mécanique.

Sur le papier, c'est la solution élégante : plus d'attente, plus d'ambiguïté avec le scroll, un troisième niveau d'interaction gratuit. En pratique, l'échec est net — le geste est peu découvrable, peu adopté par les développeurs tiers, et coûteux en épaisseur et en prix.

L'iPhone XR (2018) est le premier à s'en passer, remplacé par le Haptic Touch ; les iPhone XS et XS Max sont les derniers à en être équipés, et à partir de l'iPhone 11 (2019) les fonctions liées sont retirées d'iOS 13 au profit du Haptic Touch, qui ne détecte aucune pression : le retour haptique dépend désormais de la durée de l'appui et non de la force appliquée.

**Retour au point de départ : le temps reste le seul discriminant.**

### 2016–aujourd'hui — Unification et reflux

Trois mouvements de fond :

**L'unification des API.** Les [Pointer Events](https://www.w3.org/TR/pointerevents3/) du W3C remplacent progressivement le couple Touch Events / Mouse Events et traitent doigt, stylet et souris de façon homogène. C'est ce que fait le jeu analysé plus bas.

**La montée de l'accessibilité.** Le WCAG 2.1 (juin 2018) introduit les critères [2.5.1](https://www.w3.org/WAI/WCAG21/Understanding/pointer-gestures.html) et [2.5.2](https://www.w3.org/WAI/WCAG21/Understanding/pointer-cancellation.html), qui condamnent frontalement le long press comme seul moyen d'accès à une fonction. Android et iOS exposent en parallèle des réglages de durée personnalisables.

**Le reflux du long press lui-même.** Le geste souffre d'un défaut structurel : il est **invisible**. Rien à l'écran n'indique qu'un élément réagit à l'appui long. Les design systems modernes ([Material 3](https://m3.material.io/), les [Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/gestures)) recommandent donc de le traiter comme un **raccourci pour utilisateurs avancés**, doublé systématiquement d'une affordance explicite : bouton « ⋮ », icône, ou glissement révélant des actions. Le long press reste utile, mais il n'est plus censé être le seul chemin.

---

# Étude de cas : le jeu w3hc.org/clic

🎮 **[Jouer](https://www.w3hc.org/clic)** · 📄 **[Code source](https://github.com/w3hc/w3hc-web/blob/main/src/app/clic/page.tsx)** · 📦 **[Dépôt w3hc/w3hc-web](https://github.com/w3hc/w3hc-web)**

Le jeu affiche une cible et demande au joueur un geste précis — clic simple, double-clic ou clic long. Comme le web n'offre aucun de ces gestes nativement, tout est reconstruit à partir des événements bruts. C'est un cas d'école : chaque concept de la première partie apparaît explicitement dans le code.

## Approche générale

Le code utilise l'API [Pointer Events](https://developer.mozilla.org/fr/docs/Web/API/Pointer_events) (`onPointerDown/Move/Up/Cancel`) plutôt que `touchstart` ou `mousedown`, ce qui unifie doigt, stylet et souris en un seul chemin de code. Stack : [Next.js](https://nextjs.org/) (App Router), [React](https://react.dev/), [Chakra UI](https://chakra-ui.com/).

```ts
LONG_PRESS_MS        = 550   // seuil d'appui long
DOUBLE_TAP_WINDOW_MS = 380   // fenêtre de double-clic
MOVE_CANCEL_PX       = 24    // touch slop
```

Comparé aux constantes natives : 550 ms au lieu de 500, 380 ms au lieu des 300 ms d'Android, 24 px de tolérance au lieu de ~8 dp. Les trois valeurs sont volontairement plus permissives — cohérent avec un outil destiné à des gens qui maîtrisent mal le geste.

## La machine à états

**`pointerdown`** capture le pointeur ([`setPointerCapture`](https://developer.mozilla.org/fr/docs/Web/API/Element/setPointerCapture), pour continuer à recevoir les événements même si le doigt sort de la cible), mémorise la position de départ, et arme un [`setTimeout`](https://developer.mozilla.org/fr/docs/Web/API/Window/setTimeout) de 550 ms.

**Si le timer expire**, `longPressFiredRef` passe à `true` et le geste `hold` est enregistré immédiatement, doigt encore posé — le comportement natif. Le `pointerup` qui suit voit ce drapeau et sort sans rien faire.

**`pointermove`** calcule la distance euclidienne depuis le point de départ et, au-delà de 24 px, annule le timer de hold et neutralise le pointeur actif. C'est le touch slop, implémenté à la main.

**`pointerup`** avant 550 ms entre dans la logique de désambiguïsation tap / double :

- pas de timer en attente → on en arme un de 380 ms qui, à expiration, enregistre `tap`
- timer déjà en attente → c'est le deuxième relâchement, on l'annule et on enregistre `double`

D'où le compromis classique : **un clic simple est confirmé avec 380 ms de retard**, puisqu'il faut attendre de savoir s'il sera suivi d'un second. Le clic long, lui, se déclenche sans latence dès le seuil franchi. Asymétrie contre-intuitive mais inévitable — c'est exactement le problème des [300 ms du web mobile](https://developer.chrome.com/blog/300ms-tap-delay-gone-away), réintroduit volontairement ici parce que le jeu a *besoin* de distinguer le double-clic.

## Ce qui neutralise le navigateur

Trois précautions indispensables, souvent oubliées :

- **[`touchAction: 'none'`](https://developer.mozilla.org/fr/docs/Web/CSS/touch-action)** — sans ça le navigateur intercepte le geste pour scroller, pincer ou zoomer, et coupe le flux de `pointermove`. La ligne la plus importante du fichier.
- **[`onContextMenu`](https://developer.mozilla.org/fr/docs/Web/API/Element/contextmenu_event)` + preventDefault()`** — empêche le menu contextuel natif du long press de s'ouvrir par-dessus le jeu.
- **[`userSelect: 'none'`](https://developer.mozilla.org/fr/docs/Web/CSS/user-select)** + variante `-webkit-` — bloque la sélection de texte et la loupe iOS.

## Le retour visuel du seuil

Un anneau [SVG](https://developer.mozilla.org/fr/docs/Web/SVG) se remplit par transition CSS sur [`stroke-dashoffset`](https://developer.mozilla.org/fr/docs/Web/SVG/Attribute/stroke-dashoffset), avec une durée pilotée par une [propriété personnalisée CSS](https://developer.mozilla.org/fr/docs/Web/CSS/Using_CSS_custom_properties) `--hold-ms` alignée sur `LONG_PRESS_MS`. Il démarre à chaque `pointerdown`, quel que soit le geste demandé, et matérialise donc en continu la frontière des 550 ms.

Le redémarrage de l'animation utilise l'astuce habituelle : retrait de la classe, `void ring.getBoundingClientRect()` pour forcer un [reflow](https://developer.mozilla.org/en-US/docs/Glossary/Reflow) synchrone, puis rajout dans un [`requestAnimationFrame`](https://developer.mozilla.org/fr/docs/Web/API/Window/requestAnimationFrame). Sans ce reflow, le navigateur regroupe les deux changements et la transition ne rejoue pas.

Ce retour visuel remplace le retour haptique natif — **aucun appel à [`navigator.vibrate()`](https://developer.mozilla.org/fr/docs/Web/API/Navigator/vibrate)** dans le code, alors que ce serait pertinent ici et supporté sur Android (mais [pas sur iOS Safari](https://caniuse.com/vibration), ce qui explique peut-être le choix). La media query [`prefers-reduced-motion`](https://developer.mozilla.org/fr/docs/Web/CSS/@media/prefers-reduced-motion) est bien prise en compte et neutralise l'anneau comme les autres animations.

## Détails notables

[`activePointerIdRef`](https://react.dev/reference/react/useRef) garantit qu'un seul pointeur est suivi à la fois : un second doigt posé pendant un appui est ignoré. Pas de multi-touch, donc pas d'ambiguïté sur qui a relâché quoi.

Le [`pointercancel`](https://developer.mozilla.org/fr/docs/Web/API/Element/pointercancel_event) est traité séparément — cas réel sur mobile quand le système reprend la main (appel entrant, geste de navigation depuis le bord). Beaucoup d'implémentations l'oublient et laissent un timer orphelin.

Décision de conception explicitement commentée dans le code : si le chronomètre de manche expire pendant qu'un `hold` est en cours, le timeout est requalifié en réussite. Le joueur faisait le bon geste, seule l'horloge l'a coupé.

## Les limites

**L'accessibilité clavier est incomplète.** `Enter` et `Espace` enregistrent toujours `tap`. Les manches `double` et `hold` sont donc mécaniquement impossibles à réussir au clavier, soit deux tiers des manches. C'est précisément le critère [WCAG 2.5.1](https://www.w3.org/WAI/WCAG21/Understanding/pointer-gestures.html) : une fonction accessible uniquement par geste pointeur. Il faudrait des touches alternatives, ou un maintien de `Espace` mesuré au [`keyup`](https://developer.mozilla.org/fr/docs/Web/API/Element/keyup_event).

**Le bouton de souris n'est pas filtré.** `handlePointerDown` ne teste pas [`e.button`](https://developer.mozilla.org/fr/docs/Web/API/MouseEvent/button), donc un clic droit maintenu enregistre un `hold`. Inoffensif ici, mais c'est le genre de détail qui compte dans une UI réelle.

**L'anneau et le timer JS sont deux horloges distinctes.** La transition CSS tourne sur le compositeur, le `setTimeout` sur la [boucle d'événements](https://developer.mozilla.org/fr/docs/Web/JavaScript/Event_loop). Sous forte charge, le timer peut dériver de quelques dizaines de millisecondes par rapport à l'anneau visuellement plein. Une implémentation plus stricte lirait [`performance.now()`](https://developer.mozilla.org/fr/docs/Web/API/Performance/now) au `pointerup` plutôt que de se fier à l'ordonnancement du timeout.

---

## Références

### Le jeu

- [w3hc.org/clic](https://www.w3hc.org/clic) — l'application
- [`src/app/clic/page.tsx`](https://github.com/w3hc/w3hc-web/blob/main/src/app/clic/page.tsx) — le code analysé
- [github.com/w3hc/w3hc-web](https://github.com/w3hc/w3hc-web) — le dépôt

### Spécifications et standards

- [W3C — Pointer Events Level 3](https://www.w3.org/TR/pointerevents3/)
- [W3C — Touch Events Level 2](https://www.w3.org/TR/touch-events/)
- [WCAG 2.1 (traduction française)](https://www.w3.org/Translations/WCAG21-fr/)
- [Comprendre le critère 2.5.1 — Gestes du pointeur](https://www.w3.org/WAI/WCAG21/Understanding/pointer-gestures.html)
- [Comprendre le critère 2.5.2 — Annulation du pointeur](https://www.w3.org/WAI/WCAG21/Understanding/pointer-cancellation.html)

### Documentation plateformes

- [Android — `ViewConfiguration`](https://developer.android.com/reference/android/view/ViewConfiguration)
- [Android — `MotionEvent`](https://developer.android.com/reference/android/view/MotionEvent)
- [Android — détecter les gestes courants](https://developer.android.com/develop/ui/views/touch-and-input/gestures/detector)
- [AOSP — source de `ViewConfiguration.java`](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/view/ViewConfiguration.java)
- [Apple — `UILongPressGestureRecognizer`](https://developer.apple.com/documentation/uikit/uilongpressgesturerecognizer)
- [Apple — Human Interface Guidelines : Gestures](https://developer.apple.com/design/human-interface-guidelines/gestures)
- [Flutter — `gestures/constants.dart`](https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/gestures/constants.dart)
- [Flutter — `GestureDetector`](https://api.flutter.dev/flutter/widgets/GestureDetector-class.html)
- [React Native — `Pressable`](https://reactnative.dev/docs/pressable)
- [Material Design 3 — Gestures](https://m3.material.io/foundations/interaction/gestures)

### MDN (web)

- [Pointer events](https://developer.mozilla.org/fr/docs/Web/API/Pointer_events)
- [`touch-action`](https://developer.mozilla.org/fr/docs/Web/CSS/touch-action)
- [`setPointerCapture()`](https://developer.mozilla.org/fr/docs/Web/API/Element/setPointerCapture)
- [`pointercancel`](https://developer.mozilla.org/fr/docs/Web/API/Element/pointercancel_event)
- [Vibration API](https://developer.mozilla.org/fr/docs/Web/API/Vibration_API)
- [`prefers-reduced-motion`](https://developer.mozilla.org/fr/docs/Web/CSS/@media/prefers-reduced-motion)

### Le délai des 300 ms

- [Chrome for Developers — 300ms tap delay, gone away](https://developer.chrome.com/blog/300ms-tap-delay-gone-away)
- [QuirksBlog — Suppressing the 300ms click delay](https://www.quirksmode.org/blog/archives/2014/04/suppressing_the.html)
- [FastClick (FT Labs)](https://github.com/ftlabs/fastclick)

### Histoire

- [The Mother of All Demos (1968, vidéo)](https://www.youtube.com/watch?v=yJDv-zdhzMY)
- [Computer History Museum — The Xerox Alto and the Birth of the Modern GUI](https://computerhistory.org/blog/the-xerox-alto-and-the-birth-of-the-modern-gui/)
- [Computer History Museum — The Lisa: Apple's Most Influential Failure](https://computerhistory.org/blog/the-lisa-apples-most-influential-failure/)
- [Microsoft Learn — Add Tap-and-Hold Support in Pocket PC Applications (2003)](https://learn.microsoft.com/en-us/previous-versions/ms839437(v=msdn.10))
- [Wikipédia — 3D Touch](https://en.wikipedia.org/wiki/3D_Touch)
- [TechTarget — What is Apple 3D Touch?](https://www.techtarget.com/enterprise-software/definition/Apple-3D-Touch)
- [AppleInsider — Apple replaces 3D Touch with Haptic Touch (2019)](https://appleinsider.com/articles/19/09/10/apple-replaces-3d-touch-with-haptic-touch-on-iphone-11-and-iphone-11-pro)
- [Computerworld — RIP Larry Tesler, inventor of copy & paste](https://www.computerworld.com/article/1615267/rip-larry-tesler-inventor-of-copy-paste.html)
