首頁 >web前端 >css教學 >在幾分鐘內建立一個黑暗模式切換(實際上有效)

在幾分鐘內建立一個黑暗模式切換(實際上有效)

Susan Sarandon
Susan Sarandon原創
2024-11-10 03:01:02431瀏覽

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中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn