También disponible en English
La afirmación a prueba
El ensayo del jueves planteó que las relaciones de un documento deben declararse una sola vez, en la estructura, o cada consumidor que lo lea las reconstruirá por separado — y a veces, de forma incorrecta. El ejemplo fue una tabla de comparación construida como una grid de CSS: visualmente una tabla, estructuralmente tres divs en fila sin nada que declarara qué valor pertenecía a qué producto.
Esa es una afirmación sobre los documentos en general. Así es como se ve en el momento en que a una de esas filas le falta un valor.
El artefacto
Tres productos, tres especificaciones, una grilla:
<div class="spec-grid">
<div class="spec-row">
<div class="spec-label">Battery life</div>
<div class="spec-value">18 hours</div>
<div class="spec-value">14 hours</div>
<div class="spec-value">20 hours</div>
</div>
<div class="spec-row">
<div class="spec-label">Water resistance</div>
<div class="spec-value">IP67</div>
<div class="spec-value">IP68</div>
</div>
</div>
.spec-row {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
}
El Producto B no tiene clasificación de resistencia al agua. El condicional de la plantilla — {value && <div class="spec-value">{value}</div>} — se escribió para evitar renderizar una caja vacía en ese caso. Un instinto razonable. Nadie quiere un div hueco en el layout.
Observa la segunda fila. Tiene tres elementos hijos donde la primera fila tiene cuatro: la etiqueta, el valor del Producto A y — dado que el div del Producto B nunca se renderizó — el valor del Producto C, ubicado en lo que ahora es la tercera posición de la grilla.
Lo que realmente se rompe
La colocación automática de la grilla (grid auto-placement) no sabe que se suponía que un espacio debía quedarse vacío. Solo sabe cuántos elementos hijos existen y los coloca en orden. La fila uno tiene cuatro elementos hijos, por lo que caen en las columnas uno a cuatro. La fila dos tiene tres, por lo que caen en las columnas uno a tres — lo que significa que el "IP68" del Producto C ahora está ubicado directamente debajo del encabezado de columna del Producto B.
Un script de extracción diseñado para recorrer esta tabla lee cada .spec-value por su índice dentro de la fila: la posición 0 se mapea al Producto A, la posición 1 al Producto B, la posición 2 al Producto C. Para la fila de resistencia al agua, la posición 1 contiene "IP68". El script lo reporta como la clasificación del Producto B. El Producto B no tiene una. El Producto C sí.
Un lector de pantalla encuentra la misma fila, pero los divs no establecen semántica de tabla ni relaciones de columna. Puede exponer "Water resistance", "IP67" e "IP68" como una secuencia de contenido, pero nada en la estructura dice a qué producto pertenece cada valor. Un oyente no tiene una relación estructural de la cual depender cuando el Producto B no tiene ningún valor.
Un asistente de IA al que se le da esa misma extracción basada en posición hereda su error. Si se le pregunta por la resistencia al agua del Producto C, puede devolver el número del Producto B — no porque haya razonado de forma incorrecta, sino porque los datos que recibió ya habían adjuntado ese valor al producto equivocado. La única señal de la fila sobre "qué valor va dónde" era la posición, y la posición dejó de significar lo que se suponía que debía significar en el momento en que se omitió un valor en lugar de renderizarlo vacío.
El momento en que realmente diverge
Reconstruye la misma fila como una fila de tabla real:
<tr>
<th scope="row">Water resistance</th>
<td>IP67</td>
<td></td>
<td>IP68</td>
</tr>
scope="row" identifica "Water resistance" como el encabezado de la fila. Los tres encabezados scope="col" identifican los productos. Juntos le dan a cada celda de datos una posición declarada: la intersección de un encabezado de fila y un encabezado de columna, no solo un espacio en una lista plana de elementos hijos.
Esa es la teoría. En la práctica, el navegador encuentra esa intersección de la misma manera que encuentra el espacio de un elemento de la grilla: recorriendo las celdas en el orden del documento y contando. Si se omite por completo ese segundo <td>, de la misma manera que el condicional de la grilla omitió su div, "IP68" se mueve a la columna del Producto B exactamente como lo hizo antes: en silencio, sin nada en pantalla o en el árbol de accesibilidad que anuncie que falta una celda. scope calcula una relación. No verifica que la celda que reclama una posición realmente pertenezca allí.
La solución nunca fue <table> como elemento. Es representar el valor faltante de forma explícita — un <td> vacío, o mejor aún, un marcador de posición real como "Not rated" — en cualquiera que sea la estructura que se esté utilizando. Una grilla con un div vacío renderizado correctamente en ese espacio detiene el mismo desplazamiento. Lo que agrega la tabla, una vez que se representa el valor faltante, es una relación que un lector de pantalla o un script de extracción pueden calcular a partir de la asociación declarada del encabezado, en lugar de reconstruirla a partir de lo que sea que esté al lado.
Una relación expresada solo a través de la posición sobrevive exactamente el tiempo que cada posición se mantenga ocupada. Una relación expresada a través de la estructura puede preservar la relación incluso cuando el valor está vacío — porque la posición vacía todavía está representada.
Lo que esto realmente muestra
<table>
<caption>Product specifications</caption>
<thead>
<tr>
<th scope="col">Specification</th>
<th scope="col">Product A</th>
<th scope="col">Product B</th>
<th scope="col">Product C</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Water resistance</th>
<td>IP67</td>
<td>Not rated</td>
<td>IP68</td>
</tr>
</tbody>
</table>
Observa lo que está haciendo esa celda central. No está ahí para cumplir con una lista de verificación de accesibilidad. "Not rated" es un dato: la ausencia de una clasificación de resistencia al agua es un hecho sobre el Producto B, y la fila necesitaba un lugar estructural para contenerlo. Una celda que no está allí no puede llevar ese hecho. Una celda que lo dice, sí puede.
Ejecuta las mismas tres comprobaciones contra la tabla corregida. Un lector de pantalla puede usar los encabezados declarados para establecer la relación entre "Water resistance" y cada columna de producto; al navegar por la fila, la celda del Producto B sigue siendo identificable como la celda del Producto B, con "Not rated" y todo. Un pase de extracción que resuelve los encabezados de fila y columna de la tabla — en lugar de contar celdas pobladas — puede asociar el Producto C con "IP68" en lugar de con cualquier celda que preceda. Un modelo que trabaja a partir de esa extracción responde correctamente, porque los datos que recibió ya llevan la asociación correcta.
Nada de esto requirió que el script de extracción fuera más inteligente, o que el lector de pantalla adivinara mejor. Requirió que la fila dejara de ser tres cajas en una línea y se convirtiera en una fila: una estructura donde se declara la identidad de cada valor, no se infiere de lo que se renderizó a su lado.
La afirmación en el ensayo del jueves Tus problemas de SEO y accesibilidad pueden compartir la misma causa raíz. no era abstracta. Así es como se ve la primera vez que un valor desaparece.
Top comments (0)