Un petit tutoriel pour réaliser un effet de cadre en HTML5/CSS3 sans image, compatible avec tous les navigateurs récents et dégradation pour EI8-. J’utiliserais LessCSS pour la création de la feuille de style (vous trouverez dans cet article annexe des informations afin d’appréhender rapidement LESS).

Commençons donc par la structure HTML5 :

?Download cadre.html
<!DOCTYPE HTML>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Cadre vintage</title>
        <link rel="stylesheet" type="text/css" media="all" href="html5reset.css" />
        <link rel="stylesheet" type="text/css" media="all" href="cadre.css" />       
 
        <!--[if lt IE 9]>
                <script src="html5shiv.js"></script>
        <![endif]-->
        <script type="text/javascript" src="css-browser-selector.js"></script>
    </head>
    <body>
        <footer>
            <nav>
                <ul>
                    <li><a href="#">CGV</a></li>
                    <li><a href="#">Mentions Légales</a></li>
                    <li><a href="#">Plan du site</a></li>
                    <li><a href="#">Signature</a></li>
                </ul>
            </nav>
        </footer>
    </body>
</html>

La structure est assez simple. Ici on va designer un footer, on utilise donc logiquement la balise HTML5 <footer>. Nous mettons une navigation à l’intérieur, donc balise <nav> et comme il s’agit d’une liste de liens, on utilise une liste non ordonnée : <ul>.
On ajoute également deux scripts pour la compatibilité avec IE8- : html5shiv.js et css-browser-selector.js. Le premier permet à IE8- de reconnaître les balises HTML5 et le second ajoute des class à la balise html afin de cibler le navigateur utilisé (très pratique pour créer des propriétés CSS en fonction du navigateur). On aurait aussi pu ne pas utiliser de HTML5 et les commentaires IE pour cibler une feuille de style alternative.

Passons au CSS.
Pour commencer, il faut créer un fichier .less.

@import url("html5reset.css");
 
/*Masquer les pointiller sur les liens dans Firefox*/
a, object{outline: none;}
:focus{-moz-outline-style: none; outline: 0;}
/*-----------------------------------*/
 
*{margin:0; padding:0;}
html{font-size:64.2%;}

On a importé un fichier CSS pour la compatibilité HTML5 (html5reset.css), on a supprimé l’effet de pointillés de Firefox lorsqu’on clique sur les liens, puis réinitialisé tous les margin et padding à 0 (cette technique est discutable pour certain, pour ma part, je trouve que ça évite beaucoup de problème par la suite). La dernière propriété permet de définir plus simplement les tailles de police, par exemple 2em = 20px (grosso modo).

On ajoute à présent des variables de couleur (grâce à LESS)

@import url("html5reset.css");
 
/*Masquer les pointiller sur les liens dans Firefox*/
a, object{outline: none;}
:focus{-moz-outline-style: none; outline: 0;}
/*-----------------------------------*/
 
*{margin:0; padding:0;}
html{font-size:64.2%;}
 
//Couleurs
@MarronFonce     : #42210B;
@MarronClair     : #B88C5C;
 
@BeigeClair      : lighten(@MarronClair,36.5%);

Nous pourrons ainsi utiliser ces couleurs au lieu des codes hexadécimaux dans nos propriétés et fonctions. Plutôt pratique pour les mises à jour. Lighten est une fonction native de LessCSS qui permet d’éclaircir une couleur.

À présent nous allons configurer trois fonctions de base, je les utilise presque tout le temps : box-shadow, rounded-corner et transformation. Ces propriétés existent déjà en CSS3, c’est vrai, mais en attendant que chaque navigateur l’implémente par défaut et pour rester compatible avec un maximum d’anciens navigateurs, il faut à chaque fois copier 4 à 5 lignes de code sinon plus.

.box-shadow(@x:0,@y:0,@blur:0,@spread:0,@color:#000,...){
  box-shadow: @arguments;
  -moz-box-shadow: @arguments;
  -webkit-box-shadow: @arguments;
}
.rounded-corners(@radius:5px,...) {
  border-radius: @arguments;
  -webkit-border-radius: @arguments;
  -moz-border-radius: @arguments;
}
.transitions(@property:all,@duration:500ms,@style:ease){
    -webkit-transition: @property @duration @style;
    -moz-transition: @property @duration @style;
    -o-transition: @property @duration @style;
    transition: @property @duration @style;
}
.Ombre(@top){
    content:' ';
    display:block;
    position:relative;
    overflow:hidden;
    width:100%;
    height:20px;
    z-index:-1;
    top:@top;
 
    .box-shadow(0,0,10px,5px,fade(@MarronFonce,80%));
    .rounded-corners(50%);
}

