PDS · brandguide v1.3 · 2026.06 by Ptolémé DE-SOUZA
> pds

Le système d'identité de Ptolémé DE-SOUZA. Né du terminal : mono, vert phosphore, ASCII et discipline système. Pensé pour rester lisible quand tout escalade.

Ownerptoleme@pdesouza.fr
ScopeWeb · email · talks · social
Version1.3 · June 2026
Sourcebrandguide.pdesouza.fr
01 · tokens

Tokens

Les variables CSS réelles du portfolio. Source de vérité : css/style.css, bloc :root.

Colors

Palette token-isée

TokenValeurRôle
--green-primary#00FF41CTA, prompts, accents, liens
--green-dim#00CC33États hover, secondaire
--green-glowrgba(0,255,65,0.15)Glow box-shadow / text-shadow
--cyan-accent#00D4FFLiens hover, info
--yellow-accent#FFD700Warnings, callouts
--red-accent#FF4444Erreurs, live-dot
--orange-accent#FF8800Accent secondaire
--bg-primary#000000Fond global
--bg-secondary#0A0E18Surface 2
--bg-terminal#080D16Cards, terminaux
--bg-terminal-header#141E2AHeaders de cards
--bg-cardrgba(8,13,22,0.9)Surface translucide
--text-primary#D8E2ECBody
--text-secondary#8899AAMeta, captions
--text-heading#FFFFFFTitres
--border-default#1E3044Bordures neutres
--border-greenrgba(0,255,65,0.2)Bordures terminales
--border-active#00FF41Hover, focus
--dot-red · --dot-yellow · --dot-greenDots window-control
Typography scale

Échelle fluide via clamp()

Tokenmin · vw · maxUsage
--text-xs0.65rem · 1.2vw · 0.75remtags, meta, kickers
--text-sm0.75rem · 1.4vw · 0.875remnav, output
--text-base0.875rem · 1.6vw · 1rembody
--text-lg1rem · 1.8vw · 1.125remlead
--text-xl1.125rem · 2vw · 1.25remnav__logo, h4
--text-2xl1.25rem · 2.5vw · 1.5remsection__title
--text-3xl1.5rem · 3vw · 2remh2
--text-4xl2rem · 4vw · 3remh1 article
--text-hero2.5rem · 5vw · 4.5remhero headline display

Familles : --font-mono = JetBrains Mono (body, UI, code) · --font-display = Space Grotesk (titres, lede) · --font-body = Space Grotesk (alias).

Radius

Coins francs uniquement

--radius-sm3pxtags, badges, dots, pills
--radius-md6pxboutons, inputs, lang-toggle
--radius-lg8pxterminal-card, widgets, sections

Plafond : 8px. Au-dessus, on perd l'esthétique TTY. Pas de border-radius: 50% sauf pour les dots window-control et le .live-dot.

Starter pack — v1.2

Le :root système, prêt à coller

Pour démarrer tout nouveau projet PDS : la palette canonique, les familles, les radius et les tokens motion en un seul bloc. C'est le nommage système (celui de ce guide) — le portfolio garde son nommage historique (--green-primary…) documenté dans les tableaux ci-dessus.

css · tokens
:root {
  /* identité — le vert ne change jamais */
  --primary:      #00FF41;
  --primary-dim:  #00CC33;
  --primary-glow: rgba(0, 255, 65, 0.35);
  --primary-soft: rgba(0, 255, 65, 0.08);
  --amber: #FFD700;  --cyan: #00D4FF;
  --red:   #FF4444;  --orange: #FF8800;

  --font-mono:    'JetBrains Mono', monospace;
  --font-display: 'Space Grotesk', sans-serif;

  --radius-sm: 3px;  --radius-md: 6px;  --radius-lg: 8px;  /* plafond : 8px */

  --t-instant: 80ms;  --t-quick: 160ms;  --t-base: 300ms;
  --t-cinematic: 2400ms;  --t-scan: 2500ms;
}

body.theme-dark {
  --bg: #000000;  --bg-2: #080D16;  --bg-3: #0F1A2A;
  --bg-card: rgba(8, 13, 22, 0.9);
  --fg: #D8E2EC;  --fg-dim: #8899AA;  --fg-strong: #FFFFFF;
  --border: rgba(0, 255, 65, 0.18);  --border-soft: #1E3044;
}

body.theme-light {
  --bg: #F4F2EA;  --bg-2: #ECEAE0;  --bg-3: #E0DDD0;
  --bg-card: #FFFFFF;
  --fg: #1B232E;  --fg-dim: #5A6470;  --fg-strong: #050810;
  --border: rgba(0, 90, 25, 0.22);  --border-soft: #C8C4B6;
}
Effets globaux

Scanline ambiante

--scanline-gradient = repeating-linear-gradient(0deg, rgba(0,0,0,0) 0px, rgba(0,0,0,0) 1px, rgba(0,255,65,0.008) 1px, rgba(0,255,65,0.008) 2px) appliqué sur body::after (z-index 9998, pointer-events none). Donne la texture CRT.

02 · components

Components

Les briques UI réelles utilisées sur le portfolio. Tout dérive de terminal-card et widget.

