Playlab42 le laboratoire
GuidesCréer un Outil HTML

Créer un Outil HTML#

Ce guide vous accompagne dans la création d'un outil HTML standalone pour Playlab42.

Objectif#

Créer un outil simple et autonome qui sera intégré au catalogue du portail.

Caractéristiques d'un outil :

  • Un seul fichier HTML (tout inclus : CSS + JS)
  • Utilise lib/theme.css pour les styles et thèmes
  • Utilise lib/dom.js pour les utilitaires DOM
  • Utile et pratique
  • Style cohérent avec la plateforme (dark/light)

Prérequis#

  • Connaissances de base en HTML, CSS, JavaScript
  • Docker installé (make serve pour tester)

Structure d'un outil#

Un outil Playlab42 se compose de :

tools/
├── mon-outil.html     # L'outil (fichier unique)
└── mon-outil.json     # Métadonnées (optionnel)

Étapes#

1. Créer le fichier HTML#

Créez un fichier tools/mon-outil.html avec la structure suivante :

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Mon Outil - Playlab42</title>
  <link rel="stylesheet" href="../lib/theme.css">
  <style>
    /* Vos styles ici - utilisez les variables CSS */
  </style>
  <script type="module">
    import { initTheme } from '../lib/theme.js';
    initTheme();
  </script>
</head>
<body>
  <!-- Interface ici -->
  <script type="module">
    import { $, on, escapeHtml, debounce } from '../lib/dom.js';
    // Logique ici
  </script>
</body>
</html>

2. Utiliser les variables CSS du thème#

Importez lib/theme.css pour avoir accès aux variables de design :

/* Variables disponibles */
--color-bg           /* Fond principal */
--color-bg-secondary /* Fond secondaire */
--color-bg-card      /* Fond des cartes */
--color-text         /* Texte principal */
--color-text-muted   /* Texte secondaire */
--color-accent       /* Couleur d'accent */
--color-accent-hover /* Accent au survol */
--color-success      /* Succès */
--color-error        /* Erreur */
--color-border       /* Bordures */
--space-sm/md/lg/xl  /* Espacements */
--radius-sm/md/lg    /* Rayons de bordure */
--transition-normal  /* Transitions */
--font-family        /* Police système */
--font-mono          /* Police monospace */

Le thème (dark/light) est géré automatiquement via lib/theme.js.

3. Créer le manifest (optionnel mais recommandé)#

Créez tools/mon-outil.json :

{
  "id": "mon-outil",
  "name": "Mon Outil",
  "description": "Description courte de l'outil",
  "tags": ["utility", "dev"],
  "author": "Votre nom",
  "icon": "🔧",
  "version": "1.0.0"
}

Champs du manifest :

ChampObligatoireDescription
idOuiIdentifiant unique (kebab-case)
nameOuiNom affiché
descriptionOuiDescription courte
tagsNonTags pour filtrage
authorNonAuteur
iconNonEmoji pour la vignette
versionNonVersion de l'outil

4. Régénérer le catalogue#

make npm CMD="run build:catalogue"

5. Tester#

make serve
# Ouvrir http://localhost:3000

Exemple complet : Compteur de mots#

Voici un outil simple qui compte les mots et caractères d'un texte.

tools/word-counter.html#

<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Compteur de mots - Playlab42</title>
  <style>
    /* Variables de couleur Playlab42 */
    :root {
      --bg: #1a1a2e;
      --bg-secondary: #16213e;
      --text: #eee;
      --text-muted: #888;
      --accent: #e94560;
      --border: #333;
    }

    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    body {
      font-family: system-ui, sans-serif;
      background: var(--bg);
      color: var(--text);
      min-height: 100vh;
      display: flex;
      flex-direction: column;
    }

    /* En-tête */
    header {
      padding: 1rem;
      background: var(--bg-secondary);
      border-bottom: 1px solid var(--border);
    }

    header h1 {
      font-size: 1.25rem;
    }

    header h1 span {
      color: var(--accent);
    }

    /* Zone principale */
    main {
      flex: 1;
      display: flex;
      flex-direction: column;
      padding: 1rem;
      gap: 1rem;
    }

    /* Zone de texte */
    textarea {
      flex: 1;
      padding: 1rem;
      background: var(--bg-secondary);
      border: 1px solid var(--border);
      border-radius: 8px;
      color: var(--text);
      font-size: 1rem;
      resize: none;
      outline: none;
    }

    textarea:focus {
      border-color: var(--accent);
    }

    textarea::placeholder {
      color: var(--text-muted);
    }

    /* Statistiques */
    .stats {
      display: flex;
      gap: 1.5rem;
      flex-wrap: wrap;
    }

    .stat {
      background: var(--bg-secondary);
      padding: 1rem 1.5rem;
      border-radius: 8px;
      text-align: center;
    }

    .stat-value {
      font-size: 2rem;
      font-weight: bold;
      color: var(--accent);
    }

    .stat-label {
      font-size: 0.875rem;
      color: var(--text-muted);
      margin-top: 0.25rem;
    }

    /* Pied de page */
    footer {
      padding: 0.5rem 1rem;
      background: var(--bg-secondary);
      border-top: 1px solid var(--border);
      font-size: 0.75rem;
      color: var(--text-muted);
    }
  </style>
