<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet href="https://jolicode.com/feed.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xml:lang="fr-FR">
    <id>https://jolicode.com/blog</id>
    <link type="text/html" rel="alternate" href="https://jolicode.com/blog"/>
    <link type="application/rss+xml" rel="self" href="https://jolicode.com/feed" />

            <title>JoliCode blog - les derniers articles</title>
        <updated>2026-07-11T01:06:20+02:00</updated>    <entry>
        <id>https://jolicode.com/blog/profiler-un-consumer-avec-blackfire</id>
        <published>2026-07-02T11:42:00+02:00</published>
        <updated>2026-07-02T11:42:00+02:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/profiler-un-consumer-avec-blackfire"/>
        <title>Profiler un consumer avec Blackfire</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="symfony" />            <category term="performance" />            <category term="blackfire" />        <summary><![CDATA[Quand on parle de profiling PHP avec Blackfire, on pense généralement à une requête HTTP : un clic sur l&#039;extension navigateur, et hop, le profil apparaît.
Mais que faire quand le code à profiler tourne…]]></summary>
        <content type="html">
            &lt;p&gt;Quand on parle de profiling PHP avec Blackfire, on pense généralement à une requête HTTP : un clic sur l&#039;extension navigateur, et hop, le profil apparaît.
Mais que faire quand le code à profiler tourne dans un consumer ou un worker ; une commande Symfony qui boucle indéfiniment, consommant une queue ou traitant des tâches en background ?
La bonne nouvelle : Blackfire peut fonctionner dans ce contexte. La moins bonne : on ne peut pas déclencher le profiling au moment où ça nous arrange... sauf si on utilise les signaux POSIX.&lt;/p&gt;

&lt;div class=&quot;c-alert c-alert--note&quot;&gt;
    &lt;p class=&quot;c-alert__title&quot;&gt;
                    &lt;span class=&quot;c-icon c-icon--monospace&quot;&gt;
                &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; aria-hidden=&quot;true&quot; class=&quot;c-icon__svg&quot; focusable=&quot;false&quot; viewBox=&quot;0 0 70 71&quot;&gt;&lt;path fill-rule=&quot;nonzero&quot; d=&quot;M35 .9c19.3 0 35 15.7 35 35s-15.7 35-35 35-35-15.7-35-35S15.7.9 35 .9m0 5c-16.552 0-30 13.449-30 30s13.448 30 30 30c16.552.103 30-13.448 30-30 0-16.551-13.448-30-30-30m0 24.9c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V33.8c0-1.7 1.3-3 3-3m0-11c.8 0 1.6.3 2.3.9.6.5.9 1.3.9 2.1 0 .2-.1.4-.1.6-.1.2-.1.4-.2.6s-.2.3-.3.5-.3.4-.4.5c-1.1 1.1-3.1 1.1-4.2 0-.2-.2-.3-.3-.4-.5s-.2-.3-.3-.5-.2-.4-.2-.6c-.1-.2-.1-.4-.1-.6 0-.8.3-1.6.9-2.1.5-.6 1.3-.9 2.1-.9&quot;/&gt;&lt;/svg&gt;
            &lt;/span&gt;
                        &lt;strong&gt;Info&lt;/strong&gt;
    &lt;/p&gt;
    &lt;div class=&quot;c-alert__content&quot;&gt;
                &lt;p&gt;
Cet article s&#039;appuie sur les signaux POSIX. Si vous n&#039;êtes pas familier avec
ce concept, je vous invite à lire d&#039;abord &lt;a href=&quot;https://jolicode.com/blog/les-signaux-posix-et-php&quot;&gt;Les signaux POSIX et PHP&lt;/a&gt;
qui pose les bases nécessaires.&lt;/p&gt;
        &lt;/div&gt;
&lt;/div&gt;

