Inicio / Artículos

typed CSS attr(): Inyectar directamente los valores de atributos HTML en los estilos

Aprende cómo la función typed CSS attr() convierte los atributos de datos en longitudes, colores y tiempos, cómo fallan sus soluciones alternativas, por qué url() está bloqueada y cómo distribuirla de forma segura hoy en día.

Editor: REACTAPP.TOP18 min de lectura4056 palabras

Fuentes: CSS attr() Reads Your HTML Now

Imagínese una barra de progreso cuyo valor, 87, ya está incluido en el marcado como data-fill="87". Hasta hace poco, para introducir esa cifra en la propiedad width era necesario utilizar un pequeño script: leer dataset.fill, añadir un signo porcentual y escribir una propiedad personalizada directamente en el mismo elemento del que provenía la cifra. El valor salía del documento solo para ser reintroducido de inmediato, con el costo adicional de un script y una operación de dibujo extra.

La función attr() elimina ese rodeo. Ahora acepta un tipo, de modo que una hoja de estilo puede leer un atributo como longitud, color, número o identificador y utilizarlo en cualquier propiedad que acepte ese tipo. Esta guía explica la sintaxis, las cuatro formas en que se puede realizar una búsqueda, los patrones que reemplazan a los scripts comunes, las restricciones de seguridad en las URLs y cómo implementar esta funcionalidad mientras el soporte de los navegadores aún no es completo.

De generador de cadenas a valor tipado

El clásico attr() ha funcionado en todos los navegadores principales desde aproximadamente 2015, pero tenía una única función: generar una cadena para la propiedad content, generalmente en un pseudo-elemento.

/* the version you already know */
blockquote::after {
  content: " — " attr(cite);
}

Eso es útil para imprimir una cita o un destino de enlace, y nada más. Al colocar el antiguo attr() dentro de width, toda la declaración era rechazada, ya que la función solo devolvía cadenas y width no necesita cadenas.

CSS Values and Units Level 5 modifica este acuerdo: se indica al navegador qué tipo de valor contiene el atributo. Una vez que el motor sabe que se trata de una longitud, un color o un número, el resultado puede utilizarse en cualquier propiedad que permita ese tipo, lo que en la práctica significa casi todas las propiedades.

