33

Leí la documentación de calc() y entendí la sintaxis, pero al estar en CSS, donde no se ejecuta ningún código, no puedo hacerme la idea de cuándo se debería usar.

El ejemplo paradigmático me suena bastante simple y no sé si tiene demasiada utilidad:

/* property: calc(expression) */
width: calc(100% - 80px);

¿Existen casos canónicos para el uso de calc()? ¿Podríais nombrar algún ejemplo en los que su uso sea práctico para una solución de un caso real?

Veo que su uso está bastante extendido entre los navegadores (93% según Can I use...). ¿Qué tan recomendado es usarlo y cuál sería su equivalente para los navegadores que no lo aceptan (IE 8 y anteriores, con algún problema con IE 9)?

Shaz
  • 28,742
  • 18
  • 37
  • 61
fedorqui
  • 15,850
  • 17
  • 58
  • 112
  • 1
    Yo la uso para retirar bordes al total, por ejemplo si tiene un ancho de 100% y bordes de 2px, pues le hago un calc de 100%-4px – GDP Jul 20 '17 at 11:32
  • 2
    Si hicieras `box-sizing: border-box` te ahorrarías tener que cambiar el valor del `calc` cada vez que se cambie el tamaño de los bordes o se agregue/cambie el padding ‎;) – Alvaro Montoro Jul 20 '17 at 13:13
  • @AlvaroMontoro ¿podrías ampliar el comentario para los novatos en el tema? – fedorqui Jul 21 '17 at 08:32

4 Answers4

26

Conozco varios casos donde no es canónico su uso, pero es mucho más sencillo hacerlo con calc que de cualquier otra manera.

Posicionamiento de imágenes de fondo

Es bastante sencillo posicionar una imagen de fondo

.ejemplo {
  width: 500px; 
  height: 500px;
  border: 1px solid black;
  background-image: url('https://i.imgur.com/W51iG94.png');
  background-repeat: no-repeat;
  background-position: 150px 50px;
}
<div class="ejemplo"></div>

Pero ¿qué ocurre cuando queremos posicionar la imagen de fondo en relación a los lados derecho e inferior y no conocemos de antemano el tamaño del elemento contendor? Con calc es lo más sencillo.

.ejemplo {
      width: 100%; 
      height: 500px;
      border: 1px solid black;
      background-image: url('https://i.imgur.com/W51iG94.png');
      background-repeat: no-repeat;
      background-position: calc(100% - 150px) calc(100% - 50px);
    }
<div class="ejemplo"></div>

Mucho más sencillo así.

Creación de un sistema GRID

Imagina que quieres diseñar tu propio sistema GRID, pongamos de 7 columnas.

Esto provocaría que en código CSS tuviéramos cifras decimales difíciles de leer y entender:

.col-1 {

  border: 1px solid black;
  width: 14.2857%;
  height: 50px;

}

.col-5 {

  border: 1px solid black;
  width: 71.4285%;
  height: 50px;

}

.col-3 {

  border: 1px solid black;
  width: 42.8517%;
  height: 50px;

}
<div class="col-1"></div>
<div class="col-5"></div>
<div class="col-3"></div>

Podemos conseguir que el código sea más legible e incluso casi "autoexplicativo" sustituyendo los feos decimales por el uso de calc.

.col-1 {

      border: 1px solid black;
      width: calc(100% / 7);
      height: 50px;

    }

    .col-5 {

      border: 1px solid black;
      width: calc(100% / 7 * 5);
      height: 50px;

    }

    .col-3 {

      border: 1px solid black;
      width: calc(100% / 7 * 3);
      height: 50px;

    }
<div class="col-1"></div>
<div class="col-5"></div>
<div class="col-3"></div>

Nuevamente, parece más sencillo hacerlo de esta manera.

Posicionar y dimensionar elementos flotantes.

Imagina que tienes que posicionar 2 elementos flotantes en un contenedor de ancho variable (y desconocido) manteniendo la relación de aspecto entre ellos, y con una separación (margen) fijo entre ellos.

