Créer un mini-jeu auto-contenu (HTML/CSS/JS dans un seul fichier) jouable sur mobile et intégrable directement dans l’application GeoGaming via un champ embeddedCode (type LONGTEXT).
🧱 1. Règles importantes de base
⚙️ Structure technique
- Un seul fichier <!DOCTYPE html> contenant HTML, CSS et JS inline.
- Aucune dépendance externe obligatoire (librairie CDN autorisée si le jeu reste fonctionnel sans).
- Responsive mobile : utilise clamp() pour tailles et espacements.
- Doit fonctionner dans une iframe ou WebView sans bloquer le scroll de la page parente.
- Pas d’images externes fragiles → préfère SVG inline ou emojis.
- Pas de boucle infinie sans setTimeout ou requestAnimationFrame.
- Éviter les alert()/prompt(), qui bloquent la WebView.
🪣 Communication avec GeoGaming
Quand le joueur termine le jeu, le bouton “Continuer” doit envoyer :
window.postMessage({
completed: true,
showModal: false,
activityKey: « nom-du-jeu », // stable, kebab-case
points: 2000, // score final (entier >= 0)
data: answers // optionnel : objet contenant les réponses
}, « * »);
Si tu collectes des réponses :
const answers = { q1: « Oui », q2: « … » };
🕹️ Bouton “Passer”
- Toujours visible dès le début.
- Affiche une confirmation avant de quitter.
- Envoie 0 point et pas de modale de fin :
window.postMessage({
completed: true,
showModal: false,
activityKey: « nom-du-jeu »,
points: 0
}, « * »);
💡 2. Conseils et bonnes pratiques
📱 Événements tactiles (mobile first)
- Favoriser touchstart ou pointerdown plutôt que click,
car les événements click ont un léger délai sur mobile et sont moins réactifs.
Exemple :
element.addEventListener(‘pointerdown’, handleTap);
// ou fallback :
element.addEventListener(‘touchstart’, handleTap, { passive: true });
- Si tu veux compatibilité totale, fais :
element.addEventListener(‘pointerdown’, handleTap);
element.addEventListener(‘click’, handleTap);
🎨 Design et ergonomie
- Style épuré, clair et fun.
- Éléments interactifs ≥ 48×48 px (recommandation Google/Apple).
- Animations simples : transform, opacity, scale.
- Utilise un conteneur racine (ex. .gg-root) pour isoler ton CSS.
- Ne bloque jamais le scroll global (overflow:hidden sur body → ❌).
📱 Bonnes pratiques mobile
- Utilise pointerdown au lieu de click pour la réactivité.
- Active { passive: true } sur les écouteurs touchstart pour de meilleures perfs.
- Évite les interactions complexes type drag sur mobile si non indispensables.
- Teste toujours sur un vrai écran tactile avant validation.
📦 Variables CSS conseillées
:root {
–gg-radius: 20px;
–gg-card-bg: rgba(0,0,0,.75);
–gg-card-brd: rgba(255,255,255,.25);
–gg-shadow: 0 10px 30px rgba(0,0,0,.35);
–gg-gap: clamp(10px, 3vw, 16px);
–gg-pad: clamp(12px, 3.5vw, 20px);
–gg-font: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
–gg-primary: #3478f6;
–gg-success: #4caf50;
–gg-danger: #e53935;
–gg-gold: #ffd700;
}
🧠 Accessibilité
- Contrastes lisibles et tailles de police dynamiques (clamp()).
- Focus visibles (outline ou box-shadow doux).
- Actions clavier (Enter/Space) si pertinent.
⚠️ À éviter absolument
- overflow:hidden sur html, body
- position: fixed plein écran sans fermeture propre
- Autoplay audio avant interaction
- eval() ou appels réseau
- Ressources hébergées ailleurs sans garantie de persistance
🧩 3. Exemple complet minimal — “Tap to Catch”
Un mini-jeu où le joueur tape sur une cible plusieurs fois pour gagner des points.
Utilise pointerdown pour être fluide sur mobile, et envoie le résultat à GeoGaming.
<!DOCTYPE html><html lang= »fr »><head>
<meta charset= »utf-8″><meta name= »viewport » content= »width=device-width,initial-scale=1″/>
<title>Mini-jeu GeoGaming – Tap to Catch</title>
<style>
:root{
–gg-radius:20px;–gg-card-bg:rgba(0,0,0,.75);–gg-card-brd:rgba(255,255,255,.25);
–gg-shadow:0 10px 30px rgba(0,0,0,.35);–gg-gap:clamp(10px,3vw,16px);
–gg-pad:clamp(12px,3.5vw,20px);–gg-font:system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif;
–gg-primary:#3478f6;–gg-danger:#e53935;–gg-gold:#ffd700;
}
body{margin:0;font-family:var(–gg-font);color:#fff;}
.gg-scene{padding:clamp(8px,2vw,24px);display:flex;justify-content:center;align-items:center;}
.gg-wrap{width:min(920px,96vw);min-height:min(86vh,900px);background:var(–gg-card-bg);
border:1px solid var(–gg-card-brd);border-radius:var(–gg-radius);box-shadow:var(–gg-shadow);
display:flex;flex-direction:column;overflow:auto;}
.gg-row{display:flex;justify-content:space-between;align-items:center;padding:var(–gg-pad);}
.gg-btn{border:0;border-radius:999px;font-weight:700;padding:clamp(12px,3vw,16px) clamp(24px,6vw,32px);
cursor:pointer;transition:transform .1s;}
.gg-btn–primary{background:var(–gg-primary);color:#fff;}
.gg-btn–danger{background:var(–gg-danger);color:#fff;}
.gg-btn:hover{transform:translateY(-2px);}
.gg-play{flex:1;display:flex;align-items:center;justify-content:center;}
.gg-target{width:64px;height:64px;border-radius:50%;background:#fff;color:#000;
display:grid;place-items:center;user-select:none;}
.gg-modal{position:fixed;inset:0;display:none;align-items:center;justify-content:center;
background:rgba(0,0,0,.75);backdrop-filter:blur(6px);z-index:1000;}
.gg-modal__card{width:min(90vw,460px);background:linear-gradient(135deg,#667eea 0%,#764ba2 100%);
border-radius:24px;text-align:center;padding:clamp(28px,6vw,48px);}
.gg-points{font-size:clamp(2rem,6vw,2.6rem);font-weight:800;color:var(–gg-gold);margin:.6rem 0;}
</style>
</head><body>
<div class= »gg-scene »>
<div class= »gg-wrap »>
<div class= »gg-row »>
<div id= »gg-hud »>Score : <strong id= »gg-score »>2000</strong></div>
<button id= »gg-skip » class= »gg-btn gg-btn–danger »>Passer</button>
</div>
<div class= »gg-play » id= »gg-play »><div class= »gg-target » id= »target »>🎯</div></div>
</div>
</div>
<div class= »gg-modal » id= »gg-end »>
<div class= »gg-modal__card »>
<h2>Bravo !</h2>
<div class= »gg-points » id= »gg-points »>2000 points</div>
<p>Tu as terminé l’activité.</p>
<button id= »gg-continue » class= »gg-btn gg-btn–primary »>Continuer</button>
</div>
</div>
<script>
(() => {
const activityKey = « tap-to-catch »;
let points = 2000;
const $ = s => document.querySelector(s);
const scoreEl = $(‘#gg-score’), target = $(‘#target’),
play = $(‘#gg-play’), modal = $(‘#gg-end’), pts = $(‘#gg-points’);
let taps=0, max=5;
function move() {
const r=play.getBoundingClientRect(), s=64;
const x=Math.random()*(r.width-s), y=Math.random()*(r.height-s);
target.style.transform=`translate(${x}px,${y}px)`;
}
move();
// Pointerdown = plus fluide sur mobile
const handleTap = () => {
taps++; points+=200; scoreEl.textContent=points;
taps>=max?end():move();
};
target.addEventListener(‘pointerdown’, handleTap);
function end(){
pts.textContent=points+ » points »;
modal.style.display=’flex’;
}
$(‘#gg-continue’).addEventListener(‘click’,()=>window.postMessage({
completed:true,showModal:false,activityKey,points
}, »* »),{once:true});
$(‘#gg-skip’).addEventListener(‘click’,()=>{
if(confirm(« Voulez-vous quitter ? »))
window.postMessage({completed:true,showModal:false,activityKey,points:0}, »* »);
});
})();
</script>
</body></html>
✅ En résumé :
- Utilise pointerdown sur mobile.
- Garde une interface simple et légère.
- Envoie ton postMessage uniquement à la fin.
- Et teste toujours sur un vrai écran tactile avant de valider ton jeu.
🤖 4. Assistant ChatGPT
https://chatgpt.com/g/g-68f232db601c8191bfaa74a7c96d3df3-creation-de-mini-jeu-v2