J’ai également crée une fonction .Ombre sur laquelle nous reviendront un peu plus bas, on peut cependant remarquer qu’elle utilise elle-même les fonctions .box-shadow et .rounded-corners.

Décomposons le menu en partant du plus précis au plus général :

  • les liens, qui auront un survol
  • les items qui les contiennent et qui seront espacés les uns des autres
  • la liste qui aura le fond beige et une ombre interne, plus un léger dégradé et une marge pour l’épaisseur du cadre (plus souple que de mettre un padding au parent)
  • la navigation qui aura un dégradé marron et des ombres
  • le footer qui contient tout notre petit monde

Attaquons nous au cadre extérieur. On crée une nouvelle fonction :

.CadreMenuExt{
    position:relative;
    border-top:1px solid lighten(@MarronClair,20%);
 
    background: @MarronFonce; /* Old browsers */
    background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzQyMjEwYiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iI2I4OGM1YyIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiM0MjIxMGIiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+);
    background: -moz-linear-gradient(left,  @MarronFonce 0%, @MarronClair 50%, @MarronFonce 100%); /* FF3.6+ */
    background: -webkit-gradient(linear, left top, right top, color-stop(0%,@MarronFonce), color-stop(50%,@MarronClair), color-stop(100%,@MarronFonce)); /* Chrome,Safari4+ */
    background: -webkit-linear-gradient(left,  @MarronFonce 0%,@MarronClair 50%,@MarronFonce 100%); /* Chrome10+,Safari5.1+ */
    background: -o-linear-gradient(left,  @MarronFonce 0%,@MarronClair 50%,@MarronFonce 100%); /* Opera 11.10+ */
    background: -ms-linear-gradient(left,  @MarronFonce 0%,@MarronClair 50%,@MarronFonce 100%); /* IE10+ */
    background: linear-gradient(left,  @MarronFonce 0%,@MarronClair 50%,@MarronFonce 100%); /* W3C */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='@{MarronClair}', endColorstr='@{MarronFonce}',GradientType=0 ); /* IE6-8 */
 
    &:before{
        .Ombre(20px);
        margin-top:-20px;
    }
    &:after{
        .Ombre(0);
        margin-top:-20px;
    }
}

On lui donne un positionnement, ça évite toujours les bugs, ensuite on lui rajoute une bordure au dessus pour donner un petit effet de relief.
Viennent ensuite les différentes propriétés pour le dégradé (générées grâce à un générateur en ligne), autant dire un certain nombre, d’où l’utilité d’utiliser une fonction si vous devez les utiliser souvent. On pourrait par exemple passer les couleurs du dégradé en paramètre afin de pourvoir réutiliser ce cadre avec différentes couleurs (attention cependant que le code du svg ne pourra pas correspondre pas aux couleurs et le dégradé ne fonctionnera donc pas avec IE9, ou aura par défaut l’image générée par le svg).
On ajoute ensuite les ombres grâce aux pseudo-élements before et after. On utilise donc notre fonction .Ombre en modifiant son paramètre. Le principe de cette fonction : on crée un élément de type block d’une hauteur de 20px (totalement arbitraire) auquel on applique un ombre portée grâce à .box-shadow. On arrondit les angles afin d’obtenir une ellipse grâce à la fonction .rounded-corner et au paramètre définit à 50%. Selon que l’ombre soit en haut ou en bas, on la décale de sa hauteur afin de ne garder visible que l’ombre (et donc de masquer le bloc vide), puis on définit le z-index à -1 afin que les pseudo-éléments restent à l’arrière plan.

Appliquons cette fonction à notre balise nav :

nav{
    .CadreMenuExt;
}

Ajoutons également un padding au footer afin de faire apparaître l’ombre supérieure :

footer{
    padding:20px 0 0 0;
}
nav{
    .CadreMenuExt;
}

Pour le moment ça devrait ressembler à peu près à ça :

Plus que l’intérieur à faire. On crée une nouvelle fonction .CadreMenuInt :

