Cette démo repose sur des modules ES natifs. En file://, le navigateur bloque leur chargement.
Ouvre le projet via un serveur HTTP local, par exemple :
cd /Users/thomas/Desktop/thomias-ui
python3 -m http.server 4173
Puis visite http://127.0.0.1:4173/#button ou http://localhost:4173/#button.
ma-lib-ui
thomias-ui
Une bibliothèque de snippets CSS d'abord, pour styler les éléments HTML natifs (<button>, <input>, <select>, <dialog>...) plutôt que de les remplacer par des balises custom.
Chaque composant de cette doc part d'une balise HTML native, lui ajoute une ou deux classes (.ui-*), et n'ajoute du JavaScript que quand le navigateur ne sait vraiment pas s'en passer. Pas de Shadow DOM, pas de composant custom : ce que tu copies est ce qui s'exécute.
Philosophie
Éléments natifs d'abord — <button>, <input>, <select>, <dialog>, <details>... plutôt que des balises custom.
CSS avant tout — l'essentiel du rendu et du comportement (accordéon, onglets, bascule) tient dans les classes .ui-*, sans JavaScript.
Zéro dépendance — aucun import, aucun build ; les snippets fonctionnent tels quels dans n'importe quelle page.
Personnalisable — un seul fichier de variables (tokens.css) pilote toute l'apparence.
CSS-firstCSS + JS minimalJS requis
La doc signale les composants qui tiennent entièrement en CSS, ceux qui gardent juste un peu de JS pour l'état ou les événements, et ceux qui ont un vrai comportement applicatif impossible à éviter (toast, drag&drop...).
Chaque exemple propose jusqu'à 3 zones copiables indépendamment : HTML, CSS et JavaScript (cette dernière n'apparaît que si le composant en a réellement besoin). Passe en mode Source brute dans la barre supérieure pour masquer les démos rendues et ne garder que les snippets.
Installation
Copie assets/css/tokens.css et assets/css/components.css dans ton projet, puis dans ta page :
C'est tout : pas de npm install, pas d'étape de compilation, pas de <script> obligatoire. Le JavaScript ne s'ajoute qu'au cas par cas, uniquement pour les composants marqués CSS + JS minimal ou JS requis — et c'est alors la zone JS de la doc que tu colles, sans import ni dépendance.
Pour naviguer dans cette page de documentation (recherche, filtres, copie par zone), ouvre-la via HTTP et non via file://, car assets/js/docs.js est un module ES. Le plus simple :
cd /Users/thomas/Desktop/thomias-ui
python3 -m http.server 4173
Cette contrainte ne concerne que la doc elle-même : dans ton propre projet, les snippets copiés fonctionnent aussi bien en file:// qu'en HTTP.
Exemples rapides
Quelques compositions prêtes à reprendre telles quelles, pour voir comment les composants se combinent dans une interface réelle.
Formulaire de connexion
Un cas simple avec carte, champs et action principale.
Une sortie claire après une action importante, avec résumé et prochaine étape.
Exemple rendu
Commande validée
Le paiement a bien été reçu et la préparation a commencé.
Code à copier
<article class="ui-card">
<div class="ui-card__body">
<div class="ui-result ui-result--success">
<svg class="ui-icon" ...>...</svg>
<h3>Commande validée</h3>
<p>Le paiement a bien été reçu et la préparation a commencé.</p>
<div class="ui-result__actions">
<button class="ui-btn ui-btn--primary">Voir le détail</button>
<button class="ui-btn ui-btn--primary ui-btn--plain">Retour au catalogue</button>
</div>
</div>
</div>
</article>
Explorer les composants
Une galerie générée depuis la documentation pour comparer rapidement le rendu de chaque composant et retrouver plus vite le bon point d'entrée.
Chargement des aperçus…
Thème & personnalisation
Le kit inclut un mode clair, sombre, et automatique (basé sur les préférences système). Teste le sélecteur en haut de la page.
Le thème repose sur l'attribut data-theme posé sur <html> et sur les variables définies dans assets/css/tokens.css. Un petit script (assets/js/theme.js) gère la persistance (localStorage) et la résolution du mode « auto ».
Variables principales
Variable
Rôle
--ui-color-primary
Couleur d'accent principale (boutons, liens, focus)
--ui-color-success
États de succès
--ui-color-warning
États d'avertissement
--ui-color-danger
États d'erreur / actions destructrices
--ui-color-bg / --ui-color-bg-elevated
Fonds de page et de surface (cartes, champs...)
--ui-color-text / --ui-color-text-secondary
Texte principal et secondaire
--ui-color-border
Bordures par défaut
--ui-radius-sm / md / lg
Rayons d'arrondi
--ui-spacing-xs … xl
Échelle d'espacement
Pour personnaliser, redéfinis ces variables sur :root (et sur :root[data-theme='dark'] pour le mode sombre) dans le CSS de ton propre projet — inutile de toucher aux fichiers du kit.
Grille flexible à 24 colonnes basée sur display: grid natif : gouttière et largeurs passent par des variables CSS inline, sans aucun calcul JavaScript.
Navigation par pages en boutons natifs ; la page active et l'ellipse sont posées directement dans le HTML de la démo (une vraie application calculerait ces pages côté serveur ou avec quelques lignes de JS).
Groupe de choix exclusif en forme de pilule, construit avec des radios natifs masqués (:checked + sélecteur de fratrie), alternative compacte aux boutons radio classiques.
Bouton flottant pour remonter en haut de page. Le style et la position utilisent position: fixed ; seule l'apparition après un seuil de défilement demande quelques lignes de JS, car le CSS ne peut pas lire scrollY.
Fais défiler la page de plus de 200px : le bouton apparaît en bas à droite de l'écran.
classe ajoutée en JS quand window.scrollY dépasse le seuil
href="#top"
remonte en haut de page nativement, sans JS
aria-label
libellé accessible (le bouton ne contient qu'une icône)
Button
CSS-first
Un <button> natif, avec des classes .ui-btn pour les variantes de couleur, de taille et d'état — aucun JavaScript requis, même pour le spinner de chargement.
attribut natif, nombre de lignes visibles (défaut : 3)
CSS resize
vertical (défaut ici), none, horizontal, both — poignée native du navigateur
disabled, readonly, placeholder, value
attributs natifs de <textarea>, inchangés
Checkbox
CSS + JS minimal
<input type="checkbox"> personnalisé en CSS pur (appearance: none + ::before). Seul l'état indéterminé demande une ligne de JS : il n'existe pas d'attribut HTML pour ça, uniquement une propriété .indeterminate.
pas d'incrémentation du spinner natif (défaut : 1)
disabled
attribut natif, inchangé
aria-label (ou un <label for>)
nom accessible du champ — indispensable, un placeholder seul ne suffit pas
Slider
CSS-first
Un <input type="range"> natif, dont le curseur et la piste sont stylés via ::-webkit-slider-thumb / ::-moz-range-thumb — le glisser à la souris et au clavier reste géré par le navigateur.
nom accessible du curseur — sinon un lecteur d'écran annonce juste une valeur sans contexte
Rate
CSS-first
Notation par étoiles en CSS pur : des <input type="radio"> masqués, un <label> par étoile, et l'ordre DOM inversé (flex-direction: row-reverse) pour que les sélecteurs de fratrie CSS colorent les étoiles précédentes au survol et à la sélection.
conteneur, une paire input[type=radio] + label par étoile, dans l'ordre décroissant
checked
étoile initialement sélectionnée
disabled (sur chaque input)
désactive toute la notation
nombre d'étoiles
ajouter/retirer des paires input/label pour changer l'échelle (ex. 10 étoiles)
Color Picker
CSS-first
Un <input type="color"> natif : la pastille est stylée en CSS (bordure, rayon), mais le sélecteur de couleur lui-même reste celui du système d'exploitation.
Un <input> classique associé à une <datalist> via l'attribut list : le filtrage des suggestions est géré par le navigateur, sans JavaScript. Compromis assumé du "natif d'abord" : l'apparence de la liste de suggestions elle-même (police, espacement, survol) reste imposée par le navigateur et ne peut pas être stylée avec nos tokens.
liste des suggestions, filtrées nativement par le navigateur à la frappe
Date Picker
CSS-first
Un <input type="date"> natif : le calendrier custom est abandonné au profit du picker fourni par le navigateur (et adapté à la langue/région de l'utilisateur), sans JavaScript.
<label class="ui-label" for="delivery-date">Date de livraison</label>
<input type="date" class="ui-input" id="delivery-date" />
Classe / attribut
Description
.ui-input
même style de base que le composant Input
value
date au format AAAA-MM-JJ
min, max
bornes de dates sélectionnables, attributs natifs
disabled
attribut natif, inchangé
<label for>
nom accessible du champ, indispensable
Time Picker
CSS-first
Un <input type="time"> natif : le picker à deux colonnes custom est abandonné au profit du sélecteur d'heure du navigateur, sans JavaScript.
granularité en secondes, ex. 300 pour un pas de 5 minutes
disabled
attribut natif, inchangé
<label for>
nom accessible du champ, indispensable
Upload
CSS-first
Un <input type="file"> natif, avec le bouton de sélection stylé via ::file-selector-button — sélecteur de fichiers et gestion de la liste par le système, zéro JavaScript. Le glisser-déposer et une liste de fichiers custom sont des améliorations qui nécessiteraient du JavaScript et ne sont pas couvertes ici.
types de fichiers acceptés, attribut natif inchangé
disabled
attribut natif, inchangé
<label for>
nom accessible du champ, indispensable
Badge
CSS-first
Un badge est un <span> positionné en absolu sur le coin d'un élément grâce à .ui-badge-wrap — aucun JavaScript, même pour la valeur 99+ qui est simplement écrite en dur dans le HTML.
conteneur relatif à poser autour de l'élément porteur du badge
.ui-badge
pastille positionnée en absolu sur le coin du conteneur
.ui-badge--dot
variante point, sans contenu texte
valeur max (ex. 99+)
écrite statiquement dans le HTML, aucun calcul JS
Tag
CSS + JS minimal
Un <span class="ui-tag"> stylé en CSS pur pour les variantes de couleur. Seule la version closable a besoin d'une ligne de JavaScript pour retirer le tag du DOM au clic sur sa croix.
base, à poser sur <span> (initiales) ou <img> (photo)
.ui-avatar--sm / --lg
taille
.ui-avatar--square
forme carrée aux coins arrondis plutôt que ronde
src, alt
attributs natifs de <img>, inchangés
Icon
CSS-first
Chaque icône est un <svg> inline au style "feather", avec la classe .ui-icon. La variante --spin anime la rotation en CSS pur, sans JavaScript. Une collection de départ est disponible plus bas : clique une icône pour copier son SVG.
anime une rotation continue en CSS, utile pour l'icône loading
width / height
taille de l'icône, portée directement sur le <svg> (16 à 20px selon le contexte)
stroke="currentColor"
hérite la couleur du texte environnant
Collection — clique une icône pour copier son SVG
Divider
CSS-first
Un <hr> natif pour le séparateur horizontal, un <div> avec deux traits en ::before/::after pour le séparateur à texte, et un <span> pour le séparateur vertical inline — pas de JavaScript.
base, à poser sur <hr>, <div> ou <span> selon l'usage
.ui-divider--dashed
trait en pointillés (sur <hr>)
.ui-divider--text
séparateur avec texte centré, deux traits générés en ::before/::after (sur <div>)
.ui-divider--vertical
séparateur vertical inline, à poser sur <span>
Collapse
CSS-first
Un <details>/<summary> natif stylé en .ui-collapse. Donner le même attribut name à plusieurs <details> force un mode accordéon exclusif (un seul panneau ouvert) nativement, sans JavaScript.
Qu'est-ce que thomias-ui ?
Un kit de snippets CSS à coller sur des éléments HTML natifs, sans dépendance ni build.
Puis-je le personnaliser ?
Oui, entièrement via les variables CSS de tokens.css.
Faut-il un framework ?
Non, les classes fonctionnent dans n'importe quelle page HTML.
conteneur du contenu, dans un <div> après <summary>
name (attribut natif)
partagé entre plusieurs <details> : force un mode accordéon exclusif
open (attribut natif)
état ouvert initial
Timeline
CSS-first
Frise chronologique verticale en .ui-timeline, sur une liste <ol> native. Le point et la ligne verticale sont posés en CSS pur via ::before et ::after, aucun JavaScript requis.
Aperçu de chargement en .ui-skeleton, avec un dégradé animé en CSS pur (@keyframes). Basculer du squelette vers le contenu réel dans une appli demande simplement de retirer la classe en JavaScript une fois les données arrivées.
TB
Thomas Boucks
Contenu réel, affiché une fois les données chargées.
Grille de paires libellé / valeur en .ui-descriptions, sur une liste <dl> native pilotée par la variable --ui-descriptions-columns. CSS Grid pur, aucun JavaScript requis.
variable inline pilotant le nombre de colonnes de la grille (empilée en dessous de 640px)
.ui-descriptions__item
paire <dt> / <dd>
Popover
CSS-first
Contenu HTML libre affiché au clic grâce à l'API native popover (Baseline 2024) : popovertarget ouvre et ferme le panneau sans JavaScript. Un déclenchement au survol demanderait quelques lignes de JS (showPopover() / hidePopover() sur mouseenter / mouseleave).
attribut natif : transforme l'élément en panneau top-layer, masqué par défaut
popovertarget
sur le bouton déclencheur : id de l'élément popover à ouvrir/fermer
.ui-popover
habillage visuel du panneau (bordure, ombre, fond)
Carousel
CSS + JS minimal
Défilement en .ui-carousel via CSS scroll-snap — le glissement lui-même ne demande aucun JavaScript. La lecture automatique, elle, utilise un setInterval minimal avec pause au survol.
setInterval + track.scrollBy(...), pause au survol via mouseenter / mouseleave
Alert
CSS-first
Un conteneur .ui-alert fermable en CSS pur grâce à l'astuce du <input type="checkbox" hidden> couplé au sélecteur :has() : aucun JavaScript n'est nécessaire, même pour la fermeture.
pilote la fermeture ; à omettre pour une alerte non fermable
.ui-alert__close (<label for="...">)
déclencheur de fermeture, reste cliquable même une fois l'alerte masquée
:has(.ui-alert__toggle:checked)
sélecteur CSS qui masque toute l'alerte à la coche — zéro JavaScript
Progress
CSS-first
Un <progress> natif stylé en .ui-progress. L'état indéterminé est celui du navigateur (pas d'attribut value, animation automatique) ; le motif rayé s'obtient avec un repeating-linear-gradient sur ::-webkit-progress-value.
état indéterminé natif du navigateur, animation automatique
.ui-progress--success / --warning / --error
couleur de statut
.ui-progress--striped
motif rayé via repeating-linear-gradient
.ui-progress-wrap / .ui-progress__text
conteneur optionnel pour afficher un texte de pourcentage à côté de la barre
Toast
JS requis
Notification impérative déclenchée via une fonction autonome showToast() : aucune balise persistante ne peut représenter une notification transitoire en HTML/CSS pur, c'est le seul composant du lot qui a vraiment besoin de JavaScript.
conteneur créé une fois, en position: fixed, qui empile les notifications
Table
CSS + JS minimal
Un vrai <table> natif : lignes et colonnes écrites en dur, zébrage et bordures gérés en CSS pur via .ui-table. Un petit script optionnel ajoute le tri au clic sur les en-têtes.
.ui-table__sort (sur un <button>) + aria-sort (sur le <th>)
optionnel : rend le tri activable au clic et au clavier (Entrée/Espace, natif sur un <button>)
Tree
CSS + JS minimal
Arborescence en <ul> imbriquées. L'ouverture/fermeture des branches utilise <details>, zéro JS. Seule la cascade coché/indéterminé entre parents et enfants demande un petit script.
expansion/repli natif, open pour une branche dépliée par défaut
.ui-checkbox
case à cocher (voir la section Checkbox)
cascade JS
synchronise coché/décoché parent ↔ enfants et l'état .indeterminate des ancêtres au change
Cascader
JS requis
Trois <select> natifs chaînés (pays / région / ville). Changer un niveau repeuple le suivant via JS ; les données sont un simple objet imbriqué, plus de composant custom.
remplace .options ; une clé par pays/région avec ses enfants
event change
sur chaque <select>, repeuple le niveau suivant
Transfer
JS requis
Deux <select multiple> natifs côte à côte. Les boutons du milieu déplacent réellement les <option> sélectionnées d'une liste à l'autre via appendChild.
Confirmation contextuelle basée sur l'API native popover : l'ouverture et l'annulation se font sans JS via popovertarget. Seul le bouton "Supprimer" a besoin de 2-3 lignes pour exécuter l'action puis refermer le popover.
exécute l'action confirmée puis appelle hidePopover()
Loading
JS requis
Fonction autonome showLoadingMask(target, { text }) qui superpose un overlay avec spinner CSS sur target (qui doit avoir position: relative) et retourne une fonction hide().
Un <dialog> natif positionné en CSS pour glisser depuis le bord droit de l'écran. Ouverture/fermeture natives via showModal()/close(), seuls les deux boutons ont besoin de JS.
à poser sur <dialog>, positionne le panneau à droite de l'écran
showModal() / close()
API native du <dialog>, ouvre/ferme le panneau
::backdrop
fond assombri natif derrière le panneau
.ui-drawer__footer
zone d'actions en bas du panneau
Tooltip
CSS-first
Une bulle d'aide en CSS pur via l'attribut data-tooltip et un ::after affiché au :hover / :focus-visible. Le contenu généré en CSS (attr()) n'est jamais exposé aux lecteurs d'écran : un <span class="ui-sr-only"> caché visuellement, relié via aria-describedby, porte le même texte pour l'accessibilité.
attribut natif sur le bouton déclencheur, référence l'id du panneau
popover
attribut natif qui transforme le <div> en overlay géré par le navigateur
.ui-dropdown
panneau du menu
.ui-dropdown-item
action du menu, un <button> par ligne
.ui-dropdown-item--divided
ajoute un séparateur au-dessus (utile pour une action destructrice)
Tabs
CSS-first
Pattern radios + labels : chaque onglet est un <input type="radio"> caché, et le panneau associé est affiché via :checked ~ [data-panel]. Zéro JavaScript.
méthode native qui ouvre la fenêtre en mode modal avec le fond
.close()
méthode native qui ferme la fenêtre
::backdrop
pseudo-élément natif du fond, stylé via .ui-dialog::backdrop
.ui-dialog__title / __footer
titre et zone d'actions du contenu
Kbd
CSS-first
Affiche des raccourcis clavier comme des touches physiques grâce à l'élément sémantique natif <kbd> — présent dans la spec HTML depuis longtemps, mais absent de la quasi-totalité des UI kits.
élément HTML natif pour représenter une saisie clavier (spec W3C)
.ui-kbd
style de base : aspect touche physique avec bordure basse épaissie
+ entre deux kbd
texte nu pour séparer les touches — pas besoin de classe
OTP / PIN
CSS + JS minimal
Saisie d'un code à N chiffres (2FA, PIN, vérification) : des <input maxlength="1"> natifs côte à côte. Le JS ajoute l'avance automatique au caractère suivant et la gestion du collé — sans ça, les champs ne sont que du CSS.
clavier numérique sur mobile, sans les boutons +/- du type number
maxlength="1"
limite native à un seul caractère par case
JS auto-avance
focus la case suivante à chaque saisie ; gère le collé (paste) en remplissant toutes les cases
Scroll Snap
CSS-first
Carrousel horizontal sans une seule ligne de JavaScript : scroll-snap-type force le navigateur à toujours s'arrêter sur un item entier. La navigation se fait aux gestes tactiles natifs, à la molette ou aux touches clavier — idéal comme alternative légère aux librairies de carousel.
chaque item occupe la pleine largeur ; ajuste à 80% pour un aperçu partiel du suivant
scroll-snap-type: x proximity
accrochage moins strict (snapping uniquement si proche d'un bord)
tabindex="0"
permet la navigation au clavier (flèches) sans JavaScript
Meter
CSS-first
L'élément HTML <meter> représente une mesure dans une plage connue et sémantiquement stable (capacité, score, stock, niveau). Différent de <progress> qui dit « en cours » : <meter> dit « valeur actuelle dans une plage ». La plupart des UI kits l'ignorent complètement.
seuils qui définissent les zones « basse » et « haute »
optimum
valeur idéale — détermine si une zone basse/haute est suboptimale ou non (colore automatiquement en vert/orange/rouge)
contenu texte
fallback accessible pour les navigateurs sans support, et lu par les lecteurs d'écran
Counter
JS requis
Nombre qui s'anime vers sa valeur cible dès qu'il entre dans le viewport, avec IntersectionObserver. Utilise l'élément sémantique <output> (résultat d'un calcul ou d'une action, selon la spec HTML) avec aria-live="polite" pour les lecteurs d'écran.
élément HTML natif pour les résultats de calcul — sémantiquement juste pour une valeur dynamique
aria-live="polite"
annonce la valeur finale aux lecteurs d'écran sans interrompre la lecture en cours
data-target
valeur numérique cible de l'animation
data-prefix / data-suffix
texte avant/après le nombre (+, €, %...)
data-decimals
nombre de décimales (défaut : 0)
IntersectionObserver
déclenche l'animation à l'entrée dans le viewport, pas au chargement de la page
Floating Label
CSS-first
Label qui descend en placeholder puis remonte quand le champ est rempli — en CSS pur grâce à :not(:placeholder-shown) et au sélecteur de fratrie ~, sans une ligne de JavaScript.
espace obligatoire pour que :placeholder-shown détecte le champ vide sans JS
:not(:placeholder-shown) ~ label
sélecteur CSS pur : champ rempli = label flottant
:focus ~ label
fratrie CSS : anime le label au focus même si le champ est encore vide
Spoiler / Details
CSS-first
Contenu révélable via l'élément HTML natif <details>/<summary> — le navigateur gère l'état ouvert/fermé nativement depuis 2014, sans JavaScript.
Qu'est-ce que le CSS-first ?
Le CSS-first consiste à exprimer le comportement visuel (accordéon, bascule...) uniquement avec du CSS, sans JavaScript, en s'appuyant sur les sélecteurs d'état natifs comme :checked, :has().
Peut-on animer l'ouverture ?
Avec interpolate-size: allow-keywords (CSS 2024) on peut transitionner height: 0 vers height: auto. En attendant, une transition sur opacity et padding est un bon compromis.
Ouvert par défaut (attribut open)
Ajoute l'attribut open sur <details> pour qu'il soit déplié au chargement.
élément natif, gère l'état ouvert/fermé sans JavaScript
<summary>
titre cliquable — le navigateur bascule open à chaque clic
open
attribut HTML pour déplier par défaut
[open]
sélecteur CSS ciblant l'état déplié
Popover API
CSS-first
Le nouvel attribut HTML popover crée un panneau non-modal avec popovertarget sur un bouton — ouverture, fermeture (Échap, clic extérieur) et accessibilité sans une ligne de JavaScript. Disponible dans tous les navigateurs modernes depuis 2024.
Le saviez-vous ?
L'attribut popover gère nativement le panneau flottant non-modal, l'accessibilité et la fermeture à l'échap — sans JavaScript.
attribut HTML natif — masqué par défaut, accessible au clavier, fermé par Échap
popovertarget="id"
sur un bouton : cible le popover à ouvrir/fermer — zéro JavaScript
::backdrop
fond derrière le popover — transparent pour non-modal, semi-opaque pour modal
Chip Filter
CSS-first
Filtres multi-sélection en « chips » sans JavaScript : des <input type="checkbox"> masqués dans un <label>, l'état actif pilé par :has(input:checked). Un type="radio" donne la sélection unique.
sélecteur CSS natif pour styler le chip actif sans JS
type="radio" + même name
variante sélection exclusive (un seul chip à la fois)
Color Swatch
CSS-first
Palette de couleurs ou de tailles prédéfinies via des radios masqués ; la couleur passe par une variable CSS inline --swatch-color, le halo de sélection par :has(input:checked).
variable CSS inline pour passer la couleur sans classe supplémentaire
:has(input:checked)
halo de sélection CSS pur — pas de JS
.ui-swatch-text
variante textuelle pour les tailles ou options courtes
Bottom Sheet
CSS + JS minimal
Panneau qui glisse depuis le bas de l'écran, très courant sur mobile. Repose sur un <dialog> natif positionné en bas avec inset: auto 0 0 0. L'animation d'entrée utilise @starting-style, la propriété CSS 2024 qui évite le JS pour animer l'insertion dans le DOM.
attribut HTML natif qui active le drag sur un élément
.is-dragging
classe CSS sur l'élément en cours de déplacement (opacité réduite)
dragstart / dragover / dragend
événements natifs HTML5 drag-and-drop
Relative Time
CSS + JS minimal
Affiche une date comme « il y a 3 heures » grâce à l'élément sémantique <time datetime=""> et Intl.RelativeTimeFormat natif — sans librairie de dates, le texte brut sert de fallback accessible.
conteneur flex avec le texte à gauche et le bouton à droite
user-select: all
le texte se sélectionne entièrement d'un clic, même sans le bouton
navigator.clipboard.writeText()
API moderne, asynchrone et sécurisée (HTTPS ou localhost)
.is-copied
classe CSS pour le retour visuel « Copié ! »
Scroll Progress
CSS-first
Barre de progression de lecture en haut de page, 100% CSS — aucun JavaScript. Utilise animation-timeline: scroll(root) qui lie une animation CSS au défilement du document. Support Chrome 115+, Firefox 110+, Safari 18+.
Défile ce panneau : la barre bleue en haut progresse — sans JavaScript.
—
—
—
<!-- Pose cet élément au début du <body> -->
<div class="ui-scroll-progress" role="progressbar" aria-label="Progression de lecture"></div>
<style>
.ui-scroll-progress {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 3px;
background: var(--ui-color-primary);
transform-origin: left center;
transform: scaleX(0);
z-index: 999;
animation: ui-scroll-progress-anim linear both;
animation-timeline: scroll(root);
}
@keyframes ui-scroll-progress-anim {
to { transform: scaleX(1); }
}
</style>
Propriété CSS
Description
animation-timeline: scroll(root)
lie l'animation au défilement de <html> — pas de JavaScript
animation-timeline: scroll(nearest)
lie au plus proche conteneur défilant — utile pour un panneau interne
transform-origin: left center
fait grandir la barre de gauche à droite via scaleX(0 → 1)
@supports (animation-timeline: scroll())
à utiliser pour appliquer ce composant uniquement quand le navigateur le supporte
Fieldset
CSS-first
L'élément HTML <fieldset> regroupe sémantiquement des champs de formulaire ; <legend> l'intitule. La plupart des UI kits remplacent ces natifs par des <div> anonymes, perdant ainsi l'accessibilité gratuite.
groupe sémantique natif ; les lecteurs d'écran annoncent le groupe à la première interaction
<legend>
intitulé accessible du groupe, flottant dans la bordure
disabled
attribut natif sur <fieldset> qui désactive tous les champs enfants d'un coup
Sticky Table
CSS-first
Un <table> dans un conteneur overflow: auto avec position: sticky sur le <thead> — le navigateur gère l'en-tête fixe au défilement horizontal et vertical, sans JavaScript.
sur le conteneur : défilement activé (requis pour que sticky fonctionne)
z-index: 1
garantit que l'en-tête passe au-dessus des cellules lors du défilement
Masonry
CSS-first
Disposition en cascade (style Pinterest) via la propriété CSS columns — zéro JavaScript, zéro librairie. Les items se répartissent automatiquement dans les colonnes de haut en bas.
nombre de colonnes (ou largeur minimale d'une colonne : columns: 280px)
break-inside: avoid
empêche un item d'être coupé entre deux colonnes
margin-bottom
écartement vertical entre items (gap ne s'applique pas aux colonnes CSS)
Container Query
CSS-first
La carte répond à sa propre largeur plutôt qu'à celle du viewport via @container — la nouveauté CSS 2023 qui remplace les media queries pour les composants réutilisables. Zéro JavaScript.
La mise en page change selon la largeur de la carte
Au-dessus de 400 px, l'image passe à gauche et le contenu à droite — automatiquement, sans media query ni JavaScript.
Format compact
Cette carte fait moins de 400 px, donc elle empile verticalement.
déclare le conteneur de référence pour les enfants
@container (min-width: 400px)
règle appliquée quand le conteneur fait au moins 400 px (pas le viewport)
container-name
optionnel : nommer un conteneur pour cibler précisément avec @container my-name (...)
Abbr Tooltip
CSS-first
Tooltip 100% CSS depuis l'attribut title natif de <abbr> — ::after { content: attr(title); } in2ecte le texte du tooltip sans JavaScript. La bulle apparaît au survol uniquement via :hover.
La technologie CSS et le
DOM sont les deux piliers du
Web.
titre cliquable du nœud ; le reste de <details> est le contenu enfant
imbrication
placer un <details> dans un autre <details> crée autant de niveaux qu'on veut
Range + Output
CSS + JS minimal
Un <input type="range"> lié à un <output> via l'attribut for : la valeur en direct s'affiche sans librairie, avec un seul attribut oninput inline. L'élément <output> est sémantique (résultat d'un calcul) et annoncé par les lecteurs d'écran.
retourne une Promise<boolean> : true si confirmé, false si annulé
value="confirm" / value="cancel"
attribut natif sur les <button> pour différencier la réponse
::backdrop
fond natif du <dialog>, stylable en CSS
Auto-grow Textarea
CSS + JS minimal
Zone de texte qui grandit automatiquement selon le contenu via un seul attribut oninput inline — pas de librairie, pas de ResizeObserver, juste une ligne.
this.style.height='auto' réinitialise d'abord, puis scrollHeight lit la hauteur nécessaire
overflow: hidden
masque la barre de défilement et force scrollHeight à refléter la vraie hauteur
resize: none
supprime la poignée de redimensionnement manuel, désormais inutile
Print Styles
CSS-first
Styles @media print pour une page imprimée propre : masquer la navigation et les boutons, adapter les couleurs, créer des sauts de page contrôlés. Pure CSS, zéro JavaScript.
Contenu principal
La navigation latérale, les boutons et le fond de page seront masqués à l'impression. Seul le contenu sémantique apparaît.
Ctrl+P / Cmd+P pour prévisualiser l'impression.
<style>
@media print {
/* Masquer les éléments non imprimables */
.topbar, .sidebar, .no-print,
button, .doc-filters, .copy-btn {
display: none !important;
}
/* Arrêtre-plan blanc, texte noir */
* {
background: white !important;
color: black !important;
box-shadow: none !important;
}
/* Liens : afficher l'URL après */
a[href]::after { content: ' (' attr(href) ')'; font-size: 0.85em; }
a[href^='#']::after { content: ''; } /* pas les ancres internes */
/* Eviter les coupures au milieu d'une carte */
.ui-card, article, section { break-inside: avoid; }
/* Saut de page forcé avant une section importante */
.page-break-before { break-before: page; }
/* Fonts plus petites pour densifier */
body { font-size: 11pt; line-height: 1.4; }
}
</style>
Règle CSS
Description
@media print
s'applique uniquement à l'impression (ou en prévisualisation d'impression)
break-inside: avoid
empêche une carte ou une section d'être coupée entre deux pages
break-before: page
force un saut de page avant l'élément
content: attr(href)
affiche l'URL des liens après leur texte pour le lecteur papier