10 điểm bởi GN⁺ 2026-04-20 | 1 bình luận | Chia sẻ qua WhatsApp
  • Tóm tắt 8 cấp độ giúp mở rộng dần phạm vi triển khai dark mode, từ tính năng mặc định của trình duyệt đến media query JavaScript
  • Cách đơn giản nhất là chỉ cần khai báo <meta name="color-scheme" content="light dark"> hoặc color-scheme: light dark để làm theo tùy chọn color scheme của người dùng
  • Ở các cấp độ cao hơn, có thể dùng hàm light-dark(), @media (prefers-color-scheme: dark), và stylesheet tách riêng theo từng scheme để điều chỉnh rộng rãi không chỉ màu sắc mà cả hình ảnh và đổ bóng
  • Có thể xây dựng bộ chuyển đổi không chỉ theo thiết lập hệ thống của người dùng mà còn cung cấp 3 lựa chọn Automatic·light·dark, đồng thời dùng :has() và chính phần tử meta thực tế để xác định theme
  • Bài viết cũng đề cập đến giới hạn về khả năng truy cập của Safari và quan sát cách prefers-color-scheme hoạt động khi in, cho thấy chỉ với các tính năng CSS gần đây cũng đã dễ tích hợp light/dark mode hơn nhiều

Triển khai dark mode theo từng cấp độ

  • Level 1: Barebone

    • Có thể kích hoạt phân biệt light/dark ngay cả khi không viết một dòng CSS nào; chỉ cần thêm <meta name="color-scheme" content="light dark"> trong phần head của tài liệu là trình duyệt sẽ bắt đầu làm theo tùy chọn color scheme của người dùng
    • Về mặt lý thuyết, thứ tự các mục trong thuộc tính content có ý nghĩa; người dùng không chỉ định tùy chọn color scheme sẽ nhận giá trị đầu tiên trong danh sách phân tách bằng khoảng trắng
    • Hiện tại không có tùy chọn trong hệ điều hành để không chọn color scheme, nên trên thực tế kết quả sẽ quy về scheme khớp với thiết lập hệ điều hành
    • Cũng có thể chỉ định một giá trị duy nhất trong content; khi đó scheme tương ứng sẽ bị áp dụng cưỡng bức mà không xét đến sở thích của người dùng
    • Thẻ meta này ở mức độ nào đó đóng vai trò là cách làm phía HTML tương ứng với phương pháp CSS ở cấp độ tiếp theo
  • Level 2: Basic

    • Trong CSS, có thể áp dụng phân biệt light/dark mode bằng khai báo html { color-scheme: light dark; }
    • Nếu DOM đã có sẵn thẻ meta thì không cần khai báo này; nếu có thể kiểm soát HTML thì nên dùng thẻ meta để trình duyệt biết chỉ thị từ trước khi phân tích CSS
    • Cả hai cách đều cho phép tận dụng kiểu mặc định của user agent và light/dark mode đi kèm trong đó
    • Nếu bổ sung CSS tại đây nhưng chủ yếu giới hạn vào việc dùng CSS system colors thì vẫn có thể tạo ra một thiết kế khá gọn gàng
    • Không giống thẻ meta luôn áp dụng cho toàn bộ tài liệu, khai báo CSS color-scheme còn có thể đặt ở vị trí khác ngoài phần tử gốc, và khác biệt này mở ra thêm khả năng tận dụng
  • Level 3: Benign

    • Có thể điều chỉnh light/dark mode đơn giản bằng hàm màu light-dark() của CSS được bổ sung tương đối gần đây
    • Trong ví dụ, nó được dùng như background-color: light-dark(black, white);color: light-dark(white, black);; đối số thứ nhất áp dụng cho light mode, còn đối số thứ hai áp dụng cho dark mode
    • Đối số có thể là màu thực tế hoặc cũng có thể là custom properties được diễn giải thành màu
    • Trong toàn bộ bài viết, tại thời điểm viết thì chỉ cấp độ này là hỗ trợ trình duyệt chưa đủ tốt
  • Level 4: Bold

    • Có thể triển khai chuyển đổi dark mode bằng media query truyền thống @media (prefers-color-scheme: dark)
    • Dù truy vấn light hay dark, cách này đều cho phép mức tùy biến cao nhất, không bị giới hạn ở việc chỉ đổi màu đơn thuần
    • Có thể điều chỉnh như giảm bão hòa ảnh bằng bộ lọc trong dark mode, hoặc thay box shadow bằng đường viền ngoài
    Quảng cáo
  • Level 5: Bisectional

    • Media query cũng có thể dùng trong HTML; có thể đặt vào thuộc tính media của phần tử link để tách stylesheet theo từng scheme
    • Ví dụ được đưa ra là nối light.cssdark.css lần lượt với prefers-color-scheme: lightprefers-color-scheme: dark
    • Nếu phạm vi tùy biến lớn thì cấu trúc file chuyên biệt sẽ phù hợp hơn, và trình duyệt có thể bỏ qua file CSS không khớp với truy vấn nên có thể giảm bớt một mục cần tải xuống
  • Level 6: Ballistic

    • Trong JavaScript, có thể dùng media query color scheme qua window.matchMedia('(prefers-color-scheme:dark)')
    • Sau khi truy vấn xem đó là light hay dark giống như các media query khác, có thể thực hiện xử lý mong muốn dựa trên kết quả
    • Trong triển khai thực tế, không nhất thiết phải bám vào duy nhất một kỹ thuật ở các cấp trên mà có thể kết hợp nhiều cách