Live specimen — v1.2

Les deux briques mères, rendues en vrai

incident_2841.log
status --last
root cause isolée · patch shippé
14:32 escalade reçue → 15:10 closed
closed · notes archived
terminal-card — survoler pour le feedback border
pds.metrics LIVE
incidents traités0
années réseau & sécu0
charge
widget — compteurs & barre ASCII animés au reveal
html · markup canonique portfolio
<div class="terminal-card">
  <div class="terminal-card__header">
    <div class="terminal-card__dots">
      <span class="terminal-card__dot terminal-card__dot--red"></span>
      <span class="terminal-card__dot terminal-card__dot--yellow"></span>
      <span class="terminal-card__dot terminal-card__dot--green"></span>
    </div>
    <span class="terminal-card__header-title">incident_2841.log</span>
  </div>
  <div class="terminal-card__body">
    <div class="terminal-card__prompt">status --last</div>
    <div class="terminal-card__output">root cause isolée · patch shippé</div>
  </div>
  <div class="terminal-card__meta">closed · notes archived</div>
</div>
terminal-card

Le composant central

La quasi-totalité des surfaces de contenu (hero, projets, articles, widget career) sont des terminal-card. Source : css/terminal.css.

ClasseRôle
.terminal-cardBase : bg --bg-terminal, bordure --border-green, radius --radius-lg
.terminal-card--heroVariante hero : shadow 0 4px 20px rgba(0,0,0,0.5), hover translateY(-3px)
.terminal-card--clickableCursor pointer, hover translateY(-4px)
.terminal-card--widgetHauteur 100% en flex pour s'aligner en grid
.terminal-card--centeredmax-width 500px centré
.terminal-card__headerBandeau : bg --bg-terminal-header, padding 0.6rem 0.85rem
.terminal-card__dots + __dot--red/yellow/greenWindow-controls, 12×12px circles
.terminal-card__header-titleNom de fichier en mono, ellipsis
.terminal-card__bodyPadding 1rem, font mono 0.85rem, line-height 1.6
.terminal-card__promptPréfixe "> ", couleur --green-primary
.terminal-card__outputRéponse en --text-primary
.terminal-card__metaPied de carte, séparé par border-top, mono --text-xs
.terminal-card__file · __desc · __tagsSous-blocs pour cards projets / articles
widget

Widgets sidebar (hero right + career)

ClasseRôle
.widgetContainer — souvent imbriqué dans .terminal-card
.widget__header · __title · __liveHeader avec live-dot rouge clignotant
.widget__body · __body--centeredZone contenu, padding 0.75rem
.widget__counter-row · __counter-label · __counterLignes "metric : 42"
.widget-career + sous-classesVariante carrière (hero, info, name, status, section-label, cta)
.widget-downloads · .widget-dl-btnBoutons download (CV PDF, etc.)
Nav · Layout · Hero

Structure de page

ClasseRôle
.nav · .nav__inner · .nav__logoHeader sticky, blur backdrop, height 60px
.nav__link · .nav__link--activeUnderline animé en hover
.nav__hamburger · .nav__drawerMobile menu
.lang-toggleFR/EN switcher, bordure verte
.containermax-width 1200px, padding 0 1.5rem
.section · .section__titleSection padding 4rem 0, titre préfixé "> "
.hero · .hero__gridGrid 1.4fr · 1fr · 0.75fr (3 col), --two-col alternative
.hero__name · __headline · __jobtitleBloc value-prop gauche
Atomes

Petits éléments transverses

.tag / .tagsPill 0.65rem, bg rgba(106,123,141,0.08), border --border-default
.live-dot6×6px rouge, animation live-pulse 2s
.terminal-prompt · .terminal-cursorPréfixe "> " et caret block
.cursor-blockBlock clignotant, animation blink-cursor 1s step-end infinite

À noter : il n'existe pas de classe .btn générique sur le portfolio. Les CTA sont des <a> avec couleur --green-primary ou des .widget-dl-btn, .lang-toggle selon le contexte.

Nouveaux — v1.2

Status chrome & skill bars

Trois composants validés depuis la v1.1 (joués en live au chapitre Motion · Status chrome ↓) : la page devient elle-même un système qui rend compte de son état.

réseau & firewalling95%
zero trust / ztna90%
ia agentique80%
pds.service — active ~/compétences 69% · --:--:--
ComposantRôleRègles
.scroll-progressBarre 2px fixée en haut, suit le scrollfill --primary + glow léger · listener passive
.statusbarBarre fixe en bas, grammaire tmuxgauche = état · centre = chemin ~/section · droite = % scroll + horloge
.skill-rowJauge htop : █░ + pourcentagewhite-space: pre obligatoire · remplissage steps(18), jamais fluide
03 · states

States

Default · hover · focus · active · disabled. Transitions 0.2s ease ou 0.3s ease, jamais plus.

Hover patterns

Comportements de survol

