Aller au contenu

Règles d'usage

Finition retenue. La marque du bon exemple est neutre, avec une coche cyan. L'exemple à écarter garde la même marque, avec une croix d'erreur.

Visuel

Un accent, un vert, aucun arc-en-ciel. Le verre et le halo cyan ont chacun un seul endroit.

Un seul accent cyan

YNÀ faire

Un mot, ou la seconde ligne

Je construis des systèmes qui tiennent en production.

Le cyan reste sur le lien, l'anneau de focus, le point de statut, ou cette seconde ligne. Doc4Geeks remplace seulement la valeur de l'accent.

YNÀ éviter

Titre entier, ou plusieurs bleus

Je construis des systèmes qui tiennent en production.

Pas de dégradé sur tout le titre. Pas de second bleu, pas de violet, pas d'ambre. Le dégradé autorisé va du blanc #fafafa au cyan.

WhatsApp un bouton, un vert

YNÀ faire

Vert plus contour

Le libellé est en encre #05070a sur #25d366. « Me contacter » dans la barre reste blanc. Le survol éclaircit le vert, sans nouveau token.

YNÀ éviter

Deux verts dans la même vue

Deux boutons verts pour écrire. Le second doit être un contour ou un lien. Le vert ne sert à rien d'autre : pas un succès, pas un tag, pas une catégorie.

<a class="yc-btn yc-btn--wa" href="https://wa.me/">
  <!-- SANITY: siteSettings.whatsappUrl -->
  Écrire sur WhatsApp
</a>
<a class="yc-btn yc-btn--outline" href="/projets">Voir mes projets</a>
Catégories neutres

YNÀ faire

Même filet pour chaque carte

n8n Docker Supabase En veille
Doc4Shop

Catalogue en ligne, commande par WhatsApp.

YNÀ éviter

Teinte par projet ou par outil

violet ambre vert bleu
Filet d'accent par carte

Interdit. L'accent n'est pas une couleur de catégorie.

Bento cartes monochromes, pas de couleur par case

YNÀ faire

Même surface, même filet

Doc4GeeksGrande carte
Doc4ShopCarte moyenne
Campus Cloud
Assistant IA

La grille change de taille, pas de teinte. Filet --yc-line, fond surface 1, sur les quatre cases.

YNÀ éviter

Une couleur par case

Pas de filet cyan, vert, bleu ou violet pour distinguer Doc4Geeks, Doc4Shop ou un type de système. L'accent n'est pas une légende de grille.

Verre deux endroits

YNÀ faire

Header, et cartes flottantes

Yoane Ngassam

Le header sticky et les panneaux du hero cinéma. Partout ailleurs, surface 1 opaque.

Doc4Geeks héberge les projets étudiants. La carte de section ne floute pas l'arrière-plan.

YNÀ éviter

Verre sur une section

Cette section ne doit pas être en verre. Le flou est réservé au header et aux cartes UI du hero.
Halo sur une carte

Le glow cyan ne va que sur le survol du CTA principal et sur l'anneau de focus.

Visage sans texte dessus

YNÀ faire

Légende sous la photo

Portrait de Yoane Ngassam, basse lumière, page À propos.
Le texte reste sous le cadre. Le scrim assombrit le bas de la photo, il ne porte pas une phrase.

YNÀ éviter

Cartouche sur le visage

Yoane Ngassam, portrait d'intervenant, exemple avec un texte posé sur le visage.

Texte posé sur le visage

Images toujours en cover

YNÀ faire

Cadre 16/10, cover

Couverture du projet Doc4Geeks.
Le cadre coupe. L'image n'est pas étirée.

YNÀ éviter

Étirement

Couverture Doc4Geeks étirée, exemple à éviter.
object-fit: fill déforme le carré dans un 16/10.
.yc-project__media { aspect-ratio: 16 / 10; }
.yc-project__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

Accessibilité

Ratios de la palette à douze couleurs. Seuils WCAG inclusifs : 4.5:1 pour le corps, 3:1 pour le grand texte.

Contraste paires réelles
AperçuPaireRatioCorps
TitreTexte 1 #fafafa sur toile #05070a19.32AA et AAA
CorpsTexte 2 #a1a1aa sur toile7.87AA et AAA
CorpsTexte 2 sur surface 1 #0f11147.38AA et AAA
MétaAncien texte 3 #71717a sur toile (remplacé par #8a8a93)4.17Échoue en corps
MétaTexte 3 sur surface 13.91Échoue en corps
LienAccent #22d3ee sur toile11.16AA et AAA
Toile #05070a sur #25d36610.17AA et AAA
Texte 1 sur WhatsApp, illisible1.90Échoue
ErreurDanger #c27c7c sur surface 15.82AA, pas AAA

