Un viaje con viñetas (que no llega muy lejos) | Programar Plus

Ol’ Trent publicó una publicación de consejos rápidos sobre el uso del recuadro box-shadow para algunos efectos simples pero con clase. Una de esas técnicas terminó con un efecto de viñeta sobre la parte superior de una imagen. Como esto:

Sí, podrías hacerlo en Photoshop, pero hacerlo a través de CSS significa 1) no es destructivo 2) puede modificarse mediante programación 3) es súper consistente

Cuando vi el ejemplo por primera vez, pensé, bueno, eso es genial, pero ¿por qué el div de envoltura? los navegadores permiten box-shadow sobre <img> elementos, así que ¿por qué no usar simplemente un inset caja-sombra en él. Eso sería HTML más limpio.

<img src="https://css-tricks.com/vignetting-woes/amazinglandscape.jpg" alt="ooooh ahhhh" class="vignette">
.vignette {
	-webkit-box-shadow: inset 0px 0px 85px rgba(0,0,0,0.4);
	-moz-box-shadow:    inset 0px 0px 85px rgba(0,0,0,0.4);
	box-shadow:         inset 0px 0px 85px rgba(0,0,0,0.4);
}

Resulta que puede usar sombras de cuadro insertadas en las imágenes, es solo que el navegador lo coloca detrás de la imagen, lo que significa que si usa una imagen completamente opaca (muy probablemente si está interesado en viñetearla), no verá el sombra en absoluto. A lo que Trento dice:

Si bien esto parece bastante inútil, tiene sentido cuando consideras otros tipos de contenido. Por ejemplo, si inserta una sombra, probablemente no querrá que eclipse el texto que contiene.

También estoy de acuerdo con otro punto que hace Trent: tal vez la especificación oficial de box-shadow debería cambiarse para decir que cuando se aplica directamente a una imagen, la sombra debe estar en la parte superior. Por desgracia, la especificación ni las implementaciones lo hacen de esta manera.

Entonces pensé, espera un minuto, no recurramos a un elemento de envoltura todavía. Después de todo, mantener nuestro HTML libre de envoltorios no semánticos es un objetivo noble. ¿Por qué no usamos un pseudo elemento en la imagen, que podemos forzar para que se siente encima y tenga el tamaño exacto de la imagen, y le ponemos la sombra del recuadro insertado?

.vignette:after {
	-webkit-box-shadow: inset 0px 0px 85px rgba(0,0,0,0.4);
	-moz-box-shadow:    inset 0px 0px 85px rgba(0,0,0,0.4);
	box-shadow:         inset 0px 0px 85px rgba(0,0,0,0.4);

	position: absolute;
	top: 0; left: 0; bottom: 0; right: 0;
	content: "";
}

Mientras trabajaba en una demostración para esto, seguía sin funcionar y me confundía muchísimo. Por un minuto pensé, me pregunto si :antes y :después de los pseudo elementos simplemente no funcionan en los elementos en línea*, pero probé que funcionaban con tramos y eso estaba descartado.

Entonces escuché de Nicolás Gallagher:

Ningún elemento con un modelo de contenido ‘vacío’ admite contenido generado a través de pseudoelementos

Por lo que Nicolás esencialmente se refiere a cualquier elemento que no tiene innerHTML. Pensar:

<br />
<br />
<input />
<img />

Esto es aparentemente porque la especificación no explicó cómo tratar con elementos de este tipo.

Al igual que la cosa de la sombra del cuadro insertado, tiene sentido. Puede pensar en un pseudo elemento :before como si estuviera dentro de las etiquetas pero antes de otro contenido, y un pseudoelemento :after como si estuviera con las etiquetas pero después de otro contenido.

<div>
   <!-- :before pseudo element -->
   Hi! I'm content!
   <!-- :after pseudo element -->
</div>

<img /> <!-- Where would pseudo content go? -->

Para la gente de jQuery, Tim Wright explica:

es que :after se parece más a .append() de jquery que a .after(), por lo que no puede colocar algo en medio de <img>

Si estás pensando, amigo, solo pon los pseudo elementos DESPUÉS o ANTES del maldito elemento, no estás solo. El problema es que no es consistente con la forma en que funciona con otros elementos y la especificación no lo cubre. Para hacer las cosas un poco más complicadas, eso es lo que hace Opera.

Así que de todos modos… eso es todo para obtener ideas sobre cómo hacerlo limpiamente. Solo usemos el div de envoltura y terminémoslo con:

<div class="vignette">
	<img src="https://css-tricks.com/vignetting-woes/handsomepeople.jpg" alt="ooooh ahhhh">
</div>
.vignette {
	-webkit-box-shadow: inset 0px 0px 85px rgba(0,0,0,0.4);
	-moz-box-shadow:    inset 0px 0px 85px rgba(0,0,0,0.4);
	box-shadow:         inset 0px 0px 85px rgba(0,0,0,0.4);
	
	line-height: 0;         /* ensure no space between bottom */
		
	display: inline-block;  /* don't go wider than image */
}
.vignette img {
	position: relative; 
	z-index: -1;            /* position beneath */
}

¿Un efecto secundario posiblemente deseable, posiblemente molesto? Con la superposición div, no puede hacer clic con el botón derecho y guardar la imagen o arrastrarla a su escritorio. Protección de imagen súper primitiva.

Ver demostración

Relacionado

  • Otra técnica de Dudley Storey en la que pones la imagen como imagen de fondo del elemento viñetado. Probablemente mejor para sitios únicos pero más difíciles de usar para sitios en los que solo desea poder usarlo donde quiera sin necesidad de cambiar CSS todo el tiempo.

* Por cierto, estaba leyendo Introducción a HTML5 y aprendí un punto interesante. En HTML5, realmente no hay distinción entre un elemento “en línea” y un elemento de “bloque”. Todos los elementos están en línea hasta que un navegador o una hoja de estilo del usuario especifique lo contrario.