Design system « Lagon »

La source de vérité unique de Vingt Mille. Tout agent — humain ou IA — qui développe la landing page, l'application ou un support de communication applique ce document. En cas de doute, ce document gagne ; en cas de manque, on le complète avant de coder.

tokens : assets/tokens.css (CSS) · tokens.json (machine-readable) · composants : assets/components.css

Principes

  1. De 7 à 77 ans. Corps de texte 17 px minimum, cibles tactiles ≥ 44 px, contrastes AA partout, une action principale par vue.
  2. Clair comme un lagon. Fonds clairs et chauds (écume, sable), l'eau en décor, le corail pour agir. Jamais de thème sombre sur les surfaces produit.
  3. La technique s'efface. Les animations accompagnent la lecture (glisse d'eau), ne la précèdent jamais. prefers-reduced-motion est un contrat.
  4. Vocabulaire de plongée, juste et accessible. Palier, palanquée, mise à l'eau — toujours compréhensibles en contexte par un non-plongeur.
  5. Tokens ou rien. Aucune valeur en dur (couleur, taille, ombre) : tout vient de tokens.css. Une valeur manquante = une PR sur les tokens, pas une exception locale.

Couleurs

Trois familles : la plage (fonds), l'eau (identité, décor) et le corail/soleil (action, chaleur). L'encre écrit tout.

Écume--vm-ecume · #FBF7F0
fond de page
Sable--vm-sable · #F3EBDD
sections alternées
Eau 100--vm-eau-100 · #DCF1F4
hovers, surfaces douces
Eau 200--vm-eau-200 · #B8E3E9
vagues, décor
Lagon 300--vm-lagon-300 · #6FCBD4
bulles, décor
Lagon 500--vm-lagon-500 · #0FA3B1
identité — jamais en texte
Lagon 700--vm-lagon-700 · #0A6973
liens, texte coloré (AA)
Lagon 900--vm-lagon-900 · #07444C
footer, fonds profonds
Corail 500--vm-corail-500 · #FF7059
CTA (texte : corail-ink)
Corail ink--vm-corail-ink · #47140A
texte sur corail
Soleil 400--vm-soleil-400 · #FFD166
badges, surlignage
Encre--vm-encre · #0B3C49
texte principal
Encre douce--vm-encre-soft · #41626D
texte secondaire

Contrastes vérifiés (WCAG 2.2)

CombinaisonRatioVerdictUsage autorisé
encre / écume≈ 10,9 : 1AAATexte courant, titres
encre-soft / écume≈ 5,9 : 1AATexte secondaire
lagon-700 / écume≈ 5,6 : 1AALiens, texte coloré
corail-ink / corail-500≈ 7,3 : 1AA+Boutons primaires
eau-100 / lagon-900≈ 9,5 : 1AAAFooter
lagon-500 / écume≈ 2,8 : 1Décor uniquement — jamais du texte
encre-faint / écume≈ 4,0 : 1limiteLégendes ≥ 20 px uniquement

✓ Faire

  • Texte = encre ou encre-soft ; liens = lagon-700
  • CTA corail-500 + texte corail-ink
  • Soleil pour la mise en avant ponctuelle (1 élément max par vue)

✗ Éviter

  • Texte en lagon-500 ou blanc sur corail-500
  • Couleur hors palette, dégradés non documentés
  • Fond sombre hors footer

Typographie

Gérez moins. Plongez plus.

Young Serif — titres (h1–h3, prix)

Nunito Sans — corps de texte 17 px, humaniste et ronde, extrêmement lisible. Graisse 700 pour appuyer, lien dans le texte.

IBM Plex Mono — −20 m · 19 € TTC/mois · données

TokenValeurUsage
--vm-text-3xlclamp(2.375rem → 4rem)h1, un seul par page
--vm-text-2xlclamp(1.75rem → 2.625rem)h2 de section
--vm-text-xlclamp(1.375rem → 1.75rem)h3, titres de carte
--vm-text-l1.25remLede, intro de section
--vm-text-m1.0625rem (17 px)Corps — taille plancher du texte courant
--vm-text-s / -xs15 px / 13 pxTexte dense, légendes (jamais des paragraphes)

