Ombres avancées avec CSS3 et box-shadow

Cet article a 462 jours. Il commence à dater, lisez-le donc en gardant son âge en tête ! Merci

CSS3 c’est pour l’aspect vendeur du nom, car au final on va aussi et surtout bénéficier du service de pseudo éléments (:after et :before) qui sont prévus depuis CSS2.1.
Les visuels que vous voyez sur la page de démonstration ne sont composés qu’avec des propriétés CSS sur une seule et unique <div> (pour chaque bloc).

Chez moi le meilleur rendu est sous Firefox, notamment pour la dernière ombre qui est un peu osée (au passage il semblerait que la propriété opacity ne fonctionne pas sur les pseudo-éléments).

Dans cette démonstration nous allons utiliser des propriétés avancées de CSS2.1 (:before et :after) qui sont des pseudo-éléments.
Des pseudo-éléments permettent de construire un élément dans la structure de votre document (DOM) sans vraiment en construire un… ok, ça commence bien pour l’explication.

Pseudo-éléments :after et :before

Imaginez que dans un site vous souhaitiez ajouter derrière un lien la langue du site en référence (qui, pour rappel, se précise grâce à l’attribut hreflang sur un élément <a>) vous pouvez le faire en passant par du CSS :

a[hreflang]:after {
	content : " ("attr(hreflang)")";
}

Ainsi tous les liens porteurs de l’attribut hreflang se verront agrémentés d’un « (en) », dans le cas d’un lien anglais par exemple :
Mon lien (en)

La base de travail

Sur le même principe nous allons pouvoir construire deux pseudo-éléments qui vont nous servir à composer les différentes ombres que vous pouvez visualiser sur la démonstration.

Pour résumer simplement notre démarche nous avons des divisions :

<div class="bloc">Without drop shadow</div>

qui portent toutes la classe bloc, cette classe nous sert de base pour les styles des blocs de démonstration (ça ressemble vaguement à une feuille… du moins j’espère).
La classe without, dans le premier exemple de la page de démonstration, me sert juste à définir le bloc qui n’a pas d’ombrage, mais passons.

Voici le CSS correspondant, les principales propriétés à retenir sont celles de positionnement et de dimensions à fixer, mais je vous donne tout de même la totalité :