CibleEffet
a standardcolor --green-primary--cyan-accent · transition 0.2s
.nav__linkcolor → --green-primary + underline qui s'étend (width 0 → 100%, 0.3s)
.nav__logotext-shadow 0 0 10px var(--green-glow)
.terminal-cardborder-color → --green-primary, shadow 0 4px 20px rgba(0,255,65,0.08)
.terminal-card--hero+ translateY(-3px), shadow 0 8px 30px var(--green-glow)
.terminal-card--clickabletranslateY(-4px), output color → --green-primary
.lang-togglebg → --green-primary, color → --bg-primary (inversé)
.widget-dl-btnbg rgba(0,255,65,0.05)0.12, border → --green-primary
Playground — v1.2

Survoler pour sentir le contrat

lien standard vert → cyan · 0.2s
nav__link underline 0 → 100%
inversion bg ↔ couleur
terminal-card
border + shadow · jamais de translate
Active / focus

État courant

.nav__link--active = même rendu que :hover (couleur --green-primary + underline 100%). La cohérence "active = comme hover" évite les surprises au clavier.

.scroll-progress (barre de progression article) suit le scroll, fill --green-primary, opacité progressive.

404 / empty

Page d'erreur

404.html existe sur le portfolio : même nav, même footer, contenu en .terminal-card--centered avec message en terminal-card__output. Pas de gros 404 décoratif : l'incident reste sobre.

04 · patterns

Patterns

Compositions récurrentes. Chaque pattern correspond à un type de page du portfolio.

Hero — page d'accueil

index.html

Grid 3 colonnes 1.4fr · 1fr · 0.75fr, gap 1.5rem, alignement start.

  • Gauche.hero__name (kicker), .hero__headline (display), .hero__jobtitle, value prop, CTAs.
  • Centre.terminal-card--hero avec prompt animé (terminal-card__prompt + .cursor-block).
  • Droite.widget (live status, métriques) ou .widget-career.

Variante .hero__grid--two-col (1fr · 1fr, gap 3rem) sur les pages internes.

Article — long-form

article.html

Layout 1 colonne centrée. Header avec .section__title, body en .bg-prose-équivalent (mono, line-height 1.7). Barre de progression scroll-progress sticky en haut. Tags .tag en pied d'article.

Career — timeline + widget

career.html

Combo .widget-career (sidebar gauche : status live, downloads, CTA contact) + stack de .terminal-card à droite, une par expérience, avec .terminal-card__header daté.

Projects — grid

projects.html · articles.html

Grille de .terminal-card--clickable, chacune avec __file (nom de fichier), __desc (résumé), __tags. Filtrage via JS, transition fade-enter (animation fadeIn 0.3s).

Page chrome — v1.2

La page comme système vivant

Pattern transverse à toutes les pages longues : scroll-progress (2px, fixé en haut) + statusbar (fixée en bas : état du service, section courante en notation ~/chemin, % de scroll, horloge live). Le chrome est du statut, pas de l'animation — il tourne en continu, en vanilla, sans dépendance. Spécimen vivant en §02 Components, implémentation complète au chapitre Motion · 17 ↓.

05 · usage

Usage

Comment étendre le portfolio sans casser l'identité.

Do

Bonnes pratiques

  • Toute nouvelle couleur passe par un token --* dans :root (style.css L8-32).
  • Toute nouvelle surface de contenu = un .terminal-card ou un dérivé.
  • Toute taille de texte = un --text-* (clamp), jamais une valeur en dur.
  • Toute animation au-dessus de 300ms doit être justifiée (intro, hero unique).
  • Le vert --green-primary reste à 10% max de la surface visible.
Don't

Refus par défaut

  • Inventer une nouvelle teinte de vert. #00FF41 ou rien.
  • Ajouter un border-radius > 8px (sauf 50% pour dots).
  • Utiliser une font autre que JetBrains Mono ou Space Grotesk.
  • Mettre une emoji dans la nav, un titre, un bouton.
  • Ajouter une animation infinie sur du contenu textuel non-décoratif.
  • Créer un composant ad hoc qui réinvente .terminal-card avec d'autres bordures.
Files map

Où vit quoi dans pdesouza-portfolio

FichierContenu
css/style.cssTokens :root, nav, layout, hero, widgets, sections — gros fichier
css/terminal.cssFamille .terminal-card isolée
css/animations.cssKeyframes blink-cursor, live-pulse, fadeIn, pulse-dot, classes .reveal / .fade-in-up
css/about.css · career.cssStyles spécifiques aux pages about.html et career.html
css/responsive.cssBreakpoints (cf. doc dédiée)
js/Theme toggle, scroll-spy, lang switcher, scroll-reveal
Procedure — ajouter un composant

Workflow

  1. Vérifier si un .terminal-card--{variant} ou un .widget existant suffit. Réutiliser plutôt que créer.
  2. Si nouveau : créer une branche preprod sur pdesouza-portfolio, ajouter la classe dans terminal.css ou un nouveau fichier.
  3. Documenter ici (Section 02 Components) — ajouter une ligne au tableau.
  4. Si un nouveau token est introduit, l'ajouter à :root ET à la Section 01 Tokens.
  5. Audit responsive (responsive.css) avant merge.
  6. Merge preprodmain uniquement après validation visuelle sur preprod.
Changelog

$ git log brandguide --oneline