Texte 3 reste la méta, 12px, jamais une phrase. Une phrase est en texte 2, au moins 14px. Doc4Geeks, accent #06b6d4 sur la toile : 8.31:1. Le bouton blanc du header porte l'encre #05070a.

États repos, survol, focus, actif
  • Le survol passe en surface 2 ou assombrit le fond. Le texte ne devient pas plus proche du fond.
  • Le focus est un anneau 2px cyan, décalé de 2px, avec un halo faible. La classe .is-focus force le même anneau sur la planche.
  • Actif : translation de 1px. Inactif : opacité 0.4. Chargement : spinner neutre, libellé visible, aria-busy.
Cibles 44px

YNÀ faire

44px minimum

YNÀ éviter

28px sur une page

Envoyer

.yc-btn--sm, 36px, reste dans les outils denses. Les boutons de page font au moins 44px. Le CTA de page peut monter à 48px.

Mouvement prefers-reduced-motion

Le point « Ouvert aux échanges et aux interventions » ne pulse pas. Les fondus durent 150 à 240 ms. La parallaxe légère ne vit que sur le hero. Si le système demande moins de mouvement, translation, parallaxe et spinner s'arrêtent. Le contenu est visible sans JavaScript.

Alt contenu ou décor

YNÀ faire

Nom et lieu

alt="Yoane Ngassam en veste sombre, lumière basse, dans un intérieur peu éclairé."

YNÀ éviter

Alt vide sur un portrait

Un portrait de page a un alt. L'alt vide est réservé au décor et aux panneaux dont les chiffres sont déjà peints, comme ui-analytics.webp.

Titres un h1, sans saut

YNÀ faire

h1, puis h2, puis h3

h1 À propos

h2 Parcours

h3 Lead Technical Engineer

YNÀ éviter

h1 puis h3

h1 Blog

h3 sans h2 de section

  • Header et footer ne portent pas de h1. Ils sont identiques d'un template à l'autre.
  • Le hero cinéma ou le hero de page porte le seul h1.

Contenu

Première personne, vouvoiement, phrases concrètes. Les faits viennent du contenu autorisé.

Ton je, et vous

YNÀ faire

Phrase concrète

Je construis des logiciels, de l'automatisation et de l'IA qui tiennent en production, et j'accompagne les équipes jusqu'à ce qu'elles s'en servent vraiment.

YNÀ éviter

Slogan creux

Pas de formule du type « sans compromis » ou « zéro échec ». On nomme le système : CRM, n8n, Docker, Doc4Shop.

Ponctuation pas de tiret long

YNÀ faire

Deux-points, « à », point

Lead Technical Engineer, févr. 2026 à aujourd'hui, Yaoundé, hybride.

YNÀ éviter

Tiret long ou demi-cadratin

Ne pas couper une phrase avec un tiret long. Réécrire avec un point, une virgule ou deux-points. Les dates s'écrivent avec « à ».

Faits parcours, BTS, chiffres

YNÀ faire

Ligne de parcours seule

L'employeur actuel n'apparaît que dans la timeline, sans client, sans pourcentage, sans logo. La ligne Esmata dit « Major BTS 2025 ». Les seuls comptes repris de l'ancien portfolio sont « 10+ projets » et « 9 secteurs ».

YNÀ éviter

Client, note, vie privée

Pas de nom de client, pas de « Major national », pas de note, pas de phrase sur la famille ou le domicile. Yaoundé reste le cadre de travail. Pas de chiffre inventé.

  • Pas de texte entre crochets à compléter, pas de faux latin. L'exemple dans un champ dit « Votre nom », et le label reste au-dessus.
  • La pastille du hero dit « Ouvert aux échanges et aux interventions », pas « Disponible pour missions ».
  • Le CITS26 se raconte au passé. Aucune autre intervention n'est inventée.

Mapping Sanity

Chaque classe yc- pointe vers un document ou un objet. Le lien WhatsApp ne vit que dans siteSettings.whatsappUrl.