.bloc {
	position: relative;
	display: inline-block;
	vertical-align: top;
	margin: 35px 26px;
	padding: 25px;
	width: 220px;
	height: 110px;
	background: #f0e8d8;
	font-size: 1.6em;
	line-height: 5em;
	font-family: Georgia, Times, Serif;
	text-align: center;
	background-image: -moz-linear-gradient(center bottom, #dacdb1 11%, #f0e8d8 56%);
	background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0.11, #dacdb1), color-stop(0.56, #f0e8d8));
	-moz-box-shadow: 0 0 65px #cdbe9f inset, 0 0 20px #beae8c inset;
	-webkit-box-shadow: 0 0 65px #cdbe9f inset, 0 0 20px #beae8c inset;
	box-shadow: 0 0 65px #cdbe9f inset, 0 0 20px #beae8c inset;
}

Ensuite nous allons construire nos pseudo-éléments de manière globale :

.bloc:after, .bloc:before {
	content: " ";
	position: absolute;
	width: 50%;
	height: 100px;
	z-index: -10;
}
.bloc:before { background: green;}
.bloc:after { background: red; }

Les pseudo-blocs ont une position absolue et un z-index négatif, le but étant de placer ces éléments sous leur parent (la <div> dont ils dépendent).

Les couleurs sont là pour faire du repérage dans le positionnement des pseudo-éléments.
A ce propos j’avais prévu de vous inviter à utiliser Firebug pour jouer avec le fond des .bloc, mais grâce à une suggestion de Felipe, je vous ai développé un petit joujou pour visualiser les pseudo-éléments sans vous demander trop d’effort :p

Bref, revenons-en à nos moutons.

Les pseudo-blocs sont prêts, il ne nous reste plus qu’à leur offrir une ombre portée ainsi qu’un positionnement précis.
Dans certains cas nous aurons besoin d’autres petites choses, mais nous verrons cela au fur et à mesure.

Tous les codes CSS suivants vont s’appliquer à un éléments porteur de la classe bloc combiné à la classe dont fait référence le code CSS.
Exemple, pour le prochain effet, nous aurons besoin d’un élément de cette forme :

<div class="bloc simple">Soft Drop Shadow</div>

Ombre simple

Il n’est jamais de trop de rappeler certaines bases, notamment sur l’utilisation de la propriété box-shadow.

.simple {
	-moz-box-shadow: 0 0 65px #cdbe9f inset, 0 0 20px #beae8c inset, 0 0 5px #816f47;
	-webkit-box-shadow: 0 0 65px #cdbe9f inset, 0 0 20px #beae8c inset, 0 0 5px #816f47;
	box-shadow: 0 0 65px #cdbe9f inset, 0 0 20px #beae8c inset, 0 0 5px #816f47;
}
.simple:after, .simple:before { display: none; }

Ici nous n’avons pas besoin des pseudo-éléments, nous les cachons.
Je vous note le code avec les préfixes -moz- et -webkit- mais pour les prochains je simplifierai, il ne faudra bien entendu pas les oublier (et ajouter le préfixe -o- et -ms- pour les rotation sous Opera et IE9 respectivement).
Ici il s’agit du même code que pour les ombres de notre base, sauf que nous ajoutons une ombre portée.

box-shadow permet d’attribuer plusieurs ombres à un élément. La syntaxe de base :

box-shadow: 1px 2px 9px #000;

permet la création d’une ombre portée dont voici les paramètres, dans l’ordre d’apparition :
1px de décalage horizontal (vers la droite), 2px de décalage vertical (vers le bas), 9px d’étendue, #000 de couleur.
Un paramètre optionnel, inset, placé après la couleur, permet d’indiquer que l’ombre portée est en fait une ombre interne à l’élément. Sans ce paramètre, l’ombre est considérée comme portée.

La multiplication de cet ensemble d’informations séparé par une virgule permet la création d’ombres multiples.

Les ombres latérales

C’est ici que l’on va découvrir l’intérêt des pseudo-éléments.

Ici nous allons positionner un des pseudo-blocs et lui donner une largeur et une hauteur, ainsi qu’une ombre portée.

.laterals:before {
	top: 15px;
	left: 0;
	width: 100%;
	height: 130px;
	border-radius: 12px / 65px;
	box-shadow: 0 0 10px #555;
}
.laterals:after { display: none; }

Le border-radius nous permet d’avoir un effet légèrement gondolé sur l’ombre, ce qui ajoute un poil de réalisme.
N’oubliez pas de profiter de la fonction X-Ray pour visualiser le pseudo-élément et son comportement.

Ombre latérale simple

Simple mais pas trop, simple dans le sens où il n’y en a que d’un côté.
Encore une fois nous n’allons utiliser qu’un des deux pseudo-éléments.

Ici nous allons utiliser une rotation pour donner cet effet.

.lateral:before {
	top: 10px;
	left: 8px;
	width: 50%;
	height: 130px;
	box-shadow: -7px 0 10px #555;
	transform: rotate(-5deg);
}
.lateral:after { display: none; }

Vous pouvez voir le petit décalage négatif de l’ombre, le but étant de le diriger vers la gauche.

Ombres horizontales

C’est un peu le même principe que précédemment, sauf que, partant du principe qu’on peut très bien ne pas connaître la hauteur d’un bloc, j’utilise cette fois les deux pseudo-éléments pour produire cet effets.

Nos deux pseudo-éléments sont donc positionnés l’un par rapport au haut du bloc parent (la <div> dont il dépend), l’autre par rapport au bas.

.horizontal:before {
	top: 0px;
	left: 5%;
	width: 90%;
	height: 50px;
	border-radius: 125px / 12px;
	box-shadow: 0 0 8px #555;
}
.horizontal:after {
	bottom: 0px;
	left: 5%;
	width: 90%;
	height: 50px;
	border-radius: 125px / 12px;
	box-shadow: 0 0 8px #565656;
}

J’ai un peu dupliqué les styles pour que ce soit bien clair, mais vous pouvez bien entendu réunir les styles communs dans une seule déclaration.

Ombre horizontale simple

C’est l’étape la plus complexe de cet article je pense.

En ajoutant la classe single à notre élément :

<div class="bloc horizontal single">Sans ombre portée</div>

et en appliquant, en plus des précédents, ce code CSS :

.single:before { display: none; }

Le tour est joué ;)