Bộ chuyển đổi người dùng và các mẫu nâng cao

  • Level 7: Beyond

    • Không nhất thiết chỉ phụ thuộc vào tùy chọn hệ thống của người dùng; có thể xây dựng color scheme switcher
    • Bộ chuyển đổi này không phải boolean đơn giản mà cần có chế độ Automatic lấy prefers-color-scheme làm mặc định ban đầu
    • Khi đặt bộ chuyển đổi lên trên đó, người dùng có thể chọn một trong ba chế độ: Automatic, light, dark
  • Level 8: Beguiling

    • Khi triển khai bộ chuyển đổi Level 7, cách phổ biến thường là thêm lớp .dark hoặc thuộc tính như data-theme="dark" vào phần tử HTML
    • Thay vào đó, có thể dùng :has() để truy vấn trực tiếp sự hiện diện của <meta name="color-scheme" content="dark">
    • Trong ví dụ, dưới selector html:has(meta[name="color-scheme"][content="dark"]), các biến CSS như --color-bg, --color-text được đặt thành giá trị dành cho dark mode
    • Có thể xác định theme dựa trên phần tử meta thực tế mà không cần lớp hay thuộc tính dữ liệu riêng
    Quảng cáo

Thảo luận bổ sung và các quan sát

  • Quan sát từ CSS Naked Day

    • Sau khi bỏ style, ở gần như mọi trang đã ghé thăm đều dễ thấy sự thiếu vắng dark mode, và điều này dẫn đến việc phân chia các cấp độ dark mode
    • Khi xây dựng một trang mới từ đầu và viết style mới, tác giả cho biết với các tính năng CSS gần đây thì việc tích hợp sẵn light/dark mode đã trở nên rất dễ dàng
  • Vấn đề khả năng truy cập của Safari

    • Bài viết chỉ ra rằng cho đến tương đối gần đây, Safari không cung cấp màu liên kết dễ truy cập trong dark mode
    • Ở CSS Naked Day trước đó, tác giả đã phát hiện vấn đề này, gỡ thẻ meta và chỉ dùng light color scheme
    • Sau đó thẻ meta được thêm lại, nhưng tác giả nhận thức rằng với người dùng Safari phiên bản cũ có thể phát sinh suy giảm khả năng truy cập
    • Cũng xác nhận rằng trong dark mode của Safari, ô nhập văn bản thiếu đường viền hiển thị rõ ràng
    • Chỉ dựa vào style của user agent thì ngay cả khi dùng HTML ngữ nghĩa đúng cũng không thể bảo đảm khả năng truy cập đầy đủ, nên tác giả đang cân nhắc cách giữ lại đủ style cho cả CSS Naked Day trong tương lai
  • In ấn và điều kiện screen and

    • Trong ví dụ Bisectional, việc dùng screen and ... được giải thích là để loại trừ mục tiêu máy in
    • Với giả định có một stylesheet lõi không phụ thuộc theme hoặc stylesheet in chuyên dụng riêng, tác giả cho rằng nên tách an toàn vì dark mode trên máy in có thể tốn nhiều mực
    • Trong thử nghiệm thực tế, ngay cả khi bật dark mode hệ thống rồi in thì đầu ra vẫn chỉ là chữ đen trên giấy trắng, và tác giả quan sát thấy trình duyệt không áp dụng dark mode style đó khi in
    • Trong thử nghiệm bổ sung, ở phần xem trước khi in thì prefers-color-scheme luôn được báo là light; điều này được xác nhận trên Firefox và Chromium
    • Bài viết cũng có một câu đùa rằng sẽ thật tiếc nếu có máy in dùng giấy đen và mực trắng

