Huashu Design 的 Tweaks 系统:用纯前端 localStorage 实现设计变体实时调参的完整指南
Huashu Design 的 Tweaks 系统用纯前端 localStorage 实现设计变体实时调参的完整指南【免费下载链接】huashu-designHuashu Design · HTML-native design skill for Claude Code · Claude Code 里 HTML 原生的设计 skill · 高保真原型 / 幻灯片 / 动画 20 设计哲学 5 维评审 MP4 导出 · Agent-agnostic项目地址: https://gitcode.com/gh_mirrors/hu/huashu-design导读Tweaks 是 Huashu Design 这套 HTML 原生设计 skill 中的核心能力之一让用户不改一行代码即可实时切换设计 variations、调整视觉参数。本文以 references/tweaks-system.md 为骨架结合仓库中的 SKILL.md、references/workflow.md 与 demos/c4-tweaks.html 演示源码完整讲解其设计动机、纯前端实现useTweaks 状态管理 浮动面板 UI CSS 变量应用、不同设计品类的典型调参选项、四条设计原则以及面向源码级持久化 host 的向前兼容方案。读完本文你可以在任意 HTML 设计稿中复刻这套「拨动即所得」的调参体验。一、Tweaks 是什么设计变体的「遥控器」Tweaks 是这个 skill 里很核心的能力——让用户不改代码就能实时切换 variations / 调整参数。它的本质是一个把设计参数化的交互层设计师或 agent把主色、字号、密度、明暗模式、布局变体等「设计自由度」暴露成可操作的控件用户拖动滑块、点选开关设计稿即时响应。在 Huashu Design 的能力矩阵中这项能力被定位为「设计变体」交付物——README 的能力表将其描述为「3 并排对比 · Tweaks 实时调参 · 跨维度探索」见 README.md 的「能做什么」一节。它在工作流中的位置也很明确SKILL.md 的「给 variations不给『最终答案』」原则规定纯视觉对比用design_canvas.jsx并排展示而交互流程 / 多选项差异则做成完整原型把选项做成 Tweaks。跨 agent 环境适配localStorage 而非 postMessage部分 design-agent 原生环境如 Claude.ai Artifacts依赖 host 的postMessage把 tweak 值回写源码做持久化。本 skill 采用纯前端 localStorage 方案——效果一致刷新保留状态但持久化发生在浏览器 localStorage 而不是源码文件。这个方案在任何 agent 环境Claude Code / Codex / Cursor / Trae / etc.都能工作。这一取舍在 SKILL.md 的适配说明中有直接对应「没有 Tweaks host postMessage改成纯前端 localStorage 版详见references/tweaks-system.md」。也就是说这不是一个实现细节的偶然选择而是 skill 刻意做出的跨环境兼容设计决策。二、何时加 Tweaks并不是每个设计都需要 Tweaks添加时机遵循三条标准用户明确要求「能调参」/「多个版本切换」设计有多个 variations 需要对比时用户没明说但你主观判断加几个有启发性的 tweaks 能帮用户看到可能性。默认推荐每个设计都加 2-3 个 tweaks颜色主题 / 字号 / layout 变体即使用户没要求——让用户看到可能性空间是设计服务的一部分。这与 workflow 中的「探索可能性空间」理念一脉相承变体不是制造选择困难而是让用户 mix and match 出最终版本见 references/workflow.md。三、实现方式纯前端版3.1 基本结构useTweaks Hook核心是一个useTweaks自定义 Hook负责状态初始化、更新与 localStorage 持久化const TWEAK_DEFAULTS { primaryColor: #D97757, fontSize: 16, density: comfortable, dark: false }; function useTweaks() { const [tweaks, setTweaks] React.useState(() { try { const stored localStorage.getItem(design-tweaks); return stored ? { ...TWEAK_DEFAULTS, ...JSON.parse(stored) } : TWEAK_DEFAULTS; } catch { return TWEAK_DEFAULTS; } }); const update (patch) { const next { ...tweaks, ...patch }; setTweaks(next); try { localStorage.setItem(design-tweaks, JSON.stringify(next)); } catch {} }; const reset () { setTweaks(TWEAK_DEFAULTS); try { localStorage.removeItem(design-tweaks); } catch {} }; return { tweaks, update, reset }; }实现要点逐行拆解代码段作用细节说明TWEAK_DEFAULTS默认参数表是「完成设计」的锚点详见后文设计原则第 3 条useState惰性初始化恢复上次状态用展开运算符{ ...TWEAK_DEFAULTS, ...JSON.parse(stored) }做增量合并即使后续新增了 tweak 字段老用户的 localStorage 也不会因缺键而崩溃try/catch包裹读写容错无痕模式 / 隐私模式下 localStorage 可能不可用或抛异常必须 catch否则整个组件树会崩update(patch)部分更新只传要改的键内部合并天然支持「多个控件各改各的」reset()一键还原清空 localStorage 并回到默认值3.2 Tweaks 面板 UI右下角浮动面板默认折叠只显示一个小按钮用户点击才展开这同时解决了「面板挡住设计内容」的问题function TweaksPanel() { const { tweaks, update, reset } useTweaks(); const [open, setOpen] React.useState(false); return ( div style{{ position: fixed, bottom: 20, right: 20, zIndex: 9999, }} {open ? ( div style{{ background: white, border: 1px solid #e5e5e5, borderRadius: 12, padding: 20, boxShadow: 0 10px 40px rgba(0,0,0,0.12), width: 280, fontFamily: system-ui, fontSize: 13, }} div style{{ display: flex, justifyContent: space-between, alignItems: center, marginBottom: 16, }} strongTweaks/strong button onClick{() setOpen(false)} style{{ border: none, background: none, cursor: pointer, fontSize: 16, }}×/button /div {/* 颜色 */} label style{{ display: block, marginBottom: 12 }} div style{{ marginBottom: 4, color: #666 }}主色/div input typecolor value{tweaks.primaryColor} onChange{e update({ primaryColor: e.target.value })} style{{ width: 100%, height: 32 }} / /label {/* 字号slider */} label style{{ display: block, marginBottom: 12 }} div style{{ marginBottom: 4, color: #666 }}字号 ({tweaks.fontSize}px)/div input typerange min{12} max{24} step{1} value{tweaks.fontSize} onChange{e update({ fontSize: e.target.value })} style{{ width: 100% }} / /label {/* 密度选项 */} label style{{ display: block, marginBottom: 12 }} div style{{ marginBottom: 4, color: #666 }}密度/div select value{tweaks.density} onChange{e update({ density: e.target.value })} style{{ width: 100%, padding: 6 }} option valuecompact紧凑/option option valuecomfortable舒适/option option valuespacious宽松/option /select /label {/* 暗黑模式toggle */} label style{{ display: flex, alignItems: center, gap: 8, marginBottom: 16, }} input typecheckbox checked{tweaks.dark} onChange{e update({ dark: e.target.checked })} / span暗黑模式/span /label button onClick{reset} style{{ width: 100%, padding: 8px 12px, background: #f5f5f5, border: none, borderRadius: 6, cursor: pointer, fontSize: 12, }}重置/button /div ) : ( button onClick{() setOpen(true)} style{{ background: #1A1A1A, color: white, border: none, borderRadius: 999, padding: 10px 16px, fontSize: 12, cursor: pointer, boxShadow: 0 4px 12px rgba(0,0,0,0.15), }} ⚙ Tweaks/button )} /div ); }注意几个值得借鉴的细节四类原生控件各司其职typecolor取主色、typerange调字号min12 max24 step1注意onChange里用e.target.value把字符串转数字、select做离散选项、typecheckbox做开关——原生控件意味着零额外依赖、任何环境都能渲染折叠按钮独立样式展开面板是白底圆角卡片折叠态是黑底胶囊按钮borderRadius: 999视觉上清晰区分两种状态position: fixed; zIndex: 9999保证面板永远浮在内容之上不受页面内部层叠上下文干扰。3.3 应用 TweaksCSS 变量桥接在主组件里调用useTweaks()拿到状态把它映射为 CSS 自定义属性变量和顶层背景/文字色function App() { const { tweaks } useTweaks(); return ( div style{{ --primary: tweaks.primaryColor, --font-size: ${tweaks.fontSize}px, background: tweaks.dark ? #0A0A0A : #FAFAFA, color: tweaks.dark ? #FAFAFA : #1A1A1A, }} {/* 你的内容 */} TweaksPanel / /div ); }CSS 里用变量button.cta { background: var(--primary); color: white; font-size: var(--font-size); }这是整套方案「零改动、纯响应」的关键机制tweak 值只写进:root级别的 CSS 变量所有组件通过var(--*)消费。当用户拖动滑块时React 重新渲染 → 变量更新 → 整棵组件树样式自动联动无需给每个组件单独传 props。仓库演示 demos/c4-tweaks.html 也采用了同构思路——不过它的实现更直接demo 把三个 slider调色 warm/cool、字型 serif/sans、密度 sparse/dense映射为 preview 元素上的warm/cool/sans/dense四个 CSS 类通过classList切换类来驱动整版 mock landing page 的视觉变化见该文件updatePreview()逻辑demos/c4-tweaks.html。3.4 仓库里的真实形态c4-tweaks 演示作为「能力演示 c*」系列之一demos/c4-tweaks.html 是一个 10 秒的产品展示动画左侧是一张模拟的 landing page 设计稿右侧是三个滑动条调色、字型、密度动画逐帧演示「光标移动到滑块 → 拖动 → 设计稿即时变化」的完整交互过程时间轴定义见 demos/c4-tweaks.html。它印证了本系统的核心设定Tweaks 的操控对象是真实的设计稿而非抽象的配置表单——用户在调参过程中直接看到设计在变这就是「拨动即所得」。四、典型 Tweak 选项按设计品类配置不同的设计类型暴露不同的调参维度。原文给出五类配置清单这里逐类展开并补充参数建议通用任何设计都可加主色color picker字号slider 12-24px字型selectdisplay font vs body font暗黑模式toggle幻灯片 deck主题light / dark / brand背景样式solid / gradient / image字体对比更装饰 vs 更克制信息密度minimal / standard / dense产品原型布局变体layout A / B / C交互速度animation speed 0.5x-2x数据量mock 数据条数 5/20/100状态empty / loading / success / error——这一项特别适合演示「空态、加载、成功、失败」四种产品状态动画速度0.5x-2x循环once / loop / ping-pongEasinglinear / easeOut / springLanding pageHero 风格image / gradient / pattern / solidCTA 文案几种变体结构single column / two column / sidebar五、Tweaks 设计原则四条硬约束1. 有意义的选项不是折腾人的每个 tweak 必须展示真实的设计选项。别加那种谁都不会真切换的 tweak比如 border-radius 0-50px 的 slider——用户调完发现所有中间值都丑。好的 tweak 暴露离散的、有思考的 variations✅「圆角风格」无圆角 / 微圆角 / 大圆角三个选项❌ 不是「圆角」0-50px slider这条原则的本质是slider 只用于「连续且每个中间值都有设计意义」的维度如字号、速度而「审美判断类」维度如圆角、字型应该用离散枚举。连续 slider 会诱导用户探索无意义中间值最终得到一堆丑的中间态离散选项则让每次切换都是「一个有完整设计的变体」。2. 少即是多一个设计的 Tweaks 面板最多 5-6 个选项。再多就变成「配置页面」失去了快速探索 variations 的意义。3. 默认值是完成设计Tweaks 是锦上添花。默认值必须本身就是一个完整、可发布的设计。用户关闭 Tweaks 面板后看到的就是产出——也就是说默认状态 ≠「未调参的初始状态」而是「我们最推荐的一版设计」。4. 合理分组选项多时分组显示用分隔线或小标题组织---- 视觉 ---- 主色 | 字号 | 暗黑模式 ---- 布局 ---- 密度 | 侧栏位置 ---- 内容 ---- 显示数据量 | 状态分组降低了面板的认知负担用户按「视觉 / 布局 / 内容」的维度快速定位目标参数而不是在一堆平铺控件里找。六、向前兼容源码级持久化 host如果你以后想把设计上传到支持源码级 tweaks如 Claude.ai Artifacts的环境也能跑保留EDITMODE 标记块const TWEAK_DEFAULTS /*EDITMODE-BEGIN*/{ primaryColor: #D97757, fontSize: 16, density: comfortable, dark: false }/*EDITMODE-END*/;标记块在 localStorage 方案里无作用只是个普通注释但在支持源码回写的 host 里会被读取实现源码级持久化。加上这个对当前环境无害同时保持向前兼容。这是一个典型的「双轨兼容」设计同一份代码在普通浏览器环境走 localStorage在 Artifacts 这类环境走 host 回写——标记块充当了两套持久化机制的「约定接口」。七、常见问题与排障Tweaks 面板挡住设计内容→ 让它可关闭。默认关闭显示一个小按钮用户点了才展开。用户切换 tweaks 后还要重复设置→ 已经用 localStorage。如果刷新后不持久检查 localStorage 是否可用无痕模式会失败要 catch。多个 HTML 页面想共享 tweaks→ 给 localStorage key 加 project namedesign-tweaks-[projectName]。这样同一项目的多页设计能共享同一套参数而不同项目互不污染——这也是 SKILL.md 中「固定尺寸内容的播放位置存 localStorage」同款思想的延伸localStorage 是按 key 隔离的命名空间前缀约定就是「项目级隔离」的惯例。我想让 tweak 之间有联动关系→ 在update里加逻辑const update (patch) { let next { ...tweaks, ...patch }; // 联动选dark mode时自动切换字体配色 if (patch.dark true !patch.textColor) { next.textColor #F0EEE6; } setTweaks(next); localStorage.setItem(...); };联动逻辑放在update内部而非组件层好处是所有入口滑块、下拉、toggle、以及未来的外部调用都会自动经过同一套联动规则不会出现「这个控件改了但那个控件没跟着变」的分叉。八、在 Huashu Design 工作流中的位置最后把 Tweaks 放回整套 skill 的工作流语境中便于你理解它的使用时机流程位置SKILL.md 的 Full pass 阶段规定「填 placeholder做 variations加 Tweaks」——Tweaks 是主干流程的收尾步骤而不是起点查询索引SKILL.md 的参考文献表把「做 Tweaks 实时调参」直接映射到本主题文档references/tweaks-system.md说明它是被 skill 主文档正式引用的一级能力变体矩阵references/workflow.md 给出的「探索矩阵」为 Tweaks 的维度选择提供了上游思考框架——视觉 / 色彩 / 字型 / Layout / Density / 交互 / 材质七大维度中挑 2-3 个来设计 variations再决定哪些维度适合做成 Tweaks 控件。综上Tweaks 系统是 Huashu Design 中「探索可能性空间」理念的落地载体通过「纯前端 localStorage 持久化 CSS 变量桥接 EDITMODE 向前兼容」四件套它把原本需要改代码才能完成的设计实验压缩成了用户随手一拨的实时交互——这正是 README 所说「拨动即所得」的体验来源。需要完整对照源码时可深入阅读 references/tweaks-system.md、SKILL.md 与 demos/c4-tweaks.html。【免费下载链接】huashu-designHuashu Design · HTML-native design skill for Claude Code · Claude Code 里 HTML 原生的设计 skill · 高保真原型 / 幻灯片 / 动画 20 设计哲学 5 维评审 MP4 导出 · Agent-agnostic项目地址: https://gitcode.com/gh_mirrors/hu/huashu-design创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考