Niveau Niveau confirmé

La fonction calc() en CSS

Articlecss

Publié par le , mis à jour le (308099 lectures)

opérations unités calculs calc

calc() est une fonction CSS3 offrant la possibilité de réaliser des opérations mathématiques en CSS (addition, soustraction, division et multiplication).

Compatibilité

La compatibilité de cette fonction est encore un peu limitée aux navigateurs récents. Les gros écueils étant les versions Internet Explorer antérieures à IE9, et les anciens Android (avant 4.4).

Navigateurs Versions Détails
Internet Explorer Internet Explorer 9+  
Firefox Firefox 4+
Firefox Mobile
préfixe -moz- jusqu'à Firefox 15
Chrome Chrome 19+
Chrome Mobile
préfixe -webkit- jusqu'à Chrome 25
Opera Opera 15+
Opera Mobile 21+
 
Safari

Safari 6+
Safari Mobile 6+

préfixe -webkit- jusqu'à Safari 6.1
Android Browser Android Browser 4.4+  

Et ma calculatrice ? Mon Préprocesseur ?

Le navigateur peint la page, connaît l'interaction entre les éléments, calcule et interprête toutes les valeurs au sein de leur contexte, qu'il s'agisse de pixels, de pourcentages, de em, rem, pt, etc. Concrètement, seul un navigateur est capable de savoir ce que donne l'expression "100px + 2em". Aucune machine à calculer ni aucun préprocesseur ne peut exécuter ce calcul en amont avant que la page ne soit lue par le navigateur, car ils ne connaissent pas le contexte de rendu.

Bref, avec calc(), on laisse faire le navigateur faire son boulot, et c'est tant mieux.

Petit exemple pratique

Prenons une boîte à qui l'on a eu le malheur d'appliquer un width: 100% :

div {
    width: 100%;
    padding: 10px;
}

Bien sûr, la boîte déborde à présent de son parent puisque sa taille réelle n'est pas de 100%, mais de 100% + 20px (la valeur du padding s'ajoute).

Fort heureusement, box-sizing est là pour nous sauver, mais sachez qu'il est également possible de procéder différemment, grâce à l'emploi de calc() :

div {
    width: calc(100% - 20px);
    padding: 10px;
}

Note : attention, l'espace est nécessaire autour du signe d'opération, sans quoi calc() n'aura pas d'effet.

Autres exemples

calc() permet bien évidemment de réaliser des opérations simples et des mélanges d'unités, mais d'autres calculs plus complexes sont envisageables également : ainsi il est par exemple possible d'imbriquer des fonctions calc() au sein des fonctions calc(), et même d'utiliser la fonction attr() pour récupérer la valeur d'un attribut HTML et s'en servir dans des calculs.

Calculs simples

Opérations simples concernant les longueurs et tailles :

.content {
  width: calc(100% / 3); 
}

Ou encore :

.content {
  border-width: calc(10px - 1em); 
}

Positionnements

Toutes les opérations destinées à définir une position ou un décalage :

.content {
  background-position: calc(100% - 50px) calc(100% - 20px);
}

Ou encore :

.content {
  position: relative;
  top: calc(100px - 5rem);
}

Constructions de grilles et gouttières

Calculs savant pour établir les largeurs de colonnes en tenant compte des gouttières. Ici 5 colonnes espacées d'une gouttière de 1em :

.truc > div {
  width: calc((100% / 5) - (1em - 1em / 5));
  float: left;
}

grille calc()

Associé à un préprocesseur (ici LESS) :

.grid (@number) {
    float: left;
    width: calc((100% - (@gutter*(@number - 1))) / @number);
}

Combinaisons avec d'autres propriétés

Centrer verticalement une image :

img {
  top: calc(50% - attr(height) / 2);
}

À l'heure actuelle, cette combinaison n'est pas reconnue par tous les navigateurs.

Combinaisons avec des variables CSS

Pour récupérer et exploiter la variable --small-value :

img {
  top: calc(var(--small-value) / 2);
}

À l'heure actuelle, cette combinaison n'est pas reconnue par tous les navigateurs.

Corrections de bugs de navigateurs

calc() permet également de pallier les déficiences navigateurs :

html {
  font-size: 62.5%;
  /* équivalent 10 pixels, sauf sous IE à partir de la version 9 (qui pense que la taille est de 9.93px) */
  font-size: calc(1em * 0.625);
  /* L'astuce pour corriger le calcul sous IE 9 à 11 */
}

Plus d'informations à propos de ce bug, sur le forum de Microsoft.

