简单记一记如何解决切换主题时的明暗闪烁

2 min

对于一个明暗双主题的 Astro 主题,在切换页面时经常会出现这样的情况。

上图中在切换页面时会出现闪烁问题
上图中在切换页面时会出现闪烁问题

这玩意的学名叫做 FOUC,也叫做 Flash of White。原因是浏览器在加载页面时,会先渲染 HTML。如果此时 JavaScript 还没来得及运行并给 html 标签加上 dark 类名,或者 CSS 还没加载完,浏览器就会默认使用白色背景,导致在暗色模式下看到一瞬间的白屏。

因为我的主题 Haku 也有这样的问题,那就来解决一下。

这是主题 global.css 中的部分代码。

@import url("https://fonts.loli.net/css2?family=Noto+Sans+SC:[email protected]");
@import url("https://fonts.loli.net/css2?family=SN+Pro:ital,wght@0,200..900;1,200..900&display=swap");
@import "tailwindcss";

@tailwind utilities;

@custom-variant dark (&:where(.dark, .dark *));

@layer base {
  :root {
    --haku-font: "SN Pro", "Noto Sans SC", sans-serif;
    --color-border: #a8a8a8;
    --color-readme: #44403b;
    --color-readme-anchor: #155dfc;
    --color-tag: #0a0a0a;
    --color-tag-bg: #dfdfdf;
    --color-tag-bg-hover: #a8a8a8;
    --color-tag-title: #0a0a0a;
    --color-tag-text: #202020;

    font-family: var(--haku-font);
    overflow-y: scroll;
  }

  html.dark {
    --color-readme-anchor: #2b7fff;
    --color-border: #3a3a3a;
    --color-readme: #d6d3d1;
    --color-tag: #dbdbdb;
    --color-tag-bg: #2b2b2b;
    --color-tag-bg-hover: #666666;
    --color-tag-title: #d5d5d6;
    --color-tag-text: #c5c5c5;
  }
}

首先要去掉 @import 指令,把它放在 <head> 标签中。因为外部 CSS 的优先级比较低,把 CDN 提供的 CSS 放在 HTML 文件里加载更好。

<head>
    <!-- 一些内容 -->
    <link
        rel="stylesheet"
        href="https://fonts.loli.net/css2?family=Noto+Sans+SC:[email protected]"
    />
    <link
        rel="stylesheet"
        href="https://fonts.loli.net/css2?family=SN+Pro:ital,wght@0,200..900;1,200..900&display=swap"
    />
</head>

然后要定义加载时的默认背景色,这就用 color-scheme 和 CSS 变量实现。

:root {
    color-scheme: light;
    background-color: var(--color-bg);
}

html.dark {
    color-scheme: dark;
    background-color: var(--color-bg);
}

--color-bg 自己看着,调成背景色就行。

最后,也是最重要的,加一个 JS 阻塞脚本。在 <head> 标签里加一个脚本,从本地配置里面找有没有 theme 一项。

  <script is:inline>
    (function () {
      const theme = localStorage.getItem("theme");
      const systemDark = window.matchMedia(
        "(prefers-color-scheme: dark)",
      ).matches;
      if (theme === "dark" || (!theme && systemDark)) {
        document.documentElement.classList.add("dark");
      } else {
        document.documentElement.classList.remove("dark");
      }
    })();
  </script>

这样就能根据读者本地环境自动适配,能够避免打开网页的时候被白光闪一下。

这个问题已经在两个 PR 里解决了:#50 还有 #49