Bordes con imágenes

La técnica 9-slice para crear bordes con imágenes


Una de las limitaciones que tenía CSS, es que, si en lugar de utilizar los bordes de los que disponemos en CSS (sólidos, punteados, etc...) quisieramos hacer algo un poco más complejo con imágenes, podría volverse una tarea muy complicada. Por esa razón, CSS3 incorporó en su momento un sistema para crear bordes extensibles basados en una imagen de molde.

La técnica 9-slice

Dicho sistema se denomina 9-slice (muy utilizado en videojuegos) y se basa en delimitar una imagen trazando cuatro líneas (en rojo). Esto hará que la imagen quede dividida en 9 fragmentos, donde el fragmento central es descartado y el resto es utilizado de molde para los bordes de un elemento:

Sistema 9-slice

De esta forma, los fragmentos 1, 3, 7 y 9 se utilizarán para las esquinas y los fragmentos 2, 4, 6 y 8 se utilizarán para los bordes laterales, pudiendo expandirlos si se requiere y considera necesario con alguna de las propiedades que veremos a continuación.

Propiedades para crear bordes

Las propiedades que podemos utilizar para crear bordes con imágenes son las siguientes:

Propiedad Descripción
border-image-width Especifica el grosor de la imagen utilizada para el borde.
border-image-source Imagen a utilizar para los bordes con imágenes mediante la técnica 9-slice.
border-image-slice Distancia donde se hace el punto de corte en la imagen.
border-image-outset Tamaño en el que el borde crece hacia fuera. Una especie de padding del borde.

Estas propiedades pueden ser algo complicadas, por lo que vamos a utilizar la siguiente imagen expandible (imagen de la izquierda), que simula ser un antiguo carrete fotográfico, como borde con imagen. Las líneas rojas no forman parte de la imagen original, sino que se utilizan en este ejemplo para dejar claro cuáles serían los límites marcados con border-image-slice. Una vez hecho esto, conseguiremos el resultado de la imagen de la derecha, en el cuál podremos ampliar el texto del elemento lo que queramos, que se adaptará solo a su contenido:

Ejemplo con border-image en CSS

Para ello, utilizaremos el siguiente código CSS, donde antes que nada, establecemos un ancho al elemento con width y un border básico para que, en el caso de navegadores que no soporten border-image, tenga al menos un borde básico y nos sirva también como base a lo que vamos a hacer a continuación:

.element {
  width: 200px;
  margin: 25px;
  border: 42px solid black;
  border-image-width: 42px;
  border-image-source: url("photo-9slice.png");
  border-image-slice: 110;
  border-image-outset: 0px;
  border-image-repeat: repeat;
}
<p>Imagen original:</p>
<div class="element"></div>

<p>Imagen con más alto:</p>
<div class="element">
  Este elemento debería ser más grande ya que
  tiene más contenido que mostrar.
</div>

Para establecer los límites y poder utilizar imágenes en los bordes, hemos hecho uso de varias de las propiedades CSS necesarias para los bordes con imágenes. Veamos una por una para ver como funcionan.

La propiedad border-image-width

La propiedad border-image-width indica el tamaño que tendrá el borde de la imagen. El tamaño puede ser indicado con unidades (píxeles o porcentajes, por ejemplo) o como un , sin indicar ninguna unidad, lo que lo tomará como múltiplo del tamaño establecido en border-width.

Propiedad Valor
border-image-width 1 | | | auto

En nuestro ejemplo, tanto indicarle un valor de 1 como dejarlo sin especificar, sería lo mismo que darle un tamaño de borde de de 42px, pero por ejemplo, si indicamos border-image-width: 2, le estaremos indicando que use un tamaño de 84px. Por otro lado, y al igual que vimos en el apartado de margin, se puede indicar 1, 2, 3 o 4 parámetros .

body {
  height: 400px;
}

.element {
  width: 500px;
  margin: 25px;
  /* border */
  border-width: 42px;
  border-style: solid;
  border-color: black;
  /* border-image */
  border-image-width: 42px;
  border-image-source: url("photo-9slice.png");
  border-image-slice: 110;
  border-image-outset: 0px;
  border-image-repeat: repeat;
  text-align: center;
}
const element = document.querySelector(".element");
const biwInput = document.querySelector("input.biw");
const biwOutput = document.querySelector("input.biw + output");
const bwInput = document.querySelector("input.bw");
const bwOutput = document.querySelector("input.bw + output");

