主题切换是网站设计中一个非常有趣的功能,它允许用户在多种预先设计的样式之间轻松切换,以改变网站的视觉表现。最常见的就是白天和黑夜主题的切换,用户可以根据自己的喜好进行设置。
除了让用户手动去切换主题外,如果能够让用户第一次访问时,网站就设置为用户系统的主题样式,这无疑是一种更好的体验。
主题切换
CSS主题切换有多种方式实现,这里就简单描述下,不是本文重点
方式1:通过自定义标签属性来实现主题切换
/* 默认主题样式 */
body {background-color: white;color: black;
}
/* 深色主题样式 */
body[data-theme="dark"] {background-color: black;color: white;
}
:root {--body-background: rgb(248, 244, 212);--text-color: #000;
}
html[data-theme="dark"] {--body-background: rgb(58, 70, 90);--text-color: #eee;
}
方式2:运行时动态地修改CSS变量的值,从而实现主题切换的效果
:root {--primary-color: #007bff;--secondary-color: #6c757d;--background-color: white;--text-color: black;
}
body {background-color: var(--background-color);color: var(--text-color);
}
document.documentElement.style.setProperty('--background-color', 'black');
document.documentElement.style.setProperty('--text-color', 'white');
方式3:使用类名切换,通过对顶层节点设置不同的类名,然后定义不同类名的CSS样式,切换主题时修改类名即可
.theme-light {background-color: white;color: black;
}
.theme-dark {background-color: black;color: white;
}
function switchTheme() {const bodyElement = document.body;if (bodyElement.classList.contains('theme-light')) {bodyElement.classList.remove('theme-light');bodyElement.classList.add('theme-dark');} else {bodyElement.classList.remove('theme-dark');bodyElement.classList.add('theme-light');}
}
方式4:link标签动态引入
function switchTheme(theme) {const linkElement = document.createElement('link');linkElement.rel = 'stylesheet';
if (theme === 'light') {linkElement.href = 'theme-light.css'; // 切换为浅色主题} else {linkElement.href = 'theme-dark.css'; // 切换为深色主题}
document.head.appendChild(linkElement);
}
扫码加入前端交流群,期待你的加入!
CSS媒体查询
CSS媒体查询是实现响应式网页设计的重要工具,它允许根据设备的特定特性来应用不同的样式规则。
使用 @media
规则可以实现这一功能,通过这个规则可以定义一个或多个条件,当这些条件满足时,相应的样式代码块将会被应用到文档上。例如,在屏幕宽度小于或等于768像素时,背景颜色可以设置为浅蓝色
/* 基础样式 */
body {background-color: lightblue;color: white;font-size: 16px;padding: 20px;
}
/* 当屏幕宽度小于或等于768像素时,应用以下样式 */
@media (max-width: 768px) {body {background-color: red;color: black;font-size: 14px;padding: 10px;}
}
CSS媒体查询还可以检测用户是否有将系统的主题色设置为两色或者暗色
- light:表示用户已告知系统选择使用浅色主题界面
- dark:表示用户已告知系统选择使用暗色主题界面
.day {background: #eee;color: black;
}
.night {background: #333;color: white;
}
@media (prefers-color-scheme: dark) {.day.dark-scheme {background: #333;color: white;}.night.dark-scheme {background: black;color: #ddd;}
}
@media (prefers-color-scheme: light) {.day.light-scheme {background: white;color: #555;}.night.light-scheme {background: #eee;color: black;}
}
这种方式的确可以实现主题跟随系统的变换而变换,但是存在以下缺点:
-
增加工作量:开发者需要编写更多的CSS代码,这可能会导致工作效率降低
-
加载时间延长:随着CSS代码量的增加,页面的加载时间可能会变长,尤其是对于那些包含大量媒体查询的复杂样式表
-
用户无法自定义:样式固定,用户无法自定义设置主题样式
JS媒体查询
JS同样拥有媒体查询的方法 matchMedia()。传入一个被用于媒体查询解析的字符串,返回一个用来媒体查询新的 MediaQueryList 对象,其中的 matchs 属性值就是匹配结果。
// 如果视口的宽度小于或等于600像素,则输出为true
const mql = window.matchMedia('(max-width: 600px)');
console.log(mql.matchs)
同样的也可以用来查询系统是否使用了暗色主题
const osThemeIsDark = matchMedia("(prefers-color-scheme: dark)").matches;
接下来就采用上面方式1的主题切换方案,结合JS媒体查询来实现跟随系统主题切换的功能。
:root {--body-background: rgb(248, 244, 212);--text-color: #000;
}
html[data-theme="dark"] {--body-background: rgb(58, 70, 90);--text-color: #eee;
}
body {padding-top: 200px;height: 100vh;width: 100vw;margin: 0;background: var(--body-background);text-align: center;color: var(--text-color);
}
<body><div><select><option value="light">白天模式</option><option value="dark">黑夜模式</option><option value="os">跟随系统</option></select></div><h1>公众号:前端筱园</h1><p>www.dengzhanyong.com</p>
</body>
const select = document.querySelector('select');
const html = document.querySelector("html");
// 获取用户设置的主题,默认是跟随系统
const localTheme = localStorage.getItem('theme') || 'os';
settingTheme(localTheme);
// 设置select选中的值
select.value = localTheme;
select.onchange = (e) => {
const theme = e.target.value;settingTheme(theme);localStorage.setItem('theme', theme)
}
function settingTheme(theme) {// 如果是跟随系统,就获取系统的主题
if (theme === 'os') {
const osThemeIsDark = matchMedia("(prefers-color-scheme: dark)").matches;html.dataset.theme = osThemeIsDark ? 'dark' : 'light';} else {html.dataset.theme = theme;}
}
监听媒体变化
现在还有一个问题,如果页面已经打开的情况下,再去修改系统主题,是否能检测到系统主题的变化,使得网页在不刷新的情况下自动切换。
答案是可以的,可以通过监听 MediaQueryList
的 change
事件,当 matches
的值发生变化时会触发。
matchMedia("(prefers-color-scheme: dark)").addEventListener("change", event => {html.dataset.theme = event.matches ? 'dark' : 'light';
})
利用媒体查询还可以检测很多内容,比如:浏览器可视区域尺寸、设备尺寸、设备目前处于横向还是纵向、检测设备宽高比、设备颜色位数等
写在最后
欢迎访问我的个人网站:www.dengzhanyong.com
【前端筱园交流群】已经正式开通啦!在这里,你可以与一群志同道合的小伙伴们交流、分享、学习、共同成长,扫码加入,期待你的加入!
关注我的公众号【前端筱园】,不错过每一篇推送