Composant schéma
ComposantSchémaChamps
Bouton .yc-btnobjet yc.buttonlabel, href, variant primary, wa, outline, ghost ou link, size sm, md ou lg
Badge .yc-badgestring statusopen ou past. Libellé ouvert verrouillé. Rendu neutre ou accent, jamais coloré par type.
Tag .yc-tagtags[]string. Exemples : n8n, Supabase, Docker. Pas de teinte.
Champ .yc-fieldcontactFormLibellés fixes. Pas de champ libre en phase 1.
Logo .yc-logositeSettingslogoMark, brandName, brandSuffix.
Item nav .yc-nav-itemyc.navItemlabel, href, current.
Stat .yc-stathero.statsvalue, label. Plafond : 10+ projets, 9 secteurs, Major BTS 2025.
Flottant .yc-floatimagealt vide si le visuel porte des chiffres peints. Verre autorisé ici.
Carte projet .yc-projectprojecttitle, summary, slug, cover avec alt, tags, status active ou paused, url. Pas de tint.
Carte article .yc-article-cardposttitle, slug, date, excerpt, cover.
Ligne parcours .yc-timeline-itemexperienceorg, role, start, end, place, summary. Refus si client ou pourcentage.
Citation .yc-quoteobjetquote, name, role.
À retenir .yc-calloutobjettitle « À retenir », body.
Terminal .yc-terminalobjetfilename, code, language.
Fil d'Ariane .yc-breadcrumbyc.crumb[]label, href. Dernier item sans lien.
Pagination .yc-paginationdérivéPage courante calculée.
Sommaire .yc-tocdérivéAncres des h2.
Partage .yc-sharesiteSettingslinkedin.
Suivre .yc-followsiteSettingslinkedin. Pas de newsletter.
Canal .yc-channelsiteSettingsemail, linkedin, whatsappUrl.
Header .yc-headersiteSettingsnav, contactCta. Verre. Même markup partout.
Hero .yc-heroherotitle, titleAccent, lede, stats max 3, secondaryCta.
Grille .yc-project-gridproject[]Une colonne, deux dès 768px.
Liste .yc-article-listpost[]Trois cartes puis lien texte.
Timeline .yc-timelineexperience[]Poste actuel en premier. Seul endroit pour l'employeur.
Bande .yc-cta-bandyc.buttonUn bouton. Vert seulement s'il répète le hero en bas de page longue.
Formulaire .yc-contact-formcontactFormLibellés seulement. L'envoi n'est pas dans le système.
Footer .yc-footersiteSettingsMarque, pages, projets, contact. Ligne légale fixe.
Intervention .yc-talkeventevent, dateLabel, place, title, peers, status past ou invite, summary.
Galerie .yc-galleryimage[]alt requis, légende sous la photo.
Templatespagetemplate accueil, page, article ou contact. kind pour les huit pages.

Schémas

Extraits des types. Les noms de champs restent ceux de l'inventaire. Aucun champ de teinte.

