1P by GN⁺ | ★ favorite | 댓글 1개
  • 98.css는 의미론적 HTML과 접근성을 바탕으로 Windows 98처럼 보이는 웹 인터페이스를 구현하는 CSS 디자인 시스템임
  • JavaScript 없이 HTML에 스타일만 적용하므로 React와 바닐라 JavaScript를 비롯한 다양한 프런트엔드 환경에서 사용할 수 있음
  • 버튼, 입력창, 선택 컨트롤부터 창·탭·트리·표·진행 표시줄까지 폭넓은 Windows 98 UI 구성요소를 제공함
  • 레이블 연결과 aria-label, ARIA 역할을 지원하며, 기본 외형을 유지하면서 여백이나 색상 같은 스타일도 재정의할 수 있음
  • unpkg, npm, GitHub Releases로 설치할 수 있고 MIT 라이선스로 배포돼 수정과 오픈소스 기여가 가능함

프로젝트 구성과 설치

  • 98.cssWindows 98 인터페이스를 충실하게 재현하는 CSS 라이브러리임
  • 의미론적 HTML을 기반으로 버튼에는 <button>, 입력 요소에는 <label>, 아이콘 버튼에는 aria-label을 사용함
  • 주요 목표에 접근성이 포함되며 키보드 탐색과 보조 기술 사용을 고려함
  • JavaScript 없이 CSS로만 HTML을 꾸미므로 특정 프런트엔드 프레임워크에 종속되지 않음
  • 가장 빠른 사용법은 unpkg에서 스타일시트를 불러오는 것임
<link rel="stylesheet" href="https://unpkg.com/98.css">;
npm install 98.css
  • 기본 외형을 유지하면서 버튼 여백이나 입력 레이블 색상 같은 개별 스타일을 자유롭게 재정의할 수 있음

기본 선택 컨트롤

  • 버튼

    • 표준 버튼은 75×23px 크기이며 돌출된 외부·내부 테두리와 기본 12px 수평 여백을 가짐
    • <button>, input[type=submit], input[type=reset]을 지원함
    • default 클래스는 활성 창에서 Enter 키를 눌렀을 때 실행할 기본 동작을 나타냄
    • 클릭하면 돌출된 테두리가 들어간 형태로 바뀌며, 비활성 버튼은 테두리를 유지한 채 레이블이 흐려짐
    • 포커스는 버튼 콘텐츠 안쪽 4px 위치의 점선 테두리로 표시됨
  • 체크박스와 라디오 버튼

    • 체크박스는 독립적이거나 비배타적인 선택을 나타내며, 선택되면 들어간 패널 안에 체크 아이콘이 표시됨
    • 체크박스 뒤에는 입력의 id를 가리키는 for 속성을 가진 <label>이 반드시 필요함
    • 이 연결을 통해 보조 기술이 입력을 인식할 수 있음
    • Tab 키 탐색과 레이블 전체 클릭을 지원함
    • 표준 checked, disabled 속성으로 선택 및 비활성 상태를 지정함
    • 여러 입력은 field-row 컨테이너로 감싸 일관된 간격을 적용함
    • 라디오 버튼은 제한된 집합에서 하나만 선택하는 컨트롤이며 input[type=radio]로 생성함
    • 같은 name 속성을 지정해 상호 배타적 그룹으로 묶고, 체크박스와 마찬가지로 레이블 및 field-row를 사용함
  • 그룹 상자

    • fieldset은 여러 컨트롤을 직사각형 프레임으로 묶고, 들어간 외부 테두리와 돌출된 내부 테두리로 새겨진 상자 같은 모습을 만듦
    • 내부에 legend를 배치하면 그룹 레이블을 표시할 수 있음

