Bộ chuyển đổi giao diện tối
Là một người cực kỳ yêu thích “giao diện tối”, hay còn gọi là “chế độ tối”, tôi đã triển khai nó trên hầu hết các ứng dụng và trang web mà tôi tự xây dựng cho mình — bắt đầu từ blog này cho đến dự án mới nhất của tôi, It Changed.
Tôi không hài lòng với các giải pháp tìm thấy trên mạng vì chúng không kết hợp giữa một công tắc chuyển đổi và media queries. Tại sao điều này lại quan trọng? Trước hết, người dùng có thể đã có sẵn tùy chọn được thiết lập từ trước. Ví dụ, trên macOS, nếu bạn chọn System Settings >> Appearance >> Dark, trình duyệt sẽ gửi (prefers-color-scheme: dark) khi người dùng truy cập một trang web. Tuy nhiên, cũng có những trường hợp người dùng muốn chuyển giao diện sang sáng cho một trang web cụ thể.
Để mang lại trải nghiệm người dùng (UX) tốt nhất, nó bắt buộc phải là sự kết hợp của nhiều giải pháp — và như bạn sẽ thấy, việc triển khai điều này không hề khó.
Vậy nên, hãy bắt đầu thôi.
Triển khai
Trước tiên, chúng ta cần một loại công tắc nào đó. Tôi rất thích dùng checkbox cho mục đích gỡ lỗi (debugging). Đây là một ví dụ đơn giản:
<label type="button">
<input type="checkbox" name="theme-switch" class="theme-switch"/>
<span>switch</span>
</label>
Bây giờ hãy chuyển sang phần JavaScript:
const themeSwitches = document.querySelectorAll('.theme-switch');
if (themeSwitches.length > 0) {
themeSwitches.forEach((themeSwitch, i) => {
if (localStorage.getItem('dark-mode') === 'true') {
themeSwitch.checked = true;
} else {
themeSwitch.checked = false;
}
themeSwitch.addEventListener('change', () => {
const { checked } = themeSwitch;
themeSwitches.forEach((el, n) => {
if (n !== i) {
el.checked = checked;
}
});
if (themeSwitch.checked) {
document.documentElement.classList.add('dark');
localStorage.setItem('dark-mode', true);
} else {
document.documentElement.classList.remove('dark');
localStorage.setItem('dark-mode', false);
}
});
});
}
Đoạn code này xử lý toàn bộ logic của công tắc chuyển đổi và lưu trạng thái của nó vào localStorage. Vì công tắc chuyển đổi chỉ có thể truy cập được sau khi trang đã tải xong, nên đoạn code này không cần chạy ngay lập tức. Một cách làm tốt là đặt nó trong file JavaScript của bạn để nó được thực thi sau (deferred).
Tuy nhiên, có một phần code thực sự cần được thực thi càng sớm càng tốt. Đây là đoạn code đó:
<script>
if (localStorage.getItem('dark-mode') === 'true' || (!('dark-mode' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.querySelector('html').classList.add('dark');
} else {
document.querySelector('html').classList.remove('dark');
}
</script>
Đoạn code này làm gì:
-
Kiểm tra xem chế độ tối đã được thiết lập trong
localStoragehay chưa; -
Nếu
localStoragekhông chứa cài đặt chế độ tối, nó sẽ kiểm tra xem người dùng có ưa thích chế độ tối hay không.
Điều này đảm bảo:
- Người dùng sẽ nhận được chế độ tối nếu họ đã bật thủ công trong những lần truy cập trước đó.
- Người dùng sẽ nhận được chế độ tối nếu cài đặt hệ thống của họ cho thấy họ ưa thích điều đó.
Thiết kế
Vì chúng ta đã triển khai công tắc chuyển đổi dưới dạng checkbox, nên nó mở ra rất nhiều khả năng thiết kế. Nó có thể hoạt động như một toggle, giữ nguyên dạng checkbox, hoặc biến thành một nút bấm — các lựa chọn gần như là vô hạn. Chính vì sự linh hoạt này, tôi sẽ không đưa ra một giải pháp thiết kế duy nhất ở đây. Hãy chọn phương án phù hợp nhất với trường hợp sử dụng của bạn.
Liên kết
Dưới đây là một vài liên kết hữu ích:
- Tôi đã công bố đoạn code này dưới dạng gói Composer trên Packagist.
- Nếu bạn muốn đóng góp, hãy xem qua dự án trên GitHub.
Vậy là xong rồi đó :)