Volver al Centro de conocimiento
Huella digital

Consultas de medios CSS en CreepJS: dos canales y una preferencia

Análisis del recolector de medios CSS fijado de CreepJS: una preferencia leída mediante una propiedad de @media y matchMedia, sus catorce claves y el barrido del rango de pantalla.

BotBrowser Team

Documentación

Quieres la documentación estructurada de Huella digital?

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.

Las preferencias de visualización son una de las pocas superficies que una página puede leer sin pedir permiso. Un recolector puede leer cada preferencia dos veces: primero deja que el motor aplique una consulta de medios y lee la propiedad personalizada resultante; después pregunta a matchMedia si la consulta coincide. Ambas respuestas proceden del mismo motor, pero no del mismo camino de código. Si el recolector guarda las dos, conserva dos observaciones independientes de una preferencia. Aquí se sigue esa separación en un recolector de código abierto fijado a una revisión, se nombran las catorce claves que presenta cada canal, se explica la rama que muestra una nota de bloqueo en lugar de un objeto vacío y se recorre el barrido que recupera dimensiones del dispositivo mediante consultas de medios, no mediante screen. BotBrowser aplica la configuración de pantalla del perfil dentro del núcleo del navegador, por lo que la respuesta de las consultas de medios y los valores comunicados por la pantalla parten de una misma configuración. No controla qué concluye un recolector de terceros cuando sus canales discrepan; nada de lo siguiente es una puntuación, una calificación ni una predicción sobre cómo un sitio clasifica una sesión.

TL;DR

  • El recolector escribe treinta reglas @media en un elemento style y después lee catorce propiedades personalizadas con getComputedStyle; cada valor llega así por la cascada, no por una llamada a una API.
  • A continuación plantea las mismas preguntas con matchMedia y guarda un segundo objeto con las mismas catorce claves. Cada preferencia queda, por tanto, con dos respuestas almacenadas.
  • Dos claves consultan intencionadamente el matchMedia de la ventana superior, no el del marco, porque un marco puede heredar un esquema de color distinto del que usa la ventana que lo contiene.
  • Si las catorce claves de un canal son falsas o están vacías, se muestra una nota de bloqueo. Una clave que no coincide se guarda como undefined; no significa lo mismo.
  • El ancho y la altura del dispositivo se recuperan probando device-width y device-height con un candidato y diez rangos de 1000 píxeles. Es una medición a través de consultas, no una lectura de screen.width.

El diagrama muestra una preferencia de medios leída por dos canales: una regla @media alimenta una propiedad personalizada que lee getComputedStyle; una consulta matchMedia devuelve matches y un barrido independiente recupera el ancho y la altura del dispositivo.

Contenido

Qué leen los dos canales

Ambos canales responden a las mismas catorce preguntas. La diferencia está en dónde se calcula cada respuesta.

El canal de propiedades personalizadas

El primer canal no llama en absoluto a una función de coincidencia. Asigna al cuerpo del documento un elemento style con treinta reglas de medios. Cada regla establece una propiedad personalizada bajo una condición y, después, el recolector lee el estilo calculado del cuerpo y extrae catorce propiedades:

body.innerHTML = `<style>@media (prefers-color-scheme: light) {body {--prefers-color-scheme: light}}</style>`;
const style = getComputedStyle(body);
style.getPropertyValue('--prefers-color-scheme').trim() || undefined;

Hay dos propiedades de este método importantes para interpretar el resultado. El motor de estilos produce el valor al aplicar una regla condicional. Si una clave no coincide, la propiedad queda sin establecer y la expresión devuelve undefined, no una cadena vacía ni false. Además, las reglas están en el documento: la respuesta es la decisión del motor para las condiciones de ese documento, no un valor calculado por un script a partir de otras lecturas. El módulo de estilos calculados de CreepJS enumera nombres de CSSStyleDeclaration y lee colores y fuentes del sistema; no usa este canal de preferencias.

Las reglas también codifican un par. (monochrome) establece --monochrome: monochrome y (monochrome: 0) establece --monochrome: non-monochrome. El valor guardado es una etiqueta elegida entre dos ramas, no un booleano.

El canal matchMedia

El segundo canal plantea al motor las mismas preguntas con Window.matchMedia. Para cada clave recorre una cadena ternaria desde la respuesta más específica hasta la menos específica:

pointer: win.matchMedia('(pointer: fine)').matches
  ? 'fine'
  : win.matchMedia('(pointer: coarse)').matches
    ? 'coarse'
    : win.matchMedia('(pointer: none)').matches
      ? 'none'
      : undefined;