텍스트 입력과 값 선택

  • 텍스트 상자

    • 한 줄 입력은 input[type=text], 여러 줄 입력은 textarea 로 구성함
    • 입력과 올바르게 연결된 레이블을 field-row로 함께 감싸 가로 배치함
    • field-row-stacked를 사용하면 레이블을 입력 요소 위에 세로로 배치할 수 있음
    • 표준 HTML의 disabled, value 속성으로 비활성 상태와 초기값을 지정함
  • 슬라이더

    • 슬라이더는 조정 범위와 현재 값을 나타내며 input[type=range]로 렌더링함
    • has-box-indicator 클래스는 기본 표시기를 상자형 표시기로 교체함
    • 슬라이더를 is-vertical 클래스가 적용된 div로 감싸면 세로 방향으로 표시할 수 있음
    • 세로 슬라이더 길이는 input의 너비와 컨테이너 div의 높이로 조정함
  • 드롭다운

    • selectoption으로 목록에서 하나의 항목을 선택하는 드롭다운을 만듦
    • 첫 번째 항목이 기본 선택되며, 다른 optionselected 속성을 지정해 초기 선택을 바꿀 수 있음

Windows 98 스타일 창

  • 제목 표시줄

    • 제목 표시줄은 창 상단 전체 너비에 배치돼 내용을 식별하고 일반적인 창 명령 버튼을 포함함
    • title-bar, title-bar-text, title-bar-controls 세 클래스로 구성함
    • 명령 버튼의 aria-label에는 Minimize, Maximize, Restore, Help, Close를 사용할 수 있음
    • aria-label은 버튼의 의도를 보조 기술에 전달하면서 명령에 맞는 아이콘 스타일도 적용함
    • 별도의 minimize, maximize, restore, help, close 클래스도 제공함
    • aria-label 텍스트와 시각적 스타일을 분리해 다국어·현지화 과정에서 렌더링이나 접근성 문제가 생기지 않도록 함
    • 최대화할 수 없는 창은 Maximize 버튼에 disabled를 지정함
    • title-bar inactive로 여러 창 가운데 비활성 상태인 제목 표시줄을 표현함
  • 창 본문과 상태 표시줄

    • window 클래스는 돌출된 외부·내부 테두리와 여백을 제공하며, 컨테이너 너비를 지정해 창 크기를 바꿀 수 있음
    • 제목 표시줄 아래의 실제 콘텐츠는 window-body 에 배치함
    • 창 아래쪽 상태 표시줄은 현재 상태나 키보드 상태 같은 문맥 정보를 보여줌
    • 전체 컨테이너에는 status-bar, 각 텍스트 영역에는 status-bar-field를 사용함
    • 상태 표시줄을 활용해 Task Manager, Notepad, File Explorer 같은 인터페이스를 구성할 수 있음

계층·탭·표 인터페이스

  • 트리 뷰

    • ul.tree-view는 객체 간 논리적 계층을 들여쓰기된 목록으로 표현함
    • 하위 ul을 중첩하면 점선 테두리와 들여쓰기가 적용되며, 내부 li에는 원하는 콘텐츠를 넣을 수 있음
    • 펼치고 접을 수 있는 계층은 자식 목록을 details 요소 안에 배치해 구성함
    • 탭 목록은 menu[role=tablist], 각 탭은 li[role=tab]로 작성함
    • 활성 탭에는 aria-selected="true"를 지정함
    • 98.css에는 탭 전환 로직이 없으므로 상태 변경은 사용자 JavaScript로 관리해야 함
    • 탭 콘텐츠는 role="tabpanel"이 지정된 창 영역에 배치함
    • menumultirows 클래스를 추가하면 여러 행의 탭을 만들 수 있음
  • 표 뷰

    • HTML tablediv.sunken-panel로 감싸 적절한 테두리와 스크롤 가능한 영역을 만듦
    • table.interactive는 본문 행 위에 마우스를 올렸을 때 포인터 커서를 표시함
    • 선택된 행에는 highlighted 클래스를 적용하며, 실제 행 선택 동작은 별도 스크립트로 구현함