.CadreMenuInt{
    position:relative;
    overflow:hidden;
    border-bottom:solid 1px lighten(@MarronClair,20%);
    margin:3px 4px 4px;
 
    background: @BeigeClair; /* Old browsers */
    background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzZhNDkzYyIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjI1JSIgc3RvcC1jb2xvcj0iI2YxZThkZSIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgPC9saW5lYXJHcmFkaWVudD4KICA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2dyYWQtdWNnZy1nZW5lcmF0ZWQpIiAvPgo8L3N2Zz4=);
    background: -moz-linear-gradient(top,  @MarronFonce -20%, @BeigeClair 25%); /* FF3.6+ */
    background: -webkit-gradient(linear, left top, left bottom, color-stop(-20%,@MarronFonce), color-stop(25%,@BeigeClair)); /* Chrome,Safari4+ */
    background: -webkit-linear-gradient(top,  @MarronFonce -20%,@BeigeClair 25%); /* Chrome10+,Safari5.1+ */
    background: -o-linear-gradient(top,  @MarronFonce -20%,@BeigeClair 25%); /* Opera 11.10+ */
    background: -ms-linear-gradient(top,  @MarronFonce -20%,@BeigeClair 25%); /* IE10+ */
    background: linear-gradient(top,  @MarronFonce -20%,@BeigeClair 25%); /* W3C */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='@{BeigeClair}', endColorstr='@{BeigeClair}',GradientType=0 ); /* IE6-8 */
 
    /*Shadow inset */
    .box-shadow(0px,0px,10px,0px,@MarronFonce,inset);
 
    li{
        float:left;
        margin-right:35px;
        position:relative;
        overflow:hidden;
    }
 
    a{
        .transitions();
        float:left;
        position:relative;
        overflow:hidden;
        font-size:2em;
        color:@MarronFonce;
        font-style:italic;
        line-height:28px;
        padding: 0 5px;
        text-decoration:none;
        height:28px;
 
        &:hover{
            background:fade(@MarronFonce,40%);
            color:@BeigeClair;
        }
    }
}

On retrouve tout d’abord des propriétés très classiques : relative, overflow (toujours par prudence), le border pour l’effet de relief et un margin pour l’épaisseur du cadre. Viennent ensuite nos propriétés de dégradé. On ajoute une ombre interne grâce à la fonction .box-shadow à laquelle on ajoute le paramètre optionnel inset.
On remarque ensuite que la fonction intègre les propriétés des autres éléments. C’est encore un point fort de LessCSS. Pour les items, on définit donc la position et l’overflow, une marge pour qu’ils ne soient pas collés les uns aux autres et un float pour IE7-.
Pour les liens, on commence par la fonction .transitions avec les paramètres vides afin d’utiliser ceux par défaut. Cette fonction va permettre de faire une petite animation au survol des liens. On définit ensuite des propriétés classiques.

Il ne reste plus qu’à appliquer cette fonction à notre menu :

footer{
    padding:20px 0 0 0;
}
nav{
    .CadreMenuExt;
}
ul{
    .CadreMenuInt;
}

Passons maintenant à la compatibilité pour IE8- et IE9 :

/*Compatibilités-----------------------------------------------*/
.ie7,.ie8{
    nav{
        overflow:hidden;
        a:hover{
            background:@MarronFonce;
            color:@BeigeClair;
        }
    }
}
.ie9{
    nav,ul{
        filter:none;
    }
}

Pour que le filter s’applique sous IE8-, il faut passer l’overflow de nav en hidden, de toute manière les ombres ne fonctionnent pas donc ça n’a pas d’importance. On définie également la couleur du survol des liens car avant IE9, point de transparence.
Pour IE9, on annule les filters utiles à IE8-, car ils empêchent l’utilisation du svg pour les dégradés.

Voilà donc le fichier au complet :

?Download cadre.less
@import url("html5reset.css");
 
/*Masquer les pointiller sur les liens dans Firefox*/
a, object{outline: none;}
:focus{-moz-outline-style: none; outline: 0;}
/*-----------------------------------*/
 
*{margin:0; padding:0;}
html{font-size:64.2%;}
 
//Couleurs
@MarronFonce     : #42210B;
@MarronClair     : #B88C5C;
@BeigeClair      : lighten(@MarronClair,36.5%);
 
.box-shadow(@x:0,@y:0,@blur:0,@spread:0,@color:#000,...){
  box-shadow: @arguments;
  -moz-box-shadow: @arguments;
  -webkit-box-shadow: @arguments;
}
.rounded-corners(@radius:5px,...) {
  border-radius: @arguments;
  -webkit-border-radius: @arguments;
  -moz-border-radius: @arguments;
}
.transitions(@property:all,@duration:500ms,@style:ease){
    -webkit-transition: @property @duration @style;
    -moz-transition: @property @duration @style;
    -o-transition: @property @duration @style;
    transition: @property @duration @style;
}
.Ombre(@top){
    content:' ';
    display:block;
    position:relative;
    overflow:hidden;
    width:100%;
    height:20px;
    z-index:-1;
    top:@top;
 
    .box-shadow(0,0,10px,5px,fade(@MarronFonce,80%));
    .rounded-corners(50%);
}
 
