TL;DR ? Dépôt GitHub, la maquette hébergée sur GitHub et bien évidemment, vous pouvez l'utiliser dans cette page et donc participer au débat.
Ce projet de portage a été rendu public le 13 Novembre 2013, pour la sortie de Dotclear 2.6 et pour l'anniversaire de Kozlika. Annoncé sur le forum à la sortie du billet.

En général, je ne glose pas trop sur les développements que j'utilise pour mon blog. Je me suis rendu compte que par rapport à des confrères, j'étais très timide sur mes travaux. C'est une erreur. De même, j'aime bien prendre mon temps et réfléchir longuement sur des éléments d'interfaces.
C'est la vue de cette animation de Matt. D. Smith qui me donne envie de me croire webdesigner

Avouez que ce mockup est superbe, son animation hypnotique, promet des lendemains qui chantent, et qu'il est sexy comme une jeune femme aux yeux revolvers qui chevauche un fougueux cabriolet cheveux au vent.
Excusez-moi, je crois que je m'égare. Gare au Garou. Et garons-nous sur le bas-côté.

L'apparition de cette animation date d'un peu après une très forte remise en cause par Apple de son interface mobile iOS. En soit, il y a eu énormément de discussions passionnantes sur ce mockup, à commencer par son auteur qui s'en est expliqué sur son blog. Il y eu aussi de nombreux ports, mais la plupart travaillaient sur un usage peu modéré du javascript. Vous le verrez plus loin pourquoi je pense que ce n'est pas une solution idéale.

État des lieux

Je vais tenter d'appliquer ce principe de formulaire dans l'excellent thème Ductile pour Dotclear qui a la class de la simplicité, l'élégance du responsive, le style de la personnalisation et la french-touch de Kozlika.
Kozlika a fait un superbe travail de design, mais il faut néanmoins reconnaître que le code HTML du formulaire de commentaire de Dotclear n'a pas bougé depuis… 2005. Du moins, dans la version reprise en 2013 pour construire le thème actuel de mon blog.

Cliquez pour voir le code. Attention : Sur ce code originel, il y a prescription.
<form action="#pr" method="post" id="comment-form">

  <h3>Ajouter un commentaire</h3>
  <fieldset>

    <p class="field">
      <label for="c_name">Nom ou pseudo&nbsp;:</label>
      <input name="c_name" id="c_name" type="text" size="30" value="" />
    </p>

    <p class="field">
      <label for="c_mail">Adresse email&nbsp;:</label>
      <input name="c_mail" id="c_mail" type="text" size="30" value="" />
    </p>

    <p class="field">
      <label for="c_site">Site web (facultatif)&nbsp;:</label>
      <input name="c_site" id="c_site" type="text" size="30" value="" />
    </p>

    <!-- ici, un piège à spam -->
      
    <p class="field">
      <label for="c_content">Commentaire&nbsp;:</label>
      <textarea name="c_content" id="c_content" cols="35" rows="7"></textarea>
    </p>

    <p class="form-help">
      Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.
    </p>

  </fieldset>
  
  <fieldset>
    <p class="buttons">
      <input type="submit" class="preview" name="preview" value="prévisualiser" />
    </p>
  </fieldset>

</form>

Depuis, on a eu le brouillon WebForms2, son polyfill JS webforms2.js, son implémentation native dans Firefox puis Safari, l'arrivée de Chrome et l'implémentation native dans MSIE. Donc j'ai mis type="email/url", maxlenght="255" et surtout required là où ils étaient parfaitement justifié. Vivent les validations côté client.

Ensuite, j'ai transformé le bouton <input type="submit" /> en un réel <button type="submit"></>, tout en sortant le texte signifiant de la valeur où il n'a rien à faire. Là aussi, les obstacles étaient levés depuis un temps.

Bref, il étant temps de moderniser ce bout de code.

Damnation et implémentation

Je vais essayer de le faire le plus possible sans une once de javascript. Cela peut paraître contradictoire pour un développeur, mais à mon sens,

  1. plus un code est simple, plus il est parfait ;
  2. émuler ce que l'on peut faire en natif fait perdre du temps ;
  3. être au plus proche des standards garanti la pérennité ;
Donc → Plus on revient vers un fonctionnement “naturel” du navigateur ou du serveur, plus on est sûr de la stabilité de ce que nous construisons.

Et surtout que l'Inspecteur Dirty Hacky ne me refasse pas le portrait
Dirty Hacky parses in .357 HTML