.rojo {
  width: 40%;
  float: left;
  height: 100%;
  margin-right: 1em;
  padding: 30px;
  background: red;
  color: white;
}

.azul {
  width: calc(60% - 1em);
  float: right;
  height: 100%;
  padding: 30px;
  color: white;
  background: blue;
}

html, body {
  height: 100%;
  background: #ccc;
  padding: 20px;
}
body {
  padding: 20px;
  background: white;
}

* {
  box-sizing: border-box;
}
<div class="rojo">
    Lorem ipsum dolor sit amet, consectetur adipisicing elit. 
</div>

<div class="azul">
  Lorem ipsum dolor sit amet, consectetur adipisicing elit. 
</div>

Consideraciones a tener en cuenta

Sintáxis

Es importante respetar las sintáxis de calc: calc(100%[espacio]-[espacio]50px) Cosas como calc(100%[espacio]-50px) o calc(100%-50px) No funcionarán en todos los navegadores.

Reglas de respaldo

Aunque la mayoría de navegadores soportan a calc, es una buena práctica usar una regla de respaldo para navegadores que no lo hagan:

.respaldo {
  width: 85%; /* Valor aproximado, para navegadores que no lo soporten */
  width: calc(100% - 100px);
}

Tenéis estos y otros casos de uso en https://css-tricks.com/a-couple-of-use-cases-for-calc/

lois6b
  • 7,419
  • 5
  • 29
  • 50
Muriano
  • 4,057
  • 18
  • 32
10

Hombre, así planteado, se me ocurren usos para cuando quieres dividir un contenedor en partes iguales, sin tener que estar a hacer el calculo de cuandos px sería.

.contenedor{
    width: 300px;
}
/** Divido el tamaño del contenedor en 10 partes usando elementos en su interior **/
.contendor .elemento{
    display: flex;
    flex-direction: row;
    width: calc(0.1 * 300px);
}

Realmente cualquier cosa que se te ocurra. Pero en mi opinión, quizás la mejor opción sería en combinación con algún lenguaje tipo Less o Sass donde puedes guardar valores en variables. Y según avances en el código realmente quizás no te importe que valor tiene. Por ejemplo:

@tam: 300px;

.contenedor{
  width: @tam;
  height: @tam;
  .elemento{
    width: calc(0.5 * @tam);
  }
}

Este ejemplo es muy pequeño y quizás no se aprecie realmente el potencial, pero imagínate un fichero con 500-700 lineas de Less donde sabes que un contenedor es de tamaño 500px y quieres en su interior dos que sean la mitad cada uno, pero no sabes cuánto vale realmente el primer contenedor. Combinando Less y la función calc() puedes otener la mitad del valor de una variable less. (la mitad, un tercio, lo que quieras,...)

Pero esto, como casi todo, es hasta donde te llegue la imaginación.

fedorqui
  • 15,850
  • 17
  • 58
  • 112
Diego Martin
  • 445
  • 3
  • 10
  • 2
    Hola Diego. Puedes poner el CSS y algo de HTML dentro de un snippet de codigo (boton `<>`) para que sea más visible lo que quieres explicar ? ^^ – lois6b Jul 20 '17 at 11:30
  • No entiendo tu segundo ejemplo, en ese caso puedes poner `width: 50%` y siempre sera la mitad del contenedor sin importar el tamaño que tenga. –  Jul 20 '17 at 12:13
  • Tienes razón y quizás el segundo ejemplo no sea el mejor de todos, pero ponte que lo quieres dividir en cachos muy pequeños, podrías tener muchos decimales, y es mucho más cómodo y simple de leer con calc, pero su uso no es impepinable ni necesario, puedes de otras formas todo lo que puedes hacer con calc, pero es mucho más intuititvo, de todos modos echalé un vistazo a la respuesta de @muriano que esta mucho mejor explicado. ;) Un saludo – Diego Martin Jul 21 '17 at 06:25
10