.CadreMenuExt{
    position:relative;
    border-top:1px solid lighten(@MarronClair,20%);
 
    background: @MarronFonce; /* Old browsers */
    background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzQyMjEwYiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iI2I4OGM1YyIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiM0MjIxMGIiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+);
    background: -moz-linear-gradient(left,  @MarronFonce 0%, @MarronClair 50%, @MarronFonce 100%); /* FF3.6+ */
    background: -webkit-gradient(linear, left top, right top, color-stop(0%,@MarronFonce), color-stop(50%,@MarronClair), color-stop(100%,@MarronFonce)); /* Chrome,Safari4+ */
    background: -webkit-linear-gradient(left,  @MarronFonce 0%,@MarronClair 50%,@MarronFonce 100%); /* Chrome10+,Safari5.1+ */
    background: -o-linear-gradient(left,  @MarronFonce 0%,@MarronClair 50%,@MarronFonce 100%); /* Opera 11.10+ */
    background: -ms-linear-gradient(left,  @MarronFonce 0%,@MarronClair 50%,@MarronFonce 100%); /* IE10+ */
    background: linear-gradient(left,  @MarronFonce 0%,@MarronClair 50%,@MarronFonce 100%); /* W3C */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='@{MarronClair}', endColorstr='@{MarronFonce}',GradientType=0 ); /* IE6-8 */
 
    &:before{
        .Ombre(20px);
        margin-top:-20px;
    }
    &:after{
        .Ombre(0);
        margin-top:-20px;
    }
}
.CadreMenuInt{
    position:relative;
    overflow:hidden;
    min-height:28px;
    border-bottom:solid 1px lighten(@MarronClair,20%);
    margin:3px 4px 4px;
 
    background: @BeigeClair; /* Old browsers */
    background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzZhNDkzYyIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjI1JSIgc3RvcC1jb2xvcj0iI2YxZThkZSIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgPC9saW5lYXJHcmFkaWVudD4KICA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2dyYWQtdWNnZy1nZW5lcmF0ZWQpIiAvPgo8L3N2Zz4=);
    background: -moz-linear-gradient(top,  @MarronFonce -20%, @BeigeClair 25%); /* FF3.6+ */
    background: -webkit-gradient(linear, left top, left bottom, color-stop(-20%,@MarronFonce), color-stop(25%,@BeigeClair)); /* Chrome,Safari4+ */
    background: -webkit-linear-gradient(top,  @MarronFonce -20%,@BeigeClair 25%); /* Chrome10+,Safari5.1+ */
    background: -o-linear-gradient(top,  @MarronFonce -20%,@BeigeClair 25%); /* Opera 11.10+ */
    background: -ms-linear-gradient(top,  @MarronFonce -20%,@BeigeClair 25%); /* IE10+ */
    background: linear-gradient(top,  @MarronFonce -20%,@BeigeClair 25%); /* W3C */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='@{BeigeClair}', endColorstr='@{BeigeClair}',GradientType=0 ); /* IE6-8 */
 
    /*Shadow inset */
    .box-shadow(0px,0px,10px,0px,@MarronFonce,inset);
 
    li{
        float:left;
        margin-right:35px;
        position:relative;
        overflow:hidden;
    }
    a{
        .transitions();
        float:left;
        position:relative;
        overflow:hidden;
        font-size:2em;
        color:@MarronFonce;
        font-style:italic;
        line-height:28px;
        padding: 0 5px;
        text-decoration:none;
        height:28px;
 
        &:hover{
            background:fade(@MarronFonce,40%);
            color:@BeigeClair;
        }
    }
}
 
footer{
    padding:20px 0 0 0;
}
nav{
    .CadreMenuExt;
}
ul{
    .CadreMenuInt;
}
 
/*Compatibilités-----------------------------------------------*/
.ie7,.ie8{
    nav{
        overflow:hidden;
        a:hover{
            background:@MarronFonce;
            color:@BeigeClair;
        }
    }
}
.ie9{
    nav,ul{
        filter:none;
    }
}

Démo :

N’hésitez pas à poser des questions ou proposer des améliorations.