Files
workbuddy_skills/open-design/design-templates/tweaks/SKILL.md
T
admin 5afeb6b78a ④段整套同步(六节 + P/S/F/J + 模板)+ open-design 移到技能根
一、④段整套同步(用户选候选 A)
- 新增 `references/stage-proto-doc/assets/产品文档模板.html`(16 KB/247 行,从备份拷、转 LF)。
- ④段 SKILL.md 换成备份 15:28 那版,并按模板口径改齐:
  · 「产品文档框架」从「四块+一附录」改成**六节** —— 页面结构/功能清单/使用场景/操作流程/
    跨页与界面状态/待确认与偏离;与模板同构,⛔ 不从空白页重写结构。
  · 编号从 R/S/F 改成 **P 页面/S 场景/F 功能/J 二级功能**,各自唯一、按展示顺序通排。
  · 保留旧版有价值的要求:每项功能补齐 做什么/入口在哪/输入边界/异常与恢复;重点功能三处标红;
    横向流程图每步写清页面/功能/操作/完成判据。
  · 完成标准「七节」→「六节」,自查清单同步换掉「需求」那批条目,补模板与配图两条。
  · 路径 `designs/<项目>/` → `docs/pm/<项目>/ui/`(2 处)。
  · 补回本机那条「每份产出文档开头都要有一张图」的引用(指向主 skill,段内不复述)。
- 主 skill 同步:四段结构表④行、④段沿革、画图小节(④段画页面结构图 + 用户路径)。

二、open-design 移到技能根(用户选候选 A)
- `git mv product-planning/references/stage-delivery/assets/open-design → open-design/`(4178 文件)。
- `.gitignore` 白名单加 `!/open-design/` —— 否则移出去就不再纳入版本控制。
- 引用改 11 处:路径形态 `assets/open-design/...` → `<技能根>/open-design/...`;
  「随段携带的自带件」改称「同根技能」;「路径怎么找」那条改成自带件(段内相对路径)与
  同根技能(`<技能根>/<技能名>/`)两类分开写。
- open-design 自己的 ATTRIBUTION.md 两处同步(它现在与 oil-ui-pro 同层)。
- 副作用(有益):命名闸门读数由 2063 降到 225 —— 此前它把 open-design 里 919 个第三方 md
  当成规则一起扫了;现在只扫本包 23 个 md。已确认④段的 H1 与 4a/4b/4c 子步仍在检查范围内。

三、验收
- check_naming.py --ws E:/ProgramData/AIProject/contentm_agent = 通过 225 / 失败 0 / 提示 27。
- 活文件里 `assets/open-design` 残留 0;工作区 CRLF 文件 0。
2026-10-15 21:17:28 +08:00

