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

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

Susan Sarandon
发布: 2024-11-10 03:01:02
原创
416 人浏览过

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 data-theme-image data-light-src="/path/to/light-logo.png" data-dark-src="/path/to/dark-logo.png" 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中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板