Vendor Prefixes: Prefijos propietarios en CSS3
Vendor Prefixes: Los prefijos propietarios en CSS
Últimamente se está oyendo hablar mucho acerca de los vendor prefixes que podemos traducir como prefijos propietarios o prefijos CSS del navegador. Son prefijos como -webkit-, -moz-, -o-, -ms-
que se anteponen al nombre de la propiedad de estilo para que el navegador correspondiente reconozca esa característica. Se aplican a estilos en experimentación que aún no forman parte de una especificación o estándar. La prudencia aconseja no usar prefijos CSS en sitios en producción, pues esas propiedades pueden sufrir alteraciones en el desarrollo de su implementación. Aunque podemos gestionar el acceso a esas propiedades por todos los navegadores usando los prefijos, esto no asegura que todos implementen de la misma forma la característica. E incluso los valores a adjudicar a una propiedad determinada pudieran ser diferentes en cada navegador aunque tuviesen el mismo nombre de propiedad tras su prefijo particular. Por lo tanto mientras no sea estándar debemos tener cuidado con estas nuevas propiedades. Debemos asegurarnos que el nuevo estilo no compromete a la seguridad y que sí lo nuevo no funciona al menos no nos rompa la página.
El pasado mes de Abril 2012 se ha anunciado que Opera asumirá los prefijos -webkit-
, como puede ver en este artículo de Bruce Lawson Opera Mobile Emulator build with experimental WebKit prefix support. Explica que los desarrolladores web (especialmente de móviles) están usando sólo -webkit-
e ignoran otros prefijos o incluso escribir la propiedad sin prefijo. Para evitar la pérdida de páginas que puedan verse con este navegador, éste procesará -webkit-
. Hay muchos artículos sobre este tema de los vendor prefixes, como este de Henri Sivonen (Mozilla) titulado Vendor Prefixes Are Hurting the Web o este otro de Kevinjohn Gallagher (Opera) con el título Is the fat lady singing for Vendor Prefixes?. Dado que estoy empezando a practicar con CSS3, me interesa este tema pues muchas propiedades hay que usarlas con prefijo. Intentaré entender todo este embrollo y trasladarlo aquí lo mejor que sepa.
Sobre los vendor prefixes podemos consultar lo que el W3C dice en CSS2.1 vendor-specific extensions o en esta página del CSS Working Group Wiki. Se puede observar que los nombres de propiedades que empiecen por "-"
o "_"
nunca serán usados en una especificación futura de CSS. Por eso se utilizan los prefijos por parte de los desarrolladores de navegadores para experimentar con nuevas características. Viene a ser algo como esto: un navegador hipotético llamado One quiere implementar una propiedad que llamará foo
, que admite un par de valores alternativos bar
o baz
. Digamos que implementará foo:bar|baz
. Si tras un tiempo de implementación al final es aceptada como estándar pero con otros valores como foo:barbaz|none
tendría un problema con esto, pues es la misma propiedad con distintos valores. Por lo tanto usará -foo:bar|baz
y evitaría el contratiempo con la especificación, pues cuando esta se hiciera efectiva solo tendría que agregar otra propiedad foo:barbaz|none
. Pero durante las fases iniciales otro navegador llamado Two podría experimentar con -foo:all|none|barfoobaz
y ahora el problema es para los desarrolladores web. Para evitarlo el primer navegador la usará con su prefijo -one-foo
y para el segundo será -two-foo
. Ahora bien, si el desarrollador web quiere experimentar con una propiedad no estándar deberá tener en cuenta todos los prefijos de todos los navegadores, si es que pretende cubrir el mayor rango posible de ellos (sin volverse loco en el intento).
Pero el caso es que en un momento dado no deberían convivir la misma propiedad con o sin prefijo. Un ejemplo: en Septiembre 2011 puse un tema sobre las sombras en CSS3 con la propiedad box-shadow
. En ese momento lo probé en Firefox 7.0.1, Opera 11.51, Chrome 14.0 y Safari 5.0 que eran las versiones actuales entonces. En todos estos a excepción de Safari funcionaba con la propiedad box-shadow
, requeriendo ese navegador el prefijo -webkit-
. Este es el código que usé:
<div id="ejemplo-box-shadow" style="width: 200px; height: 75px; background-color: orange; border: blue solid 1px; box-shadow: 20px 15px 10px 5px rgba(0,0,255,0.5); -webkit-box-shadow: 20px 15px 10px 5px rgba(0,0,255,0.5); "></div>
A fecha de hoy (primeros de Mayo 2012) tengo la versión Chrome 18.0 y Safari 5.1.5 y ya funciona la propiedad box-shadow
en este último. Ambos usan el mismo WebKit y la misma herramienta que permite ver un elemento. En la imagen anterior capturada con esa herramienta puede ver el estilo declarado para ese elemento para Chrome y Safari, observándose que ambas propiedades son "aparentemente" establecidas como conformes, es decir, no aparecen tachadas como sobrescritas o desconocidas. Se supone que si el navegador encuentra la propiedad sin prefijo debe desechar la prefijada. Además en el estilo computado, el que al final utiliza el navegador para gestionar el elemento, aparecen ambas. La vemos como -webkit-box-shadow
:
Y un poco más abajo también como box-shadow
:
Ejemplo comparativo con vendor-prefixes
Hagamos un comparativo con Chrome 18.0/Safari 5.1.5, Firefox 12.0 y Opera 11.62 (versiones actuales). Veamos este ejemplo de un elemento con sombra:
Ejemplo:
-webkit-box-shadow: 20px 15px 10px 5px rgba(0,0,255,0.5) -moz-box-shadow: 20px 15px 10px 5px rgba(0,0,255,0.5); -o-box-shadow: 20px 15px 10px 5px rgba(0,0,255,0.5);
Se trata de un elemento <pre>
con -XXX-box-shadow
prefijado con webkit
para Chrome/Safari, moz
Firefox y o
para Opera. No incluyo a propósito la propiedad sin prefijo box-shadow
. Como los tres la soportan sin prefijo, ninguno de ello debería presentar la sombra. Esto es lo que se espera, pero solo lo hace correctamente Opera. Veamos que dice la herramienta de cada uno inspeccionando el estilo. En Opera no aparece la propiedad dentro del grupo de las prefijadas (las que empiezan por -o-
), lo que es lógico pues Opera implementó esta propiedad desde el principio sin prefijo:
Vemos la estándar box-shadow: none
un poco más abajo en esa lista:
Opera 11.62 hace lo que debe puesto que ha adoptado box-shadow
debe ignorar -o-box-shadow
. -De todas formas de esto sólo se me puede culpar a mí, puesto que tenía que conocer que -o-box-shadow
no sería soportado por Opera. Pero veamos que hace Firefox 12. No encontramos -moz-box-shadow
en el grupo de las prefijadas, aunque este navegador si usó el prefijo desde el inicio de la implementación:
Sin embargo sí vemos que ha traducido -moz-box-shadow
a box-shadow
, lo que quiere decir que en el fondo está soportando su prefijo:
Por último veamos Chrome/Safari, encontrándola entre las prefijadas:
Y las que no tienen prefijo:
¿Qué sucede si ya hay un montón de páginas que usan el prefijo webkit
o moz
para box-shadow
pero no agregan esta propiedad sin prefijo? Pues que se está relegando a otros navegadores que usan la propiedad sin prefijo. La culpa al final es de los desarrolladores web, pero en el fondo auspiciado por los navegadores para no perder páginas ya erróneamente desarrolladas.
Por lo tanto puedo deducir que en el caso de que use una propiedad nueva tendría que consultar la documentación de los navegadores para ver si la implementan con o sin prefijo. Y poner primero las prefijadas y la última sin prefijo. Por ejemplo, si one, two, three
son prefijos para la propiedad foo
pondría algo así:
elemento.clase { -one-foo: ...; -two-foo: ...; -three-foo: ...; ... foo: ...; }
Poner primero las prefijadas y la última sin prefijo obedece a que los navegadores deberían sobrescribir los estilos que se vayan encontrando. Por ejemplo, puede encontrar color:"yellow"
y más adelante color:"red"
, de tal forma que esta última sobrescribirá la anterior. Así que aunque un navegador aún no soporte la propiedad sin prefijo, cuando ese momento llegue debería sobreecribir la prefijada.
Cómo deciden los navegadores incluir un vendor prefix a una nueva propiedad de CSS
El criterio para que un navegador comience usando prefijo no está claro. Antes vimos que Opera implementó box-shadow
sin prefijo desde el inicio. Pero Firefox implementó -moz-box-shadow
con prefijo y sin embargo no se lo puso a text-shadow
. ¿Porqué?, esto lo pregunta alguien y la respuesta en WebTech de Mozilla Developer Center es:
Robert O'Callahan Says: Sometimes it’s a tough call. I think generally if a feature is in a W3C spec which is in Last Call or Candidate Recommendation, we’d not bother with a vendor prefix. Also, if another major browser has shipped an implementation of the feature without a vendor prefix, we’d follow. Opera and Safari have already shipped text-shadow without a vendor prefix.
A veces es difícil. Pienso que generalmente si una característica ya está en un avanzado estado en la especificación ( LC, CR ), no vamos a usar un prefijo. También si otro navegador importante ya ha iniciado la implementación de la característica sin prefijo le seguimos. Opera y Safari ya habían iniciado text-shadow sin prefijo.
No me parece una respuesta convincente y tampoco hay un estándar que normalice la situación. Por lo que no sabremos a qué atenernos cuando aparezca una nueva característica ¿La implementaran con o sin prefijo? Mientras tanto a escribir prefijos como locos o usar atajos JavaScript para solucionarlo. Por ejemplo, a PrefixFree de Lea Verou. Por mi parte en este sitio desde hace tiempo ya estoy usando una función que he creado llamada estiloNoCss()
para incorporar con JavaScript propiedades aún no estándar. En aquel momento para usar con opacity
y filter: alpha(opacity...)
de IE8 (por cierto IE9 soporta ambas a la vez). También para user-select
que, aunque estaba en el borrador WD del año 2000 W3C user-select
, ahora ya no se contempla como parte del estándar, ni siquiera en fase de borrador. Reconozco que estiloNoCss()
no es una buena solución, pues se apoya en la identificación del navegador y esto no es adecuado. Pues cualquier cosa que hagamos para controlar las diferencias en el manejo de características de HTML o CSS debería basarse en la existencia de la misma y no en el navegador.
estiloNoCss()
de este sitio. Ahora con el gestor de vendor prefixes VpForCss puedo determinar si un navegador soporta una determinada propiedad, en cuyo caso puedo obtener su prefijo si lo llevara o bien un valor null si no la soporta.