Ombres courbés

C’est certainement l’effet le plus en vogue chez les webdesigner qui souhaitent apporter une touche de profondeur à leur design.

Pour réaliser cet effet nous allons devoir utiliser nos deux pseudo-éléments.

.curve:before {
	top: 52px;
	left: 5px;
	transform: rotate(-5deg);
	box-shadow: 7px 6px 15px #333;
}
.curve:after {
	top: 52px;
	right: 5px;
	transform: rotate(5deg);
	box-shadow: -7px 6px 15px #333;
}

Trop facile !

Toujours sur le même principe, il vous suffira d’ajouter la classe single stylée plus haut pour faire disparaitre l’ombre d’un des deux côtés. De quoi simplifier bien largement vos intégrations !

Ombre expérimentale

Alors celle-ci… ne regardez pas sous Chrome !
Vous ne verrez rien non plus avec le X-Ray pour la simple et bonne raison que j’utilise l’intégralité des pseudo-éléments (et pas uniquement leur box-shadow), pour produire cet effet, encore expérimental.

Le code CSS correspondant est plutôt lourd en terme d’ombre portée… et plutôt difficile à paramétrer :

.drop:before {
	left: -5px;
	top: 167px;
	width: 280px;
	height: 4px;
	background: #aaa;
	border-radius: 140px / 2px;
	box-shadow: 0 0 5px #aaa, 0 0 10px #888, 0 0 15px #666;
}
.drop:after {
	left: 8px;
	top: 168px;
	width: 255px;
	height: 2px;
	background: #666;
	border-radius: 125px / 1px;
	box-shadow: 0 0 5px #444, 0 0 8px #333, 0 0 10px #666;
}

Le pseudo-élément :before me sert d’ombre large, les drop-shadow y sont donc plutôt complexes.
Le pseudo-élément :after me sert d’ombre fine, au centre de l’ombre large, les drop-shadow sont donc légèrement plus simples.
Dans les deux cas nous les transformons dans un premier temps en ellipse.

Bogues

S’il vous arrivait de ne pas voir apparaitre les ombres, vous êtes peut-être dans le cas rencontré par les lecteurs et commentateurs du blog :
Les blocs ombrés sont des enfants d’au moins un autre bloc (un « container » quoi). Si ce bloc possède une couleur de fond, les ombres vont disparaitre derrière le bloc.
C’est une histoire de z-index à réordonner.
Dans notre exemple, il suffit de positionner votre élément (en absolute ou relative) et de lui attribuer un z-index de 0, exemple :

.container {
   position:relative;
   z-index: 0;
}

Merci à Ivan d’avoir fourni un exemple de code concret.

Le mot de la fin

J’espère que vous n’aurez pas eu trop de difficulté à suivre ce tutoriel dont les explications restent relativement sommaires.
La fonction X-Ray devrait vous servir à trouver certaines réponses à vos questions, mais pour les autres n’hésitez pas à me consulter via le système de commentaire de ce blog.

J’ai bien conscience que le sujet a déjà été abordé ici : Nimbupani et là : NicolasGallagher. Mais cet article est en rédaction depuis début décembre, et il apporte ma vision des choses, je me suis donc décidé à le terminer :)

Merci pour votre lecture !