pointer y any-pointer prueban tres ramas cada uno; color-gamut prueba rec2020, luego p3 y después srgb; display-mode prueba fullscreen, standalone, minimal-ui y, por último, browser. Si ninguna rama coincide, la clave termina como undefined, el mismo marcador que usa el primer canal. Por eso se pueden comparar directamente los dos objetos clave por clave.

Qué ventana consulta cada canal

Catorce claves no implican catorce ámbitos idénticos. La mayoría de las consultas pasan por win.matchMedia, donde win es la referencia a la ventana del propio recolector. Hay dos excepciones: prefers-color-scheme y orientation llaman al identificador global matchMedia; un comentario del código fuente indica que se prefiere la ventana principal.

Esa elección es deliberada y observable. Un marco puede estar dentro de un documento cuyo esquema de color u orientación se informe de forma distinta desde el contexto propio del marco. Si el recolector necesita la respuesta de la ventana contenedora, tiene que preguntar a esa ventana en vez de al marco. Al comparar ejecuciones, conviene registrar el ámbito usado por cada clave: una diferencia entre ejecuciones en prefers-color-scheme puede deberse al ámbito tanto como a la configuración.

La misma asimetría aparece en el canal @media por un motivo estructural. Las reglas de la cascada se aplican al documento que contiene el elemento style; un marco que lee su propio cuerpo no puede ver una regla escrita en el cuerpo del documento padre. Por tanto, los dos canales consultan ámbitos distintos para una misma clave y un desacuerdo no demuestra por sí solo una inconsistencia.

ClaveFuente del valor @mediaFuente del valor matchMedia
prefers-reduced-motionno-preference o reduceno-preference o reduce
prefers-color-schemelight o darklight o dark, consultado en la ventana principal
monochromemonochrome o non-monochromelas mismas dos etiquetas
inverted-colorsinverted o nonelas mismas dos etiquetas
forced-colorsnone o activelas mismas dos etiquetas
any-hoverhover o nonelas mismas dos etiquetas
hoverhover o nonelas mismas dos etiquetas
any-pointerfine, coarse o nonelas mismas tres etiquetas
pointerfine, coarse o nonelas mismas tres etiquetas
device-aspect-ratiola cadena de proporción reducidala misma cadena de proporción
device-screenwidth x heightel mismo par
display-modefullscreen, standalone, minimal-ui o browserlas mismas cuatro etiquetas
color-gamutsrgb, p3 o rec2020las mismas tres etiquetas
orientationlandscape o portraitlas mismas dos, consultadas en la ventana principal

La tabla es la parte del informe que se puede verificar sin ejecutar nada: si un objeto almacenado contiene para una clave un valor que no aparece en su columna, ese valor no llegó por ese canal.

Qué demuestra y qué no una respuesta almacenada

Un objeto con catorce claves parece una medición. En realidad, es un conjunto de etiquetas; las preguntas útiles son qué etiquetas faltan y qué significa su ausencia.

undefined es el resultado de una rama

Cada clave termina en undefined cuando ninguna rama coincide, y el recolector almacena ese valor tal cual. Por eso un informe distingue tres estados para una clave: una etiqueta coincidente, una clave ausente porque nada coincidió y un objeto entero que nunca se mostró porque fallaron todas las claves. Solo el tercer estado es una condición de bloqueo.

Al registrar una ejecución, mantén los dos objetos separados en vez de fusionarlos. Un objeto fusionado no permite saber si pointer: fine vino de la cascada o de matchMedia; esa procedencia es precisamente lo que aporta el segundo canal.

La rama de bloqueo

La función de presentación comprueba ambos objetos antes de dibujar. Para cada canal cuenta las claves cuyo valor es verdadero y, cuando el recuento es cero, imprime una nota de bloqueo en lugar de una lista vacía:

!Object.keys(mediaCSS).filter((key) => !!mediaCSS[key]).length ? HTMLNote.BLOCKED : modal(...)

La diferencia importa porque las dos lecturas tienen causas distintas. Un objeto @media vacío significa que la cascada no aplicó ninguna de las treinta reglas, lo cual apunta al elemento style, al documento o a una restricción de hojas de estilo. Un objeto matchMedia vacío significa que ninguna rama coincidió con ninguna de las catorce consultas. Eso es muy poco habitual en una sesión de escritorio normal y describe un contexto muy restringido, no una preferencia ausente.

Si el informe muestra bloqueo en un canal y etiquetas en el otro, describe un problema de ámbito o de hojas de estilo, no un ajuste de privacidad. Primero hay que tratarlo como un hallazgo de configuración.

