歡迎來到本系列的第7篇。在之前的文章中,我們主要關注組件的內部邏輯。今天,我們將把視野擴大到整個網站的架構。

深色模式(Dark Mode)不僅僅是一個“開關”,它涉及到 CSS 架構設計的核心思想——變量化。同時,為了讓網站“記住”用户的選擇,我們將第一次接觸瀏覽器的LocalStorage(本地存儲)。

本篇我們將實現:

基於 CSS 變量 的主題系統。

一個平滑切換的“日/夜”開關。

使用 JS 讀取和保存用户偏好。

第一部分:HTML 結構搭建 (語義化與開關)

HTML 結構相對簡單。我們主要需要兩部分:

頁面內容:用於展示主題切換後的效果(如標題、卡片、文字)。

切換開關:一個用於觸發切換的按鈕。

<body>

<!-- 1. 頂部導航欄 -->
<nav class="navbar">
    <div class="logo">MyBrand</div>
    
    <!-- 
       主題切換開關 
       aria-label 是為了無障礙訪問
    -->
    <button id="theme-toggle" class="theme-btn" aria-label="切換主題">
        <!-- 默認顯示月亮圖標 (代表點擊進入深色模式) -->
        <span class="icon">🌙</span>
        <span class="text">深色模式</span>
    </button>
</nav>

<!-- 2. 主要內容區域 -->
<main class="container">
    
    <div class="hero-section">
        <h1>歡迎來到我的博客</h1>
        <p>這是一個演示 CSS 變量和深色模式切換的頁面。</p>
    </div>

    <div class="card-grid">
        <!-- 卡片 1 -->
        <div class="card">
            <h2>文章標題 1</h2>
            <p>這是一些示例文本。在深色模式下,背景變黑,文字變白。</p>
            <a href="#" class="link">閲讀更多</a>
        </div>
        
        <!-- 卡片 2 -->
        <div class="card">
            <h2>文章標題 2</h2>
            <p>CSS 變量讓這一切變得非常簡單且高效。</p>
            <a href="#" class="link">閲讀更多</a>
        </div>
    </div>

</main>

</body>

第二部分:CSS 樣式 (CSS 變量的魔力)

這是本篇的核心。我們不再直接寫死顏色(如 #ffffff 或 #000000),而是定義變量。

邏輯如下:

在 :root (默認狀態) 下定義一套“淺色”變量。

在 [data-theme="dark"] 狀態下重新定義這套變量為“深色”值。

在具體的 CSS 規則中,只使用 var(--variable-name)。

/* --- 1. 定義變量系統 --- */

/* 默認 (淺色) 主題 /
:root {
/
背景色 */
--bg-body: #f4f7f6;
--bg-card: #ffffff;
--bg-nav: #ffffff;

/* 文本色 */
--text-primary: #333333;
--text-secondary: #666666;

/* 強調色 */
--accent-color: #007bff;

/* 邊框與陰影 */
--border-color: #e1e4e8;
--shadow: 0 4px 12px rgba(0,0,0,0.08);

}

/* 深色主題 (通過 data-theme="dark" 屬性激活) */
[data-theme="dark"] {
--bg-body: #1a1a1a;
--bg-card: #2d2d2d;
--bg-nav: #2d2d2d;

--text-primary: #ffffff;
--text-secondary: #b0b0b0;

--accent-color: #4dabf7; /* 深色模式下通常顏色要稍微亮一點 */

--border-color: #444444;
--shadow: 0 4px 12px rgba(0,0,0,0.3);

}

/* --- 2. 應用變量 --- */

body {
background-color: var(--bg-body);
color: var(--text-primary);
font-family: -apple-system, sans-serif;
/* 核心:添加 transition 讓顏色切換平滑過渡 */
transition: background-color 0.3s ease, color 0.3s ease;
margin: 0;
}

.navbar {
background-color: var(--bg-nav);
border-bottom: 1px solid var(--border-color);
padding: 15px 30px;
display: flex;
justify-content: space-between;
align-items: center;
transition: background-color 0.3s ease, border-color 0.3s ease;
}

.card {
background-color: var(--bg-card);
border: 1px solid var(--border-color);
box-shadow: var(--shadow);
padding: 25px;
border-radius: 8px;
transition: background-color 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
}

.card h2 { margin-top: 0; }

.card p { color: var(--text-secondary); }

.link { color: var(--accent-color); text-decoration: none; }

/* --- 開關按鈕樣式 --- */
.theme-btn {
background: transparent;
border: 1px solid var(--border-color);
color: var(--text-primary);
padding: 8px 16px;
border-radius: 20px;
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
transition: all 0.3s ease;
}

.theme-btn:hover {
background-color: var(--bg-body);
}

/* 佈局輔助 */
.container { max-width: 800px; margin: 40px auto; padding: 0 20px; }
.card-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }

第三部分:JS 交互邏輯 (LocalStorage)

JavaScript 的任務不僅僅是切換類名,更重要的是持久化用户的選擇。

邏輯流程:

初始化:頁面加載時,檢查 localStorage。如果有保存的設置,立即應用。

切換:點擊按鈕時,切換 html 標籤上的 data-theme 屬性。

保存:將當前的狀態('dark' 或 'light')寫入 localStorage。

document.addEventListener('DOMContentLoaded', () => {

// 1. 獲取 DOM 元素
const toggleBtn = document.getElementById('theme-toggle');
const toggleIcon = toggleBtn.querySelector('.icon');
const toggleText = toggleBtn.querySelector('.text');

// 我們將把 data-theme 屬性放在 <html> 標籤上
const htmlElement = document.documentElement;

// --- 輔助函數:更新按鈕 UI ---
const updateButtonUI = (isDark) => {
    if (isDark) {
        toggleIcon.textContent = '☀';
        toggleText.textContent = '淺色模式';
    } else {
        toggleIcon.textContent = '🌙';
        toggleText.textContent = '深色模式';
    }
};

// --- 2. 初始化:檢查本地存儲 ---
// 檢查用户之前是否選過
const savedTheme = localStorage.getItem('myAppTheme');

// 如果之前保存了 'dark',則立即應用
if (savedTheme === 'dark') {
    htmlElement.setAttribute('data-theme', 'dark');
    updateButtonUI(true);
} else {
    // 默認是淺色,無需操作 (或者也可以顯式設置 'light')
    updateButtonUI(false);
}

// --- 3. 綁定點擊事件 ---
toggleBtn.addEventListener('click', () => {
    
    // 檢查當前是否是深色模式
    const currentTheme = htmlElement.getAttribute('data-theme');
    
    if (currentTheme === 'dark') {
        // -> 切換為淺色
        htmlElement.setAttribute('data-theme', 'light');
        localStorage.setItem('myAppTheme', 'light'); // 保存到本地
        updateButtonUI(false);
    } else {
        // -> 切換為深色
        htmlElement.setAttribute('data-theme', 'dark');
        localStorage.setItem('myAppTheme', 'dark'); // 保存到本地
        updateButtonUI(true);
    }
    
});

});

總結

恭喜!您構建了一個現代化的、具備記憶功能的深色模式系統。

我們學到了:

CSS 變量 (:root):這是現代 CSS 最強大的功能之一,它徹底改變了我們管理顏色的方式。

屬性選擇器 ([data-theme="..."]):利用 HTML 屬性來驅動 CSS 樣式的變化。

LocalStorage:如何使用簡單的 setItem 和 getItem 讓網頁擁有“記憶”,極大地提升了用户體驗。

這套邏輯是通用的,您可以將其應用到任何規模的 Web 項目中。