✓ Faire

  • Young Serif réservée aux titres et aux prix
  • Webfonts en font-display: swap ; les fallbacks (Georgia, system-ui) font partie du contrat
  • text-wrap: balance sur les titres

✗ Éviter

  • Young Serif en corps de texte ou en italique
  • Texte courant sous 17 px
  • Plus de deux graisses par vue

Espacements, rayons, ombres, breakpoints

Échelle d'espacement (base 4 px)

--vm-space-1…10 : 4 · 8 · 12 · 16 · 24 · 32 · 48 · 64 · 96 · 128 px

Rayons & ombres

radius-s 10
radius-m 16
radius-l 24
shadow-s
shadow-m
shadow-l

Les ombres sont teintées d'encre (jamais de gris neutre). Breakpoints mobile-first : 30rem, 48rem, 64rem, 80rem.

Boutons & liens

<a class="vm-btn vm-btn--primary" href="…">Essayer gratuitement</a>
<a class="vm-btn vm-btn--secondary" href="…">Voir les tarifs</a>
<a class="vm-link" href="…">Lien dans le texte</a>

✓ Faire

  • Un seul bouton primaire (corail) par vue
  • Libellés à l'infinitif, orientés bénéfice (« Essayer gratuitement »)
  • Min-height 44 px déjà garanti par le composant

✗ Éviter

  • Deux CTA corail côte à côte
  • « Cliquez ici », « Soumettre »
  • Bouton pour une navigation simple (utiliser .vm-link)

Badges

Niveau N2 Populaire ★ 2 mois offerts CACI à jour Expire bientôt CACI expiré
<span class="vm-badge vm-badge--ok">CACI à jour</span>

Les variantes sémantiques (ok / alerte / erreur) sont réservées à des états réels de données — jamais décoratives.

Cartes

Calendrier du club

Sorties, formations, piscine : tout le programme, les places restantes et l'équipe du jour.

<div class="vm-card vm-card--lift">
  <span class="vm-card__icon" aria-hidden="true">📅</span>
  <h3>Titre</h3><p>Texte.</p>
</div>

<div class="vm-card vm-card--pricing vm-card--featured">
  <span class="vm-badge vm-badge--corail">Recommandé</span>
  <h3>Exploration</h3>
  <p class="vm-price">19 €<small> TTC/mois</small></p>
  <ul><li>Inclus</li><li class="is-off">Non inclus</li></ul>
  <a class="vm-btn vm-btn--primary" href="…">Choisir</a>
</div>

Une seule carte --featured par grille tarifaire. --lift uniquement si la carte entière est cliquable ou introduit une action.

Formulaires

Tel qu'il apparaîtra pour vos membres.

Cette adresse semble incomplète — il manque le domaine.

<div class="vm-field">
  <label for="nom">Nom du club</label>
  <input class="vm-input" id="nom" type="text">
  <p class="vm-field__hint">Aide contextuelle.</p>
</div>
<!-- erreur : .vm-field--error + aria-invalid + aria-describedby -->

✓ Faire

  • Label visible toujours (jamais placeholder seul)
  • Messages d'erreur en français complet, qui expliquent comment corriger
  • aria-describedby relie erreur et champ

✗ Éviter

  • Erreur signalée par la couleur seule
  • Validation agressive pendant la frappe
  • Champs obligatoires non signalés

Alertes & tableaux

ℹ️ La sortie de samedi est visible des membres depuis le 2 juin.
Votre inscription à « Calanque des Anges » est confirmée.
⚠️ Votre CACI expire dans 23 jours — pensez à le renouveler avant la sortie du 12 juillet.
Cette sortie est réservée aux niveaux N2 et plus.

Tableau : voir .vm-table (utilisé dans cette page). Sur mobile, préférer des cartes empilées à un tableau scrollable quand il y a plus de 3 colonnes.

FAQ (accordéon natif)

Mes données sont-elles récupérables si je change d'outil ?

