11 KiB
name, description, triggers, od
| name | description | triggers | od | |||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| tweaks | 将任意 HTML 制品包裹在一个侧面板中,提供实时的、参数化的控件——强调色、字号比例、密度、动效、主题——这些控件会实时改写 CSS 自定义属性,并持久化到 localStorage。让用户可以在不重新提示 agent 的情况下探索设计的各种变体。当需求中提到「变体」「并排选项」「调一下」「让我调整」「实时 knob」「实时调参」时使用。 |
|
|
Tweaks Skill · 参数化变体面板
将任意 HTML 制品包裹在一个侧面板中,提供实时控件来改写 CSS 自定义属性,并持久化到 localStorage。灵感来自 huashu-design 的调参模式。
产出内容
一个自包含的单一 HTML 文件,包含两层:
- 舞台(Stage)——原始制品(着陆页 / 幻灯片 / 仪表板),经过改写,使所有视觉决策都从 CSS 自定义属性中读取:
--accent、--scale、--density、--mode、--motion。 - 面板(Panel)——一个固定侧边栏(小视口下为抽屉式),包含绑定到这些自定义属性的表单控件,通过一个极简的 vanilla-JS 桥接层实现。每次更改都会按制品标识持久化到
localStorage。
用户可以:
- 打开制品,看到舞台以其保存的偏好(或合理的默认值)呈现。
- 在面板中调整强调色 / 字号比例 / 密度 / 模式 / 动效,舞台立即更新——无需重渲染。
- 按 T 隐藏/显示面板;按 R 重置为默认值。
- 刷新页面——所有选择都会持久保留。
何时使用
- 用户生成的内容他们喜欢 80%,剩下的 20% 想自己微调。
- 你在展示设计系统/品牌,希望观众实时感受变体效果(而不是让你重新运行 agent)。
- 你在交付一个独立演示(例如作品集项目),希望观众可以交互体验。
何时不要使用
- 一次性制品,无需迭代(例如 runbook——参数无意义)。
- 制品的价值在于固定比例(例如数据可视化信息图,经过精心平衡——加上 knob 反而会破坏它)。
5 个标准 knob
选择与制品匹配的子集。如果只有 2 个重要,不要全部 5 个都上——杂乱就是退步。
1. --accent——强调色
一个包含 5–8 种精选色板的下拉选择(不要提供一个自由颜色选择器——用户会选到糟糕的颜色然后怪你)。
const ACCENT_PRESETS = [
{ id: 'rust', val: '#c96442', label: 'Rust' },
{ id: 'cobalt', val: '#2c4d8e', label: 'Cobalt' },
{ id: 'sage', val: '#4a7a3f', label: 'Sage' },
{ id: 'plum', val: '#7a3f6a', label: 'Plum' },
{ id: 'graphite',val: '#3a3a3a', label: 'Graphite' },
];
制品中所有原先硬编码强调色的地方都使用 var(--accent)。边框 / 链接 / 引用块分割线 / CTA 全部同步切换。
2. --scale——字号比例(0.85 / 1.0 / 1.15)
三种设置:紧凑(0.85)、正常(1.0)、宽松(1.15)。所有 font-size 声明通过 calc(... * var(--scale)) 乘以 var(--scale)。
不要超过 ±15%——超出后布局会破坏(列流、断点、行数)。
3. --density——布局密度(紧凑 / 正常 / 宽敞)
三种设置,切换间距比例:紧凑(0.75)/ 正常(1.0)/ 宽敞(1.4)。所有 padding / gap / margin 声明乘以 var(--density)。
这是影响最大的 knob——也是最脆弱的,因此每个布局关键容器必须在包裹之前将其基础间距声明为自定义属性。
4. --mode——浅色 / 深色
一个二态切换。在 <html> 元素上设置 data-mode="light" 与 "dark",制品的 :root 选择器以两组颜色集响应。
如果制品已经有基于媒体查询的深色模式,替换为 data-attr 版本——用户的选择应优先于其操作系统。
5. --motion——关闭 / 微妙 / 生动
三种设置。映射到一个 CSS 变量 --motion-mult,用于缩放所有 transition-duration / animation-duration 声明:
- 关闭——
0s(同时禁用 WebGL canvas / 装饰性动画)。 - 微妙——
1.0(制品原生的时间设定)。 - 生动——
1.6(较慢的过渡,更明显的动效)。
尊重 prefers-reduced-motion:如果用户设置了该偏好,默认使用关闭,无论存储的偏好是什么。
宿主集成协议(必读)
Open Design 查看器工具栏有一个 Tweaks 开关,用于从 iframe 外部驱动面板可见性。要让该开关绑定到你的面板,你的制品必须采用以下两种协议之一(选其一,不要混用)。工具栏一旦检测到任一信号就会启用自身。
协议 A——postMessage(推荐用于 agent 生成的制品)
当面板通过 JS(React、vanilla、任意动态方式)挂载时使用。
制品 → 宿主:
- 挂载时,向
window.parent发送{ type: '__edit_mode_available', visible?: boolean }。告知工具栏存在面板;可选的visible报告面板的初始状态,使工具栏开关一开始就同步。省略visible适用于常见的"面板已在屏幕上"的情况(宿主将缺失字段视为true,以保证旧版无参数消息继续工作)。传递visible: false以声明默认关闭的面板。 - 当用户本地关闭面板(× 按钮、Esc 等)时,发送
{ type: '__edit_mode_dismissed' }。工具栏切换为"关闭"。
宿主 → 制品:
{ type: '__activate_edit_mode' }——打开面板(setOpen(true))。{ type: '__deactivate_edit_mode' }——关闭面板(setOpen(false))。
最小监听器:
window.addEventListener('message', (e) => {
const t = e?.data?.type;
if (t === '__activate_edit_mode') setOpen(true);
else if (t === '__deactivate_edit_mode') setOpen(false);
});
// 或者,对于默认关闭的面板:
// window.parent.postMessage({ type: '__edit_mode_available', visible: open }, '*');
window.parent.postMessage({ type: '__edit_mode_available' }, '*');
// 在关闭处理程序中:
const dismiss = () => {
setOpen(false);
window.parent.postMessage({ type: '__edit_mode_dismissed' }, '*');
};
面板可以默认打开或关闭——宿主会将其开关同步到制品报告的状态。
协议 B——基于 class(由 assets/wrap.html 使用)
仅在直接包裹模板时使用。制品包含一个 .tw-panel 元素,并通过切换 .tw-hidden 类来控制可见性。查看器的 iframe 桥接层(位于 apps/web/src/runtime/srcdoc.ts)在初始渲染时隐藏面板,通过 MutationObserver 监听该类,并在两个方向中继状态。制品无需额外 JS,模板已包含所需内容。
选择器是固定的:.tw-panel(面板根元素)和 .tw-hidden(隐藏状态)。如果重命名其中任何一个,桥接层将无法找到它。
反模式
不要发明第三种协议,也不要重命名任何一组标识符。工具栏开关只绑定到 A 或 B。使用自定义 class 且没有 postMessage 的自定义面板将导致工具栏变灰不可用。
实现原语
阅读 assets/wrap.html——它将面板 + 桥接层作为惰性模板提供。你的工作是:
- 获取用户现有的制品 HTML。
- 将其强调色 / 模式 / 间距 / 字号提升为自定义属性(搜索硬编码的
#hex/Npx/Nrem并转换)。 - 将内容粘贴到
wrap.html的标记区域。 - 编辑
assets/wrap.html的KNOBS数组,只保留你确定与这个制品相关的 knob。如果只有 2 个重要,不要全部 5 个都上。 - 将
STORAGE_KEY改为一个唯一的 slug(tweaks-<artifact-slug>)。
wrap.html 中的桥接层:
- 首次渲染时加载
localStorage[STORAGE_KEY]JSON。 - 将值作为
document.documentElement.style.setProperty('--accent', ...)应用。 - 监听每个表单控件的
change事件并写回。 - 暴露 T(切换面板)和 R(重置)。
工作流程
步骤 1——获取制品
与 critique skill 相同的选项:
- 项目文件(项目文件夹中的
index.html)。 - 粘贴到聊天中的 HTML。
- 由你在此轮对话中生成。
步骤 2——决定哪些 knob 适用
先阅读制品的 CSS。对每个 knob,决定是 / 否:
--accent——如果制品有 1 个使用了 ≥ 3 次的强调色,则为是。--scale——如果制品以文字驱动(文章、幻灯片、定价页),则为是。--density——如果制品有一致的 gap / padding 节奏(幻灯片、仪表板、着陆页),则为是。runbook 不需要(已经很密集了)。--mode——如果制品有原创的深色模式 token,或者你愿意为其推导,则为是。--motion——如果制品有任何值得缩放的 transition / animation,则为是。静态报告 / critique 报告不需要。
默认:3 个 knob 是最佳选择。 五个太杂,一个不值得做一个面板。
步骤 3——将硬编码值提升为自定义属性
打开 assets/wrap.html 的 <style> 块——复制其自定义属性命名方案(--accent、--scale 等)。在用户的制品中,找到所有相关位置并改写:
color: #c96442→color: var(--accent)font-size: 18px→font-size: calc(18px * var(--scale))padding: 24px 32px→padding: calc(24px * var(--density)) calc(32px * var(--density))transition: opacity 200ms→transition: opacity calc(200ms * var(--motion-mult))
如果制品已经使用了 clamp() 或 vw,将外层值乘以自定义属性——不要拆解 clamp(...)。
步骤 4——粘贴到包裹模板中
将制品的 <style> 和 <body> 复制到 wrap.html 的标记区域。保持面板 + 桥接层完整。
步骤 5——测试循环
打开结果,点击每个 knob 至少一次,刷新页面,确认选择已持久化。如果某个 knob 破坏了布局——移除它,不要发布。
输出约定
<artifact identifier="tweaks-<artifact-slug>" type="text/html" title="<Artifact Title> · Tweaks">
<!doctype html>
<html>...</html>
</artifact>
在 artifact 之前放一句话("已将 X 包裹上 3-knob 调参面板——强调色 / 字号比例 / 模式。")。在 </artifact> 之后结束。
硬性规则
- 不要提供自由颜色选择器——只提供精选色板。用户有了自由度就会选糟糕的颜色;帮他们避免这一点就是全部意义所在。
- 按制品标识持久化——使用
tweaks-<slug>,而不是全局键。两个选项卡中打开的两个制品不得共享状态。 - 尊重
prefers-reduced-motion——如果用户设置了该偏好,动效默认使用关闭,仅在显式点击时覆盖。 - 单文件——除了制品已有的导入外,无外部 CSS / JS / 字体。将面板 + 桥接层内联。
- 在小于 720px 的视口上,面板默认隐藏——通过右上角的"T"按钮滑入抽屉。
- 不要发布超过 5 个 knob。 3 个是最佳选择。