Param Panel · 悬浮调参板
载入预览…
做可调参 demo / 动效预览 / 主题试色页:参数 ≥3 个、需要当场看两档差别、颜色要受色板约束、调完要把当前值带走回写 params.json 的场景
不适用场景
面向最终用户的生产界面(板只该在 lab/preview 挂)、没有参数可调的静态页、需要表单校验与提交的表单场景、移动端为主的阅读页
元数据
标签
验证证据
2026-11-04 · python packs/param-panel/scripts/assert_tokens.py + 真机 ego lite 逐项
PASS assert_tokens param-panel / css 8887 字节 · 14 个类 / js 27678 字节 · 导出 2 · 用到类 6 个(缺定义 0) / schema 类型 6 种 / 预览引用 1 条全部解析 / 预览 hex 27 个 / 挂载判定 命中 4 例 · 反例 3 例全不中 / 真机:站内 iframe、直开 preview/index.html、?lab=0 三态各一测(前两态挂板、lab=0 不挂但默认值照落);版式/组件/其它三栏切换正确;圆角 14→28px、内边距 24→40px、投影 12→32px、速度 1.6→3s、标题文本、网格开关全部即时驱动卡片;色板点 #3f7dca 生效且刷新后仍在;收起 56×56 方块→点开还原;Tab 焦点环 solid 2px rgb(254,128,25);console 无报错归属与许可
- 旧主题库 components/param-panel(自产) · MIT面板本体:pattern 结构、tab 分栏、swatch 吸附、拖/缩放/折叠、localStorage 记忆与底栏动作
- sai-assets PACK-CONTRACT v1 · MIT包形状、pack.json 字段与断言最低集的规则来源
取用
固定版本(CDN)
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/nakamotosai/sai-assets@pack/param-panel@1.0.0/packs/param-panel/src/param-panel.css">raw 直取(跟随 main)
curl -O https://raw.githubusercontent.com/nakamotosai/sai-assets/main/packs/param-panel/SKILL.md仓库内落地
python scripts/apply.py param-panel --to <项目目录>整包下载
https://assets.ouuut.com/downloads/param-panel.zip源码与规则
pack.json{
"schema": 1,
"id": "param-panel",
"title": "Param Panel · 悬浮调参板",
"kind": "ui",
"category": "web-ui",
"version": "1.0.0",
"license": "MIT",
"attribution": [
{
"name": "旧主题库 components/param-panel(自产)",
"license": "MIT",
"what": "面板本体:pattern 结构、tab 分栏、swatch 吸附、拖/缩放/折叠、localStorage 记忆与底栏动作"
},
{
"name": "sai-assets PACK-CONTRACT v1",
"license": "MIT",
"what": "包形状、pack.json 字段与断言最低集的规则来源"
}
],
"origin": {
"project": "theme-lab (旧主题库)",
"path": "components/param-panel",
"derived_from": null
},
"entry": "SKILL.md",
"files": {
"src": [
"src/param-panel.css",
"src/param-panel.js",
"src/params.schema.json"
],
"usage": "docs/usage.md",
"assert": "scripts/assert_tokens.py",
"preview": "preview/index.html"
},
"tags": [
"tuner",
"demo",
"lab",
"swatch",
"palette",
"slider",
"localstorage",
"drag",
"resize",
"export",
"a11y",
"reduced-motion"
],
"when": "做可调参 demo / 动效预览 / 主题试色页:参数 ≥3 个、需要当场看两档差别、颜色要受色板约束、调完要把当前值带走回写 params.json 的场景",
"not_for": "面向最终用户的生产界面(板只该在 lab/preview 挂)、没有参数可调的静态页、需要表单校验与提交的表单场景、移动端为主的阅读页",
"default_variant": null,
"verified": {
"date": "2026-11-04",
"how": "python packs/param-panel/scripts/assert_tokens.py + 真机 ego lite 逐项",
"evidence": "PASS assert_tokens param-panel / css 8887 字节 · 14 个类 / js 27678 字节 · 导出 2 · 用到类 6 个(缺定义 0) / schema 类型 6 种 / 预览引用 1 条全部解析 / 预览 hex 27 个 / 挂载判定 命中 4 例 · 反例 3 例全不中 / 真机:站内 iframe、直开 preview/index.html、?lab=0 三态各一测(前两态挂板、lab=0 不挂但默认值照落);版式/组件/其它三栏切换正确;圆角 14→28px、内边距 24→40px、投影 12→32px、速度 1.6→3s、标题文本、网格开关全部即时驱动卡片;色板点 #3f7dca 生效且刷新后仍在;收起 56×56 方块→点开还原;Tab 焦点环 solid 2px rgb(254,128,25);console 无报错"
},
"canonical_home": null
}
docs/usage.md# param-panel · 用法与配对规则
> 悬浮调参板:把一件作品的参数从代码里搬到一个**可拖、可缩放、可导出**的小板上,调完把值抄回 `params.json` 就是定稿。它只解决"看得见地调参",不解决"参数该是什么"。
## 什么时候用
- 做**可调参 demo / 动效预览 / 主题试色页**:参数 ≥3 个、且"看一眼就知道该往哪调"的场景。
- 需要**给用户当场看两档差别**(圆角 0→28、阴影 0→40、速度 0.2→4s)时,比改代码截图快一个量级。
- 需要**色板约束**:颜色只允许从给定 swatches 里挑(禁自由取色),避免调出库外颜色。
- 需要**调完能带走**:底栏「复制 / 导出」把当前值变成可粘贴的 JSON,直接回写 `params.json`。
## 接入步骤
```html
<link rel="stylesheet" href="./src/param-panel.css" />
<script type="module">
import { mountParamPanel, shouldMountLabPanel } from "./src/param-panel.js";
const params = await fetch("./params.json").then((r) => r.json()); // {slug, version, defaults, schema}
const apply = (p) => {
const s = document.documentElement.style;
s.setProperty("--demo-radius", p.radius + "px");
s.setProperty("--demo-shadow", p.shadow + "px");
s.setProperty("--demo-accent", p.accent);
};
apply(params.defaults); // 生产:先落默认值,不挂板
if (shouldMountLabPanel()) { // preview / ?lab=1 才挂板
mountParamPanel({ params, title: "调参", corner: "br", onChange: apply });
}
</script>
```
1. 把 `src/param-panel.{css,js}` 拷进项目(`apply.py` 整包拷贝时已在包里)。
2. 写一份 `params.json`:`defaults` 给一套能看的默认值,`schema` 每项必带 `key/label/type`。
3. `onChange` 里**只做赋值**(写 CSS 变量 / 调引擎参数),不要在里面做重活;面板每次输入都会调它。
4. 生产环境**不挂板**:`shouldMountLabPanel()` 只在路径像 `preview`、或 URL 带 `?lab=1` 时返回 true;要强制也可以 `mountParamPanel({force:true})` / 关掉用 `?lab=0`。
5. 调好后点底栏「复制」,把 JSON 粘回 `params.json.defaults`。
## 参数
`mountParamPanel(opts)`:
| 字段 | 默认 | 说明 |
|---|---|---|
| `params` | — | `{slug, version, defaults, current?, schema[]}`;`slug` 决定 localStorage 命名空间 |
| `onChange` | `()=>{}` | 每次改动收到一份当前值对象(含全部 key) |
| `title` | `"参数"` | 板头标题 |
| `corner` | `"br"` | 最小化方块落点:`tl` / `tr` / `bl` / `br` |
| `force` | — | `true`/`false` 覆盖 `shouldMountLabPanel()` 的判断 |
| `storageKey` / `defaultsKey` / `positionKey` / `sizeKey` / `collapseKey` | 自动 | 想自己控命名空间时才传 |
`params.schema[]` 每项:
| 字段 | 说明 |
|---|---|
| `key` | 取值键名(与 `defaults` 对应) |
| `label` | 板上显示名 |
| `type` | `range` / `number` / `select` / `checkbox` / `swatch` / `text`(`color` 已废弃,降级为 `text`) |
| `tab` | 可选,归到「颜色 / 字体 / 版式 / 组件 / 其它」中的一栏;不写按顺序放进默认栏 |
| `min` / `max` / `step` | `range` / `number` 用 |
| `options` | `select` 用 |
| `swatches` | `swatch` 用:只允许从这里挑;写进 `defaults` 的值会被吸附到最近的 swatch |
板本身的能力(不用配置):整板收到 56×56 方块(点开)、标题拖动、右下角缩放、底栏**设为默认 / 恢复默认 / 撤回 / 复制 / 导出**、滚轮滚动(无可见滚动条)、位置与尺寸与折叠态记在 `localStorage`。
## 无障碍硬项
- 所有输入都是原生 `input`/`select`(可键盘聚焦、可 Tab 走完、有可见焦点环),不是 div 假控件。
- 每个控件有 `label`(点标签即聚焦控件)。
- 最小化方块 56×56、按钮命中区 ≥44px,鼠标与触屏都能点。
- `prefers-reduced-motion: reduce` 时小板不做位移/缩放动画(瞬时切换)。
- 焦点不留在折叠后的隐藏控件上:折叠时把焦点交回方块按钮。
## 反模式
- **把板挂到生产界面** —— 板是 lab 器具,终用户看到等于"半成品";只允许 `preview` 路径或 `?lab=1`。
- **颜色开自由取色** —— 一旦允许手输 hex,作品很快会漂到库外;颜色一律用 `swatch` + `swatches` 约束。
- **一个 key 全局复用** —— 多个 demo 用同一个 `slug` 会互相覆盖 localStorage;`slug` 必须唯一。
- **在 `onChange` 里重建 DOM / 重新 fetch** —— 拖一次 range 会触发几十次,必须只做赋值。
- **把 `params.json` 当真相源之外再抄一份默认值** —— 默认值只在 `params.json.defaults` 里写一次;代码里再写一遍就会出现两套默认。
- **用板替代验收** —— 板让你看到差别,不证明差别达标;对比度/字号地板/双视口仍要照跑项目锁里的断言。
## 来源与许可
- 出处:本机旧主题库 `components/param-panel/`(2026-07 起在多个 demo 里被复用,旧库时代被手抄出 12 份副本并漂移 —— 本包就是为终结手抄而收进公开包仓)。
- 许可:**MIT**(自产)。随包拷贝 `src/params.schema.json` 可拿去校验自己的 `params.json`。
- 变更历史:入包时重命名 CSS/JS 前缀为 `pp-`、localStorage 命名空间改为 `saiassets.param-panel.*`(不再沿用旧库前缀),并删掉库内 `.prev.*` 旧版;行为未改。
SKILL.md---
name: param-panel
description: Add a floating, schema-driven parameter panel to a demo or preview page — tabs, swatches constrained to a palette, sliders/selects/checkboxes, collapse to a 56px square, drag and resize, set-default / reset / undo / copy / export, state kept in localStorage. Use when a page has three or more tunable parameters and you need to see two levels of a value side by side instead of editing code, especially when colours must stay inside a fixed palette. Lab-only by design; never mount it on a production surface.
---
# param-panel · 悬浮调参板
一件作品的参数从代码里搬到一个**可拖、可缩放、可收起、可导出**的小板上:面板由 `params.schema` 生成(标签分栏 / swatch 受色板约束 / range / select / checkbox / text),
底栏能「设为默认 → 复制 → 粘回 `params.json`」,位置、尺寸、折叠态与当前值都记在 `localStorage`。
**它是 lab 器具**:`shouldMountLabPanel()` 只在路径像 `preview` 或 URL 带 `?lab=1` 时返回 true,widget 不会漏到生产界面。
## When to use
- 做**可调参 demo / 动效预览 / 主题试色页**:参数 ≥3 个,调一下就想立刻看到差别。
- 需要**给用户当场看两档**(圆角 0→28、阴影 0→40、速度 0.2→4s),比改代码截图快一个量级。
- 颜色必须**受色板约束**:`type: "swatch"` + `swatches: []`,写进来的值会被吸附到最近的 swatch,禁自由取色。
- 调完要**把值带走**:底栏「复制」给出可直接粘回 `params.json.defaults` 的 JSON。
## When not to use
- 面向最终用户的生产界面 —— 板是半成品外观,只允许 `preview` 路径或 `?lab=1`。
- 没有参数可调的静态页(白装一层壳)。
- 需要校验与提交的表单场景(用表单件,不要拿调参板当表单)。
- 移动端为主的阅读页:板会占掉画面右下角。
## Files
| path | use |
|---|---|
| `src/param-panel.js` | 无依赖 ES module:面板生成、拖/缩放/折叠、localStorage、导入导出(`mountParamPanel` / `shouldMountLabPanel`) |
| `src/param-panel.css` | 面板全部样式(`.pp-param-panel` 作用域;含可见焦点环与 `prefers-reduced-motion` 降级) |
| `src/params.schema.json` | `params.json` 的 JSON Schema,可直接拿去校验自己的参数文件 |
| `preview/index.html` | 活预览:面板驱动一张真卡(色/圆角/内边距/阴影/速度/标题/网格),含深浅切换 |
| `docs/usage.md` | 接入步骤 + `opts` 与 `schema` 参数表 + 无障碍硬项 + 反模式 |
| `scripts/assert_tokens.py` | 自检:元数据 / 声明路径 / 预览引用解析 / 前缀一致性 / 旧命名空间残留 |
## Agent apply steps
1. 拷 `src/param-panel.{css,js}` 进项目静态资源(`apply.py` 整包拷贝时已在包里);CSS 用 `<link>`,JS 用 `<script type="module">`。
2. 写 `params.json`:`{slug, version, defaults, schema[]}`。`slug` 必须**每个 demo 唯一**(localStorage 命名空间靠它)。
3. 写 `apply(values)`:只做赋值(写 CSS 变量 / 调引擎参数),**不要**在里面重建 DOM 或重新 fetch。
4. 生产先 `apply(params.defaults)`;仅当 `shouldMountLabPanel()` 为真才 `mountParamPanel({params, onChange: apply})`。
5. 想固定落点用 `corner: "tl"|"tr"|"bl"|"br"`;想改板头 `title`;`force: true/false` 可覆盖挂载判断。
6. 自检:`python packs/param-panel/scripts/assert_tokens.py`;页面侧查四件事 —— 拖一次滑块卡片立刻变、刷新后值还在、点最小化收成 56×56 方块能点开、Tab 能走完控件且焦点环可见。
## Hard rules
- **lab-only**:除 `preview` 路径与 `?lab=1` 外不许挂板;要临时关掉用 `?lab=0`。
- 颜色只用 `type: "swatch"` + `swatches`;禁止 free color(手输 hex 会让作品漂到库外)。
- 每个 demo 的 `slug` 唯一;禁止多页共用一个 slug(会互相覆盖 localStorage)。
- `onChange` 里只做赋值;拖动时会连发几十次,重活一律 debounce 到板外。
- 默认值只在 `params.json.defaults` 写一次;代码里再抄一份就会出现两套默认。
- 控件一律原生 `input` / `select`,不许 div 假控件;可见焦点环与减少动效降级两块在 `src/param-panel.css` 末尾,别删。
- 板让你看到差别,**不**证明差别达标:对比度 / 字号地板 / 双视口仍照跑项目锁里的断言。
- 包内不写盘符路径、内网地址、自有域名或客户代号。
- 消费后必须在项目 `design-lock.json` 记 `assets[]`(`pack` / `version` / `sha`)。