# Light, Dark and Custom Mode

### prefers-color-scheme

1. 先暸解 [CSS Variables ](https://bpio.gitbook.io/docs/edu-css/chapter5-management/css-variables)用法
2. prefers-color-scheme 可以幫助我們快速設定 Light 與 Dark mode

我們來看看一個範例：

```html
<button>Button</button>
```

```css
button {
    background-color: red;
}

@media (prefers-color-scheme: dark) {
  button {
    background-color: green;
  }  
}
```

{% embed url="<https://codepen.io/neillin1023/pen/abRdxVV>" %}

這個範例應該蠻直覺的，按鈕原本的背景是紅色，當您使用的瀏覽器是使用 Dark Mode 的時候，按鈕背景就會變成綠色，您可以打開 codepen 嘗試看看。

同理，如果寫的是 @media (prefers-color-scheme: light) ，那就是瀏覽器如果是 Light Mode 就會套用。

也就是說，`prefers-color-scheme` 是跟著瀏覽器外觀決定。一般情況下，瀏覽器的外觀是預設成跟隨系統外觀。

接下來，我們再使用 CSS Variables 技巧，讓 CSS 更貼近實際 CSS 管理的情況。

{% embed url="<https://codepen.io/neillin1023/pen/gOBPyKJ>" %}

實務上我們通常會將顏色的部分做統一管理，這樣會方便我們切換主題色，所以通常會使用變數系統。如果不考慮 IE 的話，原生 CSS 可以使用 CSS Variables 的技術。

所以只需要在不同的情況下，改變變數的值，就可以達成調出 Light 與 Dark Mode 的主題色。您可以自行發想您的 CSS 如何管理，在此例中，我們是使用了一個主要的設定 `--color-theme`，然後在 Dark Mode 的時候，利用 CSS Variables 的特性，將 `--color-theme` 的值設為 `--color-theme-dark-mode`，之後如果我們有需要改變顏色，只要在最上頭的變數區修正即可，不需要動到主要的內容，就可以達成兩種主題色的設定。如果您還看不太明白這一段 CSS 如何運作，可以先到  [CSS Variables](https://bpio.gitbook.io/docs/edu-css/chapter5-management/css-variables) 這篇文章複習。

### 更貼心的做法

現在我們來看看 Mozilla 文件網站的做法，您會發現到他的右上角 theme 的地方，有一個選項是 OS Default 選項。

<figure><img src="https://3433349642-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FfNa3cQXA0JgS2aeBbzYg%2Fuploads%2FO7hjCETb9F3wLIvpm7Eg%2F%E6%88%AA%E5%9C%96%202023-04-14%20%E4%B8%8B%E5%8D%885.47.04.png?alt=media&#x26;token=57cde5c2-df63-4bd9-aa89-f27cd91a54e5" alt=""><figcaption></figcaption></figure>

這樣的做法可以提供使用者自己決定外觀，不讓網站外觀跟隨瀏覽器的外觀的時候使用。但一開始不知道使用者的想法時，先判斷一開始進網站時使用者的瀏覽器環境，給予預設外觀的選項，如果使用者想改變再去改變。

那實作上應該要如何做呢？

我們可以查看 Mozilla 的 \_theme.scss 檔，發現預設外觀就是使用我們上述教過的觀念，而切換成 Light/Dark 時使用 classname 的變化，切換該載入那一些 CSS Variables，不過他這裡還有使用 scss 的 @mixin 和 js，所以如果您不太會 js 的話，需要跟您的前端工程師討論如何實行，因為還需要記錄使用者的選擇，可能會跟 session 或 cookie 有關，這樣才會形成一個完整的體驗。

<details>

<summary>Mozilla _theme.scss</summary>

```scss
@use "sass:color";
@use "../vars.scss" as *;
@use "../color-palette" as *;

@mixin light-theme {
  --text-primary: #{$mdn-theme-light-text-primary};
  --text-secondary: #{$mdn-theme-light-text-secondary};
  --text-inactive: #{$mdn-theme-light-text-inactive};
  --text-link: #{$mdn-theme-light-text-link};
  --text-invert: #{$mdn-theme-light-text-invert};

  --background-primary: #{$mdn-theme-light-background-primary};
  --background-secondary: #{$mdn-theme-light-background-secondary};
  --background-tertiary: #{$mdn-theme-light-background-tertiary};
  --background-toc-active: #{$mdn-theme-light-background-toc-active};
  --background-mark-yellow: #{color.adjust(
      $mdn-color-light-theme-yellow-30,
      $alpha: -0.6
    )};
  --background-mark-green: #{color.adjust(
      $mdn-color-light-theme-green-30,
      $alpha: -0.6
    )};
  --background-information: #{color.adjust(
      $mdn-theme-light-icon-information,
      $alpha: -0.9
    )};
  --background-warning: #{color.adjust(
      $mdn-theme-light-icon-warning,
      $alpha: -0.9
    )};
  --background-critical: #{color.adjust(
      $mdn-theme-light-icon-critical,
      $alpha: -0.9
    )};
  --background-success: #{color.adjust(
      $mdn-theme-light-icon-success,
      $alpha: -0.9
    )};

  --border-primary: #{$mdn-theme-light-border-primary};
  --border-secondary: #{$mdn-theme-light-border-secondary};

  --button-primary-default: #{$mdn-theme-light-button-primary-default};
  --button-primary-hover: #{$mdn-theme-light-button-primary-hover};
  --button-primary-active: #{$mdn-theme-light-button-primary-active};
  --button-primary-inactive: #{$mdn-theme-light-button-primary-inactive};

  --button-secondary-default: #{$mdn-theme-light-button-secondary-default};
  --button-secondary-hover: #{$mdn-theme-light-button-secondary-hover};
  --button-secondary-active: #{$mdn-theme-light-button-secondary-active};
  --button-secondary-inactive: #{$mdn-theme-light-button-secondary-inactive};
  --button-secondary-border-focus: #{$mdn-theme-light-button-secondary-border-focus};
  --button-secondary-border-red: #{$mdn-theme-light-button-secondary-border-red};
  --button-secondary-border-red-focus: #{$mdn-theme-light-button-secondary-border-red-focus};

  --icon-primary: #{$mdn-theme-light-icon-primary};
  --icon-secondary: #{$mdn-theme-light-icon-secondary};
  --icon-information: #{$mdn-theme-light-icon-information};
  --icon-warning: #{$mdn-theme-light-icon-warning};
  --icon-critical: #{$mdn-theme-light-icon-critical};
  --icon-success: #{$mdn-theme-light-icon-success};

  --accent-primary: #{$mdn-theme-light-accent-primary};
  --accent-primary-engage: #{color.adjust(
      $mdn-theme-light-accent-primary,
      $alpha: -0.9
    )};

  --accent-secondary: #{$mdn-theme-light-accent-secondary};
  --accent-tertiary: #{color.adjust(
      $mdn-color-light-theme-blue-50,
      $alpha: -0.9
    )};

  --shadow-01: #{$mdn-theme-light-shadow-01};
  --shadow-02: #{$mdn-theme-light-shadow-02};
  --focus-01: #{$mdn-theme-light-focus-01};
  --field-focus-border: #{$mdn-theme-light-field-focus-border};

  --code-token-tag: #{$mdn-theme-light-code-token-tag};
  --code-token-punctuation: #{$mdn-theme-light-code-token-punctuation};
  --code-token-attribute-name: #{$mdn-theme-light-code-token-attribute-name};
  --code-token-attribute-value: #{$mdn-theme-light-code-token-attribute-value};
  --code-token-comment: #{$mdn-theme-light-code-token-comment};
  --code-token-default: #{$mdn-theme-light-code-token-default};
  --code-token-selector: #{$mdn-theme-light-code-token-selector};
  --code-background-inline: #{$mdn-theme-light-code-background-inline};
  --code-background-block: #{$mdn-theme-light-code-background-block};

  --notecard-link-color: #{$mdn-color-neutral-80};

  --scrollbar-bg: transparent;
  --scrollbar-color: rgba(0, 0, 0, 0.25);

  --category-color: #{$mdn-color-light-theme-blue-50};
  --category-color-background: #{$mdn-color-light-theme-blue-50}10;
  --code-color: #{$mdn-color-light-theme-blue-40};
  --mark-color: #{$mdn-color-light-theme-blue-10};

  --plus-accent-color: #{$mdn-color-dark-theme-red-60};
  --html-accent-color: #{$mdn-color-light-theme-red-60};
  --css-accent-color: #{$mdn-color-light-theme-blue-60};
  --js-accent-color: #{$mdn-color-light-theme-yellow-40};
  --http-accent-color: #{$mdn-color-light-theme-green-60};
  --apis-accent-color: #{$mdn-color-light-theme-violet-60};
  --learn-accent-color: #{$mdn-color-light-theme-pink-60};

  --plus-code-color: #{$mdn-color-light-theme-blue-60};
  --html-code-color: #{$mdn-color-light-theme-red-70};
  --css-code-color: #{$mdn-color-light-theme-blue-60};
  --js-code-color: #{$mdn-color-light-theme-yellow-60};
  --http-code-color: #{$mdn-color-light-theme-green-60};
  --apis-code-color: #{$mdn-color-light-theme-violet-60};
  --learn-code-color: #{$mdn-color-light-theme-pink-60};

  --plus-mark-color: #{$mdn-color-light-theme-red-10};
  --html-mark-color: #{$mdn-color-light-theme-red-10};
  --css-mark-color: #{$mdn-color-light-theme-blue-10};
  --js-mark-color: #{$mdn-color-light-theme-yellow-10};
  --http-mark-color: #{$mdn-color-light-theme-green-10};
  --apis-mark-color: #{$mdn-color-light-theme-violet-10};
  --learn-mark-color: #{$mdn-color-light-theme-pink-10};

  --plus-accent-background-color: #{$mdn-color-light-theme-red-50}30;
  --html-accent-background-color: #{$mdn-color-light-theme-red-50}30;
  --css-accent-background-color: #{$mdn-color-light-theme-blue-50}30;
  --js-accent-background-color: #{$mdn-color-light-theme-yellow-50}30;
  --http-accent-background-color: #{$mdn-color-light-theme-green-50}30;
  --apis-accent-background-color: #{$mdn-color-light-theme-violet-50}30;
  --learn-accent-background-color: #{$mdn-color-light-theme-pink-50}30;

  --plus-accent-engage: #{color.adjust(
      $mdn-color-light-theme-red-50,
      $alpha: -0.3
    )};
  --html-accent-engage: #{color.adjust(
      $mdn-color-light-theme-red-50,
      $alpha: -0.3
    )};
  --css-accent-engage: #{color.adjust(
      $mdn-color-light-theme-blue-50,
      $alpha: -0.3
    )};
  --js-accent-engage: #{color.adjust(
      $mdn-color-light-theme-yellow-50,
      $alpha: -0.3
    )};
  --http-accent-engage: #{color.adjust(
      $mdn-color-light-theme-green-50,
      $alpha: -0.3
    )};
  --apis-accent-engage: #{color.adjust(
      $mdn-color-light-theme-violet-50,
      $alpha: -0.3
    )};
  --learn-accent-engage: #{color.adjust(
      $mdn-color-light-theme-pink-50,
      $alpha: -0.3
    )};

  --modal-backdrop-color: #{rgba($mdn-theme-dark-background-primary, 0.1)};
  --blend-color: #{$mdn-color-white}80;

  --text-primary-red: #{$mdn-color-light-theme-red-60};
  --text-primary-green: #{$mdn-color-light-theme-green-60};
  --text-primary-blue: #{$mdn-color-light-theme-blue-60};
  --text-primary-yellow: #{$mdn-color-light-theme-yellow-60};

  --collections-link: #{$mdn-color-light-theme-red-70};
  --collections-header: #{$mdn-color-light-theme-red-10};
  --collections-mandala: #{$mdn-color-light-theme-red-30};
  --collections-icon: #{$mdn-color-light-theme-red-50};

  --updates-link: #{$mdn-color-light-theme-blue-60};
  --updates-header: #{$mdn-color-neutral-light-70};
  --updates-mandala: #{$mdn-color-light-theme-blue-30};
  --updates-icon: #{$mdn-color-light-theme-blue-50};

  --form-limit-color: #{$mdn-color-neutral-60};
  --form-limit-color-emphasis: #{$mdn-color-neutral-70};
  --form-invalid-color: #{$mdn-color-light-theme-red-60};
  --form-invalid-focus-color: #{$mdn-color-light-theme-red-50};
  --form-invalid-focus-effect-color: #{rgba($mdn-color-light-theme-red-50, 0.2)};

  color-scheme: light;
}

@mixin dark-theme {
  --text-primary: #{$mdn-theme-dark-text-primary};
  --text-secondary: #{$mdn-theme-dark-text-secondary};
  --text-inactive: #{$mdn-theme-dark-text-inactive};
  --text-link: #{$mdn-theme-dark-text-link};
  --text-invert: #{$mdn-theme-dark-text-invert};

  --background-primary: #{$mdn-theme-dark-background-primary};
  --background-secondary: #{$mdn-theme-dark-background-secondary};
  --background-tertiary: #{$mdn-theme-dark-background-tertiary};
  --background-toc-active: #{$mdn-theme-dark-background-toc-active};
  --background-mark-yellow: #{color.adjust(
      $mdn-color-dark-theme-yellow-30,
      $alpha: -0.6
    )};
  --background-mark-green: #{color.adjust(
      $mdn-color-light-theme-green-30,
      $alpha: -0.6
    )};
  --background-information: #{color.adjust(
      $mdn-theme-light-icon-information,
      $alpha: -0.9
    )};
  --background-warning: #{color.adjust(
      $mdn-theme-light-icon-warning,
      $alpha: -0.9
    )};
  --background-critical: #{color.adjust(
      $mdn-theme-light-icon-critical,
      $alpha: -0.9
    )};
  --background-success: #{color.adjust(
      $mdn-theme-light-icon-success,
      $alpha: -0.9
    )};

  --border-primary: #{$mdn-theme-dark-border-primary};
  --border-secondary: #{$mdn-theme-dark-border-secondary};

  --button-primary-default: #{$mdn-theme-dark-button-primary-default};
  --button-primary-hover: #{$mdn-theme-dark-button-primary-hover};
  --button-primary-active: #{$mdn-theme-dark-button-primary-active};
  --button-primary-inactive: #{$mdn-theme-dark-button-primary-inactive};

  --button-secondary-default: #{$mdn-theme-dark-button-secondary-default};
  --button-secondary-hover: #{$mdn-theme-dark-button-secondary-hover};
  --button-secondary-active: #{$mdn-theme-dark-button-secondary-active};
  --button-secondary-inactive: #{$mdn-theme-dark-button-secondary-inactive};
  --button-secondary-border-focus: #{$mdn-theme-light-button-secondary-border-focus};
  --button-secondary-border-red: #{$mdn-theme-light-button-secondary-border-red};
  --button-secondary-border-red-focus: #{$mdn-theme-light-button-secondary-border-red-focus};

  --icon-primary: #{$mdn-theme-dark-icon-primary};
  --icon-secondary: #{$mdn-theme-dark-icon-secondary};
  --icon-information: #{$mdn-theme-dark-icon-information};
  --icon-warning: #{$mdn-theme-dark-icon-warning};
  --icon-critical: #{$mdn-theme-dark-icon-critical};
  --icon-success: #{$mdn-theme-dark-icon-success};

  --accent-primary: #{$mdn-theme-dark-accent-primary};
  --accent-primary-engage: #{color.adjust(
      $mdn-theme-dark-accent-primary,
      $alpha: -0.9
    )};
  --accent-secondary: #{$mdn-theme-dark-accent-secondary};
  --accent-tertiary: #{color.adjust(
      $mdn-color-light-theme-blue-50,
      $alpha: -0.9
    )};

  --shadow-01: #{$mdn-theme-dark-shadow-01};
  --shadow-02: #{$mdn-theme-dark-shadow-02};
  --focus-01: #{$mdn-theme-dark-focus-01};
  --field-focus-border: #{$mdn-theme-dark-field-focus-border};

  --code-token-tag: #{$mdn-theme-dark-code-token-tag};
  --code-token-punctuation: #{$mdn-theme-dark-code-token-punctuation};
  --code-token-attribute-name: #{$mdn-theme-dark-code-token-attribute-name};
  --code-token-attribute-value: #{$mdn-theme-dark-code-token-attribute-value};
  --code-token-comment: #{$mdn-theme-dark-code-token-comment};
  --code-token-default: #{$mdn-theme-dark-code-token-default};
  --code-token-selector: #{$mdn-theme-dark-code-token-selector};
  --code-background-inline: #{$mdn-theme-dark-code-background-inline};
  --code-background-block: #{$mdn-theme-dark-code-background-block};

  --notecard-link-color: #{$mdn-color-neutral-10};

  --scrollbar-bg: transparent;
  --scrollbar-color: rgba(255, 255, 255, 0.25);

  --category-color: #{$mdn-color-dark-theme-blue-30};
  --category-color-background: #{$mdn-color-dark-theme-blue-30}70;
  --code-color: #{$mdn-color-dark-theme-blue-20};
  --mark-color: #{$mdn-color-dark-theme-blue-70};

  --plus-accent-color: #{$mdn-color-dark-theme-red-30};
  --html-accent-color: #{$mdn-color-dark-theme-red-40};
  --css-accent-color: #{$mdn-color-dark-theme-blue-30};
  --js-accent-color: #{$mdn-color-dark-theme-yellow-40};
  --http-accent-color: #{$mdn-color-dark-theme-green-40};
  --apis-accent-color: #{$mdn-color-dark-theme-violet-40};
  --learn-accent-color: #{$mdn-color-dark-theme-pink-40};

  --plus-code-color: #{$mdn-color-dark-theme-blue-20};
  --html-code-color: #{$mdn-color-neutral-light-70};
  --css-code-color: #{$mdn-color-dark-theme-blue-20};
  --js-code-color: #{$mdn-color-dark-theme-yellow-30};
  --http-code-color: #{$mdn-color-dark-theme-green-30};
  --apis-code-color: #{$mdn-color-dark-theme-violet-30};
  --learn-code-color: #{$mdn-color-dark-theme-pink-30};

  --plus-mark-color: #{$mdn-color-dark-theme-red-70};
  --html-mark-color: #{$mdn-color-dark-theme-red-70};
  --css-mark-color: #{$mdn-color-dark-theme-blue-70};
  --js-mark-color: #{$mdn-color-dark-theme-yellow-70};
  --http-mark-color: #{$mdn-color-dark-theme-green-70};
  --apis-mark-color: #{$mdn-color-dark-theme-violet-70};
  --learn-mark-color: #{$mdn-color-dark-theme-pink-70};

  --plus-accent-background-color: #{$mdn-color-light-theme-red-50}30;
  --html-accent-background-color: #{$mdn-color-light-theme-red-50}30;
  --css-accent-background-color: #{$mdn-color-light-theme-blue-50}30;
  --js-accent-background-color: #{$mdn-color-light-theme-yellow-50}30;
  --http-accent-background-color: #{$mdn-color-light-theme-green-50}30;
  --apis-accent-background-color: #{$mdn-color-light-theme-violet-50}30;
  --learn-accent-background-color: #{$mdn-color-light-theme-pink-50}30;

  --plus-accent-engage: #{color.adjust(
      $mdn-color-dark-theme-red-40,
      $alpha: -0.3
    )};
  --html-accent-engage: #{color.adjust(
      $mdn-color-dark-theme-red-40,
      $alpha: -0.3
    )};
  --css-accent-engage: #{color.adjust(
      $mdn-color-dark-theme-blue-30,
      $alpha: -0.3
    )};
  --js-accent-engage: #{color.adjust(
      $mdn-color-dark-theme-yellow-40,
      $alpha: -0.3
    )};
  --http-accent-engage: #{color.adjust(
      $mdn-color-dark-theme-green-40,
      $alpha: -0.3
    )};
  --apis-accent-engage: #{color.adjust(
      $mdn-color-dark-theme-violet-40,
      $alpha: -0.3
    )};
  --learn-accent-engage: #{color.adjust(
      $mdn-color-dark-theme-pink-40,
      $alpha: -0.3
    )};

  --modal-backdrop-color: #{rgba($mdn-theme-dark-background-primary, 0.7)};
  --blend-color: #{$mdn-color-black}80;

  --text-primary-red: #{$mdn-color-dark-theme-red-30};
  --text-primary-green: #{$mdn-color-dark-theme-green-30};
  --text-primary-blue: #{$mdn-color-dark-theme-blue-30};
  --text-primary-yellow: #{$mdn-color-dark-theme-yellow-30};

  --collections-link: #{$mdn-color-dark-theme-red-30};
  --collections-header: #{$mdn-color-dark-theme-red-90};
  --collections-mandala: #{$mdn-color-dark-theme-red-70};
  --collections-icon: #{$mdn-color-dark-theme-red-60};

  --updates-link: #{$mdn-color-dark-theme-blue-30};
  --updates-header: #{$mdn-color-black};
  --updates-mandala: #{$mdn-color-dark-theme-blue-20};
  --updates-icon: #{$mdn-color-dark-theme-blue-30};

  --form-limit-color: #{$mdn-color-neutral-40};
  --form-limit-color-emphasis: #{$mdn-color-neutral-30};
  --form-invalid-color: #{$mdn-color-light-theme-red-30};
  --form-invalid-focus-color: #{$mdn-color-light-theme-red-40};
  --form-invalid-focus-effect-color: #{rgba($mdn-color-light-theme-red-40, 0.2)};

  color-scheme: dark;
}

body,
:root {
  --mdn-color-white: #{$mdn-color-white};
  --mdn-color-black: #{$mdn-color-black};
  --mdn-color-ads: #{$mdn-color-ads};
  --mdn-color-background-highlight: #{$mdn-color-light-theme-yellow-10};
  --mdn-color-dark-grey: #{$mdn-color-neutral-70};
  --mdn-background-dark: #{$mdn-theme-dark-background-primary};
  --mdn-background-light: #{$mdn-theme-light-background-primary};
  --mdn-background-light-grey: #{$mdn-color-neutral-10};
  --color-announcement-banner-accent: #{$mdn-color-light-theme-pink-40};
}

.light {
  @include light-theme;
}

.dark {
  @include dark-theme;
}

// OS Default.
:root:not(.light):not(.dark) {
  @media (prefers-color-scheme: light) {
    @include light-theme;
  }

  @media (prefers-color-scheme: dark) {
    @include dark-theme;
  }
}

```

</details>

### 標題還有一個 Custom Mode 還沒講噎？

其實剛剛就已經講完囉，就是使用 classname 的變化，切換該載入那一些 CSS Variables。您可以自行設定很多不同的外觀主題色。

### 還有兩個跟主題色有關的事！

那就是 `accent-color` 這個屬性，這是改變瀏覽器預設顏色的一個屬性，可以適時的搭配 CSS Variables 使用。

另一個則是 `color-scheme` ，如果將它設成 dark ，則會幫您反轉顏色，這樣調起色來事半功倍！

### 小結

其實這件事情並不難，所以也沒什麼好總結，只要您的 CSS 管理有準則可以遵循，主題色的 CSS 管理會自然而然地迎刃而解。

### 作業

實作 Mozilla 的顏色改變系統，並且多新增一種自己的客製化主題色。

### 參考連結

* [prefers-color-scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme)
* [accent-color](https://developer.mozilla.org/en-US/docs/Web/CSS/accent-color)
