CSS Highlight API: pintura y accesibilidad
Usa resaltados personalizados con texto semántico, rangos limitados, foco de teclado, contraste y una alternativa accesible.
BotBrowser Team
Quieres la documentación estructurada de Plataforma?
Este artículo forma parte de la biblioteca editorial. Para pasos de configuración, material de referencia y actualizaciones continuas, entra en la sección de docs.
CSS Highlight API puede dar estilo a coincidencias de búsqueda, anotaciones y selecciones sin cambiar el DOM, pero un resaltado es presentación, no significado. BotBrowser puede repetir un fixture de resaltado propio de la página e inspeccionar estados de reserva visibles; no puede certificar el habla de tecnologías de asistencia, la intención de lectura de una persona ni la implementación completa de pintura del navegador. Mantén el contrato acotado: expón el texto en el DOM, registra rangos limitados, pinta una capa distinguible y conserva una alternativa semántica y operable con teclado.
TL;DR
CSS.highlights.set('search-hit', new Highlight(range))aplica estilo a unRange; no añade texto, etiquetas ni semántica accesible.- Un pseudo-elemento de resaltado como
::highlight(search-hit)debe usar un par de colores con contraste seguro y nunca ser la única señal. - Conserva el texto fuente y un contador de resultados anunciado en el DOM; prueba por separado el foco de teclado, la selección, los colores forzados, el zoom y la reducción de movimiento.
- Reconstruye los rangos tras cambios de contenido, limita su cantidad y longitud y elimina entradas obsoletas con
CSS.highlights.delete(name). - BotBrowser puede comprobar el estado propio de la página y la alternativa, pero este artículo reúne orientación basada en fuentes y no declara una ejecución de BotBrowser.
Contenido
- Conserva el significado en el DOM
- Registra rangos limitados
- Pinta una capa visible y accesible
- Gestiona selección, foco y alternativa
- Clasifica resultados con una tabla de decisión
- Capacidad y limitación de BotBrowser
- Conclusión práctica
Conserva el significado en el DOM
La API de resaltado cambia cómo se pinta el texto existente. Un Range apunta a nodos ya presentes, por lo que un lector de pantalla, una copia, la búsqueda de texto y el renderizado sin CSS necesitan el mismo contenido fuente. Coloca el contador de resultados en una región aria-live="polite", conserva la consulta en un campo etiquetado y muestra una lista o párrafo normal para las anotaciones. No codifiques una advertencia solo con un fondo amarillo o un efecto de pintura personalizado.
La API es independiente de la Selection API. Una selección del usuario puede verse mediante ::selection; un resaltado personalizado con nombre es un objeto Highlight propiedad de la aplicación. Da a cada estado un nombre estable como search-hit o current-hit y elimínalo cuando la consulta esté vacía. El nombre es un hook de CSS, no una etiqueta que una tecnología de asistencia vaya a anunciar.
Para un límite relacionado con la identidad visual, gestión del color CSS y accesibilidad de pantalla explica cómo separar un resultado de estilo visible en el navegador de una afirmación de capacidad. Para la interacción de teclado, localización y privacidad de Keyboard Layout API muestra por qué una etiqueta visual no debe convertirse en una inferencia sobre quien la usa. La revisión de compatibilidad de Web Components añade una matriz para separar detección de funciones y promesas al lector.
Registra rangos limitados
Crea rangos a partir de nodos de texto renderizados, no de una cadena HTML no confiable. Un fixture sencillo puede recorrer nodos de texto, buscar una consulta con plegado de mayúsculas y llamar a range.setStart() y range.setEnd() para cada coincidencia. Limita MAX_HIGHLIGHTS y MAX_MATCH_CHARS; la página debe seguir respondiendo cuando la consulta coincide con miles de palabras.
const MAX_HIGHLIGHTS = 100;
const MAX_MATCH_CHARS = 120;
function showMatches(root, query) {
CSS.highlights.delete('search-hit');
if (!query || !window.Highlight) return 0;
const ranges = [];
for (const node of root.querySelectorAll('[data-search-text]')) {
const text = node.textContent || '';
let offset = 0;
while (offset < text.length && ranges.length < MAX_HIGHLIGHTS) {
const start = text.toLocaleLowerCase().indexOf(query.toLocaleLowerCase(), offset);
if (start < 0) break;
if (query.length <= MAX_MATCH_CHARS) {
const range = new Range();
range.setStart(node.firstChild, start);
range.setEnd(node.firstChild, start + query.length);
ranges.push(range);
}
offset = start + Math.max(query.length, 1);
}
}
CSS.highlights.set('search-hit', new Highlight(...ranges));
return ranges.length;
}
El fixture exacto depende de los límites de los nodos de texto; el código de producción debe gestionar con seguridad nodos anidados y un firstChild vacío. Recalcula después de renderizar, localizar, filtrar o reciclar una lista virtual. Un rango que apunta a contenido eliminado es estado obsoleto, no una razón para mantener vivo un objeto de resaltado antiguo.
Pinta una capa visible y accesible
Usa el pseudo-elemento ::highlight(search-hit) para la capa con nombre y mantén pequeñas las declaraciones. El estilo de CSS Highlight API está limitado intencionadamente frente a los elementos normales; el navegador puede ignorar propiedades no compatibles. Elige un primer plano y fondo que cumplan el objetivo de contraste del producto y añade una señal no cromática, como un contorno, icono o contador de resultados en la interfaz cercana.
::highlight(search-hit) {
color: #111827;
background-color: #fde68a;
text-decoration: underline 0.12em solid #92400e;
}
@media (forced-colors: active) {
::highlight(search-hit) {
color: CanvasText;
background-color: Highlight;
text-decoration: underline;
}
}
No supongas que un resaltado personalizado se pinta igual en todos los motores o contextos incrustados. Detecta CSS.highlights, Highlight y el selector que uses; después muestra la clase de reserva o el marcador en línea si falta algún requisito. Un resultado de pintura visible en el navegador no demuestra que un lector de pantalla anuncie «coincidencia» ni que un modo de alto contraste conserve tus colores.
Gestiona selección, foco y alternativa
La búsqueda debe mover el foco a un control de resultado real, no a un rango pintado. Ofrece botones Anterior y Siguiente con un nombre accesible, conserva la consulta en el campo y desplaza el resultado seleccionado a la vista sin atrapar el foco del teclado. Si el usuario selecciona texto, no sobrescribas ::selection con el resaltado de búsqueda; las dos capas expresan estados distintos.
Cuando los resaltados no sean compatibles, añade un elemento mark temporal o una clase CSS alrededor de un nodo de texto confiable y expón el mismo contador y navegación. Elimina esa alternativa antes de aplicar una consulta nueva para que no se acumulen envoltorios anidados. Para anotaciones de solo lectura, una nota adyacente con aria-describedby es más segura que depender de un parche de color que desaparece al imprimir o con colores forzados.
Comprueba el zoom al 200 %, el espaciado del texto, la navegación solo con teclado, prefers-reduced-motion y un lector de pantalla. El resaltado no debe ocultar los contornos de foco, recortar los trazos descendentes ni crear una animación que parpadee para cada resultado. Si la consulta está vacía o no hay coincidencias, borra el resaltado nombrado y anuncia «0 resultados» en la región activa.
Clasifica resultados con una tabla de decisión
| Observed stage | What it establishes | Safe next action |
|---|---|---|
CSS.highlights or Highlight absent | Named highlights cannot start in this context | Use semantic mark or plain text and keep navigation usable |
| API present, query empty | There is no range to paint | Delete search-hit, announce the cleared state, and retain focus |
| Ranges created, paint visible | The page applied one browser-visible style layer | Verify contrast, focus, forced colors, and accessible result text |
| Content or locale changed | Existing offsets may be stale | Rebuild ranges from current text nodes and discard the old object |
| Match count exceeds 100 | The fixture hit its bounded workload | Show a capped result state and offer narrower search, not unbounded work |
| Forced colors or print differs | Presentation changed by user agent or medium | Keep the DOM result and a non-color cue as the source of meaning |
Cada fila describe una condición observable, no una promesa universal. Registra el estado visible, la longitud de la consulta, el número de rangos y la rama de reserva; no guardes el documento completo ni infieras el comportamiento de lectura.
| Punto de control | Registro | Lo que no demuestra |
|---|---|---|
| Detección de API | CSS.highlights=true, Highlight=true | Que un lector de pantalla anuncie la pintura |
| Ciclo del rango | rangeCount=3, longitud y nodos | Que el texto pertenezca a una identidad |
| Alternativa | fallback=mark y motivo | Que el motor tenga un fallo de accesibilidad |
| Foco | focusTarget=result-2 y orden de teclado | La intención de la persona |
| Limpieza | se elimina el resaltado antiguo | Que se borró estado remoto |
| Comprobación | Registro mínimo | No inferir |
|---|---|---|
| Compatibilidad API | window.CSS, CSS.highlights, window.Highlight | Que todas las propiedades CSS estén soportadas |
| Límites de consulta | MAX_HIGHLIGHTS=100, MAX_MATCH_CHARS=120 | Que el documento completo sea seguro de guardar |
| Rango actual | nodo fuente y rangeCount | Que el texto tenga relación con una identidad |
| Foco | focusTarget=result-2 | Que la persona quiera leer ese resultado |
| Modo visual | normal, colores forzados o impresión | Que la pantalla física use los mismos colores |
| Limpieza | nombre eliminado antes de volver a renderizar | Que se haya borrado un estado remoto |
Capacidad y limitación de BotBrowser
BotBrowser puede ejecutar un fixture de página autorizado y comprobar el contador DOM, el número de rangos y la rama de reserva. No puede certificar la lectura de un lector de pantalla ni garantizar que cada motor pinte ::highlight() igual.
{
"api": "CSS.highlights",
"highlightName": "search-hit",
"rangeCount": 3,
"maxHighlights": 100,
"domResultText": "3 matches",
"assistiveTechnologySpeech": "not-asserted"
}
Repite el fixture tras una versión del navegador, un cambio de localización, una lista virtual o un nuevo token de contraste. Compara la primera diferencia visible y conserva la alternativa DOM en el registro de aceptación.
Fuentes
- CSS Custom Highlight API specification
- MDN: CSS Custom Highlight API
- MDN: ::highlight()
- BotBrowser advanced features
Conclusión práctica
Usa la pintura personalizada de CSS Highlight API para enfatizar texto que ya tiene significado semántico. Limita y reconstruye los rangos, conserva el estado y la navegación en el DOM y ofrece una alternativa sin color con contraste seguro. Trata la salida de BotBrowser como evidencia de la página, no como prueba de voz asistiva ni de la interpretación de una persona.
BotBrowser puede repetir un fixture autorizado que registra rangos Highlight, aplica ::highlight() y comprueba la alternativa visible; este artículo no afirma una ejecución de BotBrowser y no puede certificar la voz de un lector de pantalla ni la percepción del color.
Este artículo forma parte de la guía: Superficies de la plataforma web: mapa de revisión
Artículos Relacionados
Gestión del color CSS y accesibilidad de la pantalla
PlataformaPreferencias de media queries y control del usuario
PlataformaView Transitions API: estado y navegación
PlataformaRevisión de compatibilidad del navegador para Web Components
PlataformaVoces de síntesis de voz y reproducción accesible en el navegador
PlataformaPopover API: foco, cierre y accesibilidad
Lleva BotBrowser de la investigación a producción
Usa estas guías para entender el modelo y después avanzar hacia validación multiplataforma, contextos aislados y despliegue de navegador preparado para escalar.