首页 web前端 css教程 在几分钟内构建一个黑暗模式切换(实际上有效)

在几分钟内构建一个黑暗模式切换(实际上有效)

Nov 10, 2024 am 03:01 AM

Build a Dark Mode Toggle in inutes (That Actually Works)

是否曾经花费数小时实现黑暗模式切换,却在页面刷新时闪烁着令人眼花缭乱的白色?或者更糟糕的是,它是否完全忽略了用户的系统偏好?是的,我也是。 ?

事情是这样的 - 黑暗模式不再只是一个流行的功能。随着越来越多的人在夜间编码(被指控有罪)和可访问性变得越来越重要,实施良好的黑暗模式实际上是现代网站或网络应用程序的必备条件。但要做好它可能会非常棘手。

好消息?在摸索各种实现并与 localStorage 进行斗争之后,我终于破解了黑暗模式切换的代码:

  • 实际上记住你的用户的偏好
  • 重新加载时不会闪烁错误的主题
  • 与系统偏好设置配合得很好
  • 实施只需 5 分钟

在这篇文章中,我将引导您构建一个您真正想要使用的黑暗模式切换开关。没有过于复杂的解决方案,没有不必要的依赖项 - 只有干净、可以立即实现的工作代码。

先决条件(你需要的东西)

让我们先把无聊的部分抛开 - 但我保证保持简短!