Oui, intégralement et à tout moment : export CSV et JSON inclus dans toutes les offres, y compris la gratuite. La réversibilité est un droit, pas une option.

Exemple ouvert

Basé sur <details>/<summary> : accessible clavier nativement, zéro JavaScript requis.

Profondeur — la signature Vingt Mille

Héritée de l'étude architecturale : la page est une plongée. Trois composants la matérialisent :

  • La jauge .vm-depth (fixe à droite, ≥ 64rem) affiche la « profondeur » de lecture, alimentée par JS (data-depth-max sur le body). Décorative : aria-hidden="true".
  • Les marqueurs de palier .vm-palier ouvrent chaque section (« Palier −5 m · Le calendrier »).
  • Les vagues .vm-wave (SVG inline) séparent les sections ; le fond s'assombrit imperceptiblement d'une section à l'autre (écume → eau-050 → eau-100) pour matérialiser la descente, sans jamais quitter le thème clair.
Palier −5 m · Le calendrier
<span class="vm-palier">Palier −5 m · Le calendrier</span>

<div class="vm-depth" aria-hidden="true">
  <div class="vm-depth__value" data-depth-value>0 m</div>
  <div class="vm-depth__track"><div class="vm-depth__fill" data-depth-fill></div></div>
</div>

Mouvement

UsageRecette
Révélation au scroll.vm-reveal — scroll-driven animation CSS (animation-timeline: view()) avec repli IntersectionObserver (classe .in ajoutée par main.js)
Micro-interactionsBoutons : lift −2 px (--vm-ease-spring, 160 ms) ; cartes : lift −4 px (280 ms)
DécorBulles .vm-bubble en ascension lente (20–35 s), maximum 6 par page
InterditsParallaxe agressive, autoplay vidéo, animation > 600 ms sur du contenu, tout mouvement sous prefers-reduced-motion

Anatomie d'une page

  1. .vm-nav sticky (72 px)
  2. Sections .vm-section (padding vertical space-9), contenu dans .vm-container (max 72rem) ou .vm-container--text (46rem) pour la prose
  3. Chaque section : .vm-palierh2 → lede → contenu en .vm-grid--2/3
  4. Alternance des fonds : écume / sable / eau-050 (jamais deux fonds identiques adjacents), vagues en jonction
  5. .vm-footer (lagon-900)

Hiérarchie : un seul h1 par page ; les niveaux ne sautent jamais (h2 → h3, pas h2 → h4).

Voix, ton, iconographie

  • Voix : chaleureuse, concrète, modeste. On vouvoie les clubs. Vocabulaire plongée juste mais toujours compréhensible hors contexte.
  • Jamais de comparaison négative avec un autre outil, nommément ou par allusion. On décrit ce qu'on apporte.
  • Icônes : pictogrammes au trait 2 px, bouts arrondis, monochromes (encre ou lagon-700), posés sur pastille .vm-card__icon. Les emojis sont tolérés en contenu éditorial, pas dans l'interface applicative.
  • Nombres : typographie française (espace insécable avant € et %, « 19 € », « −20 m »), IBM Plex Mono pour les données alignées.

Checklist de l'agent (avant toute livraison)

  1. Toutes les valeurs viennent de tokens.css — zéro hex, taille ou ombre en dur.
  2. Un seul h1, hiérarchie de titres sans saut, landmarks (header/nav/main/footer) présents.
  3. Texte courant ≥ 17 px, contrastes conformes au tableau §Couleurs, focus visible sur tout élément interactif.
  4. Cibles tactiles ≥ 44 px, navigation 100 % clavier, prefers-reduced-motion respecté.
  5. Un seul CTA primaire corail par vue ; libellés orientés bénéfice.
  6. Images : SVG d'abord, alt systématique (vide si décorative + aria-hidden).
  7. Aucune dépendance externe obligatoire : la page doit rester fonctionnelle sans réseau (fallbacks de police inclus).
  8. Ton : pas de jargon inexpliqué, pas de comparaison négative, prix toujours en TTC.