Le principe de moindre pouvoir 🦄
C’est un des axiomes du web ,
proposés par Tim Berners-Lee en 1998
— à côté de kiss , du design
modulaire, de la tolérance ou de la décentralisation .
Le mantra de l’amélioration progressive , en somme…
Définition 📈
Un graphique de données est :
un ensemble de clés, 🔑
associées à une ou plusieurs valeurs, 💶
disposées sur une échelle, 📏
mises en forme afin d’en faciliter la compréhension. 🖌
Vous pourrez en apprendre plus sur
la représentation graphique de données sur Wikipédia .
Piqûre de rappel 💉
Le plus important, c’est le contenu — et par extension, sa sémantique.
Pour décrire un ensemble de clés et valeurs en html , il n’existe pas moult choix — en fait, deux :
les listes de définitions : <dl>
, <dt>
et <dd>
;
les tableaux de données , solution plébiscitée.
Et oui, ça sert à ça en vrai.
Un diagramme en barres 📊
<table style="--scale: 3000">
<caption>Temps de chargement pour ffoodd.fr</caption>
<thead>
<tr>
<td></td>
<th scope="col">Temps de chargement cumulé</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Trafic HTTP terminé</th>
<td style="--value: 2980">
<span>2980 ms</span>
</td>
</tr>
<tr>[…]</tr>
</tbody>
</table>
Le tableau nu 🔥
Temps de chargement pour ffoodd.fr
Temps de chargement cumulé
Temps : backend
4 ms
Temps : Frontend
96 ms
Délai : premier octet
102 ms
Délai : dernier octet
129 ms
Délai : première image
188 ms
Délai : premier CSS
194 ms
Délai : premier JS
326 ms
DOM Interactif
836 ms
Chargement du DOM
836 ms
DOM complet
2561 ms
Trafic HTTP terminé
2980 ms
Un peu d’habillage 🧦
@media screen and (min-width: 30em) {
table {
border-collapse: collapse;
border-spacing: 0;
caption-side: bottom;
contain: content;
empty-cells: hide;
font-feature-settings: "tnum";
}
th,
td {
border: 0;
padding: 0;
}
}
Le tableau en slip 🧜
Temps de chargement pour ffoodd.fr
Temps de chargement cumulé
Temps : backend
4 ms
Temps : Frontend
96 ms
Délai : premier octet
102 ms
Délai : dernier octet
129 ms
Délai : première image
188 ms
Délai : premier CSS
194 ms
Délai : premier JS
326 ms
DOM Interactif
836 ms
Chargement du DOM
836 ms
DOM complet
2561 ms
Trafic HTTP terminé
2980 ms
Un habillage structuré ▥
Basé sur un article de Miriam Suzanne sur CSS Tricks .
@supports (grid-template-columns: repeat(var(--scale, 100), minmax(0, 1fr))) {
tr {
display: grid;
grid-auto-rows: 1fr;
grid-row-gap: .5rem;
grid-template-columns:
minmax(min-content, 15em)
repeat(var(--scale, 100), minmax(0, 1fr))
10ch;
}
th {
grid-column: 1 / 1;
}
td {
grid-column: 2 / var(--value, 0);
}
}
Le tableau en jeans 👖
Temps de chargement pour ffoodd.fr
Temps de chargement cumulé
Temps : backend
4 ms
Temps : Frontend
96 ms
Délai : premier octet
102 ms
Délai : dernier octet
129 ms
Délai : première image
188 ms
Délai : premier CSS
194 ms
Délai : premier JS
326 ms
DOM Interactif
836 ms
Chargement du DOM
836 ms
DOM complet
2561 ms
Trafic HTTP terminé
2980 ms
Un détail 👓
td span {
background: inherit;
-webkit-text-fill-color: transparent;
-webkit-background-clip: text;
left: 100%;
position: absolute;
}
Le tableau décemment vêtu 👕
Temps de chargement pour ffoodd.fr
Temps de chargement cumulé
Temps : backend
4 ms
Temps : Frontend
96 ms
Délai : premier octet
102 ms
Délai : dernier octet
129 ms
Délai : première image
188 ms
Délai : premier CSS
194 ms
Délai : premier JS
326 ms
DOM Interactif
836 ms
Chargement du DOM
836 ms
DOM complet
2561 ms
Trafic HTTP terminé
2980 ms
Un peu de classe 💼
Combinant une technique
proposée par Trys Mudford et
les astuces de Taylor Hunt .
table {
--stripes: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E");
--zig: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E");
}
tr:nth-child(5n+5) {
--background: var(--stripes);
}
td {
background:
linear-gradient(to right, #3cb371, #444, #0000cd, #639, crimson)
calc(var(--value, 0) / var(--scale, 100) * 100%) 0% /
calc(var(--scale, 100) * 100%) 100%,
transparent var(--background) center / contain;
background-blend-mode: hard-light;
}
Le tableau bien sapé 👔
Temps de chargement pour ffoodd.fr
Temps de chargement cumulé
Temps : backend
4 ms
Temps : Frontend
96 ms
Délai : premier octet
102 ms
Délai : dernier octet
129 ms
Délai : première image
188 ms
Délai : premier CSS
194 ms
Délai : premier JS
326 ms
DOM Interactif
836 ms
Chargement du DOM
836 ms
DOM complet
2561 ms
Trafic HTTP terminé
2980 ms
On peaufine les détails 💅
table:hover tr {
opacity: .5;
}
table:hover tr:hover {
opacity: 1;
}
@media screen and (-ms-high-contrast: active) {
td {
background-image:
linear-gradient(to right, Window, ButtonFace, ButtonShadow, ButtonText, highlight),
var(--background);
}
}
Le tableau du samedi soir 🕺
Temps de chargement pour ffoodd.fr
Temps de chargement cumulé
Temps : backend
4 ms
Temps : Frontend
96 ms
Délai : premier octet
102 ms
Délai : dernier octet
129 ms
Délai : première image
188 ms
Délai : premier CSS
194 ms
Délai : premier JS
326 ms
DOM Interactif
836 ms
Chargement du DOM
836 ms
DOM complet
2561 ms
Trafic HTTP terminé
2980 ms
On fait des vagues 🌊
Dans le HTML
<table style="--scale: 3000; --1: 4; --2: 96; --3: 102; --4: 129; --5: 188;">
Dans le CSS
tr:nth-of-type(5) td {
grid-column: var(--4, 0) / var(--value, 0);
}
Le tableau du Niagara 🌄
Temps de chargement pour ffoodd.fr
Temps de chargement cumulé
Temps : backend
4 ms
Temps : Frontend
96 ms
Délai : premier octet
102 ms
Délai : dernier octet
129 ms
Délai : première image
188 ms
Délai : premier CSS
194 ms
Délai : premier JS
326 ms
DOM Interactif
836 ms
Chargement du DOM
836 ms
DOM complet
2561 ms
Trafic HTTP terminé
2980 ms
Un diagramme linéaire 🗠
<table style="--y: 32; --x: 13; --t-1: 'Jan.'; --t-2: 'Fév.'; […]">
<caption>Température mensuelle moyenne en 2017</caption>
<thead>
<tr>
<th scope="col">Année</th>
<th scope="col">Jan.</th>
<th scope="col">[…]</th>
</tr>
</thead>
<tbody>
<tr style="--year: '2017'; --1: 8; --2: 6; --3: 9; --4: 12; --5: 15; --6: 21; --7: 24; --8: 25; --9: 22; --10: 19; --11: 14; --12: 9;">
<th scope="row">2017</th>
<td>8 °C</td>
<td>[…]</td>
</tr>
</tbody>
</table>
Le tableau linéaire ⊟
Température mensuelle moyenne en 2017
Année
Jan.
Fév.
Mars
Avr.
Mai
Juin
Juil.
Août
Sep.
Oct.
Nov.
Déc.
2017
8 °C
6 °C
9 °C
12 °C
15 °C
21 °C
24 °C
25 °C
22 °C
19 °C
14 °C
9 °C
Une échelle 📏
table {
padding: calc(24em - 2rem) 0 1rem;
}
table::after {
--scale: calc( ( 100% - ( var(--y) * 1px) ) / var(--y) );
background-image: repeating-linear-gradient(
to bottom,
white,
white var(--scale),
rgba(0, 0, 0, .25) calc(var(--scale) + .25rem)
);
}
Température mensuelle moyenne en 2017
Année
Jan.
Fév.
Mars
Avr.
Mai
Juin
Juil.
Août
Sep.
Oct.
Nov.
Déc.
2017
8 °C
6 °C
9 °C
12 °C
15 °C
21 °C
24 °C
25 °C
22 °C
19 °C
14 °C
9 °C
Un tracé 🖉
@supports (clip-path: polygon(0% calc( 100% - ( var(--1) * 100% / var(--y))))) {
table {
--offset: calc((100% / var(--x)) / 2);
}
table [style]::before {
background: linear-gradient(to top, blue, red 75%);
clip-path: polygon(
0% 100%,
calc((100% / var(--x) * 1)) 100%,
calc((100% / var(--x) * 1)) calc(100% - (var(--1) / var(--y) * 100%)),
/* Premier point */
calc((100% / var(--x) * 1) + var(--offset)) calc(100% - (var(--1) / var(--y) * 100%)),
/* Deuxième point */
calc((100% / var(--x) * 2) + var(--offset)) calc(100% - (var(--2) / var(--y) * 100%)),
100% calc(100% - (var(--12) / var(--y) * 100%)),
100% 100%,
0% 100%
);
}
}
Température mensuelle moyenne en 2017
Année
Jan.
Fév.
Mars
Avr.
Mai
Juin
Juil.
Août
Sep.
Oct.
Nov.
Déc.
2017
8 °C
6 °C
9 °C
12 °C
15 °C
21 °C
24 °C
25 °C
22 °C
19 °C
14 °C
9 °C
Température mensuelle moyenne en 2017
Année
Jan.
Fév.
Mars
Avr.
Mai
Juin
Juil.
Août
Sep.
Oct.
Nov.
Déc.
2017
8 °C
6 °C
9 °C
12 °C
15 °C
21 °C
24 °C
25 °C
22 °C
19 °C
14 °C
9 °C
Température mensuelle moyenne en 2017
Année
Jan.
Fév.
Mars
Avr.
Mai
Juin
Juil.
Août
Sep.
Oct.
Nov.
Déc.
2017
8 °C
6 °C
9 °C
12 °C
15 °C
21 °C
24 °C
25 °C
22 °C
19 °C
14 °C
9 °C
Température mensuelle moyenne en 2017
Année
Jan.
Fév.
Mars
Avr.
Mai
Juin
Juil.
Août
Sep.
Oct.
Nov.
Déc.
2017
8 °C
6 °C
9 °C
12 °C
15 °C
21 °C
24 °C
25 °C
22 °C
19 °C
14 °C
9 °C
2018
10 °C
4 °C
7 °C
13 °C
17 °C
20 °C
22 °C
23 °C
26 °C
17 °C
14 °C
10 °C
Au radar 🔆
<table style="--scale: 20; --step: 5; --items: 7; --1: 14; --2: 11; --3: 13; --4: 16; --5: 10; --6: 12; --7: 4; --8: var(--1);">
<caption>Niveau d’intérêt par domaine, sur 20</caption>
<thead>
<tr>
<th scope="col">Accessibilité</th>
<th scope="col">Référencement</th>
</tr>
</thead>
<tbody>
<tr>
<td><span>14</span></td>
<td><span>11</span></td>
</tr>
</tbody>
</table>
Niveau d’intérêt par domaine, sur 20
Accessibilité
Référencement
Performance
Compatibilité
Sécurité
Qualité de code
Test
14
11
13
16
10
12
4
Étalonnage 🞋
table {
--radius: 10em;
--size: calc( var(--radius) / var(--scale) );
background-image:
repeating-radial-gradient(
circle at 50%, black, black 2px, white 0, white calc(var(--size) * var(--step))
),
repeating-radial-gradient(
circle at 50%, grey, grey 2px, white 0, white var(--size)
);
border-radius: 50%;
height: calc( var(--radius) * 2 );
width: calc( var(--radius) * 2 );
}
Niveau d’intérêt par domaine, sur 20
Accessibilité
Référencement
Performance
Compatibilité
Sécurité
Qualité de code
Test
14
11
13
16
10
12
4
Encerclement 💞
Basé sur une explication d’Ana Tudor .
table {
--part: calc( 360deg / var(--items) );
}
[scope="col"] {
--away: calc( (var(--radius) * -1) - 50% );
left: 50%;
position: absolute;
top: 50%;
transform:
translate3d(-50%, -50%, 0)
rotate( calc(var(--part) * var(--index, 1)) )
translate( var(--away) )
rotate( calc(var(--part) * var(--index, 1) * -1) );
}
Niveau d’intérêt par domaine, sur 20
Accessibilité
Référencement
Performance
Compatibilité
Sécurité
Qualité de code
Test
14
11
13
16
10
12
4
Placer les points ⚆
td:nth-of-type(3) span {
--pos: calc( 100% - (var(--4) * 100% / (var(--scale) * var(--ratio) ) ) );
background: linear-gradient( to top left, rebeccapurple 10%, indigo 75% );
clip-path: polygon(
100% var(--pos),
calc( 100% - ( var(--3) * 100% / var(--scale) ) ) 100%,
100% 100%
);
height: 100%;
position: absolute;
width: 100%;
}
Niveau d’intérêt par domaine, sur 20
Accessibilité
Référencement
Performance
Compatibilité
Sécurité
Qualité de code
Test
14
11
13
16
10
12
4
Distribution des parts 🍕
Détournement d’une technique publiée par Sara Soueidan sur Codrops .
td {
--skew: calc( 90deg - var(--part) );
border-bottom: 1px solid rebeccapurple;
transform:
rotate( calc(var(--part) * var(--index, 1)) )
skew( var(--skew) );
transform-origin: 100% 100%;
}
Niveau d’intérêt par domaine, sur 20
Accessibilité
Référencement
Performance
Compatibilité
Sécurité
Qualité de code
Test
14
11
13
16
10
12
4
Houston 📡
Illustration de la déformation du carré par la fonction skew()
Un peu de trigonométrie ⊾
Figurez-vous que Stereokai a implémenté plusieurs fonctions de trigonométrie
pour calculer les sinus , cosinus et tangente d’un angle.
td span {
--opposite: calc( 180 - (90 + (90 - (360 / var(--items)))) );
/* Trouver l’angle opposé, en radians */
--angle: calc( var(--opposite) * 0.01745329251 );
/* calc()uler le sinus, sorcellerie ! */
--sin-term1: var(--angle);
--sin-term2: calc((var(--angle) * var(--angle) * var(--angle)) / 6);
--sin-term3: calc((var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle)) / 120);
--sin-term4: calc((var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle)) / 5040);
--sin-term5: calc((var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle)) / 362880);
--sin: calc(var(--sin-term1) - var(--sin-term2) + var(--sin-term3) - var(--sin-term4) + var(--sin-term5));
/* calc()uler l’hypothénuse : largeur initiale / sinus de l’angle opposé */
--hypo: calc( var(--unitless-radius) / var(--sin) );
/* trouver le ratio : largeur après déformation / largeur initiale */
--ratio: calc( var(--hypo) / var(--unitless-radius) );
}
Niveau d’intérêt par domaine, sur 20
Accessibilité
Référencement
Performance
Compatibilité
Sécurité
Qualité de code
Test
14
11
13
16
10
12
4
Niveau d’intérêt par domaine, sur 20
Accessibilité
Référencement
Performance
Compatibilité
Sécurité
Qualité de code
Test
Gaël
14
11
13
16
14
10
4
Luc
18
10
11
16
10
12
11
Un diagramme en tarte 🥧
<table>
<caption>Répartition du poids des ressources pour ffoodd.fr</caption>
<thead class="sr-only">
<tr>
<th scope="col">Ressource</th>
<th scope="col">Proportion</th>
</tr>
</thead>
<tbody>
<tr style="--color: #734bf9; --term: 'HTML';">
<th scope="row">HTML</th>
<td style="--value: 2; --start: 0; ">2 %</td>
</tr>
<tr>[…]</tr>
</tbody>
</table>
Répartition du poids des ressources pour ffoodd.fr
Ressource
Proportion
HTML
2 %
CSS
2 %
JS
32 %
Json
1 %
Images
44 %
Webfonts
17 %
Autres
2 %
Devenir légende 🧞
tbody {
display: table-row;
}
tbody tr {
display: table-cell;
}
tbody [scope="row"]::before {
background: var(--color, currentColor) var(--background);
content: "";
display: inline-block;
height: 1rem;
transform: translate3d(-.2rem, .1rem, 0);
width: 1rem;
}
Répartition du poids des ressources pour ffoodd.fr
Ressource
Proportion
HTML
2 %
CSS
2 %
JS
32 %
Json
1 %
Images
44 %
Webfonts
17 %
Autres
2 %
Devenir tarte 🥧
Valeur entre 25 et 50%
Valeur entre 50 et 75%
Valeur entre 75 et 100%
Devenir tarte 🥧
On réitère nos calculs de trigo précédents, et on ajoute une petite couche de complexité avec
des variables pseudo-booléennes, sur une idée de
Roman Komarov .
td::before {
clip-path: polygon(
50% 50%,
50% 0%,
100% 0%,
calc(50% + (var(--pos-B) * 1% * var(--lt-25, 1)) + (var(--gt-25, 0) * 50%)) calc(50% - (var(--pos-A) * 1% * var(--lt-25, 1))),
calc(50% + (var(--gt-25, 0) * 50% )) calc(50% + (var(--gt-25, 0) * 50%)),
calc(50% + (var(--pos-A) * 1% * var(--lt-50, 1)) + (var(--gt-50, 0) * 50%)) calc(50% + (var(--pos-B) * 1% * var(--lt-50, 1)) + (var(--gt-50, 0) * 50%)),
calc(50% - (var(--gt-50, 0) * 50% )) calc(50% + (var(--gt-50, 0) * 50%)),
calc(50% - (var(--pos-B) * 1% * var(--lt-75, 1)) - (var(--gt-75, 0) * 50%)) calc(50% + (var(--pos-A) * 1% * var(--lt-75, 1))),
calc(50% - (var(--gt-75, 0) * 50% )) calc(50% - (var(--gt-75, 0) * 50%)),
calc(50% - (var(--pos-A) * 1% * var(--gt-75, 0))) calc(50% - (var(--pos-B) * 1% * var(--gt-75, 0))),
50% 50%
);
}
Répartition du poids des ressources pour ffoodd.fr
Ressource
Proportion
HTML
2 %
CSS
2 %
JS
32 %
Json
1 %
Images
44 %
Webfonts
17 %
Autres
2 %
Devenir doughnut 🍩
Inspiré par les expérimentations d’Ana Tudor avec mask-*
sur CodePen .
@supports (mask: var(--mask)) {
table {
mask-image: radial-gradient(
circle at 50% calc(50% - 2.5rem),
transparent 0%,
transparent var(--offset),
white calc(var(--offset) + 1px),
white 100%
);
}
}
Répartition du poids des ressources pour ffoodd.fr
Ressource
Proportion
HTML
2 %
CSS
2 %
JS
32 %
Json
1 %
Images
44 %
Webfonts
17 %
Autres
2 %
Devenir polaire ☃
td::before {
--zoom: 50;
transform:
translate3d(-50%, -50%, 0)
rotate( var(--position) )
scale( calc((var(--zoom) + (var(--value) / (100 / var(--zoom)))) / 100) );
}
Répartition du poids des ressources pour ffoodd.fr
Ressource
Proportion
HTML
2 %
CSS
2 %
JS
32 %
Json
1 %
Images
44 %
Webfonts
17 %
Autres
2 %
Interrupteur de styles ☑
Adrian Roselli a découvert que
tripatouiller le display
des tableaux abîme sa sémantique exposée
— c’est pourquoi j’implémente le composant inclusif du toggle button conçu par Heydon Pickering pour désactiver les styles à volonté.
document.addEventListener( "DOMContentLoaded", function () {
var switches = document.querySelectorAll( '[role="switch"]' );
Array.prototype.forEach.call( switches, function( el, i ) {
el.addEventListener( 'click', function() {
var checked = this.getAttribute( 'aria-checked' ) === 'true' || false;
this.setAttribute( 'aria-checked', !checked );
var chart = this.parentNode.nextElementSibling;
chart.classList.toggle( 'table-charts' );
});
});
});
Commutateur
Permet de désactiver les styles sur le tableau suivant.
Actifs
Inactifs
Temps de chargement pour ffoodd.fr
Temps de chargement cumulé
Temps : backend
4 ms
Temps : Frontend
96 ms
Délai : premier octet
102 ms
Délai : dernier octet
129 ms
Délai : première image
188 ms
Délai : premier CSS
194 ms
Délai : premier JS
326 ms
DOM Interactif
836 ms
Chargement du DOM
836 ms
DOM complet
2561 ms
Trafic HTTP terminé
2980 ms
Explorez 🚀
Les pistes ne manquent pas :
les propriétés all
— avec les valeurs initial
, inherit
mais surtout revert
—
et contain
;
scroll-snap
pour des diagrammes « déroulables » ;
attr()
et counter()
avec un typage faible et la possibilité de s’en servir ailleurs que dans la propriété content
;
les shapes
, regions
et exclusions
pour explorer
d’autres types de graphiques ;
Houdini , un ensemble de spécifications du futur — jetez donc un œil aux
essais de Vincent De Oliveira ;
et probablement beaucoup d’autres idées et techniques à découvrir…
Conclusion
Les avantages sont multiples — tout comme les inconvénients :
pas de JavaScript pour l’aspect graphique ;
mais JavaScript est requis pour l’accessibilité ;
le balisage est libre et maîtrisable ;
rwd ,
whcm et autres préférences utilisateurs sont gérables grâce à CSS ;
HTML et CSS sont statiques ;
mais il faut travailler en amélioration progressive .
Learning is fun 💃
Merci
Et à bientôt ☺
Crédits