- 98.css est un système de design CSS qui permet de créer des interfaces web ressemblant à Windows 98, en s’appuyant sur du HTML sémantique et l’accessibilité
- Comme il applique uniquement des styles au HTML sans JavaScript, il peut être utilisé dans divers environnements frontend, notamment React et JavaScript vanilla
- Il fournit un large éventail de composants d’interface Windows 98, des boutons, champs de saisie et contrôles de sélection jusqu’aux fenêtres, onglets, arborescences, tableaux et barres de progression
- Il prend en charge l’association des libellés,
aria-label et les rôles ARIA, et permet aussi de redéfinir des styles comme les marges ou les couleurs tout en conservant l’apparence de base
- Il peut être installé via unpkg, npm ou GitHub Releases, et est distribué sous licence MIT, ce qui permet les modifications et les contributions open source
Structure du projet et installation
- 98.css est une bibliothèque CSS qui recrée fidèlement l’interface Windows 98
- Elle repose sur du HTML sémantique :
<button> pour les boutons, <label> pour les éléments de saisie, et aria-label pour les boutons d’icône
- L’un de ses objectifs principaux est l’accessibilité, avec une prise en compte de la navigation au clavier et des technologies d’assistance
- Comme elle habille le HTML uniquement avec du CSS, sans JavaScript, elle ne dépend d’aucun framework frontend particulier
- La façon la plus rapide de l’utiliser consiste à charger la feuille de style depuis unpkg
<link rel="stylesheet" href="https://unpkg.com/98.css">
npm install 98.css
- Il est possible de redéfinir librement des styles individuels, comme les marges des boutons ou la couleur des libellés de champ, tout en conservant l’apparence de base
Contrôles de sélection de base
-
Boutons
- Le bouton standard mesure 75×23px et possède des bordures externe et interne en relief, avec une marge horizontale par défaut de 12px
- Prend en charge
<button>, input[type=submit] et input[type=reset]
- La classe
default indique l’action par défaut exécutée lorsqu’on appuie sur Entrée dans une fenêtre active
- Lors d’un clic, l’apparence change pour faire rentrer la bordure en relief ; un bouton désactivé conserve sa bordure, mais son libellé devient estompé
- Le focus est indiqué par une bordure en pointillés placée à 4px à l’intérieur du contenu du bouton
-
Cases à cocher et boutons radio
- Les cases à cocher représentent des sélections indépendantes ou non exclusives ; lorsqu’elles sont cochées, une icône de validation apparaît dans le panneau enfoncé
- Après une case à cocher, un
<label> avec un attribut for pointant vers l’id du champ est obligatoire
- Cette liaison permet aux technologies d’assistance de reconnaître le champ
- La navigation avec la touche Tab et le clic sur l’ensemble du libellé sont pris en charge
- Les attributs standard
checked et disabled permettent de définir l’état sélectionné ou désactivé
- Plusieurs champs peuvent être enveloppés dans un conteneur
field-row pour appliquer un espacement cohérent
- Les boutons radio sont des contrôles permettant de choisir un seul élément dans un ensemble limité, créés avec
input[type=radio]
- En leur attribuant le même
name, ils forment un groupe mutuellement exclusif et utilisent, comme les cases à cocher, des libellés et field-row
-
Boîtes de groupe
fieldset permet de regrouper plusieurs contrôles dans un cadre rectangulaire, avec une bordure externe enfoncée et une bordure interne en relief donnant un aspect de boîte gravée
- En plaçant un
legend à l’intérieur, on peut afficher le libellé du groupe
Saisie de texte et sélection de valeurs
-
Champs de texte
- Les champs sur une ligne utilisent
input[type=text] et les champs multilignes utilisent textarea
- Le champ et son libellé correctement associé peuvent être enveloppés ensemble dans
field-row pour un alignement horizontal
- Avec
field-row-stacked, le libellé peut être placé verticalement au-dessus du champ
- Les attributs HTML standard
disabled et value permettent de définir l’état désactivé et la valeur initiale
-
Curseurs
- Les curseurs indiquent une plage de réglage et la valeur actuelle, et sont rendus avec
input[type=range]
- La classe
has-box-indicator remplace l’indicateur par défaut par un indicateur en forme de boîte
- En enveloppant le curseur dans un
div avec la classe is-vertical, on peut l’afficher en orientation verticale
- La longueur d’un curseur vertical se règle via la largeur de l’
input et la hauteur du div conteneur
-
Listes déroulantes
select et option permettent de créer une liste déroulante pour choisir un élément dans une liste
- Le premier élément est sélectionné par défaut, mais on peut modifier la sélection initiale en ajoutant l’attribut
selected à un autre option
Fenêtres de style Windows 98
-
Barre de titre
- La barre de titre occupe toute la largeur en haut de la fenêtre, identifie son contenu et inclut les boutons de commande habituels
- Elle se compose de trois classes :
title-bar, title-bar-text et title-bar-controls
- L’
aria-label des boutons de commande peut prendre les valeurs Minimize, Maximize, Restore, Help, Close
aria-label transmet l’intention du bouton aux technologies d’assistance tout en appliquant le style d’icône correspondant à la commande
- Des classes distinctes
minimize, maximize, restore, help, close sont aussi fournies
- La séparation entre le texte de
aria-label et le style visuel évite les problèmes de rendu ou d’accessibilité lors de la traduction et de la localisation
- Pour une fenêtre qui ne peut pas être agrandie, on ajoute
disabled au bouton Maximize
title-bar inactive permet de représenter une barre de titre inactive parmi plusieurs fenêtres
-
Corps de fenêtre et barre d’état
- La classe
window fournit des bordures externe et interne en relief ainsi que des marges, et la taille de la fenêtre peut être modifiée en définissant la largeur du conteneur
- Le contenu réel sous la barre de titre se place dans
window-body
- La barre d’état en bas de la fenêtre affiche des informations contextuelles comme l’état courant ou l’état du clavier
- Utiliser
status-bar pour le conteneur complet et status-bar-field pour chaque zone de texte
- La barre d’état permet de composer des interfaces comme le Gestionnaire des tâches, le Bloc-notes ou l’Explorateur de fichiers
Interfaces hiérarchiques, onglets et tableaux
-
Vue arborescente
ul.tree-view représente la hiérarchie logique entre objets sous la forme d’une liste indentée
- En imbriquant des
ul enfants, on applique une bordure en pointillés et une indentation, et chaque li peut contenir le contenu souhaité
- Une hiérarchie repliable/dépliable se construit en plaçant la liste enfant dans un élément
details
-
Onglets
- La liste des onglets s’écrit avec
menu[role=tablist] et chaque onglet avec li[role=tab]
- L’onglet actif reçoit
aria-selected="true"
- 98.css n’inclut pas la logique de changement d’onglet : les changements d’état doivent donc être gérés par le JavaScript de l’utilisateur
- Le contenu de l’onglet se place dans une zone de fenêtre avec
role="tabpanel"
- En ajoutant la classe
multirows à menu, on peut créer des onglets sur plusieurs lignes
-
Vue tableau
- Il suffit d’envelopper une
table HTML dans div.sunken-panel pour créer les bordures appropriées et une zone défilante
table.interactive affiche un curseur pointeur au survol des lignes du corps du tableau
- Les lignes sélectionnées reçoivent la classe
highlighted, tandis que la logique réelle de sélection doit être implémentée par un script séparé
Progression et bordures génériques
-
Barres de progression
- Le pourcentage d’avancement d’une tâche longue se représente en plaçant
progress-indicator-bar dans progress-indicator et en le définissant par sa largeur
- La forme par défaut est une barre unie ; en ajoutant la classe
segmented, elle devient une barre de progression segmentée
-
Bordures de champ
- Les champs de texte, cases à cocher, listes déroulantes, spin boxes et list boxes utilisent un style de champ avec bordures externe et interne enfoncées
- Les champs ordinaires utilisent à l’intérieur la couleur de surbrillance des boutons, tandis que les champs de texte en lecture seule ou désactivés utilisent la couleur de surface des boutons
- Les champs d’état n’appliquent qu’une bordure externe enfoncée, et conviennent aux barres d’état ou aux contenus en lecture seule mis à jour dynamiquement
- Trois classes génériques sont fournies pour les zones de travail et contenus dynamiques hors formulaires
field-border: zone de travail normale
field-border-disabled: zone de travail désactivée
status-field-border: contenu d’état dynamique
- Comme ces classes ne définissent que les bordures, la couleur d’arrière-plan et une marge intérieure minimale, il faut en général ajouter soi-même du
padding supplémentaire
Licence et contribution
- 98.css est distribué sous licence MIT
- Les bugs peuvent être vérifiés et signalés via GitHub Issues
- Les pull requests sont acceptées et des revues de code sont proposées, avec un accueil particulier pour les personnes qui contribuent pour la première fois à l’open source
1 commentaires
Avis sur Hacker News
Ce projet a été réalisé pendant un processus de rétablissement après un burnout, et je continue à y être particulièrement attaché
https://notes.jordanscales.com/98-css-reflections
L’équilibre entre ergonomie et nostalgie était remarquable, au point de me donner envie de l’utiliser davantage dans d’autres projets
En faisant mes recherches, j’ai aussi découvert le portfolio du créateur de 98.css, et comme nous avons exactement les mêmes goûts musicaux, je me suis demandé si tous ceux qui créent des bibliothèques rétro avaient des goûts similaires
Le flat design est ridicule, mais il a quand même fait gagner beaucoup d’argent à quelqu’un, puis tout le monde l’a suivi comme un troupeau
Après presque 20 ans, il n’est même plus surprenant qu’on n’arrive pas à s’en défaire, et la manière dont les onglets sur plusieurs lignes fonctionnaient naturellement dans les anciennes interfaces était vraiment excellente
C’est simplement que la philosophie du « moins c’est mieux » entre en conflit avec les interfaces sursaturées, façon pâte à modeler, d’Apple, Google et de leurs imitateurs
Ils prenaient un espace vertical précieux et la position des lignes changeait à chaque clic, ce qui était déroutant ; si un bouton sert à indiquer un état, il ne devrait pas bouger après un clic
On a des moniteurs et des cartes graphiques capables d’afficher des millions de couleurs, mais on ne les exploite pas ; il ne s’agit pas de tout transformer en coloriage, mais Windows 10 et 11 donnent une impression déprimante
Ce qui m’a toujours dérangé dans l’écosystème 98.css, c’est que MS Sans Serif est subtilement inexact, donc j’ai fini par le corriger moi-même
https://github.com/jdan/98.css/pull/230
Ce projet a déjà connu plusieurs retours en popularité : 471 points et 93 commentaires en 2024 https://news.ycombinator.com/item?id=42056918, 606 points et 182 commentaires en 2022 https://news.ycombinator.com/item?id=33310554, 838 points et 211 commentaires en 2020 https://news.ycombinator.com/item?id=22940564
Windows 95: https://alexbsoft.github.io/win95.css/, sites web des années 1980 : https://bootstra386.com/, Windows XP: https://botoxparty.github.io/XP.css/, GeoCities: https://code.divshot.com/geo-bootstrap/, Counter-Strike 1.6: https://cs16.samke.me/, Edward Tufte: https://edwardtufte.github.io/tufte-css/, Windows 98: https://jdan.github.io/98.css/, Windows 7: https://khang-nd.github.io/7.css/, PlayStation: https://micah5.github.io/PSone.css/, NES: https://nostalgic-css.github.io/NES.css/, Apple System: https://sakofchit.github.io/system.css/, SNES: https://snes-css.sadlative.com/, The Sims: https://thesimscss.inbn.dev/
J’aimerais créer un site web avec ce style, mais je n’arrive pas à imaginer dans quel contexte il conviendrait.
XFCE-winxp-tc, qui donne à Linux l’apparence de Windows XP, est également excellent. Chicago95 est bien connu, mais si vous voulez une ambiance 2004, https://github.com/rozniak/xfce-winxp-tc convient très bien.
Je ne sais pas si c’est à cause de préoccupations juridiques.
Supprimer les boutons désactivés au lieu de les afficher est l’une des tendances UI les plus déroutantes d’aujourd’hui, et voir un bouton grisé me rassure au contraire.
Il ne fallait les désactiver que si l’utilisateur s’attendait à pouvoir les utiliser et pouvait facilement déduire pourquoi ils étaient désactivés ; s’il n’existait aucun moyen de les activer ou qu’on ne s’attendait pas à ce qu’ils s’appliquent, il fallait les supprimer. S’il était difficile de comprendre la raison, mieux valait les laisser actifs et afficher un message d’erreur utile en cas de mauvaise utilisation.
https://learn.microsoft.com/en-us/windows/win32/uxguide/top-...
Mais les recommandations modernes https://learn.microsoft.com/en-us/windows/apps/develop/ui/co... n’abordent ni cette distinction subtile ni même les boutons désactivés.
En revanche, s’il a les droits d’exécution mais que l’action est impossible à cause du mode ou de l’état courant, il faut afficher un bouton grisé. Dans un formulaire, il faudrait même pouvoir cliquer sur un bouton grisé afin d’expliquer ce qui ne va pas. Chez une grande compagnie aérienne, l’absence de ce type d’indication m’a empêché de finaliser l’enregistrement.
Les infobulles au survol et les petits symboles
?ont disparu, et on dirait que tous les logiciels supposent qu’on sait les utiliser dès le premier jour.https://ka.ge est un site personnel réalisé avec 98.css qui reproduit l’environnement de bureau Windows 98 de façon fidèle au pixel près et réellement fonctionnelle, le tout en moins de 1 Mo.
C’est dommage que, dans l’exemple des onglets sur plusieurs lignes, les onglets ne changent pas aléatoirement de place au clic comme sous le vrai Windows.
À chaque nouvelle version du système d’exploitation, les designers UI ont dû présenter comme des améliorations des changements fonctionnellement inutiles, plus gourmands en ressources, plus lents et moins stables.
less faster, on dirait que ces designers UI ont aussi mis la main sur ce commentaire.J’ai du mal à comprendre pourquoi on se concentre sur ce type de reproduction alors que l’interface de Windows 98 n’était même pas le sommet de l’expérience utilisateur.
L’embonpoint actuel résulte de l’empilement de technologies et de couches d’abstraction ; l’essentiel est donc de comprendre ce qui est nécessaire à une bonne expérience utilisateur et de trouver l’ensemble minimal de composants techniques.
Les coins arrondis et les marges flottantes en sont des exemples typiques, et l’influence de macOS ainsi que des designers web a nui à l’utilisabilité.