Preferences(偏好设置)把偏好值放到 data-* 属性上并保持更新,让你的 CSS 直接跟着用户的选择走:列表还是网格、紧凑还是宽松、浅色还是深色。它不渲染任何控件,切换按钮由你自己写;它负责应用属性、保存值,以及在服务端渲染时提前生效。
用法
Preferences 只有一个根,以 display: contents 渲染,不产生自己的盒子。偏好实例来自 @ui-sdk/core 的 createPreference 或 createColorScheme:
Preferences
└── 你的内容(属性写在 Preferences 元素上,global 时写在 html 上)先定义偏好,指定可选值、默认值与要写出的属性:
import { createPreference } from "@ui-sdk/core";
const view = createPreference({
values: ["list", "grid"],
defaultValue: "list",
attribute: "data-view",
});
export { view };把它交给 Preferences,然后按属性写样式:
import { Preferences } from "@ui-sdk/react/preferences";
import { view } from "./preferences";
function FileList() {
return (
<Preferences preferences={[view]}>
<ul data-slot="files">
<li>三季度路线图.pdf</li>
<li>发布检查清单.md</li>
</ul>
</Preferences>
);
}[data-view="grid"] [data-slot="files"] {
display: grid;
grid-template-columns: repeat(2, 1fr);
}初始是 data-view="list"。调用 view.set("grid") 后属性更新,列表变成网格。
持久化保存
加上 storage,选择在两次访问之间保留:
import { createLocalStorage, createPreference } from "@ui-sdk/core";
const view = createPreference({
values: ["list", "grid"],
defaultValue: "list",
attribute: "data-view",
storage: createLocalStorage("app-preferences-view"),
});改动写入本地存储,刷新后恢复。也可以用 createSessionStorage 或 createCookieStorage;服务端渲染时 cookie 存储要按请求创建,详见服务端渲染。保存的值不在 values 里时会被忽略,回到 defaultValue。
读取与切换
值会影响渲染内容时用 usePreference,它在变化时更新组件,也可以用在 Preferences 之外。同一个偏好实例在哪里需要就传到哪里:
import { Button } from "@ui-sdk/react/button";
import { usePreference } from "@ui-sdk/react/preferences";
import { view } from "./preferences";
function ViewSwitch() {
const { value } = usePreference(view);
return (
<div role="group" aria-label="视图">
<Button
aria-pressed={value === "list"}
onClick={(event) =>
view.set("list", { reason: "press", event: event.nativeEvent })
}
>
列表
</Button>
<Button
aria-pressed={value === "grid"}
onClick={(event) =>
view.set("grid", { reason: "press", event: event.nativeEvent })
}
>
网格
</Button>
</div>
);
}set(next, { reason, event }) 返回是否真的改变,reason 默认 "imperative"。偏好自己的 onValueChange 在变化前触发,details.cancel() 可以拒绝这次变更,参见状态与事件。
整页与多个偏好
定义时设 global: true,属性就写到 <html> 上,整页样式都能用;仍然要把它传给 Preferences,一个挂载的 Preferences 就够:
const density = createPreference({
values: ["comfortable", "compact"],
defaultValue: "comfortable",
attribute: "data-density",
global: true,
storage: createLocalStorage("app-preferences-density"),
});一次可以应用多个偏好,各自用自己的属性与 global 设置:
import { createColorScheme, createLocalStorage } from "@ui-sdk/core";
const colorScheme = createColorScheme({
storage: createLocalStorage("app-color-scheme"),
});
<Preferences preferences={[view, density, colorScheme]}>
<App />
</Preferences>;共享同一个实例的多个包装器跟随同一个值;要互相独立的选择就用不同实例与不同存储键。createColorScheme 的可选值是 "system"、"light"、"dark",写出 data-color-scheme,并额外解析浏览器的偏好。
服务端渲染
服务端渲染时 Preferences 会输出一段脚本,在页面绘制前应用已保存的值,CSS 可以立刻使用这些属性。usePreference 先用服务端的值,hydration 之后再更新。
内容安全策略要求 nonce 时,把响应里的 nonce 传进来:
<Preferences preferences={[view]} nonce={nonce}>
<App />
</Preferences>基于 cookie 的偏好要按请求创建,避免把一位访客的选择带给另一位。完整做法参见服务端渲染。
样式与状态
Preferences 没有自己的 data-slot 状态属性,它写出的就是你在 attribute 里指定的那个属性,值来自 values。样式全部围绕这些属性展开:
[data-view="grid"] [data-slot="files"] {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 12px;
}
[data-density="compact"] [data-slot="files"] li {
padding-block: 4px;
}
[data-color-scheme="dark"] {
color-scheme: dark;
}global 偏好写在 <html> 上,因此选择器可以从根开始匹配整页;非 global 偏好只影响 Preferences 内部的内容。详见样式与动画。
API
Preferences:
| 属性 | 类型 | 说明 |
|---|---|---|
preferences |
readonly PreferenceLike[] |
要应用的偏好,来自 createPreference 或 createColorScheme。 |
nonce |
string |
内容安全策略给内联脚本的 nonce。 |
usePreference(preference) 返回偏好当前状态并在变化时更新组件:普通偏好是 { value },配色方案还包含 system(浏览器偏好)与 resolved(实际生效的方案)。
createPreference 的选项:values、defaultValue、attribute(必须以 data- 开头)、global、storage、onValueChange。实例提供 get()、set(next, change?)、subscribe(listener)、initialState 与 follow(store, key)。
键盘与无障碍
Preferences 本身不渲染可聚焦元素,没有键盘交互,也不播报变化。无障碍表现取决于你写的控件:
- 一组互斥的视图按钮用
role="group"加aria-label命名,每个按钮用aria-pressed表达当前选择;也可以改用分段控件或切换。 - 属性变化不会自动通知屏幕阅读器。选择会改变内容含义时,自己补一条状态文本。
- 深浅色切换要说明当前是「跟随系统」还是显式选择,不要只靠图标颜色。