biwInput.addEventListener("input", () => {
  biwOutput.value = element.style.borderImageWidth = `${biwInput.value}px`;
});

bwInput.addEventListener("input", () => {
  bwOutput.value = element.style.borderWidth = `${bwInput.value}px`;
});
<p>Propiedad <code>border-image-width</code>: <input class="biw" type="range" min="0" max="125" value="42"><output>42px</output></p>
<p>Propiedad <code>border-width</code>: <input class="bw" type="range" min="0" max="125" value="42"><output>42px</output></p>

<div class="element">
  Aquí el contenido.
</div>

Consejo: No olvides que hay que indicar un border-width y un border-style para que el borde CSS esté definido y se pueda visualizar.

La propiedad border-image-source

La propiedad border-image-source establece, mediante la función CSS url(), la imagen que vamos a utilizar para crear nuestro borde con imágenes. Para ello, indicaremos una imagen al igual que lo hacemos con la propiedad background-image:

Propiedad Valor
border-image-source none | | url("imagen.png")

Veamos algunos ejemplos:

body {
  height: 400px;
}

.element {
  width: 500px;
  height: 190px;
  margin: 25px;
  /* border */
  border: 42px solid black;
  /* border-image */
  border-image-width: 42px;
  border-image-source: url("photo-9slice.png");
  border-image-slice: 110;
  border-image-outset: 0px;
  border-image-repeat: repeat;
  display: grid;
  place-items: center;
}

select {
  padding: 1rem;
  font-size: 1.5rem;
}
<p>Propiedad <code>border-image-source</code>:</p>
<select>
  <option>url("photo-9slice.png")</option>
  <option>linear-gradient(indigo, purple, deeppink)</option>
  <option>radial-gradient(red, gold, green)</option>
  <option>none</option>
</select>

<div class="element">
  Aquí el contenido.
</div>
const select = document.querySelector("select");
const element = document.querySelector(".element");
select.addEventListener("change", () => {
  element.style.borderImageSource = select[select.selectedIndex].textContent;
});

Truco: ¡Recuerda que como imagen de fondo puedes usar gradientes de CSS, ya que internamente se interpretan como imágenes!

La propiedad border-image-slice

La propiedad border-image-slice define el desplazamiento de las líneas divisorias de la imagen, o lo que es lo mismo, el tamaño de los bordes. Por defecto, el valor es de 100% (tamaño de ancho completo de la imagen), pero también podemos usar números sin unidad , que simbolizan píxeles de recorte. Se pueden especificar 1, 2, 3 ó 4 parámetros .

Propiedad Valor
border-image-slice 100% | | fill

Por otro lado, añadiendo la palabra clave opcional fill, indicaremos que queremos rellenar el elemento con el fondo del fragmento 5, que por defecto es descartado. Útil en casos que quieras aprovechar el fondo.

En nuestro caso, nos podría valer tanto con 110 (110 píxeles de recorte) como con 23%, ya que es más o menos la cantidad apropiada para establecer el límite tanto de ancho como de alto.

body {
  height: 400px;
}

.element {
  width: 500px;
  height: 190px;
  margin: 25px;
  /* border */
  border: 42px solid black;
  /* border-image */
  border-image-width: 42px;
  border-image-source: url("photo-9slice.png");
  border-image-slice: 110;
  border-image-outset: 0px;
  border-image-repeat: repeat;
  display: grid;
  place-items: center;
}

select {
  padding: 1rem;
  font-size: 1.5rem;
}
<p>Propiedad <code>border-image-slice</code>: <input type="range" min="0" max="200" value="110"><output>110</output></p>

<div class="element">
  Aquí el contenido.
</div>
const element = document.querySelector(".element");
const input = document.querySelector("input");
const output = document.querySelector("output");

input.addEventListener("input", () => {
  output.value = element.style.borderImageSlice = input.value;
});

La propiedad border-image-outset

La propiedad border-image-outset establece el factor de crecimiento (hacia fuera) de la imagen. Muy útil para compensar la imagen si se extiende hasta el contenido. Usar con cuidado, ya que puede desplazar el contenido.

Propiedad Valor
border-image-outset 0 |
body {
  height: 450px;
}

.element {
  width: 200px;
  height: 125px;
  margin: 100px;
  /* border */
  border: 42px solid black;
  /* border-image */
  border-image-width: 42px;
  border-image-source: url("photo-9slice.png");
  border-image-slice: 110;
  border-image-outset: 0px;
  border-image-repeat: repeat;
  display: grid;
  place-items: center;
}

