- 98.css là một hệ thống thiết kế CSS để triển khai giao diện web trông giống Windows 98, dựa trên HTML ngữ nghĩa và khả năng truy cập
- Vì chỉ áp dụng style cho HTML mà không cần JavaScript, có thể dùng trong nhiều môi trường front-end khác nhau, bao gồm React và vanilla JavaScript
- Cung cấp nhiều thành phần UI Windows 98, từ nút, ô nhập liệu, control chọn cho đến cửa sổ, tab, cây, bảng và thanh tiến trình
- Hỗ trợ liên kết nhãn,
aria-label và vai trò ARIA; đồng thời có thể ghi đè các style như khoảng cách hoặc màu sắc trong khi vẫn giữ ngoại hình mặc định
- Có thể cài đặt qua unpkg, npm, GitHub Releases và được phân phối theo giấy phép MIT, cho phép chỉnh sửa và đóng góp mã nguồn mở
Cấu trúc dự án và cài đặt
- 98.css là thư viện CSS tái hiện trung thực giao diện Windows 98
- Dựa trên HTML ngữ nghĩa: dùng
<button> cho nút, <label> cho phần tử nhập liệu, và aria-label cho nút biểu tượng
- Khả năng truy cập là một trong các mục tiêu chính, có cân nhắc điều hướng bằng bàn phím và sử dụng công nghệ hỗ trợ
- Vì chỉ dùng CSS để trang trí HTML mà không cần JavaScript, thư viện không phụ thuộc vào framework front-end cụ thể nào
- Cách dùng nhanh nhất là tải stylesheet từ unpkg
<link rel="stylesheet" href="https://unpkg.com/98.css">
npm install 98.css
- Có thể tự do ghi đè từng style riêng lẻ, như khoảng cách của nút hoặc màu nhãn của ô nhập liệu, trong khi vẫn giữ ngoại hình mặc định
Control chọn cơ bản
-
Nút
- Nút tiêu chuẩn có kích thước 75×23px, có viền ngoài và viền trong nổi, cùng khoảng cách ngang mặc định 12px
- Hỗ trợ
<button>, input[type=submit], input[type=reset]
- Class
default biểu thị hành động mặc định sẽ chạy khi nhấn phím Enter trong cửa sổ đang hoạt động
- Khi bấm, viền nổi chuyển sang dạng lõm; nút bị vô hiệu vẫn giữ viền nhưng nhãn bị làm mờ
- Trạng thái focus được hiển thị bằng viền nét đứt nằm cách nội dung nút 4px về phía trong
-
Checkbox và radio button
- Checkbox biểu thị lựa chọn độc lập hoặc không loại trừ lẫn nhau; khi được chọn, biểu tượng dấu tick sẽ hiển thị bên trong một panel lõm
- Sau checkbox bắt buộc phải có
<label> với thuộc tính for trỏ đến id của input
- Liên kết này giúp công nghệ hỗ trợ nhận biết input
- Hỗ trợ điều hướng bằng phím Tab và bấm vào toàn bộ nhãn
- Dùng các thuộc tính HTML chuẩn
checked, disabled để chỉ định trạng thái được chọn và bị vô hiệu
- Nhiều input được bọc trong container
field-row để áp dụng khoảng cách nhất quán
- Radio button là control chỉ cho phép chọn một mục trong một tập hợp giới hạn, được tạo bằng
input[type=radio]
- Gán cùng thuộc tính
name để nhóm thành nhóm loại trừ lẫn nhau, và dùng nhãn cùng field-row giống như checkbox
-
Group box
fieldset nhóm nhiều control trong một khung chữ nhật, tạo vẻ ngoài như một hộp được khắc với viền ngoài lõm và viền trong nổi
- Đặt
legend bên trong để hiển thị nhãn nhóm
Nhập văn bản và chọn giá trị
-
Text box
- Ô nhập một dòng dùng
input[type=text], ô nhập nhiều dòng dùng textarea
- Bọc input cùng nhãn được liên kết đúng trong
field-row để bố trí ngang
- Dùng
field-row-stacked để bố trí nhãn theo chiều dọc phía trên phần tử nhập liệu
- Dùng các thuộc tính HTML chuẩn
disabled, value để chỉ định trạng thái bị vô hiệu và giá trị ban đầu
-
Slider
- Slider biểu thị phạm vi điều chỉnh và giá trị hiện tại, được render bằng
input[type=range]
- Class
has-box-indicator thay thế chỉ báo mặc định bằng chỉ báo dạng hộp
- Có thể hiển thị theo chiều dọc bằng cách bọc slider trong
div có class is-vertical
- Chiều dài slider dọc được điều chỉnh bằng chiều rộng của
input và chiều cao của container div
-
Dropdown
- Tạo dropdown chọn một mục từ danh sách bằng
select và option
- Mục đầu tiên được chọn mặc định; có thể thay đổi lựa chọn ban đầu bằng cách đặt thuộc tính
selected cho một option khác
Cửa sổ phong cách Windows 98
-
Thanh tiêu đề
- Thanh tiêu đề nằm trải hết chiều rộng phía trên cửa sổ, dùng để nhận diện nội dung và chứa các nút lệnh cửa sổ thông dụng
- Được cấu thành từ ba class
title-bar, title-bar-text, title-bar-controls
aria-label của nút lệnh có thể dùng Minimize, Maximize, Restore, Help, Close
aria-label truyền ý định của nút cho công nghệ hỗ trợ, đồng thời áp dụng style biểu tượng phù hợp với lệnh
- Cũng cung cấp các class riêng
minimize, maximize, restore, help, close
- Tách văn bản
aria-label khỏi style trực quan để tránh phát sinh vấn đề render hoặc khả năng truy cập trong quá trình đa ngôn ngữ hóa và bản địa hóa
- Với cửa sổ không thể phóng to, đặt
disabled cho nút Maximize
- Dùng
title-bar inactive để biểu thị thanh tiêu đề của cửa sổ đang không hoạt động giữa nhiều cửa sổ
-
Nội dung cửa sổ và thanh trạng thái
- Class
window cung cấp viền ngoài và viền trong nổi cùng khoảng cách; có thể thay đổi kích thước cửa sổ bằng cách chỉ định chiều rộng container
- Nội dung thực tế bên dưới thanh tiêu đề được đặt trong
window-body
- Thanh trạng thái ở cuối cửa sổ hiển thị thông tin ngữ cảnh như trạng thái hiện tại hoặc trạng thái bàn phím
- Dùng
status-bar cho container tổng thể, và status-bar-field cho từng vùng văn bản
- Có thể dùng thanh trạng thái để cấu thành các giao diện như Task Manager, Notepad, File Explorer
Giao diện phân cấp, tab và bảng
-
Tree view
ul.tree-view biểu diễn phân cấp logic giữa các đối tượng dưới dạng danh sách có thụt lề
- Khi lồng
ul con, viền nét đứt và thụt lề sẽ được áp dụng; bên trong li có thể đặt nội dung tùy ý
- Phân cấp có thể mở và thu gọn được tạo bằng cách đặt danh sách con bên trong phần tử
details
-
Tab
- Danh sách tab được viết bằng
menu[role=tablist], mỗi tab bằng li[role=tab]
- Gán
aria-selected="true" cho tab đang hoạt động
- 98.css không có logic chuyển tab, nên việc thay đổi trạng thái phải được quản lý bằng JavaScript của người dùng
- Nội dung tab được đặt trong vùng cửa sổ có
role="tabpanel"
- Thêm class
multirows vào menu để tạo tab nhiều hàng
-
Table view
- Bọc HTML
table trong div.sunken-panel để tạo viền phù hợp và vùng có thể cuộn
table.interactive hiển thị con trỏ pointer khi di chuột lên các hàng trong phần thân bảng
- Áp dụng class
highlighted cho hàng được chọn; hành vi chọn hàng thực tế được triển khai bằng script riêng
Trạng thái tiến trình và viền dùng chung
-
Thanh tiến trình
- Tỷ lệ hoàn thành của tác vụ dài được chỉ định bằng chiều rộng của
progress-indicator-bar đặt trong progress-indicator
- Dạng mặc định là thanh đơn sắc; thêm class
segmented sẽ chuyển thành thanh tiến trình phân đoạn
-
Viền trường
- Text box, checkbox, dropdown, spin box và list box dùng style trường với viền ngoài và viền trong lõm
- Trường thông thường dùng màu nhấn của nút ở bên trong, còn trường văn bản chỉ đọc hoặc bị vô hiệu dùng màu bề mặt nút
- Trường trạng thái chỉ áp dụng viền ngoài lõm, dùng cho thanh trạng thái hoặc nội dung chỉ đọc thay đổi động
- Cung cấp ba class dùng chung để có thể dùng cho vùng làm việc và nội dung động ngoài form
field-border: vùng làm việc thông thường
field-border-disabled: vùng làm việc bị vô hiệu
status-field-border: nội dung trạng thái động
- Các class này chỉ định nghĩa viền, màu nền và khoảng cách tối thiểu, nên thường cần tự chỉ định
padding bổ sung
Giấy phép và đóng góp
- 98.css được phân phối theo giấy phép MIT
- Có thể xác nhận và báo lỗi tại GitHub Issues
- Dự án nhận pull request và cung cấp code review, đặc biệt chào đón những người lần đầu đóng góp cho mã nguồn mở
1 bình luận
Ý kiến trên Hacker News
Dự án này được tạo ra trong quá trình hồi phục sau kiệt sức, và đến giờ tác giả vẫn rất trân trọng nó
https://notes.jordanscales.com/98-css-reflections
Sự cân bằng giữa tính tiện dụng và cảm giác hoài niệm rất tinh tế, khiến tôi muốn dùng nó nhiều hơn trong các dự án khác
Trong lúc tìm hiểu, tôi còn phát hiện portfolio của tác giả 98.css, thậm chí gu âm nhạc cũng y hệt, nên tôi tự hỏi có phải những người làm thư viện retro đều có cùng sở thích như vậy không
Thiết kế phẳng trông lố bịch nhưng vẫn giúp ai đó kiếm được rất nhiều tiền, rồi mọi người đều làm theo như bầy cừu
Việc gần 20 năm trôi qua mà vẫn chưa thoát khỏi nó giờ cũng chẳng còn đáng ngạc nhiên, và cách UI ngày xưa xử lý tab nhiều dòng một cách tự nhiên thực sự rất tuyệt
Chỉ là triết lý càng ít càng tốt xung đột với kiểu UI giống đất nặn đồ chơi bão hòa quá mức của Apple, Google và những kẻ bắt chước họ
Chúng chiếm mất không gian dọc quý giá, và mỗi lần nhấp thì vị trí các dòng lại đổi chỗ gây rối; nếu là nút biểu thị trạng thái thì sau khi nhấp không nên di chuyển
Chúng ta có màn hình và card đồ họa hiển thị được hàng triệu màu mà lại không tận dụng; không cần làm mọi thứ như tranh tô màu, nhưng Windows 10 và 11 tạo cảm giác u ám
Điều luôn khiến tôi khó chịu trong hệ 98.css là MS Sans Serif hơi sai lệch một chút, nên cuối cùng tôi quyết định tự sửa
https://github.com/jdan/98.css/pull/230
Dự án này trước đây cũng đã nhiều lần nổi lên: năm 2024 đạt 471 điểm, 93 bình luận https://news.ycombinator.com/item?id=42056918, năm 2022 đạt 606 điểm, 182 bình luận https://news.ycombinator.com/item?id=33310554, năm 2020 đạt 838 điểm, 211 bình luận https://news.ycombinator.com/item?id=22940564
Windows 95: https://alexbsoft.github.io/win95.css/, website thập niên 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/
Muốn làm một trang web dùng kiểu giao diện này nhưng không nghĩ ra nó sẽ hợp với loại nào
XFCE-winxp-tc cũng rất tuyệt, dùng để làm Linux trông như Windows XP. Chicago95 thì khá nổi tiếng, nhưng nếu muốn không khí năm 2004 thì https://github.com/rozniak/xfce-winxp-tc rất phù hợp
Không rõ có phải vì lo ngại pháp lý hay không
Cách bỏ hẳn nút bị vô hiệu hóa và không hiển thị nó là một trong những xu hướng UI gây khó hiểu nhất hiện nay, và nhìn thấy nút màu xám lại khiến tôi yên tâm hơn
Chỉ nên vô hiệu hóa khi người dùng kỳ vọng nó có thể áp dụng và có thể dễ dàng suy ra lý do bị vô hiệu hóa; nếu không có cách nào để kích hoạt hoặc người dùng không kỳ vọng nó sẽ áp dụng thì nên bỏ đi. Nếu khó hiểu được lý do thì tốt hơn nên để nó ở trạng thái hoạt động và hiển thị thông báo lỗi hữu ích khi dùng sai
https://learn.microsoft.com/en-us/windows/win32/uxguide/top-...
Nhưng trong hướng dẫn hiện đại https://learn.microsoft.com/en-us/windows/apps/develop/ui/co..., không chỉ sự phân biệt tinh tế này mà ngay cả bản thân nút bị vô hiệu hóa cũng không được đề cập
Ngược lại, nếu có quyền thực hiện nhưng hiện tại không thể do chế độ hoặc trạng thái, thì phải hiển thị nút màu xám. Trong biểu mẫu, ngay cả nút màu xám cũng cần cho bấm và phải cho biết có vấn đề gì. Ở một hãng hàng không lớn, vì không có hướng dẫn như vậy nên tôi đã không thể hoàn tất check-in
Tooltip khi rê chuột và biểu tượng
?nhỏ đã biến mất, và dường như mọi phần mềm đều giả định người dùng đã biết cách dùng ngay từ ngày đầuhttps://ka.ge là một trang cá nhân làm bằng 98.css, tái hiện môi trường desktop Windows 98 chính xác đến từng pixel và hoạt động thật sự, với dung lượng dưới 1MB
Hơi tiếc là các tab trong ví dụ tab nhiều dòng không tự đổi vị trí ngẫu nhiên khi bấm như Windows gốc
Mỗi phiên bản hệ điều hành mới, các nhà thiết kế UI đều phải đóng gói thành cải tiến cả những thay đổi vốn không cần thiết về mặt chức năng, tốn tài nguyên hơn, chậm hơn và kém ổn định hơn
less fasterlàm tôi có cảm giác như chính những nhà thiết kế UI đó cũng đã động vào bình luận nàyWindows 98 UI cũng đâu phải đỉnh cao của trải nghiệm người dùng, nên khó hiểu vì sao lại tập trung vào những nỗ lực tái hiện kiểu này
Sự cồng kềnh ngày nay là kết quả của công nghệ và các lớp trừu tượng chồng chất, nên điều cốt lõi là hiểu những yếu tố cần thiết cho trải nghiệm người dùng tốt và tìm ra cấu phần công nghệ tối thiểu
Góc bo tròn và khoảng đệm lơ lửng là những ví dụ tiêu biểu, còn ảnh hưởng từ macOS và các nhà thiết kế web đã làm hại tính khả dụng