- 98.css es un sistema de diseño CSS que implementa interfaces web con aspecto de Windows 98, basado en HTML semántico y accesibilidad
- Como solo aplica estilos al HTML sin JavaScript, puede usarse en diversos entornos frontend, incluidos React y JavaScript vanilla
- Ofrece una amplia variedad de componentes de UI de Windows 98, desde botones, campos de entrada y controles de selección hasta ventanas, pestañas, árboles, tablas y barras de progreso
- Admite la asociación de etiquetas,
aria-label y roles ARIA, y también permite redefinir estilos como márgenes o colores manteniendo la apariencia base
- Se puede instalar vía unpkg, npm o GitHub Releases, y se distribuye bajo licencia MIT, lo que permite modificaciones y contribuciones open source
Estructura del proyecto e instalación
- 98.css es una biblioteca CSS que recrea fielmente la interfaz de Windows 98
- Se basa en HTML semántico: usa
<button> para botones, <label> para elementos de entrada y aria-label para botones con íconos
- Entre sus objetivos principales se incluye la accesibilidad, considerando la navegación con teclado y el uso de tecnologías de asistencia
- Como estiliza HTML solo con CSS y sin JavaScript, no depende de ningún framework frontend específico
- La forma más rápida de usarlo es cargar la hoja de estilos desde unpkg
<link rel="stylesheet" href="https://unpkg.com/98.css">
npm install 98.css
- Se pueden redefinir libremente estilos individuales, como el margen de los botones o el color de las etiquetas de entrada, manteniendo la apariencia base
Controles básicos de selección
-
Botones
- El botón estándar mide 75×23 px y tiene bordes externos e internos en relieve, además de un margen horizontal básico de 12 px
- Admite
<button>, input[type=submit] e input[type=reset]
- La clase
default indica la acción predeterminada que se ejecuta al presionar Enter en la ventana activa
- Al hacer clic, el borde en relieve cambia a una forma hundida, y los botones deshabilitados conservan el borde mientras la etiqueta se atenúa
- El foco se muestra con un borde punteado ubicado 4 px hacia dentro del contenido del botón
-
Casillas de verificación y botones de opción
- Las casillas de verificación representan selecciones independientes o no excluyentes; al seleccionarlas, se muestra un ícono de check dentro de un panel hundido
- Después de una casilla de verificación debe haber un
<label> con un atributo for que apunte al id del input
- Esta asociación permite que las tecnologías de asistencia reconozcan el input
- Admiten navegación con la tecla Tab y clic en toda la etiqueta
- Los atributos estándar
checked y disabled definen los estados seleccionado y deshabilitado
- Se pueden envolver varios inputs en un contenedor
field-row para aplicar un espaciado consistente
- Los botones de opción son controles para elegir una sola opción dentro de un conjunto limitado y se crean con
input[type=radio]
- Al asignarles el mismo atributo
name, se agrupan como un grupo mutuamente excluyente; al igual que las casillas de verificación, usan etiquetas y field-row
-
Cajas de grupo
fieldset agrupa varios controles dentro de un marco rectangular y crea una apariencia de caja grabada con un borde externo hundido y un borde interno en relieve
- Al colocar un
legend dentro, se puede mostrar una etiqueta de grupo
Entrada de texto y selección de valores
-
Cajas de texto
- Las entradas de una línea se componen con
input[type=text], y las entradas multilínea con textarea
- La entrada y su etiqueta correctamente asociada se envuelven juntas en
field-row para colocarlas en horizontal
- Con
field-row-stacked, la etiqueta puede colocarse verticalmente encima del elemento de entrada
- Los atributos estándar de HTML
disabled y value definen el estado deshabilitado y el valor inicial
-
Sliders
- Un slider representa un rango de ajuste y su valor actual, y se renderiza con
input[type=range]
- La clase
has-box-indicator reemplaza el indicador predeterminado por un indicador tipo caja
- Si el slider se envuelve en un
div con la clase is-vertical, puede mostrarse en orientación vertical
- La longitud de un slider vertical se ajusta con el ancho del
input y la altura del div contenedor
-
Menús desplegables
- Con
select y option se crea un desplegable para elegir un elemento de una lista
- El primer elemento queda seleccionado por defecto, y se puede cambiar la selección inicial agregando el atributo
selected a otro option
Ventanas estilo Windows 98
-
Barra de título
- La barra de título se coloca a todo lo ancho en la parte superior de la ventana, identifica el contenido e incluye los botones comunes de comando de ventana
- Se compone de tres clases:
title-bar, title-bar-text y title-bar-controls
- En el
aria-label de los botones de comando pueden usarse Minimize, Maximize, Restore, Help y Close
aria-label comunica la intención del botón a las tecnologías de asistencia y también aplica el estilo de ícono correspondiente al comando
- También se ofrecen las clases independientes
minimize, maximize, restore, help y close
- Se separa el texto de
aria-label del estilo visual para evitar problemas de renderizado o accesibilidad durante procesos de multilenguaje y localización
- En ventanas que no pueden maximizarse, se especifica
disabled en el botón Maximize
- Con
title-bar inactive se representa una barra de título inactiva entre varias ventanas
-
Cuerpo de la ventana y barra de estado
- La clase
window proporciona bordes externos e internos en relieve y márgenes; se puede cambiar el tamaño de la ventana especificando el ancho del contenedor
- El contenido real debajo de la barra de título se ubica en
window-body
- La barra de estado en la parte inferior de la ventana muestra información contextual, como el estado actual o el estado del teclado
- Para el contenedor completo se usa
status-bar, y para cada área de texto, status-bar-field
- Con la barra de estado se pueden construir interfaces como Task Manager, Notepad o File Explorer
Interfaces de jerarquías, pestañas y tablas
-
Vista de árbol
ul.tree-view representa jerarquías lógicas entre objetos como una lista con sangría
- Al anidar
ul secundarios, se aplican bordes punteados y sangría; dentro de cada li se puede colocar el contenido deseado
- Las jerarquías expandibles y colapsables se construyen colocando la lista hija dentro de un elemento
details
-
Pestañas
- La lista de pestañas se escribe con
menu[role=tablist], y cada pestaña con li[role=tab]
- A la pestaña activa se le asigna
aria-selected="true"
- 98.css no incluye lógica de cambio de pestañas, por lo que los cambios de estado deben gestionarse con JavaScript del usuario
- El contenido de las pestañas se coloca en un área de ventana con
role="tabpanel"
- Al agregar la clase
multirows a menu, se pueden crear pestañas de varias filas
-
Vista de tabla
- Una
table de HTML se envuelve con div.sunken-panel para crear bordes adecuados y un área con desplazamiento
table.interactive muestra un cursor de puntero cuando el mouse pasa sobre las filas del cuerpo
- A las filas seleccionadas se les aplica la clase
highlighted, y el comportamiento real de selección de filas se implementa con un script aparte
Progreso y bordes genéricos
-
Barra de progreso
- El porcentaje de avance de una tarea larga se define colocando
progress-indicator-bar dentro de progress-indicator y ajustando su ancho
- La forma predeterminada es una barra de color sólido; al agregar la clase
segmented, se convierte en una barra de progreso segmentada
-
Bordes de campo
- Las cajas de texto, casillas de verificación, desplegables, spin boxes y cajas de lista usan un estilo de campo con bordes externos e internos hundidos
- Los campos normales usan el color de resaltado de botones en el interior, mientras que los campos de texto de solo lectura o deshabilitados usan el color de superficie de botón
- A los campos de estado solo se les aplica el borde externo hundido, y se usan en barras de estado o en contenido dinámico de solo lectura
- Se ofrecen tres clases genéricas para poder usarlas también en áreas de trabajo y contenido dinámico fuera de formularios
field-border: área de trabajo normal
field-border-disabled: área de trabajo deshabilitada
status-field-border: contenido dinámico de estado
- Como estas clases solo definen borde, color de fondo y margen mínimo, por lo general hay que especificar
padding adicional manualmente
Licencia y contribuciones
- 98.css se distribuye bajo la licencia MIT
- La verificación y el reporte de bugs pueden hacerse en GitHub Issues
- Acepta pull requests y ofrece code review; da la bienvenida especialmente a quienes contribuyen por primera vez al open source
1 comentarios
Comentarios de Hacker News
Este proyecto fue hecho durante un proceso de recuperación del burnout, y todavía le tengo un cariño especial
https://notes.jordanscales.com/98-css-reflections
Tiene un equilibrio excelente entre usabilidad y nostalgia, y me dieron ganas de usarlo más en otros proyectos
Mientras investigaba también encontré el portafolio del creador de 98.css, y hasta tenemos exactamente los mismos gustos musicales, así que me quedé pensando si todos los que hacen librerías retro tienen gustos parecidos
El diseño plano es ridículo, pero le hizo ganar muchísimo dinero a alguien y después todos lo siguieron como borregos
A estas alturas ya ni sorprende que no hayamos podido salir de eso después de casi 20 años, y la forma en que las pestañas multilínea funcionaban naturalmente en las interfaces antiguas era realmente excelente
El problema es que la filosofía de que menos es mejor choca con esas interfaces saturadas, como de plastilina, de Apple, Google y todos sus imitadores
Desperdiciaban valioso espacio vertical y, cada vez que hacías clic, cambiaba la posición de las líneas, lo que resultaba confuso; si un botón representa un estado, no debería moverse después de hacer clic
Tenemos monitores y tarjetas gráficas capaces de mostrar millones de colores y aun así no los aprovechamos; no hace falta convertir todo en un libro para colorear, pero Windows 10 y 11 se sienten deprimentes
Lo que siempre me molestó en el ecosistema de 98.css es que MS Sans Serif era sutilmente inexacta, así que al final decidí corregirla yo mismo
https://github.com/jdan/98.css/pull/230
Este proyecto ya se ha vuelto popular varias veces antes: 471 puntos y 93 comentarios en 2024 https://news.ycombinator.com/item?id=42056918, 606 puntos y 182 comentarios en 2022 https://news.ycombinator.com/item?id=33310554, 838 puntos y 211 comentarios en 2020 https://news.ycombinator.com/item?id=22940564
Windows 95: https://alexbsoft.github.io/win95.css/, sitio web de los años 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/
Quisiera crear un sitio web para aplicar este estilo, pero no se me ocurre en dónde quedaría bien
XFCE-winxp-tc, que hace que Linux se vea como Windows XP, también es excelente. Chicago95 es muy conocido, pero si quieres una vibra de 2004, https://github.com/rozniak/xfce-winxp-tc encaja muy bien
No sé si sea por preocupaciones legales
La forma de quitar los botones deshabilitados y ni siquiera mostrarlos es una de las tendencias de UI más confusas de hoy, y ver un botón gris incluso me da tranquilidad
Solo debían deshabilitarse cuando el usuario pudiera esperar razonablemente que aplicaran y pudiera inferir con facilidad por qué están deshabilitados; si no había forma de habilitarlos o no se esperaba que aplicaran, debían eliminarse. Si la razón no era fácil de entender, era mejor dejarlos habilitados y mostrar un mensaje de error útil cuando se usaran incorrectamente
https://learn.microsoft.com/en-us/windows/win32/uxguide/top-...
Pero en la guía moderna https://learn.microsoft.com/en-us/windows/apps/develop/ui/co... ya no se trata ni esa distinción sutil ni siquiera los botones deshabilitados en sí
En cambio, si tiene permiso para ejecutarla pero no puede por el modo o estado actual, debe mostrarse un botón gris. En formularios, incluso los botones grises deberían poder pulsarse y explicar qué está mal. En una gran aerolínea no pude completar el check-in por falta de ese tipo de aviso
Los tooltips al pasar el mouse y el pequeño signo
?han desaparecido, y parece que todo el software asume que ya sabes usarlo desde el primer díahttps://ka.ge es un sitio personal hecho con 98.css que recrea de forma funcional y con precisión al píxel un entorno de escritorio de Windows 98 en menos de 1 MB
Es una lástima que, en el ejemplo de pestañas de varias líneas, las pestañas no cambien de lugar aleatoriamente al hacer clic como en Windows original
Con cada nueva versión del sistema operativo, los diseñadores de UI tuvieron que vender como mejoras cambios que eran funcionalmente innecesarios, consumían más recursos y además eran más lentos e inestables
less faster, parece que esos diseñadores de UI también metieron mano en este comentarioCuesta entender por qué se enfocan tanto en recrear esto cuando la UI de Windows 98 ni siquiera fue la cumbre de la experiencia de usuario
La hinchazón actual es resultado de capas y capas de tecnología y niveles de abstracción acumulados, así que lo clave es entender qué hace falta para una buena experiencia de usuario y encontrar el conjunto mínimo de componentes tecnológicos
Las esquinas redondeadas y los espacios flotantes son ejemplos representativos, y la influencia de macOS y de los diseñadores web perjudicó la usabilidad