Lene Saile - Blog de desarrollo web
Escribo sobre cosas relacionadas con el desarrollo web. Proyectos, enfoques y observaciones, cosas que he aprendido o que considero importantes. Me especializo en sitios web creativos a medida teniendo en cuenta especialmente la accesibilidad y el rendimiento.
2024-01-02T11:30:00Z
https://www.lenesaile.com/es/
Lene Saile
hola@lenesaile.com
Adopción de dimensiones de rejilla de hermanos gracias a subgrid
2024-01-02T11:30:00Z
https://www.lenesaile.com/es/blog/adopcion-de-dimensiones-de-rejilla-de-hermanos-gracias-a-subgrid/
<p>Subgrids utiliza las pistas de rejilla de una rejilla predecesora para alinear sus elementos de rejilla. Por ejemplo, puede crear varias columnas en el elemento <code><body></code> y pasarlas “hacia abajo”, sin importar la profundidad. Lo más importante aquí: El sistema de rejilla que se pasa hacia abajo <strong>debe</strong> estar en un predecesor. Subgrid busca en el árbol DOM el elemento más cercano que defina una plantilla de columna o fila (<code>grid-template-columns</code> o <code>grid-template-rows</code>) que <em>no</em> esté marcado como subgrid.</p>
<p>Así que si creo un layout en el elemento <code><body></code>, puedo transferirlo a los landmarks <code><header></code>, <code><main></code> y <code><footer></code>, que entonces afecta a sus hijos.</p>
<h2 id="¿podemos-definir-las-dimensiones-de-la-rejilla-en-un-elemento-hermano"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/adopcion-de-dimensiones-de-rejilla-de-hermanos-gracias-a-subgrid/#%C2%BFpodemos-definir-las-dimensiones-de-la-rejilla-en-un-elemento-hermano">¿Podemos definir las dimensiones de la rejilla en un elemento hermano?</a></h2>
<p>Lo que quiero conseguir es la alineación vertical de los hermanos: las dimensiones dinámicas de los elementos dentro del <code><header></code> landmark deberían implementar la plantilla de columna de rejilla definida para el elemento <code><body></code>. Las líneas de la cuadrícula deben “anidarse” contra el logotipo SVG y el nombre del sitio web junto a él. Los elementos dentro de <code><main></code> y <code><footer></code> deben poder participar en esta cuadrícula colocándose a lo largo de las dimensiones del logotipo y el nombre de la página.</p>
<p>El título debe abarcar toda la columna, y quiero que el contenido del elemento <code><section></code> comience a la altura del título inicial de la página. El <code><nav></code> en el <code><footer></code> debe estar exactamente donde termina el título de la página.</p>
<p>Grid y subgrid no son temas fáciles de entender. Por eso intento llegar al resultado en detalle y paso a paso.</p>
<p>El código HTML es el siguiente:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>wrapper flow<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>logo<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span>
<span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 100 100<span class="token punctuation">"</span></span>
<span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1em<span class="token punctuation">"</span></span>
<span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1em<span class="token punctuation">"</span></span>
<span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/2000/svg<span class="token punctuation">"</span></span>
<span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>circle</span> <span class="token attr-name">cx</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>50<span class="token punctuation">"</span></span> <span class="token attr-name">cy</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>50<span class="token punctuation">"</span></span> <span class="token attr-name">r</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>50<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Marzipan<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cluster<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Main<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cluster<span class="token punctuation">"</span></span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>list<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Cake<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Ice cream<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Candy<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>flow<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Cupcake cake candy chupa chups tart marzipan chocolate bar<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>
Halvah shortbread topping muffin cookie gingerbread bear claw. Cheesecake oat cake
caramels powder powder cookie jelly-o. Bonbon bonbon chupa chups chupa chups
croissant. Dessert marshmallow sesame snaps liquorice jelly beans powder
marshmallow cookie. Candy shortbread wafer chocolate bar chocolate tiramisu sesame
snaps..
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>
Shortbread sesame snaps dragée brownie pastry bear claw soufflé. Tootsie roll cake
sugar plum candy jelly beans biscuit. Macaroon chupa chups bonbon cookie macaroon.
Croissant croissant jelly ice cream jelly-o tootsie roll shortbread chocolate bar.
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>copyright<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>© 2024<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Complementary<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cluster<span class="token punctuation">"</span></span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>list<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Imprint<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Privacy<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span></code></pre>
<h2 id="el-sistema-de-rejilla"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/adopcion-de-dimensiones-de-rejilla-de-hermanos-gracias-a-subgrid/#el-sistema-de-rejilla">El sistema de rejilla</a></h2>
<p>El sistema de rejilla se define para el elemento <code><body></code>. Utilizo nombres implícitos cuando configuro la rejilla para poder referirme a ellos fácilmente más tarde:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">body</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span>
[full-start] min-content
[logo-text-start] min-content
[logo-text-end] 1fr
[full-end]<span class="token punctuation">;</span>
<span class="token property">grid-template-rows</span><span class="token punctuation">:</span>
[header] auto
[main] 1fr
[footer] auto<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Las propiedades <code>grid-template-columns</code> y <code>grid-template-rows</code> crean vías de cuadrícula explícitas: las columnas de la cuadrícula deben envolver firmemente el logotipo SVG y luego alinearse con el título del sitio web. La propiedad <code>min-content</code> lo hace por nosotros: <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/min-content" rel="noopener">representa la anchura mínima real del contenido</a>.<br />
Esta pista de cuadrícula será tan pequeña como el elemento de contenido más ancho de esta columna. Así que tenemos que asegurarnos de que sólo el SVG y el título de la página hacen esto.</p>
<p>El resto del espacio disponible se reserva para el menú, que queda asegurado por la unidad <code>fr</code>.</p>
<p>Las líneas explícitas especifican que el encabezado y el pie de página deben tener una altura determinada por su contenido, mientras que la parte principal debe ocupar el espacio restante. Esto sitúa el <code><footer></code> en la parte inferior de la ventana gráfica. Para evitar que los elementos de <code><main></code> se dividan de forma extraña cuando hay poco contenido, también establecí la regla <code>main {place-content: start;}</code>.</p>
<p>Sin un subgrid, los <code>landmarks</code> ahora se atascan en las pistas que tienen asignadas.</p>
<div class="codepen" data-height="100%" data-theme-id="default" data-default-tab="result" data-slug-hash="LYapGMy" data-user="madrilene" style="box-sizing: border-box; display: flex; flex-direction: column; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<p><a href="https://codepen.io/madrilene/pen/LYapGMy">the Pen</a></p>
<a href="https://codepen.io/madrilene/pen/LYapGMy"><img style="max-width: 100%;box-shadow: 1px 1px 5px #999;" src="https://codepen.io/madrilene/pen/LYapGMy/image/large.png" /></a>
<p>Herencia de pistas de rejilla de un hermano - 1</p>
<p></p></div><p></p>
<p>Podemos contrarrestar esto haciendo que ocupen todo el ancho de la columna:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">body > *</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> 1/-1<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Pero en lugar de utilizar la notación <code>1/-1</code> para toda la anchura, también podemos utilizar nuestro mecanismo de nomenclatura.<br />
Las líneas con nombre crearon un área con nombre, que ahora puedo referenciar como <code>full</code>.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">body > *</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> full<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Las columnas de la plantilla de rejilla que se definieron en el elemento <code><body></code> deben ser adoptadas ahora por los tres hijos del elemento <code><body></code>.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">body > *</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> full<span class="token punctuation">;</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> subgrid<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Ya no son los propios landmarks los que se colocan en la cuadrícula, sino <em>sus hijos</em>, como si el diseño de la columna de la cuadrícula estuviera definido en los propios landmarks. No se trata simplemente de una copia del valor, sino que los tres elementos utilizan literalmente las pistas de cuadrícula del cuerpo.</p>
<p>Ahora los hijos de los tres landmarks definen las dimensiones de la rejilla, pero es el elemento equivocado el que define la anchura: según <code>min-content</code>, la palabra más larga del elemento “heading” dentro de <code><main></code> define ahora las dimensiones de la primera zona de la rejilla.</p>
<div class="codepen" data-height="100%" data-theme-id="default" data-default-tab="result" data-slug-hash="LYapGaL" data-user="madrilene" style="box-sizing: border-box; display: flex; flex-direction: column; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<p><a href="https://codepen.io/madrilene/pen/LYapGaL">the Pen</a></p>
<a href="https://codepen.io/madrilene/pen/LYapGaL"><img style="max-width: 100%;box-shadow: 1px 1px 5px #999;" src="https://codepen.io/madrilene/pen/LYapGaL/image/large.png" /></a>
<p>Herencia de trazas de cuadrícula de un hermano- 2</p>
<p></p></div><p></p>
<p>Los tres landmarks tienen sólo dos hijos, por lo que la tercera columna está vacía.</p>
<p>Liberemos primero a los hijos del elemento <code><main></code> de la tarea de determinar las dimensiones de la rejilla. Deben extenderse por todo el ancho disponible de la envoltura, lo que en nuestro caso sólo afecta al elemento H1, ya que todo lo que hay en el elemento <code><section></code> debe alinearse donde comienza el título del sitio web.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">main > *</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> full<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">main > section</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> logo-text-start / full-end<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<div class="codepen" data-height="100%" data-theme-id="default" data-default-tab="result" data-slug-hash="YzgywMY" data-user="madrilene" style="box-sizing: border-box; display: flex; flex-direction: column; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<p><a href="https://codepen.io/madrilene/pen/YzgywMY">the Pen</a></p>
<a href="https://codepen.io/madrilene/pen/YzgywMY"><img style="max-width: 100%;box-shadow: 1px 1px 5px #999;" src="https://codepen.io/madrilene/pen/YzgywMY/image/large.png" /></a>
<p>Herencia de trazas de cuadrícula de un hermano- 3</p>
<p></p></div><p></p>
<p><strong>¡Casi!</strong></p>
<p>Todavía falta un último paso, a saber, <code>.logo</code> con sus dos hijos también debe heredar las columnas de la rejilla de planos.</p>
<p>Sin embargo, debe extenderse desde <code>full-start</code> hasta <code>logo-text-end</code>.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.logo</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> full-start / logo-text-end<span class="token punctuation">;</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> subgrid<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Saltamos varios niveles, por así decirlo, hasta que finalmente permitimos a los hijos de <code>.logo</code> definir las dimensiones, que ahora podemos pasar a los hermanos de <code><header></code> y sus hijos.</p>
<h2 id="colocaciones-finales-en-la-rejilla"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/adopcion-de-dimensiones-de-rejilla-de-hermanos-gracias-a-subgrid/#colocaciones-finales-en-la-rejilla">Colocaciones finales en la rejilla</a></h2>
<p>He establecido un punto de ruptura para el elemento <code><nav></code> en el <code><header></code> de forma que en pantallas pequeñas quede alineado con el nombre de la página en la segunda línea y en pantallas más anchas ocupe el área entre el final del nombre de la página y el final de la envoltura:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">header > nav</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> logo-text-start / full-end<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">min-width</span><span class="token punctuation">:</span> 30em<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">header > nav</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> logo-text-end / full-end<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Por último, el párrafo del <code><footer></code> debe abarcar el ancho del nombre del sitio. Esto se consigue con otra área denominada <code>logo-text</code>.<br />
Esto hace que el siguiente <code><nav></code> ocupe el espacio restante:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">footer > p</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> logo-text<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<div class="codepen" data-height="100%" data-theme-id="default" data-default-tab="result" data-slug-hash="RwdWrap" data-user="madrilene" style="box-sizing: border-box; display: flex; flex-direction: column; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<p><a href="https://codepen.io/madrilene/pen/RwdWrap">the Pen</a></p>
<a href="https://codepen.io/madrilene/pen/RwdWrap"><img style="max-width: 100%;box-shadow: 1px 1px 5px #999;" src="https://codepen.io/madrilene/pen/RwdWrap/image/large.png" /></a>
<p>Herencia de trazas de cuadrícula de un hermano- final</p>
<p></p></div><p></p>
<p>Ahora tenemos un layout en el que la rejilla está definida en el <code><body></code>, pero sólo está realmente implementada por los hijos del <code><header></code>.</p>
<h2 id="notas"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/adopcion-de-dimensiones-de-rejilla-de-hermanos-gracias-a-subgrid/#notas">Notas</a></h2>
<p>Más abajo en el CSS encontrarás todos los pequeños ajustes que he hecho, las clases <code>.cluster</code> y <code>.flow</code> vienen de las primitivas de layout de <a href="https://every-layout.dev/" rel="noopener">every-layout.dev</a>, el tipo de fluido y las escalas de espacio fueron calculadas con la ayuda de <a href="https://utopia.fyi/" rel="noopener">utopia.fyi</a>.</p>
<p>Cathy Dutton ya tuvo la idea de utilizar una columna de rejilla padre flexible para la alineación vertical entre hermanos en un artículo publicado en <a href="https://css-tricks.com/achieving-vertical-alignment-thanks-subgrid/#aa-can-grid-help-us" rel="noopener">css-tricks.com</a> en 2020.</p>
<p>Gracias a la <a href="https://indieweb.social/@bobmonsour/111686716289873152" rel="noopener">referencia de Bob Monsour</a> al artículo de Ryan Mulligan <span lang="en">“<a href="https://ryanmulligan.dev/blog/x-scrolling-centered-max-width-container/" rel="noopener">Horizontal Scrolling in a Centered Max-Width Container</a>”</span>, he encontrado un <a href="https://www.annalytic.com/css-subgrid-vs-nested-grid.html" rel="noopener">artículo de Anna Monus</a> que explica las diferencias entre las subcuadrículas y las cuadrículas anidadas (<code>grid-template-columns: subgrid;</code> y <code>grid-template-columns: inherit;</code>).<br />
Merece la pena leerlo en este contexto.</p>
Sobre subgrid y líneas coloreadas
2023-11-15T15:20:00Z
https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/
<p>Para mí, la subgrid siempre fue lo que me faltaba para integrar realmente grid en mis flujos de trabajo.</p>
<p>Con <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/grid" rel="noopener">CSS grid</a>, las “pistas de fila” y “columna” creadas en un grid sólo pueden utilizarse para posicionar a los hijos directos del grid contenedora. Subgrid permite compartir todos los valores definidos en el grid antepasado.</p>
<p>En lugar de definir explícitamente los nombres de las líneas y las funciones de dimensionamiento de las pistas, utilizamos la palabra clave <code>subgrid</code> como valor de <code>grid-template-columns</code> o <code>grid-template-rows</code> para heredar la pista del grid más cercana.</p>
<p>Por ejemplo, un grid clásica de 12 columnas podría ser creada como padre para toda la página, y entonces podemos organizar nuestros elementos dentro de ella, sin importar lo profundamente anidados que estén. Puede consultar la <a href="https://caniuse.com/?search=subgrid" rel="noopener">compatibilidad actual del navegador con CSS Subgrid en caniuse.com</a>.</p>
<p><a href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#toc-skipped" id="skip-toc" class="visually-hidden">Saltar el índice de contenidos</a></p>
<h2 id="table-of-contents">índice de contenidos</h2>
<nav id="toc" class="table-of-contents"><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#preparacion">Preparación</a><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#configuracion-del-html">Configuración del HTML</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#el-sistema-de-grid-principal">El sistema de grid principal</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#activar-subgrid">Activar subgrid</a></li></ol></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#colocacion-de-elementos-en-el-grid">Colocación de elementos en el grid</a><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#header">header</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#main">main</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#footer">footer</a></li></ol></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#lineas-del-grid-coloreadas">Líneas del grid coloreadas</a><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#idea-1-anadir-un-hueco-y-un-color-de-fondo-del-grid-principal">Idea 1: Añadir un hueco y un color de fondo del grid principal</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#idea-2-border-color">Idea 2: border-color</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#idea-3-background-image-linear-gradient">Idea 3: background-image: linear-gradient</a></li></ol></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#actualizacion-ideas-de-la-comunidad-css">Actualización: Ideas de la comunidad CSS</a><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#linear-gradient-sin-numero-magico">linear-gradient sin número mágico</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#pseudoelementos">Pseudoelementos</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#marcadores-de-posicion-implementados-vista-movil">Marcadores de posición implementados, vista móvil</a></li></ol></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#actualizacion-2-el-enfoque-de-josh-comeau">Actualización 2: El enfoque de Josh Comeau</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#mas-sobre-subgrid">Más sobre subgrid</a></li></ol></nav><div id="toc-skipped"></div>
<h2 id="preparacion"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#preparacion">Preparación</a></h2>
<p>Tengo un diseño para un nuevo proyecto que parece hecho para Subgrid: La página está dividida en cuatro columnas dentro de un wrapper. Las cuatro columnas están visiblemente divididas por líneas verticales de 1px de ancho que van desde la parte superior de la página hasta la parte inferior. Todos los elementos y contenidos se alinean directamente con estas líneas, algunos contenidos se reparten en varias columnas, otros sólo empiezan en una línea específica del grid.</p>
<h3 id="configuracion-del-html"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#configuracion-del-html">Configuración del HTML</a></h3>
<p>He escrito el siguiente HTML para el prototípo:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>wrapper<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>landmark<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>logo<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> Biscuit! <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span><span class="token punctuation">></span></span><span class="token comment"><!-- ... --></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>landmark<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>chocolate<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>Chocolate cake<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Cookie tart cake cotton candy chocolate chocolate.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</span><span class="token punctuation">></span></span>Jelly beans<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>
Jelly beans gummi bears halvah halvah croissant lemon drops donut gummi bears
candy canes. Icing sugar plum chupa chups jelly-o soufflé jelly-o pudding
lollipop. Chocolate bar muffin bonbon pie tootsie roll danish bear claw
cheesecake.
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h3</span><span class="token punctuation">></span></span>Cheesecake<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h3</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Powder halvah soufflé caramels soufflé chocolate cake halvah.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span>
<span class="token comment"><!-- ... --></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>section</span><span class="token punctuation">></span></span>
<span class="token comment"><!-- ... --></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>section</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>landmark<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>
Diving into subgrid. Created and maintained by
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Lene<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>list<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>RSS Feed<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Follow<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Crafted with semantic HTML.<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span></code></pre>
<aside>Aquí y en todos los siguientes ejemplos de Codepen, he colocado innecesariamente un <code>div</code> entre <code><body></code> y los tres “puntos de referencia” <code><header></code>, <code><main></code> y <code><footer></code>. Sin embargo, esos puntos de referencia deben ser hijos directos de <code><body></code> <a href="https://webaim.org/techniques/semanticstructure/" rel="noopener">para ser interpretados correctamente por los lectores de pantalla</a>. Si quieres incluir los siguientes intentos en algún sitio, recominedo, eliminar el <code>div</code> y establecer el grid padre directamente en <code><body></code>.</aside>
<h3 id="el-sistema-de-grid-principal"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#el-sistema-de-grid-principal">El sistema de grid principal</a></h3>
<p>Primero, defino las dimensiones para El wrapper.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.wrapper</span> <span class="token punctuation">{</span>
<span class="token property">inline-size</span><span class="token punctuation">:</span> <span class="token function">clamp</span><span class="token punctuation">(</span>16rem<span class="token punctuation">,</span> 93vw<span class="token punctuation">,</span> 120rem<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">margin-inline</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span>
<span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Este wrapper es para dar cabida a mi sistema de grid en toda la página. Sin embargo, quiero dejar mi clase envoltorio intacta, sólo debe ocuparse de lo que su nombre implica.</p>
<p>Mi elemento <code><body></code> tiene un elemento <code>child</code> directo, mi wrapper. Utilizo este selector para definir el sístema grid por separado (una nueva clase con el nombre correspondiente también sería una opción).</p>
<pre class="language-css"><code class="language-css"><span class="token selector">body > div</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>4<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>El wrapper se divide ahora en cuatro columnas del mismo tamaño, que se colocan directamente una al lado de la otra sin espacio entre ellas.</p>
<div class="codepen" data-height="100%" data-theme-id="default" data-default-tab="result" data-slug-hash="yLZoZgE" data-user="madrilene" style="box-sizing: border-box; display: flex; flex-direction: column; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<p><a href="https://codepen.io/madrilene/pen/yLZoZgE">the Pen</a></p>
<a href="https://codepen.io/madrilene/pen/yLZoZgE"><img style="max-width: 100%;box-shadow: 1px 1px 5px #999;" src="https://codepen.io/madrilene/pen/yLZoZgE/image/large.png" /></a>
<p>subgrid con líneas: por defecto</p>
<p></p></div><p></p>
<p>El wrapper tiene tres elementos <code>child</code>, que ahora se colocan cada uno en las tres primeras columnas. Este es su comportamiento natural ya que son elementos <code>child</code> de un grid y se dividen a sí mismos en los carriles disponibles.</p>
<h3 id="activar-subgrid"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#activar-subgrid">Activar subgrid</a></h3>
<p>Quiero que los tres hitos conserven el grid padre para sí mismos.</p>
<p><strong>¡Activemos subgrid!</strong></p>
<pre class="language-css"><code class="language-css"><span class="token selector">:is(header, main, footer).landmark</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> subgrid<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Esto no funciona todavía. Puedo ver en las herramientas de desarrollo que la subgrid está activo, pero los tres todavía están atrapados en una columna.</p>
<figure role="group"><picture><source type="image/avif" srcset="https://www.lenesaile.com/assets/images/subgrid-devtools-500w.avif 500w, https://www.lenesaile.com/assets/images/subgrid-devtools-900w.avif 900w, https://www.lenesaile.com/assets/images/subgrid-devtools-1256w.avif 1256w" sizes="100vw" /><source type="image/webp" srcset="https://www.lenesaile.com/assets/images/subgrid-devtools-500w.webp 500w, https://www.lenesaile.com/assets/images/subgrid-devtools-900w.webp 900w, https://www.lenesaile.com/assets/images/subgrid-devtools-1256w.webp 1256w" sizes="100vw" /><source type="image/jpeg" srcset="https://www.lenesaile.com/assets/images/subgrid-devtools-500w.jpeg 500w, https://www.lenesaile.com/assets/images/subgrid-devtools-900w.jpeg 900w, https://www.lenesaile.com/assets/images/subgrid-devtools-1256w.jpeg 1256w" sizes="100vw" /><img src="https://www.lenesaile.com/assets/images/image-placeholder.png" data-src="/assets/images/subgrid-devtools-500w.jpeg" class="innerimage" width="500" height="145" alt="Fragmento del DOM de las herramientas de desarrollo de Firefox. Muestra el elemento body del documento y su elemento hijo, un div con clase wrapper. Los tres 'landmarks' header, main y footer están subordinados a éste. El wrapper está etiquetado como grid, los tres 'landmarks' como subgrid." loading="lazy" decoding="async" /></picture><figcaption>Captura de pantalla de la pestaña inspector de las herramientas para desarrolladores de Firefox</figcaption></figure>
<p>Esto se debe a que siguen siendo elementos <code>hijos</code> directos del sístema grid padre.</p>
<p>En cambio, quiero que ocupen todo el ancho del wrapper y, por tanto, especificaré exactamente qué columnas del grid principal deben ocupar:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">:is(header, main, footer).landmark</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> subgrid<span class="token punctuation">;</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> 1 / -1<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<div class="codepen" data-height="100%" data-theme-id="default" data-default-tab="result" data-slug-hash="zYeEJKw" data-user="madrilene" style="box-sizing: border-box; display: flex; flex-direction: column; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<p><a href="https://codepen.io/madrilene/pen/zYeEJKw">the Pen</a></p>
<a href="https://codepen.io/madrilene/pen/zYeEJKw"><img style="max-width: 100%;box-shadow: 1px 1px 5px #999;" src="https://codepen.io/madrilene/pen/zYeEJKw/image/large.png" /></a>
<p>subgrid con lineas: subgrid activada</p>
<p></p></div><p></p>
<p>Ahora nuestros tres puntos de referencia se apoderan del grid antecesora y colocan a sus propios hijos en ella. Sólo <code><main></code> se extiende por todas las columnas, pero aún así sólo ordena sus elementos en la primera columna. Llegaremos a eso en un momento.</p>
<p>Si echo un vistazo a las herramientas de desarrollo, también puedo ver que se han creado tres filas automáticamente.</p>
<p>Merece la pena echar un vistazo a las herramientas de desarrollo del grid: En Firefox, cuando la página contiene un grid con un subgrid, la entrada de la subgrid aparece sangrada bajo su principal en la sección del grid superpuesta.</p>
<figure role="group"><picture><source type="image/avif" srcset="https://www.lenesaile.com/assets/images/subgrid-devtools-details-500w.avif 500w, https://www.lenesaile.com/assets/images/subgrid-devtools-details-900w.avif 900w, https://www.lenesaile.com/assets/images/subgrid-devtools-details-1280w.avif 1280w" sizes="100vw" /><source type="image/webp" srcset="https://www.lenesaile.com/assets/images/subgrid-devtools-details-500w.webp 500w, https://www.lenesaile.com/assets/images/subgrid-devtools-details-900w.webp 900w, https://www.lenesaile.com/assets/images/subgrid-devtools-details-1280w.webp 1280w" sizes="100vw" /><source type="image/jpeg" srcset="https://www.lenesaile.com/assets/images/subgrid-devtools-details-500w.jpeg 500w, https://www.lenesaile.com/assets/images/subgrid-devtools-details-900w.jpeg 900w, https://www.lenesaile.com/assets/images/subgrid-devtools-details-1280w.jpeg 1280w" sizes="100vw" /><img src="https://www.lenesaile.com/assets/images/image-placeholder.png" data-src="/assets/images/subgrid-devtools-details-500w.jpeg" class="innerimage" width="500" height="272" alt="Captura de pantalla que muestra las casillas de verificación del grid superpuesta y los ajustes de visualización del grid para hacer visibles las líneas de un subgrid o del grid principal. El grid envolvente está seleccionado." loading="lazy" decoding="async" /></picture><figcaption>Captura de pantalla de las herramientas para desarrolladores de Firefox: Panel CSS, vista Diseño, sección grid</figcaption></figure>
<aside>Si quieres saber más sobre esto: <a href="https://firefox-source-docs.mozilla.org/devtools-user/page_inspector/how_to/examine_grid_layouts/index.html" rel="noopener">Firefox docs for the CSS Grid Inspector, chapter “Examine grid layouts”</a>.</aside>
<h2 id="colocacion-de-elementos-en-el-grid"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#colocacion-de-elementos-en-el-grid">Colocación de elementos en el grid</a></h2>
<p>Por defecto, los elementos hijo se colocan en la primera línea disponible del grid</p>
<h3 id="header"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#header">header</a></h3>
<p>Por ahora no tengo que preocuparme del elemento <code>anchor</code> en el <code><header></code>.<br />
Pero de acuerdo con el diseño, el SVG debe alinearse con la cuarta línea del grid. Logro esto alineando generalmente a la última línea de la columna:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">header.landmark svg</span> <span class="token punctuation">{</span>
<span class="token property">grid-column-start</span><span class="token punctuation">:</span> -1<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h3 id="main"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#main">main</a></h3>
<p>Mientras que <code><main></code> es un subgrid, su hijo <code><article></code> no lo es: el entorno de flujo por defecto está en su lugar. Por alguna razón tuve la sensación de que debería usar los valores de subgrid con moderación. Pero en realidad no hay ninguna razón para ello. Así que convierto todos los elementos <code><article></code> que son hijos directos de <code><main></code> en subgrids también.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">main.landmark > article</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> subgrid<span class="token punctuation">;</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> 1 / -1<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<aside>Debe tenerse en cuenta que los elementos de artículo seleccionados <em>no</em> son subgrids de <code><main></code>, sino siempre del elemento antepasado más cercano que defina una plantilla de columna que <em>no</em> sea subgrid. Esa es nuestro wrapper, seleccionada mediante <code>body > div</code>.</aside>
<p>Mirando el diseño, el título y el párrafo del <code><article></code> no deben colocarse en el flujo normal del grid, sino que cada uno ocupa su propia fila dentro de la primera columna. Hago que el párrafo ocupe su propia fila dentro de la misma columna.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">main.landmark > article > p</span> <span class="token punctuation">{</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> 2<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Las secciones de <code><main></code> están distribuidas en diferentes columnas en el diseño del escritorio. Ahora las coloco explícitamente en la fila y columna que se les ha asignado. En nuestro ejemplo, sólo hay secciones dentro de <code>main.landmark</code>, pero teniendo en cuenta la compatibilidad futura, <code><main></code> dará cabida a más elementos <code><article></code> en un HTML más extendido.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">article.work > section:first-of-type</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> 2 / -1<span class="token punctuation">;</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> 3<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">article.work > section:nth-of-type(2)</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> 3 / -1<span class="token punctuation">;</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> 4<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">article.work > section:nth-of-type(3)</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> span 1 / -1<span class="token punctuation">;</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> 5<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">article.work > section:last-of-type</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> 3 / -1<span class="token punctuation">;</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> 6<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h3 id="footer"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#footer">footer</a></h3>
<p>También quiero colocar elementos en su propia fila en el <code><footer></code>. Puedo definir esta fila extra directamente en el landmark, y además del subgrid definido anteriormente, obtiene su propio valor <code>grid-template-rows</code>.</p>
<aside>¡Ten en cuenta que también se pueden crear subgrids para <code>grid-template-rows</code>!<br />
No llegaré tan lejos en este ejemplo, pero es muy útil para crear elementos que necesitan su propio diseño de filas repetidas dentro de su columna asignada.</aside>
<p>Se supone que el primer elemento hijo se extenderá sobre dos columnas y filas, el menú y el último pragrafo se alinearán uno debajo del otro a la derecha.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">footer.landmark</span> <span class="token punctuation">{</span>
<span class="token property">grid-template-rows</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>2<span class="token punctuation">,</span> auto<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">footer.landmark p:first-of-type</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> span 2<span class="token punctuation">;</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> 1 / -1<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">footer.landmark p:last-of-type,
footer.landmark nav</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> span 2 / -1<span class="token punctuation">;</span>
<span class="token property">place-self</span><span class="token punctuation">:</span> end<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p><code>repeat(2, auto)</code> significa que habrá dos filas en el grid, y ambas deberán tener la misma altura, determinada dinámicamente en función de su contenido.<br />
auto" indica que la altura de las filas se ajustará automáticamente a su contenido.</p>
<div class="codepen" data-height="100%" data-theme-id="default" data-default-tab="result" data-slug-hash="qBgXwrK" data-user="madrilene" style="box-sizing: border-box; display: flex; flex-direction: column; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<p><a href="https://codepen.io/madrilene/pen/qBgXwrK">the Pen</a></p>
<a href="https://codepen.io/madrilene/pen/qBgXwrK"><img style="max-width: 100%;box-shadow: 1px 1px 5px #999;" src="https://codepen.io/madrilene/pen/qBgXwrK/image/large.png" /></a>
<p>subgrid con líneas: colocando elementos</p>
<p></p></div><p></p>
<h2 id="lineas-del-grid-coloreadas"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#lineas-del-grid-coloreadas">Líneas del grid coloreadas</a></h2>
<p>Nuestros elementos están alineados, pasemos a la parte que a priori parece sencilla: las columnas de nuestra maquetación deben estar marcadas por líneas divisorias de colores. ¿No puedo simplemente colorear las líneas del grid?</p>
<p>Mi idea original en realidad era, añadir de alguna manera un color a las líneas del grid de la columna en El wrapper en el fondo.</p>
<p>Eso no funciona. Puedo mostrar las líneas en las herramientas de desarrollo, pero no parece que tenga una manera de abordar realmente estas líneas con un color, usando CSS. Sin embargo, he encontrado algunos enfoques en la naturaleza (En otras palabras: Stack Overflow, también me atreví a preguntar Chat GTP, pero eso fue completamente inútil). Los enfoques no fueron creados con subgrid en mente, pero intentarlo no hace daño.</p>
<h3 id="idea-1-anadir-un-hueco-y-un-color-de-fondo-del-grid-principal"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#idea-1-anadir-un-hueco-y-un-color-de-fondo-del-grid-principal">Idea 1: Añadir un hueco y un color de fondo del grid principal</a></h3>
<p>Intentemos añadir un <code>gap</code> de 1px y un <code>background-color</code> pardel grid principal.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">body > div</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>4<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">background-color</span><span class="token punctuation">:</span> blue<span class="token punctuation">;</span>
<span class="token property">gap</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>A continuación, se asigna a las subgrids el color de fondo predeterminado.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">:is(header, main, footer).landmark,
main.landmark > article</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> subgrid<span class="token punctuation">;</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> 1 / -1<span class="token punctuation">;</span>
<span class="token property">background-color</span><span class="token punctuation">:</span> white<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<div class="codepen" data-height="100%" data-theme-id="default" data-default-tab="result" data-slug-hash="WNPEPwE" data-user="madrilene" style="box-sizing: border-box; display: flex; flex-direction: column; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<p><a href="https://codepen.io/madrilene/pen/WNPEPwE">the Pen</a></p>
<a href="https://codepen.io/madrilene/pen/WNPEPwE"><img style="max-width: 100%;box-shadow: 1px 1px 5px #999;" src="https://codepen.io/madrilene/pen/WNPEPwE/image/large.png" /></a>
<p>subgrid con lineas: gap y color de fondo</p>
<p></p></div><p></p>
<p>Bueno, eso sólo funcionó a medias. Las filas creadas intrínsecamente del grid principal son de color azul, pero como los contenedores de la subgrid abarcan todas las columnas, los huecos ya no son visibles.</p>
<h3 id="idea-2-border-color"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#idea-2-border-color">Idea 2: border-color</a></h3>
<p>¿Puedo asignar un borde a todos los elementos del grid?</p>
<pre class="language-css"><code class="language-css"><span class="token selector">body > div > *</span> <span class="token punctuation">{</span>
<span class="token property">border-inline-start</span><span class="token punctuation">:</span> 1px solid blue<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">body > div > *:last-of-type</span> <span class="token punctuation">{</span>
<span class="token property">border-inline-end</span><span class="token punctuation">:</span> 1px solid blue<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Ahora mi grid principal tiene un borde a la derecha y a la izquierda, porque son las zonas que no están superpuestas.</p>
<p>¿Y si repito esto para las subgrids?</p>
<pre class="language-css"><code class="language-css"><span class="token selector">:is(header, main, footer).landmark > *,
main.landmark > article > *</span> <span class="token punctuation">{</span>
<span class="token property">border-inline-start</span><span class="token punctuation">:</span> 1px solid blue<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">:is(header, main, footer).landmark > *:last-of-type,
main.landmark > article > *:last-of-type</span> <span class="token punctuation">{</span>
<span class="token property">border-inline-end</span><span class="token punctuation">:</span> 1px solid blue<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Ahora tengo un caos de líneas verticales, dobladas a los lados, porque el borde sólo se aplica donde hay elementos.</p>
<div class="codepen" data-height="100%" data-theme-id="default" data-default-tab="result" data-slug-hash="xxMLQQr" data-user="madrilene" style="box-sizing: border-box; display: flex; flex-direction: column; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<p><a href="https://codepen.io/madrilene/pen/xxMLQQr">the Pen</a></p>
<a href="https://codepen.io/madrilene/pen/xxMLQQr"><img style="max-width: 100%;box-shadow: 1px 1px 5px #999;" src="https://codepen.io/madrilene/pen/xxMLQQr/image/large.png" /></a>
<p>subgrid con lineas: borde</p>
<p></p></div><p></p>
<h3 id="idea-3-background-image-linear-gradient"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#idea-3-background-image-linear-gradient">Idea 3: background-image: linear-gradient</a></h3>
<p>Con este planteamiento, me despido del intento de dirigirme directamente del grid.</p>
<p>Recordé que podría colocar un <a href="https://projects.verou.me/css3patterns/#vertical-stripes" rel="noopener">patrón CSS</a> repetido en el fondo, ya que las cuatro columnas son totalmente uniformes. Esto sería como una capa gráfica que sólo intenta replicar las columnas de mi grid.</p>
<p>Tras un poco de ensayo y error, me decidí por esta variante:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">body > div</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>4<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">linear-gradient</span><span class="token punctuation">(</span>to right<span class="token punctuation">,</span> blue 1px<span class="token punctuation">,</span> transparent 1px<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">background-size</span><span class="token punctuation">:</span> 24.95%<span class="token punctuation">;</span>
<span class="token property">background-repeat</span><span class="token punctuation">:</span> repeat<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Eso es establecer un gradiente lineal para la propiedad <code>background-image</code>, creando un patrón repetitivo de una línea azul de 1 píxel sobre un fondo transparente, y cubriendo el 24,95% del ancho del contenedor. Perdón por el número aleatorio (también llamado “<a href="https://es.wikipedia.org/wiki/N%C3%BAmero_m%C3%A1gico_(inform%C3%A1tica)" rel="noopener">número mágico</a>”, como he aprendido de <a href="https://mastodon.social/@chriskirknielsen/111416719319408777" rel="noopener">Christopher Kirk-Nielsen</a>).</p>
<div class="codepen" data-height="100%" data-theme-id="default" data-default-tab="result" data-slug-hash="ExrvBYa" data-user="madrilene" style="box-sizing: border-box; display: flex; flex-direction: column; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<p><a href="https://codepen.io/madrilene/pen/ExrvBYa">the Pen</a></p>
<a href="https://codepen.io/madrilene/pen/ExrvBYa"><img style="max-width: 100%;box-shadow: 1px 1px 5px #999;" src="https://codepen.io/madrilene/pen/ExrvBYa/image/large.png" /></a>
<p>subgrid con lineas: linear-gradient</p>
<p></p></div><p></p>
<p>Es algo “hacky” y ligeramente fuera, pero lo suficientemente bueno por ahora. Estoy bastante seguro de que todavía voy a cambiarlo, ya que no produce un buen resultado en todos los viewport.</p>
<p>Esta no es una solución satisfactoria - Espero que alguien va a venir con una idea mejor y hágamelo saber acerca de él 😬</p>
<p>Es bastante “hacky” y muestra una clara desviación del grid en los dispositivos móviles. Estoy bastante segura de que todavía voy a cambiarlo, ya que no produce un resultado agradable en cada viewport.<br />
De hecho, espero que a alguien se le ocurra una idea mejor y me la comunique 😬.</p>
<h2 id="actualizacion-ideas-de-la-comunidad-css"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#actualizacion-ideas-de-la-comunidad-css">Actualización: Ideas de la comunidad CSS</a></h2>
<p>Compartí el artículo en Mastodon y, como esperaba, gente con talento aportó nuevas ideas! 🎉</p>
<h3 id="linear-gradient-sin-numero-magico"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#linear-gradient-sin-numero-magico">linear-gradient sin número mágico</a></h3>
<p><a href="https://chriskirknielsen.com/" rel="noopener">Christopher Kirk-Nielsen</a> se encargó de mi problema con los números mágicos, transformando mi solución en una mucho más limpia:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">body > div</span> <span class="token punctuation">{</span>
<span class="token property">--cols</span><span class="token punctuation">:</span> 4<span class="token punctuation">;</span>
<span class="token property">--line-size</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--cols<span class="token punctuation">)</span><span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">linear-gradient</span><span class="token punctuation">(</span>to right<span class="token punctuation">,</span> blue <span class="token function">var</span><span class="token punctuation">(</span>--line-size<span class="token punctuation">)</span><span class="token punctuation">,</span> transparent 0<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">background-size</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token punctuation">(</span>100% - <span class="token function">var</span><span class="token punctuation">(</span>--line-size<span class="token punctuation">)</span><span class="token punctuation">)</span> / <span class="token function">var</span><span class="token punctuation">(</span>--cols<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Puso el número de columnas y el <code>line-width</code> en variables y calculó el ancho deseado a partir de ahí. Lo bueno es que ahora hay una relación entre eg grid y el patrón, y podemos ajustar dinámicamente el grosor de la línea (<code>--line-size</code>), y el número de columnas (<code>--cols</code>) - se aplicarán tanto para el sistema de grid subyacente como para las líneas visibles.</p>
<div class="codepen" data-height="100%" data-theme-id="default" data-default-tab="result" data-slug-hash="JjxOGEr" data-user="chriskirknielsen" style="box-sizing: border-box; display: flex; flex-direction: column; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<p><a href="https://codepen.io/chriskirknielsen/pen/JjxOGEr">the Pen</a></p>
<a href="https://codepen.io/chriskirknielsen/pen/JjxOGEr"><img style="max-width: 100%;box-shadow: 1px 1px 5px #999;" src="https://codepen.io/chriskirknielsen/pen/JjxOGEr/image/large.png" /></a>
<p>subgrid con líneas: linear-gradient (sin número mágico)</p>
<p></p></div><p></p>
<p>Eso es fantástico, gracias! 💚</p>
<h3 id="pseudoelementos"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#pseudoelementos">Pseudoelementos</a></h3>
<p><a href="https://kizu.dev/" rel="noopener">Roma Komarov</a> ha añadido la idea de crear elementos vacíos adicionales como marcadores de posición y pseudoelementos como líneas de grid.</p>
<p>Roma aplica pseudoelementos CSS antes de los hijos del elemento <code><article></code> y después del propio elemento <code><article></code>.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">article > *::before,
article::after</span> <span class="token punctuation">{</span>
<span class="token property">contenido</span><span class="token punctuation">:</span> <span class="token string">''</span><span class="token punctuation">;</span>
<span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
<span class="token comment">/* Omitir intencionadamente el inset-inline, utilizando la posición "inicial" de estos elementos */</span>
<span class="token property">inset-block</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">anchura</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span>
color de <span class="token property">fondo</span><span class="token punctuation">:</span> azul<span class="token punctuation">;</span>
<span class="token property">z-index</span><span class="token punctuation">:</span> -1<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">article::after</span> <span class="token punctuation">{</span>
<span class="token property">derecha</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Roma observa que este método genera más líneas de las necesarias: cuando hay elementos que empiezan en las mismas líneas del grid, se solapan, lo que se hace visible al añadir un valor de <code>opacity</code>:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">article > *::before,
article::after</span> <span class="token punctuation">{</span>
<span class="token comment">/* todas las demás declaraciones CSS */</span>
<span class="token property">opacidad</span><span class="token punctuation">:</span> 0.1<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<div class="codepen" data-height="100%" data-theme-id="default" data-default-tab="result" data-slug-hash="MWLOaxW" data-user="kizu" style="box-sizing: border-box; display: flex; flex-direction: column; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<p><a href="https://codepen.io/kizu/pen/MWLOaxW">the Pen</a></p>
<a href="https://codepen.io/kizu/pen/MWLOaxW"><img style="max-width: 100%;box-shadow: 1px 1px 5px #999;" src="https://codepen.io/kizu/pen/MWLOaxW/image/large.png" /></a>
<p>subgrid con líneas: pseudo-elementos</p>
<p></p></div><p></p>
<p>Me encanta esta idea, ¡gracias! 💚</p>
<p>Roma recomienda colocar elementos vacíos en el grid, ya que cualquier cambio en las posiciones actuales de los hijos de <code><article></code> en el grid puede hacer desaparecer las líneas. Vamos a hacer esto en la siguiente sección.</p>
<h3 id="marcadores-de-posicion-implementados-vista-movil"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#marcadores-de-posicion-implementados-vista-movil">Marcadores de posición implementados, vista móvil</a></h3>
<p>Tomé la idea de Roma e implementé los marcadores de posición en otro codepen, y también añadí un media query para la mayoría de los elementos colocados en la rejilla, para hacerlos abarcar todo el ancho en dispositivos móviles (y conseguir que las líneas “funcionen” también en viewports pequeños).</p>
<p>No quiero aburrir a nadie y no voy a repetir todos los ajustes CSS aquí, si quieres saber los detalles, echa un vistazo al Codepen:</p>
<div class="codepen" data-height="100%" data-theme-id="default" data-default-tab="result" data-slug-hash="NWowaov" data-user="madrilene" style="box-sizing: border-box; display: flex; flex-direction: column; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<p><a href="https://codepen.io/madrilene/pen/NWowaov">the Pen</a></p>
<a href="https://codepen.io/madrilene/pen/NWowaov"><img style="max-width: 100%;box-shadow: 1px 1px 5px #999;" src="https://codepen.io/madrilene/pen/NWowaov/image/large.png" /></a>
<p>subgrid con lineas: pseudo-elementos, placeholders y media query</p>
<p></p></div><p></p>
<h2 id="actualizacion-2-el-enfoque-de-josh-comeau"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#actualizacion-2-el-enfoque-de-josh-comeau">Actualización 2: El enfoque de Josh Comeau</a></h2>
<p>Unos días después, Josh Comeau publicó uno de sus elaborados artículos, <em>An Interactive Guide to CSS Grid</em>. Allí no entra en subgrid, pero <a href="https://www.joshwcomeau.com/css/interactive-guide-to-grid/#grid-construction-3" rel="noopener">contribuye casualmente a la solución de mi problema</a>.</p>
<p>Para ilustrar mejor sus ejemplos, enfatiza las líneas invisibles del grid. Su enfoque es similar al de Roma: En el contexto de los pseudoelementos no trabaja con <code>background-color</code>, sino con <code>border-left</code> (Estoy utilizando propiedades lógicas en su lugar, <code>border-inline-end</code> o <code>border-right</code> funcionaría de la misma manera para nosotros).</p>
<p>Su solución tiene un caso de uso diferente, así que pude simplificar su versión.<br />
Apliqué esto al último ejemplo con los marcadores de posición vacíos (mantuve las líneas discontinuas de Josh para hacer más visible la diferencia con la solución anterior):</p>
<pre class="language-css"><code class="language-css"><span class="token selector">div.placeholder > *::before,
div.placeholder::after</span> <span class="token punctuation">{</span>
<span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">''</span><span class="token punctuation">;</span>
<span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
<span class="token property">inset-block</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">border-inline-start</span><span class="token punctuation">:</span> 2px dashed blue<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">div.placeholder::after</span> <span class="token punctuation">{</span>
<span class="token property">right</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<div class="codepen" data-height="100%" data-theme-id="default" data-default-tab="result" data-slug-hash="yLZjKzb" data-user="madrilene" style="box-sizing: border-box; display: flex; flex-direction: column; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<p><a href="https://codepen.io/madrilene/pen/yLZjKzb">the Pen</a></p>
<a href="https://codepen.io/madrilene/pen/yLZjKzb"><img style="max-width: 100%;box-shadow: 1px 1px 5px #999;" src="https://codepen.io/madrilene/pen/yLZjKzb/image/large.png" /></a>
<p>subgrid with lines: Josh Comeau's pseudo-element borders</p>
<p></p></div><p></p>
<h2 id="mas-sobre-subgrid"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/sobre-subgrid-y-lineas-coloreadas/#mas-sobre-subgrid">Más sobre subgrid</a></h2>
<ol class="list">
<li>Clásico: <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout/Subgrid" rel="noopener">mdn</a></li>
<li>Rachel Andrew con grandes ejemplos en su <a href="https://12daysofweb.dev/2022/css-subgrid/" rel="noopener">artículo para 12 Days of Web, edición 2022</a></li>
<li><a href="https://web.dev/articles/css-subgrid" rel="noopener">Artículo en web.dev</a>, basado en la idea de un grid “macro” a nivel de página</li>
<li>Se pueden encontrar grandes ejemplos de subgrid en <a href="https://gridbyexample.com/examples/#css-grid-level-2-examples" rel="noopener">gridbyexample.com</a></li>
<li><a href="https://ishadeed.com/article/learn-css-subgrid/" rel="noopener">“Learn CSS Subgrid”</a> por Ahmad Shadeed</li>
<li><a href="https://www.youtube.com/watch?v=tueTFd2TQUA&t=2266s" rel="noopener">Michelle Barker introduce subgrid</a> en su “Creative CSS Layout” en CSS Day 2022 (YouTube)</li>
<li>… y un libro: <a href="https://www.oreilly.com/library/view/css-the-definitive/9781098117603/" rel="noopener">CSS - The Definitive Guide</a></li>
</ol>
<p>Mi compañero se rió cuando me vio sentado delante del ordenador con un libro técnico, de la vieja escuela. En realidad, aquí es donde primero miro cuando quiero aprender o saber algo sobre CSS. Hay más de 80 páginas sólo sobre diseño de cuadrícula, y lo recomiendo encarecidamente. Por supuesto, no es barato y ocupa una cantidad notablemente grande de espacio en la estantería.</p>
<figure role="group"><picture><source type="image/avif" srcset="https://www.lenesaile.com/assets/images/css-definitive-guide-grid-500w.avif 500w, https://www.lenesaile.com/assets/images/css-definitive-guide-grid-900w.avif 900w, https://www.lenesaile.com/assets/images/css-definitive-guide-grid-1280w.avif 1280w" sizes="100vw" /><source type="image/webp" srcset="https://www.lenesaile.com/assets/images/css-definitive-guide-grid-500w.webp 500w, https://www.lenesaile.com/assets/images/css-definitive-guide-grid-900w.webp 900w, https://www.lenesaile.com/assets/images/css-definitive-guide-grid-1280w.webp 1280w" sizes="100vw" /><source type="image/jpeg" srcset="https://www.lenesaile.com/assets/images/css-definitive-guide-grid-500w.jpeg 500w, https://www.lenesaile.com/assets/images/css-definitive-guide-grid-900w.jpeg 900w, https://www.lenesaile.com/assets/images/css-definitive-guide-grid-1280w.jpeg 1280w" sizes="100vw" /><img src="https://www.lenesaile.com/assets/images/image-placeholder.png" data-src="/assets/images/css-definitive-guide-grid-500w.jpeg" class="innerimage" width="500" height="375" alt="El libro CSS - The Definitive Guide yace abierto en mi escritorio en un capítulo sobre subgrid, con mi teclado detrás. " loading="lazy" decoding="async" /></picture></figure>
Grabación, edición e integración de un sonido de evento de clic
2023-11-06T00:00:00Z
https://www.lenesaile.com/es/blog/grabacion-edicion-e-integracion-de-un-sonido-de-evento-de-clic/
<p>¿Quién recuerda los días en los que la música u otros ruidos desagradables empezaban a sonar de repente en cuanto visitabas una página web? Yo recuerdo bajar los altavoces antes de conectarme (sí, también recuerdo encender y apagar internet deliberadamente. Eso salía caro).</p>
<p>Durante mucho tiempo estuvo mal visto reproducir sonidos no solicitados. Los visitantes deben tener el control sobre si quieren reproducir conscientemente algo que desencadene algún tipo de sonido e imagen en movimiento. Sin embargo, en los últimos años han vuelto a aparecer pequeños sonidos de interacción en los sitios web personales, y me parece estupendo. Mi sitio web también emite un (espero) sutil sonido en dos lugares, concretamente cuando se cambia entre “claro” y “oscuro” y cuando se cambia de idioma. A continuación te explico cómo puedes integrar tú mismo un sonido que tenga como disparador un “evento de clic”.</p>
<h2 id="grabar-sonido"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/grabacion-edicion-e-integracion-de-un-sonido-de-evento-de-clic/#grabar-sonido">Grabar sonido</a></h2>
<p>Esto es muy sencillo. Hoy en día siempre tenemos un dispositivo de grabación con nosotros. Yo me pongo al lado de mi viejo interruptor de la luz y grabo su nítido sonido con mi móvil.</p>
<figure role="group"><picture><source type="image/avif" srcset="https://www.lenesaile.com/assets/images/wall-500w.avif 500w, https://www.lenesaile.com/assets/images/wall-900w.avif 900w, https://www.lenesaile.com/assets/images/wall-1280w.avif 1280w" sizes="100vw" /><source type="image/webp" srcset="https://www.lenesaile.com/assets/images/wall-500w.webp 500w, https://www.lenesaile.com/assets/images/wall-900w.webp 900w, https://www.lenesaile.com/assets/images/wall-1280w.webp 1280w" sizes="100vw" /><source type="image/jpeg" srcset="https://www.lenesaile.com/assets/images/wall-500w.jpeg 500w, https://www.lenesaile.com/assets/images/wall-900w.jpeg 900w, https://www.lenesaile.com/assets/images/wall-1280w.jpeg 1280w" sizes="100vw" /><img src="https://www.lenesaile.com/assets/images/image-placeholder.png" data-src="/assets/images/wall-500w.jpeg" class="innerimage" width="500" height="346" alt="Una pared blanca con un viejo interruptor pulsador" loading="lazy" decoding="async" /></picture><figcaption>Mi interruptor de la luz utilizado para el sonido de mi interruptor temático. Así que en cierto modo, en realidad está encendiendo y apagando mi luz.</figcaption></figure>
<p>Ahora envía el sonido a tu ordenador. Aquí todavía tenemos que cortarlo. Una herramienta gratuita y fácil de usar para esto es <em>Audacity</em>.</p>
<h2 id="instalar-y-usar-audacity"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/grabacion-edicion-e-integracion-de-un-sonido-de-evento-de-clic/#instalar-y-usar-audacity">Instalar y usar Audacity</a></h2>
<p>Mi sistema funciona bajo Ubuntu, he instalado <em>Audacity</em> a través de la Snap Store:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sudo</span> <span class="token function">apt</span> update
<span class="token function">sudo</span> <span class="token function">apt</span> <span class="token function">install</span> snapd
<span class="token function">sudo</span> snap <span class="token function">install</span> audacity</code></pre>
<p>Puedes encontrar las descargas para todos los sistemas operativos en la página web: <a href="https://www.audacityteam.org/download/" rel="noopener">https://www.audacityteam.org/download/</a></p>
<p>Una vez que hayas iniciado Audacity, arrastra tu archivo de audio a la ventana de edición. En mi caso, tengo un archivo `.ogg’ de mi teléfono móvil.</p>
<figure role="group"><picture><source type="image/avif" srcset="https://www.lenesaile.com/assets/images/audacity-500w.avif 500w, https://www.lenesaile.com/assets/images/audacity-900w.avif 900w" sizes="100vw" /><source type="image/webp" srcset="https://www.lenesaile.com/assets/images/audacity-500w.webp 500w, https://www.lenesaile.com/assets/images/audacity-900w.webp 900w" sizes="100vw" /><source type="image/jpeg" srcset="https://www.lenesaile.com/assets/images/audacity-500w.jpeg 500w, https://www.lenesaile.com/assets/images/audacity-900w.jpeg 900w" sizes="100vw" /><img src="https://www.lenesaile.com/assets/images/image-placeholder.png" data-src="/assets/images/audacity-500w.jpeg" class="innerimage" width="500" height="276" alt="Interfaz de Audacity con archivo de audio cargado" loading="lazy" decoding="async" /></picture><figcaption>La interfaz de Audacity parece un poco desordenada, pero no necesitas la mayor parte de ella</figcaption></figure>
<p>Ahora puedes seleccionar un rango para su clip de audio y luego separarlo del resto del archivo mediante “Edit” > “Clip Boundaries” > “Split”.</p>
<p>Ahora tiene tres áreas.</p>
<figure role="group"><picture><source type="image/avif" srcset="https://www.lenesaile.com/assets/images/audacity-three-areas-500w.avif 500w, https://www.lenesaile.com/assets/images/audacity-three-areas-900w.avif 900w" sizes="100vw" /><source type="image/webp" srcset="https://www.lenesaile.com/assets/images/audacity-three-areas-500w.webp 500w, https://www.lenesaile.com/assets/images/audacity-three-areas-900w.webp 900w" sizes="100vw" /><source type="image/jpeg" srcset="https://www.lenesaile.com/assets/images/audacity-three-areas-500w.jpeg 500w, https://www.lenesaile.com/assets/images/audacity-three-areas-900w.jpeg 900w" sizes="100vw" /><img src="https://www.lenesaile.com/assets/images/image-placeholder.png" data-src="/assets/images/audacity-three-areas-500w.jpeg" class="innerimage" width="500" height="190" alt="Interfaz de Audacity con el archivo de audio dividido en tres áreas, la del medio es la selección que exportaremos" loading="lazy" decoding="async" /></picture></figure>
<p>Puedes seleccionar y borrar las secciones que no quieras. Asegúrese de que el sonido comienza bastante rápido, ya que un retraso audible después del “evento clic” no parece natural.</p>
<p>Puedes ajustar el volumen en la parte izquierda del área de edición. Mi sonido original es un poco demasiado alto e intenso para mi gusto, sobre todo porque probablemente nadie espera que la acción haga un ruido audible. Baja un poco el volumen hasta que suene cómodo en relación con el volumen normal de reproducción de música (hoy en día la gente no baja el volumen de los altavoces).</p>
<p>Hay otros efectos que puedes probar, pero ahora estoy contento con mi grabación.</p>
<p>Puedes guardar tu sonido a través de “File” > “Export” y “Export as mp3”. Se te pedirá la ubicación de almacenamiento y podrás añadir metadatos.</p>
<p>Mi archivo tiene un tamaño de 3,9kb.</p>
<h2 id="integra-el-sonido-en-tu-web"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/grabacion-edicion-e-integracion-de-un-sonido-de-evento-de-clic/#integra-el-sonido-en-tu-web">Integra el sonido en tu web</a></h2>
<p>Para que esto funcione, no necesitas un <em>generador de sitios web estáticos</em> (una herramienta que crea sitios web por adelantado, lo que los hace más rápidos y seguros ya que no dependen del procesamiento del lado del servidor), simplemente puedes poner tu sonido en una carpeta de tu elección.</p>
<p>En mi proyecto, añado <code>light-on.mp3</code> a una carpeta situada en <code>src/assets/sounds</code>.</p>
<p>Construí este sitio web con <a href="https://www.11ty.dev/" rel="noopener">Eleventy</a> y necesito un llamado <em>build-step</em> que transfiera todo desde mi carpeta de entrada a la carpeta de salida.</p>
<p>Uso <a href="https://www.11ty.dev/docs/copy/" rel="noopener">Passthrough File Copy</a> en mi <a href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/">archivo de configuración de Eleventy</a> para asegurar que la carpeta y su contenido son copiados.</p>
<pre class="language-js"><code class="language-js">eleventyConfig<span class="token punctuation">.</span><span class="token function">addPassthroughCopy</span><span class="token punctuation">(</span><span class="token string">'src/activos/sonidos/'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>En tu HTML tienes que definir el área que debe “escuchar” el click. En mi caso se trata de un “botón” junto a mi nombre y el elemento de navegación.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>theme-toggle<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token comment"><!-- svg icon, zum Beispiel --></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></code></pre>
<p>Incrusto mi sonido como parte de un conmutador de temas con algunos ajustes más, pero la parte relevante en mi JavaScript es esta:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">let</span> switchSound<span class="token punctuation">;</span>
document<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'DOMContentLoaded'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
switchSound <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Audio</span><span class="token punctuation">(</span><span class="token string">'/assets/sounds/light-on.mp3'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
switchSound<span class="token punctuation">.</span><span class="token function">load</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token function-variable function">onClick</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>switchSound<span class="token punctuation">)</span> <span class="token punctuation">{</span>
switchSound<span class="token punctuation">.</span><span class="token function">play</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
window<span class="token punctuation">.</span><span class="token function-variable function">onload</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'#theme-toggle'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> onClick<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>La primera línea declara una variable llamada <code>switchSound</code> sin inicializarla. Al estar definida en un ámbito superior puede ser accedida por diferentes partes del código. A continuación escuchamos el evento <code>DOMContentLoaded</code>, que se dispara cuando el documento HTML ha sido completamente cargado y analizado, pero los recursos externos como las hojas de estilo y las imágenes pueden no haber sido completamente cargados todavía. Esto es para asegurarse de que la carga del sonido no bloquea ningún recurso importante.</p>
<p>Dentro del escuchador de eventos para <code>DOMContentLoaded</code>, creo el nuevo objeto <code>Audio</code> y lo asigno a la variable <code>switchSound</code>. La reproducción del sonido (usando el método <code>play()</code> del <code>HTMLMediaElement</code>) tiene lugar en la función <code>onClick</code>.</p>
<p>El evento <code>load</code> para el objeto <code>window</code> se activa cuando se carga toda la página, incluyendo estilos, imágenes y otros recursos. Está disponible a través de la propiedad <code>onload</code>.<br />
Hago que escuche los clics en el <code>button</code> con un ID de <code>#theme-toggle</code>.</p>
<p>Para un conmutador de temas válido tienen que pasar más cosas, puedes echar un vistazo al <a href="https://github.com/madrilene/lenesaile.com/blob/main/src/assets/scripts/theme-toggle.js" rel="noopener">conmutador de temas de esta página web en GitHub</a>.</p>
Demos para Eleventy Excellent
2023-10-30T00:00:00Z
https://www.lenesaile.com/es/blog/demos-para-eleventy-excellent/
<p>He creado algunas <a href="https://eleventy-excellent.netlify.app/blog/demo-pages/" rel="noopener">ramas de demostración para Eleventy Excellent</a>.</p>
<p>A veces recibo peticiones a través de los <em>issues</em> para añadir <em>tags</em>, o algo similar. Esto tiene sentido, por supuesto, pero después de muchos años de lucha con temas premium de otras herramientas (<em>ejem WordPress ejem</em>) que querían cubrir todos los casos de uso concebibles y por lo tanto eran excesivamente complejos y cojos, es muy importante para mí mantener un espíritu limpio y simple.</p>
<p>El starter debe poder desarrollarse en todas las direcciones posibles sin tener que tomar demasiadas decisiones de antemano. Por eso decidí crear algunos refinamientos adicionales como “demos” que viven en su propia rama, pero se mueven junto con la rama principal.</p>
<p>Hasta ahora, me he ocupado de cuestiones que me han sido solicitadas específicamente: <a href="https://eleventy-excellent-gallery.netlify.app/gallery/" rel="noopener">galería</a>, <a href="https://eleventy-excellent-tags.netlify.app/tags/" rel="noopener">etiquetas</a> y <a href="https://eleventy-excellent-pagination.netlify.app/blog/page-1/" rel="noopener">paginación</a>. Habrá más en el futuro.</p>
Internacionalización con Eleventy 2.0 y Netlify
2023-01-25T00:00:00Z
https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/
<p>Soy de Alemania, y ahora vivo y trabajo en España. Mi vida cotidiana es trilingüe, ya que el inglés es el idioma en el que se desarrolla gran parte de mi actividad profesional.</p>
<p>Cuando hace unos meses rehice mi página web personal (¡ahí es donde estás ahora mismo!), se me ocurrió la idea de que todo el contenido estuviera disponible en los tres idiomas. Si lo piensas bien, ¡es realmente una contribución a la accesibilidad!</p>
<p>Vale, digamos que al menos es un esfuerzo de <em>Diseño Inclusivo</em>. Como consecuencia natural, estoy aumentando mi alcance.</p>
<p>Así que me puse manos a la obra. Y he aprendido mucho por el camino. Por ejemplo, nunca había visto la cabecera <code>accept-language</code>, que indica el idioma natural y la configuración regional que prefiere el usuario.</p>
<p>En este artículo, que publico junto con mi <a href="https://cfe.dev/speakers/lene-saile/" rel="noopener">charla relámpago en TheJam.dev 2023</a>, explico cómo se puede hacer una configuración básica para la internacionalización con <a href="https://www.11ty.dev/" rel="noopener">Eleventy</a>.</p>
<p>Eleventy incluye un <a href="https://www.11ty.dev/docs/plugins/i18n/" rel="noopener">plugin específico para este propósito</a> con la versión 2.0 que hace las cosas difíciles por nosotros en segundo plano.</p>
<p>Mi objetivo es construir un proyecto multilingüe de arranque, que sea lo más simple y comprensible posible.</p>
<p><strong>¡Comencemos!</strong></p>
<p><a href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#toc-skipped" id="skip-toc" class="visually-hidden">Saltar el índice de contenidos</a></p>
<h2 id="table-of-contents">índice de contenidos</h2>
<nav id="toc" class="table-of-contents"><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#instalacion-de-eleventy-20">Instalación de Eleventy 2.0</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#ajustes-basicos">Ajustes básicos</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#internacionalizacion-y-localizacion">Internacionalización y Localización</a><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#localizacion">Localización</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#internacionalizacion">Internacionalización</a></li></ol></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#patrones-para-urls-especificas-de-la-localizacion">Patrones para URLs específicas de la localización</a><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#localizacion-explicita-e-implicita">Localización explícita e implícita</a></li></ol></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#creando-la-estructura-de-carpetas">Creando la estructura de carpetas</a><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#slugs-de-url-localizadas">Slugs de URL localizadas</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#separar-el-codigo-de-la-traduccion">Separar el código de la traducción</a></li></ol></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#datos-globales">Datos globales</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#usando-el-plugin-de-internacionalizacion-de-eleventy">Usando el plugin de internacionalización de Eleventy</a><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#uso-de-la-etiqueta-de-idioma-para-la-plantilla-de-pagina-actual">Uso de la etiqueta de idioma para la plantilla de página actual</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#usando-el-filtro-locale_links">Usando el filtro 'locale_links</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#usando-el-filtro-locale_url">Usando el filtro “locale_url”</a><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#locale_url-para-x-default-hreflang">‘locale_url’ para ‘x-default hreflang’</a></li></ol></li></ol></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#mas-ajustes-de-accesibilidad">Más ajustes de accesibilidad</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#redirigir-al-visitante-a-su-directorio-de-idioma-preferido">Redirigir al visitante a su directorio de idioma preferido</a><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#%C2%BFcomo-se-determina-el-idioma-preferido">¿Cómo se determina el idioma preferido?</a></li></ol></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#estableciendo-reglas-de-redireccion-con-netlify">Estableciendo reglas de redirección con Netlify</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#notas-finales">Notas finales</a><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#mencion-honorifica-css">Mención honorífica CSS</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#bifurcar-el-repositorio">Bifurcar el repositorio</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#algunos-enlaces-relacionados-con-el-tema">Algunos enlaces relacionados con el tema</a></li></ol></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#thejamdev-2023-lightning-talk">TheJam.dev 2023 Lightning Talk</a></li></ol></nav><div id="toc-skipped"></div>
<h2 id="instalacion-de-eleventy-20"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#instalacion-de-eleventy-20">Instalación de Eleventy 2.0</a></h2>
<p>Asegúrate de que <a href="https://docs.npmjs.com/downloading-and-installing-node-js-and-npm" rel="noopener">tienes Node.js instalado</a> primero. Después de ejecutar <code>npm init</code>, obtendrás un archivo <code>package.json</code> que contendrá metadatos sobre tu proyecto y registrará tus dependencias.</p>
<aside>Si todo esto te suena críptico, te recomiendo encarecidamente <a href="https://css-tricks.com/a-complete-beginners-guide-to-npm/" rel="noopener">esta serie para principiantes para npm</a> en CSS Tricks. Josh Comeau escribió una excelente guía sobre <a href="https://www.joshwcomeau.com/javascript/terminal-for-js-devs/" rel="noopener">cómo usar la Terminal</a>. </aside>
<p>Crea una nueva carpeta de proyecto, luego “cd” en ella. Yo he llamado a la mía “eleventy-i18n-starter”.</p>
<p>Para instalar Eleventy en tu proyecto, en la línea de comandos, ejecuta:</p>
<pre class="language-plaintext"><code class="language-plaintext">npm install @11ty/eleventy --save-dev</code></pre>
<p>¡No necesitamos instalar nada más! 🎉</p>
<h2 id="ajustes-basicos"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#ajustes-basicos">Ajustes básicos</a></h2>
<p>Primero, creamos un <a href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/">Eleventy config file</a>. Aquí especificamos dónde se encuentran nuestros archivos fuente, y cómo debe llamarse nuestra carpeta de salida. También hago saber a Eleventy que usamos Nunjucks` como <a href="https://www.11ty.dev/docs/languages/" rel="noopener">lenguaje de plantilla</a> global por defecto para nuestros archivos markdown y HTML.</p>
<pre class="language-js"><code class="language-js">module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">eleventyConfig</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token punctuation">{</span>
<span class="token literal-property property">dir</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token literal-property property">input</span><span class="token operator">:</span> <span class="token string">'src'</span><span class="token punctuation">,</span>
<span class="token literal-property property">output</span><span class="token operator">:</span> <span class="token string">'dist'</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token literal-property property">markdownTemplateEngine</span><span class="token operator">:</span> <span class="token string">'njk'</span><span class="token punctuation">,</span>
<span class="token literal-property property">htmlTemplateEngine</span><span class="token operator">:</span> <span class="token string">'njk'</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>A continuación, crea la carpeta de entrada <code>src</code>, y en ella dos carpetas, llamadas <code>_data</code> (la necesitaremos más adelante) e <code>_includes</code>. Dentro de <code>_includes</code> añade un archivo llamado <code>base.njk</code>. Contiene nuestro “layout” principal:</p>
<p><code>base.njk</code>:</p>
<pre class="language-html"><code class="language-html"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">doctype</span> <span class="token name">html</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">dir</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">charset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>UTF-8<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">http-equiv</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>X-UA-Compatible<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ie=edge<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width, initial-scale=1.0<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>{{ title }}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span>
<span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span>
<span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://cdn.jsdelivr.net/gh/kimeiga/bahunya/dist/bahunya.min.css<span class="token punctuation">"</span></span>
<span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>canonical<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ meta.url }}{{ page.url }}<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>description<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ description }}<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span>
{% include "header.njk" %}
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>{{ title }}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span>
{{ content | safe }}
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span>
{% include "footer.njk" %}
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre>
<p>Para nuestro sencillo ejemplo, sólo necesitamos un <a href="https://www.11ty.dev/docs/layouts/" rel="noopener">layout</a> que utilizamos para envolver todo el contenido.</p>
<p>Sólo he puesto algunos detalles muy básicos y necesarios en la sección <code>head</code>.<br />
La etiqueta <code>title</code> y la meta <code>description</code> serán extraídas de nuestros archivos de plantilla markdown, para el estilo utilizo un framework CSS sin clases de 10KB en un CDN, ya que este artículo no trata sobre CSS.</p>
<p>El landmark <code>main</code> contiene nuestro contenido, que es generado por archivos markdown. Para evitar que se llene demasiado, externalizo los puntos de referencia <code>header</code> y <code>footer</code> en layout partials.</p>
<p>Añade los parciales <code>header.njk</code> y <code>footer.njk</code> en la misma carpeta.<br />
Por ahora, déjalos vacíos.</p>
<p>Para poder iniciar Eleventy con un comando rápido, añadimos el script CLI en la sección scripts del <code>package.json</code>.</p>
<p><code>package.json</code>:</p>
<pre class="language-js"><code class="language-js"> <span class="token string-property property">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token string-property property">"start"</span><span class="token operator">:</span> <span class="token string">"eleventy --serve --watch"</span><span class="token punctuation">,</span>
<span class="token string-property property">"build"</span><span class="token operator">:</span> <span class="token string">"eleventy"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span></code></pre>
<aside>Para el comando <code>start</code> (desarrollo local) establezco los flags <code>--serve</code> (inicia el propio servidor de desarrollo por defecto de Eleventy) y <code>--watch</code> (vigila los cambios en los archivos). Hay más flags disponibles: lee sobre <a href="https://www.11ty.dev/docs/usage/" rel="noopener">command line usage</a> en los docs de Eleventy.</aside>
<h2 id="internacionalizacion-y-localizacion"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#internacionalizacion-y-localizacion">Internacionalización y Localización</a></h2>
<p>Antes de continuar, echemos un vistazo rápido a los términos internacionalización (también conocido como “i18n”) y localización (“l10n”), con los que inevitablemente te encontrarás.</p>
<h3 id="localizacion"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#localizacion">Localización</a></h3>
<p>El objetivo de la localización es situar los contenidos en un contexto estructural y lingüísticamente correcto para una localización determinada.</p>
<p>Las localizaciones están circunscritas por códigos de idioma ISO, compuestos por un idioma base y el país (territorio) de uso.<br />
Tomando el alemán como ejemplo, existen los siguientes códigos de localización:</p>
<dl>
<dt><code>de</code></dt>
<dd>Alemán</dd>
<dt><code>de-AT</code></dt>
<dd>Alemán (Austria)</dd>
<dt><code>de-CH</code></dt>
<dd>Alemán (Suiza)</dd>
<dt><code>de-DE</code></dt>
<dd>Alemán (Alemania)</dd>
<dt><code>de-LI</code></dt>
<dd>Alemán (Liechtenstein)</dd>
<dt><code>de-LU</code></dt>
<dd>Alemán (Luxemburgo)</dd>
</dl>
<p>Como parte del proceso de localización, hay que traducir el contenido, ajustar el tono de voz para adaptarlo a la cultura, añadir las divisas y unidades de medida utilizadas en esa región o ajustar los elementos multimedia para que el mensaje se transmita correctamente en ese contexto cultural.</p>
<p>En el ámbito de este artículo nos preocupamos más de la <strong>internacionalización</strong>.</p>
<h3 id="internacionalizacion"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#internacionalizacion">Internacionalización</a></h3>
<p>Con la internacionalización <em>preparas</em> tu código para los requisitos de las diferentes localizaciones y todo el proceso de localización.</p>
<p>Por ejemplo, queremos asegurarnos de que los idiomas que se escriben de derecha a izquierda se manejen correctamente con CSS, o que los contenidos de texto de diferentes idiomas puedan ocupar diferentes cantidades de espacio sin romper el diseño. Las palabras alemanas pueden ser bastante largas.</p>
<p>Si quieres saber más al respecto, aquí tienes algunas palabras largas en alemán.<br />
Por alguna razón las he ido recopilando a lo largo de los años.</p>
<details>
<summary>Larga lista de palabras largas en alemán</summary>
<div lang="de" id="wordlist">
<ul class="list">
<li>Pfarrgemeinderatsmitglied</li>
<li>Liegenschaftskataster</li>
<li>Kunstfahndungsdienststelle</li>
<li>Nichtregierungsorganisationen</li>
<li>Mobilfunkanrufaufzeichnungen</li>
<li>Säbelscheidenschienbein</li>
<li>Strukturentwicklungszulagen</li>
<li>nordwestmecklenburgisch</li>
<li>Unabhängigkeitsreferendum</li>
<li>Junggesellinnenabschiedsparty</li>
<li>Reiseschnäppchenportal</li>
<li>Selbstverwirklichungsversprechen</li>
<li>Unabhängigkeitsbestrebungen</li>
<li>Nachwuchsunternehmerin</li>
<li>Desinformationskampagne</li>
<li>Bundesprogrammkommission</li>
<li>Hauptstadtbüroleiterin</li>
<li>Altmedienbesitzstandswahrung</li>
<li>Weltanschauungsvereinigungen</li>
<li>aufmerksamkeitsökonomisch</li>
<li>Majestätsbeleidungsparagrafen</li>
<li>Zweckentfremdungsverbot</li>
<li>Suchmaschinenergebnisseiten</li>
<li>Lebensmittelverteilungszentrum</li>
<li>Rassekaninchenzüchterverein</li>
<li>Bundespsychotherapeutenkammer</li>
<li>Auslandsdirektinvestitionen</li>
<li>Nachwuchsleistungszentren</li>
<li>Wildwasserkajakstrecke</li>
<li>Satellitenbeobachtungsprogramm</li>
<li>Onlinelebensmittellieferdienst</li>
<li>Jahresdurchschnittstemperaturen</li>
<li>Androgenisierungserscheinungen</li>
<li>Urananreicherungszentrum</li>
<li>Senatsuntersuchungsausschuss</li>
<li>Energieforschungskooperationen</li>
<li>Antiglobalisierungstendenzen</li>
<li>Renationalisierungstendenzen</li>
<li>Eiskunstlaufenthusiastinnen</li>
<li>Antikorruptionsstaatsanwältin</li>
<li>Selbstzerfleischungsprozess</li>
<li>Unterbringungsmöglichkeiten</li>
<li>Heimatschutzministerium</li>
<li>Verkehrsüberwachungsmaßnahmen</li>
<li>Geschwindigkeitsbegrenzungsschild</li>
<li>Kunsthandwerkergenossenschaft</li>
<li>Fruchstaftgetränkehersteller</li>
<li>Wahlkampffinanzierungsgesetze</li>
<li>Unabhängigkeitsbefürworterin</li>
<li>Vizepräsidentschaftskandidatin</li>
<li>Geldwäscheverdachtsmeldungen</li>
</ul>
</div>
</details>
<p>La internacionalización y la localización serias no son tarea fácil.</p>
<p>En mi página web personal (y en el ámbito de este proyecto) me tomo la libertad de determinar que sólo haya <em>un</em> idioma inglés o español para todos los visitantes, así que todos mis lectores deben aguantarme mezclando alegremente el inglés británico y el americano en ortografía y expresiones, porque no sé hacerlo mejor.</p>
<p>Como en esta página web, utilizo el inglés, el español y el alemán para el proyecto inicial, simplemente porque sé hablar estos idiomas y no me fío de las traducciones automáticas. De lo contrario, un lenguaje RTL, por supuesto, habría sido intrigante.</p>
<aside>Por favor, siéntete libre de añadir más idiomas vía “pull request” al <a href="https://github.com/madrilene/eleventy-i18n" rel="noopener">proyecto starter que estamos creando</a>, especialmente si es un idioma RTL.</aside>
<p>Empecemos a pensar en una estructura para nuestros tres idiomas.</p>
<h2 id="patrones-para-urls-especificas-de-la-localizacion"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#patrones-para-urls-especificas-de-la-localizacion">Patrones para URLs específicas de la localización</a></h2>
<p>Hay varios patrones para construir identificadores en las URLs.</p>
<p>Puedes optar por comprar dominios específicos de un país (<code>tuweb.de</code>), crear subdominios (<code>de.tuweb.com</code>), añadir parámetros de URL (<code>tuweb.com?lang=de</code>) o crear subdirectorios localizados (<code>tuweb.com/de</code>).<br />
Google Search Central ofrece <a href="https://developers.google.com/search/docs/specialty/international/managing-multi-regional-sites#locale-specific-urls" rel="noopener">una tabla con algunos pros y contras</a>.</p>
<p>Eleventy compila basándose en la estructura de carpetas, por lo que los subdirectorios localizados funcionan perfectamente. Este es también el procedimiento <a href="https://www.11ty.dev/docs/i18n/" rel="noopener">recomendado por Eleventy</a>.</p>
<h3 id="localizacion-explicita-e-implicita"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#localizacion-explicita-e-implicita">Localización explícita e implícita</a></h3>
<p>A continuación, tenemos que considerar si queremos tener subdirectorios localizados <em>explícitos</em> o <em>implícitos</em>. Es decir, ¿queremos un código de localización de dos letras para <em>todos</em> los idiomas en la URL (explícito), o debe mostrarse nuestro idioma principal en la URL sin un identificador (implícito)?</p>
<p>Ambas variantes son posibles. Si tienes un idioma primario muy claro en el sitio web y tal vez ni siquiera quieres traducir todos los contenidos, tiene sentido no mostrar un código de idioma en la URL para tu idioma primario. En los <a href="https://www.11ty.dev/docs/i18n/#distinct-urls-using-implied-default-language" rel="noopener">docs del plugin</a> se explica cómo se configura esto.</p>
<p>En mi sitio personal todo el contenido está traducido. Y aunque considero que el inglés es el idioma principal, valoro los tres idiomas por igual. Así que opté por el esquema explícito, y así es como queremos hacerlo también en este proyecto starter.</p>
<h2 id="creando-la-estructura-de-carpetas"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#creando-la-estructura-de-carpetas">Creando la estructura de carpetas</a></h2>
<p>Vamos a añadir tres carpetas, nombradas por el código ISO de dos caracteres de cada idioma, y en cada una crearemos un archivo llamado <code>index.md</code>.</p>
<p>Nuestra estructura de carpetas tiene ahora este aspecto:</p>
<!-- prettier-ignore -->
<pre class="language-md"><code class="language-md">│
├── src
│ │
│ ├── _data
│ ├── _includes
│ ├── de
│ │ └── index.md
│ ├── en
│ │ └── index.md
│ ├── es
│ │ └── index.md
│</code></pre>
<p>Pondremos algunos contenidos localizados en cada archivo:</p>
<p><strong><code>en/index.md</code>:</strong></p>
<pre class="language-md"><code class="language-md"><span class="token front-matter-block"><span class="token punctuation">---</span>
<span class="token front-matter yaml language-yaml"><span class="token key atrule">title</span><span class="token punctuation">:</span> <span class="token string">'English Page'</span>
<span class="token key atrule">description</span><span class="token punctuation">:</span> <span class="token string">'This is the english version of the homepage'</span></span>
<span class="token punctuation">---</span></span>
This is a minimal starter for localized content with Eleventy.</code></pre>
<p><strong><code>de/index.md</code>:</strong></p>
<pre class="language-md"><code class="language-md"><span class="token front-matter-block"><span class="token punctuation">---</span>
<span class="token front-matter yaml language-yaml"><span class="token key atrule">title</span><span class="token punctuation">:</span> <span class="token string">'Deutsche Seite'</span>
<span class="token key atrule">description</span><span class="token punctuation">:</span> <span class="token string">'Dies ist die deutsche Version der Startseite'</span></span>
<span class="token punctuation">---</span></span>
Dies ist ein minimaler Starter für lokalisierte Inhalte mit Eleventy.</code></pre>
<p><strong><code>es/index.md</code>:</strong></p>
<pre class="language-md"><code class="language-md"><span class="token front-matter-block"><span class="token punctuation">---</span>
<span class="token front-matter yaml language-yaml"><span class="token key atrule">title</span><span class="token punctuation">:</span> <span class="token string">'Página en español'</span>
<span class="token key atrule">description</span><span class="token punctuation">:</span> <span class="token string">'Esta es la versión en español de la página inical'</span></span>
<span class="token punctuation">---</span></span>
Este es un starter mínimo para contenido localizado con Eleventy.</code></pre>
<p>Si no cambiamos la estructura de enlaces permanentes en Eleventy, tomará nuestra estructura de carpetas dentro del directorio de entrada tal cual. El archivo <code>index.md</code> es reconocido como la raíz del directorio de idioma y transferido directamente como <code>index.html</code>.</p>
<p>Añadamos una subpágina, la clásica “sobre mí”, y añadamos algunos contenidos localizados.</p>
<p>Llamo a esta página <code>about.md</code> en ambos idiomas. Esto tiene varias ventajas:</p>
<ul class="list">
<li>El plugin i18n Eleventy reconoce las páginas relacionadas</li>
<li>odas las páginas se muestran en el mismo orden</li>
<li>Mantiene mi proyecto lo más mínimo y simple posible.</li>
</ul>
<p>Si miramos en nuestra carpeta de salida, vemos que ahora se han creado tres nuevas subcarpetas, todas llamadas “about”.</p>
<!-- prettier-ignore -->
<pre class="language-md"><code class="language-md">│
├── dist
│ │
│ ├── de
│ │ └── index.html
│ │ └── about
│ │ └── index.html
│ ├── en
│ │ └── index.html
│ │ └── about
│ │ └── index.html
│ ├── es
│ │ └── index.html
│ │ └── about
│ │ └── index.html
│</code></pre>
<p>Ten en cuenta que esta es la estructura final de nuestra página web, por lo que el enlace para la página “Sobre mí” en alemán sería: <code>www.mywebsite.com/de/about</code>.</p>
<h3 id="slugs-de-url-localizadas"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#slugs-de-url-localizadas">Slugs de URL localizadas</a></h3>
<p>Prefiero que los idiomas sean coherentes entre sí. Así que vamos a ajustar los permalinks.</p>
<p><strong><code>de/about.md</code>:</strong></p>
<pre class="language-md"><code class="language-md"><span class="token front-matter-block"><span class="token punctuation">---</span>
<span class="token front-matter yaml language-yaml"><span class="token key atrule">title</span><span class="token punctuation">:</span> <span class="token string">'Über mich'</span>
<span class="token key atrule">description</span><span class="token punctuation">:</span> <span class="token string">'Eine deutsche Unterseite'</span>
<span class="token key atrule">permalink</span><span class="token punctuation">:</span> /de/ueber<span class="token punctuation">-</span>mich/index.html</span>
<span class="token punctuation">---</span></span>
Ich bin Webentwicklerin und Designerin, geboren in Berlin, zu Hause in Madrid.</code></pre>
<p>Si quieres automatizar esto un poco más y hacerlo más intuitivo de usar, aquí hay una solución inteligente que encontré en el <a href="https://github.com/11ty/eleventy-base-blog/tree/v2" rel="noopener">Eleventy Base Blog</a>.</p>
<p>Tienes que añadir <a href="https://www.11ty.dev/docs/data-template-dir/" rel="noopener">Parent Directory Data Files</a> dentro de todas las subcarpetas localizadas.</p>
<p>Aquí, pasas el idioma respectivo en el permalink y compruebas si hay una entrada de datos frontmatter opcional en tu plantilla, que se slugifica (¿existe esa palabra? 😶).</p>
<p><strong><code>es/es.11tydata.js</code>:</strong></p>
<pre class="language-js"><code class="language-js">module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token literal-property property">lang</span><span class="token operator">:</span> <span class="token string">'es'</span><span class="token punctuation">,</span>
<span class="token function-variable function">permalink</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// Slug override for localized URL slugs</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>data<span class="token punctuation">.</span>slugOverride<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>data<span class="token punctuation">.</span>lang<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">slugify</span><span class="token punctuation">(</span>data<span class="token punctuation">.</span>slugOverride<span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">/</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p><strong><code>es/about.md</code>:</strong></p>
<pre class="language-md"><code class="language-md"><span class="token front-matter-block"><span class="token punctuation">---</span>
<span class="token front-matter yaml language-yaml"><span class="token key atrule">title</span><span class="token punctuation">:</span> <span class="token string">'Sobre mí'</span>
<span class="token key atrule">description</span><span class="token punctuation">:</span> <span class="token string">'Una subpágina en español'</span>
<span class="token key atrule">slugOverride</span><span class="token punctuation">:</span> sobre mi</span>
<span class="token punctuation">---</span></span>
Soy una desarrolladora y diseñadora nacida en Berlín, viviendo en Madrid.</code></pre>
<p>En nuestro caso podríamos simplemente slugificar el título, pero prefiero tratar estas cosas por separado, ya que puedo optar por ajustar el <em>string</em> del título, manteniendo intacto el permalink.</p>
<h3 id="separar-el-codigo-de-la-traduccion"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#separar-el-codigo-de-la-traduccion">Separar el código de la traducción</a></h3>
<p>Para entender cómo funciona todo esto es crucial que te sumerjas en <a href="https://www.11ty.dev/docs/data-cascade/" rel="noopener">Eleventys Data Cascade</a>.</p>
<p>Siempre queremos <mark>separar el código de la traducción</mark>. Ya separamos el código de los contenidos: nuestro código vive en archivos de diseño (en este caso sólo <code>base.njk</code>), y nuestros contenidos son creados por Markdown en archivos de plantilla. También necesitamos almacenar algunos <em>strings</em> localizados para el diseño. <em>Podrías</em> almacenarlas en el Frontmatter del archivo de diseño, pero yo prefiero tener todo en un solo lugar.</p>
<p>¡Los datos globales entran en juego!</p>
<h2 id="datos-globales"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#datos-globales">Datos globales</a></h2>
<p>En la carpeta <code>_data</code> ponemos todo lo que queremos que esté disponible globalmente en nuestro proyecto. Válidos son todos los valores <code>*.json</code> y <code>module.exports</code> de los archivos <code>*.js</code>.</p>
<p>Crea los siguientes ficheros de datos:</p>
<p><code>meta.js</code>:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// holds all our meta data</span>
module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token literal-property property">url</span><span class="token operator">:</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">URL</span> <span class="token operator">||</span> <span class="token string">'http://localhost:8080'</span><span class="token punctuation">,</span>
<span class="token literal-property property">siteName</span><span class="token operator">:</span> <span class="token string">'18n-starter'</span><span class="token punctuation">,</span>
<span class="token literal-property property">siteDescription</span><span class="token operator">:</span>
<span class="token string">"Minimal starter for localized content, using Eleventy's own Internationalization (I18n) plugin"</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p><code>languages.js</code>:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// same locale codes as in your localized subdirectories</span>
module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token literal-property property">en</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token literal-property property">dir</span><span class="token operator">:</span> <span class="token string">''</span><span class="token punctuation">,</span> <span class="token comment">// stands for the direction of the language set in the head, defaults to LTR (left to right)</span>
<span class="token literal-property property">availableText</span><span class="token operator">:</span> <span class="token string">'This page is also available in:'</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token literal-property property">de</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token literal-property property">availableText</span><span class="token operator">:</span> <span class="token string">'Diese Seite ist auch verfügbar in:'</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token literal-property property">es</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token literal-property property">availableText</span><span class="token operator">:</span> <span class="token string">'Esta página también está disponible en:'</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p><code>layout.js</code>:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// sets a global layout for all templates, can be overwritten later in the Eleventy Data Cascade</span>
module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token string">'base.njk'</span><span class="token punctuation">;</span></code></pre>
<p><code>navigation.js</code>:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// just my personal preference for creating navigation in Eleventy</span>
module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token literal-property property">en</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token literal-property property">text</span><span class="token operator">:</span> <span class="token string">'Home'</span><span class="token punctuation">,</span>
<span class="token literal-property property">url</span><span class="token operator">:</span> <span class="token string">'/en/'</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span>
<span class="token literal-property property">text</span><span class="token operator">:</span> <span class="token string">'About me'</span><span class="token punctuation">,</span>
<span class="token literal-property property">url</span><span class="token operator">:</span> <span class="token string">'/en/about-me/'</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token literal-property property">de</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token literal-property property">text</span><span class="token operator">:</span> <span class="token string">'Startseite'</span><span class="token punctuation">,</span>
<span class="token literal-property property">url</span><span class="token operator">:</span> <span class="token string">'/de/'</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span>
<span class="token literal-property property">text</span><span class="token operator">:</span> <span class="token string">'Über mich'</span><span class="token punctuation">,</span>
<span class="token literal-property property">url</span><span class="token operator">:</span> <span class="token string">'/de/ueber-mich/'</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token literal-property property">es</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token literal-property property">text</span><span class="token operator">:</span> <span class="token string">'Inicio'</span><span class="token punctuation">,</span>
<span class="token literal-property property">url</span><span class="token operator">:</span> <span class="token string">'/es/'</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span>
<span class="token literal-property property">text</span><span class="token operator">:</span> <span class="token string">'Sobre mi'</span><span class="token punctuation">,</span>
<span class="token literal-property property">url</span><span class="token operator">:</span> <span class="token string">'/es/sobre-mi/'</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>Siempre intento evitar complejidades innecesarias. Pongo todas mis strings localizados en mi carpeta de datos globales dentro de <code>languages.js</code>, y accedo a ellos usando “dot notation”.</p>
<p>Si necesito más strings localizados, los añado allí. Lo mismo ocurre con la navegación. Me gusta tener el máximo control sobre qué páginas muestro dónde, y cómo deberían llamarse en el menú.</p>
<h2 id="usando-el-plugin-de-internacionalizacion-de-eleventy"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#usando-el-plugin-de-internacionalizacion-de-eleventy">Usando el plugin de internacionalización de Eleventy</a></h2>
<p>Para activar el paquete i18n-Plugin, añádelo a tu <code>eleventy.js</code>:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token punctuation">{</span>EleventyI18nPlugin<span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'@11ty/eleventy'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">eleventyConfig</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
eleventyConfig<span class="token punctuation">.</span><span class="token function">addPlugin</span><span class="token punctuation">(</span>EleventyI18nPlugin<span class="token punctuation">,</span> <span class="token punctuation">{</span>
<span class="token literal-property property">defaultLanguage</span><span class="token operator">:</span> <span class="token string">'en'</span> <span class="token comment">// Required</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// other settings</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>Necesitamos especificar un idioma por defecto. Yo elijo el inglés, pero puede ser cualquier idioma: <code>defaultLanguage: 'en'</code></p>
<p>El plugin nos ofrece una adición a la <a href="https://www.11ty.dev/docs/data-eleventy-supplied/#page-variable" rel="noopener">variable “page”</a> (<code>page.lang</code>) así como dos nuevos <a href="https://www.11ty.dev/docs/filters/#universal-filters" rel="noopener">filtros universales</a> (<code>locale_url</code> y <code>locale_links</code>) para los “template languages” más comunes.</p>
<p>Veamos primero <code>page.lang</code>.</p>
<h3 id="uso-de-la-etiqueta-de-idioma-para-la-plantilla-de-pagina-actual"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#uso-de-la-etiqueta-de-idioma-para-la-plantilla-de-pagina-actual">Uso de la etiqueta de idioma para la plantilla de página actual</a></h3>
<p><code>page.lang</code> representa la etiqueta de idioma para la plantilla de página actual, y por defecto tendrá el valor que hayamos pasado como <code>defaultLanguage</code> en el plugin.</p>
<p>En primer lugar lo usamos para el atributo html <code>lang</code> y para acceder al valor de dirección (<code>dir</code>) que se puede establecer en <code>languages.js</code>.</p>
<p><code>base.njk</code>:</p>
<pre class="language-html"><code class="language-html"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">doctype</span> <span class="token name">html</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ page.lang }}<span class="token punctuation">"</span></span> <span class="token attr-name">dir</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ languages[page.lang].dir or 'ltr' }}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token comment"><!-- rest of the template --></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre>
<p>Toma tu nombre de directorio localizado y lo establece como el idioma del documento actual, y en caso del atributo <code>dir</code>, hace un bucle a través de <code>languages.js</code> en <code>_data</code> y obtiene la dirección correspondiente (RTL, o por defecto LTR).</p>
<p><strong>Esto es muy importante.</strong></p>
<p>El atributo <code>lang</code> se utiliza para definir el idioma de un elemento. <mark>Es utilizado por los lectores de pantalla para proporcionar el acento y la pronunciación correctos</mark>, el User Agent (tu browser) puede seleccionar las variantes correctas de glifos y comillas, separación silábica, ligaduras y espaciado.<br />
Como todo en accesibilidad, también beneficia a los motores de búsqueda.</p>
<aside>Haciendo el <a href="https://web.dev/learn/accessibility/more-html/#page-language" rel="noopener">curso Learn Accessibility</a> aprendí que es recomendable usar códigos de lenguaje ISO de dos caracteres para una mayor cobertura de <em>Assistive technology</em>, ya que muchos de ellos no soportan códigos de lenguaje extendidos.</aside>
<p>En este caso, definimos el idioma de todo el documento, pero también se puede establecer el idioma en cualquier elemento, por ejemplo <code><p lang="fr">Bonjour mon ami</p></code>, <em>dentro</em> de un documento que esté escrito en otro idioma.</p>
<p>Ya lo he hecho antes con la lista de palabras en alemán. Imagina un lector de pantalla intentando leer en voz alta esta molesta lista cuando está configurado un idioma que no es el alemán.</p>
<p>A continuación, lo utilizamos para nuestra navegación en el punto de referencia <code>header</code> y como un simple conmutador de idioma en el landmark <code>footer</code>.</p>
<p><code>header.njk</code>:</p>
<pre class="language-html"><code class="language-html">{% set activePage = page.url %}
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>header</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Primary<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span>
{% for item in navigation[page.lang] %}
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ item.url }}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>{{ item.text }}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span>
{% endfor %}
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://github.com/madrilene/eleventy-i18n<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>GitHub<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>header</span><span class="token punctuation">></span></span></code></pre>
<p>Este parcial realiza un bucle a través de nuestro archivo de navegación global, y basándose en el valor establecido por <code>page.lang</code>, muestra el menú en el idioma actual.</p>
<aside>En el proyecto final he incluido una <a href="https://github.com/madrilene/eleventy-i18n/blob/main/src/_data/helpers.js" rel="noopener">función de ayuda</a> para añadir <code>aria-current="page"</code> a la página actual.<br />
</aside>
<p><code>footer.njk</code>:</p>
<!-- prettier-ignore -->
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span>
{{ languages[page.lang].availableText }}
{% for link in page.url | locale_links %} {%-if not loop.first %}/{% endif %}
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ link.url }}<span class="token punctuation">"</span></span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ link.lang }}<span class="token punctuation">"</span></span> <span class="token attr-name">hreflang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ link.lang }}<span class="token punctuation">"</span></span>
<span class="token punctuation">></span></span>{{ link.label }}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span>
<span class="token punctuation">></span></span>
{% endfor %}
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span></code></pre>
<p>En el <code>footer</code> primero hacemos un bucle a través de <code>languages.js</code> para obtener el <em>string</em> en el idioma actual, luego usamos el primero de los nuevos filtros universales disponibles: <code>locale_links</code>.</p>
<h3 id="usando-el-filtro-locale_links"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#usando-el-filtro-locale_links">Usando el filtro 'locale_links</a></h3>
<p><code>locale_links</code> devuelve un array con el contenido alternativo de una URL especificada. La página original pasada al filtro no se incluye en los resultados.<br />
Haciendo un bucle a través de este array se obtiene un selector de idioma perfecto.</p>
<p>También puedes crear un selector de idioma con códigos de localización de dos caracteres que muestre primero el idioma actual:</p>
<!-- prettier-ignore -->
<pre class="language-html"><code class="language-html"><span class="token comment"><!-- alternative language switcher --></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>footer</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>nav</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ page.url }}<span class="token punctuation">"</span></span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ page.lang }}<span class="token punctuation">"</span></span> <span class="token attr-name">hreflang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ page.lang }}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
{{ page.lang | upper }}
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
{% for link in page.url | locale_links %}
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ link.url }}<span class="token punctuation">"</span></span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ link.lang }}<span class="token punctuation">"</span></span> <span class="token attr-name">hreflang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ link.lang }}<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
{{ link.lang | upper }}<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span>
<span class="token punctuation">></span></span>
{% endfor %}
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>nav</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>footer</span><span class="token punctuation">></span></span></code></pre>
<p>En el elemento <code>head</code> de <code>base.njk</code> hacemos saber al “user agent” que existen <mark>versiones alternativas del documento actual</mark>. El atributo <code>hreflang</code> indica en qué idioma está el recurso enlazado.</p>
<aside><a href="https://alvaromontoro.com/" rel="noopener">Álvaro Montoro</a> escribió un <a href="https://www.htmhell.dev/adventcalendar/2022/21/#meta-types" rel="noopener">excelente artículo sobre el atributo <code>rel</code></a> en el HTMHell Advent Calendar editon 2022. </aside>
<p>Ten en cuenta que cada versión lingüística debe listarse a sí misma, así como todas las demás versiones lingüísticas. <a href="https://developers.google.com/search/docs/specialty/international/localized-versions#all-method-guidelines" rel="noopener">Las URL alternativas deben ser <em>completamente cualificadas</em></a>, incluyendo el método de transporte - es decir <code>http</code> o <code>https</code>.</p>
<p>Primero añadimos el atributo “link” a la URL actual y, a continuación, hacemos un bucle sobre las versiones alternativas.</p>
<p><code>base.njk</code>:</p>
<!-- prettier-ignore -->
<pre class="language-html"><code class="language-html"><span class="token comment"><!-- stylesheet here --></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>alternate<span class="token punctuation">"</span></span> <span class="token attr-name">hreflang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ page.lang }}<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ meta.url }}{{ page.url }}<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
{% for link in page.url | locale_links %}
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span>
<span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>alternate<span class="token punctuation">"</span></span>
<span class="token attr-name">hreflang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ link.lang }}<span class="token punctuation">"</span></span>
<span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ meta.url }}{{ link.url }}<span class="token punctuation">"</span></span>
<span class="token punctuation">/></span></span>
{% endfor %}
<span class="token comment"><!-- Canonical URL here --></span>
</code></pre>
<p>El hecho de que el idioma actual sea “filtrado” del filtro es muy útil para este caso de uso. Pero ahora quiero ser muy estricto y tener en cuenta también la anotación <code>x-default hreflang</code>. La anotación <code>hreflang x-default</code> se utiliza para indicar el idioma o la región por defecto de una página cuando no se especifica explícitamente ningún otro idioma o región. No estoy completamente seguro de lo que esto significa, pero lo incluiré con la estrategia en mente, que el inglés es mi idioma por defecto. Por favor, corregidme si me equivoco.</p>
<p>Para que esto funcione necesito el enlace canónico a mi idioma por defecto, inglés, en <em>cada</em> página. Así que pasar por el filtro <code>locale_links</code> no me ayuda.</p>
<p>Lo bueno es que hay otro filtro del que aún no hemos hablado.</p>
<h3 id="usando-el-filtro-locale_url"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#usando-el-filtro-locale_url">Usando el filtro “locale_url”</a></h3>
<p>En palabras de la documentación de Eleventy, <code>locale_url</code> acepta cualquier <em>string</em> de URL arbitrario y lo transforma usando la configuración regional de la página actual. Funciona como se espera si la URL ya contiene un código de idioma. Esto es muy útil en cualquier código compartido usado por contenido internacionalizado (layouts, partials, includes, etc).</p>
<p>En nuestro proyecto inicial no lo necesitamos, pero imagínese usarlo como un parcial de diseño para una <em>call to action</em>, enviando al visitante a la página localizada del blog, por ejemplo. La sintaxis es la siguiente:</p>
<pre class="language-html"><code class="language-html"><a href="{{ "/blog/" | locale_url }}">Blog<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span></code></pre>
<p>Sin embargo, parece que sólo funciona si no cambias los permalinks, así que de momento no lo uso, al menos no la aplicación convencional.</p>
<h4 id="locale_url-para-x-default-hreflang"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#locale_url-para-x-default-hreflang">‘locale_url’ para ‘x-default hreflang’</a></h4>
<p>Si echas un <a href="https://www.11ty.dev/docs/plugins/i18n/#locale_url-filter" rel="noopener">vistazo a la documentación</a>, también verás que es posible anular la configuración regional raíz con un segundo argumento.</p>
<p>Los documentos dicen que es “improbable que lo necesites”, pero en nuestro caso es <em>exactamente</em> lo que necesitamos.</p>
<p>Bajo nuestro bucle para versiones alternativas, añadimos ahora este atributo <code>link</code>:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span>
<span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>alternate<span class="token punctuation">"</span></span>
<span class="token attr-name">hreflang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>x-default<span class="token punctuation">"</span></span>
<span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ meta.url }}{{ page.url | locale_url('en') }}<span class="token punctuation">"</span></span>
<span class="token punctuation">/></span></span></code></pre>
<p>Es muy posible que tu plugin EleventyI18n se esté quejando ahora en la consola. En el fichero de configuración de Eleventy tenemos que cambiar el errorMode de “strict” por defecto a “allow-fallback”.</p>
<pre class="language-js"><code class="language-js">eleventyConfig<span class="token punctuation">.</span><span class="token function">addPlugin</span><span class="token punctuation">(</span>EleventyI18nPlugin<span class="token punctuation">,</span> <span class="token punctuation">{</span>
<span class="token literal-property property">defaultLanguage</span><span class="token operator">:</span> <span class="token string">'en'</span><span class="token punctuation">,</span>
<span class="token literal-property property">errorMode</span><span class="token operator">:</span> <span class="token string">'allow-fallback'</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2 id="mas-ajustes-de-accesibilidad"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#mas-ajustes-de-accesibilidad">Más ajustes de accesibilidad</a></h2>
<p>Necesitamos hacer algunos ajustes importantes en términos de accesibilidad. Los puntos de referencia de navegación (<code>nav</code>) deben tener una etiqueta, y estas etiquetas deben ser traducidas también. Lo mismo ocurre con el enlace “saltar navegación”.<br />
Ambos <em>strings</em> pueden establecerse en <code>languages.js</code>.</p>
<p>Puedes encontrarlo implementado en el repositorio de GitHub para <a href="https://github.com/madrilene/eleventy-i18n/blob/main/src/_includes/header.njk" rel="noopener"><code>header.njk</code></a> y <a href="https://github.com/madrilene/eleventy-i18n/blob/main/src/_data/languages.js" rel="noopener"><code>languages.js</code></a>.</p>
<h2 id="redirigir-al-visitante-a-su-directorio-de-idioma-preferido"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#redirigir-al-visitante-a-su-directorio-de-idioma-preferido">Redirigir al visitante a su directorio de idioma preferido</a></h2>
<p>Antes de que podamos utilizar nuestro código en un sitio en vivo, tenemos que ajustar un detalle muy importante.</p>
<p>Ahora mismo, obtenemos un 404 en la raíz de la URL, ya que todavía no estamos redirigiendo al visitante a su directorio de idioma preferido.</p>
<h3 id="¿como-se-determina-el-idioma-preferido"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#%C2%BFcomo-se-determina-el-idioma-preferido">¿Cómo se determina el idioma preferido?</a></h3>
<p>Las redirecciones basadas en el idioma que vamos a configurar coinciden con el <mark>primer idioma informado</mark> por el navegador en la cabecera <code>Accept-Language</code>.</p>
<p>Esto puede ajustarse en la configuración de preferencias del navegador. Además, si no oculta su dirección IP, informa a los clientes usuarios sobre su geolocalización a nivel de país.</p>
<p>En teoría, podría segmentarme por ser un hablante de alemán residente en España y mostrar un contenido muy específico para ese caso de uso, siempre que comparta esa información con mi navegador. ¿Verdad?</p>
<p>Puedes ver qué valores están configurados en las herramientas de desarrollo mirando la pestaña de “network”. Mis diferentes navegadores muestran diferentes preferencias, pero Chrome me da esto:</p>
<pre class="language-html"><code class="language-html">accept-language: en-GB,en;q=0.9,es-ES;q=0.8,es;q=0.7,de-DE;q=0.6,de;q=0.5,en-US;q=0.4</code></pre>
<p>Como puedes ver, dirigirse a la gente basándose en estos indicadores no es una buena idea.<br />
Mi idioma preferido es el inglés (británico).</p>
<aside>El idioma preferido va seguido de la <em>sintaxis de valor de calidad</em> (<code>q=0.x</code>). <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language" rel="noopener">Más información en MDN.</a><br />
</aside>
<p>En nuestro proyecto, además de proporcionar una manera fácil para que los usuarios cambien a su idioma preferido real, queremos siempre volver por defecto a un idioma base (en caso de que el idioma preferido del los visitantes se establezca en francés, por ejemplo).</p>
<h2 id="estableciendo-reglas-de-redireccion-con-netlify"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#estableciendo-reglas-de-redireccion-con-netlify">Estableciendo reglas de redirección con Netlify</a></h2>
<p>En el ámbito de nuestro proyecto de ejemplo estamos trabajando con <a href="https://www.netlify.com/" rel="noopener">Netlify Hosting</a>.</p>
<p>Netlify provee una implementación directa para las reglas de redirección, ya sea usando el archivo <code>_redirects</code>, o el archivo de configuración de Netlify.</p>
<p>Personalmente, prefiero utilizar el archivo <code>_redirects</code> sólo para <a href="https://eleventy-excellent.netlify.app/blog/post-with-301-redirects/" rel="noopener">redirecciones internas automatizadas</a> cuando una página ha cambiado permanentemente de ubicación (algún tipo de separación de preocupaciones).</p>
<p>Dado que creo un archivo de configuración de Netlify (<code>netlify.toml</code>) de todos modos para informar cuál es mi directorio de salida y cuál es mi script de construcción, pongo mis tres redirecciones basadas en el idioma allí también. ¡También me gusta la sintaxis limpia!</p>
<p><code>netlify.toml</code>:</p>
<!-- prettier-ignore -->
<pre class="language-md"><code class="language-md"><span class="token title important"><span class="token punctuation">#</span> tell netlify about your build script and output directory</span>
[build]
command = "npm run build"
publish = "dist"
<span class="token title important"><span class="token punctuation">#</span> redirect to english, spanish or german landing pages</span>
[[redirects]]
from = "/"
to = "/de"
status = 302
force= true
conditions = {Language = ["de"]}
[[redirects]]
from = "/"
to = "/es"
status = 302
force = true
conditions = {Language = ["es"]}
[[redirects]]
from = "/"
to = "/en"
status = 302
force = true
</code></pre>
<p>Redirigimos el directorio raíz en función del idioma preferido, el que aparece en primer lugar en el <em>string</em> de cabecera <code>Accept-Language</code> (<code>conditions = {Language = ["de"]}</code>). Se trata de un array, por lo que puede establecer varios valores.</p>
<p>Establece tantas redirecciones como idiomas hayas creado. El último debe ser el idioma alternativo, es decir, el idioma que has establecido como <code>defaultLanguage</code> en el i18n-Plugin.</p>
<aside>Puedes <a href="https://docs.netlify.com/routing/redirects/" rel="noopener">leer más sobre esto en los documentos de Netlify</a>, como qué códigos de estado HTTP tienen qué efecto, o cómo crear y redirigir correctamente páginas de error 404 para todos los idiomas. También hay un <a href="https://explorers.netlify.com/learn/exploring-netlify-redirects/localization-with-conditional-redirects" rel="noopener">capítulo específico para esto dentro de la serie de vídeos Jamstack Explorers</a>.</aside>
<h2 id="notas-finales"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#notas-finales">Notas finales</a></h2>
<p>¡El proyecto está listo para ser desplegado!</p>
<p>Mi forma preferida de hacerlo es crear un <a href="https://docs.github.com/en/get-started/quickstart/create-a-repo" rel="noopener">nuevo repositorio en GitHub</a> y <a href="https://www.youtube.com/watch?v=4h8B080Mv4U" rel="noopener">desplegarlo en Netlify</a>.</p>
<p>Todavía faltan muchas cosas. Por ejemplo, podrías necesitar colecciones localizadas y un filtro de fecha localizado para tu blog.</p>
<p>He querido mantener este starter lo más simple y directo posible. Puedes refinar y automatizar muchos procesos, ya sea la navegación, los permalinks, o las subpáginas de los respectivos idiomas, que pueden ser asignados entre sí con claves en el frontmatter, en lugar de utilizar el mismo nombre de carpeta.</p>
<p>Lamentablemente, el CSS no tiene cabida en este artículo. Por todos los medios, por favor reemplace la hoja de estilos CDN con la suya propia, sólo sirve para dar una apariencia razonablemente agradable sin tener que establecer clases.</p>
<h3 id="mencion-honorifica-css"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#mencion-honorifica-css">Mención honorífica CSS</a></h3>
<p>La pseudo-clase <code>:lang</code> empareja elementos basándose en el idioma en el que se determina que están. Una buena forma de dar un toque creativo a tus idiomas.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">nav:lang(de)</span> <span class="token punctuation">{</span>
<span class="token property">background-color</span><span class="token punctuation">:</span> darkseagreen<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p><em>Logical properties</em> proporcionan soporte automático para la internacionalización y te ayudan a construir frontends estables e inclusivos. Recomiendo leer <a href="https://web.dev/learn/css/logical-properties/" rel="noopener">el capítulo sobre el tema en el curso Aprende CSS en web.dev</a>.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.element</span> <span class="token punctuation">{</span>
<span class="token property">padding-block-start</span><span class="token punctuation">:</span> 2em<span class="token punctuation">;</span>
<span class="token property">padding-block-end</span><span class="token punctuation">:</span> 2em<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h3 id="bifurcar-el-repositorio"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#bifurcar-el-repositorio">Bifurcar el repositorio</a></h3>
<p>Puedes <a href="https://github.com/madrilene/eleventy-i18n" rel="noopener">bifurcar el repo</a> que creé junto con el artículo. He añadido algunas características extra, como un script de ayuda para indicar la página actual a los lectores de pantalla, redirecciones a páginas 404 localizadas y strings de datos globales para mejorar la accesibilidad (etiquetas aria traducidas, skip-link…). Para evitar el bloqueo de renderizado, he inlineado el CSS minificado después de eliminar los estilos que no se utilizan.</p>
<p>Me encantaría que se añadieran más idiomas. Aquí tienes una <a href="https://docs.github.com/en/get-started/quickstart/contributing-to-projects" rel="noopener">explicación de cómo contribuir a los proyectos</a>. Añade una nueva carpeta con tu localización en <code>src</code>, y crea los archivos de plantilla correspondientes en tu idioma. Añade el array de navegación a <code>navigation.js</code>, los <em>strings</em> localizados a <code>languages.js</code> y añade las redirecciones a <code>netlify.toml</code>.</p>
<p>Por último, una pequeña disculpa: Sólo puedo expresarme en términos ingleses y nunca he leído un libro de referencia en español sobre tecnologías web. Utilicé muchas frases extrañas en pseudoespañol o ni siquiera intenté traducir algunos términos.</p>
<h3 id="algunos-enlaces-relacionados-con-el-tema"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#algunos-enlaces-relacionados-con-el-tema">Algunos enlaces relacionados con el tema</a></h3>
<ul class="list">
<li><a href="https://youtube.com/watch?v=sfPNgt3joWI&t=1122" rel="noopener">YouTube: Internationalization (i18n), Eleventy 🎈 Weekly №16</a></li>
<li><a href="https://www.smashingmagazine.com/2020/11/internationalization-localization-static-sites/" rel="noopener">Smashing Magazine: I18n And l10n For Static Sites</a></li>
<li><a href="https://benmyers.dev/blog/eleventy-data-cascade/" rel="noopener">benmyers.dev: I Finally Understand Eleventy’s Data Cascade</a></li>
<li><a href="https://www.hoeser.dev/blog/2022-07-08-i18n-urls/" rel="noopener">hoeser.dev: I18n of URLs</a></li>
<li><a href="https://www.youtube.com/watch?v=QwOoU8T24UY" rel="noopener">YouTube: How a screen reader sounds without “lang” attribute defined</a></li>
<li><a href="https://www.w3.org/TR/REC-html40/struct/dirlang.html" rel="noopener">w3.org: Language information and text direction</a></li>
</ul>
<h2 id="thejamdev-2023-lightning-talk"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/internacionalizacion-con-eleventy-20-y-netlify/#thejamdev-2023-lightning-talk">TheJam.dev 2023 Lightning Talk</a></h2>
<div class="youtube-embed"> <lite-youtube videoid="CZqpcoGpYfU" style="background-image: url('https://i.ytimg.com/vi/CZqpcoGpYfU/hqdefault.jpg');">
<button type="button" class="lty-playbtn">
<span class="lyt-visually-hidden">Hassle-free internationalization with Eleventy 2.0 and Netlify</span>
</button>
</lite-youtube></div>
Lightning talk con TheJam.dev 2023
2023-01-10T00:00:00Z
https://www.lenesaile.com/es/blog/lightning-talk-con-thejamdev-2023/
<p>El día 25 de enero participaré en <a href="https://cfe.dev/events/the-jam-2023/" rel="noopener">TheJam.dev 2023</a>. Cuando veo a los <a href="https://cfe.dev/events/the-jam-2023/#speakers" rel="noopener">otros ponentes en la alineación</a> puedo sentir claramente el síndrome del impostor dando patadas… 😬</p>
<p>En fin, estoy preparando algo sobre internacionalización con Eleventy, que empaquetaré en un <em>Lightning Talk</em> de diez minutos, y además publicaré un artículo en profundidad en el blog. ¡Estoy súper contenta con la oportunidad que me ha brindado <a href="https://cfe.dev/speakers/brian-rinaldi/" rel="noopener">Brian Rinaldi</a>!</p>
Página web para Stephen
2022-12-05T00:00:00Z
https://www.lenesaile.com/es/blog/pagina-web-para-stephen/
<p>Basado en el workflow “be the browser’s mentor” de Andy Bell para <a href="http://buildexcellentwebsit.es/" rel="noopener">buildexcellentwebsit.es</a>, recientemente publiqué un <a href="https://github.com/madrilene/eleventy-excellent" rel="noopener">starter para Eleventy</a>. Stephen Anfield, estudiante de posgrado de la Escuela de Trabajo Social de la Universidad de Michigan, se encontró con él y me preguntó si podía personalizar la plantilla para él. Así surgió su nuevo sitio web personal en <a href="http://stephenanfield.com/" rel="noopener">stephenanfield.com</a>.</p>
<p>¡Pocas veces he disfrutado tanto de un proyecto! Qué maravilloso es cuando alguien aprecia genuinamente el valor y la importancia de <em>progressive enhancement</em>, el rendimiento y la accesibilidad. ¡Gracias Stephen! 😃</p>
Estructuración del archivo de configuración de Eleventy
2022-11-29T00:00:00Z
https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/
<p><a href="https://www.11ty.dev/" rel="noopener">Eleventy</a> viene con algunos valores básicos por defecto. Por ejemplo, la carpeta de salida por defecto es <code>_site</code>, y Eleventy busca tus archivos fuente en el directorio raíz.</p>
<p>Esto está bien para proyectos muy pequeños. Un archivo de configuración adicional no es necesario para trabajar con Eleventy. Sin embargo, soy un gran fan de la estructura, la organización y la claridad, y la mayoría de mis proyectos son bastante grandes. También tengo preferencias personales, y Eleventy es bastante abierto al respecto: puedes organizarlo y llamarlo como quieras.</p>
<p>Empecemos!</p>
<p><a href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#toc-skipped" id="skip-toc" class="visually-hidden">Saltar el índice de contenidos</a></p>
<h2 id="table-of-contents">índice de contenidos</h2>
<nav id="toc" class="table-of-contents"><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#crear-un-archivo-de-configuracion-eleventyjs">Crear un archivo de configuración .eleventy.js</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#estructurar-la-carpeta-de-entrada">Estructurar la carpeta de entrada</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#externalizacion-de-las-personalizaciones">Externalización de las personalizaciónes</a><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#metodo-1-importar-el-archivo-y-hacer-un-bucle-sobre-los-nombres-de-las-collections">Método 1: Importar el archivo y hacer un bucle sobre los nombres de las collections</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#metodo-2-named-exports">Método 2: named exports</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#metodo-3-mas-archivos-de-configuracion-como-plugin">Método 3: Más archivos de configuración como plugin</a></li></ol></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#estructurando-passthrough-file-copies">Estructurando Passthrough File Copies</a><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#mantener-intacta-la-estructura-de-directorios">Mantener intacta la estructura de directorios</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#copiar-los-archivos-a-otro-directorio">Copiar los archivos a otro directorio</a><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#mas-trucos-de-copia-de-archivos-passthrough">Más trucos de copia de archivos Passthrough</a></li></ol></li></ol></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#wrap-up">Wrap up</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#eleventy-meetup">Eleventy Meetup</a></li></ol></nav><div id="toc-skipped"></div>
<h2 id="crear-un-archivo-de-configuracion-eleventyjs"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#crear-un-archivo-de-configuracion-eleventyjs">Crear un archivo de configuración .eleventy.js</a></h2>
<p>Añade un nuevo archivo en el directorio raíz llamado <code>.eleventy.js</code> (<a href="https://www.11ty.dev/docs/config/" rel="noopener">a partir de Eleventy 2.0 también puede llamarse <code>eleventy.config.js.</code></a>).</p>
<p>Hagamos primero un pequeño ajuste en la estructura de carpetas.</p>
<pre class="language-js"><code class="language-js">module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">eleventyConfig</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token punctuation">{</span>
<span class="token literal-property property">dir</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token literal-property property">input</span><span class="token operator">:</span> <span class="token string">'src'</span><span class="token punctuation">,</span>
<span class="token literal-property property">output</span><span class="token operator">:</span> <span class="token string">'dist'</span><span class="token punctuation">,</span>
<span class="token literal-property property">includes</span><span class="token operator">:</span> <span class="token string">'_includes'</span><span class="token punctuation">,</span>
<span class="token literal-property property">layouts</span><span class="token operator">:</span> <span class="token string">'_layouts'</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>Nuestra carpeta de salida es ahora <code>dist</code>, y todos nuestros archivos fuente van a la carpeta <code>src</code>.<br />
También, debido a mi preferencia personal, saco la carpeta <code>layouts</code> fuera de la carpeta <code>_includes</code>, donde normalmente vive y me aseguro de que estén uno al lado del otro.</p>
<p>Esto deja nuestra raíz para todos los archivos que absolutamente tienen que estar allí - como <code>package.json</code> y <code>README.md</code>, así como los archivos de configuración de otros módulos que utilizas en tu proyecto.</p>
<h2 id="estructurar-la-carpeta-de-entrada"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#estructurar-la-carpeta-de-entrada">Estructurar la carpeta de entrada</a></h2>
<p>Crea una carpeta llamada <code>src</code> en tu directorio raíz.<br />
Aunque no vamos a tocar la mayoría de las carpetas, este es el aspecto que podría tener tu proyecto web habitual:</p>
<!-- prettier-ignore -->
<pre class="language-md"><code class="language-md">│
├── src
│ │
│ ├── _data
│ ├── _includes
│ ├── _layouts
│ ├── assets
│ ├── pages
│ ├── posts
│ ├── projects
│</code></pre>
<p><code>pages</code> es para tus páginas estáticas como <code>index.md</code>, <code>about.md</code>, etc., <code>posts</code> contiene los artículos de tu blog, y <code>projects</code> es sólo otra carpeta de colección que creamos para que valga la pena sacar la lógica de <code>eleventy.js</code>.</p>
<p>… Porque <em>puedes</em> configurar todas tus <em>collections</em>, <em>shortcodes</em> y <em>filters</em> ahí mismo.</p>
<aside>Si aún no lo has hecho, deberías dirigirte a la <a href="https://www.11ty.dev/docs/config/" rel="noopener">documentación de Eleventy</a> para familiarizarte con todas las opciones de configuración disponibles.</aside>
<h2 id="externalizacion-de-las-personalizaciones"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#externalizacion-de-las-personalizaciones">Externalización de las personalizaciónes</a></h2>
<p>Quiero que mis proyectos crezcan libremente sin preocuparme de que mi archivo de configuración se vuelva demasiado abarrotado. Así que me ocupo de las personalizaciones en otro lugar e importo sólo el valor de retorno de mis funciones.</p>
<p>Mi preferencia es crear una nueva carpeta en el directorio raíz, llamada <code>config</code>.</p>
<p>Otro método habitual es añadir una carpeta a <code>src</code> con el nombre de <code>_11ty</code>. Encontré esto en el <em>starter</em> de <a href="https://nicolas-hoizey.com/" rel="noopener">Nicolas Hoizeys</a> <a href="https://github.com/nhoizey/pack11ty/tree/master/src" rel="noopener">pack11ty</a>. Puedes nombrar la carpeta como quieras y ponerla donde quieras.<br />
En este caso, seguiré fingiendo que has creado una carpeta llamada <code>config</code> en tu directorio raíz.</p>
<p>No necesitamos avisar a Eleventy sobre la existencia de esta carpeta. Simplemente la usamos para exportar nuestros valores de retorno e importarlos a <code>.eleventy.js</code>.</p>
<p>Introduzco dos buenas maneras de manejar esto, usando <a href="https://www.11ty.dev/docs/collections/" rel="noopener">collections</a> como ejemplo.</p>
<h3 id="metodo-1-importar-el-archivo-y-hacer-un-bucle-sobre-los-nombres-de-las-collections"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#metodo-1-importar-el-archivo-y-hacer-un-bucle-sobre-los-nombres-de-las-collections">Método 1: Importar el archivo y hacer un bucle sobre los nombres de las <em>collections</em></a></h3>
<p>Crea un archivo llamado <code>collections.js</code> en tu carpeta <code>config</code>.<br />
Ahora define todas las <em>collections</em> que quieras usar:</p>
<pre class="language-js"><code class="language-js">module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token function-variable function">posts</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">collectionApi</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> collectionApi<span class="token punctuation">.</span><span class="token function">getFilteredByGlob</span><span class="token punctuation">(</span><span class="token string">'src/posts/**/*.md'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token function-variable function">projects</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">collectionApi</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> collectionApi<span class="token punctuation">.</span><span class="token function">getFilteredByGlob</span><span class="token punctuation">(</span><span class="token string">'src/projects/**/*.md'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<aside><code>/**/*</code> coincide con cualquier número de directorios entre <code>/src/posts/</code> y tus archivos <code>.md</code>. De esta manera podemos asegurarnos de que Eleventy encuentra todos los archivos markdown por muy anidados que estén, y podemos seguir ordenando los contenidos por año, luego por mes, etc. <strong>Organize all the things!</strong><br />
</aside>
<p>Tu <code>eleventy.js</code> ahora se ve así:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> collections <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./config/collections.js'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token parameter">eleventyConfig</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token comment">// Collections</span>
Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>collections<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">collectionName</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
eleventyConfig<span class="token punctuation">.</span><span class="token function">addCollection</span><span class="token punctuation">(</span>collectionName<span class="token punctuation">,</span> collections<span class="token punctuation">[</span>collectionName<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token punctuation">{</span>
<span class="token literal-property property">dir</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token literal-property property">input</span><span class="token operator">:</span> <span class="token string">'src'</span><span class="token punctuation">,</span>
<span class="token literal-property property">output</span><span class="token operator">:</span> <span class="token string">'dist'</span><span class="token punctuation">,</span>
<span class="token literal-property property">includes</span><span class="token operator">:</span> <span class="token string">'_includes'</span><span class="token punctuation">,</span>
<span class="token literal-property property">layouts</span><span class="token operator">:</span> <span class="token string">'_layouts'</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>Hacemos un “loop” sobre todas las <em>collections</em> definidas en <code>collections.js</code> y las importamos a nuestro archivo de configuración. Ahora harías exactamente lo mismo para tus <em>collections</em>, <em>shortcodes</em>, <em>filters</em>, etc.</p>
<p>Si quieres ver este método en acción, visita el <a href="https://github.com/hexagoncircle/ryan-mulligan-dev/blob/main/.eleventy.js" rel="noopener">repositorio público</a> del <a href="https://ryanmulligan.dev/" rel="noopener">sitio personal de Ryan Mulligan</a>.</p>
<p><strong>¡Muy ordenado!</strong></p>
<p>Este método tiene la ventaja de producir un archivo de configuración realmente compacto. Sin embargo, hay algo que no me gusta.</p>
<p>Hemos traído estructura en ella, pero también quiero ver lo que se está utilizando en mi proyecto, allí mismo, en mi archivo de configuración.</p>
<p>Quiero ver qué <em>collections</em> estoy definiendo, qué <em>filters</em>, etc. ¡Así que aquí viene el método dos!</p>
<h3 id="metodo-2-named-exports"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#metodo-2-named-exports">Método 2: named exports</a></h3>
<p>En lugar de <code>collections.js</code> crea otra carpeta dentro de <code>config</code> llamada <code>collections</code>, y en ella pon un archivo llamado <code>index.js</code>:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// blog posts</span>
<span class="token keyword">const</span> <span class="token function-variable function">getPosts</span> <span class="token operator">=</span> <span class="token parameter">collection</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> collection<span class="token punctuation">.</span><span class="token function">getFilteredByGlob</span><span class="token punctuation">(</span><span class="token string">'src/posts/**/*.md'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token comment">// projects</span>
<span class="token keyword">const</span> <span class="token function-variable function">getProjects</span> <span class="token operator">=</span> <span class="token parameter">collection</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> collection<span class="token punctuation">.</span><span class="token function">getFilteredByGlob</span><span class="token punctuation">(</span><span class="token string">'src/projects/**/*.md'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
getPosts<span class="token punctuation">,</span>
getProjects
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>Las exportaciones nombradas pueden hacerse individualmente o agruparse en la parte inferior. Si se exporta todo al final del módulo, como en el ejemplo de aquí, queda mucho más claro, por lo que naturalmente prefiero este método.</p>
<p>Y dentro de tu <code>eleventy.js</code>:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token punctuation">{</span>getPosts<span class="token punctuation">,</span> getProjects<span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./config/collections/index.js'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token parameter">eleventyConfig</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token comment">// Collections</span>
eleventyConfig<span class="token punctuation">.</span><span class="token function">addCollection</span><span class="token punctuation">(</span><span class="token string">'posts'</span><span class="token punctuation">,</span> getPosts<span class="token punctuation">)</span><span class="token punctuation">;</span>
eleventyConfig<span class="token punctuation">.</span><span class="token function">addCollection</span><span class="token punctuation">(</span><span class="token string">'projects'</span><span class="token punctuation">,</span> getProjects<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token punctuation">{</span>
<span class="token literal-property property">dir</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token literal-property property">input</span><span class="token operator">:</span> <span class="token string">'src'</span><span class="token punctuation">,</span>
<span class="token literal-property property">output</span><span class="token operator">:</span> <span class="token string">'dist'</span><span class="token punctuation">,</span>
<span class="token literal-property property">includes</span><span class="token operator">:</span> <span class="token string">'_includes'</span><span class="token punctuation">,</span>
<span class="token literal-property property">layouts</span><span class="token operator">:</span> <span class="token string">'_layouts'</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p><strong>¡Hecho!</strong></p>
<p>Todo está ordenado y puedo ver de un vistazo lo que estoy importando para este proyecto.</p>
<p>Si hay demasiados <em>filtros</em>, <em>colecciones</em> o <em>códigos cortos</em>, los divido más en sus propias carpetas, por ejemplo sólo los filtros para manejar la fecha en un lugar común. Los bloques más grandes, como los shortcodes de <a href="https://www.11ty.dev/docs/plugins/image/" rel="noopener">Eleventy Image</a>, tienen su propia carpeta.<br />
Los _values_ exportados se importan primero en el archivo padre <code>index.js</code> y luego se vuelven a exportar juntos para el archivo <code>eleventy.js</code>. 🤪</p>
<h3 id="metodo-3-mas-archivos-de-configuracion-como-plugin"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#metodo-3-mas-archivos-de-configuracion-como-plugin">Método 3: Más archivos de configuración como plugin</a></h3>
<p>Después de compartir este artículo en Mastodon, <a href="https://front-end.social/@eleventy@fosstodon.org/109501433721579265" rel="noopener">Zach me indicó</a> que hay aún otra forma de externalizar mis componentes de configuración:</p>
<pre class="language-js"><code class="language-js">eleventyConfig<span class="token punctuation">.</span><span class="token function">addPlugin</span><span class="token punctuation">(</span><span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'other-config-file.js'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>No sólo es la notación más compacta, ya que no tengo que importar primero mis <em>return values</em>, sino que tampoco tengo que modificar ningún código: Los archivos de configuración externalizados funcionan igual que el propio <code>elventy.js</code>, devolviendo una función <em>callback</em>. Y puedo ver lo que estoy importando!</p>
<p>Ilustro esto con el ejemplo de una minificación html usando el <code>addTransform</code> incorporado de Eleventy.</p>
<p>En tu <code>eleventy.js</code>:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">// ¡nada que importar! :)</span>
module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token parameter">eleventyConfig</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token comment">// Transforms</span>
eleventyConfig<span class="token punctuation">.</span><span class="token function">addPlugin</span><span class="token punctuation">(</span><span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./config/transforms/html-config.js'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token punctuation">{</span>
<span class="token literal-property property">dir</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token literal-property property">input</span><span class="token operator">:</span> <span class="token string">'src'</span><span class="token punctuation">,</span>
<span class="token literal-property property">output</span><span class="token operator">:</span> <span class="token string">'dist'</span><span class="token punctuation">,</span>
<span class="token literal-property property">includes</span><span class="token operator">:</span> <span class="token string">'_includes'</span><span class="token punctuation">,</span>
<span class="token literal-property property">layouts</span><span class="token operator">:</span> <span class="token string">'_layouts'</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>Tu <code>html-config.js</code>:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> htmlmin <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'html-minifier-terser'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> isProduction <span class="token operator">=</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">ELEVENTY_ENV</span> <span class="token operator">===</span> <span class="token string">'production'</span><span class="token punctuation">;</span>
module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token parameter">eleventyConfig</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
eleventyConfig<span class="token punctuation">.</span><span class="token function">addTransform</span><span class="token punctuation">(</span><span class="token string">'html-minify'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">content<span class="token punctuation">,</span> path</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>path <span class="token operator">&&</span> path<span class="token punctuation">.</span><span class="token function">endsWith</span><span class="token punctuation">(</span><span class="token string">'.html'</span><span class="token punctuation">)</span> <span class="token operator">&&</span> isProduction<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> htmlmin<span class="token punctuation">.</span><span class="token function">minify</span><span class="token punctuation">(</span>content<span class="token punctuation">,</span> <span class="token punctuation">{</span>
<span class="token literal-property property">collapseBooleanAttributes</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
<span class="token literal-property property">collapseWhitespace</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
<span class="token literal-property property">decodeEntities</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
<span class="token literal-property property">includeAutoGeneratedTags</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
<span class="token literal-property property">removeComments</span><span class="token operator">:</span> <span class="token boolean">true</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> content<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p><strong>¡Excelente!</strong></p>
<p>A continuación: <a href="https://www.11ty.dev/docs/copy/" rel="noopener">Passthrough File Copy</a>.</p>
<h2 id="estructurando-passthrough-file-copies"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#estructurando-passthrough-file-copies">Estructurando Passthrough File Copies</a></h2>
<p>A veces sólo queremos copiar archivos a nuestra carpeta de salida, sin someterlos a más procesos de transformación. Exactamente como están. Así es como entran en juego los “Passthrough File Copies”.</p>
<h3 id="mantener-intacta-la-estructura-de-directorios"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#mantener-intacta-la-estructura-de-directorios">Mantener intacta la estructura de directorios</a></h3>
<p>Supongamos que has almacenado tus fuentes locales en <code>src/assets/fonts</code>.</p>
<p>Si deseas mantener la misma estructura de anidamiento, añade lo siguiente a <code>eleventy,js</code> (he eliminado el código del ejemplo de los métodos de ayuda para mayor claridad):</p>
<pre class="language-js"><code class="language-js">module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token parameter">eleventyConfig</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token comment">// Passthrough Copy</span>
eleventyConfig<span class="token punctuation">.</span><span class="token function">addPassthroughCopy</span><span class="token punctuation">(</span><span class="token string">'src/assets/fonts/'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token punctuation">{</span>
<span class="token literal-property property">dir</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token literal-property property">input</span><span class="token operator">:</span> <span class="token string">'src'</span><span class="token punctuation">,</span>
<span class="token literal-property property">output</span><span class="token operator">:</span> <span class="token string">'dist'</span><span class="token punctuation">,</span>
<span class="token literal-property property">includes</span><span class="token operator">:</span> <span class="token string">'_includes'</span><span class="token punctuation">,</span>
<span class="token literal-property property">layouts</span><span class="token operator">:</span> <span class="token string">'_layouts'</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
<p>Ahora tus fuentes se copiarán con la misma estructura de directorios, en <code>dist/assets/fonts/</code>.</p>
<p>Normalmente tengo más carpetas en <code>assets</code> que deberían ser copiadas. ¡Hay una manera concisa para esto también!</p>
<pre class="language-js"><code class="language-js"><span class="token punctuation">[</span><span class="token string">'src/assets/fonts/'</span><span class="token punctuation">,</span> <span class="token string">'src/assets/images/'</span><span class="token punctuation">,</span> <span class="token string">'src/assets/pdf/'</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">path</span> <span class="token operator">=></span>
eleventyConfig<span class="token punctuation">.</span><span class="token function">addPassthroughCopy</span><span class="token punctuation">(</span>path<span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Colocamos todos los directorios en un <em>array</em> y aplicamos el método <code>forEach()</code> para ejecutar el passthrough una vez por cada elemento del <em>array</em>.</p>
<h3 id="copiar-los-archivos-a-otro-directorio"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#copiar-los-archivos-a-otro-directorio">Copiar los archivos a otro directorio</a></h3>
<p>A veces quieres copiar tus archivos a <em>otro</em> directorio. Para mí, esto tiene sentido especialmente para mis variantes de favicon. <em>Puedes</em> decirle al navegador que los busque dentro de una carpeta, pero mi experiencia ha sido que es mejor ponerlos en el directorio raíz de la página web. Sin embargo, no quiero verlos en mi carpeta de entrada (¡demasiado ruido!), así que suelo ponerlos todos en <code>src/assets/images/favicon/</code>.</p>
<p>Para copiar un solo archivo al directorio raíz de <code>dist</code>, escribe:</p>
<pre class="language-js"><code class="language-js">eleventyConfig<span class="token punctuation">.</span><span class="token function">addPassthroughCopy</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token string-property property">'src/assets/images/favicon/apple-touch-icon.png'</span><span class="token operator">:</span> <span class="token string">'apple-touch-icon.png'</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Podrías hacer esto para cada archivo favicon, pero sería una repetición innecesaria. En su lugar, puedes seleccionar todos los archivos del directorio favicon con el * (asterisco) wildcard:</p>
<pre class="language-js"><code class="language-js">eleventyConfig<span class="token punctuation">.</span><span class="token function">addPassthroughCopy</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token string-property property">'src/assets/images/favicon/*'</span><span class="token operator">:</span> <span class="token string">'/'</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Por cierto, respecto a los favicons, recomiendo leer <a href="https://evilmartians.com/chronicles/how-to-favicon-in-2021-six-files-that-fit-most-needs" rel="noopener">este artículo de Andrey Sitnik</a>.</p>
<h4 id="mas-trucos-de-copia-de-archivos-passthrough"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#mas-trucos-de-copia-de-archivos-passthrough">Más trucos de copia de archivos Passthrough</a></h4>
<p>Aquí puedes ser realmente creativo.<br />
Puedes dirigirte a cualquier imagen JPG en cualquier dirección, y enviarlas todas juntas a una carpeta de salida común:</p>
<pre class="language-js"><code class="language-js">eleventyConfig<span class="token punctuation">.</span><span class="token function">addPassthroughCopy</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token string-property property">'**/*.jpg'</span><span class="token operator">:</span> <span class="token string">'img'</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Puedes combinar tus assets en un array, manteniendo la estructura de directorios:</p>
<pre class="language-js"><code class="language-js"><span class="token punctuation">[</span><span class="token string">'src/activos/imágenes/'</span><span class="token punctuation">,</span> <span class="token string">'src/activos/fonts/'</span><span class="token punctuation">,</span> <span class="token string">'src/activos/pdf/'</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">ruta</span> <span class="token operator">=></span>
eleventyConfig<span class="token punctuation">.</span><span class="token function">addPassthroughCopy</span><span class="token punctuation">(</span>path<span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>O aplicar este interesante filtrado que descubrí en el <a href="https://github.com/rknightuk/rknight.me/blob/master/.eleventy.js" rel="noopener">código fuente</a> del <a href="https://rknight.me/" rel="noopener">sitio web personal de Robb Knight</a>, donde todo lo que hay dentro de <code>src/assets</code> y <code>src/files</code> se copia en el directorio de salida, <em>excepto</em> los archivos CSS y los archivos que empiezan por guión bajo.</p>
<pre class="language-js"><code class="language-js"><span class="token punctuation">[</span><span class="token string">'src/assets'</span><span class="token punctuation">,</span> <span class="token string">'src/files'</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">path</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
eleventyConfig<span class="token punctuation">.</span><span class="token function">addPassthroughCopy</span><span class="token punctuation">(</span>path<span class="token punctuation">,</span> <span class="token punctuation">{</span>
<span class="token function-variable function">filter</span><span class="token operator">:</span> <span class="token parameter">path</span> <span class="token operator">=></span> <span class="token operator">!</span>path<span class="token punctuation">.</span><span class="token function">endsWith</span><span class="token punctuation">(</span><span class="token string">'.css'</span><span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token operator">!</span>path<span class="token punctuation">.</span><span class="token function">startsWith</span><span class="token punctuation">(</span><span class="token string">'_'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h2 id="wrap-up"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#wrap-up">Wrap up</a></h2>
<p>Así es como actualmente estoy estructurando mis proyectos. Puedes ver estos métodos aplicados en mi starter <a href="https://github.com/madrilene/eleventy-excellent/blob/main/.eleventy.js" rel="noopener">eleventy-excellent</a>. En el repositorio de la página web personal de Miriam Suzanne se puede encontrar un magnífico ejemplo de un <a href="https://github.com/mirisuzanne/mia/blob/main/.eleventy.js" rel="noopener">Eleventy config perfectamente ordenado</a>.</p>
<p>Siempre merece la pena echar un vistazo al <a href="https://github.com/11ty/eleventy-base-blog/blob/main/eleventy.config.js" rel="noopener">arranque oficial de Eleventy</a>, porque allí se pueden encontrar ideas de vanguardia.</p>
<p>En general, siempre es una gran idea bucear en los repositorios de los <em><a href="https://www.11ty.dev/docs/starter/" rel="noopener">starters</a></em> o en los sitios personales de otras desarrolladoras.</p>
<p>¡Hay tantas ideas geniales por ahí!</p>
<h2 id="eleventy-meetup"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/estructuracion-del-archivo-de-configuracion-de-eleventy/#eleventy-meetup">Eleventy Meetup</a></h2>
<p>Me invitaron como ponente al Eleventy Meetup Ep. 12 el 16 de marzo de 2023 y di una breve charla basada en este artículo.</p>
<div class="youtube-embed"> <lite-youtube videoid="nlaN-mifrWk" style="background-image: url('https://i.ytimg.com/vi/nlaN-mifrWk/hqdefault.jpg');">
<button type="button" class="lty-playbtn">
<span class="lyt-visually-hidden">How to keep your Eleventy config file organized</span>
</button>
</lite-youtube></div>
<pre class="language-plaintext"><code class="language-plaintext"></code></pre>
Mi primera página web
2022-11-19T00:00:00Z
https://www.lenesaile.com/es/blog/mi-primera-pagina-web/
<p>Hoy ha ocurrido algo curioso. La primera página web que hice como autónoma, a finales de 2008, me ha vuelto a atrapar. Hacía 14 años que no la veía ni sabía nada de ella, y ahora ha vuelto.</p>
<p>La razón por la que volvió a mi vida fue, por supuesto, que apareció un error. Una actualización “forzada” a PHP 8.1 en el servidor hizo que el sitio web fallara, así que pensaron en mí, la creadora, para arreglarlo.</p>
<p>La imagen es familiar para todos los desarrolladores de WordPress:</p>
<figure role="group"><picture><source type="image/avif" srcset="https://www.lenesaile.com/assets/images/ruefetto-php-error-500w.avif 500w, https://www.lenesaile.com/assets/images/ruefetto-php-error-900w.avif 900w" sizes="100vw" /><source type="image/webp" srcset="https://www.lenesaile.com/assets/images/ruefetto-php-error-500w.webp 500w, https://www.lenesaile.com/assets/images/ruefetto-php-error-900w.webp 900w" sizes="100vw" /><source type="image/jpeg" srcset="https://www.lenesaile.com/assets/images/ruefetto-php-error-500w.jpeg 500w, https://www.lenesaile.com/assets/images/ruefetto-php-error-900w.jpeg 900w" sizes="100vw" /><img src="https://www.lenesaile.com/assets/images/image-placeholder.png" data-src="/assets/images/ruefetto-php-error-500w.jpeg" class="innerimage" width="500" height="688" alt="Captura de pantalla de muchas líneas de errores PHP causados por una incompatibilidad con PHP 8.1" loading="lazy" decoding="async" /></picture><figcaption>Hello darkness my old friend.</figcaption></figure>
<p>Debajo de lo que parecía un metro de mensajes de error de PHP, apareció entonces la página web, tal y como lo había dejado hace más de una década.</p>
<h2 id="como-nacio-la-pagina-web"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/mi-primera-pagina-web/#como-nacio-la-pagina-web">Cómo nació la página web</a></h2>
<p>Corría el año 2008 y yo acababa de registrar mi negocio como autónoma ante las autoridades alemanas en la ciudad sureña de Friburgo de Brisgovia (quizá os resulte familiar por ser la ciudad de origen de <a href="https://www.smashingmagazine.com/" rel="noopener">Smashing Magazine</a>). Una noche estaba visitando una bodega de jazz y entablé conversación con un músico. Resulta que estaban buscando un logotipo, un folleto y una página web para sus sesiones regulares de jazz. Yo, por supuesto, me ofrecí inmediatamente. Y así fue como conseguí mi primer trabajo, si no recuerdo mal, pagado con 400 euros por todo.</p>
<p>Se convirtió en una página web con WordPress, porque tenía que ser mantenido regularmente con fotos y noticias. Estábamos entre WordPress 2.5 y 2.7 y tenía muy poco en común con lo que se conoce hoy en día. Los menús de WordPress, por ejemplo, se introdujeron dos años después con la versión 3.0.</p>
<h2 id="un-tema-sencillo"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/mi-primera-pagina-web/#un-tema-sencillo">Un tema sencillo</a></h2>
<p>Por aquel entonces, todo se hacía con “hacks”. WordPress era una plataforma de blogging pura, y si querías hacer algún tipo de CMS con él, tenías que hacer un montón de trucos.</p>
<p>No había mucha semántica en aquel entonces, pero todos esos divs eran realmente un gran paso adelante con respecto a las tablas que todavía eran omnipresentes en aquel entonces.</p>
<p>En aquella época había construido el menú principal así:</p>
<pre class="language-php"><code class="language-php"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>menu<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span><span class="token punctuation">></span></span>
<span class="token php language-php"><span class="token delimiter important"><?php</span>
<span class="token variable">$homeActive</span> <span class="token operator">=</span> <span class="token constant boolean">true</span><span class="token punctuation">;</span>
<span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token variable">$_GET</span> <span class="token keyword">as</span> <span class="token variable">$key</span> <span class="token operator">=></span> <span class="token variable">$value</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$key</span> <span class="token operator">!=</span> <span class="token string double-quoted-string">""</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token variable">$homeActive</span> <span class="token operator">=</span> <span class="token constant boolean">false</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token delimiter important">?></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>page_item<span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">is_home</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string double-quoted-string">" current_page_item"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span> <span class="token delimiter important">?></span></span><span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token keyword">echo</span> <span class="token function">get_option</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'home'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token delimiter important">?></span></span><span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Home<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>subitemmenu0<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Home<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span>
<span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token function">wp_list_pages</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'title_li=&depth=1&include=51,53,18,289'</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token delimiter important">?></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>No sé realmente lo que estaba haciendo. ¿Qué es lo que intenta conseguir esa variable <code>$homeActive</code> ahí arriba? La función de WordPress <code>is_home()</code> existe desde la versión 1.5.0, y en ese caso obviamente quería que añadiera la clase <code>current_page_item</code> a “Home” si estaba activa, para poder mostrar un indicador visual de dónde estamos. ¡Eso no funcionó! Seguramente perdí algunas horas tratando de averiguar por qué, hasta que finalmente me rendí.</p>
<aside>Hay algunas peculiaridades contraintuitivas en WordPress que, al igual que en JavaScript, no se pueden arreglar fácilmente en futuras versiones, ya que esto rompería incontables páginas web. La función <code>is_home()</code> no se refiere incondicionalmente a tu página de inicio, sino que devuelve <code>true</code> si la configuración de lectura en el backend de WordPress está configurada como “blog posts” en lugar de una página estática.</aside>
<p>Luego codifiqué los enlaces a las páginas con los IDs 51, 53, 18 y 289 directamente en el template.<br />
¡Terminado es el menú de WordPress hecho en 2008!</p>
<pre class="language-html"><code class="language-html"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">html</span> <span class="token name">PUBLIC</span> <span class="token string">"-//W3C//DTD XHTML 1.0 Transitional//EN"</span> <span class="token string">"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"</span><span class="token punctuation">></span></span></code></pre>
<p>¡Mira eso! Ya nadie entiende eso. Tampoco nadie lo entendía entonces.</p>
<p>Encontré algunos elementos codificados más, por ejemplo en el pie de página. Obviamente no sabía cómo mostrar esta información de otra manera. ¿Ya teníamos widgets?</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>footer<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>footerInnerPadding<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
Ruefetto Jazz Sessions <span class="token entity named-entity" title=" ">&emsp;</span>| <span class="token entity named-entity" title=" ">&emsp;</span> Kart<span class="token entity named-entity" title="ä">&auml;</span>userstr. 2 | Granatg<span class="token entity named-entity" title="ä">&auml;</span><span class="token entity named-entity" title="ß">&szlig;</span>le 3
| 79102 Freiburg | Jeden Donnerstag ab 21 Uhr <span class="token entity named-entity" title=" ">&emsp;</span>|<span class="token entity named-entity" title=" ">&emsp;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.ruefettojazzsessions.de/impressum/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Impressum<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>¡Incluso escribí explícitamente las entidades HTML 4 para las diéresis alemanas!</p>
<p>En general, escribí un tema muy, muy simple. Se arregla con sólo unas pocas líneas de CSS, la mitad de las cuales ni siquiera se necesitan, y algunas de las cuales copié despistadamente de algún página web. No es que lo recuerde, pero no hablo sueco.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">#sidebar .bloggy-meddelande</span> <span class="token punctuation">{</span>
<span class="token comment">/* Stilen på själva inlägget */</span>
<span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span>
<span class="token property">border</span><span class="token punctuation">:</span> 1px solid #e1e1e1<span class="token punctuation">;</span>
<span class="token property">background-color</span><span class="token punctuation">:</span> #f8f8f8<span class="token punctuation">;</span>
<span class="token property">padding</span><span class="token punctuation">:</span> 2px<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Este también es bonito:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">#menu a,
#menu a:link,
#menu a:active,
#menu a:visited,
#menu a:focus,
#menu a:hover</span> <span class="token punctuation">{</span>
<span class="token property">text-decoration</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span>
<span class="token property">border</span><span class="token punctuation">:</span> 0px<span class="token punctuation">;</span>
<span class="token property">block-size</span><span class="token punctuation">:</span> 30px<span class="token punctuation">;</span>
<span class="token property">line-height</span><span class="token punctuation">:</span> 30px<span class="token punctuation">;</span>
<span class="token property">padding</span><span class="token punctuation">:</span> 15px 15px 15px 15px<span class="token punctuation">;</span>
<span class="token comment">/*_padding:0px 15px 0px 15px; there are some things IE doesn't understand about padding */</span>
<span class="token property">white-space</span><span class="token punctuation">:</span> nowrap<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>😱…<br />
¡pero mira ese comentario! 😂</p>
<p>¿JavaScript? No existe. No sabía cómo escribirlo en ese momento, y realmente no era necesario en absoluto.</p>
<h2 id="no-necesitabamos-ningun-plugin"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/mi-primera-pagina-web/#no-necesitabamos-ningun-plugin">No necesitábamos ningún plugin.</a></h2>
<p>Lo mejor: dos plugins. <strong>¡Dos!</strong></p>
<p>Tres, si se cuenta el plugin <em>Hello Dolly</em> que vino con WordPress durante mucho tiempo. Luego había Akismet y TinyMCE Advanced, que por lo visto se usaba mucho hasta hace poco para cambiar el color por defecto del contenido con <code>#ff0004</code> (¡seguro para la web!).</p>
<h2 id="¿que-habia-pasado-todos-esos-anos"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/mi-primera-pagina-web/#%C2%BFque-habia-pasado-todos-esos-anos">¿Qué había pasado todos esos años?</a></h2>
<p>Recuerdo que al principio actualicé WordPress gratis unas cuantas veces. Era agotador, porque el Hosting requería una entrada manual de credenciales FTP en el backend de WordPress, para todas las actualizaciones. Luego, en algún momento, quise cobrar una pequeña tarifa plana mensual por ello, y eso no funcionó, así que nosotros, el sitio web y yo, tomamos caminos distintos. El mío estuvo marcado por el desarrollo personal, golpes de suerte y de destino, el de la página web: absolutamente impasible.</p>
<p>El administrador de la web (¡que por supuesto seguía siendo “admin”!) utilizaba la página de inicio y la de la galería como sustituto del blog y aparentemente no echaba nada en falta.</p>
<p>Llegó la gran ola del “Responsive Web Design”, y mientras la gran mayoría de los sitios se fueron haciendo con los “media querys”, a mi página no la importó. Vino muy bien que la web fuera tan jodidamente estrecha.</p>
<figure role="group"><picture><source type="image/avif" srcset="https://www.lenesaile.com/assets/images/ruefetto-narrow-500w.avif 500w, https://www.lenesaile.com/assets/images/ruefetto-narrow-900w.avif 900w, https://www.lenesaile.com/assets/images/ruefetto-narrow-1280w.avif 1280w" sizes="100vw" /><source type="image/webp" srcset="https://www.lenesaile.com/assets/images/ruefetto-narrow-500w.webp 500w, https://www.lenesaile.com/assets/images/ruefetto-narrow-900w.webp 900w, https://www.lenesaile.com/assets/images/ruefetto-narrow-1280w.webp 1280w" sizes="100vw" /><source type="image/jpeg" srcset="https://www.lenesaile.com/assets/images/ruefetto-narrow-500w.jpeg 500w, https://www.lenesaile.com/assets/images/ruefetto-narrow-900w.jpeg 900w, https://www.lenesaile.com/assets/images/ruefetto-narrow-1280w.jpeg 1280w" sizes="100vw" /><img src="https://www.lenesaile.com/assets/images/image-placeholder.png" data-src="/assets/images/ruefetto-narrow-500w.jpeg" class="innerimage" width="500" height="146" alt="Captura de pantalla del sitio web en mi pantalla. Sólo ocupa un 30% del espacio" loading="lazy" decoding="async" /></picture><figcaption>¡Es bastante moderna! Incluso mantuve el contenido legible a un máximo de 45 a 75 caracteres por línea.</figcaption></figure>
<p>Hacer páginas web con 600 a 800 píxeles de ancho no era tan inusual por el año 2008. Podría jurar que llenaba bastante bien mi monitor por aquel entonces.</p>
<p>Así que aquí está de nuevo, 14 años de trabajo sin errores después. Me olvidé de comprobar la versión exacta de WordPress que se ejecutaba allí antes de borrarlo, pero seguro que estaba terriblemente anticuado. Esto, y TinyMCE Advanced, es lo que finalmente hizo caer el sitio cuando se activó PHP 8.1.<br />
No puedo imaginar que nadie haya actualizado este sitio web desde 2008. ¿Es esto posible? ¿Estaban ejecutando una versión de PHP compatible todo ese tiempo?</p>
<p>Ahora sólo queda un plugin: Akismet. Supongo que es todo gracias a la naturaleza simple de mi programación en ese entonces y la ausencia de plugins que había dejado que pudiera llegar tan lejos.</p>
<p>He puesto una nueva instalación de WordPress, he sustituido la declaración docytpe antígua por <code><!DOCTYPE></code> y he eliminado esa extraña lógica <code>$homeActive</code>, ya que no parecía hacer nada más que confundir. He cambiado <code>is_home()</code> por <code>is_front_page()</code> para que el indicador de página actual para “home” funcione realmente. Luego he quitado algo del CSS que sobraba y lo hize enfocable por el teclado de nuevo.<br />
Todo lo demás está como siempre. No te lo vas a creer, pero los web vitals son geniales.</p>
<p>Os invito a visitarla, pero, “disclaimer”: Su Hosting te hace pagar por certificados SSL con al menos 2,99 euros al mes. Así que, no hay SSL.</p>
<p>👉👉👉 <a href="http://www.ruefettojazzsessions.de/" rel="noopener">www.ruefettojazzsessions.de</a></p>
<p>Cuántos pueden decir que su primera web “profesional” sigue ahí, en todo su dudoso esplendor? Fue un bonito encuentro desde luego.</p>
Mis redes sociales a finales de 2022
2022-11-07T00:00:00Z
https://www.lenesaile.com/es/blog/mis-redes-sociales-a-finales-de-2022/
<p>Twitter está un poco raro ahora mismo. Una pena, porque era la única plataforma social que utilizaba activamente. <a href="https://front-end.social/@lene" rel="noopener">¡Ahora estoy en Mastodon</a>! Me interesa principalmente una timeline rica sobre temas de desarrollo web, así que elegí el servidor front-end.social. En Linkedin todavía tengo un perfil, pero ya no lo enlazo en mi página. No uso la plataforma y, para ser sincera, no la entiendo.</p>
Mención de mi página web
2022-11-01T00:00:00Z
https://www.lenesaile.com/es/blog/mencion-de-mi-pagina-web/
<p>Descubrí la <a href="https://ulf.codes/2022-10-28-outfit/" rel="noopener">mención de mi página web</a> del desarrollador Ulf Schneider. ¡Muchas gracias!</p>
<p>A Ulf le gustó especialmente la tipografía que utilizo aquí. Encontré a ‘Outfit’ a través del boletín de Oliver Schöndorfer <a href="https://pimpmytype.com/category/fontfriday/" rel="noopener">Font Friday</a>.</p>
Algunas notas personales sobre WordPress en 2022
2022-10-26T00:00:00Z
https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/
<p>En 2007 buscaba un CMS que tuviera sentido para mí. Tras probar <a href="https://www.joomla.org/" rel="noopener">Joomla</a> y <a href="https://typo3.org/" rel="noopener">TYPO3</a> (y odiarlo), descubrí <a href="https://wordpress.org/" rel="noopener">WordPress</a>. Fue una revelación.</p>
<p>Sigo pensando que es una gran solución para páginas web complejas. Es extremadamente potente y personalizable, y no me siento demasiado limitada como desarrolladora para crear cualquier cosa que tenga en mente. Sin embargo, algunos desprecian el CMS, diciendo que es inseguro e hinchado. Me gustaría compartir brevemente mis pensamientos personales sobre el estado actual de WordPress, y por qué todavía me gusta usarlo.</p>
<p><a href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#toc-skipped" id="skip-toc" class="visually-hidden">Saltar el índice de contenidos</a></p>
<h2 id="table-of-contents">índice de contenidos</h2>
<nav id="toc" class="table-of-contents"><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#lo-que-me-gusta-de-wordpress">Lo que me gusta de WordPress</a><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#wordpress-es-gratuito-seguro-y-rapido">WordPress es gratuito, seguro y rápido</a></li></ol></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#%C2%BFque-es-lo-que-no-me-gusta">¿Qué es lo que no me gusta?</a><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#tienes-que-hacerlo-a-su-manera">Tienes que hacerlo a su manera</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#page-builders-y-temas-hinchados">Page builders y temas hinchados</a></li></ol></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#como-estoy-trabajando-con-wordpress">Cómo estoy trabajando con WordPress</a><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#plugins-que-suelo-instalar">Plugins que suelo instalar</a></li></ol></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#lo-que-pienso-sobre-full-site-editing">Lo que pienso sobre Full Site Editing</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#%C2%BFque-pasa-con-gutenberg">¿Qué pasa con Gutenberg?</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#entonces-%C2%BFcual-es-la-tecnica-correcta-para-construir-un-tema-hoy-en-dia">Entonces, ¿cuál es la técnica correcta para construir un tema hoy en día?</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#conclusion">Conclusión</a><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#%C2%BFque-es-wordpress-para-mi">¿Qué es WordPress para mí?</a></li></ol></li></ol></nav><div id="toc-skipped"></div>
<h2 id="lo-que-me-gusta-de-wordpress"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#lo-que-me-gusta-de-wordpress">Lo que me gusta de WordPress</a></h2>
<h3 id="wordpress-es-gratuito-seguro-y-rapido"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#wordpress-es-gratuito-seguro-y-rapido">WordPress es gratuito, seguro y rápido</a></h3>
<p>WordPress es de uso completamente gratuito y, si no la lías, es seguro y rápido. El otro día leí que <mark>El núcleo de WordPress es responsable de sólo el 0,6% de las vulnerabilidades, el otro 99,4% son hechas por plugins y temas</mark>. <sup class="footnote-ref"><a href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#fn1" id="fnref1">[1]</a></sup></p>
<p>Las páginas web hechas con WordPress pueden ser hackeados. Eso es un hecho. Pero, también pueden ser hackeados páginas que usaon otros sístemas. Podemos decir igualmente que páginas web pueden ser hackeados. Lo que ocurre es que muchísimas páginas web utilizan WordPress. Los hackers conocen el sistema y las posibles vulnerabilidades. Y estas vulnerabilidades las solemos crear nosotros, y no el núcleo de WordPress.</p>
<p>Lo que podemos hacer eficientemente contra los intentos de hackeo es una entrada de blog por sí misma, pero aquí va uno de los consejos más importantes: busca una buena plataforma de alojamiento. ¡Vale la pena gastar un poco más de dinero aquí! Un buen hosting también tiene un buen equipo de servicio que te ayudará rápidamente si hay un problema. Todo lo demás está, como he dicho, fuera del alcance de lo que quiero escribir aquí. Pero tienes que saber: Si lo haces bien, es muy difícil que los hackers lleguen a tus datos o exploten tu sitio web.</p>
<p>En relación a la velocidad, recomiendo este vídeo un poco enfadado realizado por Alex Young. Lo dice todo.</p>
<div class="youtube-embed"> <lite-youtube videoid="rhWhBi7W14A" style="background-image: url('https://i.ytimg.com/vi/rhWhBi7W14A/hqdefault.jpg');">
<button type="button" class="lty-playbtn">
<span class="lyt-visually-hidden">How To Make A SLOW WordPress Site</span>
</button>
</lite-youtube></div>
<p>Además, es fácil hacer que una página web de WordPress sea descubrible, tiene posibilidades de eCommerce, y gracias al plugin “Advanced Custom fields” campos de contenido flexibles.</p>
<p>La comunidad es enorme, y todavía no me he encontrado con un problema que no estuviera ya resuelto antes o con el que no me ayudaran al instante.</p>
<p>Por último, un punto muy importante: WordPress existe desde hace mucho tiempo, muchos de mis clientes ya han trabajado con el backend de WordPress y están familiarizados con él.</p>
<h2 id="¿que-es-lo-que-no-me-gusta"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#%C2%BFque-es-lo-que-no-me-gusta">¿Qué es lo que no me gusta?</a></h2>
<p>Las siguientes reflexiones se refieren al trabajo con WordPress en el back-end y el front-end, es decir, no a una solución como CMS sin cabeza.</p>
<h3 id="tienes-que-hacerlo-a-su-manera"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#tienes-que-hacerlo-a-su-manera">Tienes que hacerlo a su manera</a></h3>
<p>Con WordPress, tienes que hacerlo a su manera. No es como Jamstack, donde todo depende de ti, añadiendo servicios, utilizando lenguajes de programación y métodos a tu gusto. Con WordPress, estás en un ecosistema cerrado, el “sistema monolítico” que les gusta mencionar a los defensores de Jamstack. Me gusta la definición no técnica: “formado por un gran bloque de piedra”. 😂</p>
<p>En términos de WordPress, esto significa que sirve como una solución “única” para el front-end y el back-end de un sitio web. Esta arquitectura monolítica limita las posibilidades de construir una página web y nos restringe a las opciones que soporta WordPress.</p>
<p>En lugar de añadir elementos de forma selectiva, a veces hay que excluir activamente lo que no se necesita. Y tengo la sensación, que los nuevos <a href="https://developer.wordpress.org/block-editor/how-to-guides/themes/block-theme-overview/" rel="noopener">Temas de bloque</a> van por este camino incluso más que los temas clásicos de PHP. Hay que trabajar con las clases CSS que se generan, y también tengo que desactivar los scripts como <code>wp-block-navigation-view</code> (responsable del comportamiento de los menús) si quiero implementar mi propia solución (sin cargar scripts adicionales innecesarios. Cada kilobyte cuenta). Más adelante hablo con más detalle de mis primeras impresiones con este nuevo modelo.</p>
<h3 id="page-builders-y-temas-hinchados"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#page-builders-y-temas-hinchados">Page builders y temas hinchados</a></h3>
<p>Estoy segura de que los page builders (Elementor, Visual Composer…) tienen mucho sentido en algunos contextos y, sobre todo, permiten a muchos entrar en el mundo del desarrollo web por su cuenta, siguiendo el principio de “no code”. Sin embargo, como desarrolladora con un enfoque particular en el rendimiento, no uso page builders.</p>
<p>Todo lo que añade un peso innecesario a una página web es algo que intento evitar. Eso me lleva a la siguiente cosa con la que nunca me he hecho amigo: Los temas que quieren servir a todos los casos de uso posibles.</p>
<p>He hablado con muchas personas que compraron un tema y luego pasaron incontables horas tratando de configurar todo.</p>
<p>No siempre, pero a menudo, es un desastre. No porque no lo hayan hecho bien. Sino porque algunos temas tratan de resolver todos los problemas, de servir a todos los sectores. He visto una combinación de tema/plugins que cargaba más de 100 archivos de script diferentes. No he contado las hojas de estilo. La página tardó más de 10 segundos en cargarse en mi conexión de internet de fibra. Por supuesto, se puede sacar mucho más rendimiento a estas páginas utilizando un CDN, optimizando los imagenes, activando la caché y demás, pero es mucho mejor tener un tema personalizado que sólo incluya lo que realmente necesitas.</p>
<p>Aquí, por supuesto, hay que ser justos: ni los page builders de terceros ni los temas de terceros son responsabilidad de WordPress.</p>
<h2 id="como-estoy-trabajando-con-wordpress"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#como-estoy-trabajando-con-wordpress">Cómo estoy trabajando con WordPress</a></h2>
<p>Transformar sitios de WordPress a Jamstack o usar WordPress como un CMS sin cabeza es en lo que más me he hecho últimamente.</p>
<p>Al trabajar de forma nativa con WordPress, me estoy asegurando de que sea lo más rápido posible mediante el uso de JavaSCript sólo cuando sea necesario - se necesita JavaScript por ejemplo para garantizar la accesibilidad. Además, estoy trabajando con un “build pipeline” para utilizar sólo archivos optimizados en la estructura final del tema.</p>
<p><mark>Trato de hacer todo lo más fácil y obvio posible.</mark> Dejo pistas e instrucciones en las secciones editables y, dependiendo del alcance del proyecto, también creo videos de introducción donde explico cada detalle importante de la página.</p>
<p>Valoro la estructura y el orden. Quiero ser capaz de entender mis propios temas en el futuro, pero también quiero que los desarrolladores posteriores entiendan el código que escribí.</p>
<h3 id="plugins-que-suelo-instalar"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#plugins-que-suelo-instalar">Plugins que suelo instalar</a></h3>
<p><a href="https://www.advancedcustomfields.com/pro/" rel="noopener">ACF Pro</a> está en cada página web que construyo. También <a href="https://yoast.com/wordpress/plugins/seo/" rel="noopener">Yoast SEO</a>, <a href="https://wp-rocket.me/es/" rel="noopener">WP Rocket Pro</a> y <a href="https://www.siteground.com/blog/sg-security/" rel="noopener">Siteground Security</a>.</p>
<p>Si convierto WordPress en un headless CMS es <a href="https://wordpress.org/plugins/acf-to-rest-api/" rel="noopener">ACF a REST API</a>, o <a href="https://www.wpgraphql.com/" rel="noopener">WP GraphQL</a> en combinación con WPGraphQL para Advanced Custom Fields.</p>
<p>Uso <a href="https://es.wordpress.org/plugins/query-monitor/" rel="noopener">Query Monitor</a> para visualizar los hooks, depurar y mejorar el rendimiento.</p>
<h2 id="lo-que-pienso-sobre-full-site-editing"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#lo-que-pienso-sobre-full-site-editing">Lo que pienso sobre Full Site Editing</a></h2>
<p>En pocas palabras, FSE es la extensión del <a href="https://wordpress.org/gutenberg/" rel="noopener">Editor Gutenberg</a> a todo la página web. Según este principio, todo el tema es personalizable directamente en el backend utilizando bloques. Es básicamente un constructor de sitios incorporado.</p>
<p>No puedo decir demasiado sobre esto, ya que todavía tengo que sumergirme realmente en el tema. Teniendo en cuenta que se basa en JavaScript y tiene un concepto similar al de los componentes, puede que al final me guste desarrollar con él. WordPress no volverá a las andadas y cada nueva versión reforzará los temas de bloque.</p>
<p>Un “template part” para el <code>header.html</code> se ve así:</p>
<pre class="language-html"><code class="language-html"><span class="token comment"><!-- wp:group {"className":"inner splitter wrapper headarea","layout":{"type":"flex","flexWrap":"nowrap"}} --></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>wp-block-group inner splitter wrapper headarea<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token comment"><!-- wp:group {"layout":{"type":"flex","flexWrap":"nowrap"}} --></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>wp-block-group<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token comment"><!-- wp:site-logo /--></span>
<span class="token comment"><!-- wp:site-title {"level":0} /--></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token comment"><!-- /wp:group --></span>
<span class="token comment"><!-- wp:navigation {"ref":52,"overlayMenu":"never","style":{"spacing":{"blockGap":"0rem"}}} /--></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token comment"><!-- /wp:group --></span></code></pre>
<p>Sin duda es algo a lo que hay que acostumbrarse. Es como escribir código a base de comentarios.</p>
<p>Como he dicho, todavía no me he acostumbrado. Mi primera impresión es que me limita un poco como desarrolladora.</p>
<p>Sin embargo, me he propuesto aprenderlo, aunque sólo sea para entenderlo de verdad, y no para criticarlo sin justificación. El hecho de que no quiera trabajar profesionalmente con temas de bloque en este momento refleja simplemente mi nivel actual de conocimientos: no puedo en conciencia ofrecer servicios en un área que no controlo completamente.</p>
<h2 id="¿que-pasa-con-gutenberg"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#%C2%BFque-pasa-con-gutenberg">¿Qué pasa con Gutenberg?</a></h2>
<p>Creo que el editor Gutenberg es genial para los editores de contenido con experiencia, y para todos aquellos que quieran invertir algo de tiempo en aprenderlo. Puedes hacer un montón de diseños diferentes y crear entradas realmente interesantes (aunque no te excedas copiando cosas de la <a href="https://wordpress.org/patterns/" rel="noopener">biblioteca de patrones de WordPress</a>, ya que esto añade peso a la página).</p>
<p>Por otro lado, algunos clientes tienen mucho miedo de romper cosas. Se puede hacer muchísimo con Gutenberg - y eso puede ser un poco abrumador. Algunos usuarios de WordPress sólo quieren el viejo editor de “Microsoft Word” de vuelta. Y tengo que decir que puedo entenderlo (por cierto, también hay maravillosos CMS en el mundo de Jamstack que tienen una experiencia de edición muy simple - estaré encantada de contarte más sobre ellos).</p>
<h2 id="entonces-¿cual-es-la-tecnica-correcta-para-construir-un-tema-hoy-en-dia"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#entonces-%C2%BFcual-es-la-tecnica-correcta-para-construir-un-tema-hoy-en-dia">Entonces, ¿cuál es la técnica correcta para construir un tema hoy en día?</a></h2>
<p>Parece que las técnicas antiguas y las nuevas chocan entre sí. Hay muchas maneras de construir con WordPress: Temas de bloque, la clásica plantilla impulsada por PHP o WordPress como CMS sin cabeza alimentando un front-end que elegimos a través de su REST API o WPGraphQL.</p>
<p>Yo no me preocuparía demasiado por eso: <mark>Lo que sea adecuado para el proyecto.</mark> A ningún usuario le importa con qué se construyó el sitio web, y a la mayoría de los clientes tampoco les importa. Por supuesto, comunico exactamente lo que recomiendo y por qué creo que es la mejor solución para el cliente según mi experiencia.<br />
Pero al final, lo que realmente importa es la calidad del producto.</p>
<h2 id="conclusion"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#conclusion">Conclusión</a></h2>
<p>Cuando descubrí WordPress, me ofreció una idea de lo que iba a significar la “experiencia del desarrollador” en el futuro. Hoy en día, más del 40% de todos las páginas web dependen de WordPress<sup class="footnote-ref"><a href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#fn2" id="fnref2">[2]</a></sup>.</p>
<p>Es enorme y tiene mucha responsabilidad. No puede hacer todo de manera diferente y mejor en la próxima versión, al igual que los meta-frameworks modernos como <a href="https://astro.build/" rel="noopener">Astro</a>. Y como WordPress tiene que moverse tan lentamente, una pequeña fracción apenas está llegando al mundo de JavaScript y React que conocemos (y rechazamos 😛).</p>
<h3 id="¿que-es-wordpress-para-mi"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#%C2%BFque-es-wordpress-para-mi">¿Qué es WordPress para mí?</a></h3>
<p>Para mí, WordPress es una cosa por encima de todo: Un CMS gratuito y muy flexible que muchos de mis clientes ya conocen. Me interesa menos el hecho de que un tema pueda ser sustituido en cualquier momento por otro.</p>
<p>Desarrollo un sitio web especialmente para las necesidades del cliente. Debe ser rápido, accesible y seguro, e idealmente durar unos cuantos años si se mantiene bien.</p>
<p>Esta página web a medida puede o no utilizar WordPress como CMS. Eso depende del proyecto y es algo que mis clientes y yo decidimos juntos.</p>
<hr class="footnotes-sep" />
<section class="footnotes">
<ol class="footnotes-list">
<li id="fn1" class="footnote-item"><p><a href="https://patchstack.com/whitepaper/the-state-of-wordpress-security-in-2021/" rel="noopener">https://patchstack.com/whitepaper/the-state-of-wordpress-security-in-2021/</a> <a href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#fnref1" class="footnote-backref">↩︎</a></p>
</li>
<li id="fn2" class="footnote-item"><p><a href="https://w3techs.com/technologies/details/cm-wordpress" rel="noopener">https://w3techs.com/technologies/details/cm-wordpress</a> <a href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#fnref2" class="footnote-backref">↩︎</a></p>
</li>
</ol>
</section>
Mi perfil de desarrolladora
2022-05-11T00:00:00Z
https://www.lenesaile.com/es/blog/mi-perfil-de-desarrolladora/
<p>He creado una página de Eleventy muy pequeña donde profundizo en mis conocimientos técnicos e intereses. La necesitaba para un par de trabajos y me di cuenta de que es útil tenerla, en caso de que me pregunten espontáneamente sobre mis habilidades y tendencias.</p>
<p>También he añadido algunos consejos sobre libros y cursos. Ahora sólo tengo que asegurarme de mantenerlo al día con todo lo que estoy aprendiendo todo el tiempo.<br />
Puedes encontrarla página aquí: <a href="https://www.lene.dev/" rel="noopener">www.lene.dev</a></p>
Nuevo shop para albertoballesteros.com: Eleventy y Snipcart
2022-02-15T00:00:00Z
https://www.lenesaile.com/es/blog/nuevo-shop-para-albertoballesteroscom-eleventy-y-snipcart/
<p>Una web rápida y segura - ¡y con una apariencia que lo refleja! - es crucial para un shop. <a href="http://albertoballesteros.com/" rel="noopener">albertoballesteros.com</a> es una página web estática basado en el método de Jamstack. Si se hace todo bien, las páginas de Jamstack son intrínsecamente muy seguras, fiables, flexibles y, sobre todo, rápidas.</p>
<p><a href="https://www.lenesaile.com/es/blog/nuevo-shop-para-albertoballesteroscom-eleventy-y-snipcart/#toc-skipped" id="skip-toc" class="visually-hidden">Saltar el índice de contenidos</a></p>
<h2 id="table-of-contents">índice de contenidos</h2>
<nav id="toc" class="table-of-contents"><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/nuevo-shop-para-albertoballesteroscom-eleventy-y-snipcart/#%C2%BFcomo-se-puede-integrar-una-tienda-en-una-pagina-web-de-jamstack">¿Cómo se puede integrar una tienda en una página web de Jamstack?</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/nuevo-shop-para-albertoballesteroscom-eleventy-y-snipcart/#integracion-tecnica-de-snipcart-con-eleventy">Integración técnica de Snipcart con Eleventy</a><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/nuevo-shop-para-albertoballesteroscom-eleventy-y-snipcart/#para-mas-seguridad-http-response-header">Para más seguridad: HTTP response header</a></li></ol></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/nuevo-shop-para-albertoballesteroscom-eleventy-y-snipcart/#%C2%A1venta-lograda!">¡Venta lograda!</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/nuevo-shop-para-albertoballesteroscom-eleventy-y-snipcart/#conclusion-y-recomendaciones">Conclusión y recomendaciones</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/nuevo-shop-para-albertoballesteroscom-eleventy-y-snipcart/#%C2%BFcual-es-la-mejor-solucion-para-una-tienda-online">¿Cuál es la mejor solución para una tienda online?</a></li></ol></nav><div id="toc-skipped"></div>
<h2 id="¿como-se-puede-integrar-una-tienda-en-una-pagina-web-de-jamstack"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/nuevo-shop-para-albertoballesteroscom-eleventy-y-snipcart/#%C2%BFcomo-se-puede-integrar-una-tienda-en-una-pagina-web-de-jamstack">¿Cómo se puede integrar una tienda en una página web de Jamstack?</a></h2>
<p>Una de las muchas ventajas del método Jamstack es el amplio abanico de aplicaciones y plataformas que se pueden implementar. Dependiendo de lo que necesite un proyecto, puedo elegir un proveedor que ofrezca exactamente eso.</p>
<p>En el caso de una tienda online, puedo elegir entre muchos proveedores que permiten a los usuarios tener una experiencia de compra moderna, personal y rápida.</p>
<p>Uno de los proveedores más populares es <a href="https://www.shopify.com/" rel="noopener">Shopify</a>. Se paga una pequeña cuota mensual por utilizarlo. A cambio, recibimos un sistema de tienda seguro y fácil de usar con conexiones sencillas a los proveedores de pago habituales, herramientas de marketing y capacidad de ampliación modular. El área de administración donde podemos introducir los datos de la empresa, añadir productos y procesar los pedidos es algo que todos los proveedores similares tienen en común.</p>
<p>Para <a href="http://albertoballesteros.com/" rel="noopener">albertoballesteros.com</a>, Shopify es demasiado potente. Tenemos un número manejable de productos, y la tienda es más bien un complemento del sitio web.</p>
<p>Hasta ahora, hemos mantenido una conexión directa con Stripe para una función de shop. <a href="https://stripe.com/es/payments/checkout" rel="noopener">Stripe Checkout</a> es un método seguro e inmediato para completar compras puntuales o suscripciones, por ejemplo. Si podemos suponer que nuestra visitante sólo comprará un producto (por ejemplo, porque no hay más productos disponibles o porque elige una subscripción específica de pago), Stripe Checkout es ideal. Stripe se queda con alrededor de un 3% por compra completada y ofrece a cambio una variedad de monedas, soporte para facturas, seguridad y encriptación de datos, etc., similar a Shopify.</p>
<p>Por nuestra parte, sin embargo, vamos a ampliar la gama de productos un poco y es posible que un comprador quiera comprar más de un artículo a la vez. Y así entra en juego un tercer proveedor: <a href="https://snipcart.com/" rel="noopener">Snipcart</a>.</p>
<aside><strong>Nota:</strong> Hemos vuelto a la solución de Stripe unos meses después de publicar este texto. No porque Snipcart no sea bueno, al contrario. Es una gran solución, especialmente para las tiendas con ventas mensuales fiables. Pero si sólo vendes un producto de vez en cuando, <a href="https://snipcart.com/faq#Pricing" rel="noopener">no merece la pena</a>: Si vendes más de 500 dólares al mes, Snipcart cuesta un 2% de comisión. Si vendes menos de 500 dólares al mes, Snipcart cobra una tarifa fija de 10 dólares al mes. Esto también se aplica si no vendes nada durante un mes. También describiré la solución con Stripe pronto en el blog.</aside>
<h2 id="integracion-tecnica-de-snipcart-con-eleventy"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/nuevo-shop-para-albertoballesteroscom-eleventy-y-snipcart/#integracion-tecnica-de-snipcart-con-eleventy">Integración técnica de Snipcart con Eleventy</a></h2>
<p>Snipcart es una solución de comercio electrónico que nos permite añadir un carrito de la compra a una página web y convertirlo en una tienda. Snipcart ofrece un carrito de la compra totalmente personalizable, webhooks y APIs, y un panel de administración intuitivo.</p>
<figure role="group"><picture><source type="image/avif" srcset="https://www.lenesaile.com/assets/images/snipcart-dashboard-500w.avif 500w, https://www.lenesaile.com/assets/images/snipcart-dashboard-900w.avif 900w" sizes="100vw" /><source type="image/webp" srcset="https://www.lenesaile.com/assets/images/snipcart-dashboard-500w.webp 500w, https://www.lenesaile.com/assets/images/snipcart-dashboard-900w.webp 900w" sizes="100vw" /><source type="image/jpeg" srcset="https://www.lenesaile.com/assets/images/snipcart-dashboard-500w.jpeg 500w, https://www.lenesaile.com/assets/images/snipcart-dashboard-900w.jpeg 900w" sizes="100vw" /><img src="https://www.lenesaile.com/assets/images/image-placeholder.png" data-src="/assets/images/snipcart-dashboard-500w.jpeg" class="innerimage" width="500" height="317" alt="Pantallazo del panel de administración de Snipcart" loading="lazy" decoding="async" /></picture><figcaption>Snipcart tiene un panel de administración atractivo y claro. Entre otras cosas, puedo encontrar información útil sobre el fenómeno de las cestas de la compra abandonadas.</figcaption></figure>
<p>Para Snipcart no importa la plataforma en la que esté construido el sitio web. Sin embargo, funciona especialmente bien con una página web de Jamstack. <a href="https://www.albertoballesteros.com/shop" rel="noopener">albertoballesteros.com</a> es una página Jamstack, construida con el generador de webs estáticas <a href="https://www.11ty.dev/" rel="noopener">Eleventy</a>.</p>
<p>Lo mejor es que la configuración de los productos se hace directamente en el código de la web.<br />
Para ello, primero tenemos que incluir un archivo de Javascript y otro de CSS.</p>
<p>En mi carpeta del proyecto tengo la siguiente estructura (simplificada):</p>
<pre class="language-md"><code class="language-md">│
└───src
│ │
│ └───_data
│ │ │ shop.json
│ │ │ ...
│ │
│ └───_includes
│ │ │
│ │ └───layouts
│ │ │ base.njk
│ │ │ ...
│ │
│ │ shop.njk
│ │ ...
│ ...</code></pre>
<p>Para evitar que el Javascript y el CSS sea cargado innecesariamente por otras páginas, especifico en mi plantilla base que sólo se incluirá en la página de la tienda:</p>
<p><em>En base.njk:</em></p>
<pre class="language-html"><code class="language-html">{% if snipcart %}
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span>
<span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span>
<span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://cdn.snipcart.com/themes/v3.3.1/default/snipcart.css<span class="token punctuation">"</span></span>
<span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">async</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://cdn.snipcart.com/themes/v3.3.1/default/snipcart.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">hidden</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>snipcart<span class="token punctuation">"</span></span> <span class="token attr-name">data-api-key</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>YOUR_PUBLIC_API_KEY<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
{% endif %}</code></pre>
<p>La tienda no necesita sub-páginas de productos, sino que lista los artículos directamente con una breve descripción.</p>
<p>Por lo tanto, sólo activo la integración en mi archivo <code>Nunjucks</code> para la tienda:</p>
<p><em>En shop.njk:</em></p>
<pre class="language-yaml"><code class="language-yaml"><span class="token punctuation">---</span>
<span class="token key atrule">title</span><span class="token punctuation">:</span> Shop
<span class="token key atrule">snipcart</span><span class="token punctuation">:</span> <span class="token boolean important">true</span>
<span class="token punctuation">---</span></code></pre>
<p>Obtenemos los productos y sus propiedades a través de un archivo <code>json</code>:</p>
<p><em>En shop.json:</em></p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"products"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{</span>
<span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"Libro El verano más largo"</span><span class="token punctuation">,</span>
<span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"Libro"</span><span class="token punctuation">,</span>
<span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">"Los textos de los que se compone este libro los escribí entre los meses de septiembre y noviembre. Lo mío es escribir canciones, grabar discos y hacer conciertos. El verano más largo son apuntes con forma de poemas que me apetecía compartir"</span><span class="token punctuation">,</span>
<span class="token property">"id"</span><span class="token operator">:</span> <span class="token string">"libro-verano"</span><span class="token punctuation">,</span>
<span class="token property">"price"</span><span class="token operator">:</span> <span class="token string">"12.00"</span><span class="token punctuation">,</span>
<span class="token property">"image"</span><span class="token operator">:</span> <span class="token string">"/assets/images/shop/libro-verano.jpg"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span>
<span class="token comment">// ...</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span></code></pre>
<p>Snipcart necesita algunos datos necesarios para crear los productos y procesar la compra.<br />
Obtenemos estos datos de nuestro archivo <code>shop.json</code> utilizando un “loop”:</p>
<pre class="language-html"><code class="language-html">{% for product in shop.products %}
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span>
<span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>snipcart-add-item<span class="token punctuation">"</span></span>
<span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ product.id }}<span class="token punctuation">"</span></span>
<span class="token attr-name">data-item-id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ product.id }}<span class="token punctuation">"</span></span>
<span class="token attr-name">data-item-price</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ product.price }}<span class="token punctuation">"</span></span>
<span class="token attr-name">data-item-url</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/shop/<span class="token punctuation">"</span></span>
<span class="token attr-name">data-item-description</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ product.description }}<span class="token punctuation">"</span></span>
<span class="token attr-name">data-item-image</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ product.image }}<span class="token punctuation">"</span></span>
<span class="token attr-name">data-item-name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>{{ product.name }}<span class="token punctuation">"</span></span>
<span class="token punctuation">></span></span>
añadir al carrito
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span>
{% endfor %}</code></pre>
<p>Este <code>button</code> es suficiente para activar Snipcart. Se requiere el nombre del producto, un ID de producto único, el precio del producto, la URL del producto (donde se encuentra el <code>button</code> “añadir al carrito”, utilizado por su rastreador cuando comprueba la integridad del pedido), la descripción del producto y la URL de la imagen del producto.</p>
<p>La clase CSS <code>snipcart-add-item</code> también es necesaria para que funcione.<br />
Dentro del loop utilizo estos datos para mostrar los productos en la interfaz de usuario.</p>
<p>Para el “toggle” del carrito de la compra tenemos el siguiente código:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>snipcart-checkout<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>ver carrito<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></code></pre>
<p>Esto nos permite comprobar el estado de nuestra cesta de la compra sin añadir nada nuevo a ella.</p>
<p>También proporcionamos a los visitantes una visión general concisa de la cesta de la compra en el resumen de productos:</p>
<pre class="language-html"><code class="language-html">Productos eligidos: <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>snipcart-items-count<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> Total:
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>snipcart-total-price<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></code></pre>
<p>Aquí se puede ver siempre cuántos productos hay en la cesta y cuál sería el precio total en este momento. Esto funciona con una inyección de Javascript.</p>
<p>Snipcart también busca nuestro atributo de idioma en el HTML para ajustar automáticamente el idioma mostrado:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>es<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre>
<h3 id="para-mas-seguridad-http-response-header"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/nuevo-shop-para-albertoballesteroscom-eleventy-y-snipcart/#para-mas-seguridad-http-response-header">Para más seguridad: HTTP response header</a></h3>
<p>Para la mayor seguridad posible de nuestras webs, establecemos <code>HTTP response header</code> (cabeceras de respuesta HTTP). La cabecera de la Política de Seguridad de Contenidos (CSP) es una capa adicional de seguridad que ayuda a detectar y mitigar ciertos tipos de ataques como el cross-site scripting (XSS) y los ataques de inyección de datos. Lo hacemos especificando exactamente qué recursos puede cargar el navegador.</p>
<p>Para que Snipcart funcione, tenemos que habilitar la recuperación de scripts a través de Snipcart en la cabecera de la Política de Seguridad de Contenidos.</p>
<h2 id="¡venta-lograda!"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/nuevo-shop-para-albertoballesteroscom-eleventy-y-snipcart/#%C2%A1venta-lograda!">¡Venta lograda!</a></h2>
<p>Según hemos conseguido nuestra primera venta, consta el artículo automáticamente dentro de la sección de productos. El comprador está listado en la sección de clientes y en la sección de pedidos aparece la transacción con los detalles del cliente.</p>
<figure role="group"><picture><source type="image/avif" srcset="https://www.lenesaile.com/assets/images/snipcart-ventas-500w.avif 500w, https://www.lenesaile.com/assets/images/snipcart-ventas-900w.avif 900w" sizes="100vw" /><source type="image/webp" srcset="https://www.lenesaile.com/assets/images/snipcart-ventas-500w.webp 500w, https://www.lenesaile.com/assets/images/snipcart-ventas-900w.webp 900w" sizes="100vw" /><source type="image/jpeg" srcset="https://www.lenesaile.com/assets/images/snipcart-ventas-500w.jpeg 500w, https://www.lenesaile.com/assets/images/snipcart-ventas-900w.jpeg 900w" sizes="100vw" /><img src="https://www.lenesaile.com/assets/images/image-placeholder.png" data-src="/assets/images/snipcart-ventas-500w.jpeg" class="innerimage" width="500" height="121" alt="Pantallazo del panel de administración de Snipcart" loading="lazy" decoding="async" /></picture><figcaption>¡Hemos vendido el primer ejemplar del libro por medio de la página web! Ahora aparece el producto en el panel de administración de Snipcart.</figcaption></figure>
<h2 id="conclusion-y-recomendaciones"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/nuevo-shop-para-albertoballesteroscom-eleventy-y-snipcart/#conclusion-y-recomendaciones">Conclusión y recomendaciones</a></h2>
<p>En caso de <a href="http://albertoballesteros.com/" rel="noopener">albertoballesteros.com</a>, manejo directamente la creación de los productos en el archivo <code>json</code>. Sin embargo, para clientes, esto sería bastante engorroso. En su lugar, pueden introducir los productos y sus propiedades en una hoja de Google (Excel) y luego convierto este formato en un archivo <code>json</code> que Eleventy puede procesar.</p>
<p>Cada cliente tiene necesidades individuales. Shopify es la primera opción para muchos por su flexibilidad, facilidad de uso y buena relación calidad-precio. Algunas grandes empresas también utilizan Shopify.</p>
<p>Para los páginas web de Jamstack en los que la tienda es más bien un complemento, Snipcart y Stripe son grandes soluciones con su combinación de fácil configuración y profunda personalización.</p>
<p>Para una web de WordPress, el propio plugin Woocommerce del CMS es un clásico. También es posible combinar WordPress con otros soluciones de tiendas.</p>
<h2 id="¿cual-es-la-mejor-solucion-para-una-tienda-online"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/nuevo-shop-para-albertoballesteroscom-eleventy-y-snipcart/#%C2%BFcual-es-la-mejor-solucion-para-una-tienda-online">¿Cuál es la mejor solución para una tienda online?</a></h2>
<p>En el momento de hacer una decisión hay que responder a las siguientes preguntas:</p>
<ul class="list">
<li>¿Cuánto quieres pagar por una solución de shop? Deben incluirse los costes de desarrollo y los costes de funcionamiento (como la contribución mensual de Shopify.</li>
<li>¿Cuáles son tus conocimientos técnicos y los de tu equipo: ¿cuánto quieres y puedes montar tú mismo?</li>
<li>¿Qué te imaginas para la funcionalidad y la facilidad de uso de la tienda?</li>
<li>¿Qué tamaño tiene o tendrá tu tienda? Hay que considerar eso para la escalabilidad y adaptabilidad de la plataforma.</li>
</ul>
<p>Ponte en el lugar de tus clientes: ¿Darías datos personales e información de pago a una página web de aspecto dudoso? ¿A un sitio web que no desprende confianza por todas partes?</p>
<p>El trabajo y el dinero invertidos en un sitio web de tienda rápido, atractivo y seguro siempre merecen la pena. Se reflejará en las ventas, y los costes incurridos al principio se recuperarán pronto.</p>
Por qué la velocidad es importante
2021-09-20T00:00:00Z
https://www.lenesaile.com/es/blog/por-que-la-velocidad-es-importante/
<p>El rendimiento de la web consiste en que todo el mundo pueda acceder rápidamente a una página web sin tener que superar obstáculos innecesarios.</p>
<p>Cuando un cliente se va a comer en un restaurante, la lentitud en el servicio suele generar malas críticas y, en consecuencia, menos clientes futuros. Del mismo modo, una velocidad lenta de una web puede conducir a una mala clasificación en Google y a un menor tráfico en la página web. La llamada “experiencia del usuario” también se resiente.</p>
<p><a href="https://www.lenesaile.com/es/blog/por-que-la-velocidad-es-importante/#toc-skipped" id="skip-toc" class="visually-hidden">Saltar el índice de contenidos</a></p>
<h2 id="table-of-contents">índice de contenidos</h2>
<nav id="toc" class="table-of-contents"><ol class="toc-list"><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/por-que-la-velocidad-es-importante/#la-pagina-web-como-experiencia">La página web como “experiencia”</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/por-que-la-velocidad-es-importante/#%C2%BFpor-que-son-tan-importantes-las-paginas-web-rapidas">¿Por qué son tan importantes las páginas web rápidas?</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/por-que-la-velocidad-es-importante/#%C2%A1haz-que-los-sitios-sean-rapidos-para-los-usuarios-de-telefonos-moviles!">¡Haz que los sitios sean rápidos para los usuarios de teléfonos móviles!</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/por-que-la-velocidad-es-importante/#%C2%BFes-posible-hacer-un-sitio-web-rapido-a-posteriomente">¿Es posible hacer un sitio web rápido a posteriomente?</a></li><li class="toc-item"><a class="toc-link" href="https://www.lenesaile.com/es/blog/por-que-la-velocidad-es-importante/#medidas-para-acelerar-tu-pagina-web">Medidas para acelerar tu página web</a></li></ol></nav><div id="toc-skipped"></div>
<h2 id="la-pagina-web-como-experiencia"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/por-que-la-velocidad-es-importante/#la-pagina-web-como-experiencia">La página web como “experiencia”</a></h2>
<p>Los visitantes son más exigentes que nunca. Visitar un sitio web ya no consiste simplemente en encontrar la información deseada.</p>
<p>Se trata de ayudar al visitante a llegar a su destino lo más fácil y rápidamente posible. Se trata de la calidad de la interacción del usuario con una página web y también de su percepción del mismo.</p>
<p>El tiempo que un visitante pasa en tu web debe ser una “experiencia” agradable, algo que se percibe positivamente durante la visita y, en el mejor de los casos, se recuerda como tal.</p>
<h2 id="¿por-que-son-tan-importantes-las-paginas-web-rapidas"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/por-que-la-velocidad-es-importante/#%C2%BFpor-que-son-tan-importantes-las-paginas-web-rapidas">¿Por qué son tan importantes las páginas web rápidas?</a></h2>
<p>La velocidad juega un papel muy importante en esto. Los sitios web de alto rendimiento retienen mejor a los usuarios que los de bajo rendimiento. La retención de usuarios, a su vez, es fundamental para mejorar las tasas de conversión (el porcentaje de usuarios que completan una acción deseada).</p>
<p>Por tanto, cuanto mejor sea el rendimiento, mayor será la probabilidad de que los usuarios permanezcan en una página, lean el contenido, realicen compras o marquen tu número de teléfono.</p>
<p>El motor de búsqueda de Google también quiere que los usuarios tengan una buena “experiencia” en la web, por lo que las páginas web rápidas también se clasifican mejor en los resultados de búsqueda. Un sitio web lento puede hacer que acabes en la página 312 de los resultados de búsqueda, independientemente de cualquier otra métrica.</p>
<h2 id="¡haz-que-los-sitios-sean-rapidos-para-los-usuarios-de-telefonos-moviles!"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/por-que-la-velocidad-es-importante/#%C2%A1haz-que-los-sitios-sean-rapidos-para-los-usuarios-de-telefonos-moviles!">¡Haz que los sitios sean rápidos para los usuarios de teléfonos móviles!</a></h2>
<p>La gente utiliza cada vez más sus teléfonos móviles para acceder a contenidos y servicios digitales. Cuando miro los análisis de los sitios web de nuestros clientes, veo esta tendencia reflejada en los datos. Un reparto típico es un 60% de móviles (con una pequeña proporción de tablets) y un 40% de ordenadores de sobremesa y de portátiles.</p>
<p>El rendimiento de una página web puede variar mucho en función del dispositivo del usuario, las condiciones de la red u otros procesos que se ejecuten en el dispositivo. Si te parece tu página web lo suficientemente rápida en un ordenador con Internet de fibra óptica, un usuario con un teléfono móvil más antiguo con datos móviles puede experimentarlo de forma muy diferente.</p>
<p>Por lo tanto, es importante optimizar los sitios web especialmente para los teléfonos móviles, sobre todo en la categoría de velocidad.</p>
<h2 id="¿es-posible-hacer-un-sitio-web-rapido-a-posteriomente"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/por-que-la-velocidad-es-importante/#%C2%BFes-posible-hacer-un-sitio-web-rapido-a-posteriomente">¿Es posible hacer un sitio web rápido a posteriomente?</a></h2>
<p><mark>El rendimiento no es una función adicional de la que se pueda encargar más tarde. Por el contrario, debe incluirse en cada paso del proceso de desarrollo.</mark></p>
<p>Esto se ve a menudo con las plantillas de WordPress: primero se carga el tema multifuncional completo y montones de plugins, y luego se instala otro plugin que se supone que minimiza y optimiza automáticamente todo lo anterior. ¡Y eso teniendo en cuenta que el propio <a href="https://www.lenesaile.com/es/blog/algunas-notas-personales-sobre-wordpress-en-2022/#wordpress-es-gratuito-seguro-y-rapido">WordPress es realmente rápido</a>!</p>
<p>Sin embargo, sólo hay un número muy limitado de posibilidades que pueden conducir a un aumento de la velocidad con poco esfuerzo.</p>
<h2 id="medidas-para-acelerar-tu-pagina-web"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/por-que-la-velocidad-es-importante/#medidas-para-acelerar-tu-pagina-web">Medidas para acelerar tu página web</a></h2>
<p>La lista de métodos y mejores prácticas para lograr una velocidad óptima es larga. Al fin y al cabo, la experiencia visual no debe resentirse, es decir, queremos imágenes fantasticas que muestren tus instalaciones o productos, y queremos una tipografía que refleje el carácter de tu empresa.</p>
<p>Ambos elementos, las imágenes y los tipos de letra, se optimizan individualmente para que dejen de ser un obstáculo para la velocidad.</p>
<p>La siguiente es una lista muy básica de medidas que deben seguirse siempre:</p>
<ol class="list">
<li>Usar un anfitrión de buena calidad</li>
<li>Programar una página web liviana</li>
<li>Optimizar todos los componentes como los scripts, tipos de letra y las imágenes</li>
<li>Utilizar caching</li>
<li>Usar una red de entrega de contenido (CDN)</li>
</ol>
<p>Una página web verdaderamente rápida no es un proceso automatizado.</p>
<p>Conseguimos sitios web eficientes con estrategia, experiencia y trabajo detallado.</p>
La claridad en los contenidos una página web
2021-01-27T00:00:00Z
https://www.lenesaile.com/es/blog/la-claridad-en-los-contenidos-una-pagina-web/
<p>Es sin duda una de las características más importantes que debe tener una página web.<br />
De poco sirve un bonito diseño si luego el visitante no entiende nada y abandona la página frustrado y jurando no volver más.</p>
<p><strong>A todos nos ha pasado:</strong></p>
<ul class="list">
<li>Buscas información sobre los servicios de una empresa de reformas que alguien te ha recomendado y no consigues entender qué hacen o dejan de hacer.</li>
<li>Necesitas descargar un documento en la web del colegio de tus hijos y no hay manera de encontrarlo.</li>
<li>Quieres comprar un artículo en concreto de un shop de ropa, te cuesta horrores encontrarlo y luego no hay manera de entender cómo hay que realizar la compra.</li>
</ul>
<p>Y luego están las páginas de las administraciones públicas, esas son las peores, están pensadas para matarte poco a poco a disgustos, mandándote de un lado para otro sin sentido, en una nueva versión de “El Proceso” de Kafka.</p>
<p>El tiempo es una de las cosas más importantes para las personas, y el sentirse bien tratado y respetado también es fundamental. De ahí la necesidad de tener muy en cuenta a la hora de realizar una web, la calidad de los textos y la disposición de los contenidos.</p>
<p>Es sencillo, <mark>si no te entienden no van a confiar, no te van a contratar, no te van a comprar, y no van a volver por tu web</mark>.</p>
<p>Todos estos aspectos los tenemos muy en cuenta en las páginas que realizamos.</p>
Detalles técnicos sobre el desarrollo de ineliagestion.com
2021-01-11T00:00:00Z
https://www.lenesaile.com/es/blog/detalles-tecnicos-sobre-el-desarrollo-de-ineliagestioncom/
<p>Acabamos de incorporar a nuestra <a href="https://www.lenesaile.com/es/proyectos">sección de proyectos</a> una de las páginas que realizamos el pasado otoño.</p>
<p>Se trata de <a href="https://www.ineliagestion.com/" rel="noopener">Inelia Gestión</a>, una asesoría fiscal, laboral y mercantil de Madrid.</p>
<p>Es nuestra primera web relacionada con este sector y hemos aprendido mucho en todo el proceso.</p>
<p>El objetivo era que desde Inelia vieran reflejada su imagen y su filosofía de trabajo en su nueva página. Son una gestoría con un trato muy cercano y una política de comunicación muy directa y clara con el cliente.</p>
<p>Ha sido genial contar con las fotografías de Gabriel Monsalve que captó toda la esencia de la asesoría. Las fotos originales de las personas y las instalaciones reales tienen un valor incalculable y contribuyen en gran medida a infundir confianza a los visitantes.</p>
<p>Queremos agradecer a Carlos Recio, director de Inelia Gestión por confiar en nosotros para desarrollar su página web.</p>
<h2 id="desarrollado-con-un-nuevo-metodo-el-jamstack"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/detalles-tecnicos-sobre-el-desarrollo-de-ineliagestioncom/#desarrollado-con-un-nuevo-metodo-el-jamstack">Desarrollado con un nuevo método, el Jamstack.</a></h2>
<p>Jamstack está orientado a los inicios del desarrollo de la web, cuando todas las páginas web eran estáticas - puros archivos HTML que se encuentran en una estructura de carpetas. En el pasado, no se podían crear sitios web dinámicos, y la libertad de diseño dejaba mucho que desear. Si querías ajustar algo que se refería a los otros archivos, por ejemplo añadir un nuevo elemento de menú, tenías que ajustarlo manualmente en todos los archivos. Sin embargo, estas páginas web, que se arreglaban con lo más esencial, eran increíblemente rápidas y seguras.</p>
<p>Así que hoy en día sólo queremos aprovechar de las ventajas de esta técnica clásica.</p>
<p>En resumen el cliente tiene las siguientes ventajas:</p>
<ol class="list">
<li>
<p><strong>Tiempos de carga muy rápidos</strong>, porque todo se carga a través de una CDN. Excurso: Una CDN (Content Delivery Network) es una red de servidores que almacenan cachés de contenido estático de un sitio web en varios lugares del mundo. Cuando un usuario visita el sitio web, el contenido se le entrega desde el servidor más cercano.</p>
</li>
<li>
<p><strong>Más seguridad</strong>, porque sin bases de datos y servidores no hay vulnerabilidades que puedan ser explotadas por los atacantes.</p>
</li>
<li>
<p><strong>Bajos costos de funcionamiento</strong>, ya que el alojamiento de archivos estáticos es barato o incluso gratuito.</p>
</li>
<li>
<p><strong>Desarrollo rápido y cambios sin complicaciones</strong>: Los desarrolladores web podemos centrarnos en el desarrollo sin estar atados a una arquitectura monolítica. Podemos desarrollar más rápido y centrado.</p>
</li>
<li>
<p><strong>Escalabilidad</strong>, significa que si el sitio web es visitado repentinamente por muchos usuarios activos al mismo tiempo, el CDN compensa esto sin problemas. En caso de un alojamiento web tradicional, dependiente del paquete contratado el sitio web ya no sería accesible, y para soportar una mayor afluencia de visitantes habría que actualizar el paquete a un considerable costo.</p>
</li>
</ol>
<h2 id="pero-¿como-resolver-el-problema-del-mantenimiento-desproporcionadamente-complicado-de-los-sitios-web-estaticos"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/detalles-tecnicos-sobre-el-desarrollo-de-ineliagestioncom/#pero-%C2%BFcomo-resolver-el-problema-del-mantenimiento-desproporcionadamente-complicado-de-los-sitios-web-estaticos">Pero, ¿cómo resolver el problema del mantenimiento desproporcionadamente complicado de los sitios web estáticos?</a></h2>
<p>Nos ayudan los llamados generadores de sitios estáticos, que crean el sitio web estático como parte de un proceso de construcción. Utilizan fuentes de datos y plantillas para generar los archivos HTML individuales.</p>
<p><a href="http://ineliagestion.com/" rel="noopener">ineliagestion.com</a> utiliza <a href="https://www.11ty.dev/" rel="noopener">Eleventy</a>, los archivos estáticos están en un repositorio privado en <a href="https://github.com/" rel="noopener">GitHub</a> (un portal creado para alojar código de aplicaciones) y <a href="https://www.netlify.com/" rel="noopener">Netlify</a> (un servicio de hosting para sitios estáticos) se encarga del alojamiento en sus CDN. Los cambios se envían directamente a GitHub y Netlify a través de mi editor de código.</p>
<p>Para el diseño de este sitio web, utilicé Figma, una herramienta de diseño y prototipado para proyectos digitales. Implementé el diseño acordado con el cliente usando el framework CSS Tailwind.</p>
<p>Queremos cargar sólo lo más esencial, como las páginas web clásicas. Por lo tanto, todos los elementos de la página web se almacenan en caché, se reducen y se limpian. Todas las imágenes se cargan con retraso y por los formatos de imagen más modernos, y se eliminan el CSS y el Javascript no utilizado.</p>
<p>Por último, vuelvo a comprobar todo el sitio en cuanto a los factores de rendimiento, accesibilidad, las denominadas mejores prácticas y la compatibilidad con los motores de búsqueda.</p>
<figure role="group"><picture><source type="image/avif" srcset="https://www.lenesaile.com/assets/images/ineliagestion-lighthouse-500w.avif 500w, https://www.lenesaile.com/assets/images/ineliagestion-lighthouse-864w.avif 864w" sizes="60vw" /><source type="image/webp" srcset="https://www.lenesaile.com/assets/images/ineliagestion-lighthouse-500w.webp 500w, https://www.lenesaile.com/assets/images/ineliagestion-lighthouse-864w.webp 864w" sizes="60vw" /><source type="image/jpeg" srcset="https://www.lenesaile.com/assets/images/ineliagestion-lighthouse-500w.jpeg 500w, https://www.lenesaile.com/assets/images/ineliagestion-lighthouse-864w.jpeg 864w" sizes="60vw" /><img src="https://www.lenesaile.com/assets/images/image-placeholder.png" data-src="/assets/images/ineliagestion-lighthouse-500w.jpeg" class="object-fit-cover innerimage" width="500" height="304" alt="Screenshot of Lighthouse audit results for ineliagestion.com, showing great results in all areas" loading="lazy" decoding="async" /></picture><figcaption>Resultados de la auditoría Lighthouse para ineliagestion.com. Lighthouse es una herramienta que proporciona información sobre el rendimiento, el SEO, la usabilidad y la accesibilidad de una página.</figcaption></figure>
Un nuevo proyecto: de la planificación a la publicación
2020-08-28T00:00:00Z
https://www.lenesaile.com/es/blog/un-nuevo-proyecto-de-la-planificacion-a-la-publicacion/
<h2 id="planificacion-y-diseno"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/un-nuevo-proyecto-de-la-planificacion-a-la-publicacion/#planificacion-y-diseno">Planificación y diseño</a></h2>
<p>Cada proyecto comienza con la planificación. Así puedo tener una sensación más concreta del proyecto. Estudio sitios web de la misma industria y busco inspiración. Empiezo a pensar en la tipografía y el esquema de color y creo una carpeta de proyecto en la que se almacena el material del cliente.</p>
<p>El siguiente paso es crear un menú y dibujo a grandes rasgos el diseño y los elementos del sitio web. Mis bosquejos no son muy detallados, ni describen cada parte del sitio web. El propósito principal es tener una idea general del proyecto.</p>
<p>Con los programas Photoshop y Figma convierto mis ideas y bocetos en diseños. A veces resuelvo cada subpágina completamente, otras veces convierto la idea en mi cabeza directamente en código.</p>
<h2 id="codigo-y-lanzamineto"><a class="heading-anchor" href="https://www.lenesaile.com/es/blog/un-nuevo-proyecto-de-la-planificacion-a-la-publicacion/#codigo-y-lanzamineto">Código y lanzamineto</a></h2>
<p>Cuando el diseño está acordado con mi cliente, utilizo el editor de código “<a href="https://code.visualstudio.com/" rel="noopener">Visual Studio Code</a>” para convertir el diseño en un código moderno y eficiente, utilizando HTML, CSS y Javascript.</p>
<p>En la fase de prueba compruebo que todo funciona correctamente. ¿El sitio web se ejecuta en todos los navegadores y sistemas operativos comunes? ¿Llegan los formularios de contacto? ¿Funcionan correctamente el módulo GDPR y el análisis de visitantes, es la página totalmente accesible?</p>
<p>La web está funcionando sin errores en mi servidor local - ahora compruebo que esto también es así en el servidor de producción.<br />
Finalmente publico la página web.</p>