27904 sujets

CSS et mise en forme, CSS3

Bonjour à tous
Dans la page https://tests.osirisnet.net/e_centrale.htm il y a divers ensembles de "tuiles", par exemple celui-ci :
upload/1659971226-48769-list1.png

Les "tuiles" ont la même taille en largeur et en hauteur, les icônes sont alignées, etc
Si on affiche cette page sur un écran étroit ça prend l'aspect suivant
upload/1659971348-48769-list2.png

À l'époque où j'ai fait ça (il y a 5 ou 6 ans) après avoir discuté ce point sur ce forum, j'ai utilisé une <table> Smiley confused

<table>
  <tr>
    <td>
      <figure id="carte" class="tile">
        <img ...>
        <figcaption>...<figcaption>
      </figure>
    </td>
    <td>...</td>
  </tr>
  <tr>...</tr>
  ...
</table>

Si la taille de la page est modifiée par un changement d'orientation de l'écran ou autre, un script JavaScript reformate la table avec un ou deux <td> par <tr>.

Je voudrais remplacer la <table> par un

<ul class="tableList>
    <li><figure><img ...><figcaption>...<figcaption></figure></li>
    ...
</ul>

en utilisant display:flex sur <ul> et éventuellement <li> et <figure>, et supprimer le script en utilisant un media-query.

A priori ça ne semble pas insurmontable, mais je n'arrive pas à retrouver mon état actuel.

Pourriez vous m'aider à sortir de ce guêpier ?
Salut Papy,

J'allais te faire un CodePen mais j'en ai déjà un tout prêt : Grid layout vs flexbox

Tu as la solution avec Grid ou avec flex, ce qui te permet aussi de voir une petite différence de comportement entre les deux. C'est une réalisation pour 4 colonnes max au lieu de 2 max pour toi, mais il suffit d'enlever deux media queries dans chaque exemple. Si tu as un problème pour l'adapter n'hésites pas à le dire, je le ferais.

PS : c'est écrit avec Pug/stylus, pour avoir la version compilée directement dans le Pen en HTML/CSS il faut cliquer sur une icône du Pen (flèche bas) et choisir "View compiled HTML" (pour Pug) et "View compiled CSS" (pour Stylus).
Modifié par Olivier C (08 Aug 2022 - 18:21)
Et puis aller... je donne ici la compilation pour les futurs lecteurs du forum (au cas où je finis par détruire le Pen). Donc, avec en exigence pour le HTML, la classe sur le conteneur et des items en enfants directs.

Solution Flex :
.summaryFlex {
  display: flex;
  flex-wrap: wrap;
  gap: 1em;
}
.summaryFlex > * {
  flex: 1 0 100%;
}
@media (min-width: 36.01em) {
  .summaryFlex > * {
    flex-basis: calc(50% - 1em);
  }
}

Solution Grid :
.summaryGrid {
  display: grid;
  gap: 1em;
}
@media (min-width: 36.01em) {
  .summaryGrid {
    grid-template-columns: repeat(2, 1fr);
  }
}

Modifié par Olivier C (09 Aug 2022 - 00:22)
Merci beaucoup
Je vais essayer ça demain, mon PC est en sauvegarde pour la soirée
Modifié par PapyJP (08 Aug 2022 - 23:18)
Hélas, il ne suffit pas de mettre un ou deux éléments par ligne, la complexité du problème vient d'ailleurs.
Dans le css de la version avec <table> j'ai :

.tileList.index table {width:100%;}
.tileList.index td {width:50%;min-width:50%;max-width:50%;}
.tileList.index td:first-child {text-align:left;}
.tileList.index td:last-child {text-align:right;}
.tileList.index td .tile {width:98%;}
.tileList.index td:only-child {width:100%;min-width:100%;max-width:100%;}
.tile, .tileList.index td:only-child .tile {width:100%;}
.tileList.index td .tile img {max-width:100%;}