Qué significa un desacuerdo

La correspondencia entre claves permite cotejar cada valor sin instrumentación adicional. Hay tres resultados que conviene distinguir:

  • Ambos canales devuelven la misma etiqueta. El motor coincide consigo mismo a través de dos rutas de código; eso es evidencia de coherencia para esa clave y nada más.
  • Un canal devuelve una etiqueta y el otro undefined. Comprueba primero el ámbito: prefers-color-scheme y orientation se consultan en la ventana principal, por lo que un marco con otro contexto puede producir exactamente este resultado.
  • Ambos canales devuelven etiquetas contradictorias. Es el caso más llamativo, pero describe la hoja de estilos y el estado de medios del documento, no un dispositivo físico.

Ninguno de estos resultados identifica a una persona, un modelo o un veredicto. Los grupos de señales que usa un detector clasifican resultados de este tipo en familias. La diferencia es que la etiqueta de familia se asigna después de una decisión, mientras que la comparación de canales está disponible antes de ella.

La línea de dispositivo táctil

El panel mostrado añade una línea derivada, touch device, que lee un único valor almacenado: si any-pointer es coarse, muestra true; en caso contrario muestra una nota de desconocido, no false. Un puntero principal grueso es frecuente en teléfonos y tabletas y también aparece en algunos paneles táctiles de escritorio. Por eso la línea sugiere una capacidad del puntero, pero no declara una clase de hardware.

El barrido del rango de pantalla

La tercera parte del recolector no lee screen.width y screen.height para detenerse ahí. Comprueba esos valores con consultas de medios.

La prueba del candidato único

El primer intento formula dos consultas con los valores exactos que comunicó el objeto de pantalla: (device-width: W px) y (device-height: H px). Si ambas responden, se devuelve el par sin cambios y termina el barrido.

El barrido solo empieza si una de las consultas falla. Ese orden hace que el caso común sea barato y reserva el camino costoso para una comprobación diagnóstica, en vez de convertirlo en la ruta predeterminada.

El recorrido de diez rangos

La alternativa construye mil reglas por paso y recorre diez pasos, de cero a diez mil píxeles, completando la dimensión que todavía no haya respondido. Las dos dimensiones se buscan independientemente dentro del mismo recorrido; si el ancho responde en el primer rango, la búsqueda de la altura continúa. Este enfoque no es exclusivo de las consultas de medios: el mismo recolector verifica valores en vez de confiar en ellos, y la superficie de huella digital CSS enumera las consultas que hacen posible esa comprobación.

El resultado es un par de números que el motor recupera mediante sus propias consultas de dispositivo, no mediante el objeto screen. Eso hace útil la lectura: si una configuración informa una resolución por screen y responde otra por device-width, el mismo informe contiene los dos números distintos y deja visible el desacuerdo.

La proporción y la prueba sin barra de tareas

Del mismo bloque salen dos valores derivados. La proporción se calcula reduciendo el ancho y la altura mediante un ayudante que obtiene el máximo común divisor y se formatea como W/H; después se consulta con matchMedia y también se escribe en una regla @media.

El segundo valor es una heurística sobre el área disponible. El recolector calcula si el ancho y la altura informados son iguales al ancho y la altura disponibles. Si una pantalla grande no muestra diferencia entre esos pares, lo trata como una condición de menor entropía:

const noTaskbar = !(width - availWidth || height - availHeight);
if (screen.width !== width || (width > 800 && noTaskbar)) {
  LowerEntropy.IFRAME_SCREEN = true;
}

La marca interesa por dos motivos. Una sesión de escritorio suele reservar parte del área para la barra de tareas o el dock, así que una igualdad exacta en una pantalla grande tiene la forma de una superficie sintética. Además, se escribe en un registro compartido que declara solo seis claves, ninguna de ellas esta; por eso la marca es una anotación ad hoc, no un campo documentado. Si otros scripts leen ese registro, la presencia de una clave no demuestra que estuviera prevista.

CampoProcedenciaQué establece
screenQuery.widthprimera prueba candidata y después diez rangos de 1000 pxla respuesta del motor a device-width
screenQuery.heightel mismo recorrido, de forma independientela respuesta del motor a device-height
device-aspect-ratioancho y altura reducidos por su máximo común divisoruna cadena de proporción, no el tipo de panel
device-screenW x H consultado mediante matchMediasi el par coincide como una condición
LowerEntropy.IFRAME_SCREENpantalla informada frente al área disponibleuna pista de pantalla de baja entropía, no un veredicto