1 bình luận

 
GN⁺ 2026-04-20
Ý kiến trên Hacker News
  • Nếu tùy biến nhiều thì dùng file riêng cũng hợp lý, nhưng phần giải thích rằng CSS không khớp với media query sẽ thậm chí không bị tải xuống thì theo tôi là không đúng với cách trình duyệt thực sự hoạt động. Theo kinh nghiệm của tôi, trình duyệt rốt cuộc vẫn tải xuống toàn bộ, chỉ khác ở mức độ ưu tiên
  • Tôi từng tự hỏi liệu đến giờ vẫn chưa có cách nào ngăn kiểu nháy lóe như flashbang xảy ra trong lúc chờ nội dung ban đầu từ máy chủ hay sao
    • Tôi thấy cách đặt background-color trong userContent.css của Firefox là khá ổn
    • Tôi chỉ cần giảm độ sáng màn hình và tắt dark mode thì hiện tượng flashbang biến mất. Thêm nữa là pin cũng dùng được lâu hơn
  • Tôi tưởng bài này sẽ nói về sở thích với độ đen của nền dark mode. Tôi cũng từng nghe nói màu đen thuần cho hiệu quả pin tốt hơn trên OLED, và tôi cũng biết có người thích màu xám bớt “đen kịt” hơn là đen hoàn toàn. Dù vậy, tôi không chắc có cần tới tận sáu cấp độ hay không, cảm giác những mức có thể nhận ra rõ rệt nhiều lắm cũng chỉ khoảng 3~4 mức
    • Tôi từng nghĩ giải pháp phổ quát hơn là chuẩn hóa khả năng tương thích với Reader Mode. Thay vì mỗi website phải giải bài toán n x m để chiều hết mọi sở thích của người dùng, sẽ tốt hơn nếu website chỉ hỗ trợ một dạng hiển thị nội dung đơn giản, còn trình duyệt đảm nhiệm phần thiết lập theo từng người dùng trên đó
    • Trên OLED thì tôi thiên về màu đen thuần. Tôi có cảm giác pixel càng ít sáng thì càng giảm áp lực burn-in, mà tuổi thọ vốn dĩ cũng có hạn nên về lâu dài tôi muốn dùng màn hình hơn 5 năm thay vì chỉ 2~3 năm
  • Với tôi thì cấp cao nhất là 9, hoặc là 0: просто tắt máy tính đi rồi đi ngủ
  • Tôi thích việc OP đã triển khai công tắc 3 trạng thái một cách tử tế
  • Tôi nghĩ sẽ thú vị hơn nếu khi cuộn xuống, các cấp độ được áp dụng động
    • Hoặc cũng được nếu ở những vị trí phù hợp trên trang, người đọc có thể tự chọn cấp độ
  • Theo tôi thì chẳng phải là 8 cấp độ sao
  • Đúng là cảm giác rất năm 2024
  • Trong tình huống này, thứ hiện ra trong đầu tôi dĩ nhiên là xkcd 3227