主题切换

sitelo-ui 自己就会根据 prefers-color-scheme 判断深色模式——如果一个站点乐意跟着操作系统走,那本页的东西一样都不需要。这个切换按钮是为了让读者能覆盖那个判断。

它是五个需要脚本的组件之一,因为这个选择存在 localStorage 里,而只有脚本读得到。按钮会在第一次按下时自己去取那段脚本。

接入方式

往 head 里放两样东西,再把按钮放到它该在的地方:

import { styles, themeScript, themeToggle } from 'sitelo/ui'

head(
  themeScript(), // 在首次绘制之前应用已保存的选择
  styles(),
)

body(
  appBar({ brand: '我的站点' },
    appBarSpacer(),
    appBarActions(themeToggle()),
  ),
)

没有第三个文件。themeScript() 是故意做成阻塞且内联的——任何被推迟的东西都会等页面先画一遍,而那正是它要防的那道深色闪光——至于切换动作本身,则搭在按钮上:

<button data-su-theme-toggle
        onclick="import('/su/theme.js').then(m=>m.toggle(this))">

两者要配套用。themeScript() 同时也是在加载时给切换按钮标上 aria-pressed 的那一位:这时还没人按过任何东西,按钮自己根本无从知道最终定的是哪套主题。

切换按钮

那个图标是纯 CSS,直接读主题属性得来——所以在首次绘制时它就已经是对的,早于任何脚本运行。它显示的是点击后会切换到哪一种。

stack({ direction: 'row', gap: 'md', align: 'center' },
  themeToggle(),
  themeToggle({ variant: 'soft' }),
  themeToggle({ variant: 'outline' }),
)

这些按钮是真能用的——本页加载了运行时。点一下会在 <html> 上设置 data-su-theme,那是 sitelo-ui 自己的属性,所以只有本页里的 sitelo-ui 组件会跟着变。本站其余部分跟的是它自己的 data-theme,由顶栏里的那个切换按钮设置。在你自己的站点上,这两者只会存在一个。

在应用栏中

sitelo
appBar({ brand: 'sitelo' },
  appBarNav(navLink({ href: '#docs', current: true }, '文档')),
  appBarSpacer(),
  appBarActions(
    themeToggle(),
    button({ size: 'sm' }, '开始使用'),
  ),
)

主题是怎么定下来的

依次是:任意祖先元素上显式的 data-theme 或 data-su-theme 优先;都没有的话,由 prefers-color-scheme 决定。两个属性名都认,是为了让 sitelo-ui 能待在一个本来就有自己主题开关的站点里——本文档正是这种情况。

自己来驱动

运行时导出了按钮所用的那几个函数,方便你做自定义控件,或者做一个浅色 / 深色 / 跟随系统的三态选择器。

import { getTheme, setTheme, toggleTheme } from 'sitelo/ui/client'

getTheme()          // 'light' | 'dark' —— 解析后的结果,不是存储值
toggleTheme()       // 切换
setTheme('dark')    // 固定
setTheme('system')  // 清掉覆盖,重新跟随系统

属性

属性类型默认值说明
labelstring'Toggle dark mode'无障碍名称,同时也是悬停提示。
variant'solid' | 'soft' | 'outline' | 'ghost' | 'link''ghost'按钮变体。
color'primary' | 'neutral' | 'success' | 'warning' | 'danger''neutral'取用哪一套配色。

themeScript() 接受一个可选的 nonce,供设有内容安全策略的站点使用。