La línea táctil y el recorrido de rangos describen la pantalla, mientras que la geometría de pantalla y ventana es otra superficie con diferencias entre resolución informada, área disponible y ventana gráfica de diseño. La huella de pantalla y ventana trata esos valores; este recolector solo verifica dos de ellos mediante consultas de medios.

Un registro emparejado para una ejecución

Los dos objetos del recolector facilitan crear un registro, siempre que se conserven ambos.

Campos que conviene guardar

Guarda los dos objetos en claves separadas, el par de pantalla recuperado por el barrido, el identificador de compilación del navegador y la URL del documento. Añade un campo que el recolector no genera: el ámbito en el que se consultó cada clave sensible. Ese campo convierte un desacuerdo en una explicación, en vez de una alarma.

Conserva las etiquetas sin procesar en lugar de un hash. El recolector muestra un hash corto junto a cada panel modal; sirve para comparar dos informes de un vistazo, pero no para diagnosticar uno, porque un hash de catorce etiquetas no indica cuál cambió.

Repetir y comparar

Dos ejecuciones con la misma configuración deberían producir las mismas etiquetas para cada clave. Una clave que cambia entre ejecuciones apunta a una configuración o a un momento distinto: la hoja de estilos quizá no se aplicó en una ejecución, el marco quizá estuvo en otro ámbito o el documento quizá se cargó en otro entorno.

Compara por clave, no por objeto, y registra el sentido del cambio. Un pointer que pasa de fine a undefined es distinto de otro que pasa de fine a coarse; solo el segundo describe un cambio en la capacidad del puntero.

Qué no puede resolver el registro

El registro describe la respuesta del motor del documento. No describe el hardware de pantalla, el sistema operativo ni el modelo que un sitio construye a partir de estas etiquetas. Una comparación limpia entre ambos canales solo indica que las dos rutas de código coincidieron en esta compilación y para este documento; habla de coherencia, no de identidad.

BotBrowser aplica dentro del núcleo las opciones de pantalla del perfil, por lo que el valor resuelto por una consulta de medios y el valor comunicado por screen proceden de la misma configuración cargada. La referencia de señales CSS documenta qué opciones están disponibles. Esto no cambia la conclusión de un recolector de terceros ante un desacuerdo, ni puede hacer que una pantalla de baja entropía se comporte como un panel físico concreto.

Preguntas frecuentes

¿Coinciden siempre los dos canales?

No necesariamente; las razones son estructurales, no aleatorias. El canal de la cascada solo se aplica al documento propietario del elemento style, mientras que dos claves de matchMedia se consultan en la ventana principal. Un marco en otro contexto puede responder legítimamente de otra manera a la misma clave.

¿Qué significa una nota de bloqueo?

Significa que todas las claves de ese canal dieron un valor falso. En el canal de cascada apunta a una hoja de estilos que no se aplicó. En el canal matchMedia apunta a un contexto restringido, porque una sesión de escritorio normal coincide con alguna rama para la mayoría de las catorce claves.

¿Por qué consultar medios en vez de leer los valores de pantalla?

Porque la consulta aporta una respuesta independiente a la misma pregunta. Una configuración puede informar una resolución por screen y responder otra por device-width; el barrido hace visible esa diferencia en el informe, en vez de dejarla para una prueba separada.

Conclusión práctica

Empieza por los dos objetos, no por el panel dibujado. Un canal procede de la cascada y el otro de una función de coincidencia; ambos responden las mismas catorce preguntas y cada clave puede faltar, tener una etiqueta o pertenecer a un canal que nunca llegó a mostrarse. Después interpreta los valores de pantalla como una medición: el barrido compara el par informado con device-width y device-height, mientras que la prueba sin barra de tareas es una pista sobre el área disponible, no un veredicto.

El siguiente paso es crear un registro emparejado para un documento bajo tu control. Guarda ambos objetos, el par de pantalla recuperado y el ámbito empleado por cada clave sensible; después repite la ejecución y compara clave por clave. Si una clave cambia, investiga la hoja de estilos o el ámbito. Si todas se mantienen, los dos canales coincidieron en esa compilación.

Fuentes

#CreepJS#Consultas De Medios CSS#matchMedia#Prefers-Color-Scheme#Pantalla

Este artículo forma parte de la guía: ¿Qué es la huella digital del navegador?

Ejecuta la misma comprobación en BotBrowser

Carga un perfil, mantén dentro del navegador las señales que acabas de leer y confirma el resultado en tu propia máquina antes del despliegue.