VersionDateContenu
v1.32026.06Unification : le Motion Guide fusionné dans la page (chap. 14-18, démos vivantes anime.js) · cover générative (topographie Phosphor Vigil) · og:image · planches PL.01/02/03 en §07 & §13
v1.22026.06Spécimens vivants + code copiable (tokens, components, states) · nouveaux composants status chrome & skill bars · Motion Guide compagnon
v1.12026.04Signatures articles/projets (§13) · exception terminal-card #080D16 (§10) · hover policy no-flip (§14) · asset policy
v1.02026.04Structure initiale 17 sections : design system (01-05) + brand (06-16)
06 · brand

Brand

Une voix, une posture, une promesse. Voici ce que PDS représente avant même la première pixel.

Mission

Stabiliser ce que les autres ont déclaré perdu.

Quand un incident escalade, c'est moi qu'on appelle. PDS est l'identité d'un opérateur cyber qui transforme le chaos en post-mortem propre — méthodique, factuel, documenté.

Pillars

Les 4 piliers de l'identité

[01]
Calme
Sang-froid procédural. Le ton ne monte jamais.
[02]
Clarté
Une commande, une intention. Pas de jargon vide.
[03]
Système
Tout est traçable, versionné, documenté.
[04]
Résolu
L'incident finit toujours par fermer.
Audience

À qui PDS parle

Pairs Ingénieurs cyber, TAM, SRE qui veulent du concret.
Clients Équipes en escalade qui ont besoin de sang-froid.
Recrutement Tech leads qui lisent vite et reconnaissent le profil.
08 · architecture

Architecture

Comment la marque s'organise : un seul nom maître, des sous-services nommés comme des units système.

Tree

Hiérarchie de la marque

pds.service
├── pds.web          // pdesouza.fr — site & articles
├── pds.talks        // conférences & meetups
├── pds.notes        // post-mortems & techniques
├── pds.mail         // signature email & newsletters
└── pds.social       // LinkedIn, Mastodon, GitHub
Naming

Règles de nommage

ServiceFormatExemple
Domaine racinepdesouza.frpdesouza.fr
Sous-produitpds.<nom>pds.notes, pds.talks
Articleverbe + objet, lowercase"escalating quietly"
Talkquestion ou commande"why does it always fail at 3am?"
Handle social@pdesouza partout@pdesouza
09 · shape

Shape

Pas de courbes inutiles. Tout est aligné sur une grille mono — comme un terminal.

Grid

Grille de base 8px

8px grid
Toutes les marges, paddings, hauteurs sont multiples de 8.
Angles vifs
Border-radius 0–3px max. Les coins sont francs, comme une fenêtre TTY.
Bordure dashed
Les séparateurs secondaires sont en pointillés, jamais pleins.
ASCII

Patterns ASCII officiels

┌──────────────┐
│  pds         │
│  $ ready     │
└──────────────┘
Box drawing
Pour boîtes terminal, headers, encadrés narratifs.
────────────────
▓▓▓░░░░░░░░░░░░░
████░░░░░░░░░░░░
▓▓▓▓▓▓▓░░░░░░░░░
Progress / load
Visualiser progress, density, charge sans graphes.
> _ ▸ ◆
  $ ●
    ▓ ▒ ░
Glyph kit
Glyphes tolérés : > _ $ ▸ ◆ ● ◣. Rien d'autre.
Layout

Cadres canoniques

3-COL HERO
SIDE NAV + BODY
10 · colors

Colors

Vert primaire identique en dark et light. Le fond bouge, l'identité reste.

Palette

8 couleurs, point.

Phosphor green
primary
CTA, accents, prompts, links.
Dim green
hover
États hover & liens secondaires.
Black null
bg/dark
Fond principal mode dark.
Terminal navy
surface
Cards, panneaux, sections.
Paper warm
bg/light
Fond principal mode light.
Ink
text/light
Texte mode light.
Amber alert
warn
Callouts, warnings, attention.
Critical red
error
Erreurs, don'ts, états bloquants.
Ratios

60 / 30 / 10

60% · BG
30% · SURFACE
10% · PRIMARY
Background Surface · cards Primary · accents only

Le vert n'occupe jamais plus de 10% de la surface visible. Sa rareté est sa force.

Contrast

Pairs validés

$ pds online
14.5 : 1 · AAA
Body text on dark
16.2 : 1 · AAA
Body text on light
14.8 : 1 · AAA
Exception

Terminal-card surface · #080D16