Document siteSettings
{ name: "siteSettings", type: "document", fields: [
  { name: "brandName", type: "string", initialValue: "Yoane" },
  { name: "brandSuffix", type: "string", initialValue: "Ngassam" },
  { name: "dataBrand", type: "string",
    options: { list: ["yoane", "doc4geeks"] },
    initialValue: "yoane" },
  { name: "logoMark", type: "string", initialValue: "YN" },
  { name: "email", type: "string",
    initialValue: "hello@doc4geeks.com" },
  { name: "linkedin", type: "url" },
  { name: "whatsappUrl", type: "url" },
  { name: "nav", type: "array", of: [{ type: "yc.navItem" }] },
  { name: "contactCta", type: "yc.button" }
]}
Document page
{ name: "page", type: "document", fields: [
  { name: "title", type: "string",
    validation: Rule => Rule.required() },
  { name: "slug", type: "slug" },
  { name: "template", type: "string",
    options: { list: ["accueil", "page", "article", "contact"] } },
  { name: "kind", type: "string", options: { list: [
    "accueil", "a-propos", "projets", "projet",
    "blog", "article", "interventions", "contact"
  ]}},
  { name: "hero", type: "hero" },
  { name: "seo", type: "seo" }
]}
Document project
{ name: "project", type: "document", fields: [
  { name: "title", type: "string",
    validation: Rule => Rule.required() },
  { name: "summary", type: "text" },
  { name: "slug", type: "slug" },
  { name: "cover", type: "image", fields: [
    { name: "alt", type: "string",
      validation: Rule => Rule.required() }
  ]},
  { name: "tags", type: "array", of: [{ type: "string" }] },
  { name: "status", type: "string",
    options: { list: ["active", "paused"] } },
  { name: "url", type: "url" }
]}
/* Pas de champ tint. */
Document post
{ name: "post", type: "document", fields: [
  { name: "title", type: "string",
    validation: Rule => Rule.required() },
  { name: "slug", type: "slug" },
  { name: "date", type: "date" },
  { name: "excerpt", type: "text",
    validation: Rule => Rule.max(180) },
  { name: "cover", type: "image", fields: [
    { name: "alt", type: "string",
      validation: Rule => Rule.required() }
  ]},
  { name: "body", type: "array", of: [{ type: "block" }] },
  { name: "author", type: "reference", to: [{ type: "author" }] }
]}
Document event
{ name: "event", type: "document", fields: [
  { name: "event", type: "string",
    validation: Rule => Rule.required() },
  { name: "dateLabel", type: "string" },
  { name: "place", type: "string" },
  { name: "title", type: "string" },
  { name: "peers", type: "string" },
  { name: "status", type: "string",
    options: { list: ["past", "invite"] } },
  { name: "summary", type: "text" },
  { name: "cover", type: "image", fields: [
    { name: "alt", type: "string",
      validation: Rule => Rule.required() }
  ]}
]}
Document experience
{ name: "experience", type: "document", fields: [
  { name: "org", type: "string",
    validation: Rule => Rule.required() },
  { name: "role", type: "string" },
  { name: "start", type: "string" },
  { name: "end", type: "string" },
  { name: "place", type: "string" },
  { name: "summary", type: "text",
    validation: Rule => Rule.custom(value => {
      if (!value) return true;
      if (/%|client/i.test(value)) {
        return "Pas de pourcentage ni de client";
      }
      return true;
    })
  }
]}
Document author
{ name: "author", type: "document", fields: [
  { name: "name", type: "string",
    initialValue: "Yoane Ngassam",
    validation: Rule => Rule.required() },
  { name: "role", type: "string",
    initialValue: "Ingénieur logiciel, automatisation et IA" },
  { name: "portrait", type: "image", fields: [
    { name: "alt", type: "string",
      validation: Rule => Rule.required() }
  ]},
  { name: "city", type: "string", initialValue: "Yaoundé" }
]}
Objet cta
{ name: "yc.button", type: "object", fields: [
  { name: "label", type: "string",
    validation: Rule => Rule.required() },
  { name: "href", type: "url" },
  { name: "variant", type: "string",
    options: { list: ["primary", "wa", "outline", "ghost", "link"] } },
  { name: "size", type: "string", initialValue: "md",
    options: { list: ["sm", "md", "lg"] } }
]}
Objet seo
{ name: "seo", type: "object", fields: [
  { name: "metaTitle", type: "string",
    validation: Rule => Rule.max(60) },
  { name: "metaDescription", type: "text",
    validation: Rule => Rule.max(160) },
  { name: "ogImage", type: "image", fields: [
    { name: "alt", type: "string",
      validation: Rule => Rule.required() }
  ]},
  { name: "noIndex", type: "boolean", initialValue: false }
]}

Réutiliser le système

Les classes ne changent pas. data-brand ne remplace que l'accent et son survol. Le commutateur de cette planche le montre sur la coche et le point.

yoanengassam.com

data-brand="yoane"

Même jeu que yoane.doc4geeks.com. Accent #22d3ee. Toile, textes, filets, vert WhatsApp et rayons restent fixes.

doc4geeks.com

data-brand="doc4geeks"

L'accent passe à #06b6d4, un cyan proche, et le survol reste #67e8f9. Rien d'autre ne bouge. Sanity écrit siteSettings.dataBrand.

Surcharge accent seulement
<html lang="fr" data-theme="cinema" data-brand="doc4geeks">

[data-brand="doc4geeks"] {
  --yc-accent: #06b6d4;
  --yc-accent-hover: #67e8f9;
}
  • yoane.doc4geeks.com utilise data-brand="yoane", sans surcharge.
  • Le bouton WhatsApp lit siteSettings.whatsappUrl. Tant que l'URL est vide, href="https://wa.me/".
  • Le texte du bouton vert reste l'encre #05070a, jamais le blanc.