cela m'assure
- que la table fait bien toute la largeur de son contenant
- que les cellules ont une largeur de 50%
- que <figure class="tile"> fait 98% de la cellule, ce qui fait que les deux <figure> apparaissent cadrées l'une à droite, l'autre à gauche avec une gouttière homogène de 2 * 2% * 50% = 2% de la largeur de la ligne.

En flex, pour que la <figure> encadrée ne touche pas ses voisines, il me semble préférable de la mettre dans un conteneur intermédiaire, en l'occurrence je mets la liste sous la forme

<ul>
    <li><figure></li>
</ul>

Mais comment faire pour gérer la largeur des <li> pour qu'ils occupent toute la largeur du conteneur, qu'il y en ait un ou 2 ? La façon dont les largeurs sont prises en compte par flex n'est pas intuitive, le mécanisme flex ayant pour but de gérer lui-même les largeurs des items.

De même pour avoir l'image et le texte côte à côte, j'utilisais

figure.tile {
	display:inline-table;
	vertical-align:middle;
	margin:0.25em 0;
	padding:2px 0.25em;
	overflow:auto;
	border:1px solid #990000;
	border-radius:0.5em;
}
figure.tile img {
	float:left;
	display:inline;
	max-width:38%;
	margin:0 0.5em 0 0;
}
figure.tile figcaption {
	text-align:justify;
	margin-right:0.25em;
}

A l'intérieur des <td> ça donne le résultat souhaité.
Si je comprends bien, il serait de bon ton d'avoir

figure.tile {display:flex;}

mais si je fais ça je tombe sur le problème agaçant des tailles d'images dans un flex, je ne sais pas comment centrer le texte en hauteur, sauf à faire

figcaption {
    display: flex; 
    flex-direction:column; 
    justify-content: center;


Je m'y perds dans ces imbrications de flex !
Les imbrications de <table> n'étaient pas plus claires j'en convient, mais j'ai fait ça pendant une douzaine d'années.
Je vais faire une page simplifiée qui n'inclut pas tous les fichiers css du site pour essayer de démêler cet écheveau ...
PapyJP a écrit :
Hélas, il ne suffit pas de mettre un ou deux éléments par ligne, la complexité du problème vient d'ailleurs. [...] Mais comment faire pour gérer la largeur des &lt;li&gt; pour qu'ils occupent toute la largeur du conteneur, qu'il y en ait un ou 2 ? La façon dont les largeurs sont prises en compte par flex n'est pas intuitive, le mécanisme flex ayant pour but de gérer lui-même les largeurs des items.

La clef ici est flex-basis, avec calc(), solution présente dans le code flexbox que je t'ai proposée plus haut. Mais passons pour aborder un point plus fondamental...

Pour le front comme pour le back, lorsque l'on est confronté a une certaine complexité pour nous, il faut décomposer le problème (comme tu le sais déjà je n'en doute pas). Personnellement j'isole mes HTML/CSS comme des composants accomplissant chacun une tâche particulière. Les grilles que je t'ai filé accomplissent la tâche de passer d'une colonne en deux colonnes en fonction d'un breakpoint. Si ça marche en soit - et ça marche - il n'y a plus à y toucher.

Maintenant on peut s'intéresser aux items. Perso je partirais sur du html comme ceci (on pourrait laisser une liste mais sémantiquement je n'en vois pas l'intérêt, surtout qu'il faudra la formater en CSS) :
<div class="summaryFlex">
  <div>
      <figure id="carte" class="tile">
        <img ...>
        <figcaption>...<figcaption>
      </figure>
  </div>
</div>

Ensuite, il faut se débrouiller pour que cet item ".tile" soit autonome en soit. Ici on se moque de la grille ! ".tile" ne doit rendre et gérer que l'item. Je vois des pourcentages dans ton code, le problème a de grandes chances de venir de là. Mais c'est difficile à voir comme cela, au pire, fait un Pen, avec juste ce qu'il faut de code pour représenter l'item ".tile".