&lt;h2&gt;Le problème&lt;/h2&gt;
&lt;p&gt;Un consumer PHP ressemble souvent à ça :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    $message &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $queue&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;consume&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;$message) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-9&quot;&gt;        sleep&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        continue&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;    $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($message);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Il tourne en permanence. On ne contrôle pas &lt;em&gt;quand&lt;/em&gt; l&#039;itération commence ni quand elle finit. Déclencher Blackfire depuis l&#039;extérieur sans interrompre le traitement en cours, c&#039;est précisément le défi.&lt;/p&gt;
&lt;p&gt;La solution : envoyer un signal UNIX au processus pour lui dire &amp;quot;commence à profiler&amp;quot; puis un second signal pour lui dire &amp;quot;arrête-toi et envoie le profil&amp;quot;.&lt;/p&gt;
&lt;h2&gt;Installation de Blackfire&lt;/h2&gt;
&lt;p&gt;L&#039;installation de Blackfire se fait via Composer :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;composer&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; require&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; blackfire/php-sdk&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;code&gt;SignalableCommandInterface&lt;/code&gt; : gérer les signaux dans une commande Symfony&lt;/h2&gt;
&lt;p&gt;Depuis Symfony 5.2, le composant Console expose l&#039;interface &lt;code&gt;SignalableCommandInterface&lt;/code&gt;.
Elle permet à une commande de s&#039;abonner à des signaux POSIX et de réagir proprement sans passer par &lt;code&gt;pcntl_signal()&lt;/code&gt; manuellement.
L&#039;interface oblige à implémenter deux méthodes :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\Console\Command\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;SignalableCommandInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;MyWorkerCommand&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-7&quot;&gt;Command&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; implements&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-7&quot;&gt;SignalableCommandInterface&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; getSubscribedSignals&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; array&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [\&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;SIGTERM&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, \&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;SIGINT&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, \&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;SIGUSR2&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; handleSignal&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $signal, &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $previousExitCode &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; int&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; false&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&lt;code&gt;getSubscribedSignals()&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Cette méthode retourne la liste des signaux que la commande veut intercepter. Les signaux sont des constantes entières définies par le système (voir &lt;code&gt;kill -l&lt;/code&gt; pour la liste complète). Les plus courants :&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Signal&lt;/th&gt;
&lt;th&gt;Valeur&lt;/th&gt;
&lt;th&gt;Usage habituel&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SIGTERM&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;Arrêt demandé (e.g. &lt;code&gt;kill &amp;lt;pid&amp;gt;&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SIGINT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Interruption clavier (&lt;code&gt;Ctrl+C&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SIGUSR1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Signal utilisateur libre n°1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SIGUSR2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;Signal utilisateur libre n°2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;&lt;code&gt;handleSignal()&lt;/code&gt; : décrypter la signature&lt;/h3&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; handleSignal&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $signal, &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $previousExitCode &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; int&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;false&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Trois éléments méritent attention :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;int $signal&lt;/code&gt;&lt;/strong&gt; : Le numéro du signal reçu. C&#039;est lui qu&#039;on inspecte avec &lt;code&gt;match&lt;/code&gt; ou &lt;code&gt;in_array&lt;/code&gt; pour savoir quoi faire ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;int|false $previousExitCode = 0&lt;/code&gt;&lt;/strong&gt; : Code de sortie renvoyé par le signal handler précédent, ou &lt;code&gt;false&lt;/code&gt; si c&#039;est le premier signal traité dans cette invocation. Utile si plusieurs signal handlers s&#039;accumulent ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Valeur de retour &lt;code&gt;int|false&lt;/code&gt;&lt;/strong&gt; : C&#039;est là que ça devient intéressant :
&lt;ul&gt;
&lt;li&gt;retourner &lt;code&gt;false&lt;/code&gt; signifie &amp;quot;je ne veux pas quitter, laisse le process continuer&amp;quot; ;&lt;/li&gt;
&lt;li&gt;retourner un entier (typiquement &lt;code&gt;0&lt;/code&gt; ou un code d&#039;erreur) signifie &amp;quot;termine le process avec ce code de sortie&amp;quot;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Autrement dit, pour un consumer qui doit continuer à tourner après avoir traité le signal (comme démarrer ou stopper un profil Blackfire), on retourne &lt;code&gt;false&lt;/code&gt;. Pour un signal d&#039;arrêt (&lt;code&gt;SIGTERM&lt;/code&gt;, &lt;code&gt;SIGINT&lt;/code&gt;), on peut soit retourner un code de sortie, soit armer un flag &lt;code&gt;shouldStop&lt;/code&gt; et retourner &lt;code&gt;false&lt;/code&gt; pour finir proprement l&#039;itération en cours.&lt;/p&gt;
&lt;h2&gt;Intégrer Blackfire via &lt;code&gt;SIGUSR2&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;L&#039;idée : on utilise &lt;code&gt;SIGUSR2&lt;/code&gt; comme interrupteur. Premier signal → on démarre la probe Blackfire. Second signal → on l&#039;arrête et on affiche le profil.&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Blackfire\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Blackfire\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;ClientConfiguration&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Blackfire\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Probe&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;ResourceAnalyserCommand&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-7&quot;&gt;Command&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; implements&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-7&quot;&gt;SignalableCommandInterface&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; bool&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $shouldStop &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; false&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; getSubscribedSignals&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; array&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $signals &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [\&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;SIGTERM&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, \&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;SIGINT&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;        // On n&#039;ajoute SIGUSR2 que si la lib Blackfire est disponible&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;class_exists&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $signals[] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; \&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;SIGUSR2&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $signals;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; handleSignal&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $signal, &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $previousExitCode &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; int&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (\&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;in_array&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($signal, [\&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;SIGTERM&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, \&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;SIGINT&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;])) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $signalName &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; SignalMap&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getSignalName&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($signal);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;            $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;stop&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;Signal {&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;$signalName&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;} received.&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (\&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;SIGUSR2&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; ===&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $signal) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $client &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Client&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; ClientConfiguration&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                $_SERVER[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;BLACKFIRE_CLIENT_ID&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                $_SERVER[&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;BLACKFIRE_CLIENT_TOKEN&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            ));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            static&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $probe &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; null&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; ===&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $probe) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;                $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;notice&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Blackfire profile started.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                $probe &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $client&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;createProbe&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            } &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                $profile &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $client&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;endProbe&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($probe);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;                $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;notice&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Blackfire profile finished.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                    &#039;url&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $profile&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getUrl&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                ]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                $probe &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; null&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; false&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Quelques points notables :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;La variable &lt;code&gt;$probe&lt;/code&gt; est &lt;code&gt;static&lt;/code&gt;&lt;/strong&gt; : elle survit entre les appels à &lt;code&gt;handleSignal()&lt;/code&gt;. Sans ça, le probe serait perdu entre le premier et le second signal ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;class_exists(Client::class)&lt;/code&gt;&lt;/strong&gt; : le package &lt;code&gt;blackfire/php-sdk&lt;/code&gt; n&#039;est pas forcément installé en production. Cette garde permet d&#039;utiliser &lt;code&gt;SIGUSR2&lt;/code&gt; uniquement quand Blackfire est disponible, sans planter si ce n&#039;est pas le cas ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;On retourne &lt;code&gt;false&lt;/code&gt;&lt;/strong&gt; : le worker ne doit pas s&#039;arrêter après un signal &lt;code&gt;SIGUSR2&lt;/code&gt;. On continue la boucle normalement pendant que Blackfire collecte les données.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Utilisation concrète&lt;/h3&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# Trouver le PID du worker&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;ps&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; aux&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; grep&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; redirectionio:crawler:analyze-resources&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# Premier signal : démarrer le profiling&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-9&quot;&gt;kill&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; -USR2&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# Laisser tourner quelques itérations...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;# Second signal : arrêter le profiling et envoyer le profil&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-9&quot;&gt;kill&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; -USR2&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;pi&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;L&#039;URL du profil apparaît dans les logs du worker. On peut ensuite l&#039;ouvrir dans l&#039;interface Blackfire pour analyser les flamegraphs, les appels de fonctions, la mémoire, etc. Exactement comme pour une requête HTTP classique.&lt;/p&gt;
&lt;p&gt;Si on ne veut pas ouvrir un deuxième terminal, nous pouvons passer le worker courant en mode &amp;quot;background&amp;quot; avec &lt;code&gt;Ctrl+Z&lt;/code&gt; puis &lt;code&gt;bg&lt;/code&gt;, et  envoyer les signaux depuis le même terminal.&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; bin/console&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; redirection:crawler:analyze-resources&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; -vv&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;15:00:28&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; NOTICE&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;    [crawler] Crawl analyzer started.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;^Z&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;      #### CTRL+Z au clavier&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;[1]+  Stopped bin/console redirection:crawler:analyze-resources -vv&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; bg&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;    #### On passe le process en background&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;[1]+ bin/console redirection:crawler:analyze-resources -vv &amp;#x26;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; kill&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; -SIGUSR2&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; %1&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;      #### On envoi le signal SIGUSR2 au premier processus en arrière-plan&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;15:00:50&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; NOTICE&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;    [crawler] Profile Blackfire started.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; kill&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; -SIGUSR2&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; %1&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;      #### On envoi le signal SIGUSR2 au premier processus en arrière-plan&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;15:01:00&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; NOTICE&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;    [crawler] Profile Blackfire finished. [&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;https://blackfire.io/XXXXXXXX/graph&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; fg&lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;    #### On repasse le process en avant-plan&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;[1]+ bin/console redirection:crawler:analyze-resources -vv&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;### ....&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Aparté : gérer l&#039;arrêt proprement avec &lt;code&gt;shouldStop&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Un consumer ne peut pas s&#039;arrêter n&#039;importe quand. Si un message est en cours de traitement au moment où &lt;code&gt;SIGTERM&lt;/code&gt; arrive (déploiement, restart Kubernetes...), couper brutalement le process risque de laisser des données dans un état incohérent.&lt;/p&gt;
&lt;p&gt;Le pattern classique : un flag booléen qu&#039;on arme à la réception du signal, qu&#039;on consulte entre chaque itération.&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;ResourceAnalyserCommand&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-7&quot;&gt;Command&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; implements&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-7&quot;&gt;SignalableCommandInterface&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; bool&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $shouldStop &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; false&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    protected&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; execute&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;InputInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $input, &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;OutputInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $output)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; int&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        while&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;            // On vérifie le flag en début d&#039;itération, pas pendant&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;shouldStop) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;                return&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Command&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;SUCCESS&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            $message &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-11&quot;&gt; $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;consume&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;            if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;$message) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-9&quot;&gt;                sleep&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;                continue&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;            // Traitement atomique : on ne sera pas interrompu ici&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;            $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($message);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; handleSignal&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $signal, &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $previousExitCode &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; int&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        if&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (\&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;in_array&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($signal, [\&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;SIGTERM&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, \&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;SIGINT&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;])) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;            $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;stop&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Signal received.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; false&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;  &lt;/span&gt;&lt;span class=&quot;syntax-10&quot;&gt;// On ne quitte pas immédiatement !&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    private&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; stop&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $reason)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;notice&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;Worker will stop after current task. {&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;$reason&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;}&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;shouldStop &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; true&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;L&#039;astuce est dans le &lt;code&gt;return false&lt;/code&gt; de &lt;code&gt;handleSignal()&lt;/code&gt; : on ne quitte pas immédiatement.
On arme juste le flag, et on laisse l&#039;itération en cours se terminer normalement. Le check &lt;code&gt;if ($this-&amp;gt;shouldStop)&lt;/code&gt; en tête de boucle assure qu&#039;on sort proprement entre deux tâches.&lt;/p&gt;
&lt;p&gt;Cette approche est particulièrement importante dans des contextes où le traitement a des effets de bord (écriture en base, appel à une API externe, mise à jour d&#039;un état distribué) et où une interruption au milieu laisserait des données incohérentes.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Profiler un consumer avec Blackfire se résume à trois ingrédients :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Implémenter &lt;code&gt;SignalableCommandInterface&lt;/code&gt; pour intercepter &lt;code&gt;SIGUSR2&lt;/code&gt; proprement ;&lt;/li&gt;
&lt;li&gt;Utiliser une variable &lt;code&gt;static&lt;/code&gt; dans &lt;code&gt;handleSignal()&lt;/code&gt; pour alterner entre &amp;quot;start probe&amp;quot; et &amp;quot;end probe&amp;quot; d&#039;un signal à l&#039;autre ;&lt;/li&gt;
&lt;li&gt;Retourner &lt;code&gt;false&lt;/code&gt; pour que le worker continue à tourner pendant et après le profiling.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Le résultat : on peut déclencher un profil Blackfire sur un consumer en production (ou en staging) sans le redémarrer, sans modifier le code, et sans interrompre le traitement en cours. Un &lt;code&gt;kill -USR2 &amp;lt;pid&amp;gt;&lt;/code&gt; suffit.&lt;/p&gt;
&lt;p&gt;Le SDK Blackfire fournit ensuite l&#039;URL du profil, qu&#039;on peut analyser dans l&#039;interface Blackfire comme pour n&#039;importe quelle requête HTTP.&lt;/p&gt;
&lt;p&gt;Enfin, le SDK propose d&#039;autres fonctionnalités avancées, comme la possibilité de créer des probes avec des paramètres spécifiques, de gérer des scénarios complexes, ou d&#039;intégrer le profiling dans des pipelines CI/CD.
Pour plus de détails, consultez la &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://docs.blackfire.io/php/integrations/sdk&quot;&gt;documentation officielle de Blackfire&lt;/a&gt;.&lt;/p&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/ameliorer-l-accessibilite-d-un-site-existant-retour-d-experience-chez-jolicode</id>
        <published>2026-07-01T09:42:00+02:00</published>
        <updated>2026-07-01T09:42:00+02:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/ameliorer-l-accessibilite-d-un-site-existant-retour-d-experience-chez-jolicode"/>
        <title>Améliorer l’accessibilité d’un site existant : retour d’expérience chez JoliCode</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="a11y" />            <category term="accessibilité" />            <category term="frontend" />            <category term="audit" />        <summary><![CDATA[Malgré des avancées réglementaires importantes, l’accessibilité numérique reste largement sous-estimée dans de nombreux projets web. Depuis le 28 juin 2025, l’European Accessibility Act (EAA) étend les…]]></summary>
        <content type="html">
            &lt;p&gt;Malgré des avancées réglementaires importantes, l’accessibilité numérique reste largement sous-estimée dans de nombreux projets web. Depuis le 28 juin 2025, l’European Accessibility Act (EAA) étend les obligations d’accessibilité à de nombreux services numériques privés. Pourtant, la réalité est encore loin des objectifs fixés : selon &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://observatoire-access-num.aveuglesdefrance.org/&quot;&gt;l’Observatoire du respect des obligations d’accessibilité numérique&lt;/a&gt;, seuls 44 sites sur 7 560 contrôlés déclarent être totalement conformes au référentiel général d’amélioration de l’accessibilité (RGAA), soit 0,58 % de l’échantillon analysé.&lt;/p&gt;
&lt;p&gt;Atteindre un bon niveau d’accessibilité demande généralement un travail de fond sur la conception, le contenu et les développements du site. Cependant, certaines améliorations simples, peu coûteuses et rapides à mettre en œuvre permettent déjà d&#039;offrir une expérience plus inclusive à l&#039;ensemble de vos visiteurs.&lt;/p&gt;
&lt;p&gt;Dans cet article, je partage les premières actions que j’ai mises en place sur le site de JoliCode. Ces améliorations ont permis de corriger plusieurs problèmes courants et constituent une première étape vers une expérience plus accessible pour toutes et tous.&lt;/p&gt;
&lt;h2&gt;L’état des lieux&lt;/h2&gt;
&lt;p&gt;La première étape consiste à réaliser ce qu’on appelle un audit d’accessibilité. Il permet de tester l’ensemble des 106 critères constitutifs du RGAA. Pour cela, l’outil &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://ara.numerique.gouv.fr/&quot;&gt;ARA&lt;/a&gt; développé par la direction interministérielle du numérique (DINUM) propose une interface en ligne et la génération de rapport et de déclaration de conformité.&lt;/p&gt;
&lt;p&gt;Pour ma part, armée de &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/&quot;&gt;la liste des critères&lt;/a&gt;, j’ai passé en revue un échantillon de pages représentatif : la page d’accueil, la page contact, la page blog et une page article. L&#039;objectif était également de mieux connaître les critères du RGAA et les différentes méthodes permettant de les évaluer. La navigation au clavier, l&#039;utilisation d&#039;un lecteur d&#039;écran et quelques extensions de navigateur m&#039;ont permis d&#039;identifier rapidement plusieurs problèmes d&#039;accessibilité. Même si l’accessibilité est prise en compte dès le début de nos projets, les mises à jour et maintenances successives, ainsi que les diverses contributions, ont introduit au fil du temps des erreurs fréquentes et faciles à corriger.&lt;/p&gt;
&lt;p&gt;Pour structurer les corrections issues de cet audit, j’ai commencé par regrouper les problèmes identifiés dans un tableau de suivi. L’objectif était de relier chaque point à son critère RGAA et de pouvoir suivre facilement les corrections à mettre en place sur le site. Le format du tableau était le suivant :&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Correction à faire&lt;/th&gt;
&lt;th&gt;Critère correspondant&lt;/th&gt;
&lt;th&gt;Statut&lt;/th&gt;
&lt;th&gt;Images&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;quot;Navigation&amp;quot; ou &amp;quot;Contrastes&amp;quot;&lt;/td&gt;
&lt;td&gt;Description de la correction à appliquer&lt;/td&gt;
&lt;td&gt;Lien vers le critère RGAA correspondant&lt;/td&gt;
&lt;td&gt;✅, 🔜 ou ❌ avec un lien vers la pull request si elle existe&lt;/td&gt;
&lt;td&gt;Capture d&#039;écran du problème constaté&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Améliorer la lisibilité des contenus&lt;/h2&gt;
&lt;p&gt;Parmi les erreurs les plus fréquentes, on retrouve souvent des problèmes de contrastes insuffisants. C’est l’un des éléments qui doit être anticipé dès la phase de conception d’un site. Il existe pourtant des &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://webaim.org/resources/contrastchecker/&quot;&gt;outils simples en ligne&lt;/a&gt; (et même des &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.figma.com/community/accessibility/contrast?resource_type=plugins&amp;amp;editor_type=all&amp;amp;price=all&amp;amp;sort_by=all_time&amp;amp;creators=all&quot;&gt;plugins Figma&lt;/a&gt;) permettant de vérifier le contraste entre une couleur d’arrière-plan et d’avant-plan. Sur le site de JoliCode la couleur rose ainsi qu’un niveau de gris, avaient un contraste insuffisant (sur fond blanc et noir pour le rose, sur fond blanc pour le gris). J’ai utilisé &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://contrast-finder.tanaguru.com/&quot;&gt;l’outil de Tanaguru&lt;/a&gt; permettant de trouver des couleurs similaires respectant les critères de contraste. L’usage du rose a été limité au fond blanc, les liens sur section sombres s’affichant désormais en jaune. Cette modification n&#039;a pas eu d’impact sur l&#039;identité visuelle du site tout en améliorant la lisibilité des contenus.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/a11y-jolisite/before-after-secondary.png&quot; data-original-width=&quot;1561&quot; data-original-height=&quot;690&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/a11y-jolisite/before-after-secondary.ae10d291.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;/media/cache/content/2026/a11y-jolisite/before-after-secondary.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1561 / 690)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/a11y-jolisite/before-after-secondary.png&quot; alt=&quot;Aperçu de la couleur de texte avant et après modification, la couleur a été légèrement assombrie sans que ça change drastiquement le rendu&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;

&lt;div class=&quot;c-alert c-alert--note&quot;&gt;
    &lt;p class=&quot;c-alert__title&quot;&gt;
                    &lt;span class=&quot;c-icon c-icon--monospace&quot;&gt;
                &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; aria-hidden=&quot;true&quot; class=&quot;c-icon__svg&quot; focusable=&quot;false&quot; viewBox=&quot;0 0 70 71&quot;&gt;&lt;path fill-rule=&quot;nonzero&quot; d=&quot;M35 .9c19.3 0 35 15.7 35 35s-15.7 35-35 35-35-15.7-35-35S15.7.9 35 .9m0 5c-16.552 0-30 13.449-30 30s13.448 30 30 30c16.552.103 30-13.448 30-30 0-16.551-13.448-30-30-30m0 24.9c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V33.8c0-1.7 1.3-3 3-3m0-11c.8 0 1.6.3 2.3.9.6.5.9 1.3.9 2.1 0 .2-.1.4-.1.6-.1.2-.1.4-.2.6s-.2.3-.3.5-.3.4-.4.5c-1.1 1.1-3.1 1.1-4.2 0-.2-.2-.3-.3-.4-.5s-.2-.3-.3-.5-.2-.4-.2-.6c-.1-.2-.1-.4-.1-.6 0-.8.3-1.6.9-2.1.5-.6 1.3-.9 2.1-.9&quot;/&gt;&lt;/svg&gt;
            &lt;/span&gt;
                        &lt;strong&gt;Info&lt;/strong&gt;
    &lt;/p&gt;
    &lt;div class=&quot;c-alert__content&quot;&gt;
                &lt;p&gt;&lt;br /&gt;
&lt;strong&gt;Critère 3.2&lt;/strong&gt; : Dans chaque page web, le contraste entre la couleur du texte et la couleur de son arrière-plan est-il suffisamment élevé ?&lt;/p&gt;
        &lt;/div&gt;
&lt;/div&gt;

&lt;h2&gt;Faciliter la navigation&lt;/h2&gt;
&lt;h3&gt;Corriger la navigation principale&lt;/h3&gt;
&lt;p&gt;Le menu de navigation principal comportait plusieurs problèmes :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Il n’utilisait pas les bons attributs &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.w3.org/WAI/standards-guidelines/aria/&quot;&gt;ARIA&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Il n’était pas totalement utilisable au clavier&lt;/li&gt;
&lt;li&gt;Le menu actif était indiqué uniquement par un changement de couleur&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pour corriger la structure du menu, je me suis basée sur le pattern “&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/examples/disclosure-navigation/&quot;&gt;Disclosure Navigation Menu&lt;/a&gt;” du guide de la Web Accessibility Initiative (WAI). Pour ne pas toucher au design mais permettre l’utilisation des sous-menus au clavier, un bouton s’affiche désormais lors de la tabulation et permet d’y accéder. La barre jaune initialement affichée uniquement au survol permet désormais d&#039;indiquer le menu actif, permettant une meilleure visibilité aux personnes qui distinguent difficilement certaines couleurs.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/a11y-jolisite/btn-submenu.png&quot; data-original-width=&quot;2468&quot; data-original-height=&quot;300&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/a11y-jolisite/btn-submenu.f66c7751.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;/media/cache/content/2026/a11y-jolisite/btn-submenu.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(2468 / 300)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/a11y-jolisite/btn-submenu.png&quot; alt=&quot;&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;

&lt;div class=&quot;c-alert c-alert--note&quot;&gt;
    &lt;p class=&quot;c-alert__title&quot;&gt;
                    &lt;span class=&quot;c-icon c-icon--monospace&quot;&gt;
                &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; aria-hidden=&quot;true&quot; class=&quot;c-icon__svg&quot; focusable=&quot;false&quot; viewBox=&quot;0 0 70 71&quot;&gt;&lt;path fill-rule=&quot;nonzero&quot; d=&quot;M35 .9c19.3 0 35 15.7 35 35s-15.7 35-35 35-35-15.7-35-35S15.7.9 35 .9m0 5c-16.552 0-30 13.449-30 30s13.448 30 30 30c16.552.103 30-13.448 30-30 0-16.551-13.448-30-30-30m0 24.9c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V33.8c0-1.7 1.3-3 3-3m0-11c.8 0 1.6.3 2.3.9.6.5.9 1.3.9 2.1 0 .2-.1.4-.1.6-.1.2-.1.4-.2.6s-.2.3-.3.5-.3.4-.4.5c-1.1 1.1-3.1 1.1-4.2 0-.2-.2-.3-.3-.4-.5s-.2-.3-.3-.5-.2-.4-.2-.6c-.1-.2-.1-.4-.1-.6 0-.8.3-1.6.9-2.1.5-.6 1.3-.9 2.1-.9&quot;/&gt;&lt;/svg&gt;
            &lt;/span&gt;
                        &lt;strong&gt;Info&lt;/strong&gt;
    &lt;/p&gt;
    &lt;div class=&quot;c-alert__content&quot;&gt;
                &lt;p&gt;&lt;br /&gt;
&lt;strong&gt;Critère 3.1&lt;/strong&gt; : Dans chaque page web, l’information ne doit pas être donnée uniquement par la couleur. Cette règle est-elle respectée ?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Critère 10.14&lt;/strong&gt; : Dans chaque page web, les contenus additionnels apparaissant via les styles CSS uniquement peuvent-ils être rendus visibles au clavier et par tout dispositif de pointage ?&lt;/p&gt;
        &lt;/div&gt;
&lt;/div&gt;

&lt;h3&gt;Ajouter un lien d’évitement&lt;/h3&gt;
&lt;p&gt;S’il est important que le menu de navigation soit accessible, il faut également permettre à l’utilisateur d’accéder facilement au contenu du site, sans forcément avoir à parcourir tout le menu. Ainsi, j’ai ajouté un lien d’accès rapide accessible via la touche tabulation dès l’arrivée sur le site. Il permet aux utilisateurs naviguant au clavier ou avec un lecteur d&#039;écran d’accéder directement au contenu du site.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/a11y-jolisite/skip-link.png&quot; data-original-width=&quot;2476&quot; data-original-height=&quot;268&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/a11y-jolisite/skip-link.382b6b38.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;/media/cache/content/2026/a11y-jolisite/skip-link.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(2476 / 268)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/a11y-jolisite/skip-link.png&quot; alt=&quot;&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;

&lt;div class=&quot;c-alert c-alert--note&quot;&gt;
    &lt;p class=&quot;c-alert__title&quot;&gt;
                    &lt;span class=&quot;c-icon c-icon--monospace&quot;&gt;
                &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; aria-hidden=&quot;true&quot; class=&quot;c-icon__svg&quot; focusable=&quot;false&quot; viewBox=&quot;0 0 70 71&quot;&gt;&lt;path fill-rule=&quot;nonzero&quot; d=&quot;M35 .9c19.3 0 35 15.7 35 35s-15.7 35-35 35-35-15.7-35-35S15.7.9 35 .9m0 5c-16.552 0-30 13.449-30 30s13.448 30 30 30c16.552.103 30-13.448 30-30 0-16.551-13.448-30-30-30m0 24.9c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V33.8c0-1.7 1.3-3 3-3m0-11c.8 0 1.6.3 2.3.9.6.5.9 1.3.9 2.1 0 .2-.1.4-.1.6-.1.2-.1.4-.2.6s-.2.3-.3.5-.3.4-.4.5c-1.1 1.1-3.1 1.1-4.2 0-.2-.2-.3-.3-.4-.5s-.2-.3-.3-.5-.2-.4-.2-.6c-.1-.2-.1-.4-.1-.6 0-.8.3-1.6.9-2.1.5-.6 1.3-.9 2.1-.9&quot;/&gt;&lt;/svg&gt;
            &lt;/span&gt;
                        &lt;strong&gt;Info&lt;/strong&gt;
    &lt;/p&gt;
    &lt;div class=&quot;c-alert__content&quot;&gt;
                &lt;p&gt;&lt;br /&gt;
&lt;strong&gt;Critère 12.7&lt;/strong&gt; : Dans chaque page web, un lien d’évitement ou d’accès rapide à la zone de contenu principal est-il présent ?&lt;/p&gt;
        &lt;/div&gt;
&lt;/div&gt;

&lt;h2&gt;Structurer l’information&lt;/h2&gt;
&lt;p&gt;L’utilisation de titres au sein d’une page web est essentielle pour structurer l’information. Elle rend la lecture plus agréable, permet de mettre des mots clés en avant d’un point de vue SEO et certains utilisateurs et utilisatrices s’en servent pour naviguer. Sur ce dernier point il est donc important d’avoir une hiérarchie de titres cohérente : éviter les sauts de titre (passer d’un h2 à un h4 par exemple) et surtout un ordre incohérent (passer d’un h1 à un h3 puis un h2). Pour cela je vous conseille l’extension &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://chromewebstore.google.com/detail/headingsmap/flbjommegcjonpdmenkdiocclhjacmbi&quot;&gt;headings map&lt;/a&gt; qui permet de visualiser simplement une erreur dans la hiérarchie de vos titres.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/a11y-jolisite/headings-map-before-after.png&quot; data-original-width=&quot;1440&quot; data-original-height=&quot;714&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/a11y-jolisite/headings-map-before-after.630baf51.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;/media/cache/content/2026/a11y-jolisite/headings-map-before-after.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1440 / 714)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/a11y-jolisite/headings-map-before-after.png&quot; alt=&quot;La page contact comportait un titre de niveau 1 &amp;quot;Contact&amp;quot; puis un titre de niveau 4 &amp;quot;Envoyez-nous un message&amp;quot; suivi d&#039;un titre de niveau 2 &amp;quot;Où nous retrouver ?&amp;quot;. Le titre de niveau 4 est désormais un titre de niveau 2.&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Sur le site de JoliCode, un titre avait été balisé de manière à prendre un certain style mais l’ordre n’était plus cohérent. Le balisage a été changé et le style géré via une classe CSS.&lt;/p&gt;

&lt;div class=&quot;c-alert c-alert--note&quot;&gt;
    &lt;p class=&quot;c-alert__title&quot;&gt;
                    &lt;span class=&quot;c-icon c-icon--monospace&quot;&gt;
                &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; aria-hidden=&quot;true&quot; class=&quot;c-icon__svg&quot; focusable=&quot;false&quot; viewBox=&quot;0 0 70 71&quot;&gt;&lt;path fill-rule=&quot;nonzero&quot; d=&quot;M35 .9c19.3 0 35 15.7 35 35s-15.7 35-35 35-35-15.7-35-35S15.7.9 35 .9m0 5c-16.552 0-30 13.449-30 30s13.448 30 30 30c16.552.103 30-13.448 30-30 0-16.551-13.448-30-30-30m0 24.9c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V33.8c0-1.7 1.3-3 3-3m0-11c.8 0 1.6.3 2.3.9.6.5.9 1.3.9 2.1 0 .2-.1.4-.1.6-.1.2-.1.4-.2.6s-.2.3-.3.5-.3.4-.4.5c-1.1 1.1-3.1 1.1-4.2 0-.2-.2-.3-.3-.4-.5s-.2-.3-.3-.5-.2-.4-.2-.6c-.1-.2-.1-.4-.1-.6 0-.8.3-1.6.9-2.1.5-.6 1.3-.9 2.1-.9&quot;/&gt;&lt;/svg&gt;
            &lt;/span&gt;
                        &lt;strong&gt;Info&lt;/strong&gt;
    &lt;/p&gt;
    &lt;div class=&quot;c-alert__content&quot;&gt;
                &lt;p&gt;&lt;br /&gt;
&lt;strong&gt;Critère 9.1&lt;/strong&gt; : Dans chaque page web, l’information est-elle structurée par l’utilisation appropriée de titres ?&lt;/p&gt;
        &lt;/div&gt;
&lt;/div&gt;

&lt;h2&gt;Donner du sens aux images&lt;/h2&gt;
&lt;p&gt;Vient le sujet majeur de l’accessibilité des images et son fameux attribut &lt;code&gt;alt&lt;/code&gt;. On en a tellement entendu parler que certains contributeurs se mettent désormais à le remplir, même quand ce n’est pas nécessaire.&lt;/p&gt;
&lt;p&gt;Voici ce qu’il faut retenir :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;une image qui n’apporte pas d’information supplémentaire &lt;strong&gt;doit avoir un attribut alt vide&lt;/strong&gt;. Cela signifie que même si l’image est porteuse d’information, si cette information est présente dans le texte à côté de l’image, il est inutile de l’indiquer dans l’attribut alt. Le lecteur d’écran risquerait de lire deux fois la même information, générant du bruit inutile. Le mieux est l’ennemi du bien.&lt;/li&gt;
&lt;li&gt;En revanche, si l&#039;image est essentielle à la compréhension, il faut remplir l’attribut alt pour la décrire.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Demandez-vous simplement : si on retire l’image, est-ce que l’information reste compréhensible dans son entièreté ? Ou au contraire est-ce que l’on perd de l’information ? Si retirer l’image ne change rien alors elle n’est pas porteuse de sens et l’attribut alt doit être présent, mais vide.&lt;/p&gt;
&lt;p&gt;En partant de ce constat, plusieurs attributs alt ont été corrigés, même si, nombre d’entre eux étant contribués dans les pages article, il en reste sûrement à améliorer.&lt;/p&gt;
&lt;p&gt;Les SVG méritent également une attention particulière. Pour les SVG en ligne, il faut là aussi, dissocier ceux qui sont porteurs d’information et ceux qui ne le sont pas. Dans le premier cas, il est nécessaire de fournir une alternative textuelle directement dans le SVG, par exemple à l&#039;aide d&#039;un élément &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;svg&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; role&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;img&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;texte de description&amp;#x3C;/&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;svg&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dans le deuxième cas, l’attribut &lt;code&gt;aria-hidden=&amp;quot;true&amp;quot;&lt;/code&gt; doit être ajouté à la balise.&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;svg&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; aria-hidden&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;…&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;svg&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;c-alert c-alert--tip&quot;&gt;
    &lt;p class=&quot;c-alert__title&quot;&gt;
                    &lt;span class=&quot;c-icon c-icon--monospace&quot;&gt;
                &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; aria-hidden=&quot;true&quot; class=&quot;c-icon__svg&quot; focusable=&quot;false&quot; viewBox=&quot;0 0 46 72&quot;&gt;&lt;path fill-rule=&quot;nonzero&quot; d=&quot;M45.7 23.2C45.7 10.7 35.5.5 23 .5S.3 10.7.3 23.2c0 1.8.2 3.6.7 5.4.6 2.9 1.7 4.7 3.2 7.2.3.6.7 1.2 1.1 1.9.5.8.9 1.6 1.4 2.3 2 3.3 3.2 5.2 3.2 9.1v9.4c0 2.4 1.7 4.3 4 4.7 1 5.1 4 8.3 9.1 8.3s8.2-3.2 9.1-8.3c2.3-.4 4-2.4 4-4.7v-9.4c0-3.9 1.2-5.9 3.2-9.1.4-.7.9-1.5 1.4-2.3.4-.7.8-1.3 1.1-1.9 1.5-2.5 2.6-4.3 3.2-7.2.5-1.8.7-3.6.7-5.4M31.2 50.9H15.287v-1.917c0-.416 0-.75-.087-1.083h16c0 .333-.087.667-.087 1.083V50.9zm-1.016 7.5H15.603c-.44 0-.703-.308-.703-.615V55.4h15.986v2.385c.088.307-.263.615-.702.615m-7.124 8c-.87 0-3.089 0-3.96-3h8c-.871 3-3.168 3-4.04 3m17.091-38.664c-.468 2.072-1.216 3.484-2.526 5.65-.375.564-.655 1.129-1.03 1.788-.468.753-.842 1.506-1.216 2.071-1.123 1.883-2.153 3.578-2.808 5.555h-18.53c-.654-1.977-1.59-3.672-2.807-5.555-.374-.659-.842-1.318-1.216-2.071-.375-.659-.749-1.318-1.03-1.789-1.31-2.26-2.059-3.577-2.527-5.743a16.5 16.5 0 0 1-.561-4.236C5.9 13.708 13.761 5.8 23.4 5.8s17.5 7.908 17.5 17.606c-.187 1.412-.374 2.824-.749 4.33&quot;/&gt;&lt;/svg&gt;
            &lt;/span&gt;
                        &lt;strong&gt;Astuce&lt;/strong&gt;
    &lt;/p&gt;
    &lt;div class=&quot;c-alert__content&quot;&gt;
                &lt;p&gt;&lt;br /&gt;
L’attribut &lt;code&gt;focusable=&amp;quot;false&amp;quot;&lt;/code&gt; n’est plus nécessaire sur les SVG inline non porteurs de sens à moins que vous ne souhaitiez supporter les navigateurs Internet Explorer et Edge avant 2020 (avant le passage à Chromium).&lt;/p&gt;
        &lt;/div&gt;
&lt;/div&gt;


&lt;div class=&quot;c-alert c-alert--note&quot;&gt;
    &lt;p class=&quot;c-alert__title&quot;&gt;
                    &lt;span class=&quot;c-icon c-icon--monospace&quot;&gt;
                &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; aria-hidden=&quot;true&quot; class=&quot;c-icon__svg&quot; focusable=&quot;false&quot; viewBox=&quot;0 0 70 71&quot;&gt;&lt;path fill-rule=&quot;nonzero&quot; d=&quot;M35 .9c19.3 0 35 15.7 35 35s-15.7 35-35 35-35-15.7-35-35S15.7.9 35 .9m0 5c-16.552 0-30 13.449-30 30s13.448 30 30 30c16.552.103 30-13.448 30-30 0-16.551-13.448-30-30-30m0 24.9c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V33.8c0-1.7 1.3-3 3-3m0-11c.8 0 1.6.3 2.3.9.6.5.9 1.3.9 2.1 0 .2-.1.4-.1.6-.1.2-.1.4-.2.6s-.2.3-.3.5-.3.4-.4.5c-1.1 1.1-3.1 1.1-4.2 0-.2-.2-.3-.3-.4-.5s-.2-.3-.3-.5-.2-.4-.2-.6c-.1-.2-.1-.4-.1-.6 0-.8.3-1.6.9-2.1.5-.6 1.3-.9 2.1-.9&quot;/&gt;&lt;/svg&gt;
            &lt;/span&gt;
                        &lt;strong&gt;Info&lt;/strong&gt;
    &lt;/p&gt;
    &lt;div class=&quot;c-alert__content&quot;&gt;
                &lt;p&gt;&lt;br /&gt;
&lt;strong&gt;Critère 4.8&lt;/strong&gt; : Chaque média non temporel a-t-il, si nécessaire, une alternative ?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Critère 4.9&lt;/strong&gt; : Pour chaque média non temporel ayant une alternative, cette alternative est-elle pertinente ?&lt;/p&gt;
        &lt;/div&gt;
&lt;/div&gt;

&lt;h2&gt;Rendre les formulaires plus explicites&lt;/h2&gt;
&lt;p&gt;Lier un champ de formulaire à un libellé est globalement maîtrisé. Mais sur le site de JoliCode, il y a un cas que nous n’avions pas forcément anticipé. Sur la page blog, le formulaire de recherche possède un champ avec un libellé non visible.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/a11y-jolisite/search-form.17a5de1c.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;/media/cache/content/2026/a11y-jolisite/search-form.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 864px; ; aspect-ratio: calc(864 / 156)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/a11y-jolisite/search-form.png&quot; alt=&quot;Le formulaire est constitué d&#039;un champ avec une icône loupe et un placeholder &amp;quot;Saisissez votre recherche&amp;quot; ainsi qu&#039;un bouton &amp;quot;Rechercher&amp;quot;&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Si un label est bien présent dans le code et accessible aux technologies d’assistance grâce à une classe &lt;code&gt;sr-only&lt;/code&gt;, il ne satisfait pas le critère 11.1.3 du RGAA :&lt;/p&gt;

&lt;div class=&quot;c-alert c-alert--note&quot;&gt;
    &lt;p class=&quot;c-alert__title&quot;&gt;
                    &lt;span class=&quot;c-icon c-icon--monospace&quot;&gt;
                &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; aria-hidden=&quot;true&quot; class=&quot;c-icon__svg&quot; focusable=&quot;false&quot; viewBox=&quot;0 0 70 71&quot;&gt;&lt;path fill-rule=&quot;nonzero&quot; d=&quot;M35 .9c19.3 0 35 15.7 35 35s-15.7 35-35 35-35-15.7-35-35S15.7.9 35 .9m0 5c-16.552 0-30 13.449-30 30s13.448 30 30 30c16.552.103 30-13.448 30-30 0-16.551-13.448-30-30-30m0 24.9c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V33.8c0-1.7 1.3-3 3-3m0-11c.8 0 1.6.3 2.3.9.6.5.9 1.3.9 2.1 0 .2-.1.4-.1.6-.1.2-.1.4-.2.6s-.2.3-.3.5-.3.4-.4.5c-1.1 1.1-3.1 1.1-4.2 0-.2-.2-.3-.3-.4-.5s-.2-.3-.3-.5-.2-.4-.2-.6c-.1-.2-.1-.4-.1-.6 0-.8.3-1.6.9-2.1.5-.6 1.3-.9 2.1-.9&quot;/&gt;&lt;/svg&gt;
            &lt;/span&gt;
                        &lt;strong&gt;Info&lt;/strong&gt;
    &lt;/p&gt;
    &lt;div class=&quot;c-alert__content&quot;&gt;
                &lt;p&gt;&lt;br /&gt;
Chaque champ de formulaire ayant une étiquette dont le contenu n’est pas visible ou à proximité (masqué, &lt;code&gt;aria-label&lt;/code&gt;) ou qui n’est pas accolé au champ (&lt;code&gt;aria-labelledby&lt;/code&gt;), vérifie-t-il une de ses conditions ?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Le champ de formulaire possède un attribut &lt;code&gt;title&lt;/code&gt; dont le contenu permet de comprendre la nature de la saisie attendue ;&lt;/li&gt;
&lt;li&gt;Le champ de formulaire est accompagné d’un passage de texte accolé au champ qui devient visible à la prise de focus permettant de comprendre la nature de la saisie attendue ;&lt;/li&gt;
&lt;li&gt;Le champ de formulaire est accompagné d’un passage de texte visible accolé au champ permettant de comprendre la nature de la saisie attendue.&lt;/li&gt;
&lt;/ul&gt;
        &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;Même si le champ possède un placeholder, celui-ci disparaissant au focus, le référentiel demande qu’un autre texte visible indique la fonction du champ de formulaire. Afin d’éviter un changement graphique, j’ai ajouté un attribut title pour répondre au critère.&lt;/p&gt;
&lt;h2&gt;Enseignements et perspectives&lt;/h2&gt;
&lt;p&gt;Ces différentes corrections représentent finalement peu d’efforts et de lignes de code pour un gain non négligeable pour de nombreuses personnes. Elles correspondent à des erreurs parmi les plus fréquemment rencontrées :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;le respect des contrastes de couleurs,&lt;/li&gt;
&lt;li&gt;la bonne gestion des alternatives textuelles des images et des SVG,&lt;/li&gt;
&lt;li&gt;une navigation entièrement utilisable au clavier,&lt;/li&gt;
&lt;li&gt;l’ajout d’un lien d’évitement,&lt;/li&gt;
&lt;li&gt;et une meilleure explicitation des formulaires.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pour autant, ce travail ne suffit pas à rendre un site conforme au RGAA. L’accessibilité reste un sujet transversal qui concerne aussi bien la conception, le design, le développement que la production de contenu. Ces premières améliorations constituent donc moins une finalité qu’un point de départ vers une démarche d’amélioration continue.&lt;/p&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/ux-days-2026-design-ia-et-conscience-ce-que-cette-journee-nous-a-appris-sur-notre-metier</id>
        <published>2026-06-24T11:42:00+02:00</published>
        <updated>2026-06-24T11:42:00+02:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/ux-days-2026-design-ia-et-conscience-ce-que-cette-journee-nous-a-appris-sur-notre-metier"/>
        <title>UX Days 2026 : Design, IA et conscience, ce que cette journée nous a appris sur notre métier</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="conférences" />            <category term="ux" />            <category term="ia" />            <category term="ui" />        <summary><![CDATA[L&#039;intelligence artificielle redessine nos métiers. C&#039;est le constat que partagent aujourd&#039;hui designers, développeurs et product managers. Les UX Days 2026 n&#039;ont pas échappé à cette réalité. Parmi les…]]></summary>
        <content type="html">
            &lt;p&gt;L&#039;intelligence artificielle redessine nos métiers. C&#039;est le constat que partagent aujourd&#039;hui designers, développeurs et product managers. Les UX Days 2026 n&#039;ont pas échappé à cette réalité. Parmi les nombreuses conférences de la journée, six nous ont particulièrement marqués et partageaient un fil rouge : &lt;strong&gt;comment concevoir avec conscience à l&#039;ère de l&#039;IA ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/ux-days-2026/uxdays01.png&quot; data-original-width=&quot;1920&quot; data-original-height=&quot;980&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/ux-days-2026/uxdays01.831d22fb.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;/media/cache/content/2026/ux-days-2026/uxdays01.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1920 / 980)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/ux-days-2026/uxdays01.png&quot; alt=&quot;Alt text&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Keynote d&#039;ouverture avec Pablo Ruiz-Múzquiz : L&#039;avenir du design UX est-il l’open source ?&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;La matinée s&#039;est ouverte avec Pablo Ruiz-Múzquiz, cofondateur et CEO de Penpot, une plateforme de design UX open source. Physicien de formation, il cofonde en 2011 Kaleidos, une entreprise avec une équipe de designers et développeurs. Pablo porte une conviction forte : les outils que nous utilisons ne sont pas neutres.&lt;/p&gt;
&lt;p&gt;Il part d&#039;un constat simple : quand on parle de &amp;quot;l&#039;avenir du design UX&amp;quot;, on ne parle pas tous de la même chose. Certains pensent à qui va concevoir, d&#039;autres aux outils utilisés,  et d&#039;autres encore à pour qui on conçoit. Pour lui, c&#039;est avant tout une question de capacité à résoudre de vrais problèmes avec les contraintes du réel.&lt;/p&gt;
&lt;p&gt;Si le matériel reste la contrainte principale de nos usages, il n&#039;évoluera pas de sitôt (smartphones, laptops, claviers, souris, sans évolution imminente à l&#039;horizon). C&#039;est donc du côté du software que viendra le changement, notamment via les LLM. Deux conséquences concrètes : des expériences de plus en plus personnalisées pour chaque utilisateur, et l&#039;émergence d&#039;agents IA qui utilisent nos interfaces exactement comme nous le ferions. Ce dernier point soulève une question nouvelle pour les designers : comment concevoir une interface qui fonctionne bien à la fois pour un humain et pour un agent IA ? C&#039;est ce que Pablo appelle la frontière entre UX et AX et cette frontière va progressivement devenir flou.&lt;/p&gt;
&lt;p&gt;Avant de parler de Penpot, Pablo s&#039;arrête sur ce qu&#039;est vraiment l&#039;open source.  Ce n&#039;est pas une collection de logiciels gratuits, c&#039;est un mécanisme juridique qui redistribue des libertés. Il produit trois choses concrètes : de la confiance car le code est auditable, une collaboration distribuée à l&#039;échelle mondiale, et une souveraineté numérique difficile à abandonner une fois acquise.&lt;/p&gt;
&lt;p&gt;Pour illustrer ces convictions, Pablo détaille les cinq choix structurants de Penpot :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Être open source pour ne pas laisser le marché aux seuls acteurs dominants&lt;/strong&gt;, même si cela implique une gouvernance plus complexe des communautés ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adopter un design basé sur les standards CSS&lt;/strong&gt; (Flexbox, Grid, tokens natifs) pour créer une correspondance directe avec le code ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Proposer une interface familière aux outils existants&lt;/strong&gt; pour réduire la courbe d&#039;apprentissage ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Parier sur les standards ouverts&lt;/strong&gt;, même quand ça coûte des mois de refonte ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Faire de Penpot un outil autant pour les designers que pour les développeurs&lt;/strong&gt;, ces derniers y sont aujourd&#039;hui deux fois plus nombreux.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Sa question de clôture est simple : &lt;strong&gt;quels seront vos choix de design ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Chez JoliCode, l&#039;&lt;a href=&quot;https://jolicode.com/nos-metiers/contributions&quot;&gt;open source est au cœur de notre travail et des outils que nous mettons en place&lt;/a&gt;. Pablo n&#039;a fait que nous donner de bons arguments pour continuer.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/ux-days-2026/uxdays02.png&quot; data-original-width=&quot;1920&quot; data-original-height=&quot;980&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/ux-days-2026/uxdays02.14b53520.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;/media/cache/content/2026/ux-days-2026/uxdays02.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1920 / 980)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/ux-days-2026/uxdays02.png&quot; alt=&quot;Alt text&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Matthieu Froidure : Comment l&#039;IA peut compléter (sans remplacer) une démarche d&#039;accessibilité numérique ?&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Matthieu Froidure est aveugle depuis quinze ans et expert en accessibilité chez Urbilog. Il arrive sur scène et avant tout, il veut mettre la salle dans sa peau.&lt;/p&gt;
&lt;p&gt;Il demande au public de se lever, de fermer les yeux, et de serrer la main du voisin le plus proche. &amp;quot;Vous venez de perdre 70% de votre information quotidienne.&amp;quot; L&#039;expérience est assez troublante.&lt;/p&gt;
&lt;p&gt;Avant d&#039;aborder l&#039;IA, Matthieu pose le cadre légal sur l’accessibilité. En France, l&#039;accessibilité numérique est une obligation depuis 2005 pour le secteur public, étendue aux grandes entreprises en 2016. Mais c&#039;est en juin 2025 que tout change avec l&#039;European Accessibility Act : toute entreprise avec plus de 2 millions d&#039;euros de chiffre d&#039;affaires ou 10 salariés ayant une relation directe avec des clients est désormais concernée. C’est un enjeu important.&lt;/p&gt;
&lt;p&gt;Ce cadre posé, il entre dans le vif du sujet : respecter les 106 critères du RGAA ne garantit pas qu&#039;une interface soit utilisable. Il diffuse deux versions sonores d&#039;un même tableau INSEE, une d&#039;abord inaccessible, puis une version conforme. Dans les deux cas, comprendre les données demande un effort cognitif considérable. Un site peut être accessible sur le papier mais incompréhensible en conditions réelles.&lt;/p&gt;
&lt;p&gt;C&#039;est là que l&#039;IA entre en jeu et Matthieu en dresse un portrait nuancé. L’IA peut être utile pour réexprimer une information complexe, la délivrer à l&#039;oral et répondre à des questions précises (par exemple pour lire un tableau de garanties mutuelles, adapter le contenu en Facile à Lire et à Comprendre pour les personnes déficientes intellectuelles). Il en a lui-même fait l&#039;expérience avec Claude pour installer un logiciel via son lecteur d&#039;écran NVDA, et globalement ça a marché.&lt;/p&gt;
&lt;p&gt;Mais l&#039;IA a trois biais qu&#039;il faut garder en tête :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Le biais d&#039;information&lt;/strong&gt; : elle peut donner des réponses fausses avec la même conviction que des réponses exactes ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;La peur du silence&lt;/strong&gt; : elle préfère inventer plutôt que se taire ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Le biais de complaisance&lt;/strong&gt; : elle ne contredira jamais l&#039;utilisateur même s&#039;il a tort.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Matthieu en a fait les frais lui-même en demandant à une IA un itinéraire de transport à Paris et il s&#039;est retrouvé à errer une demi-heure dans la mauvaise direction.&lt;/p&gt;
&lt;p&gt;Matthieu appuie donc sur l’importance de tester nos interfaces avec des extreme users. L&#039;IA n&#039;a pas les données pour couvrir ces cas extrêmes mais ce sont précisément ces cas qui une fois résolus bénéficient à tous. La télécommande a été conçue pour les personnes à mobilité réduite. Le SMS pour les sourds.&lt;/p&gt;
&lt;p&gt;Sa conclusion est simple : &lt;strong&gt;concevoir pour les extrêmes, c&#039;est concevoir mieux pour tout le monde&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/ux-days-2026/uxdays03.png&quot; data-original-width=&quot;1920&quot; data-original-height=&quot;980&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/ux-days-2026/uxdays03.99d23869.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;/media/cache/content/2026/ux-days-2026/uxdays03.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1920 / 980)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/ux-days-2026/uxdays03.png&quot; alt=&quot;Alt text&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Antoine Pezé, Les choix qu’on ne faisait pas : quand un designer reprend le pouvoir sur le code&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Antoine Pezé est UX designer et entrepreneur depuis une quinzaine d&#039;années, ancien coach UX chez Leroy Merlin. En octobre 2025, il se lance seul dans le développement d&#039;une application mobile autour du jeu de cartes Riftbound, sans savoir coder et avec Claude Code comme seul partenaire.&lt;/p&gt;
&lt;p&gt;Il en tire quatre enseignements qu&#039;il n&#039;aurait jamais vus sans avoir été confronté au code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Premier enseignement&lt;/strong&gt; : tester en conditions réelles révèle des choix de design invisibles. En voulant afficher 50 000 tournois d&#039;un coup, l&#039;application plantait. La solution n&#039;était pas technique, c&#039;était un choix de design qu&#039;il n&#039;avait pas fait au préalable. Airbnb n&#039;affiche que 270 résultats par recherche, pas plus. La performance est une dimension UX qu&#039;on délègue habituellement au développeur et en travaillant seul directement dans le code, Antoine a dû se confronter à de nouvelles problématiques.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Deuxième enseignement&lt;/strong&gt; : réduire la boucle de feedback rend les maquettes facultatives. Après quelques semaines, Antoine réalise qu&#039;il n&#039;utilise plus Figma depuis six mois. Il décrit son intention, Claude propose un design, et il itère directement depuis le code. Le code est devenu un support d&#039;itération qui remplace le sketch et ça change tout à sa manière de concevoir.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Troisième enseignement&lt;/strong&gt; : discuter avec une IA pousse à beaucoup mieux spécifier. En lui demandant de designer une page et de remonter les cas limites, Antoine se retrouve face à une dizaine de questions précises sur la densité de navigation, l&#039;intégration dans l&#039;onboarding, ou encore la terminologie. Des questions qu&#039;il n&#039;aurait pas forcément formulées seul.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Quatrième enseignement&lt;/strong&gt; : mettre du code en production efface les frontières entre design et développement. Antoine avoue lui-même avoir du mal à définir son activité car il ne sait pas lire une ligne de code, mais ce qu&#039;il fait se rapproche davantage d&#039;un job de développeur. Il a réussi à mettre une application en production sur iPhone et Android, intégrer de l&#039;OCR pour scanner des cartes, récupérer et analyser des données satellites pour détecter des coupes sauvages en France. Des choses qu&#039;il pensait impossibles il y a encore six mois. Et il pose la question symétrique : qu&#039;est-ce qui empêche un développeur de designer, maintenant que les maquettes peuvent être générées par l&#039;IA ?&lt;/p&gt;
&lt;p&gt;Antoine conclut sur une note réaliste. Claude Code fonctionne très bien sur des projets jeunes, des prototypes et des contextes où l&#039;enjeu est d&#039;explorer. Ce qu&#039;il retient surtout, c&#039;est le sentiment de pouvoir enfin explorer sans limite et seul. Mais cette liberté a ses limites dès qu&#039;on entre dans le monde de l&#039;entreprise. Sur des projets legacy avec des années de règles métier, c&#039;est une autre affaire. Antoine affirme que la vraie barrière qui résiste n&#039;est pas la production, c&#039;est la prise de décision. Selon une étude du MIT, 95% des projets IA en entreprise en 2025 n&#039;ont pas généré de valeur, non pas parce que les modèles étaient mauvais, mais parce que les organisations ne savaient pas se mettre d&#039;accord sur ce qu&#039;il fallait construire. L&#039;IA peut produire plus vite, encore faut-il savoir quoi lui demander.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/ux-days-2026/uxdays04.png&quot; data-original-width=&quot;1920&quot; data-original-height=&quot;980&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/ux-days-2026/uxdays04.8df9db92.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;/media/cache/content/2026/ux-days-2026/uxdays04.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1920 / 980)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/ux-days-2026/uxdays04.png&quot; alt=&quot;Alt text&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Anna E. Cook : L&#039;accessibilité comme infrastructure à l&#039;ère de l&#039;IA&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Anna E. Cook, product et systems designer spécialisée en accessibilité et design inclusif, ouvre l&#039;après-midi avec un message clair : l&#039;IA ne rendra pas vos interfaces accessibles à votre place. Elle le démontre en déconstruisant quatre mythes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Premier mythe&lt;/strong&gt; : l&#039;IA rendrait enfin nos interfaces vraiment adaptatives. Faux. Les utilisateurs ont toujours zoomé, changé la taille de leur texte, modifié leurs paramètres. La variabilité était déjà là avant l’IA.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Deuxième mythe&lt;/strong&gt; : le diagnostic médical comme stratégie de design. Nielsen, figure incontournable de l&#039;UX, a proposé de personnaliser une interface en fonction du diagnostic d&#039;un utilisateur. Anna s&#039;y oppose car le diagnostic arrive souvent longtemps après que le besoin s&#039;est manifesté. Elle en sait quelque chose car pendant des mois sa vision s&#039;est dégradée sans qu&#039;elle ait encore de diagnostic. Ce dont elle avait besoin : zoomer et agrandir le texte, cela ne nécessitait donc aucun étiquetage médical. Deux personnes avec le même diagnostic peuvent avoir des besoins très différents et inversement. Ce qui compte c&#039;est la variabilité des besoins, pas le diagnostic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Troisième mythe&lt;/strong&gt; : plus une interface est personnalisée, meilleure elle est. Anna nuance. Une interface qui change constamment pour s&#039;adapter à vous fragilise vos repères, pourtant essentiels. Imaginez vous connecter à votre application bancaire et ne plus reconnaître rien : les menus ont bougé, les couleurs ont changé. Vous êtes perdu. La bonne approche est celle de GitHub : tous les liens sont soulignés par défaut pour ceux qui en ont besoin, mais chacun peut désactiver cette option. En résumé, d&#039;abord garantir les besoins essentiels et ensuite laisser la liberté d&#039;adapter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Quatrième mythe&lt;/strong&gt; : l&#039;IA peut réparer une structure d&#039;accessibilité cassée. C’est faux. Les modèles ont été entraînés sur un web massivement inaccessible et reproduisent ce qu&#039;ils ont appris. Un collègue d&#039;Anna chez Microsoft évalue quotidiennement les LLM sur leur capacité à produire du code conforme WCAG. Résultat : au mieux 30% de réussite par défaut. Les données WebAIM 2026 confirment cette tendance : 95,9% des pages d&#039;accueil présentent des erreurs d&#039;accessibilité de base, avec une hausse de 10% des erreurs par page. Une tendance attribuée en partie à la généralisation de l&#039;IA dans la production de code.&lt;/p&gt;
&lt;p&gt;En réponse à ses mythes, Anna propose six questions à se poser avant d&#039;intégrer l&#039;IA dans un système : quels sont nos biais de design ? Quels éléments ne doivent jamais changer ? Qui est responsable des sorties de l&#039;IA ? Quelle est l&#039;expérience par défaut sans IA ? Quels signaux les utilisateurs envoient-ils déjà via leurs paramètres OS et navigateur ? Et surtout, est-ce que l&#039;IA résout réellement un problème ici ?&lt;/p&gt;
&lt;p&gt;Anna n&#039;est pas contre l’IA, elle est pour &lt;strong&gt;l’utiliser avec intention&lt;/strong&gt;. Elle le rappelle clairement : l&#039;IA a des coûts réels, financiers, opérationnels, environnementaux et n&#039;est pas la bonne réponse à chaque problème.&lt;/p&gt;
&lt;p&gt;Elle termine sur un message clair pour les designers : &lt;strong&gt;&amp;quot;Tous les produits n&#039;ont pas besoin de l&#039;IA. Mais chaque produit a besoin de systèmes accessibles et bien conçus.”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/ux-days-2026/uxdays05.png&quot; data-original-width=&quot;1920&quot; data-original-height=&quot;980&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/ux-days-2026/uxdays05.009ed6db.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;/media/cache/content/2026/ux-days-2026/uxdays05.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1920 / 980)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/ux-days-2026/uxdays05.png&quot; alt=&quot;Alt text&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Bastien Hughes : Designer, l’IA va nous rendre idiots… ou meilleurs&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Bastien Hughes, Head of Design chez Bouygues Telecom, pose d&#039;emblée une question volontairement provocatrice : l&#039;IA va-t-elle nous rendre idiots ou meilleurs ? Sa réponse : les deux, cela dépend de comment on l&#039;utilise.&lt;/p&gt;
&lt;p&gt;Pour introduire son propos, Bastien remonte soixante-treize ans en arrière. En 1953, Albert Ducrocq, ingénieur français et créateur du premier robot français, publiait L&#039;Ère des robots. Sa mise en garde est étonnamment actuelle : &amp;quot;Il doit y avoir un juste équilibre entre la part humaine et la vie artificielle.&amp;quot; D’autres penseurs feront écho à cette inquiétude dans les décennies suivantes.&lt;/p&gt;
&lt;p&gt;Ces mises en garde sont aujourd&#039;hui confirmées par la science. Pourquoi avons-nous cette tendance naturelle à déléguer ? Parce que le cerveau, qui représente 2% de notre poids mais consomme 20% de notre énergie, cherche naturellement à économiser ses ressources. Le GPS a affaibli notre sens de l&#039;orientation. Le correcteur orthographique a baissé notre vigilance. Aujourd&#039;hui, un quart des utilisateurs français d&#039;IA déclarent déjà ne plus être capables d&#039;effectuer certaines tâches sans elle.&lt;/p&gt;
&lt;p&gt;Trois facteurs aggravent cette tendance. D&#039;abord, le design des IA elles-mêmes est conçu pour capter notre attention et contourner notre esprit critique. Ensuite, la pression des entreprises à mettre de l&#039;IA partout, souvent sans se demander si elle résout un vrai problème. Enfin, l&#039;argument économique : réduire les coûts, augmenter la productivité. Résultat, comme le résume Maslow : &amp;quot;Quand le seul outil dont vous disposez est un marteau, tout ressemble à un clou.&amp;quot;&lt;/p&gt;
&lt;p&gt;Pourtant, quand l&#039;IA répond à un vrai besoin, Bastien le confirme : ça fonctionne. Le Bon Coin en est un bon exemple. En générant automatiquement les descriptions d&#039;annonces à partir d&#039;une photo et d&#039;un titre, ils ont résolu un vrai problème : rédiger dix annonces, c&#039;est long et fastidieux pour beaucoup d&#039;utilisateurs.&lt;/p&gt;
&lt;p&gt;C&#039;est dans cet esprit que Bastien propose un cadre : &lt;strong&gt;le Nutri-Score de l&#039;IA&lt;/strong&gt;. En bas de l&#039;échelle (E), l&#039;usage le plus néfaste : je demande, l&#039;IA exécute et je valide sans réfléchir. En haut (A), l&#039;usage le plus bénéfique : je structure ma pensée et je sollicite l&#039;IA pour me challenger, elle me pose des questions qui m&#039;obligent à raisonner. C&#039;est ce qu&#039;il appelle le &amp;quot;ping-pong de la pensée”.&lt;/p&gt;
&lt;p&gt;L&#039;IA permet aussi de tester ses idées plus tôt dans le processus. Prototyper rapidement, itérer avec des personas IA, tester avant de présenter.&lt;/p&gt;
&lt;p&gt;Bastien conclut : &lt;strong&gt;l&#039;IA nous rendra idiots ou meilleurs selon l&#039;usage qu&#039;on en fait.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/ux-days-2026/uxdays06.png&quot; data-original-width=&quot;1920&quot; data-original-height=&quot;980&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/ux-days-2026/uxdays06.9736c100.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;/media/cache/content/2026/ux-days-2026/uxdays06.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1920 / 980)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/ux-days-2026/uxdays06.png&quot; alt=&quot;Alt text&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Marine Lochet : Fiction et design : concevoir depuis ce qui dérange&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Marine Lochet, designer et auteure, est la dernière conférencière que nous souhaitons vous présenter et son intervention mérite qu&#039;on s&#039;y attarde. Elle démarre par trois récits, deux fictifs et un réel, apportant une atmosphère particulière dans la salle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Le premier, Never Let Me Go de Kazuo Ishiguro&lt;/strong&gt; : des enfants élevés dans un pensionnat idyllique, destinés à donner leurs organes. Tout a été si bien lissé qu&#039;ils acceptent leur propre fin comme une formalité.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Le deuxième, BioShock&lt;/strong&gt; : un personnage qui n&#039;a jamais pris une seule décision, sa liberté n&#039;était qu&#039;un scénario écrit par quelqu&#039;un d&#039;autre.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Le troisième, réel celui-là : le scroll infini, inventé par Aza Raskin pour fluidifier la navigation&lt;/strong&gt;. Quelques années plus tard, il réalise qu&#039;il a créé un mécanisme de capture de l&#039;attention humaine.&lt;/p&gt;
&lt;p&gt;Trois récits avec une même mécanique : le design comme machine à lisser le réel jusqu&#039;à rendre la résistance impossible. Ce mécanisme, Marine le retrouve au cœur même de notre pratique, dans un principe que tous les designers connaissent : &amp;quot;Don&#039;t Make Me Think&amp;quot; de Steve Krug. L&#039;idée : une interface doit être si intuitive que l&#039;utilisateur n&#039;a jamais à réfléchir pour l&#039;utiliser. On l&#039;applique désormais aux formulaires de résiliation, aux paramètres de consentement, aux confirmations de paiement, là où la friction peut protéger l’utilisateur. Car face à une décision importante, un moment de résistance force la délibération consciente.&lt;/p&gt;
&lt;p&gt;On a supprimé la charge cognitive inutile, mais on a supprimé avec elle la capacité à vraiment choisir.&lt;/p&gt;
&lt;p&gt;Marine applique ce même regard à notre vocabulaire de designer. Dire &amp;quot;utilisateur&amp;quot; plutôt que &amp;quot;personne&amp;quot;, ces mots décrivent les humains du point de vue du système et rendent certaines questions morales impossibles à formuler. Optimiser un tunnel de conversion, c&#039;est une compétence technique. Décider ce qu&#039;on est prêt à faire subir à une personne pour l&#039;atteindre, c&#039;est une responsabilité.&lt;/p&gt;
&lt;p&gt;Pour illustrer concrètement cela, elle distingue deux types de friction. Les frictions négatives qui manipulent ou épuisent l&#039;utilisateur contre son intérêt :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dark pattern&lt;/strong&gt; : trompe activement (écrire &amp;quot;renoncer à vos avantages&amp;quot; plutôt que &amp;quot;se désabonner&amp;quot;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sludge&lt;/strong&gt; : épuise délibérément (un formulaire de résiliation en douze étapes).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et les frictions positives qui orientent ou éclairent dans son intérêt :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Nudge&lt;/strong&gt; : oriente sans contraindre (arrondir automatiquement ses achats pour alimenter un livret d&#039;épargne).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Boost&lt;/strong&gt; : force la conscience (demander à l&#039;utilisateur de taper le mot &amp;quot;supprimer&amp;quot; plutôt que de simplement cliquer sur valider).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;L&#039;enjeu, Marine le formule simplement : &lt;strong&gt;remplacer la fluidité anesthésiante par une friction émancipatrice&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Ce que nous retenons de cette journée&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Six conférences avec une conviction partagée : l&#039;IA est un outil et non une solution. Ce qui fait la valeur du designer aujourd&#039;hui est sa capacité à concevoir avec intention avec une vraie conscience de l&#039;impact de ses choix.&lt;/p&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/comprendre-enfin-les-tty-et-pty-avec-le-composant-process-de-symfony</id>
        <published>2026-06-15T10:42:00+02:00</published>
        <updated>2026-06-15T10:42:00+02:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/comprendre-enfin-les-tty-et-pty-avec-le-composant-process-de-symfony"/>
        <title>Comprendre (enfin) les TTY et PTY avec le composant Process de Symfony</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="symfony" />        <summary><![CDATA[Vous est-il déjà arrivé de lancer une commande (composer, rsync ou une commande Symfony) directement dans votre terminal pour y admirer de jolies barres de progression colorées, mais de constater que…]]></summary>
        <content type="html">
            &lt;p&gt;Vous est-il déjà arrivé de lancer une commande (&lt;code&gt;composer&lt;/code&gt;, &lt;code&gt;rsync&lt;/code&gt; ou une commande Symfony) directement dans votre terminal pour y admirer de jolies barres de progression colorées, mais de constater que cette même commande, une fois exécutée via un script PHP, perdait soudainement tout son formatage ?&lt;/p&gt;
&lt;p&gt;C’est un grand classique lorsque l&#039;on utilise le composant &lt;code&gt;Process&lt;/code&gt; de Symfony. Pour comprendre l&#039;origine de ce comportement (et surtout comment y remédier), il faut plonger un instant dans la façon dont Linux gère les flux et les terminaux. Rassurez-vous, c&#039;est plus simple qu&#039;il n&#039;y paraît.&lt;/p&gt;
&lt;h2&gt;Retour aux bases : les flux standards&lt;/h2&gt;
&lt;p&gt;Sous Linux, chaque processus dispose par défaut de trois flux standards, identifiés par des descripteurs de fichiers (&lt;em&gt;file descriptors&lt;/em&gt;) :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;0 : STDIN&lt;/strong&gt; (l&#039;entrée standard)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;1 : STDOUT&lt;/strong&gt; (la sortie standard)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2 : STDERR&lt;/strong&gt; (la sortie d&#039;erreur standard)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ce qu&#039;il faut retenir, c&#039;est que ces flux se comportent comme de simples tuyaux. Ce qui se trouve au bout du tuyau détermine le comportement du programme. Généralement, on rencontre trois scénarios :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Un fichier :&lt;/strong&gt; Par exemple, lorsque vous redirigez une sortie avec &lt;code&gt;ls &amp;gt; file.txt&lt;/code&gt; ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Un &lt;abbr title=&quot;Pseudo TeletYpe&quot;&gt;PTY&lt;/abbr&gt; / &lt;abbr title=&quot;TeleTYpewriter&quot;&gt;TTY&lt;/abbr&gt; (Terminal) :&lt;/strong&gt; Lorsque vous exécutez la commande directement devant votre écran ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Un pipe (tube) :&lt;/strong&gt; Lorsque vous enchaînez des commandes (&lt;code&gt;ls | grep php&lt;/code&gt;) ou que vous lancez un sous-processus de manière programmatique.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Le &amp;quot;problème&amp;quot; de l&#039;exécution programmatique&lt;/h2&gt;
&lt;p&gt;Par défaut, quand vous utilisez le composant &lt;code&gt;Process&lt;/code&gt; pour lancer une commande, Symfony utilise des &lt;strong&gt;pipes&lt;/strong&gt; (notre troisième scénario).&lt;/p&gt;
&lt;p&gt;Le programme exécuté (prenons Composer) est intelligent : il analyse ce qui se trouve au bout du tuyau de sa sortie standard (STDOUT). S&#039;il détecte un &lt;em&gt;pipe&lt;/em&gt; au lieu d&#039;un terminal, il en déduit qu&#039;il est exécuté par une machine ou un script. Pour éviter de polluer d&#039;éventuels fichiers de logs avec des caractères invisibles (les fameux codes ANSI qui génèrent les couleurs) ou des barres de progression illisibles, il bascule automatiquement en mode &amp;quot;texte brut&amp;quot;.&lt;/p&gt;
&lt;p&gt;C&#039;est pour cette raison exacte que vos couleurs disparaissent.&lt;/p&gt;
&lt;h2&gt;La méthode &lt;code&gt;setTty(true)&lt;/code&gt; : le lien direct&lt;/h2&gt;
&lt;p&gt;Le composant Process propose une première solution avec la méthode &lt;code&gt;setTty(true)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;En l&#039;activant, vous branchez &lt;em&gt;directement&lt;/em&gt; les flux de votre sous-processus sur le vrai terminal de votre système (celui depuis lequel vous avez lancé votre script PHP).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;La conséquence :&lt;/strong&gt; Les couleurs et les animations sont de retour. La commande s&#039;affiche exactement comme si vous l&#039;aviez tapée vous-même.
&lt;strong&gt;La limite :&lt;/strong&gt; Puisque le flux est branché directement sur votre écran, votre script PHP n&#039;y a plus accès. La synchronisation automatique fait que l&#039;affichage est immédiat, mais il devient impossible de capturer la sortie avec un &lt;code&gt;$process-&amp;gt;getOutput()&lt;/code&gt; pour l&#039;inspecter.&lt;/p&gt;
&lt;h2&gt;La magie de &lt;code&gt;setPty(true)&lt;/code&gt; : l&#039;illusion parfaite&lt;/h2&gt;
&lt;p&gt;C&#039;est ici qu&#039;interviennent les Pseudo-Terminaux (PTY).&lt;/p&gt;
&lt;p&gt;Lorsque vous utilisez &lt;code&gt;setPty(true)&lt;/code&gt;, vous demandez au système de créer un terminal émulé de toutes pièces. Un PTY fonctionne comme un &lt;strong&gt;duo composé d&#039;un contrôleur et d&#039;un terminal virtuel&lt;/strong&gt; :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Le script PHP (via Symfony Process) agit comme le &lt;strong&gt;contrôleur&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Le processus enfant (votre commande) est branché sur le &lt;strong&gt;terminal virtuel&lt;/strong&gt; émulé (qui prend la forme d’un fichier dynamique, souvent &lt;code&gt;/dev/pts/X&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Pour le processus enfant, l&#039;illusion est totale. Il détecte bien un terminal au bout de son tuyau et génère donc ses couleurs et ses barres de progression interactives.&lt;/p&gt;
&lt;p&gt;Côté PHP, en tant que &amp;quot;contrôleur&amp;quot; du PTY, la donne change : l&#039;envoi vers l&#039;écran n&#039;est plus automatique (il agit comme un &lt;em&gt;buffer&lt;/em&gt;). C&#039;est à vous de lire ce qui sort du terminal virtuel. Vous retrouvez ainsi le meilleur des deux mondes : vous forcez le programme à conserver son affichage riche, tout en gardant la capacité d&#039;intercepter et de manipuler le flux sortant directement dans votre code PHP.&lt;/p&gt;
&lt;h2&gt;Démo time !&lt;/h2&gt;
&lt;p&gt;On commence avec une commande Symfony qui affiche, si l&#039;exécuteur le permet, des choses en couleur :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;#!/usr/bin/env php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; __DIR__&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;/vendor/autoload.php&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\Console\Output\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;OutputInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\Console\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;SingleCommandApplication&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; SingleCommandApplication&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    -&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;setCode&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;OutputInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $output)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; int&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $output&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;writeln&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;&amp;#x3C;info&gt;Hello World!&amp;#x3C;/info&gt;&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $output&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;writeln&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;&amp;#x3C;comment&gt;This is a single command application.&amp;#x3C;/comment&gt;&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        $output&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;writeln&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;&amp;#x3C;error&gt;Goodbye!&amp;#x3C;/error&gt;&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        return&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; 0&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    -&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ensuite, nous exécutons cette commande Symfony, avec le composant Process. En fonction des arguments, nous activons ou non PTY ou TTY :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; __DIR__&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;/vendor/autoload.php&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$process &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\Process\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;__DIR__&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;/console.php&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$process&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;setTty&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(($argv[&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;??&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;===&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;tty&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$process&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;setPty&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(($argv[&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;??&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;===&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;pty&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$process&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;mustRun&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-9&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;Output captured:&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;dump&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($process&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getOutput&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;());&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et voici le résultat :&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/tty/tty-pty-none.png&quot; data-original-width=&quot;1190&quot; data-original-height=&quot;655&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/tty/tty-pty-none.3626426a.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;/media/cache/content/2026/tty/tty-pty-none.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1190 / 655)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/tty/tty-pty-none.png&quot; alt=&quot;Alt text&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h2&gt;En résumé&lt;/h2&gt;
&lt;p&gt;Si vous construisez des outils en ligne de commande ou des &lt;em&gt;workers&lt;/em&gt; asynchrones en PHP :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Par défaut (Pipes) :&lt;/strong&gt; À privilégier pour les tâches de fond où la sortie doit être parsée ou logguée proprement, sans caractères d&#039;échappement ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;setTty(true)&lt;/code&gt; :&lt;/strong&gt; Idéal si vous voulez simplement déléguer l&#039;affichage et l&#039;interactivité à l&#039;utilisateur, sans avoir besoin d&#039;analyser la sortie côté PHP ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;setPty(true)&lt;/code&gt; :&lt;/strong&gt; La solution de choix pour forcer un affichage riche (couleurs, animations) tout en conservant le contrôle du flux sortant dans votre script.&lt;/li&gt;
&lt;/ul&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/comment-utiliser-les-attributs-php-sur-un-controleur-symfony</id>
        <published>2026-06-10T14:42:00+02:00</published>
        <updated>2026-06-10T14:42:00+02:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/comment-utiliser-les-attributs-php-sur-un-controleur-symfony"/>
        <title>Comment utiliser les attributs PHP sur un contrôleur Symfony ?</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="symfony" />        <summary><![CDATA[PHP 8.0 a introduit les attributs, et c&#039;est une excellente fonctionnalité ! Le code devient plus lisible, plus simple à écrire, et bénéficie pleinement de la coloration syntaxique et du linting de notre…]]></summary>
        <content type="html">
            &lt;p&gt;PHP 8.0 a introduit les attributs, et c&#039;est une excellente fonctionnalité ! Le code devient plus lisible, plus simple à écrire, et bénéficie pleinement de la coloration syntaxique et du &lt;em&gt;linting&lt;/em&gt; de notre IDE.&lt;/p&gt;
&lt;p&gt;Symfony a très vite adopté cette nouveauté. On les utilise aujourd&#039;hui partout : pour configurer les routes, ajouter des contraintes de validation ou déclarer des &lt;em&gt;listeners&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Dans cet article, nous allons voir &lt;strong&gt;comment créer et ajouter un attribut PHP personnalisé&lt;/strong&gt; sur un contrôleur pour lui injecter un comportement automatique, comme du logging.&lt;/p&gt;

&lt;div class=&quot;c-alert c-alert--note&quot;&gt;
    &lt;p class=&quot;c-alert__title&quot;&gt;
                    &lt;span class=&quot;c-icon c-icon--monospace&quot;&gt;
                &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; aria-hidden=&quot;true&quot; class=&quot;c-icon__svg&quot; focusable=&quot;false&quot; viewBox=&quot;0 0 70 71&quot;&gt;&lt;path fill-rule=&quot;nonzero&quot; d=&quot;M35 .9c19.3 0 35 15.7 35 35s-15.7 35-35 35-35-15.7-35-35S15.7.9 35 .9m0 5c-16.552 0-30 13.449-30 30s13.448 30 30 30c16.552.103 30-13.448 30-30 0-16.551-13.448-30-30-30m0 24.9c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V33.8c0-1.7 1.3-3 3-3m0-11c.8 0 1.6.3 2.3.9.6.5.9 1.3.9 2.1 0 .2-.1.4-.1.6-.1.2-.1.4-.2.6s-.2.3-.3.5-.3.4-.4.5c-1.1 1.1-3.1 1.1-4.2 0-.2-.2-.3-.3-.4-.5s-.2-.3-.3-.5-.2-.4-.2-.6c-.1-.2-.1-.4-.1-.6 0-.8.3-1.6.9-2.1.5-.6 1.3-.9 2.1-.9&quot;/&gt;&lt;/svg&gt;
            &lt;/span&gt;
                        &lt;strong&gt;Info&lt;/strong&gt;
    &lt;/p&gt;
    &lt;div class=&quot;c-alert__content&quot;&gt;
                &lt;p&gt;
Nous avions déjà parlé des attributs &lt;a href=&quot;https://jolicode.com/blog/rate-limit-your-symfony-apis&quot;&gt;en 2021 pour limiter le débit de vos API (Rate Limit)&lt;/a&gt;. Cependant, Symfony a bien évolué depuis, et leur utilisation est devenue encore plus simple.&lt;/p&gt;
        &lt;/div&gt;
&lt;/div&gt;

&lt;h2&gt;1. Créer un attribut&lt;/h2&gt;
&lt;p&gt;La première étape consiste à créer une classe PHP classique. Pour indiquer qu&#039;elle servira d&#039;attribut, on lui ajoute elle-même l&#039;attribut natif &lt;code&gt;#[\Attribute]&lt;/code&gt; :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;App\AuditLog&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Psr\Log\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;LogLevel&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;#[\Attribute(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;\Attribute&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;TARGET_METHOD&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; |&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; \Attribute&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;TARGET_CLASS&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;Loggable&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; __construct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        public&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; string&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $level &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; LogLevel&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;INFO&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Les paramètres du constructeur sont optionnels. Ils permettent de passer des options au moment où on utilise l&#039;attribut. Ici, notre propriété &lt;code&gt;$level&lt;/code&gt; sert à définir le niveau de log souhaité.&lt;/p&gt;
&lt;h2&gt;2. Utiliser l&#039;attribut sur un contrôleur&lt;/h2&gt;
&lt;p&gt;Une fois notre classe créée, nous pouvons l&#039;appliquer directement sur un contrôleur, soit sur la classe entière, soit sur une méthode (action) spécifique :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;App\Controller&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; App\AuditLog\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Loggable&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Psr\Log\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;LogLevel&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Bridge\Twig\Attribute\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Template&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Bundle\FrameworkBundle\Controller\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;AbstractController&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\Routing\Attribute\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Route&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;#[Loggable()]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;final&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;HomepageController&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; extends&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-7&quot;&gt;AbstractController&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    #[Loggable(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;LogLevel&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;CRITICAL&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    #[Route(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;/&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, name: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;app_homepage&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    #[Template(&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;homepage/index.html.twig&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; index&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ici, la méthode &lt;code&gt;index&lt;/code&gt; hérite de la configuration par défaut de la classe, et ajoute un nouveau niveau de log plus critique (&lt;code&gt;CRITICAL&lt;/code&gt;).&lt;/p&gt;
&lt;h2&gt;3. Créer le listener pour activer le comportement&lt;/h2&gt;
&lt;p&gt;Pour que notre attribut serve à quelque chose, il faut intercepter l&#039;appel du contrôleur. Symfony déclenche l&#039;événement &lt;code&gt;ControllerEvent&lt;/code&gt; juste avant d&#039;exécuter l&#039;action d&#039;un contrôleur. C&#039;est le moment idéal pour vérifier la présence de notre attribut.&lt;/p&gt;
&lt;p&gt;Selon votre version de Symfony, l&#039;implémentation est devenue de plus en plus simple.&lt;/p&gt;
&lt;h3&gt;Avec Symfony 6.2+&lt;/h3&gt;
&lt;p&gt;On utilise la méthode &lt;code&gt;getAttributes()&lt;/code&gt; de l&#039;événement pour récupérer notre attribut et appliquer notre logique :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;namespace&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;App\AuditLog&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Psr\Log\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;LoggerInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Psr\Log\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;NullLogger&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\EventDispatcher\Attribute\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;AsEventListener&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\HttpKernel\Event\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;ControllerEvent&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;LoggerListener&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; __construct&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        private&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; readonly&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; LoggerInterface&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $logger &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; new&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; NullLogger&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    ) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    #[AsEventListener(priority: &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; logSymfony62&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;ControllerEvent&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $event)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;        foreach&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; ($event&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getAttributes&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;()[&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Loggable&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;??&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; [] &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $attribute) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;            $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($attribute&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;level, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Controller is loggable&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;                &#039;controller&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $event&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getController&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;            ]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Avec Symfony 8.1+&lt;/h3&gt;
&lt;p&gt;Symfony 8.1 a introduit des événements spécifiques aux attributs. Plus besoin de boucler manuellement, l&#039;événement contient directement l&#039;attribut ciblé :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\HttpKernel\Event\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;ControllerAttributeEvent&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\HttpKernel\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;KernelEvents&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;LoggerListener&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    #[AsEventListener(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;KernelEvents&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;CONTROLLER&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &#039;.&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; Loggable&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, priority: &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; logSymfony81&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;ControllerAttributeEvent&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $event)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($event&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;attribute&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;level, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Controller is loggable&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;controller&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $event&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;kernelEvent&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getController&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        ]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Avec Symfony 8.2+&lt;/h3&gt;
&lt;p&gt;La version 8.2 simplifira encore la syntaxe grâce à un attribut dédié (&lt;code&gt;AsControllerAttributeListener&lt;/code&gt;) qui cible directement notre classe :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\HttpKernel\Attribute\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;AsControllerAttributeListener&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\HttpKernel\Event\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;ControllerAttributeEvent&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; Symfony\Component\HttpKernel\Event\&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;ControllerEvent&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;class&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span class=&quot;syntax-6&quot;&gt;LoggerListener&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    #[AsControllerAttributeListener(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;ControllerEvent&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;Loggable&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;::class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;    public&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt; function&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; logSymfony82&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;ControllerAttributeEvent&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $event)&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-11&quot;&gt;        $this&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($event&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;attribute&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;level, &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;Controller is loggable&#039;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-1&quot;&gt;            &#039;controller&#039;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; =&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $event&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;kernelEvent&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;getController&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;        ]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Les attributs apportent une grande souplesse à l&#039;écosystème PHP, et Symfony propose des outils parfaits pour les exploiter.&lt;/p&gt;
&lt;p&gt;Grâce à eux, vous pouvez ajouter des comportements à vos contrôleurs de manière propre et déclarative, sans polluer vos méthodes avec du code répétitif.&lt;/p&gt;
&lt;p&gt;Bien sûr, Symfony intègre déjà nativement des attributs puissants pour gérer la sécurité (&lt;code&gt;#[IsGranted]&lt;/code&gt;), le cache (&lt;code&gt;#[Cache]&lt;/code&gt;), ou encore le rate limiting (&lt;code&gt;#[RateLimit]&lt;/code&gt;). Mais créer vos propres attributs ouvre la porte à des cas d&#039;usage métiers très intéressants :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Le Feature Flipping (&lt;code&gt;#[FeatureFlag(&#039;new-dashboard&#039;)]&lt;/code&gt;)&lt;/strong&gt; : pour activer ou désactiver l&#039;accès à une route selon le déploiement progressif d&#039;une fonctionnalité ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;La télémétrie et l&#039;observabilité (&lt;code&gt;#[TrackActivity(&#039;checkout&#039;)]&lt;/code&gt;)&lt;/strong&gt; : pour envoyer des statistiques précises à des outils tiers comme OpenTelemetry ou Plausible dès qu&#039;un utilisateur visite une page clé ;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;La transformation de réponse (&lt;code&gt;#[Serialize]&lt;/code&gt;)&lt;/strong&gt; : pour intercepter le retour de vos contrôleurs et le formater automatiquement selon un standard précis (comme le format JSON d&#039;une API spécifique).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Les possibilités n&#039;ont de limite que votre imagination !&lt;/p&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/ameliorer-la-maintenance-de-vos-workflows-github</id>
        <published>2026-06-09T11:42:00+02:00</published>
        <updated>2026-06-09T11:42:00+02:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/ameliorer-la-maintenance-de-vos-workflows-github"/>
        <title>Améliorer la maintenance de vos workflows GitHub</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="ci" />            <category term="github" />        <summary><![CDATA[Les failles de sécurité liées à la supply chain sont de plus en plus courantes. En tant que mainteneur d&#039;un projet open source populaire, l&#039;enjeu est de taille. Votre code est déployé sur de nombreux…]]></summary>
        <content type="html">
            &lt;p&gt;Les failles de sécurité liées à la &lt;em&gt;supply chain&lt;/em&gt; sont de plus en plus courantes. En tant que mainteneur d&#039;un projet open source populaire, l&#039;enjeu est de taille. Votre code est déployé sur de nombreux serveurs de production et postes de développeurs. Il est donc crucial de garder vos pipelines CI à jour et sécurisés.&lt;/p&gt;
&lt;p&gt;Le projet open source qui nous importe aujourd&#039;hui est Castor 🦫, que nous vous invitons &lt;a href=&quot;https://jolicode.com/blog/le-task-runner-castor-est-maintenant-disponible-en-version-1&quot;&gt;à découvrir&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Pour nous aider dans cette tâche, nous avons choisi &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/zizmorcore/zizmor&quot;&gt;zizmor&lt;/a&gt;. C&#039;est un outil d&#039;analyse statique de code. Il détecte les vulnérabilités dans vos fichiers de configuration de CI et propose souvent des correctifs. Pour faire simple, c&#039;est l&#039;équivalent de PHPStan, mais pour vos workflows.&lt;/p&gt;
&lt;p&gt;Une fois installé, une simple exécution (&lt;code&gt;zizmor .&lt;/code&gt;) permet de détecter les erreurs de configuration et de les corriger rapidement. Nous avons ainsi amélioré notre sécurité et facilité la maintenance à long terme.&lt;/p&gt;
&lt;p&gt;Voici un exemple de rapport généré par l&#039;outil :&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;error[unpinned-uses]: unpinned action reference
  --&amp;gt; ./.github/actions/cache/action.yaml:48:13
   |
48 |       uses: actions/cache@v5
   |             ^^^^^^^^^^^^^^^^ action is not pinned to a hash (required by blanket policy)
   |
   = note: audit confidence → High
   = note: this finding has an auto-fix
   = help: audit documentation → https://docs.zizmor.sh/audits/#unpinned-uses

help[artipacked]: credential persistence through GitHub Actions artifacts
  --&amp;gt; ./.github/workflows/artifacts.yml:16:9
   |
16 |         - name: Checkout
   |  _________^
17 | |         uses: actions/checkout@v6
   | |_________________________________^ does not set persist-credentials: false
   |
   = note: audit confidence → Low
   = note: this finding has an auto-fix
   = help: audit documentation → https://docs.zizmor.sh/audits/#artipacked

error[github-env]: dangerous use of environment file
  --&amp;gt; ./.github/actions/cache/action.yaml:14:7
   |
14 | /       run: |
15 | |         set -e
16 | |
17 | |         # Should be the same command as the one in tools/static/castor.php
...  |
44 | |         echo cache_dirname_test=$cache_dirname_test &amp;gt;&amp;gt; $GITHUB_ENV
45 | |         echo cache_key_test=$cache_key_test &amp;gt;&amp;gt; $GITHUB_ENV
   | |__________________________________________________________^ write to GITHUB_ENV may allow code execution
   |
   = note: audit confidence → Low
   = help: audit documentation → https://docs.zizmor.sh/audits/#github-env

error[template-injection]: code injection via template expansion
  --&amp;gt; ./.github/actions/install/action.yaml:28:108
   |
28 |       run: composer install --prefer-dist --no-progress --optimize-autoloader --classmap-authoritative ${{ inputs.composer-flags }}
   |       --- this run block                                                                                   ^^^^^^^^^^^^^^^^^^^^^ may expand into attacker-controllable code
   |
   = note: audit confidence → High
   = note: this finding has an auto-fix
   = help: audit documentation → https://docs.zizmor.sh/audits/#template-injection
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Un grand nombre d&#039;erreurs peuvent être corrigées automatiquement :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;zizmor&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; --fix=all&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cependant, pour trouver le hash de commit correspondant à un tag, l&#039;outil a besoin d&#039;un token GitHub avec les bonnes permissions. Il convient donc de le relancer avec la variable configurée :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;ghp_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; zizmor&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; --fix=all&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ou, si vous utilisez la CLI &lt;code&gt;gh&lt;/code&gt; au quotidien :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;gh&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; auth token`&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; zizmor&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; --fix=all&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En revanche, zizmor ne gère pas les montées de version majeures des actions. Pour cela, nous utilisons deux autres méthodes.&lt;/p&gt;
&lt;p&gt;En local, nous exécutons &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://github.com/azat-io/actions-up&quot;&gt;actions-up&lt;/a&gt;. Cet outil détecte les actions utilisées et propose des mises à jour. C&#039;est le &lt;code&gt;composer update&lt;/code&gt; de vos workflows.&lt;/p&gt;
&lt;p&gt;En CI, nous faisons confiance à &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://dependabot.com/&quot;&gt;Dependabot&lt;/a&gt;. Il vérifie régulièrement vos dépendances et ouvre des Pull Requests automatiquement. Voici notre configuration :&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;updates&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;    - &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;package-ecosystem&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;github-actions&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      directory&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;/&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      target-branch&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;main&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      schedule&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          interval&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;monthly&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      groups&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          github-actions&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;              patterns&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;                  - &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&#039;*&#039;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;      cooldown&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;          default-days&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;7&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En conclusion, nous vous recommandons vivement d&#039;adopter zizmor. C&#039;est un outil puissant, rapide à prendre en main, qui sécurise vos pipelines et vous fait gagner un temps précieux au quotidien.&lt;/p&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/afup-day-2026-paris-retour-sur-une-journee-dans-l-ecosysteme-php</id>
        <published>2026-05-26T15:30:00+02:00</published>
        <updated>2026-05-26T15:30:00+02:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/afup-day-2026-paris-retour-sur-une-journee-dans-l-ecosysteme-php"/>
        <title>AFUP Day 2026 Paris, retour sur une journée dans l&#039;écosystème PHP</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="php" />            <category term="symfony" />            <category term="afup" />            <category term="ia" />        <summary><![CDATA[Ce vendredi 22 mai 2026, l&#039;équipe JoliCode était présente à l&#039;AFUP Day 2026 Paris à l&#039;ESGI Paris pour une journée de conférences. L&#039;ambiance était conviviale et studieuse, avec un programme axé sur l&#039;IA,…]]></summary>
        <content type="html">
            &lt;p&gt;Ce vendredi 22 mai 2026, l&#039;équipe JoliCode était présente à l&#039;AFUP Day 2026 Paris à l&#039;ESGI Paris pour une journée de conférences. L&#039;ambiance était conviviale et studieuse, avec un programme axé sur l&#039;IA, l&#039;architecture et la productivité.&lt;/p&gt;
&lt;p&gt;Nous avons eu l&#039;honneur de présenter deux sujets, Loïck Piera sur Castor 🦫 et Grégoire Pineau sur Symfony IA.&lt;/p&gt;
&lt;h2&gt;Les temps forts techniques&lt;/h2&gt;
&lt;p&gt;Le programme a couvert des thématiques essentielles qui font évoluer nos pratiques au quotidien, et a aussi rappelé quelques bases que nous connaissons bien.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/afup-day/salle-conference.jpg&quot; data-original-width=&quot;3589&quot; data-original-height=&quot;2262&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/afup-day/salle-conference.4ddd7dc1.webp&quot; /&gt;&lt;source type=&quot;image/jpeg&quot; srcset=&quot;/media/cache/content/2026/afup-day/salle-conference.jpg&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(3589 / 2262)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/afup-day/salle-conference.jpg&quot; alt=&quot;La salle de la conférence&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h3&gt;Intelligence Artificielle et Innovation dans Symfony&lt;/h3&gt;
&lt;p&gt;L&#039;IA a été au cœur de plusieurs échanges, montrant son intégration progressive dans nos outils quotidiens :&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Symfony AI et Embeddings&lt;/strong&gt; : Grégoire Pineau a présenté son retour sur l&#039;utilisation des embeddings dans Symfony pour des recherches et rapprochements sémantiques poussés - &lt;a href=&quot;https://jolicode.com/blog/notre-retour-sur-le-symfonylive-paris-2026#embeddings-en-php-symfony-ai-en-pratique-gregoire-pineau&quot;&gt;nous vous invitons à retrouver notre récapitulatif de Symfony Live où nous en parlions déjà&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Assistance Générative&lt;/strong&gt; : Florence Cauchy a partagé un guide pratique sur l&#039;intégration de GitHub Copilot et Claude dans nos workflows. Nous partageons ses conseils :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mise en place de skills ;&lt;/li&gt;
&lt;li&gt;Définition de guidelines pour les agents ;&lt;/li&gt;
&lt;li&gt;Exploiter aussi bien le CLI que les agents cloud.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Si vous n&#039;avez pas encore d&#039;assistance par LLM pour coder, c&#039;était une bonne introduction !&lt;/p&gt;
&lt;h3&gt;Outillage, Productivité et Modernisation&lt;/h3&gt;
&lt;p&gt;Nous avons exploré de nouveaux outils pour optimiser notre productivité :&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Castor&lt;/strong&gt; : Loïck Piera nous a fait découvrir les coulisses de ce &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://castor.jolicode.com/&quot;&gt;task runner open source&lt;/a&gt; indispensable 😉 (oui c&#039;est nous qui le développons). L&#039;occasion de parler des outils sur lesquels repose Castor comme &lt;code&gt;symfony/console&lt;/code&gt;, et de parler de fonctionnalités peu connues comme :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Les events pour étendre le fonctionnement ;&lt;/li&gt;
&lt;li&gt;Les imports distants pour utiliser des tasks extérieures ;&lt;/li&gt;
&lt;li&gt;Les remote exécutions, pour lancer des outils sans les installer.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nous vous invitons &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://speakerdeck.com/pyrech/sous-le-capot-de-castor-le-task-runner-php&quot;&gt;à lire les slides qui sont pleines d&#039;informations&lt;/a&gt; !&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sidekicks applicatifs&lt;/strong&gt; : Nicolas Grekas a présenté comment tirer partie de Caddy/FrankenPHP pour faciliter la configuration de nos applications HTTP. Grâce à ces sidekicks, l&#039;application peut continuer à tourner en mode worker (donc sans avoir à redémarrer de 0 à chaque requête, comme c&#039;est le cas avec PHP-FPM) mais en ayant sa configuration à jour :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;côté application, on récupère une valeur de configuration, qui est locale et toujours à jour, grâce à un simple appel à une fonction ;&lt;/li&gt;
&lt;li&gt;un thread tourne à côté pour mettre à jour cette valeur dès qu&#039;il y a un changement (ce thread est setup très simplement dans Caddy / FrankenPHP).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Il aimerait voir cette nouvelle manière de faire devenir un standard dans l&#039;écosystème PHP, à la place du traditionnel php-fpm, afin d&#039;être égaux avec les technos alternatives à PHP comme node, qui proposent des comportement similaires.&lt;/p&gt;
&lt;h3&gt;Architecture et Qualité du Code&lt;/h3&gt;
&lt;p&gt;La durabilité logicielle et les bonnes pratiques restent fondamentales :&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Qualité avec PHPStan&lt;/strong&gt; : Vincent Langlet &amp;amp; Étienne Dhennin nous font la présentation de leur mise en place d&#039;un écosystème de qualité chez Weglot.
Ils nous ont présenté les différents fixers, linters et outils de tests disponibles et qu&#039;ils ont utilisés, pour répondre à leur définition de la qualité : &lt;strong&gt;la capacité d&#039;un système à remplir son rôle aujourd&#039;hui comme demain&lt;/strong&gt;, au travers de trois dimensions : fonctionnelle, temporelle et humaine.
La direction de l&#039;entreprise avait besoin de justifications pour le temps passé à faire de la qualité, et ils ont donc introduit un métrique original : le taux de hotfix en production (et donc oui il a baissé).&lt;/p&gt;
&lt;p&gt;Parmi leurs retours d’expérience sur PHPStan en particulier :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ne pas générer de baseline sur les projets legacy, prioriser les niveaux &amp;quot;bas&amp;quot; (jusqu’au niveau 5) - nous sommes partagés sur ce point car la baseline permet justement de pouvoir contribuer à des projets legacy avec une analyse statique élevé sur nos nouveaux développements ;&lt;/li&gt;
&lt;li&gt;Privilégier les extensions spécifiques à votre projet (&lt;code&gt;phpstan-doctrine&lt;/code&gt; par exemple) aux niveaux 9 et 10, souvent superflus.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;API Design Pragmatique&lt;/strong&gt; : Amaury Bouchard a exploré des alternatives au design d&#039;APIs. Il critique pas mal JWT car ça ne gère pas l&#039;expiration par exemple. Un des points que nous partageons est que HTTP Basic aujourd&#039;hui c&#039;est tout à fait valide et sécurisé (car en HTTPS il n&#039;y a pas de &amp;quot;fuite&amp;quot; des identifiants).
Le take-away du talk concerne le choix entre &lt;abbr title=&quot;Remote Procedure Call&quot;&gt;RPC&lt;/abbr&gt; et REST. Vouloir utiliser absolument du REST alors que notre API ne gère pas des états mais des actions est un mauvais choix de design. En résumé :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RPC pour les API qui font des actions (envoyer un mail, publier des redirections...) ;&lt;/li&gt;
&lt;li&gt;REST pour les API qui modifient des états, le CRUD, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En bref, un appel à la simplicité, quitte à remettre en questions des dogmes acquis depuis très longtemps, de très bons sujets de réflexion lors de votre prochaine conception d’API.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tests sans Mocks&lt;/strong&gt; : Imen Ezzine a présenté une solution intéressante pour tester efficacement des APIs externes sans recourir aux mocks - et surtout pour pallier à l&#039;imcompatibilité de &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://php-vcr.github.io/&quot;&gt;PHP-VCR&lt;/a&gt; avec le client HTTP de Symfony.&lt;/p&gt;
&lt;p&gt;En utilisant le format HAR,  un nouveau decorator RecorderHttpClient, et un simple attribut &lt;code&gt;#[UseRecord]&lt;/code&gt; sur les fonctions de test (via le bridge phpunit), nous pouvons enregistrer une réponse API, de sorte à pouvoir utiliser plus tard cet enregistrement pour simuler des appels à une API externe. Ainsi, pas besoin de créer un mock ! L’appel est joué réellement une seule fois, puis réutilisé directement au moment de lancer les tests. Un mécanisme permet de configurer son mode d’enregistrement, pour rafraîchir périodiquement, rejouer l’appel à chaque fois ou au contraire le verrouiller.&lt;/p&gt;
&lt;p&gt;L’ensemble apparaît comme une solution efficace et très élégante pour s’affranchir d’une problématique que nous avons tous déjà rencontrée. Une PR est soumise sur Symfony 8.2.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Gestion des Exceptions&lt;/strong&gt; : Smaïne Milianni a récapitulé les meilleures pratiques pour gérer et logguer les exceptions proprement. De bons rappels et peut être même des guidelines à coller dans nos &lt;code&gt;AGENTS.md&lt;/code&gt; !&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Relancer systématiquement les exceptions attrapées dans les couches “profondes” de l’application (métier, infra…), pour les gérer uniquement dans les couches d’interface.&lt;/li&gt;
&lt;li&gt;Ne pas catcher la terre entière (&lt;code&gt;catch \Exception&lt;/code&gt;) ;&lt;/li&gt;
&lt;li&gt;Ne pas catcher &lt;code&gt;Throwable&lt;/code&gt; si on est bas niveau ;&lt;/li&gt;
&lt;li&gt;Ne jamais &amp;quot;rien faire&amp;quot; dans le &amp;quot;catch&amp;quot; (il faut au moins un log) ;&lt;/li&gt;
&lt;li&gt;Utiliser une exception pour faire du contrôle de flow classique (nous sommes un peu partagé à ce sujet aussi - c&#039;est parfois élégant) ;&lt;/li&gt;
&lt;li&gt;Catcher et relancer une exception sans n&#039;avoir rien changé / rien fait : autant de rien catcher ;&lt;/li&gt;
&lt;li&gt;ne jamais oublier l&#039;argument &lt;code&gt;$previous&lt;/code&gt; quand on rethrow / mute une exception ;&lt;/li&gt;
&lt;li&gt;créer des exceptions custom, utiliser des constructeurs statiques... pour nos exceptions métier.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/afup-day/http-mock.jpg&quot; data-original-width=&quot;4080&quot; data-original-height=&quot;3072&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/afup-day/http-mock.42f43150.webp&quot; /&gt;&lt;source type=&quot;image/jpeg&quot; srcset=&quot;/media/cache/content/2026/afup-day/http-mock.jpg&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(4080 / 3072)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/afup-day/http-mock.jpg&quot; alt=&quot;Imen Ezzine sur scène&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;h3&gt;Sujets intemporels&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Passkeys et WebAuthn&lt;/strong&gt; : Sylvain Combraque nous a fait un rapide cours historique sur les manières de stocker les mots de passe utilisateurs, du très sécurisé plaintext jusqu&#039;au hashage, sans oublier l&#039;&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://openid.net/developers/how-connect-works/&quot;&gt;OICD&lt;/a&gt; pour nous montrer comment avec le temps, nous avons pu répondre aux questions de sécurité liées à nos systèmes d&#039;authentification.
Il nous a finalement expliqué qu&#039;au final, la manière la plus simple de sécuriser nos mots de passe, c&#039;est de ne pas en avoir ! Chose possible aujourd&#039;hui et depuis 2015 grâce aux Passkeys, une solution d&#039;authentification locale, unique par compte, et impossible à faire fuiter. Cela fait 10 ans que nous en entendons parler et que les briques techniques existent, et pourtant, ce n&#039;est toujours pas &amp;quot;grand public&amp;quot; ! Les mots de passe ont encore de beaux jours devant eux... À nous de faire bouger les lignes !&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Gotchas PHP&lt;/strong&gt; : Frédéric Bouchery a animé une session interactive sur les comportements inattendus et les subtilités du langage. Par exemple, saviez-vous que &lt;code&gt;new daTeTimE();&lt;/code&gt; fonctionne aussi bien que &lt;code&gt;new DateTime();&lt;/code&gt; mais que vos constantes elles, sont sensibles à la casse ?
C&#039;était une façon très ludique de nous expliquer des principes du langage comme la compilation, les changements de syntaxes...&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Au-delà de la technique, l&#039;AFUP Day a été un moment de partage plein d&#039;échanges et riche en apprentissages. Nous sommes vraiment ravis de retrouver un événement à Paris.&lt;/p&gt;
&lt;p&gt;Les perspectives ouvertes par l&#039;IA et la modernisation des outils promettent des projets passionnants pour JoliCode. Un grand merci à l&#039;AFUP Paris et à tous les bénévoles pour cette organisation impeccable 👏 Comptez sur nous l&#039;année prochaine !&lt;/p&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/comment-integrer-l-ia-dans-son-workflow-ux-ui</id>
        <published>2026-05-12T12:42:00+02:00</published>
        <updated>2026-05-12T12:42:00+02:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/comment-integrer-l-ia-dans-son-workflow-ux-ui"/>
        <title>Comment intégrer l&#039;IA dans son workflow UX/UI</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="ux" />            <category term="ia" />            <category term="ui" />        <summary><![CDATA[&amp;quot;On a déjà deux agents IA qui tournent en interne.&amp;quot;
Ce n&#039;est pas en réunion que j&#039;ai entendu ça. C&#039;est en mission chez un client, dans leurs bureaux. Ce genre de phrase, je l&#039;entends de plus…]]></summary>
        <content type="html">
            &lt;p&gt;&lt;em&gt;&lt;strong&gt;&amp;quot;On a déjà deux agents IA qui tournent en interne.&amp;quot;&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Ce n&#039;est pas en réunion que j&#039;ai entendu ça. C&#039;est en mission chez un client, dans leurs bureaux. Ce genre de phrase, je l&#039;entends de plus en plus souvent maintenant, entre deux écrans, dans les couloirs, pendant la pause déjeuner. L&#039;IA fait désormais partie du quotidien.&lt;/p&gt;
&lt;p&gt;Quelques semaines plus tard, un autre client nous présente deux pages de maquettes générées par Claude, structure de page et premières intentions de contenu, pour expliquer ce qu&#039;il voulait avant même d’ouvrir Figma.&lt;/p&gt;
&lt;p&gt;C&#039;est ce constat qui nous a poussés à écrire cet article, pour poser ce que ça change dans notre façon de travailler, et ce qu&#039;on peut en tirer concrètement pour nos projets et nos clients.&lt;/p&gt;
&lt;p&gt;Ce que j&#039;ai trouvé en explorant, c&#039;est que l&#039;IA ne remplace pas le designer, elle redistribue son temps et son attention. Certaines tâches s&#039;accélèrent, d&#039;autres disparaissent, et de nouvelles compétences deviennent importantes. La principale d&#039;entre elles, j&#039;y reviendrai, est la capacité à formuler des demandes précises. Savoir parler à une IA, ça s&#039;apprend et ça change tout à la qualité des résultats.&lt;/p&gt;
&lt;h2&gt;Le prompt : la compétence clé&lt;/h2&gt;
&lt;p&gt;Avant de parler workflow et processus, un point essentiel, la qualité des résultats dépend de la qualité de la demande. C&#039;est le cœur du sujet.&lt;/p&gt;
&lt;p&gt;Un bon prompt, c&#039;est un bon brief. Précis, contextualisé, avec une intention claire et des contraintes définies. L&#039;erreur la plus fréquente, celle que j&#039;ai faite moi-même au début, c&#039;est de rester trop vague en espérant que l&#039;IA devine l&#039;intention derrière la demande.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ex : Prompt trop vague&lt;/strong&gt;&lt;br /&gt;
&lt;em&gt;&amp;quot;Génère un UX flow pour un site e-commerce.&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/workflow-ia-ux-ui/article-ai-design-01.jpg&quot; data-original-width=&quot;1920&quot; data-original-height=&quot;1080&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/workflow-ia-ux-ui/article-ai-design-01.58842894.webp&quot; /&gt;&lt;source type=&quot;image/jpeg&quot; srcset=&quot;/media/cache/content/2026/workflow-ia-ux-ui/article-ai-design-01.jpg&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1920 / 1080)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/workflow-ia-ux-ui/article-ai-design-01.jpg&quot; alt=&quot;Génère un UX flow pour un site e-commerce&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ex : Prompt structuré&lt;/strong&gt;&lt;br /&gt;
&lt;em&gt;&amp;quot;Génère un UX flow pour la page produit d&#039;un site e-commerce de mode féminine, destiné à des femmes de 25–40 ans sur mobile. Inclure : découverte produit, sélection de taille, ajout au panier, paiement, et edge cases (taille indisponible, rupture de stock, code promo invalide). Format en étapes claires avec états d&#039;erreur et micro-interactions.&amp;quot;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/workflow-ia-ux-ui/article-ai-design-02.jpg&quot; data-original-width=&quot;1920&quot; data-original-height=&quot;1080&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/workflow-ia-ux-ui/article-ai-design-02.cf2c3f4f.webp&quot; /&gt;&lt;source type=&quot;image/jpeg&quot; srcset=&quot;/media/cache/content/2026/workflow-ia-ux-ui/article-ai-design-02.jpg&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1920 / 1080)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/workflow-ia-ux-ui/article-ai-design-02.jpg&quot; alt=&quot;Génère un UX flow pour la page produit d’un site e-commerce &quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;La différence se joue sur &lt;strong&gt;la précision de la demande&lt;/strong&gt;. Un prompt efficace s&#039;articule autour de cinq dimensions. Elles font la différence entre un résultat générique et une base vraiment exploitable :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;La tâche&lt;/strong&gt; : Ce que l’IA doit faire concrètement, générer un écran, proposer un flow, décliner un composant.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Le contexte&lt;/strong&gt; : Où s&#039;intègre cet écran ou ce parcours dans l&#039;expérience globale ? Quel est l&#039;état précédent ?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Les éléments clés du design&lt;/strong&gt; : Les caractéristiques visuelles ou fonctionnelles importantes que l&#039;IA doit intégrer dans sa proposition.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Les comportements attendus&lt;/strong&gt; : Comment réagissent ces éléments lors de l&#039;interaction (hover, tap, scroll, états vides, erreurs…)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Les contraintes :&lt;/strong&gt; Le support, la mise en page, le style visuel, les guidelines de marque. Plus ces contraintes sont précises, plus le résultat est pertinent.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;J&#039;aime les métaphores, alors en voici une : l&#039;IA ressemble à un nouveau collaborateur. Il ne connaît pas encore le contexte, les clients, les process. Sans brief précis, il produit quelque chose de générique. Bien briefé, il va vite et produit une base exploitable.&lt;/p&gt;
&lt;p&gt;Apprendre à prompter, c&#039;est apprendre à collaborer avec l&#039;IA, formuler ce qu&#039;on veut, donner le bon niveau de contexte, et itérer ensemble.&lt;/p&gt;
&lt;h2&gt;Intégrer l&#039;IA à chaque étape&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Maintenant qu&#039;on a posé ce cadre, voyons comment ça se traduit dans le travail. J&#039;ai organisé cette exploration en trois phases : explorer, concevoir et produire. L&#039;IA n&#039;y joue pas le même rôle à chaque étape.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Une précision importante avant de commencer : &amp;quot;plus vite&amp;quot; ne veut pas dire &amp;quot;sans effort&amp;quot;. Une partie du temps gagné en génération est réinvestie en correction, ajustement et validation. L&#039;IA produit une base et non un livrable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Explorer plus vite et plus largement&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Un brief flou est l&#039;une des principales sources de perte de temps en début de projet. Avant, cela voulait dire plusieurs allers-retours avant même de commencer à concevoir. Aujourd&#039;hui, on peut envoyer le brief tel quel à un agent conversationnel, demander une structure claire avec l&#039;utilisateur cible, le problème, les objectifs attendus et les questions de clarification manquantes, et avoir une base de travail en quelques minutes.&lt;/p&gt;
&lt;p&gt;Sur les UX flows, même constat. L&#039;IA est capable de générer rapidement plusieurs scénarios, explorer des alternatives et identifier des états qu&#039;on aurait pu oublier (erreurs, compte vide, connexion perdue..). La décision reste entièrement humaine : simplifier, prioriser, arbitrer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Concevoir et itérer sans blocage&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Pour cette phase de conception, j&#039;ai testé spécifiquement deux outils : Figma Make et Claude Design.&lt;/p&gt;
&lt;p&gt;Figma Make permet de générer plusieurs propositions d&#039;écrans à partir d&#039;une description ou de frames, de tester des patterns qu&#039;on n&#039;aurait pas spontanément tentés, et d&#039;explorer des déclinaisons visuelles rapidement. C&#039;est un véritable moteur d&#039;expérimentation. Par défaut, il génère des écrans génériques, sans personnalité, sans cohérence graphique. Cette limite se réduit selon ce qu&#039;on lui fournit un prompt détaillé ou en faisant plusieurs itérations.&lt;/p&gt;
&lt;p&gt;Claude Design va plus loin dans l&#039;aboutissement. La richesse de l&#039;interface et la finesse des ajustements possibles sont intéressantes. C&#039;est à ce jour la solution qui produit les prototypes les plus aboutis parmi celles que j&#039;ai testées. Si le prompt semble insuffisant, Claude Design propose automatiquement un questionnaire pour affiner la demande avant de générer.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/workflow-ia-ux-ui/article-ai-design-03.jpg&quot; data-original-width=&quot;1920&quot; data-original-height=&quot;1080&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/workflow-ia-ux-ui/article-ai-design-03.424b3206.webp&quot; /&gt;&lt;source type=&quot;image/jpeg&quot; srcset=&quot;/media/cache/content/2026/workflow-ia-ux-ui/article-ai-design-03.jpg&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1920 / 1080)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/workflow-ia-ux-ui/article-ai-design-03.jpg&quot; alt=&quot;Exemple de Claude&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Autre détail qui a son importance, Claude Design propose deux modes d&#039;itération complémentaires : le chat pour les changements globaux, et les commentaires inline pour les ajustements précis sur un élément spécifique. En pratique, ça permet de générer rapidement des visuels utiles pour aligner une équipe, transformer une idée produit en prototype testable.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/workflow-ia-ux-ui/article-ai-design-04.jpg&quot; data-original-width=&quot;1920&quot; data-original-height=&quot;1080&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/workflow-ia-ux-ui/article-ai-design-04.5660d1fd.webp&quot; /&gt;&lt;source type=&quot;image/jpeg&quot; srcset=&quot;/media/cache/content/2026/workflow-ia-ux-ui/article-ai-design-04.jpg&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1920 / 1080)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/workflow-ia-ux-ui/article-ai-design-04.jpg&quot; alt=&quot;Exemple de Claude&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Dans les deux cas, ce que ces outils produisent est une base de travail, pas un livrable finalisé. Le designer reste celui qui la transforme en proposition exploitable.&lt;/p&gt;
&lt;p&gt;Au-delà des écrans, l&#039;IA change aussi la façon de rédiger les interfaces. Sur le microcopy, ces petits textes qui guident l&#039;utilisateur à chaque étape, c&#039;est une aide précieuse. En définissant le ton et le style de communication de la marque dans le prompt, on peut générer rapidement des textes cohérents pour tous les états d&#039;interface : succès, erreur, chargement, état vide.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Produire et livrer plus efficacement&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Le handoff, ce moment où le designer passe le relais au développeur, est souvent un moment de friction. L&#039;IA ne le supprime pas, mais elle le fluidifie. En connectant Figma à Claude via le protocole MCP (Model Context Protocol), Claude peut lire directement la structure du fichier (composants, calques, propriétés…) et produire une base de documentation de specs. C&#039;est une piste que je n&#039;ai pas encore testée personnellement, mais qui mérite d&#039;être mentionnée. La qualité du résultat dépendrait directement de la structure du fichier Figma : un fichier bien nommé avec des composants propres donnerait un résultat exploitable.&lt;/p&gt;
&lt;p&gt;C&#039;est d&#039;ailleurs une règle qui vaut pour tout l&#039;article : l&#039;IA amplifie que ce qui est déjà structuré. Un design system solide n&#039;est plus seulement un outil de cohérence visuelle, c&#039;est ce qui rend le travail exploitable par l&#039;IA.&lt;/p&gt;
&lt;h2&gt;Et côté client, qu’est-ce que ça change vraiment ?&lt;/h2&gt;
&lt;p&gt;Les deux anecdotes d&#039;introduction ne sont pas des cas isolés, elles disent quelque chose d&#039;important : le niveau de préparation et d&#039;attente des clients change. Quand un client arrive avec des maquettes générées par IA pour illustrer son idée, il ne cherche pas à faire le travail du designer. Il cherche à être compris plus vite, à aligner plus tôt, à éviter les allers-retours.&lt;/p&gt;
&lt;p&gt;Pour le designer, ça change la nature de la conversation. On ne part plus d&#039;une page blanche commune, on part d&#039;idées déjà mises en forme, parfois précises, parfois approximatives mais toujours révélatrices de ce que le client a en tête. Le client montre une direction, une intention mais pas une solution. C&#039;est au designer de définir ce qu&#039;il y a derrière.&lt;/p&gt;
&lt;p&gt;La prochaine étape naturelle, que j&#039;imagine, c&#039;est l&#039;atelier client en live : générer des variantes d&#039;écrans directement en réunion pour itérer en temps réel sur une direction. L&#039;enjeu est simple : aligner plus vite.&lt;/p&gt;
&lt;h2&gt;Pour conclure&lt;/h2&gt;
&lt;p&gt;L&#039;IA transforme réellement la pratique du design. Elle accélère certaines étapes, structure des réflexions qui prenaient du temps, et ouvre des possibilités d&#039;exploration.&lt;/p&gt;
&lt;p&gt;Elle ne connaît pas les utilisateurs, leurs habitudes, leurs frustrations, ce qui les fait décrocher. La créativité graphique, la sensibilité visuelle, la cohérence d&#039;une marque, tout cela reste entièrement humain. Mais elle propose des directions qu&#039;un designer seul n&#039;aurait pas explorées, pas parce qu&#039;elles sont meilleures, mais parce qu&#039;elle n&#039;a pas nos biais.&lt;/p&gt;
&lt;p&gt;Sur un prochain sujet, nous pourrons approfondir un aspect particulier : comment configurer Claude avec des instructions personnalisées (system prompts, custom instructions) pour lui donner un contexte permanent et des compétences adaptées à la pratique du designer.&lt;/p&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/claude-code-cursor-symfony-ai-vercel-ai-sdk-3-formations-pour-garder-la-main</id>
        <published>2026-04-28T15:42:00+02:00</published>
        <updated>2026-04-28T15:42:00+02:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/claude-code-cursor-symfony-ai-vercel-ai-sdk-3-formations-pour-garder-la-main"/>
        <title>Claude Code, Cursor, Symfony/AI, Vercel AI SDK : 3 formations pour garder la main</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="symfony" />            <category term="ia" />            <category term="formation" />        <summary><![CDATA[Trois formations IA sont disponibles dès maintenant sur JoliCampus, avec des sessions ouvertes à l&#039;inscription. Elles sont construites sur ce qu&#039;on pratique chez JoliCode et Premier Octet : des projets…]]></summary>
        <content type="html">
            &lt;p&gt;Trois formations IA sont disponibles dès maintenant sur &lt;a href=&quot;https://jolicampus.com&quot;&gt;JoliCampus&lt;/a&gt;, avec des sessions ouvertes à l&#039;inscription. Elles sont construites sur ce qu&#039;on pratique chez JoliCode et Premier Octet : des projets clients qui tournent en production avec des agents de code IA, des outils qu&#039;on utilise tous les jours, des patterns qu&#039;on a éprouvés, testés et parfois jetés !&lt;/p&gt;
&lt;p&gt;Voici ce qu&#039;elles contiennent :&lt;/p&gt;
&lt;h2&gt;Maîtriser les agents IA pour le développement&lt;/h2&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/formation-ia-article/1.png&quot; data-original-width=&quot;1600&quot; data-original-height=&quot;900&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/formation-ia-article/1.00556eaa.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;/media/cache/content/2026/formation-ia-article/1.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1600 / 900)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/formation-ia-article/1.png&quot; alt=&quot;les agents IA pour le développement&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;La porte d&#039;entrée ! Une journée pour sortir de l&#039;autocomplete et entrer dans le travail avec des agents. Au programme : fonctionnement réel d&#039;un agent (mémoire, contexte, permissions), standardisation des conventions d&#039;équipe via le fichier &lt;code&gt;AGENTS.md&lt;/code&gt; (agnostique, fonctionne avec Claude Code, Codex, Cursor), utilisation de l&#039;agent comme QA automatisée avec génération de tests, lecture des logs et boucle d&#039;auto-réparation sous supervision, puis création de commandes personnalisées pour industrialiser chez vous les revues de code, la détection de N+1, de failles XSS, et les patterns propres à votre équipe.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://jolicampus.com/formations/maitriser-les-agents-ia-pour-le-developpement&quot;&gt;Voir la formation&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;L&#039;IA pour développeurs frontend, avec Cursor et le Vercel AI SDK&lt;/h2&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/formation-ia-article/2.png&quot; data-original-width=&quot;1600&quot; data-original-height=&quot;900&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/formation-ia-article/2.97fd7971.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;/media/cache/content/2026/formation-ia-article/2.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1600 / 900)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/formation-ia-article/2.png&quot; alt=&quot;IA pour développeurs frontend&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Les deux faces du sujet. Jour 1 côté workflow : Cursor dans ses trois modes (Ask, Plan, Agent), commandes custom, intégration d&#039;outils via MCP, &lt;code&gt;.cursorrules&lt;/code&gt; pour que l&#039;agent respecte l&#039;architecture du projet, gestion du contexte sur les sessions longues.&lt;/p&gt;
&lt;p&gt;Jour 2 côté produit : construire un vrai assistant avec le Vercel AI SDK (streaming, tool calling, architecture RAG sur vos contenus, typage strict avec Zod), déploiement et les questions qui vont avec (coûts API, monitoring, stratégies de cache).&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://jolicampus.com/formations/ia-developpement-frontend-cursor-vercel-ai-sdk&quot;&gt;Voir la formation&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Maîtriser l&#039;IA avec Symfony&lt;/h2&gt;
&lt;p&gt;&lt;picture class=&quot;js-dialog-target&quot; data-original-url=&quot;/media/original/2026/formation-ia-article/3.png&quot; data-original-width=&quot;1600&quot; data-original-height=&quot;900&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/media/cache/content-webp/2026/formation-ia-article/3.1a9a7438.webp&quot; /&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;/media/cache/content/2026/formation-ia-article/3.png&quot; /&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; style=&quot;width: 996px; ; aspect-ratio: calc(1600 / 900)&quot; src=&quot;https://jolicode.com//media/cache/content/2026/formation-ia-article/3.png&quot; alt=&quot;IA avec Symfony&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Pour intégrer l&#039;IA générative dans vos applications Symfony avec la même rigueur que le reste de votre code. On s&#039;appuie &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://ai.symfony.com/&quot;&gt;sur Symfony/AI&lt;/a&gt;, le composant officiel de l&#039;écosystème, auquel on contribue directement.&lt;/p&gt;
&lt;p&gt;Socle de 2 jours : fondamentaux des LLM, ChatInterface, typage fort des sorties en objets PHP, RAG complet sur vos documents avec pgvector. Puis deux modules optionnels : le premier transforme votre application en orchestrateur d&#039;agents capables d&#039;appeler votre code PHP et vos APIs internes, le second aborde MCP, le McpBundle, les stratégies de test, le monitoring en production et la maîtrise des coûts.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://jolicampus.com/formations/maitriser-ia-symfony&quot;&gt;Voir la formation&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Et si vous ne codez pas ?&lt;/h2&gt;
&lt;p&gt;POs, chefs de projets, dirigeantes et dirigeants de boîte tech : l&#039;IA change aussi la façon dont vos équipes travaillent, à quelle cadence, sur quelle échelle de temps. Notre formation &lt;a href=&quot;https://jolicampus.com/formations/culture-engineering&quot;&gt;Culture Engineering&lt;/a&gt; a un module dédié pour comprendre ce qui bouge pour mieux collaborer avec vos équipes tech.&lt;/p&gt;
&lt;h2&gt;Pourquoi chez nous ?&lt;/h2&gt;
&lt;p&gt;On forme sur ce qu&#039;on pratique. Nos projets tournent en production avec ces outils, on contribue directement à Symfony/AI, et on publie sur &lt;a href=&quot;https://jolicode.com/blog/tag/ia&quot;&gt;notre blog&lt;/a&gt; ce qu&#039;on apprend. Ces formations vont évoluer avec l&#039;écosystème. Certains modules seront réécrits dans six mois. Un participant d&#039;aujourd&#039;hui peut revenir dans un an sur une session mise à jour, et ce sera la suite logique, pas une répétition 🙂&lt;/p&gt;
&lt;p&gt;Toutes nos formations &lt;a href=&quot;https://jolicampus.com/formations/financement&quot;&gt;sont certifiées Qualiopi, finançables via les OPCO&lt;/a&gt;, et modulables en intra-entreprise. Un doute sur la formation qui correspond à votre équipe ? &lt;a href=&quot;https://jolicampus.com/contact&quot;&gt;Écrivez-nous !&lt;/a&gt;.&lt;/p&gt;

        </content>
    </entry>    <entry>
        <id>https://jolicode.com/blog/plan-de-migration-vers-tailwind-css-v4-la-methode-presque-sans-douleur</id>
        <published>2026-04-27T10:20:00+02:00</published>
        <updated>2026-04-27T10:20:00+02:00</updated>
        <link type="text/html" rel="alternate" href="https://jolicode.com/blog/plan-de-migration-vers-tailwind-css-v4-la-methode-presque-sans-douleur"/>
        <title>Plan de migration vers Tailwind CSS v4 🚀 : la méthode (presque) sans douleur</title>
        <author>
            <name>JoliCode Team</name>
            <uri>https://jolicode.com/</uri>
        </author>            <category term="framework" />            <category term="méthodologie" />            <category term="css" />            <category term="tailwind" />        <summary><![CDATA[Ça y est, le grand jour est arrivé ! Vous avez enfin décidé de vous attaquer à cette fameuse dette technique qui vous fait faire des cauchemars la nuit. 😅
Beaucoup de nos projets (et sans doute les vôtres)…]]></summary>
        <content type="html">
            &lt;p&gt;Ça y est, le grand jour est arrivé ! Vous avez enfin décidé de vous attaquer à cette fameuse dette technique qui vous fait faire des cauchemars la nuit. 😅&lt;/p&gt;
&lt;p&gt;Beaucoup de nos projets (et sans doute les vôtres) reposent encore sur d’anciens frameworks CSS basés sur &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://sass-lang.com/&quot;&gt;Sass&lt;/a&gt;, comme &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://getbootstrap.com/docs/4.6/getting-started/introduction/&quot;&gt;Bootstrap v4&lt;/a&gt; ou d&#039;autres solutions maison (coucou &lt;a href=&quot;https://jolicode.com/blog/notre-framework-dinterface-atomic-builder-partie-2&quot;&gt;Atomic Builder&lt;/a&gt; 👋). Et soyons honnêtes : maintenir et mettre à jour ces projets relève souvent du parcours du combattant. On fait face à un manque cruel de flexibilité, la dette technique s’accumule, et les dépendances commencent à poser plus de soucis qu&#039;elles n&#039;en résolvent.&lt;/p&gt;
&lt;p&gt;Sans oublier que Sass, bien qu’ayant été notre fidèle compagnon pendant de très (très) nombreuses années, devient de moins en moins indispensable avec l&#039;évolution fulgurante du CSS natif. Nous avons d’ailleurs eu l’occasion d’en parler dans notre article expliquant pourquoi &lt;a href=&quot;https://jolicode.com/blog/passer-a-postcss-pour-un-projet-sans-sass&quot;&gt;passer à PostCSS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Le choix s&#039;est donc naturellement porté vers &lt;strong&gt;Tailwind CSS v4&lt;/strong&gt;. C&#039;est une solution qui a fait ses preuves chez JoliCode (&lt;a href=&quot;https://jolicode.com/blog/jai-teste-tailwind-css&quot;&gt;J&#039;ai testé Tailwind CSS&lt;/a&gt;) et, cerise sur le gâteau, cette nouvelle version ne nécessite aucune dépendance liée à Sass.&lt;/p&gt;
&lt;p&gt;L’année dernière, nous avons amorcé des phases de migration pour plusieurs de nos projets clients. Aujourd’hui, j’ai envie de partager avec vous quelques retours d’expérience et une méthodologie (testée et approuvée ✅) pour que ces migrations se passent le mieux possible !&lt;/p&gt;
&lt;h2&gt;Phase 1 : Migration des feuilles de styles CSS 🎨&lt;/h2&gt;
&lt;p&gt;L’objectif de cette première phase est simple : utiliser du CSS natif et dire officiellement au revoir à notre bon vieux Sass. 👋&lt;/p&gt;
&lt;p&gt;La toute première étape consiste à migrer la configuration globale de votre projet (généralement définie dans des fichiers tels que &lt;code&gt;variables.scss&lt;/code&gt; ou &lt;code&gt;global.scss&lt;/code&gt;) vers un système basé sur des &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://developer.mozilla.org/fr/docs/Web/CSS/Reference/Properties/--*&quot;&gt;&lt;em&gt;custom properties&lt;/em&gt;&lt;/a&gt; (variables CSS), en ciblant la racine du document HTML via la pseudo-classe &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://developer.mozilla.org/fr/docs/Web/CSS/Reference/Selectors/:root&quot;&gt;&lt;code&gt;:root&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Avant (Sass) :&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$color-primary: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;#f7d325&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$color-secondary: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;#ff2951&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;$font-family-sans-serif: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;Source Sans Pro&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;sans-serif&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Après (CSS natif) :&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;:root&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  --color-primary: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;#f7d325&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  --color-secondary: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;#ff2951&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  --font-sans: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;Source Sans Pro&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;sans-serif&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;c-alert c-alert--note&quot;&gt;
    &lt;p class=&quot;c-alert__title&quot;&gt;
                    &lt;span class=&quot;c-icon c-icon--monospace&quot;&gt;
                &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; aria-hidden=&quot;true&quot; class=&quot;c-icon__svg&quot; focusable=&quot;false&quot; viewBox=&quot;0 0 70 71&quot;&gt;&lt;path fill-rule=&quot;nonzero&quot; d=&quot;M35 .9c19.3 0 35 15.7 35 35s-15.7 35-35 35-35-15.7-35-35S15.7.9 35 .9m0 5c-16.552 0-30 13.449-30 30s13.448 30 30 30c16.552.103 30-13.448 30-30 0-16.551-13.448-30-30-30m0 24.9c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V33.8c0-1.7 1.3-3 3-3m0-11c.8 0 1.6.3 2.3.9.6.5.9 1.3.9 2.1 0 .2-.1.4-.1.6-.1.2-.1.4-.2.6s-.2.3-.3.5-.3.4-.4.5c-1.1 1.1-3.1 1.1-4.2 0-.2-.2-.3-.3-.4-.5s-.2-.3-.3-.5-.2-.4-.2-.6c-.1-.2-.1-.4-.1-.6 0-.8.3-1.6.9-2.1.5-.6 1.3-.9 2.1-.9&quot;/&gt;&lt;/svg&gt;
            &lt;/span&gt;
                        &lt;strong&gt;Info&lt;/strong&gt;
    &lt;/p&gt;
    &lt;div class=&quot;c-alert__content&quot;&gt;
                &lt;p&gt;&lt;br /&gt;
Utilisez dès maintenant la &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://tailwindcss.com/docs/theme#default-theme-variable-reference&quot;&gt;nomenclature de Tailwind CSS v4&lt;/a&gt; pour nommer vos variables CSS. Ça vous fera gagner un temps précieux pour la suite. 👌&lt;/p&gt;
        &lt;/div&gt;
&lt;/div&gt;

&lt;h3&gt;Cas n°1 : Les variables et fonctions&lt;/h3&gt;
&lt;p&gt;Une fois votre thème mis en place, votre première mission sera de remplacer tous vos &lt;code&gt;$&lt;/code&gt; par des &lt;code&gt;var(--)&lt;/code&gt;. C&#039;est aussi l&#039;occasion de traduire vos fonctions Sass en CSS moderne. 👀&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Avant (Sass) :&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;.button&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  width&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: math.&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  background-color&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: $color-primary;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  &amp;#x26;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;:hover&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  &amp;#x26;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;:focus-visible&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  &amp;#x26;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;:active&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;    background-color&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;darken&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;($color-primary, &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Après (CSS natif) :&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;.button&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;  /* Le CSS natif s&#039;occupe des calculs dynamiquement ! */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  width&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;calc&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; /&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; 3&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  background-color&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(--color-primary);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  &amp;#x26;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;:hover&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  &amp;#x26;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;:focus-visible&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  &amp;#x26;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;:active&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;    /* Magie ! Les couleurs relatives en CSS natif */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;    background-color&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;hsl&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-12&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; var&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(--color-primary) &lt;/span&gt;&lt;span class=&quot;syntax-12&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;syntax-12&quot;&gt; s&lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt; calc&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;syntax-12&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; -&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; 15&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;)); &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aujourd&#039;hui, le CSS natif est devenu suffisamment mature pour remplacer la quasi-totalité des fonctions Sass. C’est particulièrement vrai pour les manipulations de couleurs, qui s’écrivent désormais très simplement grâce aux &lt;a href=&quot;https://jolicode.com/blog/les-couleurs-relatives-en-css&quot;&gt;couleurs relatives en CSS&lt;/a&gt;. 🎨&lt;/p&gt;
&lt;h3&gt;Cas n°2 : Le piège du sélecteur d’imbrication &lt;code&gt;&amp;amp;&lt;/code&gt; et du BEM&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;La bonne nouvelle&lt;/strong&gt;, c&#039;est que le CSS natif gère désormais très bien le &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://developer.mozilla.org/fr/docs/Web/CSS/Reference/Selectors/Nesting_selector&quot;&gt;&lt;em&gt;nesting&lt;/em&gt;&lt;/a&gt; (l&#039;imbrication). L&#039;esperluette (&lt;code&gt;&amp;amp;&lt;/code&gt;) pour cibler des états comme &lt;code&gt;&amp;amp;:hover&lt;/code&gt; ou &lt;code&gt;&amp;amp;:focus&lt;/code&gt; fonctionne parfaitement sans Sass.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;La mauvaise nouvelle ?&lt;/strong&gt; Si vous utilisiez le &lt;code&gt;&amp;amp;&lt;/code&gt; pour concaténer des classes, typiquement avec la &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://getbem.com/&quot;&gt;méthodologie BEM&lt;/a&gt; (&lt;code&gt;&amp;amp;__element&lt;/code&gt; ou &lt;code&gt;&amp;amp;--modifier&lt;/code&gt;), le CSS natif ne comprendra pas cette syntaxe. Il va falloir « désimbriquer » ces éléments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Avant (Sass) :&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;.link&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  color&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: $color-primary;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  &amp;#x26;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;:hover&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  &amp;#x26;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;:focus-visible&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  &amp;#x26;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;:active&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;    color&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: $color-secondary;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;  /* Sass va compiler ce code en &quot;.link--secondary&quot; */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  &amp;#x26;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;--secondary {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;    color&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: $color-secondary;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Après (CSS natif) :&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;.link&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  color&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(--color-primary);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;  /* Ce code fonctionne toujours en CSS natif ! */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  &amp;#x26;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;:hover&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  &amp;#x26;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;:focus-visible&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;  &amp;#x26;&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt;:active&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;    color&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(--color-secondary);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;/* Il faut sortir la classe enfant de l&#039;imbrication */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;.link--secondary&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;  color&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(--color-secondary);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Cas n°3 : Les media queries&lt;/h3&gt;
&lt;p&gt;Pour les &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://developer.mozilla.org/fr/docs/Web/CSS/Guides/Media_queries/Using&quot;&gt;&lt;em&gt;media queries&lt;/em&gt;&lt;/a&gt; générées via des mixins ou des fonctions Sass (du type &lt;code&gt;@include media-breakpoint-up(md)&lt;/code&gt;), passez simplement les valeurs en dur pour le moment (&lt;code&gt;@media (min-width: 768px)&lt;/code&gt;). Nous nous en occuperons plus tard, lors de l&#039;intégration de Tailwind.&lt;/p&gt;
&lt;h3&gt;Cas n°4 : Les mixins, boucles et règles complexes&lt;/h3&gt;
&lt;p&gt;Pour les cas très spécifiques (boucles &lt;code&gt;@for&lt;/code&gt;, mixins complexes), la solution la plus pragmatique est de récupérer la version déjà compilée et de la coller dans votre fichier CSS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Avant (Sass) :&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;@for&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; $i &lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; 1&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; through&lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt; 3&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;  .button--size-&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;#{$i} {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;    padding&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: #{$i}rem;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Après (CSS natif) :&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;.button--size-1&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; { &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;rem&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;.button--size-2&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; { &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;rem&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;.button--size-3&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; { &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;rem&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;À la fin de cette phase, &lt;strong&gt;le code CSS de votre projet n&#039;utilise techniquement plus aucune fonctionnalité propre à Sass. 👏&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Phase 2 : Migration (provisoire) des classes utilitaires 🧰&lt;/h2&gt;
&lt;p&gt;C&#039;est l&#039;étape de la « prise de masse » avant la sèche. 💪&lt;/p&gt;
&lt;p&gt;L&#039;idée est de récupérer le CSS compilé de toutes les classes utilitaires de votre ancien framework et de les stocker dans un nouveau dossier, par exemple &lt;code&gt;utilities/&lt;/code&gt;. Pour garder l&#039;esprit clair, séparez-les par thématique : &lt;code&gt;utilities/typography.css&lt;/code&gt;, &lt;code&gt;utilities/spacing.css&lt;/code&gt;, etc.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Exemple d&#039;un fichier &lt;code&gt;utilities/spacing.css&lt;/code&gt; (récupéré depuis Bootstrap v4) :&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;.mt-1&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; { &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;margin-top&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;0.25&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;rem&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; !important&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;.mt-2&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; { &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;margin-top&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;rem&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; !important&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;.mb-3&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; { &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;margin-bottom&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;rem&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt; !important&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;/* ... */&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Votre CSS global va grossir temporairement. C&#039;est normal, ne paniquez pas ! Ces fichiers agiront comme un filet de sécurité visuel en attendant que Tailwind prenne le relais. 😮‍💨&lt;/p&gt;
&lt;h2&gt;Phase 3 : On débranche l’ancien framework CSS 💔&lt;/h2&gt;
&lt;p&gt;Si les phases 1 et 2 ont été faites minutieusement, vous pouvez supprimer l&#039;ancien framework CSS de vos dépendances. Visuellement, rien ne devrait bouger. 🤞&lt;/p&gt;

&lt;div class=&quot;c-alert c-alert--warning&quot;&gt;
    &lt;p class=&quot;c-alert__title&quot;&gt;
                    &lt;span class=&quot;c-icon c-icon--monospace&quot;&gt;
                &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; aria-hidden=&quot;true&quot; class=&quot;c-icon__svg&quot; focusable=&quot;false&quot; viewBox=&quot;0 0 70 62&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;m41.198 3.519 27.854 47.924C71.752 56.135 68.377 62 62.806 62H7.098c-5.402 0-8.947-5.865-6.077-10.557L28.875 3.52c2.7-4.692 9.622-4.692 12.323 0Zm-8.586 2.944L5.427 52.936C4.274 54.725 5.592 57 7.734 57h54.37c2.306 0 3.624-2.275 2.471-4.063L37.39 6.464c-.988-1.95-3.79-1.95-4.778 0ZM33 45.9c1.1-1.1 3.1-1.1 4.2 0 .2.2.3.3.4.5s.2.3.3.5.2.4.2.6c.1.2.1.4.1.6 0 .8-.3 1.6-.9 2.1-.5.6-1.3.9-2.1.9s-1.6-.3-2.3-.9c-.6-.5-.9-1.3-.9-2.1 0-.2.1-.4.1-.6.1-.2.1-.4.2-.6s.2-.3.3-.5.3-.4.4-.5m2.2-27.1c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V21.8c0-1.7 1.3-3 3-3&quot;/&gt;&lt;/svg&gt;
            &lt;/span&gt;
                        &lt;strong&gt;Avertissement&lt;/strong&gt;
    &lt;/p&gt;
    &lt;div class=&quot;c-alert__content&quot;&gt;
                &lt;p&gt;&lt;br /&gt;
&lt;strong&gt;Attention aux comportements JS !&lt;/strong&gt; Si votre ancien framework (comme Bootstrap) gérait des modales, des info-bulles ou des menus déroulants, la suppression de son CSS (et de ses scripts associés) risque de casser les animations ou le positionnement. Prévoyez un peu de temps de développement pour vérifier les interactions JavaScript et réécrire ces quelques composants. Profitez-en pour utiliser du JS vanilla ou du CSS moderne (&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://developer.mozilla.org/fr/docs/Web/API/Popover_API&quot;&gt;l&#039;API Popover native&lt;/a&gt; est parfaite pour ça, par exemple !).&lt;/p&gt;
        &lt;/div&gt;
&lt;/div&gt;

&lt;h2&gt;Phase 4 : L&#039;adieu à Sass 🫡&lt;/h2&gt;
&lt;p&gt;C&#039;est l&#039;heure de désinstaller une fois pour toutes &lt;code&gt;sass&lt;/code&gt; (ou &lt;code&gt;node-sass&lt;/code&gt;). N’oubliez pas de renommer tous vos fichiers &lt;code&gt;.scss&lt;/code&gt; en &lt;code&gt;.css&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Lors de cette étape, il est très probable que le compilateur échoue lors de son premier lancement. Si la console se remplit d&#039;erreurs, pas de panique, c&#039;est un grand classique ! Cela s&#039;explique généralement par l&#039;une de ces trois raisons :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;L&#039;import fantôme&lt;/strong&gt; : Votre &lt;em&gt;bundler&lt;/em&gt; (comme &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://webpack.js.org/&quot;&gt;Webpack&lt;/a&gt;) ou votre point d&#039;entrée JavaScript (le fameux &lt;code&gt;app.js&lt;/code&gt;) pointe toujours vers un fichier portant l’extension &lt;code&gt;.scss&lt;/code&gt;. Pensez à bien mettre à jour vos chemins d&#039;importation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Le reliquat de Sass&lt;/strong&gt; : Il reste quelques &lt;code&gt;$&lt;/code&gt;, &lt;code&gt;@use&lt;/code&gt;, etc. qui traînent dans vos fichiers CSS. Pensez à les supprimer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Les commentaires&lt;/strong&gt; : Les commentaires d&#039;une seule ligne écrits avec &lt;code&gt;//&lt;/code&gt; étaient valides avec Sass, mais pas avec votre CSS natif.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;L&#039;erreur classique à corriger :&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;// Ce commentaire était valide en Sass, mais fera planter votre CSS natif !&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;.element&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; { &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(--color-primary); }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-10&quot;&gt;/* Remplacez-le par la syntaxe standard ! */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;.element&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; { &lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(--color-primary); }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Une fois ces petits ajustements faits, respirez un grand coup. Le plus dur techniquement est derrière vous. 🎉&lt;/p&gt;
&lt;h2&gt;Phase 5 : L&#039;entrée en scène de Tailwind CSS v4 🤩&lt;/h2&gt;
&lt;p&gt;Il est enfin temps d&#039;installer Tailwind CSS v4 et &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://postcss.org/&quot;&gt;PostCSS&lt;/a&gt; (indispensable dans nos projets Symfony avec &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://symfony.com/doc/current/frontend/encore/index.html&quot;&gt;Webpack Encore&lt;/a&gt;). Je vous conseille de suivre la &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://tailwindcss.com/docs/installation/using-postcss&quot;&gt;documentation officielle&lt;/a&gt; pour le &lt;em&gt;setup&lt;/em&gt; initial.&lt;/p&gt;
&lt;h3&gt;Le piège de l&#039;import global (et du Preflight)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;La documentation officielle vous recommandera d’importer Tailwind de cette façon :&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;@import&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;tailwindcss&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En utilisant cet import global, Tailwind embarque par défaut plusieurs &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://developer.mozilla.org/fr/docs/Web/CSS/Reference/At-rules/@layer&quot;&gt;&lt;em&gt;layers CSS&lt;/em&gt;&lt;/a&gt; (&lt;code&gt;theme&lt;/code&gt;, &lt;code&gt;base&lt;/code&gt;, &lt;code&gt;components&lt;/code&gt; et &lt;code&gt;utilities&lt;/code&gt;), injectant par la même occasion son propre &lt;em&gt;reset CSS&lt;/em&gt; (Preflight). Si votre projet possédait déjà un fichier de &lt;em&gt;reset&lt;/em&gt; (comme &lt;code&gt;normalize.css&lt;/code&gt;), vous risquez d’avoir des conflits.&lt;/p&gt;

&lt;div class=&quot;c-alert c-alert--note&quot;&gt;
    &lt;p class=&quot;c-alert__title&quot;&gt;
                    &lt;span class=&quot;c-icon c-icon--monospace&quot;&gt;
                &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; aria-hidden=&quot;true&quot; class=&quot;c-icon__svg&quot; focusable=&quot;false&quot; viewBox=&quot;0 0 70 71&quot;&gt;&lt;path fill-rule=&quot;nonzero&quot; d=&quot;M35 .9c19.3 0 35 15.7 35 35s-15.7 35-35 35-35-15.7-35-35S15.7.9 35 .9m0 5c-16.552 0-30 13.449-30 30s13.448 30 30 30c16.552.103 30-13.448 30-30 0-16.551-13.448-30-30-30m0 24.9c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V33.8c0-1.7 1.3-3 3-3m0-11c.8 0 1.6.3 2.3.9.6.5.9 1.3.9 2.1 0 .2-.1.4-.1.6-.1.2-.1.4-.2.6s-.2.3-.3.5-.3.4-.4.5c-1.1 1.1-3.1 1.1-4.2 0-.2-.2-.3-.3-.4-.5s-.2-.3-.3-.5-.2-.4-.2-.6c-.1-.2-.1-.4-.1-.6 0-.8.3-1.6.9-2.1.5-.6 1.3-.9 2.1-.9&quot;/&gt;&lt;/svg&gt;
            &lt;/span&gt;
                        &lt;strong&gt;Info&lt;/strong&gt;
    &lt;/p&gt;
    &lt;div class=&quot;c-alert__content&quot;&gt;
                &lt;p&gt;&lt;br /&gt;
&lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://tailwindcss.com/docs/preflight#disabling-preflight&quot;&gt;Désactivez Preflight dans un premier temps&lt;/a&gt;, ainsi que l&#039;ensemble des &lt;em&gt;layers CSS&lt;/em&gt; importés pour limiter les régressions et les problématiques liées à la &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://developer.mozilla.org/fr/docs/Web/CSS/Guides/Cascade/Introduction&quot;&gt;cascade CSS&lt;/a&gt;.&lt;/p&gt;
        &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Pour cela, privilégiez des imports ciblés (sans les &lt;em&gt;layers CSS&lt;/em&gt;) :&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;@import&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;tailwindcss/theme.css&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;@import&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt; &quot;tailwindcss/utilities.css&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;La magie de la directive &lt;code&gt;@theme&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;La révolution de la v4, c&#039;est qu&#039;elle est &lt;em&gt;CSS-first&lt;/em&gt;. Fini l&#039;énorme fichier &lt;code&gt;tailwind.config.js&lt;/code&gt; à la racine de votre projet ! Le thème que vous aviez anticipé lors de la phase 1 s&#039;intègre désormais presque nativement grâce à la nouvelle directive &lt;code&gt;@theme&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Avant :&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;:root&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  --color-primary: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;#f7d325&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  --color-secondary: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;#ff2951&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  --font-sans: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;Source Sans Pro&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;sans-serif&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Après :&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;@theme static {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  --color-primary: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;#f7d325&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  --color-secondary: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;#ff2951&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  --font-sans: &lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;Source Sans Pro&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;sans-serif&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;c-alert c-alert--note&quot;&gt;
    &lt;p class=&quot;c-alert__title&quot;&gt;
                    &lt;span class=&quot;c-icon c-icon--monospace&quot;&gt;
                &lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; aria-hidden=&quot;true&quot; class=&quot;c-icon__svg&quot; focusable=&quot;false&quot; viewBox=&quot;0 0 70 71&quot;&gt;&lt;path fill-rule=&quot;nonzero&quot; d=&quot;M35 .9c19.3 0 35 15.7 35 35s-15.7 35-35 35-35-15.7-35-35S15.7.9 35 .9m0 5c-16.552 0-30 13.449-30 30s13.448 30 30 30c16.552.103 30-13.448 30-30 0-16.551-13.448-30-30-30m0 24.9c1.7 0 3 1.3 3 3v15.3c0 1.7-1.3 3-3 3s-3-1.3-3-3V33.8c0-1.7 1.3-3 3-3m0-11c.8 0 1.6.3 2.3.9.6.5.9 1.3.9 2.1 0 .2-.1.4-.1.6-.1.2-.1.4-.2.6s-.2.3-.3.5-.3.4-.4.5c-1.1 1.1-3.1 1.1-4.2 0-.2-.2-.3-.3-.4-.5s-.2-.3-.3-.5-.2-.4-.2-.6c-.1-.2-.1-.4-.1-.6 0-.8.3-1.6.9-2.1.5-.6 1.3-.9 2.1-.9&quot;/&gt;&lt;/svg&gt;
            &lt;/span&gt;
                        &lt;strong&gt;Info&lt;/strong&gt;
    &lt;/p&gt;
    &lt;div class=&quot;c-alert__content&quot;&gt;
                &lt;p&gt;&lt;br /&gt;
&lt;strong&gt;Gardez vos variables CSS accessibles partout !&lt;/strong&gt;  Pensez à générer vos variables CSS avec le comportement &lt;code&gt;static&lt;/code&gt;, comme indiqué dans la &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://tailwindcss.com/docs/theme#generating-all-css-variables&quot;&gt;documentation Tailwind&lt;/a&gt;. Ainsi, elles ne seront pas limitées aux classes utilitaires de Tailwind, mais resteront utilisables partout dans vos propres composants CSS personnalisés.&lt;/p&gt;
        &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;C&#039;est aussi le moment parfait pour utiliser la directive &lt;code&gt;@variant&lt;/code&gt; afin d&#039;uniformiser ces fameuses &lt;em&gt;media queries&lt;/em&gt; que nous avions mises en dur plus tôt !&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Avant :&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-4&quot;&gt;@media&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; (&lt;/span&gt;&lt;span class=&quot;syntax-5&quot;&gt;min-width&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-3&quot;&gt;768&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;px&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;  .card&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;    color&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(--color-primary);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Après :&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;@variant md {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-8&quot;&gt;  .card&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-5&quot;&gt;    color&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;syntax-9&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;(--color-primary);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ce petit changement vous garantit que tout votre CSS sur-mesure restera parfaitement synchronisé avec le comportement natif des classes utilitaires de Tailwind.&lt;/p&gt;
&lt;h2&gt;Phase 6 : Le nettoyage de printemps 🧹&lt;/h2&gt;
&lt;p&gt;Je préfère être honnête : cette étape demande de la patience. 🙃&lt;/p&gt;
&lt;p&gt;L&#039;objectif est de supprimer le dossier &lt;code&gt;utilities/&lt;/code&gt; créé à la phase 2. Pour ce faire, vous allez devoir remplacer dans tous vos templates HTML/Twig les anciennes classes par les nouvelles générées par Tailwind.&lt;/p&gt;
&lt;p&gt;Vos meilleurs alliés ici seront l&#039;outil de recherche de votre IDE, les expressions régulières (Regex), et bien sûr votre LLM (&lt;em&gt;Large Language Model&lt;/em&gt;) favori (ChatGPT, Claude, GitHub Copilot, etc.) pour automatiser un maximum le processus.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Avant (Bootstrap v4) :&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;d-flex flex-column flex-md-row&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;...&amp;#x3C;/&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Après (Tailwind CSS v4) :&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;flex flex-col md:flex-row&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;...&amp;#x3C;/&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Le boss final : Les grilles 😎&lt;/h3&gt;
&lt;p&gt;Il n&#039;y a pas de solution magique ici. Les vieux frameworks utilisaient généralement &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://developer.mozilla.org/fr/docs/Web/CSS/Guides/Flexible_box_layout&quot;&gt;Flexbox&lt;/a&gt; pour simuler un comportement de grille. Tailwind, lui, utilise le module natif &lt;a rel=&quot;nofollow noopener noreferrer&quot; href=&quot;https://developer.mozilla.org/fr/docs/Web/CSS/Guides/Grid_layout&quot;&gt;&lt;em&gt;CSS Grid Layout&lt;/em&gt;&lt;/a&gt; (modèle de disposition en grille).&lt;/p&gt;
&lt;p&gt;Il va falloir repasser sur vos grilles manuellement. 😬&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L&#039;ancienne approche (Flexbox) :&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;row&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;col-12 col-md-6&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;...&amp;#x3C;/&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;col-12 col-md-6&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;...&amp;#x3C;/&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;La nouvelle approche (Grid) :&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;syntax-0&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;syntax-8&quot;&gt; class&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;syntax-1&quot;&gt;&quot;grid grid-cols-1 md:grid-cols-2&quot;&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;...&amp;#x3C;/&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;...&amp;#x3C;/&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;syntax-2&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span class=&quot;syntax-4&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;syntax-2&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C&#039;est fastidieux, mais le DOM en ressort considérablement allégé et plus lisible. 🥳&lt;/p&gt;
&lt;h2&gt;Conclusion : Tout ça pour ça ? 🤔&lt;/h2&gt;
&lt;p&gt;Oui, mille fois oui. Migrer vers Tailwind CSS v4 n&#039;est pas un parcours de santé pour un projet existant. Mais les avantages sont colossaux.&lt;/p&gt;
&lt;p&gt;Vous dites adieu à l&#039;usine à gaz qu&#039;était devenu Sass au fil des années. Le poids final de votre CSS sera drastiquement réduit puisque Tailwind ne compile que ce que vous utilisez réellement. Sans parler des performances pures : avec son nouveau moteur écrit en Rust, les temps de compilation sont si rapides qu’on les remarque à peine.&lt;/p&gt;
&lt;p&gt;Enfin, la &lt;em&gt;Developer Experience&lt;/em&gt; (DX) de vos équipes s&#039;en trouvera transformée. C&#039;est un investissement en temps conséquent, mais c&#039;est un cadeau inestimable que vous faites au futur de votre projet. 🎁&lt;/p&gt;
&lt;h3&gt;Et concrètement, comment on vend ça à sa direction ?&lt;/h3&gt;
&lt;p&gt;C&#039;est souvent la question qui fâche. On sait que le chantier est nécessaire, mais il est difficile d&#039;estimer le temps que cela va prendre sans y laisser des plumes, d&#039;où &lt;strong&gt;l&#039;effet tunnel&lt;/strong&gt; qui effraie les équipes produit.&lt;/p&gt;
&lt;p&gt;Pour débloquer la situation, nous avons l&#039;habitude chez JoliCode de commencer par un &lt;strong&gt;audit flash (1 à 2 jours)&lt;/strong&gt;. L&#039;objectif est de poser les choses à plat pour vous fournir un plan de bataille concret à défendre en interne :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Un état des lieux clair&lt;/strong&gt; de la dette front actuelle (dépendances, risques, obsolescence).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Une estimation chiffrée&lt;/strong&gt; de la migration, découpée par phase.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Un plan de déploiement progressif&lt;/strong&gt; qui ne bloque pas votre &lt;em&gt;roadmap&lt;/em&gt; produit.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Les bénéfices attendus&lt;/strong&gt; (gains de performance, vélocité des équipes, etc.).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Si vous avez besoin d&#039;aide pour cadrer votre propre migration, &lt;a href=&quot;https://jolicode.com/contact&quot;&gt;n&#039;hésitez pas à nous faire signe&lt;/a&gt; !&lt;/p&gt;
&lt;p&gt;J’espère que ce retour d’expérience vous sera utile et vous donnera le courage de sauter le pas. N&#039;hésitez pas à partager vos propres astuces ou vos galères de migration dans les commentaires ! 🙂&lt;/p&gt;

        </content>
    </entry></feed>