(merci @victorbritopro)

Application en responsive

Intégré à certains éléments ou attributs HTML :

sizes="(min-width: 36em) calc(.333 * (100vw - 12em)), 100vw"

Source et explications au sein d'un article sur SmashingMagazine. (et en version française sur La Cascade).

wolf

Comme ces divers exemples le montrent aisément, les possibilités d'usage de calc() sont extrêmement vastes. Dès lors que des nombres sont impliqués : les longueurs, les fréquences, les angles, les durées, les nombres et les entiers. En d'autres termes, vous pouvez l'utiliser partout sauf dans les chaines de caractères.

Alternatives ?

Pour les anciennes versions de navigateurs qui ne reconnaissent pas calc(), son support peut être détecté via Modernizr en JavaScript.

Voici l'extrait de code en question :

var el = document.createElement("div");
var isCalcSupported;

el.style.cssText = "width: calc(2em);"; //adapter pour inclure les préfixes nécessaires
isCalcSupported = !!el.style.length;

(merci @jacqueminv)

Grâce à cette détection, une alternative peut éventuellement être mise en place.

Cet article est inspiré d'un extrait du livre de Hugo Giraudel à paraître prochainement

Commentaires

Très bon article, merci pour ces explications claires.

Par contre n'y a t'il pas une erreur dans le code js donné à la fin (variable non définit : el.style.length à remplacer par el.style.cssText ?).
Je me trompe surement mais en l'état je ne comprends pas le fonctionnement du code.

Non, la fonction sert à détecter si l'utilisation de calc() fonctionne. Si ce n'est pas le cas, la troisième ligne ne sera pas prise en compte et la longueur de l'élément (el.style.length) sera égale à 0 et ce qui affectera la valeur "false" à isCalcSupported.

excellent article ( et très attendu ) ;) merci Raphaël
et merci @Victor BRITO pour les solutions de fallback !
Quelle belle propriété ce calc() !

@eXystenZ merci pour tes explication et oui en y mettant 3 neurones sur le dossier le code devient tout de suite plus compréhensible.

Merci pour cet article très intéressant mais maintenant, je vais devoir repasser sur le code CSS de tous mes projets... ;)

Peut-on m'expliquer (néophyte), car je ne comprend rien du tout...
.truc est normalement une classe mais avant on a le code suivant:
.truc(@num: @number)
{
& > * {width: calc(~"(100% -" @gutter ~"* (" @num ~"- 1)) / " @num);}
& > :nth-child(@{num}n+1){clear: left; margin-left: 0}
}
.truc(@num: @number) sert à quoi?
& > * ... width:calc(... Est-ce que cela veut dire "Calcule la largeur (de quoi? class truc ?) sur la base des variables "gutter" et "num". Pourquoi & > * devant ???
Idem pour la deuxième ligne qui semble indiquer que le dernier? doit être "rétablit" ???

Est-ce quelqu'un pourrait m'expliquer tout ça en langage clair... Pour un "Papy".

Merci

Si > est le sélecteur du Parent
& nested parent
* Tous

Si * alors > et & ne sont pas nécessaires ?
Où je n'ai encore rien compris...

Malheureusement encore aujourd'hui (12 mai 2017) les calculs restent très aléatoires avec le navigateurs sur mobiles.

Cas concret :

Page -> Main -> tabs

#page {width:100%} #main {width:100%} #tabs { position:fixed; right : calc(100% - 38px); }

windows.width = 360px -> 360px -> calc(100% - 38px); }

Va nous donner un résultat surprenant suivant le debogger Chrome et sur l'écran (différent) :

Valeur calculée -> left 322px : (360px-38px = 322) jusque là tout va bien, mais c'est faux.

On trouve :

------------------- -> right : -317px !!!

et l'élément est en dehors de la zone d'affichage, bien placé à right: -317px;

explication : chrome utilise "window.innerWidth" pour calculer la base de 100% et non l'hérityage de l'élément parent, et un Samsung Galaxy S7 rapporte à l'initialisation :window.innerWidth = 677px;

La conséquence est qu'il est actuellement impossible d'utiliser avec des mobiles les positionnement par attribut "right" avec ou sans calc.

La largeur utilisable devient innerWidth qui définit le bord droit de l'affichage (peut prendre de valeur différentes aléatoires).

Suivant les conditions (initialisation ?) l'affichage sera ramené à l'échelle du inner.

Un scroll horizontal rendu possible (innerWidth > window.width) est un des symptômes.

NB : La situation est encore pire avec FireFox sur mobile.