34 commentaires et 7 trackbacks sur “Ombres avancées avec CSS3 et box-shadow”

  1. fascicularia dit :
    6 juillet 2011

    Superbe tuto. Une petie question : quels sont les navigateurs qui n’intègrent pas ces nouvelles applications ?

  2. Geoffrey dit :
    6 juillet 2011

    Bonjour et merci :)

    Tous les navigateurs modernes, c’est à dire Firefox 3.6+, Chrome 10+, Safari 4+, Opera 11+, Internet Explorer 9 et peut-être même des versions antérieures pour certains d’entre eux, prennent en charge ces effets combinant pseudo-élément, transformations et ombres portées.
    Hélas pour IE8 et inférieur, je n’ai trouvé aucune alternative CSS/JS acceptable, les ombres portées n’étant jamais attribuées aux pseudo-éléments.

    Au plaisir ;)

  3. fascicularia dit :
    6 juillet 2011

    Merci Geoffrey.

  4. Anne Onyme dit :
    19 juillet 2011

    Dans la lignée des tutos de tutplus mais en français et en plus poussé. Un grand merci. Avec CSS3 les images liées au design vont disparaitre au fur et à mesure, entre les ombres portées, les background en dégradés, les ombres des polices et leur contours, font-face pour les polices exotiques… ca devient bon !

  5. Geoffrey dit :
    27 juillet 2011

    @fascicularia : je vous en prie :)

    @Anne Onyme : Merci pour cette comparaison. En effet ces styles, et bien d’autres encore (je pense notamment aux styles de mise en page en colonne, aux display, et aux pseudo-classes) sont très prometteurs !

  6. Matthieu dit :
    7 août 2011

    Super démo, mais ça ne marche plus à partir du moment où les blocs à ombrer sont eux même dans une div avec un background. Car le z-index à -10 fait passer l’ombre derrière ce background.

    Perso, je n’ai pas trouver de solutions à ce problème. Si quelqu’un à une idée…

  7. Geoffrey dit :
    7 août 2011

    Bonjour,
    Effectivement je n’ai pas testé ce cas.
    Je vais voir ce qu’il en est, a priori une petite réorganisation des z-index pourraient faire l’affaire.
    Merci et bonne soirée, je vous tiens au courant ;)

  8. jeremy dit :
    8 août 2011

    Salut,
    j’ai eu le même problème avec le background de mon #main
    la solution n’est pas trop compliquée, et fonctionne chez moi sur firefox 6 beta, safari 5.0.5 et chrome 13

    il suffit de mettre un z-index:1; à notre .bloc .
    pour le .bloc:before et :after, on peut laisser -10, mais -1 fonctionne aussi bien

    En tous cas, merci Geoffroy pour cette technique, c’est génial

    Bonne journée

  9. Geoffrey dit :
    8 août 2011

    Bonjour Jérémy,

    Merci pour ton intervention.
    Apparemment cette technique ne fonctionne pas pour Firefox 4 et 5. J’ai effectué un bref test en vain hier soir, je vais essayer de trouver du temps pour approfondir la chose.

    Bonne continuation et bonne journée à vous ;)

  10. Matthieu dit :
    8 août 2011

    la seule solution est de placer un div dédié uniquement à l’ombrage juste après le div à ombrer.et de mettre les z-index comme suit :
    div parent avec background en z-index:0
    div à ombrer en z-index:10
    div dédié à l’ombrage en z-index:5

    il faut ensuite positionner le div dédié à l’ombrage en fonction de ce qu’on veut faire

  11. jeremy dit :
    8 août 2011

    R-hello
    Geoffroy> je viens d’installer FF 5.0.1 et cela marche aussi, je précise que je suis sur mac snow leopard.

    Par contre c vrai que je n’ai pas exactement utilisé ta méthode, et j’ai fait un truc à ma sauce, peut être que certaines choses différent mais je n’arrive pas à voir quoi

    J’ai fait un exemple simplifié qui fonctionne chez moi dans tous mes navigateurs
    C’est les ombres latérales, je n’utilise donc que le :before
    http://beta.jeremycastelli.com/testombre/
    J’ai appliqué des background à tous les blocs parent, et cela ne semble pas poser de problèmes.
    Pouvez vous me dire si cela marche chez vous.
    Vous pouvez regarder le code source, et me dire si vous voyez une différence, j’ai inclus les style directement dans le head.

    a+

  12. Geoffrey dit :
    8 août 2011

    @Matthieu : l’intérêt à la base et d’éviter l’ajout de div inutiles, c’est dommage d’en arriver là :)
    @ Jérémy : merci pour ton implication. Effectivement ta solution fonctionne aussi chez moi sur ton exemple. Je n’ai, pour le moment, pas distinguer de différences de positionnement et la hiérarchie des z-index semble identique, même si les valeurs ne sont pas les mêmes. Je ferai des essais dans la semaine pour voir ce qui bogue.

    Encore merci à vous ;)

  13. jeremy dit :
    8 août 2011

    J’avais dit une betise, l’exemple posté marchait dans FF et chrome, j’en avais deduit(webkit) que cela marchait aussi dans safari. Et ben non!

    Par contre cela marchait dans mon vrai projet.
    La difference : un reset CSS que je n’avais pas inclus dans mon exemple
    J’ai donc rajouté une feuille de style, c’est celle de Boilerplate HTML5, et tout refonctionne bien http://html5boilerplate.com/

    http://beta.jeremycastelli.com/testombre/

  14. Nicolas dit :
    29 novembre 2011

    Très très joli ! J’espère que tout finira par tourner correctement à la fois sur Chrome et IE.

  15. Thierry dit :
    20 décembre 2011

    Intéressant.

    Mais il y a visiblement une limite de taille (au sens propre) puisque ça ne marche plus si on n’a pas de hauteur fixe sur le bloc… Existe-t-il une solution de contournement ?

  16. Geoffrey dit :
    20 décembre 2011

    Bonjour Thierry,

    L’effet fonctionne parfaitement même sans hauteur fixe sur le bloc à ombrer.
    Je viens d’essayer sur ma page de démonstration en retirant le height des .bloc. Du moment que la hauteur est au moins définie par un contenu ou un padding, c’est censé fonctionner.
    As-tu une démonstration en ligne ?

  17. Ivan dit :
    4 février 2012

    Bonjour,
    Vos effets sont super, mais je n’arrive pas à les utiliser dans mon site… Mes ombres before et after se retrouve toujours en arrière de mes div parent.
    Avez-vous trouvé une solution ?
    J’ai bien essayé les différents moyens évoqués dans les commentaires, mais rien n’y fait cela ne fonctionne jamais avec un background sur la div .bloc.
    pouvez-vous m’aidez?
    merci

  18. Geoffrey dit :
    5 février 2012

    Bonsoir,
    Merci pour le retour.
    J’ai un doute sur le problème évoqué. Auriez-vous une démonstration en ligne ?
    Un lien (si privé) de test à m’envoyer par mail ?
    Merci, je pourrais ainsi éventuellement compléter mon tutoriel.

  19. houssem dit :
    6 février 2012

    Salut, je suis un nouveau dans le développement WEB, je débute avec html5/css 3 et j’ai un gros souci, c’est que toutes mes pages web laissent un petit espace en haut et sur les cotés… un espace de 5px.
    et j’arrive pas a m’en débarrasser.
    Alors je m’adresse à vous pour me donner une solution.
    Merci et bonne continuation pour le taff que vous faites.

  20. Geoffrey dit :
    6 février 2012

    Bonsoir,
    L’élément body possède par défaut des marges que l’on peut annuler grâce à ce morceau de CSS :

    body {
       margin: 0;
    }

    Bonne continuation.

  21. Ivan dit :
    6 février 2012

    Bonsoir, je vous réponds par rapport au problème que j’ai mentionner hier dans vos commentaires.
    Je suis désolé, je n’ai pas de lien à vous passer pour vous montrer…
    Mais le problème est simple si vous ajoutez une div avec un background sous les div bloc alors l’ombre de celle-ci se retrouve masquée par le background de la div.
    Après mainte recherche j’ai trouvé comment régler le problème (à l’aide de ce site : http://nicolasgallagher.com/css-drop-shadows-without-images/demo/). Il ma suffit d’ajouter ce code CSS pour chaque div avec background qui se trouve sous la div bloc :

    .exemple { position: relative;
    z-index: 1;
    }
    .exemple::after { content: "";
    display: block;
    clear: both;
    visibility: hidden;
    height: 0;
    font-size: 0;
    }

    Voilà, j’espère, que c’est clair….
    Merci encore pour le tuto.

  22. Geoffrey dit :
    7 février 2012

    Hello Ivan,
    Merci pour ce partage de code.
    Il me semble que c’était finalement le même problème que rencontrait Jeremy (plus haut dans les commentaires).

    Je vais compléter mon article afin d’y ajouter cette précision que tu as apportée.
    Merci bien :)

  23. houssem dit :
    7 février 2012

    merci, geoffrey et moi qui croie que c’est un gros problème.
    merci.

  24. Lagouach dit :
    13 février 2012

    Absolument magnifiques les effets !
    Ça évite de confectionner des ombrages sur mesure à accoler aux div!!!
    Gros merci!!

  25. Nalex dit :
    1 mars 2012

    Merci pour ce tuto, ça augmente encore toutes les possibilités de CSS3.

    Par contre est il possible d’appliquer une transition sur ses ombres ? Et surtout sur quel élément appliquer notre :hover ?

  26. Geoffrey dit :
    1 mars 2012

    Bonjour,
    Il est tout à fait possible de prévoir une animation en CSS3 sur les ombres portées.
    Mon petit portail personnel illustre très bien cela : crofte.fr

    div {
       transition: box-shadow 1s;
       box-shadow: 0 0 0 #444;
    }
    div:hover {
       box-shadow: 3px 3px 5px #444;
    }

    Dans le cadre d’une animation sur les pseudo-éléments, il suffit de changer le sélecteur au :hover en div:hover:before, par exemple.
    Attention cependant, l’animation sur les pseudo-éléments ne fonctionne pas sur Chrome pour le moment.

  27. Bruno dit :
    3 mars 2012

    Merci pour le tuto et le déboggage.
    Je me suis fait avoir aussi par les div en-dessous, et j’avoue que j’ai mis un moment à comprendre.
    L’essentiel est là, j’ai mon ombre.

  28. Geoffrey dit :
    3 mars 2012

    Aucun problème. Merci aux commentateurs qui ont permis de préciser le code et d’expérimenter quelques cas particuliers :)
    Bonne continuation Bruno ;)

  29. Isa dit :
    19 mai 2012

    Super mais pour trouver les bonnes mesures c’est galère !
    j’abandonne.

  30. Geoffrey dit :
    19 mai 2012

    Hello,
    Des mesures ? Les largeurs sont en pourcentages et les placements peuvent être définis à partir du haut ou du bas de l’élément en cas de besoins d’adaptation.
    C’est toujours dommage d’abandonner… Je ne vous aurais pas écrit beaucoup d’article si j’avais arrêté à chaque obstacle :)
    Si tu as des questions n’hésite pas.
    Bon courage.

  31. Jonas dit :
    21 mai 2012

    Bonjour

    j’ai un souci avec le curve-shadow. Les positions des pseudo-éléments ne sont pas les mêmes sous firefox/chrome que sur safari. Tout s’affiche correctement sous safari or sous firefox/chrome je ne vois pas les ombres. quelqu’un aurai une idée comment résoudre ce problème?

    merci

  32. Stéphane dit :
    25 mai 2012

    Merci pour cet excellent tuto.

    @Jonas : il y a un bug dans l’exemple, il faut utiliser les propriétés -moz-transform pour Mozilla et -webkit-transform pour Chrome.

  33. Geoffrey dit :
    25 mai 2012

    @Jonas , @Stéphane :
    Hello,
    j’en parle dans le début de l’article. Pour alléger le code je n’écris pas tous les préfixes.
    Merci pour le rappel ;)

  34. jonas dit :
    25 mai 2012

    @Stéphane @Geoffrey

    Salut,
    Merci pour vos réponses. j’avais bien ajouté les propriétés -moz- et -webkit- mais le problème ce pose toujours. J’utilise wordpress pour faire mon site, ce problème se pose quand je prévisualise la page via wordpress mais quand je passe directement par le navigateur (donc pas barre de menu wordpress) tout s’affiche correctement. Je ne sais pas vous allez comprendre ce que je veux dire :-)

    Je vais bientôt mettre mon site en ligne donc je vous tiendrai au courant si le problème se pose toujours ou pas.

    Bonne continuation!

Laisser un commentaire

Les sites qui en parlent

  1. CSS3 drop shadow le 10 mai 2011
  2. デザインダイアログ - 擬似要素(:before・:after)を使ったbox-shadowのエフェクトでデザインアクセント! le 23 mars 2012
 
Le studio web