Le meilleur exemple que je puisse donner, c'est l'attribut placeholder="". Trop peu d'entre vous se souviennent du temps où l'on essayait de créer le comportement placeholder en javascript : Ça plantait, des fois ça merdait quand on cliquait pour mettre . Quand Apple a intégré placeholder dans le moteur Webkit pour l'application iTunes, on ne se doutait pas que nos calvaires allaient prendre fin.

Bref, il faut voir l'implémentation de ce design de formulaire comme un simple exercice, au même titre que je fais mes kata le matin avec mon kawa. Cette “futilité” doit vous rester en mémoire d'autant plus que depuis la première apparition de cette animation, il existe des bibliothèques javascript (!) qui feront ce boulot. Et qui feront dégainer Dirty Hacky, mais si vous pensez courir plus vite qu'un parseur .357 Magnum, vous pouvez tenter.

Premiers commits dans la Joie !

Toute animation sera en transitions CSS. Sur les plus petits éléments possibles avec le moins possible de repaint/reflow dans le viewport du navigateur afin de garder une belle fluidité d'animation. Parce que là aussi, l'optimisation et la beauté doit passer par l'optimisation des ressources clients.

Dans le mockup, les labels passent en bleu quand le champ a le focus. Une seule solution pour l'implémenter en CSS pur : dans le source html, renverser l'ordre les éléments <label></> et <input />.

Première mauvaise surprise : je ne connais aucun sélecteur css pour cibler un élement <input /> ou <textarea></> tant que son contenu est vide. La pseudo-classe :empty s'applique à la propriété DOM (el).innerHtml d'un élément, pas à sa valeur. Cibler en css l'attribut/valeur [value=""] ne sert à rien. J'ai dû le faire en JS. Bien évidemment, c'est juste une modification de classe, pilotant l'effet graphique en transitions css.

J'ai un autre souci : la pseudo-classe :invalid s'applique dès le chargement de page, hors je veux changer l'aspect que quand le champ a été changé.
Les pseudo-propriétés propriétaires :-moz-ui-invalid et :-moz-submit-invalid seraient parfaites, mais je veux que ça marche sur tous les navigateurs modernes d'un coup sans me prendre la tête et y revenir dessus dans 3 mois. N'oubliez jamais : les préfixes proprios, c'est pas pour mettre en production !

Donc, il me faut écrire un autre polyfill javascript qui mettra au formulaire la class modified. Ça tient en une dizaine de lignes, mais c'est frustrant.

var tagIfEmpty = '.field';
var markedNotEmpty = 'notEmpty';
var tagIfModified = 'form';
var markedModified = 'modified';

$(tagIfModified).on('change input',tagIfEmpty+' :input',function () {
	var $p = $(this).closest(tagIfEmpty);
	if (this.value!=='') {
		$p.addClass(markedNotEmpty);
	} else {
		$p.removeClass(markedNotEmpty);
	}
	$p.closest(tagIfModified).addClass(markedModified);
});

Oui, le code est ignoble. Et comble de l'insulte, comme j'utilise jQuery depuis un bail sur mon blog, je l'ai pas fait en javascript natif. Honte à Sapajou Hilare.

Bonus d'interface

Il m'a semblé cohérent de n'afficher le bouton d'envoi qu'une fois que le formulaire soit valide, donc quand les champs obligatoires soient remplis et les informations au bon format. Ce qui veut dire que si le site web n'est pas vide, mais que l'url n'est pas valide, le formulaire ne peut être soumis.

Il me manquait aussi un aspect visuel pour indiquer les champs invalides. Plutôt que mettre les champs en rouge, ce qui est trop agressif et impérieux, j'ai tenté avec un léger dégradé sur les champs invalides. C'est peut-être pas le meilleur choix puisque cela casse l'impression d'espace qu'amène les floating labels.

Un message d'information apparaît quand le focus est donné sur au <textarea></>. Ceux qui commentent régulièrement des blogs ont l'habitude d'avoir soit du BBCode, du Wiki, du MarkUp ou un jeu très restreint de balises HTML. J'estime que c'est inutile (j'en parlerais bientôt) et qu'écrire une adresse web a forcément comme intention de mettre un lien. C'était le comportement par défaut de Dotclear, que j'ai gardé.

Et enfin, quand le bouton de validation apparaît, un texte complémentaire indique si la publication est immédiate ou sujette à validation à priori.

Et voilà !

Vous pouvez tentez la maquette hébergée sur GitHub ou naviguer dans le commit de la version exposée pour ce billet, car je vous parie que l'an prochain, j'aurais complètement refait le vrai formulaire en bas. Ou voir la capture et le code dessous :

