<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Franklin</title>
    <description>The latest articles on DEV Community by Franklin (@ortizfranklindev).</description>
    <link>https://dev.to/ortizfranklindev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2493878%2F8821e986-c182-499a-92e4-d91e7e960256.png</url>
      <title>DEV Community: Franklin</title>
      <link>https://dev.to/ortizfranklindev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ortizfranklindev"/>
    <language>en</language>
    <item>
      <title>Notas desde el pase: El nombre que ya era correcto</title>
      <dc:creator>Franklin</dc:creator>
      <pubDate>Sat, 03 Oct 2026 12:22:18 +0000</pubDate>
      <link>https://dev.to/ortizfranklindev/notas-desde-el-pase-el-nombre-que-ya-era-correcto-51fo</link>
      <guid>https://dev.to/ortizfranklindev/notas-desde-el-pase-el-nombre-que-ya-era-correcto-51fo</guid>
      <description>&lt;p&gt;&lt;em&gt;También disponible en &lt;a href="https://dev.to/ortizfranklindev/notes-from-the-pass-the-name-that-was-already-right-3pl0"&gt;Inglés&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  La afirmación a prueba
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/ortizfranklindev/el-navegador-ya-cuenta-con-una-arquitectura-de-accesibilidad-la-mayoria-de-los-proyectos-la-17ch"&gt;El ensayo del jueves&lt;/a&gt; sostuvo que ARIA es una intervención semántica: debe suministrar lo que el documento nativo no expresa por sí mismo, no reemplazar lo que sí expresa. La afirmación se probó contra dos errores enviados a producción en el mismo control de navegación: un &lt;code&gt;aria-label&lt;/code&gt; que reemplazó un nombre accesible correcto, y un &lt;code&gt;&amp;lt;div role="button"&amp;gt;&lt;/code&gt; sustituyendo a un control que ya existía como &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Esta Nota desarmó ambos, una pieza a la vez, y confirmó qué queda una vez que todo lo innecesario se retira.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reducción uno: El botón
&lt;/h2&gt;

&lt;p&gt;Tal como se envió a producción:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Dismiss navigation panel"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Close menu
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Antes de tocar nada, recorre el cálculo del nombre accesible de la forma en que el navegador realmente lo ejecuta. &lt;code&gt;aria-labelledby&lt;/code&gt;: no presente. &lt;code&gt;aria-label&lt;/code&gt;: presente, valor "Dismiss navigation panel". El cálculo se detiene allí. Nunca llega al contenido del propio botón, porque nada le exige seguir buscando una vez que un paso anterior ya produjo una respuesta.&lt;/p&gt;

&lt;p&gt;Quita el único atributo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;
  Close menu
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Recorre el cálculo de nuevo. &lt;code&gt;aria-labelledby&lt;/code&gt;: no presente. &lt;code&gt;aria-label&lt;/code&gt;: no presente. Contenido: "Close menu", y &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; es uno de los &lt;em&gt;roles&lt;/em&gt; de los que el cálculo lee un nombre directamente. Ese es el nombre accesible ahora. Siempre pudo haberlo sido. No se agregó nada para producirlo. El único cambio fue eliminar lo que lo estaba superando en prioridad.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reducción dos: El Div
&lt;/h2&gt;

&lt;p&gt;El segundo control de la misma revisión, completo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;tabindex=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"menu-close"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Close menu
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;closeControl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.menu-close&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;closeControl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;closeMenu&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;closeControl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;keydown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Enter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;closeMenu&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quita primero el &lt;em&gt;handler&lt;/em&gt; de &lt;code&gt;keydown&lt;/code&gt;, deja todo lo demás:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;tabindex=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"menu-close"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Close menu
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.menu-close&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;closeMenu&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El div todavía se anuncia como un botón. Todavía es alcanzable mediante Tab. El clic todavía cierra el menú. Presiona Enter o Espacio en su lugar, y no sucede absolutamente nada: &lt;code&gt;role="button"&lt;/code&gt; nunca vino con la activación por teclado que un botón real proporciona de forma automática. Ese siempre fue el trabajo del &lt;em&gt;handler&lt;/em&gt; de &lt;code&gt;keydown&lt;/code&gt;, y es la única pieza que acaba de salir.&lt;/p&gt;

&lt;p&gt;Quita el resto y cambia el elemento:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"menu-close"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Close menu
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.menu-close&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;closeMenu&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Un &lt;em&gt;listener&lt;/em&gt;, sin cambios. Sin atributo &lt;em&gt;role&lt;/em&gt;, sin &lt;em&gt;tabindex&lt;/em&gt;, sin &lt;em&gt;handler&lt;/em&gt; de &lt;code&gt;keydown&lt;/code&gt;. Tab todavía lo alcanza. El clic todavía cierra el menú. Enter y Espacio también lo cierran, porque un botón nativo dispara el mismo evento &lt;code&gt;click&lt;/code&gt; en respuesta a los tres, sin que se le ordene hacerlo. El div necesitaba dos asignaciones de eventos separadas para cubrir lo que la etiqueta ahora cubre con una.&lt;/p&gt;

&lt;h2&gt;
  
  
  Si la afirmación se mantuvo
&lt;/h2&gt;

&lt;p&gt;Ambas reducciones llegan a la respuesta que predice la tesis del ensayo: un documento cuyas semánticas tienen la cantidad justa, ni más ni menos. El botón nunca necesitó una etiqueta: el contenido ya estaba haciendo ese trabajo, y lo único que se interponía en su camino era un atributo agregado para satisfacer una lista de verificación. El div nunca necesitó ser un div. Llevaba un &lt;em&gt;role&lt;/em&gt;, un &lt;em&gt;tabindex&lt;/em&gt; y un &lt;em&gt;handler&lt;/em&gt; de &lt;code&gt;keydown&lt;/code&gt; para simular un comportamiento que &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; siempre iba a proporcionar por sí solo, por una asignación de evento menos de las que el div jamás logró manejar.&lt;/p&gt;

&lt;p&gt;Ninguna de las dos correcciones agregó nada. Ambas restaron exactamente la pieza que sustituía a algo que la plataforma ya tenía, y el resultado en cada caso no fue una versión más pequeña de lo mismo. Fue la cosa funcionando correctamente por primera vez.&lt;/p&gt;

&lt;h3&gt;
  
  
  Artículo de referencia:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://dev.to/ortizfranklindev/el-navegador-ya-cuenta-con-una-arquitectura-de-accesibilidad-la-mayoria-de-los-proyectos-la-17ch"&gt;El navegador ya cuenta con una arquitectura de accesibilidad. La mayoría de los proyectos la ignoran.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>html</category>
      <category>arquitectura</category>
      <category>a11y</category>
      <category>desarrolloweb</category>
    </item>
    <item>
      <title>Notes from the Pass: The Name That Was Already Right</title>
      <dc:creator>Franklin</dc:creator>
      <pubDate>Sat, 03 Oct 2026 12:19:33 +0000</pubDate>
      <link>https://dev.to/ortizfranklindev/notes-from-the-pass-the-name-that-was-already-right-3pl0</link>
      <guid>https://dev.to/ortizfranklindev/notes-from-the-pass-the-name-that-was-already-right-3pl0</guid>
      <description>&lt;p&gt;&lt;em&gt;Also available in &lt;a href="https://dev.to/ortizfranklindev/notas-desde-el-pase-el-nombre-que-ya-era-correcto-51fo"&gt;Español&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Claim Being Tested
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/ortizfranklindev/the-browser-already-has-an-accessibility-architecture-most-projects-ignore-it-3bgk"&gt;Thursday's essay&lt;/a&gt; argued that ARIA is a semantic intervention: it should supply what the native document doesn't already express, not replace what it does. The claim was tested against two shipped mistakes on the same navigation control — an &lt;code&gt;aria-label&lt;/code&gt; that replaced a correct accessible name, and a &lt;code&gt;&amp;lt;div role="button"&amp;gt;&lt;/code&gt; standing in for a control that already existed as &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This Note takes both apart, one piece at a time, and confirms what's left once everything unnecessary comes off.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reduction One: The Button
&lt;/h2&gt;

&lt;p&gt;As shipped:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Dismiss navigation panel"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Close menu
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before touching anything, walk the accessible name computation the way the browser actually runs it. &lt;code&gt;aria-labelledby&lt;/code&gt;: not present. &lt;code&gt;aria-label&lt;/code&gt;: present, value "Dismiss navigation panel." The computation stops there. It never reaches the button's own content, because nothing requires it to keep looking once an earlier step has already produced an answer.&lt;/p&gt;

&lt;p&gt;Remove the one attribute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;
  Close menu
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Walk the computation again. &lt;code&gt;aria-labelledby&lt;/code&gt;: not present. &lt;code&gt;aria-label&lt;/code&gt;: not present. Content: "Close menu," and &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; is one of the roles the computation reads a name from directly. That's the accessible name now. It always could have been. Nothing was added to produce it. The only change was removing the thing that had been outranking it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reduction Two: The Div
&lt;/h2&gt;

&lt;p&gt;The same review's second control, in full:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;tabindex=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"menu-close"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Close menu
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;closeControl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.menu-close&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;closeControl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;closeMenu&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;closeControl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;keydown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Enter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;closeMenu&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Strip the keydown handler first, leave everything else:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;tabindex=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"menu-close"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Close menu
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.menu-close&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;closeMenu&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The div still announces as a button. It's still reachable by Tab. Click still closes the menu. Press Enter or Space instead, and nothing happens at all — &lt;code&gt;role="button"&lt;/code&gt; never came with the keyboard activation a real button provides automatically. That was always the keydown handler's job, and it's the only thing that just left.&lt;/p&gt;

&lt;p&gt;Strip the rest, and change the element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"menu-close"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Close menu
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.menu-close&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;closeMenu&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One listener, unchanged. No role attribute, no tabindex, no keydown handler. Tab still reaches it. Click still closes the menu. Enter and Space close it too, because a native button fires the same click event in response to all three, without being told to. The div needed two separate event bindings to cover what the tag now covers with one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Whether the Claim Held
&lt;/h2&gt;

&lt;p&gt;Both reductions land on the answer the essay's thesis predicts: a document whose semantics are the correct amount, no more and no less. The button never needed a label — content had already been doing that job, and the only thing standing in its way was an attribute added to satisfy a checklist. The div never needed to be a div. It was carrying a role, a tabindex, and a keydown handler to simulate behavior &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; was always going to provide on its own, for one less event binding than the div ever managed.&lt;/p&gt;

&lt;p&gt;Neither fix added anything. Both subtracted exactly the piece that was substituting for something the platform already had, and the result in each case wasn't a smaller version of the same thing. It was the thing working correctly for the first time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reference Article:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://dev.to/ortizfranklindev/the-browser-already-has-an-accessibility-architecture-most-projects-ignore-it-3bgk"&gt;The Browser Already Has an Accessibility Architecture. Most Projects Ignore It.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>html</category>
      <category>a11y</category>
      <category>webdev</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Notas desde el pase: Cada menú apuntaba a la misma fila</title>
      <dc:creator>Franklin</dc:creator>
      <pubDate>Sat, 03 Oct 2026 12:16:14 +0000</pubDate>
      <link>https://dev.to/ortizfranklindev/notas-desde-el-pase-cada-menu-apuntaba-a-la-misma-fila-30hm</link>
      <guid>https://dev.to/ortizfranklindev/notas-desde-el-pase-cada-menu-apuntaba-a-la-misma-fila-30hm</guid>
      <description>&lt;p&gt;&lt;em&gt;También disponible en &lt;a href="https://dev.to/ortizfranklindev/notes-from-the-pass-every-menu-pointed-at-the-same-row-300j"&gt;Inglés&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  La afirmación a prueba
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/ortizfranklindev/los-popovers-nativos-no-reemplazaron-a-las-bibliotecas-de-js-reemplazaron-al-codigo-de-union-3gjh"&gt;El ensayo del martes&lt;/a&gt; sostuvo que el anclaje implícito ya resuelve el caso ordinario del &lt;em&gt;popover&lt;/em&gt; sin costo alguno. Un &lt;em&gt;popover&lt;/em&gt; vinculado a su propio activador (&lt;em&gt;trigger&lt;/em&gt;) no necesita nombres, ni &lt;em&gt;scoping&lt;/em&gt;, ni nada más. La brecha solo aparece cuando un requisito real sale de ese caso y, aun así, solo en combinación con algo fácil de pasar por alto: la estructura del DOM heredada de antes de que la plataforma resolviera el recorte de superposiciones (&lt;em&gt;overlay clipping&lt;/em&gt;), cuando las superposiciones debían vivir separadas de aquello que las activaba.&lt;/p&gt;

&lt;p&gt;Esta Nota reproduce exactamente esa combinación —un nombre de &lt;em&gt;anchor&lt;/em&gt; compartido y un &lt;em&gt;popover&lt;/em&gt; ubicado como hermano en lugar de descendiente— a lo largo de un &lt;em&gt;grid&lt;/em&gt; de múltiples filas real, y confirma si aplicar el &lt;em&gt;scope&lt;/em&gt; realmente corrige cada fila y no solo la que se probó por casualidad.&lt;/p&gt;

