Le UX Toolkit vend la possession de ses composants plutôt que leur installation. Ce blog possède déjà les siens : 43 Twig Components, 148 contrôleurs Stimulus, 35 gabarits sur 66 qui portent de l'ARIA. La question n'est donc pas de savoir s'il faut posséder, mais ce qu'un générateur fait gagner quand on possède déjà.
Un pacte que je n'ai pas tenu
En décidant de devenir développeur backend, j'avais conclu un pacte avec les technos frontend : « never ever, blocked, banned, sashay away ». Si vous lisez ce billet, c'est que je n'ai pas tenu parole.
Il y a dix ans, j'écrivais du jQuery pour tout. Ensuite, j'ai voulu mettre tout le JavaScript et le CSS sur une île déserte pour tout brûler. Entre les deux, ce blog a fini par accumuler 43 Twig Components et 148 contrôleurs Stimulus, écrits à la main.
C'est précisément ce qui rend Symfony UX Toolkit intéressant à regarder, et pas pour la raison que son argumentaire met en avant. Il promet de vous rendre propriétaire de vos composants. Quand on l'est déjà, la promesse ne dit plus rien, et il reste la seule question utile : qu'est-ce qu'un générateur fait gagner à quelqu'un qui écrit déjà ses composants ?
Précision d'entrée de jeu : ce bundle n'est pas installé sur ce projet. Ce qui suit distingue donc ce que j'ai mesuré ici de ce que je relaie depuis sa documentation.
Ce que le Toolkit propose, et pourquoi c'est différent d'un bundle
La philosophie vient de shadcn/ui, côté React. Elle est inhabituelle : ce n'est pas une dépendance qu'on installe et qu'on appelle, c'est du code qu'on génère et qui devient le sien.
composer require --dev symfony/ux-toolkit
bin/console ux:install alert-dialogLa commande dépose dans le projet un trio : une classe Twig Component pour la logique et les props, un gabarit Twig sémantique avec ses slots, et un contrôleur Stimulus pour le comportement clavier et les attributs ARIA.
La différence avec un bundle classique est réelle. Un composant qui vit dans vendor/ s'utilise tel quel ou se configure par ses options prévues. Un composant généré dans le src/ du projet se modifie ligne à ligne, sans surcharge ni contournement. Le bundle est en --dev parce qu'il ne sert qu'à générer : une fois le composant posé, on ne l'a plus dans le chemin d'exécution.
Le corollaire est moins mis en avant : le code généré ne recevra jamais de correctif. Un composer update améliore un composant de bundle ; il ne touchera jamais celui qu'on possède. La propriété se paie en maintenance, et c'est un choix, pas un avantage net.
Ce que ce blog a construit sans lui
Le composant le plus proche de ce que le Toolkit génère, ici, est la palette de commandes ouverte au clavier. Elle fait 129 lignes de Stimulus et 61 lignes de Twig, soit 190 lignes.
Ce qu'elle porte, mesuré sur le fichier : role="combobox", role="listbox", role="search", aria-modal, aria-expanded, aria-controls, aria-autocomplete, aria-live, aria-label, aria-hidden, l'attribut inert sur l'arrière-plan, et le renvoi de focus à la fermeture.
C'est exactement la liste qu'un composant de bibliothèque éviterait d'écrire. Elle n'est pas difficile, elle est fastidieuse et facile à oublier à moitié. Sur l'ensemble du projet, 35 gabarits de composants sur 66 portent au moins un attribut ARIA, ce qui veut dire que le tiers restant n'en avait pas besoin ou n'en a pas reçu, et cette distinction-là ne se mesure pas à la commande.
Voilà ce qu'un générateur apporte réellement, et il n'a rien à voir avec le volume de code. Écrire un aria-controls prend deux secondes. Savoir qu'un combobox en demande un, plus un aria-expanded, plus un aria-activedescendant, ça demande d'avoir lu la spécification, ou d'avoir un modèle sous la main. Le Toolkit est ce modèle.
Ce qu'un générateur ne remplace pas
Un générateur ne donne pas la propriété du code. Il donne le premier jet.
La nuance compte, parce que le jour où un lecteur signale que la palette ne rend pas le focus au bon élément après une recherche vide, le correctif est à écrire, dans le fichier du projet, par nous. Que les 190 lignes de départ aient été tapées ou générées ne change rien à ce moment-là.
Ce que le générateur déplace, c'est le point de départ : au lieu de partir d'une page blanche avec la spécification ARIA ouverte dans un onglet, on part d'un composant qui marche et qu'on adapte. Pour un composant standard, modale, menu déroulant, onglets, sélecteur de date, c'est un gain net. Pour un composant qui n'existe dans aucune bibliothèque parce qu'il est propre au produit, il n'y a rien à générer.
Ce que j'en ferais
Sur ce projet, l'arbitrage me paraît assez simple, et il n'est pas celui de l'argumentaire.
Ce n'est pas un bundle qui remplace ce qu'on a. C'est un bundle en --dev qu'on installe le jour où on doit écrire un composant standard, pour ne pas repartir de la spécification. Les 43 composants déjà écrits restent ce qu'ils sont, personne ne les régénère, et le coût d'entrée est nul puisque rien ne part en production.
Reste une question que la doc ne pose pas et que je trouve plus intéressante que le débat sur la propriété du code : à partir de combien de composants faits maison est-ce que la dette cesse d'être le code, pour devenir le fait que chacun soit accessible à sa manière ?
Ce billet est publié sous licence Creative Commons BY-NC-SA 4.0 (attribution, pas d'usage commercial, partage dans les mêmes conditions).