진행 상태와 범용 테두리

  • 진행 표시줄

    • 긴 작업의 완료 비율은 progress-indicator 안에 progress-indicator-bar를 배치하고 너비로 지정함
    • 기본 형태는 단색 막대이며, segmented 클래스를 추가하면 분할형 진행 표시줄로 바뀜
  • 필드 테두리

    • 텍스트 상자, 체크박스, 드롭다운, 스핀 상자, 목록 상자는 들어간 외부·내부 테두리의 필드 스타일을 사용함
    • 일반 필드는 내부에 버튼 강조 색상을 사용하고, 읽기 전용 또는 비활성 텍스트 필드는 버튼 표면 색상을 사용함
    • 상태 필드에는 들어간 외부 테두리만 적용하며, 상태 표시줄이나 동적으로 바뀌는 읽기 전용 콘텐츠에 활용함
    • 폼 이외의 작업 영역과 동적 콘텐츠에도 사용할 수 있도록 세 가지 범용 클래스를 제공함
      • field-border: 일반 작업 영역
      • field-border-disabled: 비활성 작업 영역
      • status-field-border: 동적 상태 콘텐츠
    • 이 클래스들은 테두리, 배경색, 최소 여백만 정의하므로 보통 추가 padding 을 직접 지정해야 함

라이선스와 기여

  • 98.css는 MIT 라이선스로 배포됨
  • 버그 확인과 신고는 GitHub Issues에서 할 수 있음
  • 풀 리퀘스트를 받고 코드 리뷰를 제공하며, 특히 오픈소스에 처음 기여하는 참여자를 환영함

댓글과 토론