&lt;h2&gt;
  
  
  Míralo romperse
&lt;/h2&gt;

&lt;p&gt;Cuatro filas, cada una con su propio menú de acciones, construidas de la manera descrita en el ensayo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"grid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"grid-row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Q3 Report &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;popovertarget=&lt;/span&gt;&lt;span class="s"&gt;"menu-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;⋮&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;menu&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"menu-1"&lt;/span&gt; &lt;span class="na"&gt;popover&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Rename&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&amp;lt;button&amp;gt;&lt;/span&gt;Archive&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&amp;lt;button&amp;gt;&lt;/span&gt;Delete&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/menu&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"grid-row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Q4 Report &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;popovertarget=&lt;/span&gt;&lt;span class="s"&gt;"menu-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;⋮&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;menu&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"menu-2"&lt;/span&gt; &lt;span class="na"&gt;popover&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Rename&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&amp;lt;button&amp;gt;&lt;/span&gt;Archive&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&amp;lt;button&amp;gt;&lt;/span&gt;Delete&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/menu&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"grid-row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Annual Summary &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;popovertarget=&lt;/span&gt;&lt;span class="s"&gt;"menu-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;⋮&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;menu&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"menu-3"&lt;/span&gt; &lt;span class="na"&gt;popover&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Rename&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&amp;lt;button&amp;gt;&lt;/span&gt;Archive&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&amp;lt;button&amp;gt;&lt;/span&gt;Delete&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/menu&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"grid-row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Budget Draft &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;popovertarget=&lt;/span&gt;&lt;span class="s"&gt;"menu-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;⋮&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;menu&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"menu-4"&lt;/span&gt; &lt;span class="na"&gt;popover&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Rename&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&amp;lt;button&amp;gt;&lt;/span&gt;Archive&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&amp;lt;button&amp;gt;&lt;/span&gt;Delete&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/menu&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.grid-row&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;anchor-name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--row-anchor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;popover&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;position-anchor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--row-anchor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;position-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;right&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Haz clic en el : de "Q3 Report". El menú se abre —&lt;code&gt;popovertarget&lt;/code&gt; maneja esa parte correctamente sin importar qué suceda— y se posiciona junto a "Budget Draft", la última fila del &lt;em&gt;grid&lt;/em&gt;. Haz clic en el : de "Annual Summary". La misma posición. Haz clic en el de "Q4 Report". La misma posición de nuevo. Cada menú, de cada fila, cae exactamente en el mismo lugar: dondequiera que se ubique el último &lt;code&gt;.grid-row&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué nada lo detuvo
&lt;/h2&gt;

&lt;p&gt;Cada &lt;code&gt;&amp;lt;menu popover&amp;gt;&lt;/code&gt; se ubica directamente después de su propio &lt;code&gt;.grid-row&lt;/code&gt;, como hermano; ambos son hijos de &lt;code&gt;.grid&lt;/code&gt;. Ninguno contiene al otro. Cuando el navegador resuelve &lt;code&gt;position-anchor: --row-anchor&lt;/code&gt; para cualquiera de los cuatro menús, busca primero el ancestro más cercano que lleve ese nombre y no encuentra ninguno, porque ningún menú está anidado dentro de una fila. Sin un ancestro que preferir, la resolución cae en la única regla que queda en pie: el último elemento del documento que lleve ese nombre de &lt;em&gt;anchor&lt;/em&gt;. Las cuatro filas comparten exactamente el mismo nombre, establecido a través de la misma clase que cada fila ya utiliza para su diseño (&lt;em&gt;layout&lt;/em&gt;). "Budget Draft" gana siempre, para cada menú, independientemente del botón en el que se haya hecho clic.&lt;/p&gt;

&lt;h2&gt;
  
  
  La solución
&lt;/h2&gt;