您可能已经拥有所需的一切,但只是为了确保我们达成共识:

  • 基本 HTML(例如,你知道
  • 一些 CSS 知识(尤其是 CSS 变量 - 但我会边做边解释)
  • Vanilla JavaScript(没什么花哨的,我保证)
  • 您最喜欢的代码编辑器
  • 大约 5 分钟的时间(也许还有一杯咖啡☕)

我们正在建设什么

在我们开始之前,先快速浏览一下我们最终会得到什么。没有花哨的 UI 库或复杂的设置 - 只是一个简单、流畅的切换,如下所示:

不要担心让它看起来像这样 - 重要的是它会完美地工作。我们将首先关注功能,然后您可以根据需要设计它的样式。

最好的部分?我们要构建的所有内容都适用于:

  • 现代浏览器(是的,甚至是 Safari!)
  • 系统深色模式偏好设置
  • 页面刷新(不再出现白屏闪烁)
  • 零外部依赖

准备好动手了吗?让我们从基础开始吧!

建立基金会

好吧,让我们动手吧!首先,我们将建立基本结构。

HTML:保持简单

我们将从一些极其简单的 HTML 开始。这部分不用想太多:

<button>



<h3>
  
  
  The CSS
</h3>

<p>Here's where things get interesting. We'll use CSS variables (aka custom properties) to handle our color scheme. Drop this in your CSS file:<br>
</p>

<pre class="brush:php;toolbar:false">:root {
  --background: #ffffff;
  --text-primary: #222222;
  --toggle-bg: #e4e4e7;
  --toggle-hover: #d4d4d8;
}


[data-theme="dark"] {
  --background: #121212;
  --text-primary: #ffffff;
  --toggle-bg: #3f3f46;
  --toggle-hover: #52525b;
}

body {
  background-color: var(--background);
  color: var(--text-primary);
  transition: background-color 0.3s ease, color 0.3s ease;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

.theme-toggle {
  border: none;
  padding: 0.5rem;
  border-radius: 9999px;
  background-color: var(--toggle-bg);
  cursor: pointer;
  transition: background-color 0.2s ease;
  align-self: flex-start;
  position: absolute;
  right: 20px;
}

.theme-toggle:hover {
  background-color: var(--toggle-hover);
}

.theme-toggle svg {
    transform-origin: center;
    transition: transform 0.3s ease;
}

.theme-toggle:active svg {
    transform: rotate(30deg);
}

h1 {
  display: flex;
}

.sun-icon {
  display: none;
  width: 24px;
  height: 24px;
}

.moon-icon {
  width: 24px;
  height: 24px;
}

[data-theme="dark"] .sun-icon {
  display: block;
}

[data-theme="dark"] .moon-icon {
  display: none;
}
登录后复制
登录后复制

专业提示:注意到我们如何使用数据主题而不是类?这使得该属性的用途非常清楚,并防止任何潜在的类命名冲突。

对于图标,您可以使用自己的 SVG 或从您最喜欢的图标库中获取一些。我喜欢使用简单的,所以我告诉 Chatgpt 想出这个:

<!-- Replace the empty SVGs with these -->
<svg>



<p>At this point, your toggle should look pretty decent, but it won't actually do anything yet. Don't worry though - in the next section, we'll add the JavaScript that makes it all work!</p>

<p><strong>A quick heads-up:</strong> I've kept the styling minimal on purpose. Feel free to spice it up with your own creative touches. Want a sliding animation? Go for it! Prefer a different icon style? Make it yours!</p>

<p>Ready to make this thing actually work? Let's move on to the JavaScript implementation!</p>

<h2>
  
  
  The JavaScript Implementation (Where It All Comes Together!)
</h2>

<p>Alright, this is where we make our toggle actually, you know... toggle. But don't worry - we're keeping it clean and simple.<br>
</p>

<pre class="brush:php;toolbar:false">const themeToggle = document.querySelector('.theme-toggle');


function toggleTheme() {
  const currentTheme = document.documentElement.getAttribute('data-theme');

  const newTheme = currentTheme === 'dark' ? 'light' : 'dark';

  document.documentElement.setAttribute('data-theme', newTheme);

  localStorage.setItem('theme', newTheme);
}

// Listen for clicks on our toggle
themeToggle.addEventListener('click', toggleTheme);


function initializeTheme() {
  const savedTheme = localStorage.getItem('theme');

  if (savedTheme) {
    document.documentElement.setAttribute('data-theme', savedTheme);
  } else {
    const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;

    document.documentElement.setAttribute(
      'data-theme',
      prefersDark ? 'dark' : 'light'
    );

    localStorage.setItem('theme', prefersDark ? 'dark' : 'light');
  }
}

// Run on page load
initializeTheme();

// Listen for system theme change

window.matchMedia('(prefers-color-scheme: dark)')
  .addEventListener('change', (e) => {
    // Only update if user hasn't manually set a preference
    if (!localStorage.getItem('theme')) {
      document.documentElement.setAttribute(
        'data-theme',
        e.matches ? 'dark' : 'light'
      );
    }
  });
登录后复制
登录后复制

让我们来分解一下这里发生的事情,因为实际上发生了一些非常酷的事情:

  1. 当有人单击切换时,我们会检查当前主题并切换到相反的主题
  2. 我们将他们的选择保存在 localStorage 中(因此它在页面加载之间保持不变)
  3. 当页面首次加载时,我们:
    • 检查他们是否保存了偏好设置
    • 如果没有,我们检查他们的系统主题
    • 应用合适的主题
  4. 作为奖励,我们会监听系统主题更改(例如有人在其操作系统上启用深色模式时)

防止错误主题的出现

这是一个常见问题:有时用户在页面加载时会看到错误主题的闪现。超级烦人,对吧?让我们通过在

中添加此脚本来解决这个问题。您的 HTML:
<script>
  // Add this to your <head> before any style sheets
  (function() {
    const savedTheme = localStorage.getItem('theme');
    if (savedTheme) {
      document.documentElement.setAttribute('data-theme', savedTheme);
    } else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
      document.documentElement.setAttribute('data-theme', 'dark');
    }
  })();
</script>
登录后复制
登录后复制

它会在加载其他内容之前立即运行,从而防止出现烦人的闪烁。

然后...就是这样!您已经有了一个可以正常工作的黑暗模式开关:

  • 记住用户偏好
  • 尊重系统设置
  • 不会闪现错误的主题
  • 过渡顺利

想让它变得更好吗?让我们继续学习一些有用的技巧,帮助您从优秀走向卓越!

让它变得更好(因为细节很重要!)

让我们从“它有效”切换到“它工作精美”,并进行一些重要但经常被忽视的改进。这些细节将专业实施与快速破解区分开来。

1. 键盘辅助功能

首先,让我们确保每个人都可以使用我们的切换按钮,无论他们如何与设备交互:

// Add this to your existing JavaScript
themeToggle.addEventListener('keydown', (e) => {
    // Toggle on Enter or Space
    if (e.key === 'Enter' || e.key === ' ') {
        e.preventDefault();
        toggleTheme();
    }
});
登录后复制
登录后复制

2. 禁用转换

当用户喜欢减少运动时禁用过渡:

@media (prefers-reduced-motion: reduce) {
  body {
    transition: none;
  }
}
登录后复制

3. 处理内容变更

这是许多开发人员错过的东西 - 某些内容可能需要根据主题进行更改。考虑具有不同版本的明/暗模式的图像:

// Add this to your toggleTheme function
function updateThemeSpecificContent(theme) {
    // Find all theme-aware images
    const themeImages = document.querySelectorAll('[data-theme-image]');

    themeImages.forEach(img => {
        const lightSrc = img.getAttribute('data-light-src');
        const darkSrc = img.getAttribute('data-dark-src');

        img.src = theme === 'dark' ? darkSrc : lightSrc;
    });
}
登录后复制

在 HTML 中使用它,如下所示:

<img src="/static/imghw/default1.png" data-src="/path/to/light-logo.png" class="lazy" data-theme-image data-light- data-dark- alt="在几分钟内构建一个黑暗模式切换(实际上有效)">
登录后复制

4. 防止主题不匹配

有时,保存的主题可能与实际显示的内容不同步。让我们添加一个安全检查:

<button>



<h3>
  
  
  The CSS
</h3>

<p>Here's where things get interesting. We'll use CSS variables (aka custom properties) to handle our color scheme. Drop this in your CSS file:<br>
</p>

<pre class="brush:php;toolbar:false">:root {
  --background: #ffffff;
  --text-primary: #222222;
  --toggle-bg: #e4e4e7;
  --toggle-hover: #d4d4d8;
}


[data-theme="dark"] {
  --background: #121212;
  --text-primary: #ffffff;
  --toggle-bg: #3f3f46;
  --toggle-hover: #52525b;
}

body {
  background-color: var(--background);
  color: var(--text-primary);
  transition: background-color 0.3s ease, color 0.3s ease;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

.theme-toggle {
  border: none;
  padding: 0.5rem;
  border-radius: 9999px;
  background-color: var(--toggle-bg);
  cursor: pointer;
  transition: background-color 0.2s ease;
  align-self: flex-start;
  position: absolute;
  right: 20px;
}

.theme-toggle:hover {
  background-color: var(--toggle-hover);
}

.theme-toggle svg {
    transform-origin: center;
    transition: transform 0.3s ease;
}

.theme-toggle:active svg {
    transform: rotate(30deg);
}

h1 {
  display: flex;
}

.sun-icon {
  display: none;
  width: 24px;
  height: 24px;
}

.moon-icon {
  width: 24px;
  height: 24px;
}

[data-theme="dark"] .sun-icon {
  display: block;
}

[data-theme="dark"] .moon-icon {
  display: none;
}
登录后复制
登录后复制

5. 性能优化技巧

这是一个巧妙的技巧,可以防止在不同主题中加载自定义字体时发生布局变化:

<!-- Replace the empty SVGs with these -->
<svg>



<p>At this point, your toggle should look pretty decent, but it won't actually do anything yet. Don't worry though - in the next section, we'll add the JavaScript that makes it all work!</p>

<p><strong>A quick heads-up:</strong> I've kept the styling minimal on purpose. Feel free to spice it up with your own creative touches. Want a sliding animation? Go for it! Prefer a different icon style? Make it yours!</p>

<p>Ready to make this thing actually work? Let's move on to the JavaScript implementation!</p>

<h2>
  
  
  The JavaScript Implementation (Where It All Comes Together!)
</h2>

<p>Alright, this is where we make our toggle actually, you know... toggle. But don't worry - we're keeping it clean and simple.<br>
</p>

<pre class="brush:php;toolbar:false">const themeToggle = document.querySelector('.theme-toggle');


function toggleTheme() {
  const currentTheme = document.documentElement.getAttribute('data-theme');

  const newTheme = currentTheme === 'dark' ? 'light' : 'dark';

  document.documentElement.setAttribute('data-theme', newTheme);

  localStorage.setItem('theme', newTheme);
}

// Listen for clicks on our toggle
themeToggle.addEventListener('click', toggleTheme);


function initializeTheme() {
  const savedTheme = localStorage.getItem('theme');

  if (savedTheme) {
    document.documentElement.setAttribute('data-theme', savedTheme);
  } else {
    const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;

    document.documentElement.setAttribute(
      'data-theme',
      prefersDark ? 'dark' : 'light'
    );

    localStorage.setItem('theme', prefersDark ? 'dark' : 'light');
  }
}

// Run on page load
initializeTheme();

// Listen for system theme change

window.matchMedia('(prefers-color-scheme: dark)')
  .addEventListener('change', (e) => {
    // Only update if user hasn't manually set a preference
    if (!localStorage.getItem('theme')) {
      document.documentElement.setAttribute(
        'data-theme',
        e.matches ? 'dark' : 'light'
      );
    }
  });
登录后复制
登录后复制

快速测试清单

发货前,请务必测试以下场景:

  • ✅ 页面刷新保持正确的主题
  • ✅ 尊重系统主题更改(如果没有手动偏好)
  • ✅ 切换功能适用于鼠标和键盘
  • ✅ 加载时不会闪烁错误的主题
  • ✅ 过渡顺利
  • ✅ 适用于所有主要浏览器(是的,甚至是 Safari!)
  • ✅ 主题特定内容正确更新

需要注意的常见问题

  1. 第三方内容:某些嵌入内容可能不尊重您的主题。像这样处理它:
<script>
  // Add this to your <head> before any style sheets
  (function() {
    const savedTheme = localStorage.getItem('theme');
    if (savedTheme) {
      document.documentElement.setAttribute('data-theme', savedTheme);
    } else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
      document.documentElement.setAttribute('data-theme', 'dark');
    }
  })();
</script>
登录后复制
登录后复制
  1. 具有透明度的图像:它们在不同的背景下看起来可能是错误的:
// Add this to your existing JavaScript
themeToggle.addEventListener('keydown', (e) => {
    // Toggle on Enter or Space
    if (e.key === 'Enter' || e.key === ' ') {
        e.preventDefault();
        toggleTheme();
    }
});
登录后复制
登录后复制

就是这样!您现在拥有一个强大、可访问且用户友好的暗模式实现,可以像冠军一样处理不同的场景。

结论

好了,给你了!我们构建了一个黑暗模式切换器,它不仅可以工作,而且效果非常好。

让我们快速回顾一下我们所取得的成就:

  • 真正记住用户偏好的切换开关?
  • 主题之间的平滑过渡,没有可怕的闪光⚡
  • 系统偏好设置检测是否有效?
  • 从一开始就内置辅助功能♿
  • 性能优化以保持流畅?‍♂️

从这里去哪里?

请随意使用此代码并使其成为您自己的代码。也许添加一些精美的动画,尝试不同的配色方案,或者将其与您现有的设计集成。我们建立的基础足够坚实,可以处理您提出的任何创意。

最后一件事...

深色模式可能看起来只是一个小细节,但正是这些小细节表明您关心用户的体验。另外,这很酷。谁不喜欢好的黑暗模式?

如果您发现这有帮助,请随时与其他开发者分享。如果您提出任何很酷的改进,我很想听听!


让我们保持联系! ?

如果您喜欢本指南并想要更多 Web 开发技巧、技巧以及偶尔关于编程的老爸笑话,请来 X 上和我一起玩!我分享我的开发者之旅中的快速技巧、编码见解和实际解决方案。

?关注我@Peboydcoder

我经常发布关于:

  • Web 开发提示和技巧
  • 前端最佳实践
  • UI/UX 见解
  • 是的,还有更多深色模式欣赏帖子?

过来打个招呼!总是很高兴与关心构建更好网络体验的其他开发人员建立联系。


以上是在几分钟内构建一个黑暗模式切换(实际上有效)的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

<🎜>:泡泡胶模拟器无穷大 - 如何获取和使用皇家钥匙
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系统,解释
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆树的耳语 - 如何解锁抓钩
3 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Java教程
1672
14
CakePHP 教程
1428
52
Laravel 教程
1332
25
PHP教程
1277
29
C# 教程
1257
24
静态表单提供商的比较 静态表单提供商的比较 Apr 16, 2025 am 11:20 AM

让我们尝试在这里造成一个术语:“静态表单提供商”。你带上html

使Sass更快的概念证明 使Sass更快的概念证明 Apr 16, 2025 am 10:38 AM

在一个新项目开始时,Sass汇编发生在眼睛的眨眼中。感觉很棒,尤其是当它与browsersync配对时,它重新加载

每周平台新闻:HTML加载属性,主要的ARIA规格以及从iframe转移到Shadow dom 每周平台新闻:HTML加载属性,主要的ARIA规格以及从iframe转移到Shadow dom Apr 17, 2025 am 10:55 AM

在本周的平台新闻综述中,Chrome引入了一个用于加载的新属性,Web开发人员的可访问性规范以及BBC Move

带有HTML对话框元素的一些动手 带有HTML对话框元素的一些动手 Apr 16, 2025 am 11:33 AM

这是我第一次查看HTML元素。我已经意识到了一段时间,但是尚未将其旋转。它很酷,

纸张形式 纸张形式 Apr 16, 2025 am 11:24 AM

购买或建造是技术的经典辩论。自己构建东西可能会感觉更便宜,因为您的信用卡账单上没有订单项,但是

'订阅播客”链接应在哪里? '订阅播客”链接应在哪里? Apr 16, 2025 pm 12:04 PM

有一段时间,iTunes是播客中的大狗,因此,如果您将“订阅播客”链接到喜欢:

托管您自己的非JavaScript分析的选项 托管您自己的非JavaScript分析的选项 Apr 15, 2025 am 11:09 AM

有很多分析平台可帮助您跟踪网站上的访问者和使用数据。也许最著名的是Google Analytics(广泛使用)

See all articles