/* the version this article is about */
.bar   { width: attr(data-fill %, 0%); }
.chip  { background-color: attr(data-brand type(<color>), #888); }
.dial  { rotate: attr(data-deg deg, 0deg); }
.card  { view-transition-name: attr(id type(<custom-ident>), none); }

Dos aclaraciones mantienen las expectativas realistas, ya que esta función es fácil de exagerar en ambos sentidos.

  • No se trata de una forma de eliminar JavaScript del estilo. Escribir style="--fill: 87%" directamente en el HTML nunca ha requerido un script. Lo nuevo es algo más específico: utilizar el valor de un atributo en un estilo que antes requería un script, pero ahora ya no lo necesita.
  • Tampoco traslada la lógica de la aplicación al CSS. La aplicación sigue decidiendo que la tarea está completada en un 87 por ciento. El único cambio es quién lee ese número del elemento: el código que usted escribió, o la hoja de estilo que de todos modos se iba a aplicar.

Anatomía de la función

La estructura gramatical cabe en una sola línea.

attr( <attr-name> <attr-type>? , <fallback-value>? )

Utilice attr(data-fill %, 0%). Tiene tres partes: el nombre del atributo a leer, el tipo con el que se debe interpretar y un valor de respaldo cuando la búsqueda no produce algo utilizable.

El tipo puede escribirse de tres formas diferentes. La primera es type(), que envuelve cualquier tipo de datos CSS entre corchetes angulares.

/* 1. type() — any CSS data type */
color:  attr(data-brand type(<color>), crimson);
width:  attr(data-w type(<length>), 0px);
scale:  attr(data-s type(<number>), 1);

La segunda forma es una unidad simple, que indica “el atributo contiene un número puro; adjunte esta unidad a él”. La tercera es raw-string, que también es el valor predeterminado cuando no se especifica ningún tipo, y reproduce el comportamiento histórico.

/* 2. unit shorthand — the attribute holds a bare number */
width:           attr(data-w px);      /* data-w="10"    → 10px  */
rotate:          attr(data-deg deg);   /* data-deg="45"  → 45deg */
animation-delay: attr(data-i s);       /* data-i="2"     → 2s    */
width:           attr(data-fill %);    /* data-fill="87" → 87%   *//* 3. raw-string — the default, and the old behaviour */
content: attr(data-name raw-string, "stranger");
content: attr(data-name);   /* identical */

La abreviatura de unidad espera un número sin unidades

attr(data-w px) significa “leer el número en data-w y tratarlo como píxeles”. Por lo tanto, el atributo debe ser data-w="10". Escribir data-w="10px" no se interpreta, ya que la unidad es especificada por la hoja de estilo y no por el marcado. Cuando desea que el HTML incluya su propia unidad (por ejemplo, porque un editor podría introducir 2rem o 10px), utilice type(<length>) y almacene el valor completo.

Un fallback inválido falla temprano y de manera diferente

La alternativa se analiza junto con el resto de la declaración, antes incluso de que el navegador inspeccione el elemento. Si escribes attr(data-w px, banana), la declaración es inválida en el momento del análisis y se descarta por completo: el atributo nunca se lee, y en su lugar se aplica cualquier declaración anterior de la misma propiedad en la regla. Se trata de un fallo completamente distinto al de un atributo faltante, que solo se descubre mucho más tarde durante el cálculo del valor. Mantenerlos separados en tu mente facilita enormemente la depuración.

Los tipos que puedes declarar

Dentro de type() se aceptan los siguientes tipos, junto con lo que debe contener el atributo y los lugares típicos donde usar cada uno.

  • <length>: valores como "12px" o "2rem", para width, padding o inset.
  • <percentage>: "87%", para width o translate.
  • <length-percentage>: cualquiera de los dos anteriores, para cualquier elemento que requiera un tamaño.
  • <number>: "4.5", para opacity, scale o como entrada para calc().
  • <integer>: "3", para grid-column, z-index o order.
  • <color>: "#4fc3f7" o "tomato", para color y background-color.
  • <angle>: "45deg" o "0.25turn", para rotate o ángulos de gradiente.
  • <time>: "300ms", para animation-delay y transition-duration.
  • <resolution>: una densidad como "2dppx", utilizada por image-resolution.
  • <custom-ident>: un identificador como "card-3", útil para nombrar elementos tales como view-transition-name o anchor-name.
  • <string>: una cadena CSS entre comillas, generalmente utilizada por content.
  • <transform-function>: algo como "rotate(20deg)", usado con la propiedad transform.
  • <image> y <url>: presentes en la gramática pero efectivamente inutilizables, por razones explicadas en la sección de seguridad a continuación.
  • *: acepta cualquier valor y es útil principalmente para la detección de características.
  • Los números necesitan calc() para convertirse en tamaños

    Un <number> no tiene dimensión. 4.5 no puede establecer un ancho por sí solo, por lo que casi siempre termina dentro de calc(). El enfoque más ordenado es almacenarlo primero en una propiedad personalizada y realizar allí los cálculos.

    .stars {
      --r: attr(data-rate type(<number>), 0);
      width: calc(var(--r) * 20%);   /* 4.5 → 90% */
    }
    

    El parámetro intermedio --r no es solo una formalidad. Permite reutilizar el valor en varias declaraciones, y dado que las propiedades personalizadas se heredan, también es la forma en que el valor llega a los elementos descendientes; como verá, esta es la única manera de compartir un atributo con elementos hijos. Si ya utiliza propiedades personalizadas como medio de transmisión entre scripts y estilos, el artículo sobre propiedades personalizadas en tiempo de ejecución aborda el otro aspecto de este mecanismo.

    Cómo se resuelve una búsqueda: cuatro resultados, no dos

    Es tentador asumir que la función funciona o bien se ignora la regla. En realidad existen cuatro resultados distintos, y confundirlos es la causa más común de sorpresas.

    • El atributo está ausente. Si se proporciona un valor de respaldo, se utiliza. De lo contrario, el resultado es el valor garantizado como inválido y la declaración se vuelve inválida en el momento del cálculo del valor (IACVT).
    • El atributo existe pero está vacío (data-x=""). Con un attr() de tipo definido, una cadena vacía no se interpreta como el tipo correspondiente, por lo que se aplica el valor de respaldo. Sin embargo, una búsqueda sin tipo se considera un éxito que genera una cadena vacía, y se omite el valor de respaldo.
    • El valor no se puede interpretar como el tipo declarado. Se utiliza el valor de respaldo si está presente; de lo contrario, ocurre IACVT.
    • El valor se puede interpretar. Se obtiene el valor y el estilo se actualiza tan pronto como cambia el atributo.

    Qué significa realmente “inválido en el momento del cálculo del valor”

    Se detecta un error de sintaxis habitual durante el análisis. El navegador descarta esa declaración y entra en vigor una declaración anterior de la misma propiedad dentro de la misma regla. El IACVT ocurre mucho más tarde, después de que la cascada ya haya elegido una declaración ganadora, por lo que no queda nada a lo que recurrir. La propiedad se comporta como si estuviera establecida en unset: las propiedades heredadas adoptan el valor del padre, y las no heredadas vuelven a su valor inicial.

    .bar {
      width: 50%;                  /* cascade drops this: the next one parsed fine */
      width: attr(data-fill %);    /* attribute missing → IACVT → width: auto */
    }
    /* result: auto. NOT 50%. */
    

    Por esta razón, proporcionar siempre un valor de respaldo no es cuestión de preferencia personal. Sin uno, un nombre de atributo escrito incorrectamente genera silenciosamente auto, 0 o un color heredado, y la consola permanece en silencio.

    Por qué attr() sin tipado no puede generar un color

    La opción predeterminada de raw-string merece especial atención. Supongamos que una regla establece background-color a partir de un atributo sin tipo, y lo pruebas en elementos que difieren únicamente en ese atributo. El elemento que no tiene el atributo recibe el valor por defecto, como era de esperar. Cada elemento que tiene el atributo se muestra transparente, incluido aquel que contiene un nombre de color perfectamente válido.

    La explicación es que un attr() sin especificar tipo sustituye el texto literal del atributo como cadena CSS, y no como un valor CSS analizado. background-color no acepta cadenas, por lo que la sustitución falla al momento de calcular el valor. La solución alternativa no ayuda: solo cubre un atributo faltante o un fallo de análisis frente a un tipo declarado, y en esta regla no se declaró nada. La búsqueda tuvo éxito al devolver una cadena; falló un paso después al tratar de utilizarla como color, ya que no hay nada para capturar ese error.

    /* data-c="red" — and the element still renders transparent */
    .swatch { background-color: attr(data-c, gold); }
    

    La conclusión es sencilla: siempre que la propiedad objetivo no sea content, se debe declarar un tipo.

    Patrones que reemplazan a un script

    Varias tareas que los equipos realizan habitualmente con unas pocas líneas de JavaScript ahora pueden hacerse completamente dentro de la hoja de estilos.

    Un ancho proveniente de un atributo data

    El marcado contiene un número simple.

    <div class="bar" data-fill="87"></div>
    

    La hoja de estilo lo interpreta mediante la abreviatura porcentual y recurre a una barra vacía como alternativa.

    .bar {
      height: 8px;
      border-radius: 99px;
      background: linear-gradient(90deg, #4fc3f7, #2dd4bf);
      width: attr(data-fill %, 0%);
    }
    

    Un color elegido en un CMS

    .chip { background-color: attr(data-brand type(<color>), #888); }
    

    El color de la marca seleccionado por un editor de contenido es un dato y no parte del diseño. Con una función attr() de tipo específico, se puede controlar directamente la hoja de estilo, sin necesidad de escribir estilos en línea en el template. Dado que la validación de <color> se realiza en el navegador, un error tipográfico del editor simplemente genera la alternativa gris en lugar de CSS inválido.

    Animaciones escalonadas de listas

    Una entrada escalonada suele implementarse estableciendo un estilo en línea dentro de un bucle de renderizado. Sin embargo, el índice ya forma parte del marcado y no es necesario convertirlo en una propiedad de estilo.

    <li data-i="0">Inbox</li>
    <li data-i="1">Drafts</li>
    <li data-i="2">Sent</li>
    

    Cada elemento lee su propio índice como número, y calc() lo convierte en un retraso.

    li {
      --i: attr(data-i type(<number>), 0);
      animation: fade-in .45s both;
      animation-delay: calc(var(--i) * .09s);
    }
    

    Si el atributo ya contiene una duración completa, la abreviatura de unidad s puede leerla directamente.

    /* or, if the attribute already holds a duration */
    li { animation-delay: attr(data-delay s, 0s); }
    

    Rutear el índice mediante --i tiene una ventaja adicional: el paso de tiempo se convierte en una única constante CSS. Al cambiar .09s en un solo lugar, todas las listas escalonadas del sitio adoptan el nuevo ritmo.

    Un nombre único de transición de vista por tarjeta

    Este patrón añade algo que antes no era posible. Cada elemento que participa en una transición de vista debe tener su propio view-transition-name único, lo que anteriormente requería generar una regla por tarjeta o asignar nombres desde un script. Ahora, una sola regla se encarga de todas las tarjetas.

    .card {
      view-transition-name: attr(id type(<custom-ident>), none);
      view-transition-class: card;
    }
    

    Tenga en cuenta que el atributo aquí es id, no un atributo data-*. attr() puede leer cualquier atributo del elemento, incluyendo id, href, title, colspan o lang. El espacio de nombres data-* es simplemente donde HTML espera que se encuentre la data personalizada de la página. Combinar el nombre con view-transition-class le permite aplicar estilos a las transiciones de todas las tarjetas con un único selector, aun cuando cada una tenga un nombre único.

    Una forma útil de verificar cualquiera de estos patrones es eliminar temporalmente la configuración base basada en scripts y confirmar que el estilo sigue apareciendo; si es así, attr() está realizando la tarea.

    Pseudo-elementos: el caso sin solución alternativa

    Cada otro beneficio mencionado en esta guía tiene una alternativa: siempre se puede establecer una propiedad personalizada de forma inline. Los pseudo-elementos son diferentes. element.style apunta al propio elemento, y ::before o ::after no son nodos DOM, por lo que no hay nada a lo que se pueda asignar un estilo inline. Las opciones anteriores eran una propiedad personalizada heredada del elemento anfitrión (lo cual aún requiere aplicar estilo de alguna manera) o una hoja de estilos generada. En contraste, un atributo en el elemento anfitrión puede leerse directamente desde ambos pseudo-elementos.

    Aquí hay un medidor cuya única información es un único atributo.

    <div class="meter" data-pct="72"></div>
    

    La etiqueta utiliza la función de cadena clásica attr() en su content, la cual funciona en todos los navegadores.

    .meter::before {
      content: attr(data-pct) "% complete";
    }
    

    La barra utiliza el formulario de tipo para establecer su ancho a partir del mismo atributo.

    .meter::after {
      content: "";
      display: block;
      height: 10px;
      width: attr(data-pct %, 0%);
      background: linear-gradient(90deg, #fbbf24, #f472b6);
    }
    

    Un atributo alimenta a dos pseudo-elementos, y ninguno de ellos requiere un elemento hijo adicional. Las insignias de conteo, los anillos de progreso, el texto de las herramientas de ayuda, los prefijos ordinales, los sufijos de unidad y las etiquetas de datos de gráficos siguen todos este patrón: un pseudo-elemento que necesita un número que el elemento anfitrión ya posee. Antes de la función attr(), ese número tenía que escribirse dos veces, una vez como atributo para el texto y otra como propiedad personalizada para la geometría, y las dos copias podían desajustarse. Ahora se escribe una sola vez.

    attr() solo lee el elemento que se está estilizando

    La función lee los atributos del elemento al que corresponde la regla. No examina ancestros ni hermanos, y no existe un equivalente de closest(). Debido a que se activa la solución alternativa, este error ocurre de forma silenciosa, por lo que casi todos lo cometen al menos una vez.

    Considere una sección que declara un color de énfasis para su contenido.

    <section class="theme" data-accent="#4fc3f7">
      <h2>Title</h2>
    </section>
    

    Leer el atributo del encabezado no sirve de nada, ya que el encabezado no lo posee.

    /* WRONG — this reads the h2, which has no data-accent */
    .theme h2 { color: attr(data-accent type(<color>), #888); }
    /* every heading is #888, forever, with no error */
    

    La solución es leer el atributo una sola vez, en el elemento que lo contiene, y pasar su valor a través de una propiedad personalizada.

    /* RIGHT — read it once on the element that has it, then inherit */
    .theme    { --accent: attr(data-accent type(<color>), #888); }
    .theme h2 { color: var(--accent); }
    .theme a  { border-bottom: 2px solid var(--accent); }
    

    La herencia se encarga de distribuirlo. Como beneficio adicional, ahora cualquier subárbol puede sobrescribir el color de énfasis con una declaración ordinaria --accent, algo que el enfoque directo con attr() nunca podría permitir.

    Por qué las URLs están prohibidas

    Cada valor generado por attr() se marca como attr()-tainted, y un valor contaminado nunca debe utilizarse para construir una URL, por ningún camino. Ambas declaraciones no tienen efecto alguno:

    span[data-icon] { background-image: url(attr(data-icon)); }
    span[data-icon] { background: image-set(attr(data-icon)); }
    

    La contaminación se transmite junto con el valor. Redirigirla mediante una propiedad personalizada, envolverla en src() o image(), o anidarla dentro de otras funciones a cualquier profundidad mantiene la marca en su lugar. Dondequiera que termine dentro de un contexto URL, la declaración se vuelve inválida en el momento del cálculo del valor.

    El modelo de amenazas explica la regla. Los valores de los atributos suelen verse influenciados por los usuarios: nombres de visualización, identificadores del CMS y títulos de comentarios. Si una hoja de estilo pudiera convertir tal valor en una solicitud de red, cualquiera que pudiera escribir en ese campo podría hacer que el navegador de cada visitante obtuviera una dirección a su elección, junto con todo lo que el navegador normalmente adjunta a dichas solicitudes. Resolver este problema en la especificación fue lo que permitió a los fabricantes de navegadores acordar incluir esta función. El anuncio de WebKit en Interop 2026 deja claro este vínculo: ahora que las cuestiones de seguridad están resueltas en la especificación, todos los motores declararon estar listos para incluir esta capacidad y hacerlo de forma interoperable.

    Tenga en cuenta los límites de esa protección. Cualquier contenido dentro de un atributo es legible por todos los scripts de la página, visible en el código fuente y se incluye en el payload HTML. La prohibición de URLs cierra una vía de extracción de datos; no hace que los atributos sean privados. Nunca coloque información confidencial en ellos.

    Detección del soporte y despliegue progresivo

    El enfoque seguro consiste en utilizar una base común que todos los navegadores entiendan, y añadir la versión con formato específico para aquellos motores que lo soporten. Comience con la base común, en este caso definida mediante una propiedad personalizada inline.

    .bar {
      width: var(--fill, 0%);          /* baseline, everyone */
    }
    

    Luego, permita que los navegadores que lo soporten pasen a utilizar el atributo.

    @supports (width: attr(x %)) {
      .bar {
        width: attr(data-fill %, 0%);  /* modern browsers take over */
      }
    }
    

    La misma prueba está disponible en tiempo de ejecución si su código necesita tomar decisiones condicionales.

    // the same answer at runtime, if you need to branch in code
    if (CSS.supports("width", "attr(x %)")) {
      // typed attr() is available
    }
    

    Prueba contra una propiedad real

    Tenga cuidado con los fragmentos de detección que utilizan un nombre de propiedad genérico, como @supports (x: attr(x type(*))), el cual ha aparecido en la documentación de referencia. No existe ninguna propiedad CSS llamada x, y todos los navegadores responden con “falso” cuando una consulta de compatibilidad menciona una propiedad que no conocen, por lo que la prueba falla incluso en navegadores donde la función funciona. Utilice una propiedad que sí exista. La consulta @supports (width: attr(x %)) es fiable: width es una propiedad real, y attr(x %) solo puede ser interpretado por motores que implementen la abreviatura de unidad. Vale la pena consultar la referencia actual de MDN para attr(), ya que los ejemplos en la documentación sí se corrigen con el tiempo.

    Depender de la cascada en lugar de @supports

    Una variante más breve omite el bloque @supports y deja que la cascada tome la decisión.

    .bar {
      width: var(--fill, 0%);        /* everyone */
      width: attr(data-fill %, 0%);  /* supporting browsers override */
    }
    

    Un navegador que no puede analizar attr() dentro de width descarta esa declaración en el momento del análisis, por lo que se aplica la línea anterior. Ese comportamiento proviene de cómo el mecanismo de cascada maneja las declaraciones inválidas. Sin embargo, la forma @supports expresa explícitamente la intención y solo requiere un par de líneas adicionales, así que úsela si desea que el comportamiento sea evidente para futuros lectores; además, verifique la forma más corta en un navegador que no la soporte antes de confiar en ella.

    En ambas variantes, mantenga una solución alternativa dentro de attr() mismo. En un navegador *que sí la soporta*, la falta de un atributo no implica recurrir a la declaración var() anterior; la declaración attr() ya ha ganado en el proceso de cascada, por lo que se convierte en IACVT y el ancho se resuelve en auto.

    Soporte de navegadores en el momento de redactar

    El soporte está cambiando rápidamente, por lo que considere estas cifras como una instantánea y confírelas en Can I use antes de depender de ellas. A partir de agosto de 2026, aproximadamente el 70 por ciento de los usuarios globales contaba con un navegador que incluía la función attr() escrita:

    • Chrome 133 y versiones posteriores: está soportado, siendo el primero en incluirlo (vea el anuncio de Chrome para desarrolladores).
    • Edge 133 y versiones posteriores: está soportado, al compartir la misma versión que Chromium.
    • Firefox 155 y versiones posteriores: está soportado; presente pero desactivado por defecto en las versiones 152 a 154.
    • Samsung Internet 29 y versiones posteriores: está soportado.
    • Safari en ordenador: aún no está disponible; solo se encuentra en la versión Technology Preview.
    • Safari en iOS: aún no está disponible, y dado que todos los navegadores de iOS utilizan WebKit, esto abarca a todos ellos.

    El método heredado attr() que acepta únicamente cadenas dentro de content es un asunto distinto. Ha estado ampliamente disponible en Baseline durante una década, desde las versiones más tempranas de cada motor importante. Si lo único que necesita es content: attr(data-label), puede utilizarlo sin ninguna de las precauciones mencionadas anteriormente.

    Interop 2026 incluye a attr() entre sus áreas de enfoque. Interop es una colaboración anual en la que Apple, Google, Microsoft y Mozilla, junto con Igalia y Bocoup, seleccionan funcionalidades que faltan o están defectuosas en al menos un motor y se comprometen a mejorarlas a lo largo del año. El progreso se monitorea en un panel público basado en las pruebas compartidas de la Plataforma Web. Ese es el indicio público más claro de que se está trabajando en la compatibilidad con Safari, pero se trata de un compromiso, no de una fecha de lanzamiento. Planifique contar con una solución alternativa para el resto de 2026. Para conocer un enfoque más amplio sobre cómo distribuir CSS moderno siguiendo lineamientos seguros, consulte distribuyendo CSS moderno de forma segura.

    Elegir entre attr() y JavaScript

    Ninguna de las herramientas es la mejor en todas las situaciones. La división que sigue sirve como guía práctica.

    Preferir attr() cuando:

    • El marcado ya contiene el valor. Extraerlo a un script solo para establecerlo de nuevo en el mismo nodo no te aporta nada.
    • Tiene que estar justo en el primer frame. El atributo existe tan pronto como el analizador crea el elemento, mientras que un script se ejecuta más tarde y puede causar una corrección visible.
    • Un pseudo-elemento necesita el valor. Los estilos en línea establecidos a través de element.style no tienen forma de afectar a ::before o ::after.
    • Tienes una política de seguridad de contenido estricta. Los estilos en línea requieren unsafe-inline o un nonce; los atributos de datos no necesitan ninguno de los dos.
  • Los elementos se añaden dinámicamente. Los nodos recién insertados adoptan su estilo de las reglas ya existentes; no es necesario ejecutar nada nuevamente, y no se requiere un MutationObserver para monitorear el árbol.
  • Deseas una única fuente de verdad. El enfoque basado en scripts suele almacenar el valor tanto en un atributo como en un estilo inline, y estos dos pueden diferir.
  • Quieres que la presentación permanezca en CSS, visible para la cascada, las consultas de medios y contenedores, así como para el inspector de estilo del navegador.
  • Mantente con JavaScript cuando:

    • El valor no tiene razón para existir en el DOM. Una medición proveniente de un ResizeObserver o de un manejador de desplazamiento no debe convertirse primero en un atributo.
    • Actualiza cada frame de animación. Llamar a el.style.setProperty('--x', v) es más directo y evita escribir un atributo.
    • La carga es grande, anidada o confidencial. Cualquier contenido dentro de un atributo es una cadena visible públicamente.
    • Necesitas Safari hoy y el valor es importante. Una barra de progreso atascada en cero en un navegador es un defecto, no una solución alternativa adecuada.

    El rendimiento está notablemente ausente en la primera lista. Establecer un estilo en línea nunca ha sido un cuello de botella. Lo que se obtiene es un mejor almacenamiento para el valor y una mayor visibilidad del mismo, no mayor velocidad.

    Lista de posibles problemas

    • Siempre proporciona una solución alternativa. Un valor faltante o ilegible sin ella genera IACVT y una propiedad unset, no la declaración anterior.
    • Un atributo vacío no es uno que falte. Si no se especifica el tipo, data-x="" devuelve una cadena vacía y la solución de respaldo nunca se activa.
    • No especificar el tipo significa texto literal. background-color: attr(data-c, gold) con data-c="red" no muestra nada; hay que indicar el tipo.
    • No se permite usar url() ni image-set(). Esta limitación es intencionada y persiste incluso con propiedades personalizadas.
    • Solo se lee el elemento al que corresponde la regla. Para los elementos ancestros, guarde el valor en una propiedad personalizada para que herede ese valor.
    • Los abreviaturas de unidades requieren números sin formato. Use data-w="10", no data-w="10px".
    • Una solución de respaldo inválida hace que la declaración falle al momento del análisis, lo cual es un error distinto y que ocurre antes que el caso de un atributo faltante.
    • Un <number> no puede definir el tamaño de nada por sí solo. Guárdelo y multiplíquelo en calc().
    • Los atributos son públicos. Mantenga la información confidencial fuera de ellos.
    • Pruebe la funcionalidad con una propiedad real, como @supports (width: attr(x %)).
    • Safari aún no lo ha implementado en el momento de escribir esto, así que mantenga una referencia para todo lo esencial.

    Conclusión

    La versión escrita de attr() supone un pequeño cambio sintáctico que tiene un efecto claro en la arquitectura: los valores que ya existen en HTML ya no necesitan un script para convertirse en estilos. Esta funcionalidad fomenta la precisión. Declare el tipo, escriba siempre un valor de respaldo, lea el atributo en el elemento que lo contiene y transfiéralo a través de propiedades personalizadas, aceptando que las URLs están permanentemente fuera de alcance. Combine cada uso esencial con un valor base hasta que Safari lo implemente, y este patrón puede entrar en producción hoy mismo, con la hoja de estilos asumiendo automáticamente el control a medida que aumente el soporte. La definición formal se encuentra en el borrador CSS Values and Units Level 5 si necesita las reglas de análisis exactas.

    Lecturas relacionadas