&lt;p&gt;Nada cambia respecto al nombre del &lt;em&gt;anchor&lt;/em&gt;, ni tampoco sobre el botón. Cada fila y su propio menú se envuelven juntos, y el nombre del &lt;em&gt;anchor&lt;/em&gt; se limita a ese contenedor (&lt;em&gt;wrapper&lt;/em&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"grid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row-wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"grid-row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      Q3 Report &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;popovertarget=&lt;/span&gt;&lt;span class="s"&gt;"menu-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;⋮&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;menu&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"menu-1"&lt;/span&gt; &lt;span class="na"&gt;popover&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Rename&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&amp;lt;button&amp;gt;&lt;/span&gt;Archive&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&amp;lt;button&amp;gt;&lt;/span&gt;Delete&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/menu&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row-wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"grid-row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      Q4 Report &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;popovertarget=&lt;/span&gt;&lt;span class="s"&gt;"menu-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;⋮&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;menu&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"menu-2"&lt;/span&gt; &lt;span class="na"&gt;popover&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Rename&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&amp;lt;button&amp;gt;&lt;/span&gt;Archive&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&amp;lt;button&amp;gt;&lt;/span&gt;Delete&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/menu&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Annual Summary y Budget Draft, mismo patrón --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.grid-row&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;anchor-name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--row-anchor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.row-wrapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;anchor-scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--row-anchor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;popover&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;position-anchor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--row-anchor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;position-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;right&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;.row-wrapper&lt;/code&gt; es ahora el ancestro común que contiene tanto a un &lt;code&gt;.grid-row&lt;/code&gt; como a su propio menú, y a nada más. Limitar &lt;code&gt;--row-anchor&lt;/code&gt; a este contenedor significa que cada menú solo puede resolver el nombre dentro del subárbol de su propio &lt;em&gt;wrapper&lt;/em&gt;. El &lt;em&gt;anchor&lt;/em&gt; con nombre idéntico de cualquier otra fila deja de existir en lo que respecta a ese menú.&lt;/p&gt;

&lt;p&gt;Haz clic de nuevo en el ⋮ de "Q3 Report". Se posiciona junto a "Q3 Report". Haz clic en el de "Annual Summary". Se posiciona junto a "Annual Summary". La solución no es específica de la fila que se probó primero: cada &lt;em&gt;wrapper&lt;/em&gt; lleva la misma regla de &lt;em&gt;scoping&lt;/em&gt;, por lo que cada fila obtiene el mismo resultado correcto, de forma independiente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Si la afirmación se sostuvo
&lt;/h2&gt;

&lt;p&gt;La afirmación del ensayo era que el anclaje implícito ya maneja el caso ordinario sin costo alguno, y que el costo real aparece solo donde un requisito sale de él mientras viejos hábitos estructurales se quedan sin examinar. Ambas mitades se sostuvieron. Anclar a la fila en lugar de al botón nunca fue el error: ese era un requisito real que el anclaje implícito genuinamente no puede cubrir. El error que nadie cometió a propósito fue dejar el menú donde siempre solían vivir las superposiciones de la era de JavaScript: junto a su activador, sin estar dentro de nada, porque esa solía ser la única forma de evitar que fuera recortado. El navegador no falló al buscar un ancestro. No había ninguno que encontrar.&lt;/p&gt;

&lt;p&gt;Aplicar &lt;em&gt;scope&lt;/em&gt; no requirió tocar el nombre del &lt;em&gt;anchor&lt;/em&gt;, ni el botón, ni la relación con el &lt;em&gt;popover&lt;/em&gt;. La única pieza que tuvo que cambiar fue exactamente la pieza que nadie tenía motivos para volver a mirar hasta que un &lt;em&gt;grid&lt;/em&gt; con más de una fila demostró lo contrario.&lt;/p&gt;

&lt;h3&gt;
  
  
  Artículo de referencia
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://dev.to/ortizfranklindev/los-popovers-nativos-no-reemplazaron-a-las-bibliotecas-de-js-reemplazaron-al-codigo-de-union-3gjh"&gt;Los popovers nativos no reemplazaron a las bibliotecas de JS; reemplazaron al código de unión.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>html</category>
      <category>a11y</category>
      <category>desarrolloweb</category>
      <category>arquitectura</category>
    </item>
    <item>
      <title>Notes from the Pass: Every Menu Pointed at the Same Row</title>
      <dc:creator>Franklin</dc:creator>
      <pubDate>Sat, 03 Oct 2026 12:06:11 +0000</pubDate>
      <link>https://dev.to/ortizfranklindev/notes-from-the-pass-every-menu-pointed-at-the-same-row-300j</link>
      <guid>https://dev.to/ortizfranklindev/notes-from-the-pass-every-menu-pointed-at-the-same-row-300j</guid>
      <description>&lt;p&gt;&lt;em&gt;Also available in &lt;a href="https://dev.to/ortizfranklindev/notas-desde-el-pase-cada-menu-apuntaba-a-la-misma-fila-30hm"&gt;Español&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Claim Being Tested
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/ortizfranklindev/native-popovers-didnt-replace-js-libraries-they-replaced-glue-code-49m1"&gt;Tuesday's essay&lt;/a&gt; argued that implicit anchoring already solves the ordinary popover case for free. A popover tethered to its own trigger needs no naming, no scoping, nothing. The gap only opens when a real requirement steps outside that case, and even then, only in combination with something easy to miss: DOM structure inherited from before the platform solved overlay clipping, when overlays had to live apart from whatever triggered them.&lt;/p&gt;

&lt;p&gt;This Note reproduces that exact combination — a shared anchor name, a popover sitting as a sibling instead of a descendant — across a real multi-row grid, and confirms whether scoping actually fixes every row, not just the one that happened to get tested.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watch It Break
&lt;/h2&gt;

&lt;p&gt;Four rows, each with its own action menu, built the way the essay described:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"grid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"grid-row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Q3 Report &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;popovertarget=&lt;/span&gt;&lt;span class="s"&gt;"menu-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;⋮&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;menu&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"menu-1"&lt;/span&gt; &lt;span class="na"&gt;popover&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Rename&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&amp;lt;button&amp;gt;&lt;/span&gt;Archive&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&amp;lt;button&amp;gt;&lt;/span&gt;Delete&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/menu&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"grid-row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Q4 Report &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;popovertarget=&lt;/span&gt;&lt;span class="s"&gt;"menu-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;⋮&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;menu&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"menu-2"&lt;/span&gt; &lt;span class="na"&gt;popover&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Rename&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&amp;lt;button&amp;gt;&lt;/span&gt;Archive&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&amp;lt;button&amp;gt;&lt;/span&gt;Delete&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/menu&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"grid-row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Annual Summary &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;popovertarget=&lt;/span&gt;&lt;span class="s"&gt;"menu-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;⋮&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;menu&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"menu-3"&lt;/span&gt; &lt;span class="na"&gt;popover&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Rename&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&amp;lt;button&amp;gt;&lt;/span&gt;Archive&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&amp;lt;button&amp;gt;&lt;/span&gt;Delete&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/menu&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"grid-row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    Budget Draft &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;popovertarget=&lt;/span&gt;&lt;span class="s"&gt;"menu-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;⋮&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;menu&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"menu-4"&lt;/span&gt; &lt;span class="na"&gt;popover&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Rename&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&amp;lt;button&amp;gt;&lt;/span&gt;Archive&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&amp;lt;button&amp;gt;&lt;/span&gt;Delete&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/menu&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.grid-row&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;anchor-name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--row-anchor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;popover&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;position-anchor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--row-anchor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;position-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;right&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Click "Q3 Report" 's ⋮. The menu opens — &lt;code&gt;popovertarget&lt;/code&gt; handles that part correctly no matter what — and positions itself against "Budget Draft," the last row in the grid. Click "Annual Summary" 's ⋮ instead. Same position. Click "Q4 Report" 's. Same position again. Every menu, from every row, lands in exactly the same place: wherever the last &lt;code&gt;.grid-row&lt;/code&gt; happens to sit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Nothing Stopped It
&lt;/h2&gt;

&lt;p&gt;Each &lt;code&gt;&amp;lt;menu popover&amp;gt;&lt;/code&gt; sits directly after its own &lt;code&gt;.grid-row&lt;/code&gt;, as a sibling, both children of &lt;code&gt;.grid&lt;/code&gt;. Neither one contains the other. When the browser resolves &lt;code&gt;position-anchor: --row-anchor&lt;/code&gt; for any of the four menus, it looks for the nearest ancestor carrying that name first, and finds none, because no menu is nested inside any row. With no ancestor to prefer, resolution falls through to the only rule left standing: the last element in the document with that anchor name. All four rows share the identical name, set through the same class every row already uses for its layout. "Budget Draft" wins every time, for every menu, regardless of which button was actually clicked.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix
&lt;/h2&gt;

&lt;p&gt;Nothing about the anchor name changes, and nothing about the button. Each row and its own menu get wrapped together, and the anchor name gets scoped to that wrapper:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"grid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row-wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"grid-row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      Q3 Report &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;popovertarget=&lt;/span&gt;&lt;span class="s"&gt;"menu-1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;⋮&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;menu&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"menu-1"&lt;/span&gt; &lt;span class="na"&gt;popover&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Rename&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&amp;lt;button&amp;gt;&lt;/span&gt;Archive&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&amp;lt;button&amp;gt;&lt;/span&gt;Delete&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/menu&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"row-wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"grid-row"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      Q4 Report &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;popovertarget=&lt;/span&gt;&lt;span class="s"&gt;"menu-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;⋮&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;menu&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"menu-2"&lt;/span&gt; &lt;span class="na"&gt;popover&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Rename&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&amp;lt;button&amp;gt;&lt;/span&gt;Archive&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&amp;lt;button&amp;gt;&lt;/span&gt;Delete&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/menu&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

  &lt;span class="c"&gt;&amp;lt;!-- Annual Summary and Budget Draft, same pattern --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.grid-row&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;anchor-name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--row-anchor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.row-wrapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;anchor-scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--row-anchor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;popover&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;position-anchor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--row-anchor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;position-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;right&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;.row-wrapper&lt;/code&gt; is now the common ancestor holding both a &lt;code&gt;.grid-row&lt;/code&gt; and its own menu, and nothing else's. Scoping &lt;code&gt;--row-anchor&lt;/code&gt; to it means each menu can only resolve the name within its own wrapper's subtree. Every other row's identically-named anchor stops existing as far as that menu is concerned.&lt;/p&gt;

&lt;p&gt;Click "Q3 Report" 's ⋮ again. It positions against "Q3 Report." Click "Annual Summary" 's. It positions against "Annual Summary." The fix isn't specific to whichever row got tested first — every wrapper carries the same scoping rule, so every row gets the same correct result, independently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Whether the Claim Held
&lt;/h2&gt;

&lt;p&gt;The essay's claim was that implicit anchoring already handles the ordinary case for free, and the real cost shows up only where a requirement steps outside it while old structural habits go un-examined. Both halves held. Anchoring to the row instead of the button was never the mistake — that was a real requirement implicit anchoring genuinely can't serve. The mistake nobody made on purpose was leaving the menu where JavaScript-era overlays always used to live: next to its trigger, not inside anything, because that used to be the only way to keep it from being clipped. The browser didn't fail to find an ancestor. There wasn't one to find.&lt;/p&gt;

&lt;p&gt;Scoping didn't require touching the anchor name, the button, or the popover relationship. The one piece that had to change was exactly the piece nobody had reason to look at again until a grid with more than one row proved it wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reference Article:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://dev.to/ortizfranklindev/native-popovers-didnt-replace-js-libraries-they-replaced-glue-code-49m1"&gt;Native Popovers Didn't Replace JS Libraries — They Replaced Glue Code&lt;/a&gt;&lt;/p&gt;

</description>
      <category>html</category>
      <category>webdev</category>
      <category>a11y</category>
      <category>architecture</category>
    </item>
    <item>
      <title>El navegador ya cuenta con una arquitectura de accesibilidad. La mayoría de los proyectos la ignoran.</title>
      <dc:creator>Franklin</dc:creator>
      <pubDate>Thu, 01 Oct 2026 19:29:49 +0000</pubDate>
      <link>https://dev.to/ortizfranklindev/el-navegador-ya-cuenta-con-una-arquitectura-de-accesibilidad-la-mayoria-de-los-proyectos-la-17ch</link>
      <guid>https://dev.to/ortizfranklindev/el-navegador-ya-cuenta-con-una-arquitectura-de-accesibilidad-la-mayoria-de-los-proyectos-la-17ch</guid>
      <description>&lt;p&gt;&lt;em&gt;Hermano estructural de &lt;a href="https://dev.to/ortizfranklindev/el-navegador-ya-tiene-una-arquitectura-la-mayoria-de-los-proyectos-la-ignoran-1e65"&gt;El navegador ya tiene una arquitectura. La mayoría de los proyectos la ignoran:&lt;/a&gt; la misma distinción, en una capa diferente.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;También disponible en &lt;a href="https://dev.to/ortizfranklindev/the-browser-already-has-an-accessibility-architecture-most-projects-ignore-it-3bgk"&gt;Inglés&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  El problema
&lt;/h4&gt;

&lt;p&gt;Una auditoría de accesibilidad da un resultado limpio. Cada control interactivo tiene un &lt;em&gt;role&lt;/em&gt;. Cada icono tiene una etiqueta. El escáner automatizado muestra una puntuación aprobatoria y el equipo continúa.&lt;/p&gt;

&lt;p&gt;Luego, un usuario de lector de pantalla intenta cerrar un panel de navegación. Escucha "Descartar panel de navegación" anunciado en un elemento que dice visiblemente "Cerrar menú". Nada se interrumpe. El control sigue funcionando; presionar Enter todavía cierra el panel. Pero el usuario al que se le dijo que escuchara "Cerrar" escuchó algo completamente diferente y tuvo que adivinar.&lt;/p&gt;

&lt;p&gt;Nadie eliminó una etiqueta. Alguien agregó una, específicamente para que el escáner dejara de señalar el control como no etiquetado. La auditoría se volvió más silenciosa. El control se volvió más difícil de usar.&lt;/p&gt;

&lt;h4&gt;
  
  
  Por qué existe el problema
&lt;/h4&gt;

&lt;p&gt;Las herramientas de accesibilidad existen porque los fallos de accesibilidad son, de otro modo, invisibles para la mayoría de las personas que construyen un producto. Una etiqueta faltante, un &lt;em&gt;role&lt;/em&gt; que nadie definió, un orden de enfoque (&lt;em&gt;focus&lt;/em&gt;) que atrapa a un usuario de teclado: nada de eso aparece en una revisión visual de QA. Los escáneres automatizados y las listas de verificación de cumplimiento surgieron para detectar exactamente ese punto ciego, y detectan una categoría real de fallo: un botón compuesto solo por un icono que un lector de pantalla anuncia simplemente como "botón".&lt;/p&gt;

&lt;p&gt;Para esa categoría específica, &lt;code&gt;aria-label&lt;/code&gt; existe por una razón real. Un control de solo icono —un glifo de papelera sin texto visible— no le da al navegador nada a partir de lo cual calcular un nombre. No hay contenido para que el cálculo del nombre accesible lea. &lt;code&gt;aria-label&lt;/code&gt; llena un espacio que de otro modo permanecería vacío.&lt;/p&gt;

&lt;p&gt;El problema comienza cuando la misma lista de verificación que señala correctamente un botón de icono sin etiqueta también señala un &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; que ya tiene texto visible, y la solución aplicada es idéntica en ambos casos: agregar un &lt;code&gt;aria-label&lt;/code&gt;. Uno de esos elementos no tenía nada con qué trabajar. El otro ya tenía un nombre. La lista de verificación no distingue entre los dos casos porque, desde la perspectiva del escáner, ambos solo necesitaban un &lt;code&gt;aria-label&lt;/code&gt; para dejar de ser señalados.&lt;/p&gt;

&lt;h4&gt;
  
  
  El primer principio
&lt;/h4&gt;

&lt;p&gt;El navegador no espera a que ARIA le diga qué es algo. Los elementos HTML nativos ya llevan funciones (&lt;em&gt;roles&lt;/em&gt;) y comportamientos implícitos: un &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; es un botón para el árbol de accesibilidad antes de que cualquier atributo lo toque, de la misma manera que ya es ejecutable mediante clic y enfocable antes de que cualquier JavaScript lo toque. El árbol de accesibilidad se deriva del propio HTML, el mismo documento que el navegador ya está procesando para todo lo demás.&lt;/p&gt;

&lt;p&gt;Los nombres accesibles siguen su propia secuencia definida, verificada en orden: primero &lt;code&gt;aria-labelledby&lt;/code&gt;, luego &lt;code&gt;aria-label&lt;/code&gt;, después el propio contenido del elemento, y finalmente los fallbacks restantes. Un &lt;code&gt;&amp;lt;button&amp;gt;Close menu&amp;lt;/button&amp;gt;&lt;/code&gt; obtiene su nombre del tercer paso de esa lista, su contenido, porque no hay nada anterior en la secuencia que lo reclame primero. En el momento en que se agrega un &lt;code&gt;aria-label&lt;/code&gt;, ese paso anterior gana en su lugar, y el contenido que un usuario vidente realmente lee nunca se llega a alcanzar.&lt;/p&gt;

&lt;p&gt;ARIA no es una capa de accesibilidad separada sobre el DOM. Es un conjunto de entradas que el mismo cálculo ya ejecuta, uno que resulta tener prioridad sobre el contenido cuando está presente, independientemente de si el contenido ya estaba diciendo lo correcto.&lt;/p&gt;

&lt;h4&gt;
  
  
  Demostrando el principio
&lt;/h4&gt;

&lt;p&gt;Un control compuesto solo por un icono, sin nada más que le dé nombre:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Delete item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;&amp;lt;!-- trash can icon --&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nada aquí entra en conflicto con nada. El &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; está oculto del árbol de accesibilidad, no hay contenido de texto para que el cálculo lo encuentre, y &lt;code&gt;aria-label&lt;/code&gt; proporciona el único nombre disponible. Este es el caso para el que se creó ARIA.&lt;/p&gt;

&lt;p&gt;Ahora el mismo atributo, en un control que ya tenía contenido:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Dismiss navigation panel"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Close menu
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visualmente, nada cambió. Un usuario vidente todavía lee "Close menu". Programáticamente, el nombre accesible ahora es "Dismiss navigation panel". El contenido no desapareció del DOM. Simplemente dejó de ser lo que alcanza el cálculo del nombre, porque &lt;code&gt;aria-label&lt;/code&gt; se sitúa antes en la secuencia y el cálculo se detiene en la primera coincidencia.&lt;/p&gt;

&lt;p&gt;Mismo atributo. Misma sintaxis. Uno llena un espacio vacío. El otro reemplaza algo que ya era correcto.&lt;/p&gt;

&lt;h4&gt;
  
  
  El punto de dolor
&lt;/h4&gt;

&lt;p&gt;Esto se envió a producción en un control de navegación global: un &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; nativo con texto visible que decía "Close menu". Una revisión de accesibilidad lo señaló durante una fase de cumplimiento, no porque careciera de nombre, sino porque la lista de verificación utilizada exigía un &lt;code&gt;aria-label&lt;/code&gt; explícito en cada control interactivo, independientemente de si ya había uno disponible en el contenido. La etiqueta agregada fue "Dismiss navigation panel", escrita para ser más descriptiva que el propio texto corto del botón.&lt;/p&gt;

&lt;p&gt;El control visible y el nombre programático ya no describían la misma cosa. Un usuario vidente lee "Close menu". Un lector de pantalla anuncia "Dismiss navigation panel". El software de control por voz que escucha la palabra que un usuario realmente puede ver —el mecanismo que el criterio &lt;em&gt;Label in Name&lt;/em&gt; de las WCAG existe para proteger— no tiene nada con qué coincidir. El usuario dice "hacer clic en cerrar menú" a un control que, en lo que respecta al árbol de accesibilidad, ya no se llama así.&lt;/p&gt;

&lt;p&gt;La misma revisión también agregó &lt;code&gt;role="button"&lt;/code&gt; a un &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; en otra parte de la misma navegación, reemplazando a un segundo control que el equipo quería que se viera igual. El &lt;em&gt;role&lt;/em&gt; es una declaración, no una transferencia. &lt;code&gt;&amp;lt;div role="button"&amp;gt;&lt;/code&gt; le dice al árbol de accesibilidad que anuncie el elemento como un botón. No le otorga a ese &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; el manejo nativo de teclado de un botón, su comportamiento de enfoque predeterminado ni su activación integrada con Enter y Espacio, cosas que el &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; ya proporciona sin tener que pedírselo. El &lt;code&gt;div&lt;/code&gt; se anuncia correctamente y no hace casi nada correctamente una vez que el teclado lo alcanza.&lt;/p&gt;

&lt;p&gt;El &lt;em&gt;Notas desde el Pase&lt;/em&gt; del sábado elimina el &lt;code&gt;aria-label&lt;/code&gt; del control de navegación y observa cómo el nombre accesible vuelve a ser "Close menu" por sí solo, y luego verifica qué le falta realmente al &lt;code&gt;&amp;lt;div role="button"&amp;gt;&lt;/code&gt; frente a lo que un &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; nativo habría proporcionado gratis.&lt;/p&gt;

&lt;h4&gt;
  
  
  La lección más amplia
&lt;/h4&gt;

&lt;p&gt;Este no es un argumento contra &lt;code&gt;aria-label&lt;/code&gt;, ni contra ARIA en general. El botón compuesto solo por un icono al principio de este artículo lo necesitaba; no había nada más que leer. El fallo aquí no fue usar el atributo. Fue usarlo sin verificar si el navegador ya tenía una respuesta.&lt;/p&gt;

&lt;p&gt;Cada anotación agregada a un documento es una afirmación sobre lo que la plataforma aún no sabe. A veces esa afirmación es correcta: un icono sin texto realmente no tiene nada con qué nombrarse a sí mismo. A veces no lo es, y la anotación sobrescribe algo que el navegador ya estaba calculando correctamente, considerado presente en una lista de verificación de cualquier manera.&lt;/p&gt;

&lt;p&gt;ARIA es una intervención semántica. Debe suministrar lo que el documento nativo no expresa por sí mismo, no reemplazar lo que sí expresa. La pregunta que vale la pena hacerse antes de agregar algo de esto no es si el control necesita más información de accesibilidad. Es si el navegador ya tiene la información y simplemente no se le ha preguntado aún.&lt;/p&gt;

</description>
      <category>html</category>
      <category>a11y</category>
      <category>desarrolloweb</category>
      <category>arquitectura</category>
    </item>
    <item>
      <title>The Browser Already Has an Accessibility Architecture. Most Projects Ignore It.</title>
      <dc:creator>Franklin</dc:creator>
      <pubDate>Thu, 01 Oct 2026 19:26:26 +0000</pubDate>
      <link>https://dev.to/ortizfranklindev/the-browser-already-has-an-accessibility-architecture-most-projects-ignore-it-3bgk</link>
      <guid>https://dev.to/ortizfranklindev/the-browser-already-has-an-accessibility-architecture-most-projects-ignore-it-3bgk</guid>
      <description>&lt;p&gt;&lt;em&gt;Structural sibling to &lt;a href="https://dev.to/ortizfranklindev/the-browser-already-has-an-architecture-most-projects-ignore-it-2bap"&gt;The Browser Already Has an Architecture. Most Projects Ignore It.&lt;/a&gt; — same distinction, different layer.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Also available in &lt;a href="https://dev.to/ortizfranklindev/el-navegador-ya-cuenta-con-una-arquitectura-de-accesibilidad-la-mayoria-de-los-proyectos-la-17ch"&gt;Español&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;An accessibility audit comes back clean. Every interactive control has a role. Every icon has a label. The automated scanner shows a passing score, and the team moves on.&lt;/p&gt;

&lt;p&gt;Then a screen reader user tries to close a navigation panel. They hear "Dismiss navigation panel" announced on an element that visibly says "Close menu." Nothing crashes. The control still works; pressing Enter still closes the panel. But the user who was told to listen for "Close" heard something else entirely, and had to guess.&lt;/p&gt;

&lt;p&gt;Nobody removed a label. Someone added one, specifically to make the scanner stop flagging the control as unlabeled. The audit got quieter. The control got harder to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the Problem Exists
&lt;/h2&gt;

&lt;p&gt;Accessibility tooling exists because accessibility failures are otherwise invisible to most of the people building a product. A missing label, a role nobody set, a focus order that traps a keyboard user: none of it shows up in a visual QA pass. Automated scanners and compliance checklists emerged to catch exactly that blind spot, and they catch a real category of failure: an icon-only button that announces as nothing but "button" to a screen reader.&lt;/p&gt;

&lt;p&gt;For exactly that category, &lt;code&gt;aria-label&lt;/code&gt; exists for a real reason. An icon-only control — a trash can glyph with no visible text — gives the browser nothing to compute a name from. There's no content for the accessible name computation to read. &lt;code&gt;aria-label&lt;/code&gt; fills a gap that would otherwise sit empty.&lt;/p&gt;

&lt;p&gt;The trouble starts when the same checklist that correctly flags an unlabeled icon button also flags a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; that already has visible text, and the fix applied is identical either way: add an &lt;code&gt;aria-label&lt;/code&gt;. One of those elements had nothing to work with. The other already had a name. The checklist doesn't distinguish between the two cases, because from the scanner's perspective, both just needed an &lt;code&gt;aria-label&lt;/code&gt; to stop being flagged.&lt;/p&gt;

&lt;h2&gt;
  
  
  The First Principle
&lt;/h2&gt;

&lt;p&gt;The browser doesn't wait for ARIA to tell it what something is. Native HTML elements already carry implicit roles and behavior — a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; is a button to the accessibility tree before any attribute touches it, the same way it's already clickable and focusable before any JavaScript touches it. The accessibility tree is derived from the HTML itself, the same document the browser is already parsing for everything else.&lt;/p&gt;

&lt;p&gt;Accessible names follow their own defined sequence, checked in order: &lt;code&gt;aria-labelledby&lt;/code&gt; first, then &lt;code&gt;aria-label&lt;/code&gt;, then the element's own content, then a few remaining fallbacks. A &lt;code&gt;&amp;lt;button&amp;gt;Close menu&amp;lt;/button&amp;gt;&lt;/code&gt; gets its name from the third step down that list, its content, because nothing earlier in the sequence is present to claim it first. The moment an &lt;code&gt;aria-label&lt;/code&gt; is added, that earlier step wins instead, and the content a sighted user actually reads never gets reached.&lt;/p&gt;

&lt;p&gt;ARIA isn't a separate accessibility layer sitting on top of the DOM. It's a set of inputs the same computation already runs, one that happens to take precedence over content when it's present, whether or not content was already saying the right thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demonstrating the Principle
&lt;/h2&gt;

&lt;p&gt;An icon-only control, with nothing else to name it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Delete item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;&amp;lt;!-- trash can icon --&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing here conflicts with anything. The &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; is hidden from the accessibility tree, there's no text content for the computation to find, and &lt;code&gt;aria-label&lt;/code&gt; supplies the only name available. This is the case ARIA was built for.&lt;/p&gt;

&lt;p&gt;Now the same attribute, on a control that already had content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Dismiss navigation panel"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Close menu
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visually, nothing changed. A sighted user still reads "Close menu." Programmatically, the accessible name is now "Dismiss navigation panel." The content didn't disappear from the DOM. It simply stopped being the thing the name computation reaches, because &lt;code&gt;aria-label&lt;/code&gt; sits earlier in the sequence and the computation stops at the first match.&lt;/p&gt;

&lt;p&gt;Same attribute. Same syntax. One fills a gap. The other replaces something that was already correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pain Point
&lt;/h2&gt;

&lt;p&gt;This shipped on a global navigation control: a native &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; with visible text reading "Close menu." An accessibility review flagged it during a compliance pass, not because it lacked a name, but because the checklist in use called for an explicit &lt;code&gt;aria-label&lt;/code&gt; on every interactive control, regardless of whether one was already available from content. The label added was "Dismiss navigation panel," written to be more descriptive than the button's own short text.&lt;/p&gt;

&lt;p&gt;The visible control and the programmatic name no longer described the same thing. A sighted user reads "Close menu." A screen reader announces "Dismiss navigation panel." Voice-control software listening for the word a user can actually see — the mechanism WCAG's Label in Name criterion exists to protect — has nothing to match. The user says "click close menu" to a control that, as far as the accessibility tree is concerned, isn't named that anymore.&lt;/p&gt;

&lt;p&gt;The same review also added &lt;code&gt;role="button"&lt;/code&gt; to a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; elsewhere in the same navigation, standing in for a second control the team wanted to look the same. The role is a declaration, not a transfer. &lt;code&gt;&amp;lt;div role="button"&amp;gt;&lt;/code&gt; tells the accessibility tree to announce the element as a button. It doesn't give that &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; a button's native keyboard handling, its default focus behavior, or its built-in activation on Enter and Space, all of which &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; already provides without being asked. The div announces correctly and does almost nothing correctly once a keyboard reaches it.&lt;/p&gt;

&lt;p&gt;Saturday's Notes from the Pass strips the &lt;code&gt;aria-label&lt;/code&gt; back off the navigation control and watches the accessible name return to "Close menu" on its own, then checks what the &lt;code&gt;&amp;lt;div role="button"&amp;gt;&lt;/code&gt; is actually missing against what a native &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; would have supplied for free.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Broader Lesson
&lt;/h2&gt;

&lt;p&gt;This isn't a case against &lt;code&gt;aria-label&lt;/code&gt;, or against ARIA generally. The icon-only button earlier in this piece needed it; there was nothing else to read. The failure here wasn't using the attribute. It was using it without checking whether the browser already had an answer.&lt;/p&gt;

&lt;p&gt;Every annotation layered onto a document is a claim about what the platform doesn't already know. Sometimes that claim is correct — an icon with no text genuinely has nothing to name itself with. Sometimes it isn't, and the annotation overwrites something the browser was already computing correctly, counted as present on a checklist either way.&lt;/p&gt;

&lt;p&gt;ARIA is a semantic intervention. It should supply what the native document doesn't already express, not replace what it does. The question worth asking before adding any of it isn't whether the control needs more accessibility information. It's whether the browser already has the information, and just hasn't been asked yet.&lt;/p&gt;

</description>
      <category>html</category>
      <category>a11y</category>
      <category>webdev</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Los popovers nativos no reemplazaron a las bibliotecas de JS; reemplazaron al código de unión.</title>
      <dc:creator>Franklin</dc:creator>
      <pubDate>Tue, 29 Sep 2026 11:16:28 +0000</pubDate>
      <link>https://dev.to/ortizfranklindev/los-popovers-nativos-no-reemplazaron-a-las-bibliotecas-de-js-reemplazaron-al-codigo-de-union-3gjh</link>
      <guid>https://dev.to/ortizfranklindev/los-popovers-nativos-no-reemplazaron-a-las-bibliotecas-de-js-reemplazaron-al-codigo-de-union-3gjh</guid>
      <description>&lt;p&gt;&lt;em&gt;Hermano estructural de &lt;a href="https://dev.to/ortizfranklindev/las-capas-de-cascada-no-reemplazaron-la-especificidad-reemplazaron-la-negociacion-6dg"&gt;Los Cascade Layers no reemplazaron la especificidad — Reemplazaron la negociación:&lt;/a&gt; la misma distinción, en una capa diferente.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;También disponible en &lt;a href="https://dev.to/ortizfranklindev/native-popovers-didnt-replace-js-libraries-they-replaced-glue-code-49m1"&gt;Inglés&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  El problema
&lt;/h2&gt;

&lt;p&gt;Llega un &lt;em&gt;ticket&lt;/em&gt; de soporte con una captura de pantalla: un &lt;em&gt;data grid&lt;/em&gt;, docenas de filas, y un menú de acciones —renombrar, archivar, eliminar— abierto en el lugar equivocado. No por unos pocos píxeles. Está anclado a la última fila de la tabla, sin importar en el botón de qué fila se hizo clic. Clic en el menú de la fila dos, posición de la fila cuarenta. Clic en la fila veinte, el mismo resultado.&lt;/p&gt;

&lt;p&gt;Nadie escribió un &lt;em&gt;bug&lt;/em&gt; que dijera "usa siempre la última fila". El equipo ya había hecho la parte más difícil y moderna correctamente: atributos &lt;code&gt;popover&lt;/code&gt; nativos en lugar de un menú desplegable administrado por JavaScript, &lt;em&gt;CSS anchor positioning&lt;/em&gt; en lugar de coordenadas calculadas manualmente. El menú está exactamente donde el código le dijo que estuviera. El código simplemente no declaró la relación que todos asumieron que contenía.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por qué existe el problema
&lt;/h2&gt;

&lt;p&gt;Antes de que el navegador proporcionara un &lt;em&gt;top layer&lt;/em&gt; de propósito general para este tipo de superposición (&lt;em&gt;overlay&lt;/em&gt;), un elemento anclado a algo enterrado dentro de un contenedor con desplazamiento (&lt;em&gt;scrollable&lt;/em&gt;) o recortado (&lt;em&gt;clipped&lt;/em&gt;) tenía un problema real: podía quedar cortado, oculto detrás de otra cosa o atrapado dentro de una región de desplazamiento que no coincidía con el resto de la página. La solución habitual era estructural. Renderizar la superposición en otro lugar, adjunta al final de &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; o donde viviera un contenedor &lt;em&gt;portal&lt;/em&gt; compartido, de modo que su posición en el DOM ya no coincidiera con su posición en la interfaz. JavaScript se encargaba de todo lo que el DOM ya no expresaba por sí mismo: mostrarlo, ocultarlo, calcular dónde debía ubicarse en relación con lo que lo activaba, y mantener esa posición actualizada a medida que la página se desplazaba o cambiaba de tamaño.&lt;/p&gt;

&lt;p&gt;Eso no era un truco para corregir un error. Era una respuesta correcta a un navegador que realmente carecía de una forma de propósito general para que este tipo de superposición escapara al recorte de sus ancestros. La superposición tenía que vivir separada de su disparador (&lt;em&gt;trigger&lt;/em&gt;), porque vivir cerca de él era lo que causaba el problema.&lt;/p&gt;

&lt;h2&gt;
  
  
  El primer principio
&lt;/h2&gt;

&lt;p&gt;La plataforma ahora separa varias responsabilidades que las implementaciones de superposición más antiguas tenían que reconstruir en JavaScript. El atributo &lt;code&gt;popover&lt;/code&gt;, combinado con &lt;code&gt;popovertarget&lt;/code&gt; en un control, le da al navegador una relación declarativa entre el control y el &lt;code&gt;popover&lt;/code&gt;. Al abrirse, el &lt;code&gt;popover&lt;/code&gt; entra al &lt;em&gt;top layer&lt;/em&gt; del navegador, fuera de las restricciones habituales de recorte y apilamiento de sus ancestros, y esa relación con el activador (&lt;em&gt;invoker&lt;/em&gt;) también proporciona una referencia de &lt;em&gt;anchor&lt;/em&gt; implícita.&lt;/p&gt;

&lt;p&gt;El &lt;em&gt;CSS anchor positioning&lt;/em&gt; maneja el enlace espacial de forma independiente: un elemento posicionado se puede ubicar en relación con un &lt;em&gt;anchor&lt;/em&gt; sin que JavaScript calcule las coordenadas.&lt;/p&gt;

&lt;p&gt;El límite importante aparece cuando un componente necesita un &lt;em&gt;anchor&lt;/em&gt; explícito en lugar del propio control activador del &lt;code&gt;popover&lt;/code&gt;. Los nombres de &lt;em&gt;anchor&lt;/em&gt; explícitos no se limitan automáticamente al subárbol DOM (&lt;em&gt;subtree&lt;/em&gt;) de un componente. Cuando múltiples &lt;em&gt;anchors&lt;/em&gt; exponen el mismo nombre, un elemento posicionado mediante &lt;em&gt;anchor&lt;/em&gt; puede resolverse con respecto al último &lt;em&gt;anchor&lt;/em&gt; coincidente en el orden del código fuente.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;anchor-scope&lt;/code&gt; cambia esa relación al restringir explícitamente dónde se puede resolver un &lt;em&gt;anchor&lt;/em&gt; con nombre.&lt;/p&gt;

&lt;p&gt;Por lo tanto, el navegador puede asumir el límite de la superposición y la mecánica de posicionamiento sin eliminar la responsabilidad arquitectónica. HTML establece la relación de interacción. CSS establece la relación espacial y su alcance (&lt;em&gt;scope&lt;/em&gt;). La pregunta restante es si el DOM realmente declara el límite del que depende el componente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demostrando el principio
&lt;/h2&gt;

&lt;p&gt;Redúcelo a dos elementos repetidos, cada uno con un &lt;em&gt;anchor&lt;/em&gt; y un hermano posicionado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item-wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"thumbnail"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"badge"&lt;/span&gt; &lt;span class="na"&gt;popover&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item-wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"thumbnail"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"badge"&lt;/span&gt; &lt;span class="na"&gt;popover&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.thumbnail&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;anchor-name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--thumb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.badge&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;position-anchor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--thumb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;position-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;top&lt;/span&gt; &lt;span class="nb"&gt;right&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;.thumbnail&lt;/code&gt; y &lt;code&gt;.badge&lt;/code&gt; son hermanos dentro de cada &lt;code&gt;.item-wrapper&lt;/code&gt;. Comparten el mismo nombre de &lt;em&gt;anchor&lt;/em&gt; explícito, pero ese nombre no se limita automáticamente al subárbol del &lt;em&gt;wrapper&lt;/em&gt;. Sin un &lt;em&gt;anchor scope&lt;/em&gt;, los nombres &lt;code&gt;--thumb&lt;/code&gt; repetidos permanecen visibles para los elementos posicionados en cualquier otra parte del documento. Cuando múltiples &lt;em&gt;anchors&lt;/em&gt; comparten ese nombre, el elemento posicionado se resuelve con respecto al último &lt;em&gt;anchor&lt;/em&gt; coincidente en el orden del código fuente.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.item-wrapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;anchor-scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--thumb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Una sola línea establece el límite faltante. Ahora cada &lt;code&gt;.badge&lt;/code&gt; puede resolver &lt;code&gt;--thumb&lt;/code&gt; solo dentro de su propio &lt;code&gt;.item-wrapper&lt;/code&gt;, por lo que el nombre de &lt;em&gt;anchor&lt;/em&gt; idéntico ya no es ambiguo entre los componentes repetidos.&lt;/p&gt;

&lt;p&gt;La parte importante no es que el navegador haya aprendido de repente qué es un componente. La parte importante es que el componente finalmente declaró el límite del que dependía la relación del &lt;em&gt;anchor&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  El punto de dolor
&lt;/h2&gt;

&lt;p&gt;Esto surgió en un &lt;em&gt;data grid&lt;/em&gt; en producción: filas repetidas, cada una con un botón de acción al final que abre un menú. El menú debía alinearse con el propio borde derecho de la fila, de forma consistente, independientemente de dónde se ubicara exactamente el botón dentro de la fila. Eso hizo que la opción más simple de la plataforma fuera insuficiente: el &lt;em&gt;anchor&lt;/em&gt; implícito de un &lt;code&gt;popover&lt;/code&gt; sigue a su propio control activador, pero el control aquí era un botón pequeño, no la fila en sí. Anclar a la fila significaba nombrarla explícitamente, lo cual fue una decisión razonable y deliberada. Los &lt;code&gt;popovers&lt;/code&gt; asociados con un activador reciben una referencia de &lt;em&gt;anchor&lt;/em&gt; implícita a ese activador; una asociación de &lt;em&gt;anchor&lt;/em&gt; explícita es un mecanismo separado.&lt;/p&gt;

&lt;p&gt;Lo que no se reconsideró fue dónde vivía el menú en el marcado (&lt;em&gt;markup&lt;/em&gt;). Antes de que el equipo adoptara el atributo &lt;code&gt;popover&lt;/code&gt; nativo, sus componentes de superposición siempre se renderizaban como hermanos de lo que los activaba, un nivel más arriba en el DOM: un hábito estructural de cuando una superposición anidada cerca de una fila corría el riesgo de ser recortada por el propio &lt;code&gt;overflow: hidden&lt;/code&gt; de esa fila. El comportamiento de &lt;em&gt;top layer&lt;/em&gt; del atributo nativo hizo que ese riesgo desapareciera. La ubicación como hermano nunca se volvió a examinar, porque nada en el cambio a &lt;code&gt;popover&lt;/code&gt; parecía requerirlo. Los &lt;code&gt;popovers&lt;/code&gt; abiertos se colocan en el &lt;em&gt;top layer&lt;/em&gt; y no se ven influenciados por el estilo de posición ni por el &lt;code&gt;overflow&lt;/code&gt; de sus ancestros.&lt;/p&gt;

&lt;p&gt;Cada fila compartía el mismo nombre de &lt;em&gt;anchor&lt;/em&gt;, definido a través de la misma clase que cada fila ya usaba para su diseño (&lt;em&gt;layout&lt;/em&gt;). El menú de ninguna fila estaba dentro de la fila a la que pertenecía, y el nombre de &lt;em&gt;anchor&lt;/em&gt; explícito no estaba limitado al subárbol de esa fila.&lt;/p&gt;

&lt;p&gt;Sin ese límite presente, el nombre de &lt;em&gt;anchor&lt;/em&gt; repetido permaneció visible en todo el &lt;em&gt;grid&lt;/em&gt;. La resolución cayó hasta el último &lt;em&gt;anchor&lt;/em&gt; coincidente en el documento. Cada menú, en cada fila, apuntaba al mismo lugar, porque el DOM no tenía un &lt;em&gt;scope&lt;/em&gt; declarado para la relación.&lt;/p&gt;

&lt;p&gt;La solución no tocó ni el nombre del &lt;em&gt;anchor&lt;/em&gt; ni el botón. Envolvió cada fila y su propio menú en un contenedor compartido, el ancestro más pequeño que contenía a ambos, y limitó el nombre del &lt;em&gt;anchor&lt;/em&gt; a ese contenedor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.row-wrapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;anchor-scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--row-anchor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El &lt;em&gt;Notas desde el Pase&lt;/em&gt; del sábado reproduce el menú fuera de lugar exactamente como se envió a producción, confirma por qué el navegador no tenía nada que preferir y muestra la solución con &lt;em&gt;scope&lt;/em&gt; manteniéndose en cada fila del &lt;em&gt;grid&lt;/em&gt;, no solo en la que se estaba probando.&lt;/p&gt;

&lt;h2&gt;
  
  
  La lección más amplia
&lt;/h2&gt;

&lt;p&gt;Esta no es una historia sobre &lt;em&gt;CSS anchor positioning&lt;/em&gt; estando incompleto. El anclaje implícito cubre el caso ordinario —un &lt;code&gt;popover&lt;/code&gt; vinculado a su propio activador— sin pedir una sola línea de nombres o de &lt;em&gt;scoping&lt;/em&gt;. El trabajo arquitectónico adicional aparece cuando un requisito real sale de ese caso. La plataforma puede establecer la relación de superposición, pero una relación de &lt;em&gt;anchor&lt;/em&gt; explícita aún necesita un límite explícito.&lt;/p&gt;

&lt;p&gt;Aquí, el problema no era que la plataforma no pudiera posicionar el menú. Era que el DOM conservaba una estructura de la implementación previa a &lt;code&gt;popover&lt;/code&gt; sin declarar el límite de componente que la nueva relación de posicionamiento requería.&lt;/p&gt;

&lt;p&gt;El Artículo 4 encontró la misma forma en el código de la aplicación: lógica que seguía administrando una responsabilidad que el navegador ya había asumido. Este es ese mismo patrón apareciendo en la estructura en lugar del comportamiento. Una forma de DOM construida para resolver un problema no deja de existir automáticamente una vez que el problema que resolvió desaparece. Se queda ahí, correcta por hábito, hasta que algo construido sobre un supuesto más nuevo choca directamente contra ella.&lt;/p&gt;

&lt;p&gt;Adoptar una característica de la plataforma no es solo cuestión de cambiar un &lt;em&gt;script&lt;/em&gt; por un atributo. Es una invitación a preguntar qué partes de la estructura circundante se construyeron para compensar una limitación que ese atributo acaba de eliminar, y cuáles de esas partes aún deben estar allí, ahora declaradas a propósito en lugar de heredadas por accidente.&lt;/p&gt;

</description>
      <category>html</category>
      <category>a11y</category>
      <category>desarrolloweb</category>
      <category>arquitectura</category>
    </item>
    <item>
      <title>Native Popovers Didn't Replace JS Libraries — They Replaced Glue Code</title>
      <dc:creator>Franklin</dc:creator>
      <pubDate>Tue, 29 Sep 2026 11:02:49 +0000</pubDate>
      <link>https://dev.to/ortizfranklindev/native-popovers-didnt-replace-js-libraries-they-replaced-glue-code-49m1</link>
      <guid>https://dev.to/ortizfranklindev/native-popovers-didnt-replace-js-libraries-they-replaced-glue-code-49m1</guid>
      <description>&lt;p&gt;&lt;em&gt;Structural sibling to &lt;a href="https://dev.to/ortizfranklindev/cascade-layers-didnt-replace-specificity-they-replaced-negotiation-4j8b"&gt;Cascade Layers Didn't Replace Specificity — They Replaced Negotiation&lt;/a&gt; — same distinction, different layer.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Also available in &lt;a href="https://dev.to/ortizfranklindev/los-popovers-nativos-no-reemplazaron-a-las-bibliotecas-de-js-reemplazaron-al-codigo-de-union-3gjh"&gt;Español&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;A support ticket comes in with a screenshot: a data grid, dozens of rows, and an action menu — rename, archive, delete — hanging open in the wrong place. Not off by a few pixels. It's anchored against the last row in the table, regardless of which row's own button was clicked. Click row two's menu, row forty's position. Click row twenty's, same result.&lt;/p&gt;

&lt;p&gt;Nobody wrote a bug that says "always use the last row." The team had already done the harder, more modern part correctly: native &lt;code&gt;popover&lt;/code&gt; attributes instead of a JavaScript-managed dropdown, CSS anchor positioning instead of manually computed coordinates. The menu is exactly where the code told it to be. The code just didn't declare the relationship everyone assumed it contained.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the Problem Exists
&lt;/h2&gt;

&lt;p&gt;Before the browser provided a general-purpose top layer for this kind of overlay, an element anchored to something buried inside a scrollable or clipped container had a real problem: it could be cut off, hidden behind something else, or trapped inside a scroll region that didn't match the rest of the page. The common fix was structural. Render the overlay somewhere else, appended to the end of &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; or wherever a shared "portal" container lived, so its position in the DOM no longer matched its position in the interface. JavaScript then handled everything the DOM no longer expressed on its own — showing it, hiding it, computing where it should sit relative to whatever triggered it, keeping that position updated as the page scrolled or resized.&lt;/p&gt;

&lt;p&gt;That wasn't a workaround for a mistake. It was a correct response to a browser that genuinely lacked a general-purpose way for this kind of overlay to escape ancestor clipping. The overlay had to live apart from its trigger, because living close to it was the thing causing the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The First Principle
&lt;/h2&gt;

&lt;p&gt;The platform now separates several responsibilities that older overlay implementations had to reconstruct in JavaScript. The &lt;code&gt;popover&lt;/code&gt; attribute, paired with &lt;code&gt;popovertarget&lt;/code&gt; on a control, gives the browser a declarative relationship between the control and the popover. When opened, the popover enters the browser's top layer, outside the normal clipping and stacking constraints of its ancestors, and that invoker relationship also provides an implicit anchor reference.&lt;/p&gt;

&lt;p&gt;CSS anchor positioning handles spatial tethering separately: a positioned element can be placed relative to an anchor without JavaScript calculating coordinates.&lt;/p&gt;

&lt;p&gt;The important boundary appears when a component needs an explicit anchor rather than the popover's own invoking control. Explicit anchor names are not automatically scoped to a component's DOM subtree. When multiple anchors expose the same name, an anchor-positioned element can resolve against the last matching anchor in source order.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;anchor-scope&lt;/code&gt; changes that relationship by explicitly restricting where a named anchor can be resolved.&lt;/p&gt;

&lt;p&gt;The browser can therefore own the overlay boundary and the positioning mechanics without removing architectural responsibility. HTML establishes the interaction relationship. CSS establishes the spatial relationship and its scope. The remaining question is whether the DOM actually declares the boundary the component depends on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demonstrating the Principle
&lt;/h2&gt;

&lt;p&gt;Reduce it to two repeated items, each with an anchor and a positioned sibling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item-wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"thumbnail"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"badge"&lt;/span&gt; &lt;span class="na"&gt;popover&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item-wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"item"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"thumbnail"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"badge"&lt;/span&gt; &lt;span class="na"&gt;popover&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.thumbnail&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;anchor-name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--thumb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.badge&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;position-anchor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--thumb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;position-area&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;top&lt;/span&gt; &lt;span class="nb"&gt;right&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;.thumbnail&lt;/code&gt; and &lt;code&gt;.badge&lt;/code&gt; are siblings inside each &lt;code&gt;.item-wrapper&lt;/code&gt;. They share the same explicit anchor name, but that name is not automatically limited to the wrapper's subtree. Without an anchor scope, the repeated &lt;code&gt;--thumb&lt;/code&gt; names remain visible to positioned elements elsewhere in the document. When multiple anchors share that name, the positioned element resolves against the last matching anchor in source order.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.item-wrapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="py"&gt;anchor-scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--thumb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One line establishes the missing boundary. Each &lt;code&gt;.badge&lt;/code&gt; can now resolve &lt;code&gt;--thumb&lt;/code&gt; only within its own &lt;code&gt;.item-wrapper&lt;/code&gt;, so the identical anchor name is no longer ambiguous across the repeated components.&lt;/p&gt;

&lt;p&gt;The important part isn't that the browser suddenly learned what a component is. The important part is that the component finally declared the boundary the anchor relationship depended on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pain Point
&lt;/h2&gt;

&lt;p&gt;This surfaced in a production data grid: repeating rows, each with a trailing action button opening a menu. The menu needed to align to the row's own right edge, consistently, regardless of exactly where the button sat inside the row. That made the platform's simplest option insufficient: a popover's implicit anchor follows its own invoking control, but the control here was a small button, not the row itself. Anchoring to the row meant naming it explicitly, which was a reasonable, deliberate call. Popovers associated with an invoker receive an implicit anchor reference to that invoker; an explicit anchor association is a separate mechanism.&lt;/p&gt;

&lt;p&gt;What wasn't reconsidered was where the menu itself lived in the markup. Before the team adopted the native &lt;code&gt;popover&lt;/code&gt; attribute, their overlay components had always rendered as a sibling to whatever triggered them, one level up in the DOM — a structural habit from when an overlay nested near a row risked being clipped by that row's own &lt;code&gt;overflow: hidden&lt;/code&gt;. The native attribute's top-layer behavior made that risk disappear. The sibling placement never got revisited, because nothing about switching to &lt;code&gt;popover&lt;/code&gt; seemed to require it. Open popovers are placed in the top layer and aren't influenced by ancestor &lt;code&gt;overflow&lt;/code&gt; or &lt;code&gt;position&lt;/code&gt; styling.&lt;/p&gt;

&lt;p&gt;Every row shared the same anchor name, set through the same class every row already used for its layout. No row's menu sat inside the row it belonged to, and the explicit anchor name wasn't scoped to that row's subtree.&lt;/p&gt;

&lt;p&gt;With that boundary absent, the repeated anchor name remained visible across the grid. Resolution fell through to the last matching anchor in the document. Every menu, on every row, pointed at the same place, because the DOM had no declared scope for the relationship.&lt;/p&gt;

&lt;p&gt;The fix didn't touch the anchor name or the button. It wrapped each row and its own menu in a shared container, the smallest ancestor holding both, and scoped the anchor name to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.row-wrapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="py"&gt;anchor-scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;--row-anchor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Saturday's &lt;em&gt;Notes from the Pass&lt;/em&gt; reproduces the misplaced menu exactly as it shipped, confirms why the browser had nothing to prefer, and shows the scoped fix holding across every row in the grid, not just the one being tested.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Broader Lesson
&lt;/h2&gt;

&lt;p&gt;This isn't a story about CSS anchor positioning being incomplete. Implicit anchoring covers the ordinary case — a popover tethered to its own trigger — without asking for a single line of naming or scoping. The additional architectural work appears when a real requirement steps outside that case. The platform can establish the overlay relationship, but an explicit anchor relationship still needs an explicit boundary.&lt;/p&gt;

&lt;p&gt;Here, the problem wasn't that the platform couldn't position the menu. It was that the DOM retained a structure from the pre-popover implementation without declaring the component boundary the new positioning relationship required.&lt;/p&gt;

&lt;p&gt;Article 4 found the same shape in application code: logic that kept managing a responsibility the browser had already taken over. This is that pattern showing up in structure instead of behavior. A DOM shape built to solve a problem doesn't automatically stop existing once the problem it solved goes away. It sits there, correct by habit, until something built on a newer assumption runs straight into it.&lt;/p&gt;

&lt;p&gt;Adopting a platform feature isn't only a matter of swapping an attribute in for a script. It's an invitation to ask which parts of the surrounding structure were built to compensate for a limitation that attribute just removed, and which of those parts still need to be there, now declared on purpose instead of inherited by accident.&lt;/p&gt;

</description>
      <category>html</category>
      <category>webdev</category>
      <category>a11y</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Notas desde el pase: La referencia que quedó obsoleta</title>
      <dc:creator>Franklin</dc:creator>
      <pubDate>Sat, 26 Sep 2026 10:43:09 +0000</pubDate>
      <link>https://dev.to/ortizfranklindev/notas-desde-el-pase-la-referencia-que-quedo-obsoleta-1ome</link>
      <guid>https://dev.to/ortizfranklindev/notas-desde-el-pase-la-referencia-que-quedo-obsoleta-1ome</guid>
      <description>&lt;p&gt;&lt;em&gt;También disponible en &lt;a href="https://dev.to/ortizfranklindev/notes-from-the-pass-the-reference-that-went-stale-1cc4"&gt;Inglés&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  La afirmación a prueba
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/ortizfranklindev/el-html-no-necesita-mas-reinvencion-necesita-confianza-en-la-plataforma-hlp"&gt;El ensayo del jueves&lt;/a&gt; sostuvo que el navegador no solo renderiza elementos nativos: para un conjunto definido de ellos, también implementa y mantiene el contrato de interacción correspondiente, y el código de la aplicación que reimplementa ese mismo contrato asume una obligación que la plataforma ya estaba cumpliendo. La afirmación específica: un &lt;em&gt;focus trap&lt;/em&gt; personalizado y un &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; nativo pueden no diferir mucho en un &lt;em&gt;diff&lt;/em&gt;, pero difieren en quién es responsable cuando el contenido del modal cambia después de abrirse.&lt;/p&gt;

&lt;p&gt;Esta Nota prueba eso directamente. El mismo fallo —nuevo contenido cargándose en un modal abierto— ejecutado contra ambas implementaciones, para ver si la diferencia es real o puramente retórica.&lt;/p&gt;

&lt;h2&gt;
  
  
  El trampa anterior
&lt;/h2&gt;

&lt;p&gt;La versión personalizada enumera los elementos enfocables del modal una sola vez, al abrirse, y verifica cada pulsación de Tab contra dos de ellos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;openModal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;previouslyFocused&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is-open&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;focusable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FOCUSABLE_SELECTOR&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;focusable&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nx"&gt;last&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;focusable&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;focusable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;keydown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;trapFocus&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;trapFocus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Tab&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shiftKey&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeElement&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;last&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shiftKey&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeElement&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;last&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;first&lt;/code&gt; y &lt;code&gt;last&lt;/code&gt; no son posiciones. Son referencias a dos nodos del DOM específicos, capturados en el momento en que se abrió el modal.&lt;/p&gt;

&lt;p&gt;Ahora se resuelve una solicitud y la lista de opciones se vuelve a renderizar para mostrar la nueva elección:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;optionsList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;renderOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updatedOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;innerHTML&lt;/code&gt; no actualiza los nodos existentes. Los descarta y construye unos nuevos, incluyendo aquello a lo que apuntaba &lt;code&gt;last&lt;/code&gt;. La variable sigue conservando una referencia. El nodo al que se refiere ya no existe en ninguna parte de la página.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;document.activeElement&lt;/code&gt; nunca podrá volver a ser igual a esa referencia. La próxima vez que un usuario navegue con Tab hacia adelante desde la nueva opción final, la segunda condición de &lt;code&gt;trapFocus&lt;/code&gt; fallará en silencio. No se dispara ningún &lt;code&gt;preventDefault()&lt;/code&gt;. Nada arroja un error. El orden de tabulación nativo del navegador simplemente toma el control, llevando el foco directamente más allá del modal hacia lo que siga en el documento: una página aún atenuada por una capa superpuesta (&lt;em&gt;overlay&lt;/em&gt;) que ya no tiene ningún control real sobre hacia dónde va el foco.&lt;/p&gt;

&lt;h2&gt;
  
  
  El nuevo contrato
&lt;/h2&gt;

&lt;p&gt;Reconstruye el mismo modal sobre &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; y ejecútalo a través del mismo &lt;em&gt;re-render&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dialog&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"preferences"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"dialog"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"options"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="c"&gt;&amp;lt;!-- options render here --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;autofocus&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Save&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dialog&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;optionsContainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.options&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showModal&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// later, a request resolves&lt;/span&gt;
&lt;span class="nx"&gt;optionsContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;renderOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updatedOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;optionsContainer&lt;/code&gt; está delimitado a la lista de opciones a propósito: el &lt;em&gt;re-render&lt;/em&gt; nunca alcanza al botón Save, que es lo único en este diálogo que no se reevalúa por sí solo. El orden de enfoque secuencial (a lo que responde Tab) se calcula de nuevo en cada pulsación de tecla, razón por la cual la nueva opción se vuelve accesible sin que ningún código note que llegó. &lt;code&gt;autofocus&lt;/code&gt; no es ese tipo de mecanismo: el navegador lo busca exactamente una vez, cuando se ejecuta &lt;code&gt;showModal()&lt;/code&gt;. Un elemento agregado después de eso, incluso con &lt;code&gt;autofocus&lt;/code&gt; definido, necesita una llamada explícita a &lt;code&gt;.focus()&lt;/code&gt;; la plataforma no lo captará por sí sola como lo hace con el orden de Tab.&lt;/p&gt;

&lt;p&gt;Nada aquí almacena en caché un elemento inicial o final, por lo que nada puede quedar obsoleto. &lt;code&gt;showModal()&lt;/code&gt; no calcula una captura instantánea de los descendientes enfocables al momento de abrirse. Hace que todo lo que esté fuera del diálogo sea inerte mientras permanezca abierto, lo que significa que el espacio de búsqueda del orden de Tab del navegador se limita a lo que realmente está dentro del diálogo, evaluado de nuevo en cada pulsación de tecla. Reemplaza el contenido de la lista de opciones a mitad de apertura y la siguiente pulsación de Tab seguirá considerando solo lo que esté actualmente allí, incluida la nueva opción. No hay ninguna referencia que invalidar, porque nunca se tomó ninguna.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qué cambió de lugar, qué no
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Responsabilidad&lt;/th&gt;
&lt;th&gt;Personalizada (&lt;em&gt;Hand-rolled&lt;/em&gt;)&lt;/th&gt;
&lt;th&gt;&lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Contención del foco&lt;/td&gt;
&lt;td&gt;Referencias en caché, verificadas en cada &lt;em&gt;keydown&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Estructural: el fondo es inerte, nada que almacenar en caché&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restauración del foco al cerrar&lt;/td&gt;
&lt;td&gt;Almacenada y restaurada manualmente&lt;/td&gt;
&lt;td&gt;Automática al cerrar, independientemente de cómo se cierre&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Escape para descartar&lt;/td&gt;
&lt;td&gt;Verificación explícita de &lt;em&gt;keydown&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Automática&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fondo inerte / apilamiento (&lt;em&gt;stacking&lt;/em&gt;)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;aria-hidden&lt;/code&gt; manual, &lt;code&gt;z-index&lt;/code&gt; manual&lt;/td&gt;
&lt;td&gt;Automática: capa superior nativa (&lt;em&gt;native top layer&lt;/em&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Objetivo de foco inicial&lt;/td&gt;
&lt;td&gt;Llamada imperativa &lt;code&gt;.focus()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Declarativa: &lt;code&gt;autofocus&lt;/code&gt;, sigue siendo una decisión de autoría&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Descartar al hacer clic en el fondo&lt;/td&gt;
&lt;td&gt;Verificación explícita de clic externo&lt;/td&gt;
&lt;td&gt;Sigue siendo explícita: &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; no tiene un comportamiento predeterminado para esto&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Cuatro responsabilidades se trasladan directamente a la plataforma. Una pasa de ser una decisión en tiempo de ejecución a una declarativa: sigue siendo decisión de la aplicación, solo que más ligera de llevar. Una no cambia en absoluto: hacer clic fuera de un &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; no hace nada por sí solo, y cerrarlo de esa manera aún requiere el mismo tipo de &lt;em&gt;listener&lt;/em&gt; que necesitaba la versión personalizada.&lt;/p&gt;

&lt;h2&gt;
  
  
  Si la afirmación se sostuvo
&lt;/h2&gt;

&lt;p&gt;La afirmación del ensayo no era que &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; tenga menos errores. Era que reimplementar un contrato que la plataforma ya posee significa asumir la responsabilidad de su corrección indefinidamente, incluyendo casos que el código original nunca anticipó. Este es exactamente ese caso. El &lt;em&gt;trap&lt;/em&gt; personalizado no falló por mal código. Falló porque "verificar contra una referencia en caché" es una estrategia con un punto ciego específico y permanente, y un &lt;em&gt;re-render&lt;/em&gt; eventualmente lo iba a encontrar.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; no cierra ese punto ciego con mejor código. Lo cierra al no tener una caché en primer lugar: toda la categoría de fallo a la que estaba expuesta la versión personalizada no se aplica a un mecanismo que reevalúa la capacidad de enfoque en vivo. Esas son las cuatro filas que se trasladaron por completo. Las dos que no lo hicieron —elegir un objetivo de foco inicial y manejar el clic en el fondo— son decisiones específicas de lo que contiene un diálogo dado, no algo que la plataforma pudiera asumir en nombre de la aplicación aunque intentara hacerlo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Artículo de referencia:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://dev.to/ortizfranklindev/el-html-no-necesita-mas-reinvencion-necesita-confianza-en-la-plataforma-hlp"&gt;El HTML no necesita más reinvención, necesita confianza en la plataforma&lt;/a&gt;&lt;/p&gt;

</description>
      <category>html</category>
      <category>desarrolloweb</category>
      <category>a11y</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Notes from the Pass: The Reference That Went Stale</title>
      <dc:creator>Franklin</dc:creator>
      <pubDate>Sat, 26 Sep 2026 10:37:39 +0000</pubDate>
      <link>https://dev.to/ortizfranklindev/notes-from-the-pass-the-reference-that-went-stale-1cc4</link>
      <guid>https://dev.to/ortizfranklindev/notes-from-the-pass-the-reference-that-went-stale-1cc4</guid>
      <description>&lt;p&gt;&lt;em&gt;Also available in &lt;a href="https://dev.to/ortizfranklindev/notas-desde-el-pase-la-referencia-que-quedo-obsoleta-1ome"&gt;Español&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Claim Being Tested
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/ortizfranklindev/html-doesnt-need-more-reinvention-it-needs-platform-trust-223o"&gt;Thursday's essay&lt;/a&gt; argued that the browser doesn't only render native elements — for a defined set of them, it also implements and maintains the interaction contract that goes with them, and application code that reimplements the same contract takes on an obligation the platform was already fulfilling. The specific claim: a hand-rolled focus trap and a native &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; might not differ much in a diff, but they differ in who's on the hook when the modal's contents change after it opens.&lt;/p&gt;

&lt;p&gt;This Note tests that directly. The same failure — new content loading into an open modal — run against both implementations, to see whether the difference is real or just rhetorical.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Old Trap
&lt;/h2&gt;

&lt;p&gt;The hand-rolled version enumerates the modal's focusable elements once, at open, and checks every Tab press against two of them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;openModal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;previouslyFocused&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is-open&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;focusable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FOCUSABLE_SELECTOR&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;focusable&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nx"&gt;last&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;focusable&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;focusable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;keydown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;trapFocus&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;trapFocus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Tab&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shiftKey&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeElement&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;last&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shiftKey&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeElement&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;last&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;first&lt;/code&gt; and &lt;code&gt;last&lt;/code&gt; aren't positions. They're references to two specific DOM nodes, captured the moment the modal opened.&lt;/p&gt;

&lt;p&gt;Now a request resolves, and the options list re-renders to show the new choice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;optionsList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;renderOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updatedOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;innerHTML&lt;/code&gt; doesn't update the existing nodes. It discards them and builds new ones — including whatever &lt;code&gt;last&lt;/code&gt; was pointing at. The variable still holds a reference. The node it refers to no longer exists anywhere in the page.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;document.activeElement&lt;/code&gt; can never equal that reference again. The next time a user tabs forward off the new final option, &lt;code&gt;trapFocus&lt;/code&gt;'s second condition silently fails. No &lt;code&gt;preventDefault()&lt;/code&gt; fires. Nothing errors. The browser's native Tab order simply takes over, carrying focus straight past the modal into whatever comes next in the document — a page still dimmed by an overlay that no longer has any actual say in where focus goes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The New Contract
&lt;/h2&gt;

&lt;p&gt;Rebuild the same modal on &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;, and run it through the identical re-render:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dialog&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"preferences"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"dialog"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"options"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="c"&gt;&amp;lt;!-- options render here --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;autofocus&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Save&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dialog&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;optionsContainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.options&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showModal&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// later, a request resolves&lt;/span&gt;
&lt;span class="nx"&gt;optionsContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;renderOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updatedOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;optionsContainer&lt;/code&gt; is scoped to the options list on purpose — the re-render never reaches the Save button, which is the one thing in this dialog that isn't re-evaluated on its own. Sequential focus order (what Tab responds to) is computed fresh at every keypress, which is why the new option becomes reachable without any code noticing it arrived. &lt;code&gt;autofocus&lt;/code&gt; isn't that kind of mechanism — the browser looks for it exactly once, when &lt;code&gt;showModal()&lt;/code&gt; runs. An element added after that, even with &lt;code&gt;autofocus&lt;/code&gt; set, needs an explicit &lt;code&gt;.focus()&lt;/code&gt; call; the platform won't pick it up on its own the way it does for Tab order.&lt;/p&gt;

&lt;p&gt;Nothing here caches a first or last element, so nothing can go stale. &lt;code&gt;showModal()&lt;/code&gt; doesn't compute a snapshot of focusable descendants at open time. It makes everything outside the dialog inert for as long as it stays open, which means the browser's Tab order search space is limited to what's actually inside the dialog, evaluated fresh at every keypress. Replace the options list's contents mid-open, and the next Tab press still only considers whatever is currently there, new option included. There's no reference to invalidate, because none was ever taken.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Moved, What Didn't
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;th&gt;Hand-rolled&lt;/th&gt;
&lt;th&gt;&lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Focus containment&lt;/td&gt;
&lt;td&gt;Cached references, checked on every keydown&lt;/td&gt;
&lt;td&gt;Structural — background is inert, nothing to cache&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restoring focus on close&lt;/td&gt;
&lt;td&gt;Manually stored and restored&lt;/td&gt;
&lt;td&gt;Automatic on close, however it closes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Escape to dismiss&lt;/td&gt;
&lt;td&gt;Explicit keydown check&lt;/td&gt;
&lt;td&gt;Automatic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inert background / stacking&lt;/td&gt;
&lt;td&gt;Manual &lt;code&gt;aria-hidden&lt;/code&gt;, manual z-index&lt;/td&gt;
&lt;td&gt;Automatic — native top layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Initial focus target&lt;/td&gt;
&lt;td&gt;Imperative &lt;code&gt;.focus()&lt;/code&gt; call&lt;/td&gt;
&lt;td&gt;Declarative — &lt;code&gt;autofocus&lt;/code&gt;, still an authoring decision&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dismiss on backdrop click&lt;/td&gt;
&lt;td&gt;Explicit click-outside check&lt;/td&gt;
&lt;td&gt;Still explicit — &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; has no default for this&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Four responsibilities move to the platform outright. One moves from a runtime decision to a declarative one — still the application's call, just a lighter one to carry. One doesn't move at all: clicking outside a &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; does nothing on its own, and closing it that way still needs the same kind of listener the hand-rolled version needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Whether the Claim Held
&lt;/h2&gt;

&lt;p&gt;The essay's claim wasn't that &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; has fewer bugs. It was that reimplementing a contract the platform already owns means owning its correctness indefinitely, including cases the original code never anticipated. This is exactly that case. The hand-rolled trap didn't fail because of bad code. It failed because "check against a cached reference" is a strategy with a specific, permanent blind spot, and a re-render was always going to find it eventually.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; doesn't close that blind spot with better code. It closes it by not having a cache in the first place — the entire category of failure the hand-rolled version was exposed to doesn't apply to a mechanism that re-evaluates focusability live. That's the four rows that moved outright. The two that didn't — choosing an initial focus target, handling a backdrop click — are decisions specific to what a given dialog contains, not something the platform could own on the application's behalf even if it tried to.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reference Article:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://dev.to/ortizfranklindev/html-doesnt-need-more-reinvention-it-needs-platform-trust-223o"&gt;HTML Doesn't Need More Reinvention — It Needs Platform Trust&lt;/a&gt;&lt;/p&gt;

</description>
      <category>html</category>
      <category>webdev</category>
      <category>a11y</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Notas desde el pase: La pregunta que seis componentes no pudieron responder</title>
      <dc:creator>Franklin</dc:creator>
      <pubDate>Sat, 26 Sep 2026 10:22:06 +0000</pubDate>
      <link>https://dev.to/ortizfranklindev/notas-desde-el-pase-la-pregunta-que-seis-componentes-no-pudieron-responder-4n4p</link>
      <guid>https://dev.to/ortizfranklindev/notas-desde-el-pase-la-pregunta-que-seis-componentes-no-pudieron-responder-4n4p</guid>
      <description>&lt;p&gt;&lt;em&gt;Also available in &lt;a href="https://dev.to/ortizfranklindev/notes-from-the-pass-the-question-six-components-couldnt-answer-4a5p"&gt;Inglés&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  La afirmación a prueba
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/ortizfranklindev/por-que-los-arboles-dom-con-abstraccion-de-componentes-se-vuelven-mas-dificiles-de-leer-3nnd"&gt;El ensayo del martes&lt;/a&gt; sostuvo que la arquitectura de componentes puede organizar el código fuente sin reemplazar la estructura del documento que produce, y que el fallo no está en dividir una página en componentes, sino en dividir una sola decisión entre componentes sin verificar si alguno de ellos conservó suficiente información para tomarla. El ejemplo fue el nivel de un encabezado, decidido a seis componentes de distancia de la posición en la página que debió haberlo establecido.&lt;/p&gt;

&lt;h2&gt;
  
  
  La cadena
&lt;/h2&gt;

&lt;p&gt;Seis componentes, compuestos de la forma en que realmente se usarían:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DashboardLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CardGroup&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Card&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Active Subscriptions"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* subscription list */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;CardGroup&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;DashboardLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cada capa realiza un solo trabajo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;DashboardLayout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Dashboard&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CardGroup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"card-grid"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CardHeader&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"card-body"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CardHeader&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"card-header"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"subheading"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Typography&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Heading&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Heading&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Heading&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;subheading&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Al renderizarse, esto produce exactamente lo que describió el ensayo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;main&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Dashboard&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-grid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-header"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h6&amp;gt;&lt;/span&gt;Active Subscriptions&lt;span class="nt"&gt;&amp;lt;/h6&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-body"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Seis preguntas, la misma respuesta
&lt;/h2&gt;

&lt;p&gt;Haz a cada capa las mismas dos preguntas. ¿Sabe dónde se ubica esta sección en la estructura general de la página? ¿Sabe qué etiqueta HTML tendrá su encabezado?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DashboardLayout&lt;/strong&gt; conoce la estructura. Renderiza el único &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; de la página, lo que significa que también sabe implícitamente que todo lo que renderice como &lt;code&gt;children&lt;/code&gt; pertenece jerárquicamente debajo de ese &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;. No conoce la etiqueta: nunca toca un encabezado directamente, solo delega lo que recibe.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CardGroup&lt;/strong&gt; no conoce ninguna de las dos cosas. Es un contenedor de cuadrícula (&lt;code&gt;grid wrapper&lt;/code&gt;). No sabe que está dentro de un &lt;em&gt;dashboard&lt;/em&gt; y no tiene ninguna opinión sobre los encabezados.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Card&lt;/strong&gt; no conoce ninguna de las dos cosas. Borde, &lt;em&gt;padding&lt;/em&gt;, un &lt;code&gt;CardHeader&lt;/code&gt;, un cuerpo. Tampoco hay lógica de encabezados aquí.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CardHeader&lt;/strong&gt; conoce la cadena de texto —"Active Subscriptions"— pero no en qué lugar de la página se ubica la tarjeta que la contiene. No decide una etiqueta. Le pasa el texto a &lt;code&gt;Typography&lt;/code&gt; con una instrucción de estilo: renderiza esto como un subencabezado (&lt;em&gt;subheading&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Typography&lt;/strong&gt; conoce la instrucción de estilo, no la estructura, y tampoco decide una etiqueta. Existe para que el sistema de diseño tenga un solo lugar que defina cómo se ve un &lt;em&gt;subheading&lt;/em&gt;. Transmite la propiedad &lt;code&gt;variant&lt;/code&gt; directamente.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Heading&lt;/strong&gt; es donde &lt;em&gt;subheading&lt;/em&gt; realmente se convierte en &lt;code&gt;&amp;lt;h6&amp;gt;&lt;/code&gt;. Esta es la única capa en toda la cadena donde se elige una etiqueta real, y también es la capa más alejada de la página, la que no tiene forma de saber que "Active Subscriptions" terminará seis niveles por debajo de un título de página que nunca ha visto.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Seis capas, seis respuestas idénticas a la segunda pregunta, hasta que la sexta cambia, exactamente en la capa menos equipada para responderla correctamente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dos caminos de regreso
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Pasar el nivel de forma explícita.&lt;/strong&gt; Entrega a cada capa una propiedad &lt;code&gt;level&lt;/code&gt; y pásala directamente, sin cambios, desde la cima:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;DashboardLayout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Dashboard&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CardGroup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"card-grid"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CardHeader&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"card-body"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CardHeader&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"card-header"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Typography&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Heading&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Heading&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Heading&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`h&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DashboardLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CardGroup&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Card&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Active Subscriptions"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* subscription list */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;CardGroup&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;DashboardLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El estilo visual se omite en esta versión para mantener visible el paso de &lt;code&gt;level&lt;/code&gt;; en la práctica viajaría por la misma ruta, pero bajo su propia propiedad, separada de la estructura tal como sostuvo el ensayo.&lt;/p&gt;

&lt;p&gt;El costo de esta solución: &lt;code&gt;CardGroup&lt;/code&gt;, &lt;code&gt;Card&lt;/code&gt;, &lt;code&gt;CardHeader&lt;/code&gt; y &lt;code&gt;Typography&lt;/code&gt; tuvieron que cambiar, a pesar de que ninguno tenía un interés real en la decisión. Agrega un séptimo contenedor a la cadena el próximo trimestre y también tendrá que recordar pasar &lt;code&gt;level&lt;/code&gt;, o la cadena se romperá de nuevo en un lugar diferente.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rastrearlo a través de contexto.&lt;/strong&gt; Deja que las capas que nunca tuvieron un interés en la decisión se mantengan exactamente como estaban en "La cadena": &lt;code&gt;CardGroup&lt;/code&gt;, &lt;code&gt;Card&lt;/code&gt;, &lt;code&gt;CardHeader&lt;/code&gt; y &lt;code&gt;Typography&lt;/code&gt; permanecen intactas, sin simplificar ni reescribir. Solo cambian dos componentes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HeadingLevelContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;DashboardLayout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Dashboard&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HeadingLevelContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;HeadingLevelContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Heading&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HeadingLevelContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`h&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;DashboardLayout&lt;/code&gt; establece el nivel una sola vez. &lt;code&gt;Heading&lt;/code&gt; lo lee desde el contexto en lugar de adivinarlo a partir de una cadena &lt;code&gt;variant&lt;/code&gt;. Nada en el medio necesita saber que el contexto existe.&lt;/p&gt;

&lt;p&gt;El costo aquí es diferente, no inexistente. Leer &lt;code&gt;Heading&lt;/code&gt; de forma aislada ya no te dice qué etiqueta renderiza: debes saber que existe un proveedor (&lt;em&gt;provider&lt;/em&gt;) en algún lugar superior del árbol. La primera solución es más fácil de rastrear leyendo solo las propiedades (&lt;code&gt;props&lt;/code&gt;). La segunda pide a menos componentes participar en una decisión que nunca les perteneció.&lt;/p&gt;

&lt;h2&gt;
  
  
  Si la afirmación se sostuvo
&lt;/h2&gt;

&lt;p&gt;La afirmación del ensayo de leer era que la arquitectura de componentes organiza el código fuente y no puede, por sí sola, reemplazar la estructura del documento, y que el fallo es una decisión dividida entre componentes, no un componente roto. Seis capas, recorridas una a una, nunca produjeron una sola capa que conservara ambas mitades de la información que la decisión necesitaba. &lt;code&gt;DashboardLayout&lt;/code&gt; tenía la estructura. &lt;code&gt;Heading&lt;/code&gt; tenía la etiqueta. Nada en el medio tenía ambas.&lt;/p&gt;

&lt;p&gt;Ambas soluciones confirman también la segunda mitad de la afirmación. Ninguna reduce los seis componentes a menos. Una transmite el contexto faltante a través de todos ellos. La otra lo transmite solo a través de los dos que realmente lo necesitaban. De cualquier manera, la solución nunca se trató de cuántos componentes existen. Se trató de a cuál se le entrega la información que le faltaba.&lt;/p&gt;

&lt;h3&gt;
  
  
  Artículo de referencia:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://dev.to/ortizfranklindev/por-que-los-arboles-dom-con-abstraccion-de-componentes-se-vuelven-mas-dificiles-de-leer-3nnd"&gt;Por qué los árboles DOM con abstracción de componentes se vuelven más difíciles de leer&lt;/a&gt; &lt;/p&gt;

</description>
      <category>html</category>
      <category>react</category>
      <category>a11y</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Notes from the Pass: The Question Six Components Couldn't Answer</title>
      <dc:creator>Franklin</dc:creator>
      <pubDate>Sat, 26 Sep 2026 10:20:06 +0000</pubDate>
      <link>https://dev.to/ortizfranklindev/notes-from-the-pass-the-question-six-components-couldnt-answer-4a5p</link>
      <guid>https://dev.to/ortizfranklindev/notes-from-the-pass-the-question-six-components-couldnt-answer-4a5p</guid>
      <description>&lt;p&gt;&lt;em&gt;Also available in &lt;a href="https://dev.to/ortizfranklindev/notas-desde-el-pase-la-pregunta-que-seis-componentes-no-pudieron-responder-4n4p"&gt;Español&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Claim Being Tested
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/ortizfranklindev/why-component-abstracted-dom-trees-become-harder-to-read-1p92"&gt;Tuesday's essay&lt;/a&gt; argued that component architecture can organize source code without being able to replace the structure of the document it produces — and that the failure isn't dividing a page into components, it's dividing a single decision across components without checking whether any one of them was left holding enough information to make it. The example was a heading level, decided six components away from the page position that should have set it.&lt;/p&gt;

&lt;p&gt;This Note runs that exact six-component chain, asks the same question at every layer, and marks precisely where the decision and the context it needed come apart. Then it puts them back together two different ways, without touching the number of components in the chain.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Chain
&lt;/h2&gt;

&lt;p&gt;Six components, composed the way they'd actually be used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DashboardLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CardGroup&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Card&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Active Subscriptions"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* subscription list */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;CardGroup&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;DashboardLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each layer does one job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;DashboardLayout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Dashboard&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CardGroup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"card-grid"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CardHeader&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"card-body"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CardHeader&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"card-header"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"subheading"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Typography&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Heading&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Heading&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Heading&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;subheading&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rendered, this produces exactly what the essay described:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;main&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Dashboard&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-grid"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-header"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h6&amp;gt;&lt;/span&gt;Active Subscriptions&lt;span class="nt"&gt;&amp;lt;/h6&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card-body"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Six Questions, Same Answer
&lt;/h2&gt;

&lt;p&gt;Ask every layer the same two questions. Does it know where this section sits in the page's outline? Does it know what HTML tag its heading will become?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;DashboardLayout&lt;/code&gt;&lt;/strong&gt; knows the outline. It renders the page's only &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;, which means it also implicitly knows that whatever it renders as &lt;code&gt;children&lt;/code&gt; belongs below that &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; in the hierarchy. It doesn't know the tag — it never touches a heading itself, only hands off to whatever it's given.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;CardGroup&lt;/code&gt;&lt;/strong&gt; knows neither. It's a grid wrapper. It doesn't know it's inside a dashboard, and it has no opinion about headings at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;Card&lt;/code&gt;&lt;/strong&gt; knows neither. Border, padding, a &lt;code&gt;CardHeader&lt;/code&gt;, a body. No heading logic lives here either.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;CardHeader&lt;/code&gt;&lt;/strong&gt; knows the string — "Active Subscriptions" — but not where the card holding it sits on the page. It doesn't decide a tag. It hands the string to &lt;code&gt;Typography&lt;/code&gt; with a styling instruction: render this like a subheading.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;Typography&lt;/code&gt;&lt;/strong&gt; knows the styling instruction, not the outline, and doesn't decide a tag either. It exists so the design system has one place that says what "subheading" looks like. It forwards &lt;code&gt;variant&lt;/code&gt; straight through.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;Heading&lt;/code&gt;&lt;/strong&gt; is where "subheading" actually becomes &lt;code&gt;&amp;lt;h6&amp;gt;&lt;/code&gt;. This is the only layer in the entire chain where a real tag name gets chosen — and it's also the layer furthest from the page, the one with no way to know that "Active Subscriptions" will land six levels below a page title it has never seen.&lt;/p&gt;

&lt;p&gt;Six layers, six identical answers to the second question, until the sixth one flips — at exactly the layer least equipped to answer it correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two Ways Back
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Thread the level explicitly.&lt;/strong&gt; Give every layer a &lt;code&gt;level&lt;/code&gt; prop and pass it straight through, unchanged, from the top:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;DashboardLayout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Dashboard&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CardGroup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"card-grid"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CardHeader&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"card-body"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CardHeader&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"card-header"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Typography&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Typography&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Heading&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Heading&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Heading&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`h&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DashboardLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CardGroup&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Card&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Active Subscriptions"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* subscription list */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;CardGroup&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;DashboardLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visual styling is left out of this version to keep the level-threading visible — in practice it would travel the same path &lt;code&gt;level&lt;/code&gt; does now, just under its own prop, separate from structure the way the essay argued it should be.&lt;/p&gt;

&lt;p&gt;The cost of this fix: &lt;code&gt;CardGroup&lt;/code&gt;, &lt;code&gt;Card&lt;/code&gt;, &lt;code&gt;CardHeader&lt;/code&gt;, and &lt;code&gt;Typography&lt;/code&gt; all had to change, even though none of them ever had a stake in the decision. Add a seventh wrapper to the chain next quarter, and it has to remember to forward &lt;code&gt;level&lt;/code&gt; too, or the chain breaks again in a new place.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Track it through context instead.&lt;/strong&gt; Let the layers that never had a stake in the decision stay exactly as they were in "The Chain" — &lt;code&gt;CardGroup&lt;/code&gt;, &lt;code&gt;Card&lt;/code&gt;, &lt;code&gt;CardHeader&lt;/code&gt;, and &lt;code&gt;Typography&lt;/code&gt; are untouched, not simplified, not rewritten. Only two components change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HeadingLevelContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;DashboardLayout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Dashboard&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HeadingLevelContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;HeadingLevelContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Heading&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HeadingLevelContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`h&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;DashboardLayout&lt;/code&gt; establishes the level once. &lt;code&gt;Heading&lt;/code&gt; reads it from context instead of guessing at it from a &lt;code&gt;variant&lt;/code&gt; string. Nothing in between needs to know the context exists.&lt;/p&gt;

&lt;p&gt;The cost here is different, not absent. Reading &lt;code&gt;Heading&lt;/code&gt; in isolation no longer tells you what tag it renders — you have to know a provider exists somewhere above it in the tree. The first fix is easier to trace by reading props alone. The second one asks fewer components to participate in a decision they never owned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Whether the Claim Held
&lt;/h2&gt;

&lt;p&gt;The essay's claim was that component architecture organizes source code and cannot, on its own, replace the structure of the document — and that the failure is a decision split across components, not a component that's broken. Six layers, walked one at a time, never produced a single layer holding both halves of the information the decision needed. &lt;code&gt;DashboardLayout&lt;/code&gt; had the outline. &lt;code&gt;Heading&lt;/code&gt; had the tag. Nothing in between had either.&lt;/p&gt;

&lt;p&gt;Both fixes confirm the second half of the claim too. Neither one reduces six components to fewer. One threads the missing context through all of them. The other threads it through none but the two that actually needed it. Either way, the fix was never about how many components exist. It was about which one gets handed the information it was missing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reference Article:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://dev.to/ortizfranklindev/why-component-abstracted-dom-trees-become-harder-to-read-1p92"&gt;Why Component-Abstracted DOM Trees Become Harder to Read&lt;/a&gt;&lt;/p&gt;

</description>
      <category>html</category>
      <category>webdev</category>
      <category>a11y</category>
      <category>react</category>
    </item>
  </channel>
</rss>
