<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Códice | Entradas sobre «tailwind»</title><description>Feed RSS de publicaciones etiquetadas con tailwind</description><link>https://javguerra.github.io/</link><item><title>Modo oscuro en Laravel</title><link>https://javguerra.github.io/blog/dark-mode</link><guid isPermaLink="true">https://javguerra.github.io/blog/dark-mode</guid><description>Con Alpine.js y Tailwind CSS</description><pubDate>Tue, 16 Aug 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://javguerra.github.io/_astro/dark-mode.CaMyMtw1_Z2hfeRn.webp&quot; alt=&quot;Modo oscuro&quot; style=&quot;max-width: 100%; height: auto;&quot; /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Etiquetas:&lt;/strong&gt; &lt;em&gt;#laravel&lt;/em&gt;, &lt;em&gt;#php&lt;/em&gt;, &lt;em&gt;#tema&lt;/em&gt;, &lt;em&gt;#css&lt;/em&gt;, &lt;em&gt;#javascript&lt;/em&gt;, &lt;em&gt;#alpine.js&lt;/em&gt;, &lt;em&gt;#tailwind&lt;/em&gt;&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;Basándome en &lt;a href=&quot;https://jetstream.laravel.com/&quot;&gt;Laravel Jetstream&lt;/a&gt; con &lt;a href=&quot;https://laravel-livewire.com/&quot;&gt;Livewire&lt;/a&gt;, que también incluye &lt;a href=&quot;https://alpinejs.dev/&quot;&gt;Alpine.js&lt;/a&gt; y &lt;a href=&quot;https://tailwindcss.com/&quot;&gt;TailwindCSS&lt;/a&gt;, en esta entrada muestro como implementar el modo oscuro en nuestras aplicaciones.&lt;/p&gt;
&lt;h1 id=&quot;el-modo-oscuro&quot;&gt;El modo oscuro&lt;/h1&gt;
&lt;p&gt;Cada vez es más frecuente que las aplicaciones se desarrollen pensando en implementar un modo oscuro (&lt;em&gt;dark mode&lt;/em&gt;) que permita descasar la vista del usuario con colores más apagados. La posibilidad de implementar estos modos con los &lt;em&gt;frameworks&lt;/em&gt; de CSS como Tailwindcss han ayudado a que esto sea así.&lt;/p&gt;
&lt;p&gt;Básicamente, el modo oscuro de Tailwindcss consiste en implementar clases con el prefijo &lt;code&gt;dark:&lt;/code&gt; delante. Ej.:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;bg-white dark:bg-black&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Esto cambiará el fondo de nuestra página si el modo oscuro está activo.&lt;/p&gt;
&lt;p&gt;Para activarlo, es suficiente con indicarlo añadiendo la clase &lt;code&gt;dark&lt;/code&gt; en la etiqueta &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;html&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;dark&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Mediante JavaScript podemos añadir o quitar esta clase y conseguir así que nuestra aplicación cambie el fondo del ejemplo anterior:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;document.documentElement.classList.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;dark&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;document.documentElement.classList.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;remove&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;dark&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;El &lt;a href=&quot;https://developer.mozilla.org/es/docs/Web/API/Document/documentElement&quot;&gt;&lt;code&gt;document.documentElement&lt;/code&gt;&lt;/a&gt; hace referencia al elemento raíz del documento, en este caso, la etiqueta &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;h1 id=&quot;pasos&quot;&gt;Pasos&lt;/h1&gt;
&lt;p&gt;Para poder mostrar el modo correctamente y cambiar de modo de forma interactiva, debemos configurar Tailwind en Laravel, necesitaremos un interruptor (&lt;em&gt;switch&lt;/em&gt;) en nuestra aplicación, y querremos también guardar nuestra opción del tema elegido para que se muestre correctamente la próxima vez que accedamos a la aplicación. para esto último me voy a servir de &lt;a href=&quot;https://developer.mozilla.org/es/docs/Web/API/Window/localStorage&quot;&gt;local.Storage&lt;/a&gt;. Esto implica lo siguiente:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Indicar a Tailwind que debe usar el cambio de modo oscuro manual.&lt;/li&gt;
&lt;li&gt;Comprobar el modo correspondiente al inicio en local.Storage.&lt;/li&gt;
&lt;li&gt;Guardar el tema la primera vez comprobando el modo del sistema al inicio.&lt;/li&gt;
&lt;li&gt;Mostrar los botones para cambiar de modo.&lt;/li&gt;
&lt;li&gt;Cambiar entre modo claro y modo oscuro y viceversa.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;indicar-a-tailwind-que-debe-usar-el-cambio-de-modo-oscuro-manual&quot;&gt;Indicar a Tailwind que debe usar el cambio de modo oscuro manual&lt;/h2&gt;
&lt;p&gt;Por defecto, Tailwindcss en Laravel está preparado para implementar el tema de las preferencias de modo en el sistema operativo. Si quiero cambiar de modo manualmente (&lt;em&gt;switch&lt;/em&gt;) en lugar de depender de la preferencia del sistema, debo indicarlo en el fichero &lt;code&gt;tailwind.config.js&lt;/code&gt; mediante &lt;code&gt;darkMode: &apos;class&apos;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;module&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;exports&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  darkMode: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;class&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora, en vez de escuchar las preferencias de &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme&quot;&gt;prefers-color-scheme&lt;/a&gt; del CSS, Tailwind usará el modo oscuro sólo si este está indicado en `&lt;html class=&quot;dark&quot;&gt; sin tomar en cuenta las preferencias del sistema.&lt;/p&gt;
&lt;h2 id=&quot;comprobar-el-modo-correspondiente-al-inicio-en-localstorage&quot;&gt;Comprobar el modo correspondiente al inicio en local.Storage&lt;/h2&gt;
&lt;p&gt;Cuando elegimos uno de los dos modos, ya sea el modo claro o el modo oscuro para nuestra aplicación, podemos guardarlo en local.Storage para así recuperarlo la siguiente vez que abramos nuestra aplicación. Podemos guardar &lt;code&gt;true&lt;/code&gt; o &lt;code&gt;1&lt;/code&gt; para indicar que el modo oscuro está activo o bien &lt;code&gt;false&lt;/code&gt; o &lt;code&gt;0&lt;/code&gt; para indicar lo contrario. En mi caso usaré &lt;code&gt;1&lt;/code&gt; y &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;En el siguiente código en javascript compruebo el valor de &lt;code&gt;localStorage.dark&lt;/code&gt;, y en función de su contenido, añado o quito la clase &lt;code&gt;dark&lt;/code&gt;a la etiqueta &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (localStorage.dark &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    document.documentElement.classList.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;dark&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;} &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    document.documentElement.classList.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;remove&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;dark&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tener este dato guardado nos permite, además de cambiar de modo, mostrar el icono correcto para intercambiar de modo. Pongamos que usamos un icono de media Luna para indicar el cambio al modo oscuro y un icono de Sol para indicar el cambio al modo claro. Si iniciamos la aplicación en modo oscuro, el icono a mostrar debe ser el Sol, y al contrario si iniciamos en el modo claro.&lt;/p&gt;
&lt;p&gt;El lugar adecuado para este código es el fichero en la ruta &lt;code&gt;/resources/js/app.js&lt;/code&gt; que es llamado desde el fichero en la ruta &lt;code&gt;/resources/views/layouts/app.blade.php&lt;/code&gt;pero dejándolo en &lt;code&gt;app.js&lt;/code&gt;se produce un molesto efecto de pestañeo (&lt;em&gt;flicker&lt;/em&gt;) que nos muestra primero la página en modo claro, y seguidamente hace el cambio al modo oscuro. Así pues, lo incluyo en el fichero &lt;code&gt;app.blade.php&lt;/code&gt;en el &lt;code&gt;head&lt;/code&gt; debajo del &lt;em&gt;title&lt;/em&gt; pero antes de los &lt;em&gt;links&lt;/em&gt;, de la siguiente forma:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (localStorage.dark &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        document.documentElement.classList.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;dark&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        document.documentElement.classList.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;remove&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;dark&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;guardar-el-tema-la-primera-vez-comprobando-el-modo-del-sistema-al-inicio&quot;&gt;Guardar el tema la primera vez comprobando el modo del sistema al inicio&lt;/h2&gt;
&lt;p&gt;La primera vez que abrimos nuestra aplicación no tenemos guardado ningún valor en local.Storage, así que conviene guardar el valor en función de algún criterio. Por defecto podría indicar que le modo oscuro está desactivado, pero ya que tengo la opción de conocer cuál es el tema del sistema operativo, ¿por qué no aprovecharlo? el siguiente código completa esta opción:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (localStorage.dark &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;dark&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; localStorage)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;    &amp;amp;&amp;amp;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; window.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;matchMedia&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;(prefers-color-scheme: dark)&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;).matches)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        localStorage.dark &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        document.documentElement.classList.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;dark&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        localStorage.dark &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        document.documentElement.classList.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;remove&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;dark&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;script&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si existe el valor &lt;code&gt;dark&lt;/code&gt; en local.Storage, o si no existe pero las preferencias del sistema &lt;code&gt;prefers-color-scheme&lt;/code&gt; indican que tenemos activado el modo oscuro, entonces guardo en local.Storage el valor de &lt;code&gt;dark = 1&lt;/code&gt; y añado la clase &lt;code&gt;dark&lt;/code&gt; a la etiqueta &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt;. En caso contrario, guardo el valor &lt;code&gt;dark = 0&lt;/code&gt; en local.Storage y quito la clase &lt;code&gt;dark&lt;/code&gt; de la etiqueta &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; ya que no tendremos activo el modo oscuro previamente, ni en local.Storage ni en el sistema operativo.&lt;/p&gt;
&lt;h2 id=&quot;mostrar-los-botones-para-cambiar-de-modo&quot;&gt;Mostrar los botones para cambiar de modo&lt;/h2&gt;
&lt;p&gt;En esta parte vamos a ver cómo usar Alpine.js para mostrar u ocultar un botón en vez de otro. el siguiente código debemos incluirlo en el fichero en la ruta &lt;code&gt;/resources/views/navigation-menu-blade.php&lt;/code&gt; que contiene el menú en la parte superior del tema de la aplicación que trae por defecto Laravel Jetstream. Debemos incluirlo además dos veces, una para el modo responsive y otra para el modo normal.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;&amp;lt;!-- Selector de tema oscuro --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;mr-2&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;moon&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;setMode h-4 w-4 text-gray-400 hover:text-gray-500 cursor-pointer&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; fill&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;none&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; @click&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;toggle&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; :class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;{&apos;block&apos;: !show, &apos;hidden&apos;: show}&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; x-cloak&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; xmlns&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;http://www.w3.org/2000/svg&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; viewBox&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;0 0 24 24&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; stroke&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;currentColor&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; stroke-linecap&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;round&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; stroke-linejoin&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;round&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; stroke-width&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; d&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;sun&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;setMode h-5 w-5 text-yellow-200 hover:text-yellow-300 cursor-pointer&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; fill&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;none&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; @click&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;toggle&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; :class&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;{&apos;hidden&apos;: !show, &apos;block&apos;: show}&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; x-cloak&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; xmlns&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;http://www.w3.org/2000/svg&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; viewBox&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;0 0 24 24&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; stroke&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;currentColor&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; stroke-linecap&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;round&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; stroke-linejoin&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;round&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; stroke-width&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; d&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;el &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; contiene dos SVG que corresponden a los iconos de la Luna y el Sol, pero incluyen algunas etiquetas de Alpine.js.&lt;/p&gt;
&lt;p&gt;Con &lt;code&gt;:class=&quot;{&apos;block&apos;: !show, &apos;hidden&apos;: show}&lt;/code&gt; se mostrará u ocultará el SVG en función del valor de la variable &lt;code&gt;show&lt;/code&gt;. Para definir su valor inicial, en la etiqueta &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt; de la línea 1, incluyo:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;x-data=&quot;{ open: false, show: localStorage.dark == 1 ? true: false, toggle() { this.show = !this.show } }&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Con x-data inicializo variables. Lo hago pasando un objeto JavaScript con algunos valores. En este caso, el &lt;code&gt;open: false&lt;/code&gt; del principio ya estaba ahí, pues laravel lo usa para desplegar o no el menú de usuario. Veamos el resto.&lt;/p&gt;
&lt;p&gt;Con &lt;code&gt;show: localStorage.dark == 1 ? true: false&lt;/code&gt; indico el valor de show en función de lo que indica local.Storage. Si local.Storage es &lt;code&gt;1&lt;/code&gt; entonces show será &lt;code&gt;true&lt;/code&gt;, y se mostrará el icono del Sol, según indica &lt;code&gt;{&apos;hidden&apos;: !show, &apos;block&apos;: show}&lt;/code&gt; del segundo SVG, (&lt;code&gt;&apos;hidden&apos; false, &apos;block&apos; true&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Para saber más sobre la directiva &lt;code&gt;:class&lt;/code&gt;, consultar: &lt;a href=&quot;https://alpinejs.dev/directives/bind#class-object-syntax&quot;&gt;x-bind en Tailwind.js&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Con la directiva &lt;code&gt;@click=&quot;toggle&quot;&lt;/code&gt; llamo a la función &lt;code&gt;toggle() { this.show = !this.show }&lt;/code&gt; definida en el &lt;code&gt;x-data&lt;/code&gt; cuando se hace clic sobre el icono. Esto sólo cambiará el icono haciendo que la variable show alterne su valor entre &lt;code&gt;true&lt;/code&gt; y &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;cambiar-entre-modo-claro-y-modo-oscuro-y-viceversa&quot;&gt;Cambiar entre modo claro y modo oscuro y viceversa&lt;/h2&gt;
&lt;p&gt;Hemos visto cómo cambiar entre el icono para el modo claro y oscuro y viceversa, pero lo que queremos es que se cambie también el modo en toda la aplicación. Para ello debemos gestionar eventos asociados a los iconos de tal forma que al hacer clic en ellos no sólo cambien el icono sino que añadan o quiten la etiqueta &lt;code&gt;dark&lt;/code&gt; del &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; y de esta forma hacer el cambio de modo efectivo. para conseguirlo, incluyo el siguiente código en el fichero en la ruta &lt;code&gt;/resources/js/app.js&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;document.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;.setMode&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;forEach&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;item&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;item.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;addEventListener&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;click&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (localStorage.dark &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            localStorage.dark &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            document.documentElement.classList.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;remove&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;dark&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            localStorage.dark &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;            document.documentElement.classList.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;add&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;dark&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    })&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Con &lt;code&gt;querySelectorAll(&quot;.setMode&quot;)&lt;/code&gt; busco todas las clases &lt;code&gt;setMode&lt;/code&gt; en la página. De esta forma localizo a todos los SVG que están implicados con los iconos de cambio de modo (ver que cada SVG incluye &lt;code&gt;class=&quot;setMode ...&lt;/code&gt;), y a cada uno de ellos le añado un evento &lt;code&gt;click&lt;/code&gt; de tal forma que, al hacer clic sobre estos iconos, realice la siguiente comprobación: Si el valor de &lt;code&gt;localStorage.dark == 1&lt;/code&gt; guardar el valor &lt;code&gt;0&lt;/code&gt; en local.Storage y quitar el modo oscuro con &lt;code&gt;document.documentElement.classList.remove(&apos;dark&apos;)&lt;/code&gt;. en caso contrario, guardar el valor &lt;code&gt;1&lt;/code&gt; en local.Storage y poner el modo oscuro con &lt;code&gt;document.documentElement.classList.add(&apos;dark&apos;)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Guarda todo y no olvides ejecutar &lt;code&gt;npm run dev&lt;/code&gt; desde el terminal para que los cambios funciones correctamente.&lt;/p&gt;
&lt;h1 id=&quot;enlaces&quot;&gt;Enlaces&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tailwindcss.com/docs/dark-mode&quot;&gt;Tailwindcss dark mode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blockpc.cl/blog/como-implementar-el-modo-oscuro-en-laravel&quot;&gt;Como implementar el modo oscuro en laravel?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><enclosure url="https://javguerra.github.io/_astro/dark-mode.CaMyMtw1_Z2hfeRn.webp"/><category>laravel</category><category>php</category><category>tema</category><category>css</category><category>javascript</category><category>alpine.js</category><category>tailwind</category><author>JavGuerra</author></item><item><title>Códice</title><link>https://javguerra.github.io/blog/codice</link><guid isPermaLink="true">https://javguerra.github.io/blog/codice</guid><description>Mi git-blog sobre programación y formación TIC</description><pubDate>Wed, 21 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://javguerra.github.io/_astro/codice.CdsWx0aT_ZlfDvy.webp&quot; alt=&quot;Códice&quot; style=&quot;max-width: 100%; height: auto;&quot; /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Etiquetas:&lt;/strong&gt; &lt;em&gt;#portfolio&lt;/em&gt;, &lt;em&gt;#web&lt;/em&gt;, &lt;em&gt;#código&lt;/em&gt;, &lt;em&gt;#astro&lt;/em&gt;, &lt;em&gt;#ssg&lt;/em&gt;, &lt;em&gt;#tailwind&lt;/em&gt;, &lt;em&gt;#javascript&lt;/em&gt;, &lt;em&gt;#typescript&lt;/em&gt;, &lt;em&gt;#zod&lt;/em&gt;, &lt;em&gt;#node.js&lt;/em&gt;, &lt;em&gt;#markdown&lt;/em&gt;, &lt;em&gt;#responsive&lt;/em&gt;, &lt;em&gt;#accesibilidad&lt;/em&gt;&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;Hacia tiempo que quería darle un giro a mi anterior &lt;a href=&quot;/blog/hola-mundo&quot;&gt;git-blog elaborado con Jekill&lt;/a&gt;. Hace un mes me topé con &lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt;, un SSG en JavaScript/TypeScript bajo Node.js, que cuenta con una excelente documentación, y que además incluye un tutorial para hacer un blog. Esto terminó de decidirme para realizar el cambio que ahora presento.&lt;/p&gt;
&lt;h1 id=&quot;retos&quot;&gt;Retos&lt;/h1&gt;
&lt;p&gt;Para llevar a cabo el desarrollo de un nuevo blog personalizado, se me presentaban dos retos:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Realizar la aplicación para que fuera operativa al menos con la mayoría de funcionalidades que ya tenía en el anterior blog.&lt;/li&gt;
&lt;li&gt;Migrar las entradas del blog anterior a la nueva estructura de la aplicación, lo que además implicaba repensar esta estructura.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;la-aplicación&quot;&gt;La aplicación&lt;/h2&gt;
&lt;p&gt;Elaborar la aplicación fue realmente sencillo. Siempre hay atascos, pero con la documentación, el tutorial, el Discord de Astro, los videos de YouTube y la IA, fueron subsanándose. El proyecto ha sido el mejor aprendizaje posible.&lt;/p&gt;
&lt;p&gt;Tres cosas me han llevado algo de tiempo:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Extender la colección de posts usando el esquema RSS.&lt;/li&gt;
&lt;li&gt;Utilizar el componente «Image» de Astro.&lt;/li&gt;
&lt;li&gt;JavaScript/TypeScript no funciona como se espera usando transiciones.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Lo relativo a la colección es lógico, porque estaba usando conceptos nuevos y aprendiendo a usar la &lt;a href=&quot;https://zod.dev/&quot;&gt;biblioteca Zod&lt;/a&gt; para definir y validar el esquema de los datos de las colección de entradas del blog. Zod permite validar el &lt;code&gt;frontmatter&lt;/code&gt; o cabecera de cada post para que no falte nada. La idea era partir del esquema &lt;code&gt;rssSchema&lt;/code&gt; y extenderlo para que incluyese algunos datos no contemplados, como por ejemplo el &lt;code&gt;coverImage&lt;/code&gt; o los &lt;code&gt;tags&lt;/code&gt; (&lt;em&gt;Ver fichero /src/content/config.ts&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;Pero confieso que el uso del componente Image me ha dado algún quebradero de cabeza, posiblemente relacionado con el schema mencionado, pues no me ha sido posible usar indistintamente imágenes con rutas relativas o rutas absolutas en los encabezados de las entradas. El problema tiene que ver con el atributo &lt;code&gt;src&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Si bien Image permite indicar una ruta relativa (/public) o remota (https), cuando se trata de usar la carpeta /src/assets surgen los problemas. La solución pasa por importar las imágenes, pero ¿qué ocurre cuando se ha de importar una colección de imágenes, para, por ejemplo, el listado de los proyectos del portafolio?&lt;/p&gt;
&lt;p&gt;Mi opción consiste en crear un arreglo con todas las imágenes importadas usando &lt;code&gt;import.meta.glob()&lt;/code&gt;, y luego acceder a cada elemento del arreglo referenciado por su ruta, como se muestra aquí:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;javascript&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; portfolio &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;@/content/_portfolio.json&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;  &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// proyectos con sus respectivas rutas de imagenes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; images&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;glob&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;@/assets/portfolio/*&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, { eager: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&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 style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; projects&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; portfolio.projects.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;project&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; image&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; images[&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;`/src/assets/portfolio/${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;project&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;img&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;as&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Record&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;any&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;project, image }; &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// a cada proyecto se le añade la imagen importada&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&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 style=&quot;color:#E1E4E8&quot;&gt;projects.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;project&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Image&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; src&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{project.image.default} &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;alt&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{project.alt} &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;inferSize&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Este ejemplo simplificado mostrará la lista de imágenes asociadas a los proyectos.&lt;/p&gt;
&lt;h2 id=&quot;la-migración&quot;&gt;La migración&lt;/h2&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;Códice versión Jekill&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;@/assets/img/codice-jekill.png&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Versión previa del blog en Jekill&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Tras conseguir que todo funcionase, llegó el turno de la migración, que pensaba que sería complicada, pero sólo fue laboriosa.&lt;/p&gt;
&lt;p&gt;Haciendo uso del filtrado de contenido del IDE fui cambiando los enlaces de Jekill de las referencias entre las distintas entradas, y creando el nuevo encabezado (&lt;code&gt;frontmatter&lt;/code&gt;) de cada entrada escrita en Markdown.&lt;/p&gt;
&lt;p&gt;A medida que este cambio se iba realizando fue necesario ir adaptando los estilos de texto haciendo uso de &lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind&lt;/a&gt; y su plugin &lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss-typography&quot;&gt;Typography&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Por último, como decidí incluir las transiciones de Astro, me topé con un problema curioso. Para activar o desactivar el menú hamburguesa uso JavaScript. La primera vez que se usa, funciona, pero en los siguientes casos, deja de hacerlo. Lo mismo ocurre con el botón «Volver».&lt;/p&gt;
&lt;p&gt;He tenido que hacer una comprobación para recargar siempre el evento click en ambos casos. Ver /src/components/Hamburger.astro y /src/components/ButtonLink.astro respectivamente.&lt;/p&gt;
&lt;p&gt;Lo mejor de todo es que ha sido divertido hacerlo, y conocer Astro, esta nueva y muy potente herramienta que creo que va a dar mucho juego. Quedan por delante innumerables mejoras y actualizaciones, por lo que la diversión continúa.&lt;/p&gt;
&lt;h1 id=&quot;características&quot;&gt;Características&lt;/h1&gt;
&lt;p&gt;La aplicación cuenta con las siguientes secciones: portada, sobre mi, porfolio, blog, temas y enlaces recomendados. Hace uso de las características avanzadas de Astro, y también:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Incluye RSS de contenidos&lt;/li&gt;
&lt;li&gt;Enlaza con la API de Mastodon&lt;/li&gt;
&lt;li&gt;Implementa sitemap para SEO&lt;/li&gt;
&lt;li&gt;Incluye estadísticas del sitio&lt;/li&gt;
&lt;li&gt;Integra Disqus para comentarios&lt;/li&gt;
&lt;li&gt;Implementa un buscador&lt;/li&gt;
&lt;li&gt;Usa tema de color claro/oscuro automático&lt;/li&gt;
&lt;li&gt;Realiza transiciones de página&lt;/li&gt;
&lt;li&gt;Es accesible&lt;/li&gt;
&lt;li&gt;Es responsive&lt;/li&gt;
&lt;li&gt;Usa TypeScript&lt;/li&gt;
&lt;li&gt;Contiene etiquetado Open Graph&lt;/li&gt;
&lt;li&gt;Es compatible con PWA&lt;/li&gt;
&lt;li&gt;Consigue una calificación de carbono «A» (&lt;a href=&quot;https://www.websitecarbon.com/website/javguerra-github-io/&quot;&gt;ver&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;licencias&quot;&gt;Licencias&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Sobre los contenidos (entradas del blog): &lt;a href=&quot;https://creativecommons.org/licenses/by-sa/4.0/deed.es&quot;&gt;CC BY-SA&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Sobre el código fuente: &lt;a href=&quot;https://github.com/JavGuerra/tienda-8-bits/blob/main/LICENSE&quot;&gt;GNU GENERAL PUBLIC LICENSE Version 3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Iconos: &lt;a href=&quot;https://www.svgrepo.com/collection/solar-linear-icons/&quot;&gt;Solar Linear Icons&lt;/a&gt; en SVG Repo&lt;/li&gt;
&lt;li&gt;Tipografía: &lt;a href=&quot;https://www.fontsquirrel.com/fonts/blogger-sans&quot;&gt;Blogger_Sans-Medium&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;saber-más&quot;&gt;Saber más&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/blog/api-mastodon&quot;&gt;Accediendo a la API de Mastodon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/JavGuerra/javguerra.github.io&quot;&gt;Repositorio en GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/blog/hola-mundo&quot;&gt;git-blog anterior elaborado con Jekill&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://es.wikipedia.org/wiki/Generador_de_sitios_est%C3%A1ticos&quot;&gt;SSG&lt;/a&gt;, &lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt; y &lt;a href=&quot;https://es.wikipedia.org/wiki/Astro_(framework)&quot;&gt;Astro en Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind&lt;/a&gt; y &lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss-typography&quot;&gt;Typography&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zod.dev/&quot;&gt;Biblioteca Zod&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.astro.build/es/tutorial/0-introduction/&quot;&gt;Construye tu primer blog con Astro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/sOXW0ZnJxbQ?si=yLRwfY4lyOMaHypc&quot;&gt;Curso de Astro, por Fazt, en Youtube&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;ir-a-la-página&quot;&gt;Ir a la página&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://javguerra.github.io/&quot;&gt;&lt;img src=&quot;/assets/img/qr.svg&quot; alt=&quot;Código QR&quot;&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href=&quot;https://javguerra.github.io/&quot;&gt;https://javguerra.github.io/&lt;/a&gt;&lt;/p&gt;
</content:encoded><enclosure url="https://javguerra.github.io/_astro/codice.CdsWx0aT_ZlfDvy.webp"/><category>portfolio</category><category>web</category><category>código</category><category>astro</category><category>ssg</category><category>tailwind</category><category>javascript</category><category>typescript</category><category>zod</category><category>node.js</category><category>markdown</category><category>responsive</category><category>accesibilidad</category><author>JavGuerra</author></item></channel></rss>