Ce que la refonte de mon portfolio m'a appris sur les dépendances
4 min de lecture
- Nuxt
- Performance
- Sécurité
Mon portfolio a tenu cinq ans sur une base artisanale : cinq pages HTML écrites à la main, du Sass, la grille de Bootstrap et une configuration Webpack montée pièce par pièce. Il fonctionnait. Deux points ont fini par le rendre coûteux à maintenir : le contenu vivait dans le balisage, et le nombre de domaines extérieurs contactés au chargement avait augmenté sans que je m'en aperçoive.
Cinq domaines pour un site statique
En reprenant l'ancien code, j'ai compté les domaines contactés au chargement d'une page :
fonts.googleapis.cometfonts.gstatic.compour la police,cdn.jsdelivr.netpour une feuille de reset — importée depuis le Sass, donc bloquante au rendu,media.giphy.compour une image décorative,www.google.compour un reCAPTCHA rattaché à un formulaire qui, lui, ne fonctionnait plus,instant.pagepour du préchargement au survol.
Cinq domaines, aucun sous mon contrôle. En reprenant chaque cas, je n'ai trouvé aucune raison de les garder. Le reCAPTCHA protégeait un formulaire hors service depuis un moment.
L'effet de bord qui valait le détour
J'ai fait le ménage pour le temps de chargement. Le gain que je n'attendais pas
est ailleurs : sans ressource externe, la politique de sécurité du contenu peut
démarrer à default-src 'none'.
default-src 'none'; script-src 'self' 'sha256-…'; style-src 'self';
img-src 'self' data:; font-src 'self'; connect-src 'self';
base-uri 'none'; form-action 'none'; frame-ancestors 'none'
Tout est interdit par défaut, et seul ce qui vient de mon domaine passe. Cette règle devient intenable dès qu'une seule police est chargée depuis un CDN.
Trois pièges rencontrés en chemin
Une police récupérée sans CDN
Auto-héberger une police suppose d'obtenir les fichiers. Le réflexe est de les télécharger depuis Google Fonts. Sauf que ça recrée une dépendance réseau au moment du build : sans accès à internet, la génération échoue.
Fontsource publie les mêmes polices sur npm. Le fichier arrive par le gestionnaire de paquets et il est versionné dans le dépôt. Rubik en variable pèse 35 Ko et couvre toutes les graisses en une requête.
Des icônes qui appelaient une API distante
Iconify résout les icônes via son API quand la collection n'est pas installée
localement. Le paquet était bien présent, mais un repli restait actif et le
build sortait vers api.iconify.design sans rien signaler.
La configuration explicite règle le problème :
icon: {
mode: 'svg', // SVG inline plutôt que masques CSS
fallbackToApi: false, // une icône manquante casse le build
}
fallbackToApi: false est la ligne qui compte. Sans elle, une icône mal
orthographiée passe inaperçue en développement et déclenche une requête externe
en production.
Un thème sombre sans fond
Le plus difficile à diagnostiquer. Tailwind 4 élague les variables déclarées dans
@theme qu'aucune classe n'utilise littéralement. Ma palette de neutres était
bien définie, mais comme aucune classe ne s'appelait bg-ink-900, les
variables disparaissaient du CSS final, et avec elles les tokens que Nuxt UI en
dérive.
Le thème sombre passait donc le texte en blanc et laissait le fond blanc. Du texte invisible, uniquement en mode sombre, uniquement en production.
Le correctif tient en un mot-clé : @theme static conserve toutes les
variables, utilisées ou non.
Ce que j'en ai fait
Aucun des trois ne serait apparu dans un test fonctionnel. Le site s'affichait correctement dans les trois cas, ce qui est précisément le problème.
J'ai donc écrit des scripts de vérification. L'un parcourt le HTML généré et échoue si une ressource pointe hors du domaine. Un autre calcule les ratios de contraste de la palette : c'est ainsi que j'ai découvert qu'une de mes teintes plafonnait à 4,49:1, pour un seuil fixé à 4,5.
Ils s'exécutent à chaque modification. Aucune de ces trois erreurs n'était détectable à l'œil, et rien ne garantit qu'elles le deviendraient dans six mois.