Une approche à la manière de composants... il n'y a que ça de vrais (et React n'a rien à voir là dedans).
Modifié par Olivier C (09 Aug 2022 - 15:50)
Avec pas mal de difficultés, je sis parvenu à quelque chose qui répond à tous les points sauf un: la gouttière entre les colonnes.
La page de tests : https://tests.osirisnet.net/tests1/index-list.html
Le CSS :

/* Mise à jour 09/08/2022 16:17:17*/
#container {
  width:100%;
  max-width: 1024px;
  margin:auto;
  border:1px solid gray;
  background:white;
}

.tileList > p {display: none;}
.tileList h2 {
  text-align: center;
  color:#900;
  margin-bottom:0.5em;
}
.index ul {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  font-size: 0.9em;
}
.index li {
  flex:0 0 50%;
  padding:0.5em 0;
}
figure.tile {
  display: flex;
  padding:0.5em;
  border:1px solid #900;
  border-radius: 0.5em;
  background: white;
}
.tile img {
  border-radius: 0.25em;
  max-width:4em;
}
.tile figcaption {
  padding-left:1em;
  text-align: center;
  flex-grow: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
figure.empty {
  visibility: hidden;
}

@media screen and (max-width:705px) {
  .index li {
    flex: 0 0 100%;
  }
}

L'image avec max-width:4em; donne un résultat apparemment satisfaisant, mais je constate que si le texte de <figcaption> est trop long, ce qui augmente la hauteur de la <figure>, l'image se déforme pour prendre la hauteur totale.
Cette histoire de déformation d'image est agaçante, car même si je parviens à la régler au coup par coup je n'arrive pas vraiment à comprendre ce qu'il faut faire. Mais c'est un autre sujet.

Une idée pour créer cette f... gouttière qui dans la version <table> fait 2% de la largeur du conteneur ?
Modifié par PapyJP (09 Aug 2022 - 16:32)
Voilà la solution toute cuite :
.index > ul {
  display: flex;
  flex-wrap: wrap;
  gap: 1em;
}
.index > ul > * {
  flex: 1 0 100%;
}
@media (min-width: 705px) {
  .index > ul > * {
    flex-basis: calc(50% - 1em);
  }
}

J'ai juste remplacé ma classe par une sélection adaptée à ton code, et remplacé mon breakpoint par le tiens (et c'est en mobile first avec min-width). Supprime le padding que tu as mis sur tes li (.index li) et ce sera ok.

Si tu change la taille de la gouttière tu devras faire de même pour la soustraction calc() dans flex-basis. Si tu ne veux pas avoir le même écart de gouttière en horizontal qu'en vertical il te faudra utiliser row-gap et column-gap en lieu et place de la propriété généraliste gap.
Modifié par Olivier C (09 Aug 2022 - 16:46)
Meilleure solution
Merci, ça marche comme je voulais !
Pour l'instant je constate que je comprends toujours rien au flex, malgré le bouquin de Raphaël qui est toujours ouvert sur mon bureau. Je me borne à appliquer des recettes, ce qui n'est pas une façon de progresser.
Il va falloir que je trouve un moyen de comprendre comment c'est fichu !
Je me suis étonné que le bouquin de Raphaël ne semble pas parler de "gap" (ou du moins je n'ai pas trouvé où)
En cherchant sur https://caniuse.com/flexbox-gap je vois que c'est une propriété qui n'est supportée que depuis un ou deux ans selon les navigateurs.
Pour ce site, nous avons essentiellement des utilisateurs spécialisés dans l'égyptologie qui ont souvent des PC ou Mac qui datent de longtemps et qui ne mettent pas systématiquement leur navigateur à jour. (C'est ce qui fait que je ne suis pas passé à ES6 pour ce site.)
Comment puis-je éviter d'utiliser gap ?
En largeur, ce n'est pas un problème, mais en hauteur je ne vois pas d'autre solution que mettre un <li> avec padding autour des <figure>.
Voyez vous une autre solution ?
Modifié par PapyJP (10 Aug 2022 - 13:16)
Là je suis au boulot, je t'en parle quand je rentrerai cet après-midi. Mais t'inquiètes, il y a une solution, un peu alambiquée mais toujours avec Flex.

