Encore une idée à la noix, mais l'élément <data> mérite qu'on lui consacre cinq minutes et quelques.
Normalement, à la fin de cet article, vous allez l'adopter.
Ça fait quoi ?
La spec est claire : <data> associe un contenu lisible par un humain à une valeur interprétable par une machine, portée par l'attribut value.
<data value="978-2-07-036024-5">L'Étranger</data>
<data value="42">quarante-deux</data>
Ce qui différencie <data> de ses voisins sémantiques :
<time> est réservé aux dates et durées, avec son attribut datetime. <abbr> gère les abréviations avec title. <meter> représente une mesure scalaire. Et <data> ? Il est le généraliste, tout ce qui a une valeur machine et un libellé humain, sans être une date.
En pratique, on le croise, des fois, pour des identifiants produits (EAN, SKU), des codes de statut, des valeurs numériques brutes à traiter en JavaScript. Ou, comme on va le voir, des prix.
Les devises en front
Afficher un prix dans différentes devises, c'est classique.
Le truc le plus pénible est le positionnement du symbole, avant pour le dollar, après pour l'euro, avant pour la livre, après pour le zloty. Et ça pour plus de 150 monnaies.
Tout ça pour afficher 12,99 € au lieu de $12.99.
data et CSS : le duo rigolo
L'idée : stocker la valeur brute dans value, laisser le <data> vide dans le HTML, et laisser le CSS générer l'affichage complet via ::after. La devise active est portée par un attribut data-currency sur :root, un seul point de contrôle pour toute la page. Ce sera côté back que l'attribut sera géré, une seule fois.
Par conséquent, ça permet d'avoir une page dans une langue et les devises dans une autre. Et hop !
Exemple de HTML
<html data-currency="USD">
…
<tbody class="prices"> <!-- classe prices nécessaire -->
<tr>
<td>Licence Solo</td>
<td><data value="9.99"></data></td>
</tr>
</tbody>
…
Styles de base
Taille brute : 13,77 kilo-octets (~1,38 kilo-octets gzip)
/* ==========================================================================
Currencies — ISO 4217
Usage : <data value="1 200.40"></data> dans un conteneur parent .prices
Activation : attribut data-currency sur :root
========================================================================== */
:root {
/* Fallback EUR — data-currency absent ou vide */
&:not([data-currency]), &[data-currency=""] {
.prices data::after {
content: attr(value) "\A0 €";
}
}
/* Symbole avant la valeur — sans espace */
&[data-currency="ANG"] .prices data::after {
content: "ƒ" attr(value);
}
&[data-currency="ARS"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="AUD"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="AWG"] .prices data::after {
content: "ƒ" attr(value);
}
&[data-currency="AZN"] .prices data::after {
content: "₼" attr(value);
}
&[data-currency="BBD"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="BDT"] .prices data::after {
content: "৳" attr(value);
}
&[data-currency="BMD"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="BND"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="BOB"] .prices data::after {
content: "Bs." attr(value);
}
&[data-currency="BRL"] .prices data::after {
content: "R$" attr(value);
}
&[data-currency="BSD"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="BTN"] .prices data::after {
content: "₹" attr(value);
}
&[data-currency="BZD"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="CAD"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="CLP"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="CNY"] .prices data::after {
content: "¥" attr(value);
}
&[data-currency="COP"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="CRC"] .prices data::after {
content: "₡" attr(value);
}
&[data-currency="CUP"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="CVE"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="DOP"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="EGP"] .prices data::after {
content: "£" attr(value);
}
&[data-currency="ERN"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="ETB"] .prices data::after {
content: "Br" attr(value);
}
&[data-currency="FJD"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="FKP"] .prices data::after {
content: "£" attr(value);
}
&[data-currency="GBP"] .prices data::after {
content: "£" attr(value);
}
&[data-currency="GHS"] .prices data::after {
content: "₵" attr(value);
}
&[data-currency="GIP"] .prices data::after {
content: "£" attr(value);
}
&[data-currency="GTQ"] .prices data::after {
content: "Q" attr(value);
}
&[data-currency="GYD"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="HKD"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="IDR"] .prices data::after {
content: "Rp" attr(value);
}
&[data-currency="ILS"] .prices data::after {
content: "₪" attr(value);
}
&[data-currency="INR"] .prices data::after {
content: "₹" attr(value);
}
&[data-currency="IRR"] .prices data::after {
content: "﷼" attr(value);
}
&[data-currency="JMD"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="JPY"] .prices data::after {
content: "¥" attr(value);
}
&[data-currency="KPW"] .prices data::after {
content: "₩" attr(value);
}
&[data-currency="KRW"] .prices data::after {
content: "₩" attr(value);
}
&[data-currency="KYD"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="KZT"] .prices data::after {
content: "₸" attr(value);
}
&[data-currency="LKR"] .prices data::after {
content: "₨" attr(value);
}
&[data-currency="LRD"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="MGA"] .prices data::after {
content: "Ar" attr(value);
}
&[data-currency="MMK"] .prices data::after {
content: "K" attr(value);
}
&[data-currency="MNT"] .prices data::after {
content: "₮" attr(value);
}
&[data-currency="MOP"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="MUR"] .prices data::after {
content: "₨" attr(value);
}
&[data-currency="MXN"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="MYR"] .prices data::after {
content: "RM" attr(value);
}
&[data-currency="MZN"] .prices data::after {
content: "MT" attr(value);
}
&[data-currency="NAD"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="NGN"] .prices data::after {
content: "₦" attr(value);
}
&[data-currency="NIO"] .prices data::after {
content: "C$" attr(value);
}
&[data-currency="NPR"] .prices data::after {
content: "₨" attr(value);
}
&[data-currency="NZD"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="PAB"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="PEN"] .prices data::after {
content: "S/." attr(value);
}
&[data-currency="PGK"] .prices data::after {
content: "K" attr(value);
}
&[data-currency="PHP"] .prices data::after {
content: "₱" attr(value);
}
&[data-currency="PKR"] .prices data::after {
content: "₨" attr(value);
}
&[data-currency="PYG"] .prices data::after {
content: "₲" attr(value);
}
&[data-currency="SAR"] .prices data::after {
content: "﷼" attr(value);
}
&[data-currency="SBD"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="SCR"] .prices data::after {
content: "₨" attr(value);
}
&[data-currency="SGD"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="SHP"] .prices data::after {
content: "£" attr(value);
}
&[data-currency="SLL"] .prices data::after {
content: "Le" attr(value);
}
&[data-currency="SRD"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="SSP"] .prices data::after {
content: "£" attr(value);
}
&[data-currency="STN"] .prices data::after {
content: "Db" attr(value);
}
&[data-currency="SYP"] .prices data::after {
content: "£" attr(value);
}
&[data-currency="THB"] .prices data::after {
content: "฿" attr(value);
}
&[data-currency="TOP"] .prices data::after {
content: "T$" attr(value);
}
&[data-currency="TRY"] .prices data::after {
content: "₺" attr(value);
}
&[data-currency="TTD"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="TWD"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="UAH"] .prices data::after {
content: "₴" attr(value);
}
&[data-currency="USD"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="UYU"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="VES"] .prices data::after {
content: "Bs." attr(value);
}
&[data-currency="WST"] .prices data::after {
content: "T" attr(value);
}
&[data-currency="XCD"] .prices data::after {
content: "$" attr(value);
}
&[data-currency="YER"] .prices data::after {
content: "﷼" attr(value);
}
&[data-currency="ZAR"] .prices data::after {
content: "R" attr(value);
}
&[data-currency="ZWL"] .prices data::after {
content: "$" attr(value);
}
/* Symbole avant la valeur — avec espace insécable */
&[data-currency="BIF"] .prices data::after {
content: "Fr" attr(value);
}
&[data-currency="BWP"] .prices data::after {
content: "P" attr(value);
}
&[data-currency="CDF"] .prices data::after {
content: "Fr" attr(value);
}
&[data-currency="CHF"] .prices data::after {
content: "Fr." attr(value);
}
&[data-currency="DJF"] .prices data::after {
content: "Fr" attr(value);
}
&[data-currency="GNF"] .prices data::after {
content: "Fr" attr(value);
}
&[data-currency="HNL"] .prices data::after {
content: "L" attr(value);
}
&[data-currency="HTG"] .prices data::after {
content: "G" attr(value);
}
&[data-currency="KES"] .prices data::after {
content: "Sh" attr(value);
}
&[data-currency="KMF"] .prices data::after {
content: "Fr" attr(value);
}
&[data-currency="LSL"] .prices data::after {
content: "R" attr(value);
}
&[data-currency="MWK"] .prices data::after {
content: "MK" attr(value);
}
&[data-currency="RWF"] .prices data::after {
content: "Fr" attr(value);
}
&[data-currency="SOS"] .prices data::after {
content: "Sh" attr(value);
}
&[data-currency="SZL"] .prices data::after {
content: "R" attr(value);
}
&[data-currency="TZS"] .prices data::after {
content: "Sh" attr(value);
}
&[data-currency="UGX"] .prices data::after {
content: "Sh" attr(value);
}
&[data-currency="XAF"] .prices data::after {
content: "Fr" attr(value);
}
&[data-currency="XOF"] .prices data::after {
content: "Fr" attr(value);
}
&[data-currency="XPF"] .prices data::after {
content: "Fr" attr(value);
}
&[data-currency="ZMW"] .prices data::after {
content: "K" attr(value);
}
/* Symbole après la valeur — avec espace insécable */
&[data-currency="AED"] .prices data::after {
content: attr(value) "\A0 د.إ";
}
&[data-currency="AFN"] .prices data::after {
content: attr(value) "\A0 ؋";
}
&[data-currency="ALL"] .prices data::after {
content: attr(value) "\A0 L";
}
&[data-currency="AMD"] .prices data::after {
content: attr(value) "\A0 ֏";
}
&[data-currency="AOA"] .prices data::after {
content: attr(value) "\A0 Kz";
}
&[data-currency="BAM"] .prices data::after {
content: attr(value) "\A0 KM";
}
&[data-currency="BGN"] .prices data::after {
content: attr(value) "\A0 лв.";
}
&[data-currency="BHD"] .prices data::after {
content: attr(value) "\A0 .د.ب";
}
&[data-currency="BYN"] .prices data::after {
content: attr(value) "\A0 Br";
}
&[data-currency="CZK"] .prices data::after {
content: attr(value) "\A0 Kč";
}
&[data-currency="DKK"] .prices data::after {
content: attr(value) "\A0 kr";
}
&[data-currency="DZD"] .prices data::after {
content: attr(value) "\A0 د.ج";
}
&[data-currency="EUR"] .prices data::after {
content: attr(value) "\A0 €";
}
&[data-currency="GEL"] .prices data::after {
content: attr(value) "\A0 ₾";
}
&[data-currency="GMD"] .prices data::after {
content: attr(value) "\A0 D";
}
&[data-currency="HRK"] .prices data::after {
content: attr(value) "\A0 kn";
}
&[data-currency="HUF"] .prices data::after {
content: attr(value) "\A0 Ft";
}
&[data-currency="IQD"] .prices data::after {
content: attr(value) "\A0 ع.د";
}
&[data-currency="ISK"] .prices data::after {
content: attr(value) "\A0 kr";
}
&[data-currency="JOD"] .prices data::after {
content: attr(value) "\A0 د.ا";
}
&[data-currency="KGS"] .prices data::after {
content: attr(value) "\A0 с";
}
&[data-currency="KHR"] .prices data::after {
content: attr(value) "\A0 ៛";
}
&[data-currency="KWD"] .prices data::after {
content: attr(value) "\A0 د.ك";
}
&[data-currency="LAK"] .prices data::after {
content: attr(value) "\A0 ₭";
}
&[data-currency="LBP"] .prices data::after {
content: attr(value) "\A0 ل.ل";
}
&[data-currency="LYD"] .prices data::after {
content: attr(value) "\A0 ل.د";
}
&[data-currency="MAD"] .prices data::after {
content: attr(value) "\A0 د.م.";
}
&[data-currency="MDL"] .prices data::after {
content: attr(value) "\A0 L";
}
&[data-currency="MKD"] .prices data::after {
content: attr(value) "\A0 ден";
}
&[data-currency="MRU"] .prices data::after {
content: attr(value) "\A0 UM";
}
&[data-currency="MVR"] .prices data::after {
content: attr(value) "\A0 .ރ";
}
&[data-currency="NOK"] .prices data::after {
content: attr(value) "\A0 kr";
}
&[data-currency="OMR"] .prices data::after {
content: attr(value) "\A0 ر.ع.";
}
&[data-currency="PLN"] .prices data::after {
content: attr(value) "\A0 zł";
}
&[data-currency="QAR"] .prices data::after {
content: attr(value) "\A0 ر.ق";
}
&[data-currency="RON"] .prices data::after {
content: attr(value) "\A0 lei";
}
&[data-currency="RSD"] .prices data::after {
content: attr(value) "\A0 дин.";
}
&[data-currency="RUB"] .prices data::after {
content: attr(value) "\A0 ₽";
}
&[data-currency="SDG"] .prices data::after {
content: attr(value) "\A0 ج.س.";
}
&[data-currency="SEK"] .prices data::after {
content: attr(value) "\A0 kr";
}
&[data-currency="TJS"] .prices data::after {
content: attr(value) "\A0 с.";
}
&[data-currency="TMT"] .prices data::after {
content: attr(value) "\A0 m.";
}
&[data-currency="TND"] .prices data::after {
content: attr(value) "\A0 د.ت";
}
&[data-currency="UZS"] .prices data::after {
content: attr(value) "\A0 Sʻ";
}
&[data-currency="VND"] .prices data::after {
content: attr(value) "\A0 ₫";
}
&[data-currency="VUV"] .prices data::after {
content: attr(value) "\A0 Vt";
}
}
Télécharger kortic-currencies.css
Concrètement, <p class="prices">Prix TTC <data value="9.99"></data></p> affiche un truc comme ça…
Prix TTC
C'est tout.
L'i18n
Offerte avec la commande
Le positionnement du symbole varie selon les conventions locales. Le dollar se met avant ($12.99), l'euro après (12,99 €), le zloty après (12,99 zł), le yen avant (¥1299). Ce n'est pas une question de goût, c'est une convention typographique que les utilisateurs remarquent quand elle est mal gérée, et qui fait désordre sur une page de tarifs.
Ces conventions sont définies par la norme CLDR (Common Locale Data Repository), le référentiel Unicode des données de localisation. Elle recense pour chaque devise son symbole, sa position, et ses règles de formatage par locale.
Les codes de devises eux-mêmes sont définis par la norme ISO 4217, qui attribue un code alphabétique à trois lettres à chaque devise mondiale.
Avec ce pattern, la règle CSS encode directement la convention CLDR de chaque devise. On génère une fois une feuille de style complète, 155 devises actives, symbole avant ou après selon la norme, et on n'y touche plus jamais. Changer la devise affichée revient à modifier un seul attribut sur :root. Le CSS fait le reste, sans toucher au DOM des prix.
Et l'accessibilité ?
La balise <data> n'a pas de rôle ARIA implicite, et l'attribut value est invisible aux technologies d'assistance. Ce qui est restitué, c'est le contenu textuel de l'élément, ici vide, donc le contenu généré par ::after.
Les pseudo-éléments CSS étant restitués par les lecteurs d'écran, c'est transparent.
Donc, visuellement et avec un lecteur d'écran, tout fonctionne. Youpi.
Bonus
Compliquer la vie des scrapers de prix.
Le contenu textuel d'un <data> vide étant généré par CSS, un scraper qui parse le HTML statique sans exécuter le CSS voit… ah ben rien. La valeur brute reste dans value, mais le prix affiché n'existe pas dans le DOM textuel.
On peut pousser un cran plus loin en encodant la valeur en base64 côté serveur, et en la décodant par JS dans un data-out que le CSS utilise à la place de value :
<data value="<?= base64_encode('9.99') ?>"></data>
Deux lignes et demie de JavaScript pour décoder.
/* Décode les valeurs base64 au chargement */
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.prices--atob data[value]').forEach(el => el.dataset.out = atob(el.getAttribute('value')));
});
Dans ce cas là, la CSS doit être adaptée pour lire le bon attribut.
.prices--atob data::after { content: "$\a0" attr(data-out); }
Styles avec atob()
Taille brute : 15,22 kilo-octets (~1,44 kilo-octets gzip)
/* ==========================================================================
Currencies — ISO 4217
Usage : <data value="[base64]"></data> dans un conteneur parent .prices
Activation : attribut data-currency sur :root
attr(data-out) = valeur décodée injectée par JS
========================================================================== */
:root {
/* Fallback EUR — data-currency absent ou vide */
&:not([data-currency]), &[data-currency=""] {
.prices--atob data::after {
content: attr(data-out) "\A0 €";
}
}
/* Symbole avant la valeur — sans espace */
&[data-currency="ANG"] .prices--atob data::after {
content: "ƒ" attr(data-out);
}
&[data-currency="ARS"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="AUD"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="AWG"] .prices--atob data::after {
content: "ƒ" attr(data-out);
}
&[data-currency="AZN"] .prices--atob data::after {
content: "₼" attr(data-out);
}
&[data-currency="BBD"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="BDT"] .prices--atob data::after {
content: "৳" attr(data-out);
}
&[data-currency="BMD"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="BND"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="BOB"] .prices--atob data::after {
content: "Bs." attr(data-out);
}
&[data-currency="BRL"] .prices--atob data::after {
content: "R$" attr(data-out);
}
&[data-currency="BSD"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="BTN"] .prices--atob data::after {
content: "₹" attr(data-out);
}
&[data-currency="BZD"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="CAD"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="CLP"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="CNY"] .prices--atob data::after {
content: "¥" attr(data-out);
}
&[data-currency="COP"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="CRC"] .prices--atob data::after {
content: "₡" attr(data-out);
}
&[data-currency="CUP"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="CVE"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="DOP"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="EGP"] .prices--atob data::after {
content: "£" attr(data-out);
}
&[data-currency="ERN"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="ETB"] .prices--atob data::after {
content: "Br" attr(data-out);
}
&[data-currency="FJD"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="FKP"] .prices--atob data::after {
content: "£" attr(data-out);
}
&[data-currency="GBP"] .prices--atob data::after {
content: "£" attr(data-out);
}
&[data-currency="GHS"] .prices--atob data::after {
content: "₵" attr(data-out);
}
&[data-currency="GIP"] .prices--atob data::after {
content: "£" attr(data-out);
}
&[data-currency="GTQ"] .prices--atob data::after {
content: "Q" attr(data-out);
}
&[data-currency="GYD"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="HKD"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="IDR"] .prices--atob data::after {
content: "Rp" attr(data-out);
}
&[data-currency="ILS"] .prices--atob data::after {
content: "₪" attr(data-out);
}
&[data-currency="INR"] .prices--atob data::after {
content: "₹" attr(data-out);
}
&[data-currency="IRR"] .prices--atob data::after {
content: "﷼" attr(data-out);
}
&[data-currency="JMD"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="JPY"] .prices--atob data::after {
content: "¥" attr(data-out);
}
&[data-currency="KPW"] .prices--atob data::after {
content: "₩" attr(data-out);
}
&[data-currency="KRW"] .prices--atob data::after {
content: "₩" attr(data-out);
}
&[data-currency="KYD"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="KZT"] .prices--atob data::after {
content: "₸" attr(data-out);
}
&[data-currency="LKR"] .prices--atob data::after {
content: "₨" attr(data-out);
}
&[data-currency="LRD"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="MGA"] .prices--atob data::after {
content: "Ar" attr(data-out);
}
&[data-currency="MMK"] .prices--atob data::after {
content: "K" attr(data-out);
}
&[data-currency="MNT"] .prices--atob data::after {
content: "₮" attr(data-out);
}
&[data-currency="MOP"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="MUR"] .prices--atob data::after {
content: "₨" attr(data-out);
}
&[data-currency="MXN"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="MYR"] .prices--atob data::after {
content: "RM" attr(data-out);
}
&[data-currency="MZN"] .prices--atob data::after {
content: "MT" attr(data-out);
}
&[data-currency="NAD"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="NGN"] .prices--atob data::after {
content: "₦" attr(data-out);
}
&[data-currency="NIO"] .prices--atob data::after {
content: "C$" attr(data-out);
}
&[data-currency="NPR"] .prices--atob data::after {
content: "₨" attr(data-out);
}
&[data-currency="NZD"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="PAB"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="PEN"] .prices--atob data::after {
content: "S/." attr(data-out);
}
&[data-currency="PGK"] .prices--atob data::after {
content: "K" attr(data-out);
}
&[data-currency="PHP"] .prices--atob data::after {
content: "₱" attr(data-out);
}
&[data-currency="PKR"] .prices--atob data::after {
content: "₨" attr(data-out);
}
&[data-currency="PYG"] .prices--atob data::after {
content: "₲" attr(data-out);
}
&[data-currency="SAR"] .prices--atob data::after {
content: "﷼" attr(data-out);
}
&[data-currency="SBD"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="SCR"] .prices--atob data::after {
content: "₨" attr(data-out);
}
&[data-currency="SGD"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="SHP"] .prices--atob data::after {
content: "£" attr(data-out);
}
&[data-currency="SLL"] .prices--atob data::after {
content: "Le" attr(data-out);
}
&[data-currency="SRD"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="SSP"] .prices--atob data::after {
content: "£" attr(data-out);
}
&[data-currency="STN"] .prices--atob data::after {
content: "Db" attr(data-out);
}
&[data-currency="SYP"] .prices--atob data::after {
content: "£" attr(data-out);
}
&[data-currency="THB"] .prices--atob data::after {
content: "฿" attr(data-out);
}
&[data-currency="TOP"] .prices--atob data::after {
content: "T$" attr(data-out);
}
&[data-currency="TRY"] .prices--atob data::after {
content: "₺" attr(data-out);
}
&[data-currency="TTD"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="TWD"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="UAH"] .prices--atob data::after {
content: "₴" attr(data-out);
}
&[data-currency="USD"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="UYU"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="VES"] .prices--atob data::after {
content: "Bs." attr(data-out);
}
&[data-currency="WST"] .prices--atob data::after {
content: "T" attr(data-out);
}
&[data-currency="XCD"] .prices--atob data::after {
content: "$" attr(data-out);
}
&[data-currency="YER"] .prices--atob data::after {
content: "﷼" attr(data-out);
}
&[data-currency="ZAR"] .prices--atob data::after {
content: "R" attr(data-out);
}
&[data-currency="ZWL"] .prices--atob data::after {
content: "$" attr(data-out);
}
/* Symbole avant la valeur — avec espace insécable */
&[data-currency="BIF"] .prices--atob data::after {
content: "Fr" attr(data-out);
}
&[data-currency="BWP"] .prices--atob data::after {
content: "P" attr(data-out);
}
&[data-currency="CDF"] .prices--atob data::after {
content: "Fr" attr(data-out);
}
&[data-currency="CHF"] .prices--atob data::after {
content: "Fr." attr(data-out);
}
&[data-currency="DJF"] .prices--atob data::after {
content: "Fr" attr(data-out);
}
&[data-currency="GNF"] .prices--atob data::after {
content: "Fr" attr(data-out);
}
&[data-currency="HNL"] .prices--atob data::after {
content: "L" attr(data-out);
}
&[data-currency="HTG"] .prices--atob data::after {
content: "G" attr(data-out);
}
&[data-currency="KES"] .prices--atob data::after {
content: "Sh" attr(data-out);
}
&[data-currency="KMF"] .prices--atob data::after {
content: "Fr" attr(data-out);
}
&[data-currency="LSL"] .prices--atob data::after {
content: "R" attr(data-out);
}
&[data-currency="MWK"] .prices--atob data::after {
content: "MK" attr(data-out);
}
&[data-currency="RWF"] .prices--atob data::after {
content: "Fr" attr(data-out);
}
&[data-currency="SOS"] .prices--atob data::after {
content: "Sh" attr(data-out);
}
&[data-currency="SZL"] .prices--atob data::after {
content: "R" attr(data-out);
}
&[data-currency="TZS"] .prices--atob data::after {
content: "Sh" attr(data-out);
}
&[data-currency="UGX"] .prices--atob data::after {
content: "Sh" attr(data-out);
}
&[data-currency="XAF"] .prices--atob data::after {
content: "Fr" attr(data-out);
}
&[data-currency="XOF"] .prices--atob data::after {
content: "Fr" attr(data-out);
}
&[data-currency="XPF"] .prices--atob data::after {
content: "Fr" attr(data-out);
}
&[data-currency="ZMW"] .prices--atob data::after {
content: "K" attr(data-out);
}
/* Symbole après la valeur — avec espace insécable */
&[data-currency="AED"] .prices--atob data::after {
content: attr(data-out) "\A0 د.إ";
}
&[data-currency="AFN"] .prices--atob data::after {
content: attr(data-out) "\A0 ؋";
}
&[data-currency="ALL"] .prices--atob data::after {
content: attr(data-out) "\A0 L";
}
&[data-currency="AMD"] .prices--atob data::after {
content: attr(data-out) "\A0 ֏";
}
&[data-currency="AOA"] .prices--atob data::after {
content: attr(data-out) "\A0 Kz";
}
&[data-currency="BAM"] .prices--atob data::after {
content: attr(data-out) "\A0 KM";
}
&[data-currency="BGN"] .prices--atob data::after {
content: attr(data-out) "\A0 лв.";
}
&[data-currency="BHD"] .prices--atob data::after {
content: attr(data-out) "\A0 .د.ب";
}
&[data-currency="BYN"] .prices--atob data::after {
content: attr(data-out) "\A0 Br";
}
&[data-currency="CZK"] .prices--atob data::after {
content: attr(data-out) "\A0 Kč";
}
&[data-currency="DKK"] .prices--atob data::after {
content: attr(data-out) "\A0 kr";
}
&[data-currency="DZD"] .prices--atob data::after {
content: attr(data-out) "\A0 د.ج";
}
&[data-currency="EUR"] .prices--atob data::after {
content: attr(data-out) "\A0 €";
}
&[data-currency="GEL"] .prices--atob data::after {
content: attr(data-out) "\A0 ₾";
}
&[data-currency="GMD"] .prices--atob data::after {
content: attr(data-out) "\A0 D";
}
&[data-currency="HRK"] .prices--atob data::after {
content: attr(data-out) "\A0 kn";
}
&[data-currency="HUF"] .prices--atob data::after {
content: attr(data-out) "\A0 Ft";
}
&[data-currency="IQD"] .prices--atob data::after {
content: attr(data-out) "\A0 ع.د";
}
&[data-currency="ISK"] .prices--atob data::after {
content: attr(data-out) "\A0 kr";
}
&[data-currency="JOD"] .prices--atob data::after {
content: attr(data-out) "\A0 د.ا";
}
&[data-currency="KGS"] .prices--atob data::after {
content: attr(data-out) "\A0 с";
}
&[data-currency="KHR"] .prices--atob data::after {
content: attr(data-out) "\A0 ៛";
}
&[data-currency="KWD"] .prices--atob data::after {
content: attr(data-out) "\A0 د.ك";
}
&[data-currency="LAK"] .prices--atob data::after {
content: attr(data-out) "\A0 ₭";
}
&[data-currency="LBP"] .prices--atob data::after {
content: attr(data-out) "\A0 ل.ل";
}
&[data-currency="LYD"] .prices--atob data::after {
content: attr(data-out) "\A0 ل.د";
}
&[data-currency="MAD"] .prices--atob data::after {
content: attr(data-out) "\A0 د.م.";
}
&[data-currency="MDL"] .prices--atob data::after {
content: attr(data-out) "\A0 L";
}
&[data-currency="MKD"] .prices--atob data::after {
content: attr(data-out) "\A0 ден";
}
&[data-currency="MRU"] .prices--atob data::after {
content: attr(data-out) "\A0 UM";
}
&[data-currency="MVR"] .prices--atob data::after {
content: attr(data-out) "\A0 .ރ";
}
&[data-currency="NOK"] .prices--atob data::after {
content: attr(data-out) "\A0 kr";
}
&[data-currency="OMR"] .prices--atob data::after {
content: attr(data-out) "\A0 ر.ع.";
}
&[data-currency="PLN"] .prices--atob data::after {
content: attr(data-out) "\A0 zł";
}
&[data-currency="QAR"] .prices--atob data::after {
content: attr(data-out) "\A0 ر.ق";
}
&[data-currency="RON"] .prices--atob data::after {
content: attr(data-out) "\A0 lei";
}
&[data-currency="RSD"] .prices--atob data::after {
content: attr(data-out) "\A0 дин.";
}
&[data-currency="RUB"] .prices--atob data::after {
content: attr(data-out) "\A0 ₽";
}
&[data-currency="SDG"] .prices--atob data::after {
content: attr(data-out) "\A0 ج.س.";
}
&[data-currency="SEK"] .prices--atob data::after {
content: attr(data-out) "\A0 kr";
}
&[data-currency="TJS"] .prices--atob data::after {
content: attr(data-out) "\A0 с.";
}
&[data-currency="TMT"] .prices--atob data::after {
content: attr(data-out) "\A0 m.";
}
&[data-currency="TND"] .prices--atob data::after {
content: attr(data-out) "\A0 د.ت";
}
&[data-currency="UZS"] .prices--atob data::after {
content: attr(data-out) "\A0 Sʻ";
}
&[data-currency="VND"] .prices--atob data::after {
content: attr(data-out) "\A0 ₫";
}
&[data-currency="VUV"] .prices--atob data::after {
content: attr(data-out) "\A0 Vt";
}
}
Télécharger kortic-currencies.css
Exemple
Petit tableau de prix avec un switch pour visualiser différentes monnaies.
| Licence | Prix |
|---|---|
| Licence Solo | |
| Licence Équipe | |
| Licence Entreprise |
Un parser sans rendu JavaScript récupère OC45OQ==. Pas franchement exploitable.
Mais un scraper sérieux avec Playwright passe quand même, il exécute le JS, il voit data-out décodé comme n'importe quel navigateur. Cette approche filtre les scrapers paresseux, pas les déterminés. Couplée à du rate limiting et une gestion de session côté serveur, elle contribue à une défense en profondeur raisonnable. Elle ne remplace pas ces couches, elle les complète.
En résumé
La balise <data> est sémantiquement propre, accessible, universellement supporté, et s'intègre dans un pattern CSS élégant pour la gestion des devises. Le switch i18n ne touche pas au DOM des prix. Le positionnement du symbole est encodé dans la CSS, conforme aux conventions locales, sans une ligne de JS supplémentaire.
Et en prime, ça complique un peu la vie des scrapers amateurs.
Pas mal pour un élément que trop de monde ignore, non ?
That's all, Folks !