305 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: tweaks
description: |
Wrap any HTML artifact with a side panel of live, parameterized
controls — accent color, type scale, density, motion, theme — that
rewrite CSS custom properties in real time and persist to
localStorage. Lets the user explore variants of a design without
re-prompting the agent. Use when the brief asks for "variants",
"side-by-side options", "tweak this", "let me adjust", "live
knobs", or "实时调参".
triggers:
- "tweaks"
- "variants"
- "tweak panel"
- "live controls"
- "adjust on the fly"
- "实时调参"
- "可调参数面板"
- "side panel"
- "knobs"
od:
mode: prototype
platform: desktop
scenario: design
upstream: "https://github.com/alchaincyf/huashu-design"
preview:
type: html
entry: index.html
design_system:
requires: false
example_prompt: "Wrap this landing page with a tweak panel — accent color, type scale, density, light/dark — persist to localStorage so the user can refresh without losing their choice."
---
# Tweaks Skill · 参数化变体面板
Wrap any HTML artifact with a side panel of live controls that rewrite
CSS custom properties in real time and persist to `localStorage`.
Inspired by the *huashu-design* tweak pattern.
## What you produce
A single self-contained HTML file with two layers:
1. **Stage** — the original artifact (landing page / deck / dashboard)
re-keyed so all visual decisions read from CSS custom properties:
`--accent`, `--scale`, `--density`, `--mode`, `--motion`.
2. **Panel** — a fixed sidebar (or drawer on small viewports) with
form controls bound to those custom properties via a tiny
vanilla-JS bridge. Persists every change to `localStorage` keyed
by the artifact identifier.
The user can:
- Open the artifact and see the stage rendered with their saved
preferences (or sensible defaults).
- Adjust accent / scale / density / mode / motion in the panel and
watch the stage update instantly — no rerender.
- Press <kbd>T</kbd> to hide / reveal the panel; <kbd>R</kbd> to
reset to defaults.
- Refresh the page — every choice is persisted.
## When to use
- The user generated something they like 80% of, and wants to dial
in the last 20% themselves.
- You're presenting a design system / brand and want the audience to
feel the variants live (instead of you re-running the agent).
- You're shipping a stand-alone demo (e.g. a portfolio piece) and
want viewers to play.
## When *not* to use
- One-shot artifacts that won't be iterated on (e.g. a runbook —
parameters don't help).
- When the artifact's value is in fixed ratios (e.g. an infographic
with carefully balanced data viz — knobs would degrade it).
## The 5 standard knobs
> Pick a subset that suits the artifact. Don't ship all 5 if only 2
> matter — clutter is a regression.
### 1. `--accent` — Accent color
A select with 5–8 curated swatches (don't ship a free color picker —
the user will pick a bad color and blame you).
```js
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' },
];
```
The artifact uses `var(--accent)` everywhere it had a hard-coded
accent before. Border / link / pull-quote rule / CTA all flip
together.
### 2. `--scale` — Type scale (0.85 / 1.0 / 1.15)
Three settings: *Compact* (0.85), *Normal* (1.0), *Generous* (1.15).
All `font-size` declarations multiply by `var(--scale)` via
`calc(... * var(--scale))`.
Don't go beyond ±15% — beyond that the layout breaks (column flow,
breakpoints, line counts).
### 3. `--density` — Layout density (Tight / Normal / Roomy)
Three settings that swap the spacing scale: *Tight* (0.75) /
*Normal* (1.0) / *Roomy* (1.4). All `padding` / `gap` / `margin`
declarations multiply by `var(--density)`.
This is the highest-impact knob — it's also the most fragile, so
**every layout-critical container must declare its base spacing in
custom properties** before you wrap.
### 4. `--mode` — Light / Dark
A 2-state toggle. Sets `data-mode="light"` vs `"dark"` on the
`<html>` element and the artifact's `:root` selector responds with
two color sets.
If the artifact already has a media-query-based dark mode, *replace*
it with the data-attr version — the user's choice should win over
their OS.
### 5. `--motion` — Off / Subtle / Lively
Three settings. Maps to a CSS variable `--motion-mult` that scales
all `transition-duration` / `animation-duration` declarations:
- *Off* — `0s` (also disables WebGL canvases / decorative animation).
- *Subtle* — `1.0` (the artifact's authored timing).
- *Lively* — `1.6` (slower transitions, more visible motion).
Respect `prefers-reduced-motion`: default to *Off* if the user has
that set, regardless of stored preference.
## Host integration contract (REQUIRED)
The OpenDesign viewer toolbar has a **Tweaks** toggle that drives panel
visibility from outside the iframe. For the toggle to bind to your panel,
your artifact **must** speak one of these two protocols (pick one; don't
mix). The toolbar enables itself the moment it sees either signal.
### Protocol A — postMessage (recommended for agent-generated artifacts)
Use this when the panel mounts via JS (React, vanilla, anything dynamic).
**Artifact → host:**
- On mount, post `{ type: '__edit_mode_available', visible?: boolean }` to
`window.parent`. Tells the toolbar a panel exists; the optional `visible`
reports the panel's initial state so the toolbar toggle starts in sync.
Omit `visible` for the common "panel is already on screen" case (the host
treats a missing field as `true` so the legacy zero-arg message keeps
working). Pass `visible: false` to declare a default-closed panel.
- When the user closes the panel locally (× button, Esc, etc.), post
`{ type: '__edit_mode_dismissed' }`. Toolbar flips to "off".
**Host → artifact:**
- `{ type: '__activate_edit_mode' }` — open the panel (`setOpen(true)`).
- `{ type: '__deactivate_edit_mode' }` — close the panel (`setOpen(false)`).
Minimal listener:
```js
window.addEventListener('message', (e) => {
const t = e?.data?.type;
if (t === '__activate_edit_mode') setOpen(true);
else if (t === '__deactivate_edit_mode') setOpen(false);
});
// Or, for a default-closed panel:
// window.parent.postMessage({ type: '__edit_mode_available', visible: open }, '*');
window.parent.postMessage({ type: '__edit_mode_available' }, '*');
// in your close handler:
const dismiss = () => {
setOpen(false);
window.parent.postMessage({ type: '__edit_mode_dismissed' }, '*');
};
```
Panel may default to open or closed — the host syncs its toggle to
whichever state the artifact reports.
### Protocol B — class-based (used by `assets/wrap.html`)
Use this only when you wrap the template verbatim. The artifact ships a
`.tw-panel` element and toggles a `.tw-hidden` class for visibility. The
viewer's iframe bridge (in `apps/web/src/runtime/srcdoc.ts`) hides the
panel on initial paint, watches the class via `MutationObserver`, and
relays state both directions. No JS required in the artifact beyond what
the template already includes.
Selectors are fixed: `.tw-panel` (the panel root) and `.tw-hidden` (the
hidden state). If you rename either, the bridge can't find it.
### Anti-pattern
Don't invent a third protocol or rename either set of identifiers. The
toolbar toggle only binds to A or B. Custom panels with custom classes
and no postMessage will leave the toolbar greyed out.
## Implementation primitives
Read `assets/wrap.html` — it ships the panel + bridge as an
inert template. Your job is to:
1. Take the user's existing artifact HTML.
2. Lift its accent / mode / spacing / scale into custom properties
(search for hard-coded `#hex` / `Npx` / `Nrem` and convert).
3. Paste the contents into the marked region of `wrap.html`.
4. Edit `assets/wrap.html`'s `KNOBS` array to keep only the knobs
you decided are relevant to *this* artifact. Don't ship 5 if 2
matter.
5. Patch the `STORAGE_KEY` to a unique slug (`tweaks-<artifact-slug>`).
The bridge in `wrap.html`:
- Loads `localStorage[STORAGE_KEY]` JSON on first paint.
- Applies values as `document.documentElement.style.setProperty('--accent', ...)`.
- Listens to every form control's `change` event and writes back.
- Exposes <kbd>T</kbd> (toggle panel) and <kbd>R</kbd> (reset).
## Workflow
### Step 1 — Acquire the artifact
Same options as the critique skill:
1. Project file (`index.html` in the project folder).
2. Pasted HTML in the chat.
3. Generated by you in this turn.
### Step 2 — Decide which knobs apply
Read the artifact's CSS first. For each knob, decide *yes / no*:
- `--accent` — yes if the artifact has 1 accent color used ≥ 3 times.
- `--scale` — yes if the artifact is type-driven (article, deck,
pricing page).
- `--density` — yes if the artifact has consistent gap / padding
rhythm (deck, dashboard, landing). No for runbooks (already dense).
- `--mode` — yes if the artifact has authored dark mode tokens, or
you're willing to derive them.
- `--motion` — yes if the artifact has any transition / animation
worth scaling. No for static reports / critique reports.
Default: **3 knobs is the sweet spot.** Five is too busy, one is
not worth a panel.
### Step 3 — Lift hard-coded values into custom properties
Open `assets/wrap.html`'s `<style>` block — copy its custom-property
naming scheme (`--accent`, `--scale`, etc.). In the user's artifact,
find every place those concerns live and rewrite:
- `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))`
If the artifact uses `clamp()` or `vw` already, multiply the
*outer* value by the custom property — don't tear apart `clamp(...)`.
### Step 4 — Paste into the wrap
Copy the artifact's `<style>` and `<body>` into the marked regions
of `wrap.html`. Keep the panel + bridge intact.
### Step 5 — Test the loop
Open the result, click each knob at least once, refresh the page,
confirm the choice persists. If a knob breaks the layout —
*remove it*, don't ship it.
## Output contract
```
<artifact identifier="tweaks-<artifact-slug>" type="text/html" title="<Artifact Title> · Tweaks">
<!doctype html>
<html>...</html>
</artifact>
```
One sentence before the artifact ("Wrapped X with a 3-knob tweak
panel — accent / scale / mode."). Stop after `</artifact>`.
## Hard rules
- **Don't ship a free color picker** — only curated swatches. Users
pick bad colors when given freedom; saving them from that is the
whole point.
- **Persist by artifact identifier** — `tweaks-<slug>`, not a global
key. Two artifacts open in two tabs must not share state.
- **Respect `prefers-reduced-motion`** — default to *Off* for motion
if the user has that set, override only on explicit click.
- **Single-file** — no external CSS / JS / fonts beyond the artifact's
existing imports. Inline the panel + bridge.
- **Panel hidden by default on viewports < 720px** — slide-in drawer
via a "T" button at top-right.
- **Don't ship more than 5 knobs.** Three is the sweet spot.