select {
  padding: 1rem;
  font-size: 1.5rem;
}
<p>Propiedad <code>border-image-outset</code>: <input type="range" min="0" max="100" value="0"><output>0px</output></p>

<div class="element">
  Aquí el contenido.
</div>
const element = document.querySelector(".element");
const input = document.querySelector("input");
const output = document.querySelector("output");

input.addEventListener("input", () => {
  output.value = element.style.borderImageOutset = input.value + "px";
});

Por defecto, esta propiedad no tiene desplazamiento, o lo que es lo mismo, el valor por defecto es 0.

La propiedad border-image-repeat

Habremos comprobado que en algunas ocasiones, el modo en que se repite la imagen de borde no es la apropiada, o al menos, no es la que más se adapta a nuestro caso específico. Este comportamiento se puede variar mediante la propiedad border-image-repeat:

Propiedad Valor Significado
border-image-repeat [repetición en X e Y] 1 parámetro. Comportamiento de repetición en ambos ejes.
[rep. en X] [rep. en Y] 2 parámetros. Comportamiento de repetición por separado.

Con dicha propiedad se establece como deben comportarse los fragmentos del borde y el tipo de repetición que deben efectuar. Se puede usar la modalidad de un parámetro en la que se aplica a todos los bordes, o la modalidad de dos parámetros donde estableces diferente comportamiento para los bordes horizontales y verticales.

Esta propiedad puede tomar los siguientes valores:

Valor Significado
stretch Los bordes se estiran hasta rellenar el área. Es el valor por defecto.
repeat Los bordes se repiten hasta rellenar el área.
round Igual que repeat, pero estira los fragmentos individualmente hasta rellenar el área completa.
space Igual que repeat, pero añade espacios hasta rellenar el área completa.

Para verlo más claramente, echemos un vistazo a esta representación visual del comportamiento de cada uno:

Border-image-repeat values

Es importante recalcar los dos últimos valores (round y space) actúan igual que repeat, pero con un comportamiento ligeramente diferente que nos puede interesar en el caso de que la zona repetida quede descompensada.

Utilizando la imagen y código CSS anterior, obtendríamos un resultado similar a este, que se adaptaría sólo al contenido que escribamos dentro del elemento HTML con clase borde :

.element {
  width: 600px;
  margin: 25px;
  padding: 1rem;
  border: 42px solid black;
  border-image-width: 42px;
  border-image-source: url("photo-9slice.png");
  border-image-slice: 110;
  border-image-outset: 0px;
  border-image-repeat: round;
}

select {
  padding: 1rem;
  font-size: 1.5rem;
}
<p>Propiedad <code>border-image-repeat</code>:</p>
<select>
  <option>stretch</option>
  <option>repeat</option>
  <option>round</option>
  <option>space</option>
</select>

<div class="element">
  En un lugar de la Mancha, de cuyo nombre no quiero acordarme, no ha mucho
  tiempo que vivía un hidalgo de los de lanza en astillero, adarga antigua,
  rocín flaco y galgo corredor. Una olla de algo más vaca que carnero,
  salpicón las más noches, duelos y quebrantos los sábados, lantejas los
  viernes, algún palomino de añadidura los domingos, consumían las tres
  partes de su hacienda.
</div>
const select = document.querySelector("select");
const element = document.querySelector(".element");
select.addEventListener("change", () => {
  element.style.borderImageRepeat = select[select.selectedIndex].textContent;
});

También es posible indicar dos parámetros en la propiedad border-image-repeat, de modo que cada parámetro se aplica a cada eje (horizontal y vertical).

Atajo: Bordes con imágenes

Como suele ser costumbre, este tipo de propiedades tienen una propiedad para ahorrar espacio y escribirlo todo de una sola vez. En este caso, la sintaxis es la siguiente:

div {
  /* border-image: <source> <slice> <width> <outset> <repeat> */

  border-image: url("photo-9slice.png") 23% 1 0 round;
}

¿Quién soy yo?

Soy Manz, vivo en Tenerife (España) y soy streamer partner en Twitch y profesor. Me apasiona el universo de la programación web, el diseño y desarrollo web y la tecnología en general. Aunque soy full-stack, mi pasión es el front-end, la terminal y crear cosas divertidas y locas.

Puedes encontrar más sobre mi en Manz.dev