Cliquez pour voir le code. Attention : Sur ce code nouveau, il y a l'excuse de la nouveauté.
HTML :
<form action="#pr" method="post" id="comment-form">
  <h3>Ajouter un commentaire</h3>

  <fieldset id="comment-1stline">
    <p class="field">
      <input name="c_name" id="c_name" maxlength="255" required="required" type="text" placeholder="Nom ou pseudo&nbsp;:" />
      <label for="c_name">Nom ou pseudo&nbsp;:</label>
    </p>
    
    <p class="field" id="g_site">
      <input name="c_site" id="c_site" maxlength="255" type="url" placeholder="Site web (facultatif)&nbsp;:" />
      <label for="c_site">Site web (facultatif)&nbsp;:</label>
    </p>
  </fieldset>

  <fieldset id="comment-2ndline">
    <p class="field">
      <input name="c_mail" id="c_mail" maxlength="255" required="required" type="email" placeholder="Adresse e-mail&nbsp;:" />
      <label for="c_mail">Adresse e-mail&nbsp;:</label>
    </p>
    <!-- honeypot -->
  </fieldset>

  <fieldset id="comment-3rdline">
    <p class="field">
      <textarea name="c_content" id="c_content" required="required" placeholder="Commentaire&nbsp;:"></textarea>
      <label for="c_content">Commentaire&nbsp;:</label>
      <span class="form-help" id="form-help">Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.</span>
    </p>
  </fieldset>
  
  <fieldset id="comment-4thline">
    <p class="remember">
      <button class="preview" name="preview" id="preview" type="submit">prévisualiser</button>
      <label class="form-help" for="preview">(votre texte sera publié après validation)</label>
    </p>
  </fieldset>
</form>

CSS :
.field label {
  position: absolute;  
  top:0.8em;
  opacity : 0;
  transition-property: opacity top;
  transition-duration: 0.4s;  
}
.field.notEmpty label {
  top : 0;
  opacity : 1 ;
}

.field.notEmpty :active + label, .field.notEmpty :active + label {
  color : blue;
}

.field input, .field textarea, .field :active  {
  margin  : 0;
  outline : none;
  width : 95%;
  background : transparent;
  border : none;
  border-radius: 0px;
}

.field :invalid {
  border : none;
  box-shadow : none;
}

.modified .field :invalid {
  background-image: /* préfixes proprios */linear-gradient(top, #EEF 0%, #FFF 30%);
}

#comment-form p {
  position : relative; 
  margin : 0;
  padding : 1em 0 0;
  border-top : 1px solid #cce ;
}

#comment-1stline p  {
  border : none;
}

#comment-1stline #g_site {
  border-left : 1px solid #cce ;
}

@media only screen and (em>max-width:479px) {
  #comment-1stline #g_site {
    border-top : 1px solid #cce ;
    border-left : none;
  }
}

@media only screen and (min-width:480px) {
  #comment-1stline .field {
    float : left;
    width : 49%;
  }
}

#comment-4thline p {
  padding-top : 0.5em;
}

#comment-4thline, .modified:invalid #comment-4thlin {
  opacity : 0;
  border-top : 1px solid #cce ;
  transition-property : opacity;
  transition-duration : 0.4s;  
}

.modified #comment-4thline, .modified:valid #comment-4thline {
  opacity : 1;
}

#form-help {
  display : block;
  opacity: 0;
  transition-property: opacity;
  transition-duration: 0.4s;  
}

textarea:active ~ #form-help {
  opacity: 1;
}

#comment-form button {
  padding : 0.33em 0.66em;
  border: 1px solid rgb(104, 104, 103);
  color : rgb(238, 238, 238);
  font-size : 0.875em;
  font-weight : bold;
  text-align : center;
  text-shadow : 0px 1px 1px rgba(0, 0, 0, 0.3);
  box-shadow : 0px 1px 2px rgba(0, 0, 0, 0.2);
  background-image : /* préfixes proprios */linear-gradient(top , rgb(119, 119, 119) 0px, rgb(85, 85, 85) 100%);
}

Le débat reste ouvert

Brad Frost a sorti un excellent article critique : Il parle de soucis sur la sémantique, que j'ai tenté d'éviter autant que possible, mais surtout de problèmes d'utilisabilité. Tellement bien raisonné et empli de bon sens que j'avais des doutes à continuer. Notamment, elle oblige au placeholder de porter le même texte que le label, une redondance inutile. Seule la mise en production permet de trancher.

CSS Tricks propose une idée alternative qui enlève la redondance du texte de label, mais la version des labels flottants à droite réduit l'espace utile du champ d'entrée.

Mais surtout, j'ai besoin de votre avis. Vous, mes fidèles lecteurs, laissez un commentaire et dites-moi si vous préférez l'ancien au nouveau formulaire.