网页主题自动适配:网页跟随系统自动切换主题

主题切换是网站设计中一个非常有趣的功能,它允许用户在多种预先设计的样式之间轻松切换,以改变网站的视觉表现。最常见的就是白天和黑夜主题的切换,用户可以根据自己的喜好进行设置。

除了让用户手动去切换主题外,如果能够让用户第一次访问时,网站就设置为用户系统的主题样式,这无疑是一种更好的体验。

主题切换

CSS主题切换有多种方式实现,这里就简单描述下,不是本文重点

方式1:通过自定义标签属性来实现主题切换

css 复制代码
/* 默认主题样式 */
body {
  background-color: white;
  color: black;
}
​
/* 深色主题样式 */
body[data-theme="dark"] {
  background-color: black;
  color: white;
}
css 复制代码
: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变量的值,从而实现主题切换的效果

css 复制代码
:root {
  --primary-color: #007bff;
  --secondary-color: #6c757d;
  --background-color: white;
  --text-color: black;
}
​
body {
  background-color: var(--background-color);
  color: var(--text-color);
}
javascript 复制代码
document.documentElement.style.setProperty('--background-color', 'black');
document.documentElement.style.setProperty('--text-color', 'white');

方式3:使用类名切换,通过对顶层节点设置不同的类名,然后定义不同类名的CSS样式,切换主题时修改类名即可

css 复制代码
.theme-light {
  background-color: white;
  color: black;
}
​
.theme-dark {
  background-color: black;
  color: white;
}
javascript 复制代码
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标签动态引入

javascript 复制代码
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像素时,背景颜色可以设置为浅蓝色

css 复制代码
/* 基础样式 */
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:表示用户已告知系统选择使用暗色主题界面
css 复制代码
.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 属性值就是匹配结果。

javascript 复制代码
// 如果视口的宽度小于或等于600像素,则输出为true
const mql = window.matchMedia('(max-width: 600px)');
console.log(mql.matchs)

同样的也可以用来查询系统是否使用了暗色主题

javascript 复制代码
const osThemeIsDark = matchMedia("(prefers-color-scheme: dark)").matches;

接下来就采用上面方式1的主题切换方案,结合JS媒体查询来实现跟随系统主题切换的功能。

css 复制代码
: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);
}
css 复制代码
<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>
javascript 复制代码
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;
    }
}

监听媒体变化

现在还有一个问题,如果页面已经打开的情况下,再去修改系统主题,是否能检测到系统主题的变化,使得网页在不刷新的情况下自动切换。

答案是可以的,可以通过监听 MediaQueryListchange 事件,当 matches 的值发生变化时会触发。

javascript 复制代码
matchMedia("(prefers-color-scheme: dark)").addEventListener("change", event => {
    html.dataset.theme = event.matches ? 'dark' : 'light';
})

利用媒体查询还可以检测很多内容,比如:浏览器可视区域尺寸、设备尺寸、设备目前处于横向还是纵向、检测设备宽高比、设备颜色位数等

写在最后

欢迎访问我的个人网站:www.dengzhanyong.com

前端筱园交流群 】已经正式开通啦!在这里,你可以与一群志同道合的小伙伴们交流、分享、学习、共同成长,扫码加入,期待你的加入!

关注我的公众号【前端筱园】,不错过每一篇推送

相关推荐
sinat_384241091 分钟前
在有网络连接的机器上打包 electron 及其依赖项,在没有网络连接的机器上安装这些离线包
javascript·arcgis·electron
小牛itbull25 分钟前
ReactPress vs VuePress vs WordPress
开发语言·javascript·reactpress
请叫我欧皇i33 分钟前
html本地离线引入vant和vue2(详细步骤)
开发语言·前端·javascript
533_36 分钟前
[vue] 深拷贝 lodash cloneDeep
前端·javascript·vue.js
GIS瞧葩菜1 小时前
局部修改3dtiles子模型的位置。
开发语言·javascript·ecmascript
zhang-zan1 小时前
nodejs操作selenium-webdriver
前端·javascript·selenium
ZBY520311 小时前
【Vue】 npm install amap-js-api-loader指南
javascript·vue.js·npm
前端拾光者2 小时前
利用D3.js实现数据可视化的简单示例
开发语言·javascript·信息可视化
木子02043 小时前
前端VUE项目启动方式
前端·javascript·vue.js
endingCode3 小时前
45.坑王驾到第九期:Mac安装typescript后tsc命令无效的问题
javascript·macos·typescript