Le token --bg-3 du système (#0F1A2A, navy bleuté) sert de fond pour les frames de contenu du brandguide et des sections en grille. Mais les terminal cards du portfolio (hero, projets, articles, about, 404…) utilisent une teinte plus profonde — la swatch Terminal navy #080D16 documentée plus haut. C'est une exception volontaire au design system, pas une dérive.

--bg-3 · #0F1A2A
frames brandguide · sections en grille
terminal-card · #080D16
cards portfolio · hero · projets · career

Pourquoi cette exception

Un terminal n'est pas une surface neutre — c'est l'écran lui-même. Il doit paraître plus profond que le fond de page pour que l'illusion CRT/console fonctionne. #080D16 donne ce contraste subtil que #0F1A2A dilue.

Quand l'appliquer

Toute classe .terminal-card et ses variantes (--hero, --widget, --clickable), plus les cards de la page Career : .career-tl__item, .career-edu__item, .career-cert, .career-lang. Pour tout le reste — sections, panels, frames, infographies — rester sur --bg-3.

Implémentation

Override unique en bout de chaîne dans preprod/css/patch-final.css §17 : .terminal-card { background: #080D16; } + sibling rule pour les .career-* avec !important (les règles bundles dans career.css spécifient --bg-3). Le système de tokens reste intact — l'exception est locale et documentée.

11 · typography

Typography

Deux familles, deux rôles. Display pour la voix, mono pour le système.

Display

Space Grotesk

Space Grotesk
display · titres · ledes
Weights : 400 · 500 · 600 · 700
HeroQuand ça escalade.48px / 700
H1Section title32px / 700
H2Sub-section24px / 600
LedePhrase d'accroche pour intro.18px / 400
Body displayPour quotes courts et résumés.16px / 400
Mono

JetBrains Mono

JetBrains Mono
body · UI · code · meta
Weights : 300 · 400 · 500 · 600 · 700
Code$ systemctl status pds15px / 500
BodyBody mono pour articles.14px / 400
UInav links · labels · captions12px / 500
Meta[meta] · 10px · 2px tracking10px / 400
Rules

Règles de mise en page

ÉlémentFamilleDétail
TitresSpace Grotesk 700letter-spacing -0.5 à -1.5px sur les grandes tailles
BodyJetBrains Mono 400line-height 1.65 · 60–72 caractères max par ligne
Meta / labelsJetBrains Mono 500UPPERCASE · letter-spacing 1.5–3px
CodeJetBrains Mono 500couleur var(--primary) pour les commandes
Lienshéritecouleur var(--primary), pas de soulignement
12 · iconography

Iconography

Stroke unique, grille 24×24, coins carrés. Comme un schéma système.

Set

Système d'icônes — base

Specs

Construction

Grille 24 × 24 px Tout est aligné sur la grille. Aucun stroke entre les pixels.
Stroke 1.6 px · linecap square Une seule épaisseur pour tout le set. Joints carrés, pas arrondis.
Color currentColor Toujours mono-color. Hérite de var(--primary) ou var(--fg).
Don'ts

À ne pas faire

pas de mix de styles
pas d'icône remplie
pas de gradient
stroke mono · une couleur
13 · imagery

Imagery

Photos sombres, traitements monochromes vert ou neutre. L'image doit ressembler à un screenshot, pas à une publicité.

Hero subjects

Que photographier

PortraitsProfil 3/4, fond sombre, pas de sourire commercial.
Captures terminalVrais logs, vrais hex, vrai code. Pas de mock.
Glitch / noisePour articles incident. Sobre, jamais kitsch.
Treatment

Traitement photo

Réduction de saturation à 30%. Highlights tirés vers le vert primaire, shadows vers le navy. Grain léger toléré.

duotone vert/navy
désaturé sombre
photo saturée
STOCK CORPORATE
photo stock corpo
Composition

Cadrage

TypeCadrageLumière
Portrait3/4 face · regard hors champside-light, ombres dures
Setup techécran 1er plan, mains flouesécran = source de lumière
Ambianceplan large, vide, urbain nuitnéons existants seulement
Article cover16:9 · sujet 1/3 droitecontrejour ou aucune source visible
Article & project signatures

Mix B + C — logo géométrique + fragment terminal

Chaque article et chaque projet porte une signature visuelle à deux étages : un logo Bauhaus mono-phosphore (B) au-dessus, un fragment de commande terminal (C) en dessous. Lisible à 36px, narratif au scroll, cohérent avec le hero.

$ skills --check t-shaped > 2 axes loaded · OK
article · t-shaped
$ trace --role bridge > 3 stakeholders connected
article · tam
>_
project · portfolio
OOM
project · archived (red)

Logo (B) — specs

viewBox 80×80. Stroke 2–2.5px, linecap square, 3 à 5 formes max, aucun fill sauf accent ponctuel. Mono-couleur via currentColor. Optionnel : enclosure pointillée stroke-dasharray="2 3" à opacity 0.35 pour le cadre architectural.

Fragment (C) — articles only

Deux lignes mono 0.65rem. Ligne 1 = commande ($ verb --flag, < 28 chars). Ligne 2 = sortie (> result, < 28 chars). Border-left 2px en accent, fond rgba(0,0,0,0.35). Le $ reste en accent, la sortie en --fg-dim.

Article-only. Sur les article-card le fragment pose le ton terminal et clarifie la promesse. Sur les project-card il rend le widget visuellement trop dense (header + sig + statut + description + tags + fragment = 6 strates). Décision : les projets gardent uniquement le logo (B), l'accent (green / red / amber) suffit à porter l'état.

Accents

green = défaut (live, active). red = projet archivé / échec documenté. amber = warning, en pause. currentColor fait propager l'accent au SVG ET au fragment d'un seul hop CSS.

Implémentation référence : preprod/js/signatures.js (data) + main.js (render) + patch-final.css §20 (visuel).

Planches — v1.2

Phosphor Vigil — la série

Direction artistique 2026.06, déclinée de l'identité : PL. 01 (relevé d'escalade — source de l'og:image et de la topographie de cover) et PL. 03 (taxonomie raisonnée — 5 signatures nouvelles prêtes à servir : VIGIL, MESH, SIGNAL, ROOT CAUSE, BRIDGE). Usage : posters, slides d'ouverture de talks, covers d'articles.

PL.01 — Relevé d'escalade : trajectoire d'incident dans une topographie de glyphes
PL. 01 — relevé d'escalade · N° 2841
PL.03 — Taxonomie des signatures : neuf spécimens dans la grammaire PDS
PL. 03 — taxonomie des signatures · 9 spec.
14 · motion

Motion · Principes

Le mouvement raconte un système qui pense. Pas d'easing fancy : steps, lin, ease-out court.

Règles

Quatre règles non négociables

R1

Le mouvement vient du système, pas du décor

On anime ce qu'un terminal animerait : du texte qui s'écrit, des logs qui arrivent, des jauges qui se remplissent. Jamais des éléments qui « dansent » pour faire joli.

R2

steps() ou ease-out court — rien d'autre

Le typewriter et les barres avancent par paliers (steps). Les reveals utilisent un outQuad ≤ 520ms. Bounce, elastic, overshoot : interdits.

R3

Une seule fois, puis calme

Les animations d'entrée jouent une fois (IntersectionObserver + unobserve). Aucune boucle infinie sauf les signaux d'état : curseur, dot live, glow lent.

R4

Dégradable, toujours

prefers-reduced-motion → état final direct. CDN anime.js indisponible → la page reste complète et lisible. L'animation est un bonus, jamais une dépendance.

Don'ts

À éviter en motion

  • ✕ Bounce, elastic, overshoot — jamais.
  • ✕ Parallax au scroll — sauf si vraiment justifié, et discret.
  • ✕ Spinners ronds génériques. Préférer un [████░░░░] ASCII.
  • ✕ Animations > 3s sauf cinematic intro.
  • ✕ Auto-play vidéo avec son.
  • Card flip 3D au hover — testé sur les 2 premières project-card de la page Projets, retiré : effet "show-off" qui rompt la grille calme. Hover des cards = uniquement border-color shift + box-shadow subtle (cf. tokens --t-instant / --t-quick).
Hover policy

Card hover — borderline only

Tous les composants type terminal-card, project-card, article-card, career-tl__item, career-cert, career-edu__item partagent une même grammaire de feedback : changement de border-color (vers --primary ou --cyan) + box-shadow lointain (offset Y 4px, blur 16-20px, opacity 0.06-0.08), durée --t-quick (160ms ease-out). Pas de translation, pas de scale, pas de rotation, pas de flip. Le pulse vient des états système (status-badge, dot live), pas des interactions.

terminal-card
border-color → --primary · box-shadow Y4 blur16 α0.07 · 160ms ease-out.
Pas de translate. Pas de scale. Pas de flip.
--t-quick (160ms) · CSS only — anime.js n'y touche jamais — survoler la carte
css
.terminal-card {
  border: 1px solid var(--border);
  transition: border-color var(--t-quick) ease-out,
              box-shadow   var(--t-quick) ease-out;
}
.terminal-card:hover {
  border-color: var(--primary);
  box-shadow: 0 4px 16px rgba(0, 255, 65, 0.07);
}
Asset policy

Images & cache discipline

Image format

WebP first, JPG fallback via CSS image-set(). Cible : ~50% du poids JPG à qualité visuelle équivalente. Exemple en prod : bg-circuit est passé de 419 KB JPG à 201 KB WebP. AVIF prévu plus tard pour gagner 20% supplémentaires sur navigateurs récents.

SVG inline · a11y

Tout <svg> purement décoratif (icône qui accompagne un texte) doit porter aria-hidden="true". Les SVG sémantiques (logo, signature article) gardent un role="img" + aria-label si nécessaire. Le décodage automatique de chaque <svg> par les lecteurs d'écran est sinon vocalisé en bruit.

Cache discipline

Tous les assets statiques sont versionnés via ?v=<git short hash> partagé entre tous les fichiers d'une même release. Un script deploy/bump-cache.sh propage le hash après chaque commit. Élimine les risques de stale-cache sur fichiers interdépendants (config-*.js, signatures.js, main.js).

15 · timing

Motion · Timing

Les tokens CSS + les constantes propres aux patterns animés.

Tokens CSS
TokenDuréeEasingUsage
--t-instant80mslinearhover · focus · feedback
--t-quick160msease-outpanneaux, dropdowns, toggles
--t-base300msease-outpage transitions, theme switch
--t-cinematic2400msease-in-outglow pulse, hero typewriter
--t-scan2500mslinearscanline, loaders longs
Constantes JS (patterns anime.js)
PatternValeurEasing anime.jsNote
Typewriter34–38ms / caractère— (setInterval)vitesse lecture humaine
Log stagger180ms entre lignes · 360ms / ligneoutQuadla signature PDS
Barres ASCII1100mssteps(18)18 = largeur de la barre
Compteurs1100msoutQuadarrondir à l'entier
Tracé SVG700ms · stagger 120msoutQuadnœuds : fade après tracé
Reveal scroll480–520ms · translateY 14–18pxoutQuadthreshold IO : 0.18
Boot sequence180ms × lignes + fade 450msoutQuadtotal < 3s obligatoire
Import
js · module
const { animate, stagger } = await import(
  'https://cdn.jsdelivr.net/npm/animejs@4.0.2/+esm'
);
16 · patterns

Motion · Patterns

Les mouvements canoniques, joués en live + code copiable. Survoler la section relance au scroll.

Typewriter

Caractère par caractère — jamais d'easing fluide

38ms / caractère · setInterval · zéro dépendance
js
function typewriter(el, { speed = 38, delay = 0 } = {}) {
  return new Promise(resolve => {
    const text = el.dataset.text || '';
    el.textContent = '';
    setTimeout(() => {
      let i = 0;
      const id = setInterval(() => {
        el.textContent = text.slice(0, ++i);
        if (i >= text.length) { clearInterval(id); resolve(); }
      }, speed);
    }, delay);
  });
}
Log stagger

La signature PDS — 180ms entre les lignes

$ systemctl status pds.service
● active (running) since 2019
$ tail -f /var/log/incidents.log
14:32 ticket #2841 → escalade reçue
14:47 root cause isolée · patch shippé
15:10 incident closed. notes archived.
delay: stagger(180) · 360ms / ligne · outQuad · translateX −10px
js · anime.js v4
animate('.term-line', {
  opacity:    [0, 1],
  translateX: [-10, 0],
  duration:   360,
  ease:       'outQuad',
  delay:      stagger(180),   // la cadence canonique
});
Barres ASCII

Le remplacement officiel de tout spinner

réseau & firewalling0%
zero trust / ztna0%
ia agentique0%
1100ms · ease: steps(18) · caractères █ et ░
js · anime.js v4
function asciiBar(pct, width = 18) {
  const filled = Math.round((pct / 100) * width);
  return '█'.repeat(filled) + '░'.repeat(width - filled);
}
document.querySelectorAll('[data-bar]').forEach(el => {
  const state = { v: 0 };
  animate(state, {
    v: +el.dataset.bar, duration: 1100, ease: 'steps(18)',
    onUpdate: () => { el.textContent = asciiBar(state.v); },
  });
});
Compteurs

Les métriques qui montent

0incidents traités
0+ans d'expérience
0certifications
1100ms · outQuad · interpolation d'objet + Math.round
js · anime.js v4
document.querySelectorAll('[data-count]').forEach(el => {
  const state = { v: 0 };
  animate(state, {
    v: +el.dataset.count, duration: 1100, ease: 'outQuad',
    onUpdate: () => { el.textContent = Math.round(state.v); },
  });
});
Tracé SVG

Les signatures se dessinent — stroke-dashoffset

700ms / forme · stagger(120) · nœuds en fade après 1s
js · anime.js v4
const shapes = card.querySelectorAll('.sig [stroke]');
shapes.forEach(s => {
  const len = s.getTotalLength();
  s.style.strokeDasharray  = len;
  s.style.strokeDashoffset = len;   // invisible au départ
});
animate(shapes, {
  strokeDashoffset: 0, duration: 700, ease: 'outQuad',
  delay: stagger(120, { start: 200 }),
});
Reveal scroll

L'entrée standard — une fois, puis plus rien

netflux
Viz live des flux réseau — conntrack · WebSocket · force-graph.
jobscout
Recherche d'emploi automatisée — FastAPI · Streamlit.
pds-analytics
Forensique visiteurs auto-hébergée — FastAPI · SQLite.
520ms · outQuad · translateY 18px · cascade 120ms / carte · IO 0.18
js · anime.js v4 + IntersectionObserver
const io = new IntersectionObserver(entries => {
  entries.forEach(entry => {
    if (!entry.isIntersecting) return;
    io.unobserve(entry.target);          // R3 : une seule fois
    animate(entry.target, {
      opacity: [0, 1], translateY: [18, 0],
      duration: 520, ease: 'outQuad',
      delay: (+entry.target.dataset.idx || 0) * 120,
    });
  });
}, { threshold: 0.18 });
Boot sequence

Cinematic intro — budget < 3 secondes

[ OK ] pds.service — mounting interface
[ OK ] tokens loaded — #00FF41 · mono · grotesk
[ OK ] motion engine — anime.js v4
[ OK ] ready. starting session…
$ session ouverte
stagger 180ms · 300ms / ligne · puis fade 450ms → contenu
js · anime.js v4
const lines = document.querySelectorAll('.boot__line');
animate(lines, {
  opacity: [0, 1], translateX: [-12, 0],
  duration: 300, ease: 'outQuad', delay: stagger(180),
});
await new Promise(r => setTimeout(r, 180 * lines.length + 450));
document.getElementById('boot').classList.add('is-done');
17 · chrome

Motion · Status chrome

Statusbar fixe + scroll progress : la page elle-même devient un système vivant.

Démo
la barre du haut suit le scroll · la statusbar vit en bas
pds.service — active ~/compétences 0% · --:--:--
scroll listener passif · horloge 1s · scrollspy au 40% du viewport
js · vanilla (zéro dépendance)
// progress bar — <div id="scroll-progress"><i></i></div>
const fill = document.querySelector('#scroll-progress i');
window.addEventListener('scroll', () => {
  const max = document.documentElement.scrollHeight - innerHeight;
  fill.style.width = Math.round(scrollY / max * 100) + '%';
}, { passive: true });

// horloge statusbar
setInterval(() => {
  clock.textContent = new Date().toLocaleTimeString('fr-FR');
}, 1000);

Ce chrome est du statut, pas de l'animation : il tourne en continu, listener passive, aucune dépendance anime.js. La progress bar fait 2px, vert + glow léger ; la statusbar reprend la grammaire tmux : gauche = état, centre = chemin, droite = métriques.

18 · fallbacks

Motion · Fallbacks

R4 en pratique : la page doit être complète sans moteur, sans CDN, sans motion.

Les trois filets
F1

prefers-reduced-motion

Détecté en JS et en CSS. Tout passe à l'état final : barres pleines, compteurs à leur valeur, lignes visibles. Les démos restent jouables au clic — c'est une demande explicite de l'utilisateur.

F2

CDN indisponible

L'import ESM est dans un try/catch. En échec : staticFallback() rend tout visible et rempli. anime.js est un enhancement, pas une dépendance.

F3

Overlay timeout

Un setTimeout inline (hors module, donc immune aux erreurs d'import) dissout le boot overlay après 2500ms quoi qu'il arrive. Écran noir impossible.

Code
js
const REDUCED = matchMedia('(prefers-reduced-motion: reduce)').matches;

async function run() {
  if (REDUCED) return staticFallback();      // F1
  let animate, stagger;
  try {
    ({ animate, stagger } = await import(
      'https://cdn.jsdelivr.net/npm/animejs@4.0.2/+esm'));
  } catch {
    return staticFallback();                 // F2
  }
  /* … séquence animée … */
}
css
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation: none !important;
    transition: none !important;
  }
  .boot { display: none; }
}
19 · voice

Voice

Comment PDS s'exprime à l'écrit, peu importe le canal.

Principes

Direct, factuel, système

do
  • Phrases courtes. Verbes d'action.
  • Métaphores système : escalade, post-mortem, runtime, p95.
  • Citer des chiffres réels (durées, incidents, années).
  • FR par défaut, EN pour articles techniques internationaux.
  • Reconnaître l'incertitude quand elle existe.
don't
  • "Synergies", "leverage", "best-in-class".
  • Emojis dans titres, nav, ou body professionnel.
  • Promesses absolues ("100% safe", "zero risk").
  • Mélanger FR/EN dans une même phrase.
  • Ton défensif, victimisation, corporate-speak.
Vocabulaire

Mots qu'on aime · qu'on évite

On utiliseOn évitePourquoi
escalade · post-mortem"crise" · "drame"Vocabulaire ops, pas émotionnel
runtime · latence · p95"performance" vaguePrécision > généralité
incident · root cause"problème" · "souci"Termes techniques nommés
shipper · livrer"déployer un livrable"Verbes courts, actifs
"je ne sais pas encore""on travaille dessus"L'honnêteté sur l'incertitude
Rewrites

Avant / après

avant

"Nous mettons un point d'honneur à offrir une expérience de qualité à tous nos clients en adressant leurs problématiques."

après

"Quand un ticket part en escalade, on prend le téléphone dans les 15 min."

avant

"Notre solution leverage les dernières technologies cloud-native pour des résultats best-in-class."

après

"On tourne sur Kubernetes. p95 sous 80ms en prod. Le reste est documenté."

20 · tone

Tone

La voix reste, le ton s'adapte. Voici les 6 contextes principaux.

Tone matrix

Six contextes, six réglages

Calme contrôlé
incident en cours
L'API est dégradée depuis 14h32. On isole la cause maintenant.
Documenté précis
post-mortem · article
Le bug est apparu après le merge de #2841. Voici la timeline.
Sec, un peu sec
notes internes · twitter
Cette feature a ramené 3 escalades en 2 semaines. À reprendre.
Curieux ouvert
questions · podcasts
Je n'ai pas encore creusé ce point — comment tu l'as résolu chez vous ?
Engagé direct
talks · keynote
On parle de cybersécurité comme on parlait d'antivirus en 2003.
Bienveillant net
mentorat · review
C'est bien parti. Trois choses à vérifier avant de merger.
Channels

Adaptation par canal

CanalTon dominantLongueurExemples
pdesouza.frDocumenté précis800–2500 motsarticles, post-mortems
LinkedInEngagé direct120–280 motsretours d'incidents, opinions
Email signatureSec3 lignes maxnom · rôle · prompt court
TalksEngagé direct20–40 minrécit d'incident comme thriller
Mentorat 1:1Bienveillant netréponse adaptéeécouter d'abord, répondre court
Signature

Phrase de clôture

Tout ce que PDS publie peut se terminer par une phrase courte, un peu sèche, qui ferme l'incident.

$ pds out.
Incident closed. Notes archived.
Quand ça escalade, c'est moi qu'on appelle.