Hacker News 의견들
  • 이 프로젝트는 번아웃 회복 과정에서 만든 것으로, 지금도 각별하게 아끼고 있음
    https://notes.jordanscales.com/98-css-reflections

    • 어릴 때 SimGolf에 정말 많은 시간을 썼는데 몇 년간 잊고 있다가 오랜만에 향수를 느꼈음
    • 작은 웹사이트 https://www.mathsheets.net/에 완벽하게 어울렸음
      사용성과 향수의 균형이 절묘해서 다른 프로젝트에도 더 활용하고 싶어짐
    • https://chiptune.appWinamp 테마를 만들 때 98.css를 출발점으로 삼았으며, 수정하고 확장하기가 매우 쉬웠음
    • 지난 몇 달간 BeOS에서 영감을 받은 복고풍 CSS 라이브러리 https://beta.mtechnic.me를 만들고 있음
      조사하다가 98.css 제작자의 포트폴리오도 발견했는데 음악 취향까지 완전히 같아서, 복고풍 라이브러리를 만드는 이들이 모두 비슷한 취향인지 궁금해졌음
    • 98.css를 오래전부터 직접 개발하지 않고, 저장소에서 양질의 풀 리퀘스트만 살펴본 뒤 작성자가 사용자 컴퓨터에서 암호화폐를 채굴할 사람은 아닌지 확인하고 전체 커밋 권한을 준다는 관리 방식이 멋짐
  • 플랫 디자인은 우스꽝스러운데도 누군가에게 큰돈을 벌어준 뒤 모두가 양 떼처럼 따르고 있음
    거의 20년이 지나도록 벗어나지 못한 것도 이제 놀랍지 않으며, 예전 UI에서 여러 줄 탭이 자연스럽게 작동하던 방식은 정말 훌륭했음

    • 플랫 또는 준플랫 디자인은 그림자·윤곽선·대비 같은 시각적 구분 요소를 적절히 쓰면 훌륭함
      적을수록 좋다는 철학이 Apple·Google과 그 모방자들의 과포화된 장난감 점토 같은 UI와 충돌할 뿐임
    • 페이지에서 무언가를 찾는 시간이 길어지면 기업이 중시하는 페이지 체류 시간이 늘어나니 지금 같은 결과가 나온 셈임
    • Win9x와 3.x에는 버튼 가장자리, 창 테두리, 탭, 텍스트 상자에 미묘한 입체 단서와 그라데이션이 있었지만 시간이 흐르며 계속 평평해졌고, 이제는 잃어버린 기술처럼 보임
    • 예전 UI에서 가장 싫었던 요소가 여러 줄 탭이었음
      귀중한 세로 공간을 차지하고 클릭할 때마다 줄의 위치가 바뀌어 혼란스러웠으며, 상태를 나타내는 버튼이라면 클릭 후 움직여서는 안 됨
    • 플랫 디자인뿐 아니라 색상 사용까지 줄어들었음
      수백만 색상을 표시할 모니터와 그래픽카드를 갖고도 활용하지 않으며, 색칠 공부처럼 만들 필요는 없지만 Windows 10과 11은 우울하게 느껴짐
  • 98.css 계열에서 늘 거슬렸던 부분은 MS Sans Serif가 미묘하게 부정확하다는 것이었고, 결국 직접 수정하기로 했음
    https://github.com/jdan/98.css/pull/230

  • 이 프로젝트는 이전에도 여러 차례 인기를 끌었음: 2024년 471점·댓글 93개 https://news.ycombinator.com/item?id=42056918, 2022년 606점·댓글 182개 https://news.ycombinator.com/item?id=33310554, 2020년 838점·댓글 211개 https://news.ycombinator.com/item?id=22940564

  • 이 스타일을 적용할 웹사이트를 만들고 싶지만 어디에 어울릴지 떠오르지 않음
    Linux를 Windows XP처럼 꾸며 주는 XFCE-winxp-tc도 훌륭함. Chicago95는 많이 알려졌지만 2004년 분위기를 원한다면 https://github.com/rozniak/xfce-winxp-tc가 잘 맞음

    • 인터넷에서는 어울릴 이유가 필요 없고, 하고 싶다는 마음만 있으면 됨
    • XFCE·GTK 테마들은 픽셀 단위로 정확하다고 내세워도 실제로는 그렇지 않다는 점이 늘 불만이었음
      법적 우려 때문에 그런지는 모르겠음
  • 비활성화된 버튼을 없애고 아예 표시하지 않는 방식은 오늘날 가장 혼란스러운 UI 추세 중 하나이며, 회색 버튼을 보면 오히려 마음이 편해짐

    • Microsoft의 예전 지침은 비활성화된 컨트롤을 세밀하게 다뤘음
      사용자가 적용 가능하다고 예상하고 비활성화 이유를 쉽게 추론할 수 있을 때만 끄고, 활성화할 방법이 없거나 적용될 것으로 예상하지 않는다면 제거해야 함. 이유를 쉽게 알기 어렵다면 활성 상태로 두고 잘못 사용했을 때 유용한 오류 메시지를 보여주는 편이 나음
      https://learn.microsoft.com/en-us/windows/win32/uxguide/top-...
      하지만 현대 지침 https://learn.microsoft.com/en-us/windows/apps/develop/ui/co...에서는 이런 미묘한 구분은 물론 비활성화 버튼 자체도 다루지 않음
    • 관리자 권한이 없거나 보기 권한만 있는 것처럼 사용자가 애초에 실행할 수 없는 기능은 버튼을 숨기는 편이 맞음
      반면 실행 권한은 있지만 현재 모드나 상태 때문에 불가능하다면 회색 버튼을 보여줘야 함. 폼에서는 회색 버튼도 클릭할 수 있게 하고 무엇이 잘못됐는지 알려줄 필요가 있음. 한 대형 항공사에서는 이런 안내가 없어 체크인을 완료하지 못했음
      마우스 올림 도움말과 작은 ? 표시는 사라졌고, 모든 소프트웨어가 첫날부터 사용법을 안다고 가정하는 듯함
  • https://ka.ge는 98.css로 만든 개인 사이트로, 1MB 미만에서 Windows 98 데스크톱 환경을 픽셀 단위로 정확하고 실제 작동하게 재현했음

  • 여러 줄 탭 예제의 탭이 클릭할 때 원래 Windows처럼 무작위로 위치를 바꾸지 않는 것은 아쉬움

  • 운영체제 새 버전마다 UI 디자이너는 기능적으로 불필요하고 자원을 더 쓰며 느리고 불안정한 변경도 개선이라고 포장해야 했음

    • less faster라는 표현을 보니 그 UI 디자이너들이 이 댓글에도 손댄 듯함
  • Windows 98 UI가 사용자 경험의 정점도 아닌데 이런 재현 작업에 집중하는 이유를 이해하기 어려움
    오늘날의 비대함은 기술과 추상화 계층이 겹겹이 쌓인 결과이므로, 좋은 사용자 경험에 필요한 요소를 이해하고 최소한의 기술 구성요소를 찾는 것이 핵심임

    • 정확히는 Windows 2000이 UI 디자인의 정점이었고 이후 계속 나빠졌음
      둥근 모서리와 떠 있는 여백이 대표적이며, macOS와 웹 디자이너의 영향은 사용성을 해쳤음