</head>
<body>
  <header>
    <h1><span>📝</span> Compteur de mots</h1>
  </header>

  <main>
    <textarea
      id="input"
      placeholder="Tapez ou collez votre texte ici..."
    ></textarea>

    <div class="stats">
      <div class="stat">
        <div class="stat-value" id="words">0</div>
        <div class="stat-label">Mots</div>
      </div>
      <div class="stat">
        <div class="stat-value" id="chars">0</div>
        <div class="stat-label">Caractères</div>
      </div>
      <div class="stat">
        <div class="stat-value" id="chars-no-space">0</div>
        <div class="stat-label">Sans espaces</div>
      </div>
      <div class="stat">
        <div class="stat-value" id="lines">0</div>
        <div class="stat-label">Lignes</div>
      </div>
    </div>
  </main>

  <footer>
    Playlab42 - Compteur de mots
  </footer>

  <script>
    // Éléments DOM
    const inputEl = document.getElementById('input');
    const wordsEl = document.getElementById('words');
    const charsEl = document.getElementById('chars');
    const charsNoSpaceEl = document.getElementById('chars-no-space');
    const linesEl = document.getElementById('lines');

    /**
     * Compte les statistiques du texte
     */
    function countStats() {
      const text = inputEl.value;

      // Mots : séparer par espaces et filtrer les vides
      const words = text.trim() ? text.trim().split(/\s+/).length : 0;

      // Caractères
      const chars = text.length;

      // Caractères sans espaces
      const charsNoSpace = text.replace(/\s/g, '').length;

      // Lignes
      const lines = text ? text.split('\n').length : 0;

      // Mise à jour affichage
      wordsEl.textContent = words;
      charsEl.textContent = chars;
      charsNoSpaceEl.textContent = charsNoSpace;
      linesEl.textContent = lines;
    }

    // Écouter les changements
    inputEl.addEventListener('input', countStats);

    // Focus initial
    inputEl.focus();
  </script>
</body>
</html>

tools/word-counter.json#

{
  "id": "word-counter",
  "name": "Compteur de mots",
  "description": "Compte les mots, caractères et lignes d'un texte",
  "tags": ["utility", "text"],
  "author": "Playlab42",
  "icon": "📝",
  "version": "1.0.0"
}

Bonnes pratiques#

Structure du code#

  1. Un seul fichier : Tout le CSS et JS dans le HTML
  2. Imports : Utilisez lib/theme.css, lib/dom.js, lib/theme.js
  3. Responsive : Fonctionne sur mobile et desktop

Style#

  1. Variables CSS : Utilisez var(--color-bg), etc. depuis theme.css
  2. Support thèmes : Les variables s'adaptent automatiquement dark/light
  3. Transitions : Ajoutez transition: background-color var(--transition-normal) pour une transition fluide
  4. Cohérence : Header avec titre, main, footer
  5. Accessibilité : Labels, placeholders, contraste

JavaScript#

  1. Utilitaires DOM : Utilisez $(), on(), escapeHtml() depuis lib/dom.js
  2. Sécurité XSS : Toujours escapeHtml() sur les entrées utilisateur
  3. Commentaires : En français
  4. Fonctions pures : Faciles à tester

Performance#

  1. Debounce : Pour les événements fréquents (input)
  2. Pas de boucles infinies : Attention aux watchers
  3. Léger : Pas de bibliothèques lourdes

Pour aller plus loin#

Ajouter des raccourcis clavier#

document.addEventListener('keydown', (e) => {
  if (e.ctrlKey && e.key === 's') {
    e.preventDefault();
    saveData();
  }
});

Sauvegarder dans localStorage#

// Sauvegarder
localStorage.setItem('mon-outil-data', JSON.stringify(data));

// Charger
const saved = localStorage.getItem('mon-outil-data');
if (saved) {
  data = JSON.parse(saved);
}

Charger depuis l'URL#

const params = new URLSearchParams(window.location.search);
const value = params.get('data');
if (value) {
  inputEl.value = decodeURIComponent(value);
}

Voir aussi#