CreativeJuiz, blog de Webdesign et développement autour de HTML5, CSS3, WordPress, PHP et jQuery.
Subtle Patterns est un site internet proposant des textures (ou motifs) gratuites au format PNG.
Compatibles avec le format pattern (.pat) de Photoshop, ce site propose également un système de recherche par mot clef qui vous permet de trouver rapidement des textures précises.
Développé et maintenu par Atle Mo, ce site est issu de la volonté de mettre à disposition des WebDesigners des ressources de qualité et entièrement gratuites.
Parcourez simplement la page d’accueil à la recherche des dernières textures. La liste des motifs est disponible avec un descriptif ou sous forme de miniature. Il est également possible de tester les texture à la volée directement sur l’image de fond du site Subtle Patterns. Cela vous permet de voir en application la texture avant de la télécharger.
Des bonus sont également disponibles à la page Subtle Patterns – Snacks, notamment des plugins et packs complets de textures au format PNG.
Subtle Patterns est également décliné sous la forme d’un plugin WordPress qui vous permet de prendre la main sur le fond imagé de votre site.
Attention, il me semble que pour fonctionner, votre thème WordPress doit autoriser les backgrounds customisés (ça semble logique).
Une autre ressource intéressante déjà présentée sur ce blog pour trouver des textures : Tileables – Never ending patterns
J’espère que ça vous fera de quoi vous ressourcer
Le plugin social subscribers counter développé par GeekPress est un outil très simple d’utilisation qui vous permet d’afficher à plusieurs endroits de votre site WordPress des statistiques globales des différents réseaux sociaux que vous utilisez.
Ce plugin est très simple d’utilisation.
Après l’avoir activé, rendez-vous dans la page de réglages pour renseigner les adresses de vos différentes pages de réseaux sociaux.
Proposant de compter le nombre de vos followers (abonnés Twitter), fans (abonnés Facebook) et lecteurs (abonnés au flux RSS feedburner), ce plugin ajoute ces chiffres dans l’administration de votre site WordPress, à la suite de vos statistiques globales.
Bien entendu, vous n’êtes pas obligés d’utiliser tous ces réseaux pour utiliser le plugin.
On regrettera peut-être l’absence de couleurs sur ces liens qui permettraient de distinguer plus rapidement les différents réseaux sociaux.
En plus de vous proposer un affichage des chiffres dans l’espace d’administration, un petit module est mis à votre disposition pour un aperçu de ces chiffres directement sur votre site.
Les réglages de ce module vous permettent d’afficher une accroche précise pour chacun des réseaux que vous utilisez, en plus du titre classique de module.
Un thème par défaut accompagne ce plugin et se veut simple et facilement personnalisable.
Pour les amateurs de CSS, il vous sera possible de personnaliser l’apparence en écrasant les styles existants directement depuis votre feuille de styles.
D’après ce que j’ai cru entendre ci et là, l’auteur pourrait bien ajouter quelques réseaux sociaux à la liste de ceux déjà disponibles.
Un second thème graphique pourrait également être ajouté (bien que déjà existant :p) et vous permettre, depuis les paramètres du plugins, de choisir entre l’une ou l’autre des apparences.
À suivre donc
Télécharger le plugin
Social Subscribers Counter
Si vous suivez le compte @NoupeMag sur Twitter (ça marche aussi pour mon compte), vous avez certainement vu passer mon slideshow en CSS3 datant de l’an dernier.
Ce n’était pas ma première tentative de slideshow comme le présentent ces premier et second travaux datant de mai 2010.
La page de démonstration du slideshow full CSS3 vous propose des fichiers sources en téléchargement, profitez-en, notamment si vous souhaitez récupérer les images.
Les codes CSS fournis ici sont légèrement différents de ceux fournis dans l’archive zippée téléchargeable. En effet, la rédaction du tutoriel que vous allez lire m’a permis d’alléger, corriger et améliorer le code disponible en téléchargement. Il fera l’objet d’une refonte prochaine.
Les codes CSS3 présents sur ce tutoriel ne tiennent pas compte des préfixes parfois nécessaires (-webkit-, -moz-, -ms-, -o-) pour une question de gain de place dans le code. Ne les oubliez pas.
Comme les expérimentations foireuses sont là pour faire ressortir les limites d’une technique, voyons ce qui nous manquait dans les versions précédentes (gardez-les sous les yeux pour tester, c’est mieux).
Sur la version contrôlable (la première) :
div
pour les conteneurs, a
pour les flèches) ;Sur la version automatique (la seconde) :
Certains de ces problèmes ne sont pas résolubles, j’ai décidé de me concentrer sur la fusion des fonctionnalités et sur la correction de certains points :
Les autres problèmes persisteront, vous le verrez.
Voici du code HTML (HTML5 en l’occurrence) que j’espère suffisamment parlant.
<section id="slideshow">
<div class="container">
<div class="c_slider"></div>
<div class="slider">
<figure>
<img src="img/dummy-640x310-1.jpg" alt="" width="640" height="310" />
<figcaption>The mirror of soul</figcaption>
</figure><!--
--><figure>
<img src="img/dummy-640x310-2.jpg" alt="" width="640" height="310" />
<figcaption>Let's cross that bridge when we come to it</figcaption>
</figure><!--
--><figure>
<img src="img/dummy-640x310-3.jpg" alt="" width="640" height="310" />
<figcaption>Sushi<em>(do)</em> time</figcaption>
</figure><!--
--><figure>
<img src="img/dummy-640x310-4.jpg" alt="" width="640" height="310" />
<figcaption>Waking Life</figcaption>
</figure>
</div>
</div>
<span id="timeline"></span>
</section>
Ce code HTML pourrait être simplifié en prenant le risque de complexifier la CSS, mais cette dernière va déjà l’être suffisamment…
Pour faire simple nous avons une section pour le slideshow qui réunie le conteneur des images (.container
), la partie qui glisse (.slider
), les différentes images légendées (sous forme de figure > figcaption
) et la petite barre de défilement du temps (#timeline
).
Pour ceux qui s’interrogent sur la présence de commentaires HTML, je vous invite à lire cet autre article du blog : Display inline-block, une valeur trop peu utilisée.
Je n’ai mis aucun élément de commande pour le moment, nous verrons cela plus tard.
Voici le code CSS servant principalement à positionner, dimensionner et décorer les différents blocs, que je ne vais pas expliquer dans la mesure où ce n’est pas le cœur de l’article, mais il est un minimum commenté
#slideshow {
position: relative;
width: 640px;
height: 310px;
padding: 15px;
margin: 0 auto 2em;
border: 1px solid #ddd;
background: #FFF;
/* CSS3 effects */
background: linear-gradient(#FFF, #FFF 20%, #EEE 80%, #DDD);
border-radius: 2px 2px 2px 2px;
box-shadow: 0 0 3px rgba(0,0,0, 0.2);
}
/* avanced box-shadow
* tutorial @
* http://www.creativejuiz.fr/blog/les-tutoriels/ombre-avancees-avec-css3-et-box-shadow
*/
#slideshow:before,
#slideshow:after {
position: absolute;
bottom:16px;
z-index: -10;
width: 50%;
height: 20px;
content: " ";
background: rgba(0,0,0,0.1);
border-radius: 50%;
box-shadow: 0 0 3px rgba(0,0,0, 0.4), 0 20px 10px rgba(0,0,0, 0.7);
}
#slideshow:before {
left:0;
transform: rotate(-4deg);
}
#slideshow:after {
right:0;
transform: rotate(4deg);
}
Avec ceci nous avons le support graphique des images. C’est un peu tout cassé, mais vous pouvez voir comment réaliser simplement un élément qui semble assez complexe, notamment au niveau des ombres et dégradés.
Gérons le débordement et l’alignement de tout ce contenu.
/* gestion des dimensions et débordement du conteneur */
#slideshow .container {
position:relative;
width: 640px;
height: 310px;
overflow: hidden;
}
/* on prévoit un petit espace gris pour la timeline */
#slideshow .container:after {
position:absolute;
bottom: 0; left:0;
content: " ";
width: 100%;
height: 1px;
background: #999;
}
/*
le conteneur des slides
en largeur il fait 100% x le nombre de slides
*/
#slideshow .slider {
position: absolute;
left:0; top:0;
width: 400%;
height: 310px;
}
/* annulation des marges sur figure */
#slideshow figure {
position:relative;
display:inline-block;
padding:0; margin:0;
}
/* petit effet de vignette sur les images */
#slideshow figure:after {
position: absolute;
display:block;
content: " ";
top:0; left:0;
width: 100%; height: 100%;
box-shadow: 0 0 65px rgba(0,0,0, 0.5) inset;
}
Nous voici sur une mise en forme plus propre, les éléments figure
sont placés sur une seule et même ligne et le débordement est caché.
Il ne nous reste plus qu’à présenter un peu mieux notre légende qui est actuellement planquée :
/* styles de nos légendes */
#slideshow figcaption {
position:absolute;
left:0; right:0; bottom: 5px;
padding: 20px;
margin:0;
border-top: 1px solid rgb(225,225,225);
text-align:center;
letter-spacing: 0.05em;
word-spacing: 0.05em;
font-family: Georgia, Times, serif;
background: #fff;
background: rgba(255,255,255,0.7);
color: #555;
text-shadow: -1px -1px 0 rgba(255,255,255,0.3);
}
Et voilà qui est mieux.
Nous allons maintenant nous occuper de la phase automatique du slideshow.
Nous verrons dans une troisième partie comment gérer des actions pour contrôler notre slideshow.
Cette phase va être quelque peu répétitive puisque nous allons devoir créer un certain nombre d’animations grâce à la fonction CSS @keyframes
. Pour en savoir plus sur les animations, je vous invite à lire la partie « animation » de cet article sur le timing sur Alsacréations.
Pour obtenir une animation cohérente tout le long d’un cycle, vous devez vous poser la question suivante :
Combien de temps est nécessaire pour la compréhension de chaque slide ?
Bien entendu, la réponse va dépendre du contenu, mais imaginons que nous ayons 4 étapes, pas beaucoup de texte, 6 ou 7 secondes peuvent sembler suffisantes pour chaque étape. Il reste alors à estimer la durée d’une transition d’une étape à l’autre, 1 ou 2 secondes. Avec 8 secondes multipliées par 4, ça nous fait 32 secondes d’animation pour boucler un cycle complet.
Commençons par animer le bloc qui contient nos images et légendes en le faisant glisser. Pour cela, créons notre animation :
/* fonction d'animation, n'oubliez pas de prefixer ! */
@keyframes slider {
0%, 20%, 100% { left: 0 }
25%, 45% { left: -100% }
50%, 70% { left: -200% }
75%, 95% { left: -300% }
}
Chaque étape est exprimée en pourcentage, aussi 50% signifie que nous en sommes à la 16ième seconde de notre animation.
Si plusieurs étapes ont la même valeur d’une propriété, ou si vous souhaitez créer un arrêt, il suffit de mettre la valeur sur la même ligne séparer par une virgule.
Par exemple, sur la première étape, nous avons 0, 20 et 100% qui cohabitent. Cela sous entend que de 0 à 20%, la valeur de left
est de 0, nous créons donc un arrêt. Cela signifie aussi que de 95 à 100%, la valeur de left
passe de -300% à 0. C’est un moyen facile de retourner à la valeur initiale pour créer une boucle.
Nous avons créer l’animation, il faut maintenant l’attribuer à notre élément de cette manière :
/* complétez le sélecteur : */
#slideshow .slider {
/* ... avec la propriété animation */
animation: slider 32s infinite;
}
Cette syntaxe de la propriété animation
est le raccourci de animation-name
, animation-duration
, animation-iteration-count
, dans cet ordre précisément ici.
Notre petite ligne d’un pixel de hauteur servant de ligne de temps va être animée sur le même principe.
Mais avant cela, donnons-lui quelques styles de couleur.
#timeline {
position: absolute;
background: #999;
bottom: 15px;
left: 15px;
height: 1px;
background: rgb(214,98,13);
background: rgba(214,98,13,.8);
width: 0;
/* fonction d'animation */
animation: timeliner 32s infinite;
}
Il est ainsi placé précisément en tenant compte des marges autour des photos, et nous lui attribuons tout de suite une animation nommée timeliner
qu’il nous faut déclarer comme ceci :
@keyframes timeliner {
0%, 25%, 50%, 75%, 100% { width: 0; }
20%, 45%, 70%, 90% { width: 640px; }
}
Ici nous savons que la ligne doit être à son maximum à quatre reprise (avant chaque changement de slide). Il y a donc 4 étapes à 640px (largeur max), et 4 (si on confond 0 et 100) à 0.
Même procédé, nous allons faire monter et descendre notre légende à chaque slide.
@keyframes figcaptionner {
0%, 25%, 50%, 75%, 100% { bottom: -55px; }
5%, 20%, 30%, 45%, 55%, 70%, 80%, 95% { bottom: 5px; }
}
Puis nous accrochons cette animation à notre élément :
/* ajouter à l'élément : */
#slideshow figcaption {
/* ... la propriété animation */
animation: figcaptionner 32s infinite;
}
Je ne sais pas si vous avez vu, mais le vignettage des slides est animé. Comme vous avez compris le principe, je vous laisse vous amuser à trouver le code
Nous avons désormais un slideshow qui fonctionne en mode automatique.
Maintenant, voyons comment procéder si l’on souhaite ajouter des boutons stop/marche, et suivant/précédent, ou « aller à la slide 3″, par exemple.
Lorsque l’on conçoit un slideshow, il est très recommandé de permettre à l’utilisateur de stopper l’animation, soit parce que ça le stresse, soit parce qu’il veut contempler une des images, soit… bref, voyons ce qu’on peut faire !
Pour rappel, nous n’avons encore aucun élément HTML pour ces contrôles, nous allons donc les rajouter progressivement.
Ces éléments de contrôles (des liens) vont cibler des ancres placés « à côté » de notre slideshow, en tant que frères indirects.
Juste avant notre élément #slideshow
, placez donc ces éléments span
:
<span id="sl_play" class="sl_command"></span>
<span id="sl_pause" class="sl_command"></span>
<span id="sl_i1" class="sl_command sl_i"></span>
<span id="sl_i2" class="sl_command sl_i"></span>
<span id="sl_i3" class="sl_command sl_i"></span>
<span id="sl_i4" class="sl_command sl_i"></span>
Ceux-ci ont des classes communes (parfois) qui nous permettrons des actions groupées par la suite.
Vous comprendrez leur utilité par la suite, ne vous faites pas de souci.
Les boutons play et pause peuvent être ajoutés dans la section #slideshow
, juste avant notre .container
.
Il n’y a aucune obligation à les placer précisément ici, il est généralement conseillé de conserver ordre d’apparition dans le DOM et ordre d’apparition à la lecture de la page. Là nous allons les placer au milieu (verticalement) du slideshow :
<a class="play_commands pause" href="#sl_pause" title="Maintain paused">Pause</a>
<a class="play_commands play" href="#sl_play" title="Play the animation">Play</a>
Ces éléments peuvent prendre leur apparence avec quelques lignes de code uniquement, et sont par défaut cachés : ils n’apparaissent qu’à certains moments bien précis que nous définirons par la suite.
.play_commands {
/* positionnement en haut à droite */
position: absolute;
top: 25px; right: 25px;
z-index: 10;
/* dimensionnement des icônes */
width: 22px;
height: 22px;
text-indent: -9999px;
border:0 none;
/* placez l'opacité à 1 si vous souhaitez voir les commandes */
opacity: 0;
/* préparation de transition sur opacicty et right */
transition: opacity 1s, right 1s;
}
/* on décale play légèrement sur la gauche */
.play { right: 55px; cursor: default; }
/* création de l'icône pause avec 2 pseudos éléments */
.pause:after,
.pause:before {
position: absolute;
display: block;
content: " ";
top:0;
width:38%;
height: 22px;
background: #fff;
background: rgba(255,255,255,0.5);
}
.pause:after { right:0; }
.pause:before { left:0; }
/* création de l'icône play avec des bordures */
.play {
width: 1px;
height: 1px;
/* les transparentes forment la flèche */
border-top: 10px solid transparent;
border-bottom: 10px solid transparent;
border-left: 20px solid #fff;
border-left: 20px solid rgba(255,255,255,0.5);
/* renseignez 1 pour voir l'icône de suite */
opacity: 0;
}
/* apparition du bouton pause au survole */
/* apparition des boutons au focus */
#slideshow:hover .pause,
.play_commands:focus {
opacity: 1;
outline: none;
}
Voilà, toutes ces lignes pour mettre en place nos deux liens. Ceux qui trouveront plus pertinent de charger des images ou un sprite, je ne leur en voudrais pas.
C’est bien, c’est beau, mais pas fonctionnel.
Nous allons maintenant utiliser la pseudo classe target
pour agir sur notre animation en fonction du bouton que nous aurons cliqué.
Lorsque pause est cliqué, nous ciblons l’élément #sl_pause
, lorsque lecture est cliqué, c’est #sl_play
qui est ciblé.
Dans notre scénario, nous n’afficherons que le bouton utile : pause quand la lecture est effective, play lorsque la pause est en place.
/* stopper les animation */
.sl_command:target ~ #slideshow .slider,
.sl_command:target ~ #slideshow figcaption,
.sl_command:target ~ #slideshow #timeline,
.sl_command:target ~ #slideshow .dots_commands li:first-child a:after {
animation-play-state: paused;
}
/* redémarrer les animations */
#sl_play:target ~ #slideshow .slider,
#sl_play:target ~ #slideshow figcaption,
#sl_play:target ~ #slideshow #timeline,
#sl_play:target ~ #slideshow .dots_commands li:first-child a:after {
animation-play-state: running;
}
/* switch entre les boutons */
.sl_command:target ~ #slideshow .pause { opacity:0; }
#sl_play:target ~ #slideshow:hover .pause,
#sl_play:target ~ #slideshow .pause:focus { opacity:1; }
.sl_command:target ~ #slideshow .play { opacity:1; right: 25px; cursor: pointer; }
#sl_play:target ~ #slideshow .play { opacity:0; right: 55px; cursor: default; }
Le premier bloc permet de stopper les animations grâce à la propriété animation-play-state
. Cette propriété placée à sa valeur paused
permet de figer l’animation.
Les sélecteurs sont assez spéciaux : .sl_command:target ~ #slideshow .slider
permet de cibler « L’élément .slider
fils de #slideshow
lorsque ce dernier est indirectement frère de .sl_command
, uniquement lorsque ce dernier est ciblé. ». Autrement dit : si .sl_command
est ciblé, alors on va chercher le frère #slideshow
puis son fils .slider
pour lui appliquer un stop.
On procède de la même manière pour les autres éléments (figcaption
, #timeline
et .dots_commands
que nous verrons plus tard dans le bonux) qui ont une animation en route également.
Pour en savoir plus sur les sélecteurs d’adjacence indirecte, lisez cet autre article du blog : Sélecteur d’adjacence indirecte en CSS3.
Le dernier bloc de code permet d’afficher les boutons en fonction de situation précise, je vous laisse essayer de décortiquer leur signification.
Il va nous falloir une paire de liens par étape de notre slideshow en gardant en tête que la flèche de gauche de notre première slide nous permet d’aller à la dernière, la flèche de droite de notre dernière slide nous permet d’aller à la première (vous me suivez ?). Lisez les title
pour comprendre l’utilité de chaque flèche. Vous pouvez placer ces liens après nos deux liens pause et lecture.
<a class="commands prev commands1" href="#sl_i4" title="Aller à la dernière slide"><</a>
<a class="commands next commands1" href="#sl_i2" title="Aller à la 2ème slide">></a>
<a class="commands prev commands2" href="#sl_i1" title="Aller à la 1ère slide"><</a>
<a class="commands next commands2" href="#sl_i3" title="Aller à la 3ème slide">></a>
<a class="commands prev commands3" href="#sl_i2" title="Aller à la 2ème slide"><</a>
<a class="commands next commands3" href="#sl_i4" title="Aller à la 4ème slide">></a>
<a class="commands prev commands4" href="#sl_i3" title="Aller à la 3ème slide"><</a>
<a class="commands next commands4" href="#sl_i1" title="Aller à la 1ère slide">></a>
C’est là que la pertinence du markup en prend un coup. Pour des raisons d’accessibilité (clavier ici) ces liens ne sont peut-être pas des plus utiles et pratiques à utiliser. Est peut-être à envisager l’ajout de l’attribut tabindex="-1"
pour empêcher le focus sur ces éléments. En effet, nous utiliserons un second système de navigation juste après, qui lui est peut-être plus pertinent.
Donnons quelques styles à nos boutons et cachons ceux qui ne sont pas utiles.
#slideshow .commands {
position: absolute;
top: 45%;
padding: 5px 13px;
border-bottom:0;
font-size: 1.3em;
color: #aaa;
text-decoration:none;
background-color: #eee;
background-image: linear-gradient(#fff,#ddd);
text-shadow: 0 0 1px #aaa;
border-radius: 50%;
box-shadow: 1px 1px 2px rgba(0,0,0,0.2);
}
#slideshow .prev { left: -48px; }
#slideshow .next { right: -48px; }
#slideshow .commands { display:none; }
#slideshow .commands1 { display: block; }
Très bien, nous avons des flèches… mais que va-t-on bien pouvoir bouger avec ?
Sur le même principe que précédemment, nous ciblons l’un des span
abandonnés en début de code. Si vous cliquez sur une des flèches, pour le moment ça ne fait que stopper l’animation (oui, tous les span
sont porteurs de la classe .sl_command
, rappelez-vous, en en ciblant un, les animations s’arrête).
En fonction de l’élément ciblé, on pourrait stopper l’animation et changer les valeurs de left
pour le slider, de width
pour la timeline, et de bottom
pour la légende. Ça serait cool hein ?
Oui mais non, parce qu’en fait ça ne fonctionne pas : les keyframes sont « prioritaires ».
C’est ce qui nous oblige à compléter notre code HTML légèrement avec ce petit morceau :
<div class="c_slider"></div>
Il est déjà présent dans le code fourni initialement (premier bloc de cet article).
Cet élément nous permet d’appliquer les images en image de fond. Ces mêmes images que nous avons déjà chargé dans le HTML, nous allons les appliquer en background CSS.
C’est en faisant bouger ce nouvel élément que nous allons donner l’illusion d’un slideshow classique. Je dis bien illusion, car vous aurez compris qu’ici on se restreint à l’idée d’un vrai diaporama, avec des images, et non du contenu textuel qu’il serait difficile de « dupliquer ».
#slideshow .c_slider {
position: absolute;
left:0; top:0;
width: 400%;
height: 310px;
/* multiple background */
background: url(img/dummy-640x310-1.jpg) 0 0 no-repeat,
url(img/dummy-640x310-2.jpg) 640px 0 no-repeat,
url(img/dummy-640x310-3.jpg) 1280px 0 no-repeat,
url(img/dummy-640x310-4.jpg) 1920px 0 no-repeat;
transition: background 1s;
}
Cette division fournies de quelques images est cachées par la partie principale du diaporama pour le moment. Lorsque nous ciblerons le contenu avec l’une de nos flèches « suivant » ou « précédent », nous cacherons ce diaporama automatique (qui se sera stoppé) pour découvrir nos slides contrôlables qui bougeront en fonction de l’élément ciblé.
Vous n’allez pas m’aimer… :
/* on cache le slider */
.sl_i:target ~ #slideshow .slider { visibility: hidden }
/* on planque la 1ère pastille (cf. bonux suivant) */
.sl_i:target ~ #slideshow .dots_commands li:first-child a:after { display:none; }
/* pour afficher la 2ème pastille (cf. bonux suivant) */
.sl_i:target ~ #slideshow .dots_commands li:first-child a:before { display:block; }
/* lorsque on cible le premier slider */
/* on cache tous les "précédent" et "suivant" */
#sl_i1:target ~ #slideshow .commands { display: none; }
/* on affiche seulement deux flèches */
#sl_i1:target ~ #slideshow .commands1 { display: block; }
/* correspond au décalage des images */
#sl_i1:target ~ #slideshow .c_slider { background-position: 0 0, 640px 0, 1280px 0, 1920px 0; }
/* on place la pastille tout à gauche */
#sl_i1:target ~ #slideshow .dots_commands li:first-child a:before { left:0; }
/* même procédé lorsqu'on cible le second slide */
#sl_i2:target ~ #slideshow .commands { display: none; }
#sl_i2:target ~ #slideshow .commands2 { display: block; }
#sl_i2:target ~ #slideshow .c_slider { background-position: -640px 0, 0 0, 640px 0, 1280px 0; }
#sl_i2:target ~ #slideshow .dots_commands li:first-child a:before { left:18px; }
/* puis le 3ème */
#sl_i3:target ~ #slideshow .commands { display: none; }
#sl_i3:target ~ #slideshow .commands3 { display: block; }
#sl_i3:target ~ #slideshow .c_slider { background-position: -1280px 0, -640px 0, 0 0, 640px 0; }
#sl_i3:target ~ #slideshow .dots_commands li:first-child a:before { left:36px; }
/* et enfin le 4ème */
#sl_i4:target ~ #slideshow .commands { display: none; }
#sl_i4:target ~ #slideshow .commands4 { display: block; }
#sl_i4:target ~ #slideshow .c_slider { background-position: -1920px 0, -1280px 0, -640px 0, 0 0; }
#sl_i4:target ~ #slideshow .dots_commands li:first-child a:before { left:54px; }
À chaque étape nous déplaçons les images, cachons tous les liens « précédent » et « suivant » sauf ceux dont on peut avoir besoin, et on déplace la petite pastille de la navigation secondaire (voir bonus qui suit).
Vous avez certaine souvent vu ces petits points au pied d’un slideshow qui servent un peu de pagination et qui, parfois, permettent de savoir précisément où on se trouve dans le déroulement des slides.
Nous allons en créer une. Pour commencer, ajoutons ce code HTML juste après l’élément timeline
<ul class="dots_commands"><!--
--><li><a title="Afficher la slide 1" href="#sl_i1">Slide 1</a></li><!--
--><li><a title="Afficher la slide 2" href="#sl_i2">Slide 2</a></li><!--
--><li><a title="Afficher la slide 3" href="#sl_i3">Slide 3</a></li><!--
--><li><a title="Afficher la slide 4" href="#sl_i4">Slide 4</a></li>
</ul>
Puis, ces quelques lignes de CSS pour donner un style bien propre à nos petits points.
N’hésitez pas à agrandir les dimensions si ça vous semble trop petit.
.dots_commands {
padding:0;
margin:32px 0 0;
text-align: center;
}
.dots_commands li {
display: inline;
padding:0; margin:0;
}
.dots_commands a {
position: relative;
display: inline-block;
height:8px; width: 8px;
margin: 0 5px;
text-indent: -9999px;
background: #fff;
border-radius: 50%;
box-shadow: 0 1px 2px rgba(0,0,0,0.55) inset;
}
/* quelques styles au focus */
.dots_commands a:focus {
outline: none;
background: orange;
}
.dots_commands li:first-child a { z-index: 25; }
Rien de sorcier ici.
On s’assure simplement en dernière ligne que le premier élément ait un z-index supérieur aux autres. Nous allons nous en servir pour l’animation qui suit.
Nous allons créer un petit disque par dessus ces puces pour représenter le slide visible par un point d’une couleur différente. Pour ce faire nous utilisons les pseudo-éléments.
/* on style after et before, on utilisera les deux */
.dots_commands li:first-child a:after,
.dots_commands li:first-child a:before {
position: absolute;
top: 0; left: 0;
content: " ";
width: 8px; height: 8px;
background: #bd9b83;
z-index:20;
border-radius: 50%;
box-shadow: 0 1px 2px rgba(0,0,0,0.55) inset;
}
/* on anime "after" */
.dots_commands li:first-child a:after {
animation: dotser 32s infinite;
}
/* on cache "before", on l'utilise uniquement au clic (cf. bonux précédent) */
.dots_commands li:first-child a:before { display:none; }
/* c'est parti pour l'animation ! */
@keyframes dotser {
0%, 100% { opacity: 1; left: 0; }
20% { opacity: 1; left: 0; }
22% { opacity: 0; left: 0; }
23% { opacity: 0; left: 18px; }
25% { opacity: 1; left: 18px; }
45% { opacity: 1; left: 18px; }
47% { opacity: 0; left: 18px; }
48% { opacity: 0; left: 36px; }
50% { opacity: 1; left: 36px; }
70% { opacity: 1; left: 36px; }
72% { opacity: 0; left: 36px; }
73% { opacity: 0; left: 54px; }
75% { opacity: 1; left: 54px; }
95% { opacity: 1; left: 54px; }
97% { opacity: 0; left: 54px; }
98% { opacity: 0; left: 0; }
}
Comme nous l’avons prévu dans notre bonus précédent, nous n’avons rien besoin de gérer en plus. Ces liens ciblent les mêmes éléments que nos flèches latérales et ont donc le même effet. (stop de l’animation et slide contrôlable avec tous les effets qui y sont liés)
L’animation peut sembler compliquée, mais c’est la logique de « disparition, déplacement, apparition » multiplié par 4 (4 étapes). Pour information, jusqu’à très récemment, Google Chrome avait du mal avec les animations sur before ou after. Je ne sais pas si ça a changé, il me semble.
Et on en restera là !
Pour être sûr que tout fonctionne, j’ai effectué un test en suivant ce tutoriel et en préfixant pour Firefox uniquement, voici le résultat : Slideshow Full CSS3.
Ce qui est bloquant est gourmand en lignes de code ici, c’est la volonté de vouloir fusionner système automatique et système contrôlable au clavier, souris ou même touché.
Ce slideshow date de novembre 2011, les techniques se sont multipliées, il existe d’autres solutions full CSS3 passant par l’utilisation détournée des éléments label
et input
de type radio
. En contrôlant la valeur de la coche avec la pseudo-classe checked
il est possible d’agir sur les éléments, de la même manière que nous le faisons ici avec la pseudo-classe target
.
Je vous invite à regarder ce qui peut se faire ailleurs, comme par exemple sur ce Slideshow Full CSS3 Responsive.
L’espace de commentaires est à vous !
Cela fait maintenant deux années que le blog Creative Juiz vous propose des articles sur les technologies du web et le web design. Cette activité aussi passionnante que chronophage a amené un certain nombre de discussions ici, sur Twitter, ou sur les autres réseaux sociaux.
C’est ce type d’interactions et de participations qui me donne envie de continuer à écrire et à partager avec vous. Ce sont ces discussions qui permettent de générer de nouvelles idées, d’améliorer celles existantes, et bannir les mauvaises pratiques.
J’ai rencontré des lecteurs du blog lors de la dernière conférence web KiwiParty. Cette brève rencontre m’a rassuré et encouragé (merci les gars) à continuer à écrire dans l’idée de contribuer – à ma manière – au web francophone (mais pas uniquement).
Sans prétention aucune, et comme je l’ai fait l’an dernier, je vous donne quelques chiffres pour le fun :
Les chiffres précis :
Les moins précis
J’en profite également pour annoncer une refonte complète du blog courant de l’année (oui c’est vague) qui aura pour objectifs principaux de revoir la navigation aux travers des différentes thématiques phares du blog, mais également d’optimiser l’interface et le chargement du blog sur les différents terminaux (mobiles, tablettes, mini-pc, pc, etc.)
La newsletter devrait également être mise plus en avant dans les mois à venir (les inscriptions sont ouvertes si ça vous dit, il suffit de compléter le formulaire dans la petite colonne latérale).
Bref, tout plein de choses qui vont arriver progressivement dans le but de vous offrir un contenu de bonne qualité, je l’espère.
N’hésitez donc pas à profiter de l’occasion pour critiquer les points qui semblent ne pas vous convenir sur ce blog.
Je vais avoir du mal à être aussi précis que l’an dernier dans mes remerciements, par peur d’avoir une liste trop longue de personnes à remercier, par peur également d’oublier des personnes, alors merci à tous pour vos commentaires, encouragements et contributions.
Merci à vous pour cette troisième année que nous entamons ensemble !
WordPress possède un formulaire de commentaire adapté à la plupart des blogs. Cependant, comment s’y prendre si vous souhaitez ajouter ou supprimer des champs et personnaliser l’affichage des commentaires ?
WordPress prévoit la possibilité de personnaliser le formulaire de commentaire grâce à la fonction comment_form( $args, $post_id )
, cette fonction est intéressante mais quelque peu limitée. Nous allons plutôt aborder quelques crochets (hooks) s’y rapprochant.
Concrètement, il faut procéder en trois étapes :
Je vous invite à éditer votre fichier functions.php
pour effectuer des tests avec les codes que je vais fournir.
Allez, c’parti !
Cette première étape va utiliser le hook comment_form_defaults
et se présente sous la forme d’une manipulation de tableau PHP.
Les champs sont enregistrés dans l’entrée « fields » du tableau et peuvent être manipulés assez simplement. Ils contiennent du code HTML que nous allons reproduire pour nos champs personnalisés.
add_filter( 'comment_form_defaults', 'juiz_manage_default_fields');
// $default contient tous les messages du formulaire de commentaire
// il contient également "comment_field", le textarea du message
if ( !function_exists('juiz_manage_default_fields')) {
function juiz_manage_default_fields( $default ) {
// Récupération des infos connues sur le visiteur
// Permet de pré-remplir nos nouveaux champs
$commenter = wp_get_current_commenter();
// Suppression d'un champ par défaut parmi : author, email, url
unset ( $default['fields']['url'] );
// Ajout des champs dans le tableau "fields"
// $commenter[] contient les infos sur le visiteur
$default['fields']['job'] = '<p class="comment-form-author-job comment-form-author">
<label for="job">'. __('Your job') . '</label>
<span class="required">*</span>
<input id="job" name="job" value="'.$commenter['comment_author_job'].'" aria-required="true" size="30" type="text" />
</p>';
$sel_female = $sel_male = '';
if ( $commenter['comment_author_gender'] != '') ${'sel_'.$commenter['comment_author_gender']} = ' checked="checked"';
$default['fields']['gender'] = '<p class="comment-form-author-gender">
<span class="label_like">'. __('Your gender') . '</span>
<label for="female">F</label> <input '. $sel_female .' id="female" name="gender" value="female" type="radio" />
<label for="male">M</label> <input '. $sel_male .' id="male" name="gender" value="male" type="radio" />
</p>';
// On retourne le tableau des champs
return $default;
}
}
Quelques explications s’imposent :
Concernant le HTML produit, je copie et adapte la structure par défaut de mon thème sur les champs de formulaire (ici TwentyEleven) dans le but de limiter les modifications futures sur la CSS (d’où la copie de la classe comment-form-author
sur comment-form-author-job
).
Le tableau des champs (fields) ressemble à cela :
array(
'author' => '<p class="comment-form-author">…',
'email' => '<p class="comment-form-email">…',
'url' => '<p class="comment-form-url">…'
);
Pour ajouter un champ il me suffit donc de faire (en remplaçant « slug » par quelque chose de pertinent) :
$default['fields']['slug'] = '<p class="comment-form-author-slug">…';
La ligne 12 récupère les cookies enregistrés lorsque qu’un commentaire est posté par un utilisateur. Ils n’existent donc pas lorsqu’un visiteur n’a jamais commenté sur votre blog, mais permettent ensuite de pré-remplir les champs (ligne 26 et 30) si l’utilisateur revient poster un commentaire.
Nous verrons plus loin comment nous enregistrons les cookies pour nos nouveaux champs de formulaire.
À noter : Il existe le hook comment_form_defaults_fields
qui permet de manipuler directement les champs (ex : unset($default['url']
). Cependant j’ai une légère préférence pour le hook comment_form_defaults
car il permet d’atteindre d’autres éléments du formulaire.
Par contre, si votre objectif est de supprimer simplement le champ url par exemple :
add_filter('comment_form_default_fields','juiz_remove_url_field');
if ( !function_exists('juiz_remove_url_field')) {
function juiz_remove_url_field( $fields ) {
unset($fields['url']); return $fields;
}
}
Cette seconde étape se découpe en deux sous-étapes si certains de vos champs sont obligatoires.
Le premier hook utilisé est comment_post
qui permet d’intervenir lorsque le commentaire a été enregistré.
Le second est preprocess_comment
qui intervient avant l’enregistrement des données en base, notamment pour les contrôler.
Nous allons donc d’abord contrôler notre champ obligatoire ‘job’ pour retourner une erreur s’il est vide :
add_filter( 'preprocess_comment', 'juiz_verify_comment_data' );
if ( !function_exists('juiz_verify_comment_data') ) {
function juiz_verify_comment_data( $commentdata ) {
// si job est vide on affiche une erreur
if ( ! isset( $_POST['job'] ) )
wp_die( __( 'Error: please fill the required field (job).' ) );
// si job dépasse 45 caractères (arbitraire) on affiche une erreur
elseif ( isset( $_POST['job'] ) AND strlen ( $_POST['job'] ) > 45 )
wp_die( __( 'Error: 45 maximum char. for "job" field.' ) );
return $commentdata;
}
}
Une fois la donnée obligatoire contrôlée, nous enregistrons les données du commentaire :
add_action( 'comment_post', 'juiz_save_comment_data' );
if ( !function_exists('juiz_save_comment_data') ) {
function juiz_save_comment_data( $comment_id ) {
// définition de la durée de vie des cookies
$comment_cookie_lifetime = apply_filters('comment_cookie_lifetime', 30000000);
if (isset($_POST['job'])) {
// on enregistre l'info en base de données
add_comment_meta( $comment_id, 'job', esc_html( $_POST['job'] ) );
// on enregistre un cookie
setcookie('comment_author_job_' . COOKIEHASH, esc_html( $_POST['job'] ), time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN);
}
// on vérifie que le champ gender respecte les valeurs prévues
if (isset($_POST['gender']) AND in_array ( $_POST['gender'] , array('male', 'female'))) {
// même schéma que précédemment
add_comment_meta( $comment_id, 'gender', esc_html($_POST['gender']) );
setcookie('comment_author_gender_' . COOKIEHASH, esc_html( $_POST['gender']), time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN);
}
}
}
Le cookie est optionnel mais il permet de reproduire le comportement de WordPress qui ajoute un cookie par champ du formulaire de commentaire pour se souvenir des données entrées par le visiteur.
En l’état, nous avons ajouté les infos en base de données, nous avons créé des cookies, et nous avons pris soin, dans l’étape précédente, de récupérer ces cookies grâce à la fonction wp_get_current_commenter()
. Sauf qu’en fait cette fonction récupère uniquement les cookies pour les champs « classiques » (Nom, url, e-mail). Il faut donc compléter cette fonction pour l’inviter à piocher dans nos cookies fraichement créés :
add_filter('wp_get_current_commenter', 'juiz_add_custom_comment_cookies');
function juiz_add_custom_comment_cookies($cookies) {
$comment_author_job = '';
if ( isset($_COOKIE['comment_author_job_'.COOKIEHASH]) )
$comment_author_job = $_COOKIE['comment_author_job_'.COOKIEHASH];
$comment_author_gender = '';
if ( isset($_COOKIE['comment_author_gender_'.COOKIEHASH]) )
$comment_author_gender = $_COOKIE['comment_author_gender_'.COOKIEHASH];
$cookies['comment_author_job'] = $comment_author_job;
$cookies['comment_author_gender'] = $comment_author_gender;
return $cookies;
}
La variable $cookies
est un tableau associatif (clé + valeur), dont la clé est le nom du cookie (sans le _cookiehash
) et la valeur est la valeur du cookie que l’on récupère s’il existe.
Maintenant il faut afficher ces informations dans notre liste de commentaire !
Cette dernière étape permet d’afficher les informations dans la liste de commentaires. Il existe un certain nombre de hooks qui permettent d’intervenir sur des zones précises d’un commentaire. Je vais en utiliser deux ici qui sont : get_comment_author_link
et get_avatar
.
Je souhaite afficher le job à côté du nom de l’auteur d’un commentaire.
add_filter( 'get_comment_author_link', 'juiz_attach_custom_info_to_comments_list' );
if ( !function_exists('juiz_attach_custom_info_to_comments_list') ) {
function juiz_attach_custom_info_to_comments_list( $author ) {
// on récupère l'info job
$job = get_comment_meta( get_comment_ID(), 'job', true );
// si l'info existe, on l'ajoute entre parenthèse après l'auteur
if ( $job )
$author .= ' (' . $job . ')';
// on retourne l'info
return $author;
}
}
La variable $author
contient le code HTML qui affiche le nom de l’auteur (avec ou sans lien vers son site). On ne fait donc que rajouter du contenu à la suite de cette chaine.
Cette technique a l’avantage d’ajouter l’information également dans le widget qui traite des derniers commentaires postés.
Pour afficher la valeur de gender, j’aurais très bien pu faire la même chose en ajoutant l’information à la suite après l’avoir récupérée avec get_comment_meta()
. Mais comme j’aime bien me compliquer la vie, j’ai décidé d’ajouter une classe autour de l’avatar de l’utilisateur.
add_filter ( 'get_avatar', 'juiz_attach_custom_gender_to_avatar');
if ( !function_exists('juiz_attach_custom_gender_to_avatar') ) {
function juiz_attach_custom_gender_to_avatar( $avatar ) {
$gender = get_comment_meta( get_comment_ID(), 'gender', true );
$gender = $gender ? $gender : 'undefined';
$avatar = '<span class="gender ' . $gender . '">' . $avatar . '</span>';
return $avatar;
}
}
La variable $avatar
contient le code HTML de l’avatar de l’utilisateur, une simple image par défaut. Ici on englobe l’avatar d’un élément span
porteur du classe gender
ainsi que d’une classe dynamique qui peut être male
, female
ou undefined
en fonction de l’info que l’on récupère en base de données.
Et voilà, c’est tout pour cette longue astuce, pour le reste c’est à vous d’imaginer.
En restant dans le premier hook (comment_form_defaults
) il est possible d’éditer les champs existants en les manipulants avec des expressions régulières, ou en les réécrivant :
$default['fields']['author'] = '
<p class="comment-form-author">
<label for="author">'. __('Name') . '</label>
<span class="required">*</span>
<input id="author" name="author" value="'.$commenter['comment_author'].'" placeholder="Votre nom ou pseudo" aria-required="true" size="30" type="text" />
</p>
';
Ici je rajouter un attribut placeholder
par exemple.
Inutile de changer quoi que ce soit dans le traitement des données, WordPress le prévoit déjà.
Merci à Nicolas pour la suggestion.
J’ai croisé dans le cœur de WordPress le hook comment_post_redirect
qui permet de choisir la page de redirection lorsqu’un utilisateur a posté un commentaire.
Par défaut cette page redirige vers le commentaire de l’utilisateur avec une ancre.
Voici comment la fonction se présente :
add_action('comment_post_redirect', 'juiz_new_comment_redirection');
if ( !function_exists('juiz_new_comment_redirection') ) {
function juiz_new_comment_redirection( $location ) {
// valeur par défaut donnée par WordPress
// à vous de personnaliser $location en fonction de vos besoins
$location = empty($_POST['redirect_to']) ? get_comment_link($comment_id) : $_POST['redirect_to'] . '#comment-' . $comment_id;
return $location;
}
}
Pour ceux qui ont peur d’avoir raté une étape :
// ajout physique d'un champ
add_filter( 'comment_form_defaults', 'juiz_manage_default_fields');
// $default contient tous les messages du formulaire de commentaire
// il contient également "comment_field", le textarea du message
if ( !function_exists('juiz_manage_default_fields')) {
function juiz_manage_default_fields( $default ) {
$commenter = wp_get_current_commenter();
// Suppression d'un champ par défaut parmi : author, email, url
//unset($default['fields']['url']);
// Ajout des champs dans le tableau "fields"
$default['fields']['job'] = '<p class="comment-form-job comment-form-author">
<label for="job">'. __('Your job') . '</label>
<span class="required">*</span>
<input id="job" name="job" aria-required="true" size="30" type="text" value="' . esc_attr($commenter['comment_author_job']) . '" />
</p>';
$sel_female = $sel_male = '';
if ( $commenter['comment_author_gender'] != '') ${'sel_' . $commenter['comment_author_gender']} = ' checked="checked"';
$default['fields']['gender'] = '<p class="comment-form-gender">
<span class="label_like">'. __('Your gender') . '</span>
<label for="female">F</label> <input id="female" '. $sel_female .' name="gender" value="female" type="radio" />
<label for="male">M</label> <input id="male" '. $sel_male .' name="gender" value="male" type="radio" />
</p>';
// On retourne le tableau des champs
return $default;
}
}
// controle des champs obligatoires à l'enregistrement
add_filter( 'preprocess_comment', 'juiz_verify_comment_data' );
if ( !function_exists('juiz_verify_comment_data') ) {
function juiz_verify_comment_data( $commentdata ) {
if ( ! isset( $_POST['job'] ) )
wp_die( __( 'Error: please fill the required field (job).' ) );
elseif ( isset( $_POST['job'] ) AND strlen ( $_POST['job'] ) > 45 )
wp_die( __( 'Error: 45 maximum char. for "job" field.' ) );
return $commentdata;
}
}
//ajout en base de données des champs
add_action( 'comment_post', 'juiz_save_comment_data' );
if ( !function_exists('juiz_save_comment_data') ) {
function juiz_save_comment_data( $comment_id ) {
$comment_cookie_lifetime = apply_filters('comment_cookie_lifetime', 30000000);
if (isset($_POST['job']) AND strlen ($_POST['job']) < 45) {
add_comment_meta( $comment_id, 'job', esc_html( $_POST['job'] ) );
setcookie('comment_author_job_' . COOKIEHASH, esc_html( $_POST['job'] ), time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN);
}
if (isset($_POST['gender']) AND in_array ( $_POST['gender'] , array('male', 'female'))) {
add_comment_meta( $comment_id, 'gender', esc_html($_POST['gender']) );
setcookie('comment_author_gender_' . COOKIEHASH, esc_html( $_POST['gender']), time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN);
}
}
}
// pour que get_commenter retourne nos cookies custom
add_filter('wp_get_current_commenter', 'juiz_add_custom_comment_cookies');
function juiz_add_custom_comment_cookies($cookies) {
$comment_author_job = '';
if ( isset($_COOKIE['comment_author_job_'.COOKIEHASH]) )
$comment_author_job = $_COOKIE['comment_author_job_'.COOKIEHASH];
$comment_author_gender = '';
if ( isset($_COOKIE['comment_author_gender_'.COOKIEHASH]) )
$comment_author_gender = $_COOKIE['comment_author_gender_'.COOKIEHASH];
$cookies['comment_author_job'] = $comment_author_job;
$cookies['comment_author_gender'] = $comment_author_gender;
return $cookies;
}
// afficher l'info job dans la liste des commentaires
add_filter( 'get_comment_author_link', 'juiz_attach_custom_info_to_comments_list' );
if ( !function_exists('juiz_attach_custom_info_to_comments_list') ) {
function juiz_attach_custom_info_to_comments_list( $author ) {
$job = get_comment_meta( get_comment_ID(), 'job', true );
if ( $job )
$author .= ' (' . $job . ')';
return $author;
}
}
// afficher l'info gender graphiquement pas loin de l'avatar
add_filter ( 'get_avatar', 'juiz_attach_custom_gender_to_avatar');
if ( !function_exists('juiz_attach_custom_gender_to_avatar') ) {
function juiz_attach_custom_gender_to_avatar( $avatar ) {
$gender = get_comment_meta( get_comment_ID(), 'gender', true );
$gender = $gender ? $gender : 'undefined';
$avatar = '<span class="gender ' . $gender . '">' . $avatar . '</span>';
return $avatar;
}
}
// redirection personnalisée après un post de commentaire
add_action('comment_post_redirect', 'juiz_new_comment_redirection');
if ( !function_exists('juiz_new_comment_redirection') ) {
function juiz_new_comment_redirection( $location ) {
$location = empty($_POST['redirect_to']) ? get_comment_link($comment_id) : $_POST['redirect_to'] . '#comment-' . $comment_id;
return $location;
}
}
L’espace de commentaire et là pour vous