El control de mínimos es otra aplicación directa de calc(), un ejemplo práctico se puede ver con las fuentes o tipografìa.

Supongamos que quiero adaptar mi tamaño de letra según el ancho del viewport, para lo cual utilizaría unidades vw:

.contenido {
  font-size: 3vw;
}
<div class="contenido">
  Icing topping gummies sesame snaps cotton candy dragée cookie wafer. Oat cake tootsie roll dessert carrot cake cake marzipan. Toffee biscuit gummies biscuit. Donut chocolate cupcake tart halvah sweet roll wafer. Lollipop halvah dessert croissant pie. Dragée cotton candy bear claw gummi bears caramels caramels tiramisu. Wafer cheesecake pie brownie powder.
</div>

Hasta ahí todo bien, 3% del ancho funciona para mi aplicación web, pero quiero garantizar que el tamaño de letra sea mínimo de 0.7rem sin importar la resolución y que el tamaño de la letra siga "adaptándose". Si bien puedo hacerlo con un media query y un (max-width: ...), con calc puedo mantener una tipografía adaptable a todas las resoluciones:

.contenido {
  font-size: calc(0.7rem + 1vw);
}
<div class="contenido">
  Icing topping gummies sesame snaps cotton candy dragée cookie wafer. Oat cake tootsie roll dessert carrot cake cake marzipan. Toffee biscuit gummies biscuit. Donut chocolate cupcake tart halvah sweet roll wafer. Lollipop halvah dessert croissant pie. Dragée cotton candy bear claw gummi bears caramels caramels tiramisu. Wafer cheesecake pie brownie powder.
</div>

En resumen, se podría eliminar algún media query si se utiliza calc() y mantener los tamaños adaptables sin perder la estética.

Shaz
  • 28,742
  • 18
  • 37
  • 61
8

Como ya te han dado varios ejemplos interesantes de uso y no creo que pueda aportar nada más en ese aspecto, voy a intentar contestarte a las otras preguntas.

Aunque la documentación de MDN es muy buena siempre es recomendable ver también que dice la especificación, en este caso https://www.w3.org/TR/css3-values/#calc-notation dónde se explica con detalle la sintaxis y peculiaridades de esta función.

El ejemplo que pones para ilustrar la pregunta: width: calc(100% - 80px); puede parecer simple e inútil al tratarse de un cálculo matemático sencillo en apariencia pero si nos fijamos más detenidamente vemos que hay dos unidades de medida distintas: porcentajes y píxeles; ahí reside la magia de calc(), hace los cálculos en tiempo real restando los 80 píxeles al ancho actual del elemento, si se modifica este tamaño recalculará el nuevo ancho.

La posibilidad de hacer cálculos con diferentes unidades al vuelo es una de las cualidades de calc() más útiles e interesantes.

El soporte de calc() es bastante amplio pero como preguntas por una alternativa se me ocurren varias opciones:

  • Si el cálculo que se hace son operaciones de números o unidades del mismo tipo puedes poner el resultado de los cálculos directamente en el valor de la propiedad o si usas un preprocesador como SASS, Less, Stylus, etc. éste puede hacer los cálculos por ti.

con calc()

   width: calc((75px + 25px) / 3);

sin calc()

   width: 33.33333px;
  • Si mezclas unidades que no cambian dinámicamente también puedes hacer tú mismo los cálculos o con algún preprocesador crear una función para convertir entre unidades y que calcule por ti.

con calc()

   width: calc(1em + 10px);

sin calc()
supongamos que tenemos definido el tamaño de fuente a 16px

   width: 26px;
  • Para casos como el del ejemplo que mezcla unidades relativas y fijas que pueden cambiar al redimensionar el navegador, por ejemplo, la única alternativa que se ajuste completamente es usar JavaScript.

con calc()

   width: calc(100% - 10px);

sin calc()
no es posible dar un valor exacto solo con css. con jQuery podría hacerse algo así:.

$(window).resize(function(){
  $('#midiv').css('width', '100%').css('width', '-=10px');
});