___
EDIT :

Ceci fut ma première conception de grille (en flex) : Layout examples for Scriptura. Elle date un peu mais du coup est bien supportée. Le problème étant qu'elle fait appel à des marges négatives, c'est pour cela que je dis que c'est alambiqué...

Tu pourrais aussi faire ce que l'on appelle une "dégradation gracieuse" : on pourrait imaginer que ceux qui ne supportent pas les fonctions CSS récentes voient les items sur une seule colonne.

Dis-moi ta position avant que je rentre chez moi, m'évitant ainsi de me lancer dans une proposition de code inutile.
Modifié par Olivier C (10 Aug 2022 - 13:47)
Tiens, je suis rentré du boulot, essayes ça :
.index > ul {
  display: flex;
  flex-wrap: wrap;
  width: calc(100% + 1rem - 0.01px);
  margin-left: -1rem !important;
}
.index > ul li {
  width: 100%;
  margin-left: 1rem !important;
  padding: 0;
}
@media (min-width: 705px) {
  .index > ul li {
    width: calc(50% - 1rem - 0.01px);
  }
}

Et si tu veux vraiment une compatibilité max n'oublies pas les préfixes (mais il vaut mieux laisser faire Autoprefixer) :
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;

  -webkit-flex-wrap: wrap;
      -ms-flex-wrap: wrap;
          flex-wrap: wrap;

Modifié par Olivier C (10 Aug 2022 - 16:39)
Merci de ton message
Je suis actuellement en attente de consultation pour une visite semestrielle de contrôle. Je vais regarder ça

Ce qui m’intéressait c’était de supprimer les <li> et <il> et de mettre un <nav> avec directement les <figure> à l’intérieur.
Je crois que je vais garder les <li> et le padding, c’est beaucoup plus clair à la lecture, ce qui ne m’empêchera pas de tester ta solution pour ma formation à l’utilisation de flex.

Je commence à comprendre pourquoi j’ai du mal à utiliser cette techno alors que je saisis bien les concepts. C’est avant tout dû au fait qu’il faille utiliser d’autres noms de propriétés pour faire des choses apparemment simples qui s’écrivent autrement dans des bloc ou des tables.
C’est un peu comme apprendre un nouveau langage, et depuis 55 ans que je pratique l’informatique j’en ai appris au moins une cinquantaine. Ça ne fait qu’un de plus … Smiley cligne
Modifié par PapyJP (10 Aug 2022 - 16:55)
Olivier C a écrit :

Tu pourrais aussi faire ce que l'on appelle une "dégradation gracieuse" : on pourrait imaginer que ceux qui ne supportent pas les fonctions CSS récentes voient les items sur une seule colonne.

Je ne suis pas en train de faire quelque chose de nouveau, je suis en train de rénover la façon dont marchent des choses qui existent depuis des années. Faire cela serait une importante régression pour les dens qui sont le cœur de nos utilisateurs.
Olivier C a écrit :

Tu pourrais aussi faire ce que l'on appelle une "dégradation gracieuse" : on pourrait imaginer que ceux qui ne supportent pas les fonctions CSS récentes voient les items sur une seule colonne.

Je ne suis pas en train de faire quelque chose de nouveau, je suis en train de rénover la façon dont marchent des choses qui existent depuis des années. Faire cela serait une importante régression pour les dens qui sont le cœur de nos utilisateurs.
La dernière solution devait résoudre ton problème en liant le meilleur des deux mondes. Une solution en flex donc, compatible IE10+.
Modifié par Olivier C (11 Aug 2022 - 07:04)