mcpField
P
paint

paint

github.com/athevon设计与 UI6981.4k 次安装3 周前更新
代码仓库

关于 paint

paint 是用于构建设计项目整体视觉风格的 Skill,流程包括头脑风暴、设计系统、实现和审查,并按页面推进;孤立组件或效果可走精简流程。

主要功能

  1. 01强制头脑风暴并确认设计方向
  2. 02生成 MASTER.md 和令牌文件
  3. 03逐页实现与验证
  4. 04在视觉检查点选择预览方式
  5. 05必须执行设计审查

适用场景

  1. 01建立 Web 应用的整体视觉风格
  2. 02创建并应用设计系统
  3. 03按已确认的方向实现页面
  4. 04调整单个组件或动画效果

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/athevon/genjutsu --skill paint

安装后在 Claude Code 中输入/paint确认已加载。

详细介绍

Paint - 大师画师

绘制一个完整的视觉宇宙。先集思广益,再设计系统,随后实现,最后审计。 这不是快速美化工具,而是一套完整的设计流程。


语气

这项技能使用两种语气:

执行期间 - 轻快的忍者风格,标志性、沉浸式。简短。

  • “正在为调色板上色……”
  • “正在用纯金色绘制主视觉……”
  • “正在设置间距设计令牌。”

报告 / 最终摘要 / 审计结果中 - 平实、客观,便于开发者阅读。完全去掉风格化表达。

  • “完成。已生成设计系统。文件:MASTER.md、tokens.css、theme.config.ts。已绘制 3 个页面。”
  • 不用神秘的文风,不用比喻。只说明变更内容、涉及的文件和下一步。

风格化表达仅用于开场和工作过程的叙述。一旦给出结果或提出问题,就不再使用。


/paint 与 /cast

/genjutsu:cast/genjutsu:paint
理念“让这个东西更美观、更惊艳”“从零开始构建一个视觉宇宙”
入口根据现有代码调整必须先集思广益;针对现有设计,询问是保留、部分调整还是重新设计
探索仅在需求模糊时轻量进行全面集思广益,绝不跳过
设计系统可选,隐式处理必需,生成 MASTER.md
审计交付前快速检查最后进行完整的 design-audit
范围一个组件 / 页面 / 效果整个项目的视觉标识

/genjutsu:paint 在实现时会调用与 /genjutsu:cast 相同的子技能。

使用真实素材构建完整网站或 Web 应用属于第三套流程 /genjutsu:bunshin:由一名艺术总监带领多个子 Agent,并进行独立审查, 直至只剩轻微问题或达到该级别的轮次上限。如果需求规模达到这种程度、技术栈属于 Web 或尚无项目, 且宿主能够启动子 Agent,paint 会在扫描技术栈后立即提出该流程,并且只有在获得肯定答复后才会切换。

铁律

  1. 绝不跳过头脑风暴。 即使用户说“只要做得好看就行”,也不行。尤其是这种时候。唯一有据可查的例外是下文所述的轻量范围,它会把头脑风暴缩短为一个问题,但绝不会省略。没人回答时,请参阅“无人回答时”:根据需求说明,以假设的形式回答问题。
  2. 头脑风暴期间一次只问一个问题。 绝不捆绑提问。第二个问题取决于第一个问题的答案。
  3. 未经验证确认,绝不继续。 视觉和交互两项都必须获得明确批准。唯一的例外是下文所述的轻量范围:此时不涉及视觉识别,因此只需交互论点——但仍须明确验证,绝不自行假定。没人回答时,请参阅“无人回答时”。
  4. 每个设计令牌都必须来自 MASTER.md。 不得使用随意设定的数值或擅自添加的十六进制值。对于没有编写 MASTER.md 的轻量范围,令牌应来自项目中已有的令牌——先阅读,再使用,不得自行创造。
  5. 每个动画都必须遵循交互论点。 时长、缓动、禁用模式——一概不例外。
  6. 未经询问,绝不安装依赖项。 没人回答时,绝不安装依赖项(参阅“无人回答时”)。
  7. 逐页处理,逐页验证。 绝不试图一次完成所有内容。
  8. 审查不是可选项。 第 5 阶段始终执行,即使用户似乎已经满意。轻量范围会缩减为快速检查——对照代码检查论点、减少动态效果、退出动画、60fps——但绝不会省略。
  9. 检测不到动画库的技术栈 → 在提出依赖项之前,优先使用技术栈的原生 API。
  10. 检测到动画库(GSAP、Motion / Framer Motion、Lottie、Rive 等)→ 尊重开发者的选择。不要提议替换,也不要擅自将 framer-motion 迁移到 motion。
  11. 展示胜过描述。 在第一个视觉关卡,先询问用户想如何查看效果,然后在整个会话中保持该方式。预览只是临时的——它用于传达设计论点,绝不会成为最终实现。

轻量范围——唯一的精简路径

paint 是一个包含五个阶段的流程,用来“让这个词动起来”或“润色这个悬停效果”并不合适。这些任务应交给 /genjutsu:cast,它是默认入口。

不过有时任务还是会进入这里:用户习惯性地输入了 /genjutsu:paint,或者宿主进行了路由。对一个按钮进行完整的艺术指导头脑风暴,不是严谨,而是额外负担。识别这种情况,并明确说明后缩短流程。

以下三个条件全部满足时,才属于轻量范围:

  • 目标是一个组件、一种效果或一个独立元素
  • 无需确立视觉识别:项目已有颜色和字体,或者尚无项目,只有草图
  • 下游没有任何内容依赖将结果系统化

如果有两个或更多条件不满足,就不属于轻量范围。运行完整流程,并用一句话说明原因。

流程变化:

阶段完整流程轻量流程
1 头脑风暴五个领域,一次只问一个问题一个问题,选择最不明显的问题,然后停止
2 论点视觉和交互,两项都须验证仅验证交互论点
3 设计系统生成 MASTER.md 和技术栈令牌文件跳过。 读取并使用项目中已有的令牌。不编写 MASTER.md。
4 实现逐页处理,逐页验证这一个组件
5 审查完整的 design-audit 子技能快速检查:对照代码检查论点、减少动态效果、退出动画、60fps

只需宣布一次,让用户知道采用了哪种流程,并可以要求更换:

“这是一个单独的组件,因此我会运行 paint 轻量流程:只问一个问题,不创建设计系统文件。如果你想要完整流程,请告诉我。”

轻量范围绝不会省略的内容: 头脑风暴问题、论点或验证。每个关卡都必须保留,只减少关卡数量。


展示你的成果——预览关卡

此流程中的一些关卡,是为了让用户在批准前先看看某些内容:交互理念、一组变体、视觉识别、设计系统。动效和颜色无法靠一句话描述清楚——看不到缓动曲线却要批准,这不是批准,而是在猜。

因此,在第一个这类关卡之前,先询问他们想如何查看。之后就不要再问了。

菜单 - 在第一个视觉关卡只展示一次,并标明推荐的默认选项:

在向你展示之前——你想如何查看?

A. 渲染页面 - 一个实时 HTML 页面:真实的缓动曲线、真实的时长,以及实际执行动效的元素。 B. 实时预览 - 在你的项目中创建一个临时路由,使用真实的技术栈和设计令牌。原生项目:使用 @Preview / #Preview 临时文件。 C. 直接展示 - 在此对话中写出。

推荐默认选项 - 在菜单中说明,绝不悄悄替用户决定:

情况默认选项
范围较小(一个悬停效果、一处过渡)C - 直接展示
范围中等或较大,使用 Web 技术栈A - 渲染页面
范围中等或较大,使用 Compose / SwiftUIB - 实时预览,A 为第二选择
正在讨论完整的视觉识别或设计系统A - 渲染页面
没有开发服务器,或不得写入代码仓库A - 渲染页面
宿主是 Cowork,且没有可写入的项目检出目录A - 渲染页面,B 不可用

选择在整个会话中保持不变。 在每个后续关卡,用一行说明模式(“在渲染页面上展示变体。”),然后继续。不要重新打开菜单。用户提出要求时即可切换——“用文本展示”“放到页面上”“直接告诉我”——立即照做,从那时起,新模式就成为会话默认选项。

当前使用哪个宿主? 此关卡在 LOAD 之前触发,因此 $SKILL_BASE 尚不存在,这段逻辑需要独立运行。检测一次即可,开销要小,然后进行映射:

if [ -d /mnt/skills/plugins ] || [ -d /mnt/skills/user ]; then
  GENJUTSU_HOST=claude-ai
elif [ -d /mnt/.claude/skills ] \
  || [ -n "$(find /sessions -maxdepth 6 -type d -path '*/.claude/skills' 2>/dev/null | head -1)" ]; then
  GENJUTSU_HOST=cowork
elif [ -n "${CLAUDE_PLUGIN_ROOT:-}" ] || [ -d "$HOME/.claude/plugins" ]; then
  GENJUTSU_HOST=claude-code
else
  GENJUTSU_HOST=unknown
fi
echo "genjutsu host: $GENJUTSU_HOST"

刻意先检测 Cowork,再检测 Claude Code:两者都可能有 ~/.claude 目录,但只有 Cowork 有以会话为根目录的 skills 挂载,因此必须优先匹配更具体的信号。

生成预览 - 识别宿主,能力不足时降级,绝不失败:

宿主A - 渲染页面C - 直接展示
claude.ai原生渲染为 artifact。直接生成即可。在对话中写出。
Cowork宿主的持久化 artifact。它会在本轮结束后保留,正适合设计系统:用户可以之后再回来查看。宿主的内嵌组件,在原处渲染。适合短任务。
Claude Code使用会话支持的 Artifact 工具,否则遵循下方的能力规则。在对话中写出。
unknown(其他任何宿主)遵循下方的能力规则。在对话中写出。

A,按能力执行。 模式 A 需要以下三种能力之一,按此顺序尝试:本会话中可为用户渲染 HTML 的工具(在 Claude 宿主上即 artifact);否则,如果会话有能力控制浏览器,就将自包含的临时 HTML 文件写入临时路径并在浏览器中打开;否则,仍生成同一个文件,将路径交给用户,并用一行说明如何打开。检查会话实际提供的工具,不要想当然地假设某个工具存在。如果都不可行,就退回 C,不要让关卡失败:内嵌预览总比中止预览好。

B - 实时预览需要可写入的项目。 Cowork 通常没有这样的项目,因此只提供 A 和 C,并用一行说明 B 不可用的原因,而不要列出无法实现的选项。

预览中应包含什么。 如果预览只是用更漂亮的字体复述那句话,就毫无价值。应展示单靠一句话无法表达的内容:

门槛预览展示内容
交互论点以 SVG 绘制的缓动曲线,并打印其精确数值;一个实际执行交互并带有重播按钮的元素;裸露的数值(时长、延迟、交错、弹簧参数);以及一个展示降级版本的减少动态效果切换开关。
一组变体并排展示每个变体对应的同一张卡片,并通过一个全局触发器同时触发它们,以便进行比较;此外,每个变体都可单独重播。
视觉识别色板及其十六进制值和相对于背景的对比度;按实际比例级别展示的字体样张;间距条、圆角和阴影示例;一个真实按钮和一张真实卡片。
设计系统渲染每个令牌类别;每个基础组件展示五种状态(默认、悬停、聚焦、激活、禁用);如同时存在浅色和深色模式,则并排展示。

预览遵循的规则:

  • 承载预览的消息必须用纯文本说明论点。 无论采用哪种模式,消息开头都要用一句话说明论点并标注(Interaction thesis: 或 Visual thesis:),然后给出 Allowed patterns: 行,用一行说明页面是提案而非构建结果,并以验证问题结尾。 消息中不包含任何实现:代码要等到之后的回合,在用户答应后再开始。选择了 A 或 B 的人绝不能还得费力寻找论点在哪里。
  • 预览是一次性的,绝不能成为最终实现。 根据已验证的论点和已加载的子技能构建真正的成果,绝不能直接移植预览标记。这一点对 Compose / SwiftUI 尤其重要:HTML 只近似呈现时序和曲线,不近似呈现渲染效果——要在页面上说明这一点。
  • 验证后删除实时预览路由,除非用户要求保留。
  • 构建预览时绝不安装依赖项。
  • 未经询问,绝不启动开发服务器。
  • 只展示论点中包含的数值。论点中没有的数值不应出现在预览中,否则预览就会变成第二个论点,而这个论点无人验证过。
  • 如果本次会话无法加载某个 Web 字体,仍要以该字体自身的名称展示它,将其设为备用字体栈中的字体,并标注 not rendered in this session。绝不能换成会话中可渲染的字体:沙盒能获取的字体不等于网站最终会使用的字体。

没有人回应时

有些会话中没有人在另一端:例如评测框架、CI 作业,或由另一个 Agent 驱动的本技能。你会从请求或宿主的说明中得知这一点(例如非交互式运行、“不要提问”,或提示中已预先回答了各个门槛),绝不能仅仅因为某个问题暂时无人回答,就自行认定如此。如果请求预先回答了某个门槛,该回答就有效:此门槛视为已回答,而非跳过。

在此类会话中,每个门槛仍须产出相应结果。变化之处在于,没有人会验证结果:

  • 探索和头脑风暴问题: 不要提问。根据简介和扫描结果逐一作答,并在论点中将每个答案标明为假设。
  • 预览门槛: 采用菜单针对当前范围和技术栈推荐的默认选项,用一行说明后继续。
  • 论点门槛: 采用你原本会提出的论点,用一行说明它尚未经过验证,然后继续。最终报告中要列出该论点,并标记为 UNVALIDATED。
  • 依赖项: 绝不安装依赖项。如果论点需要项目当前不具备的库,就使用技术栈的原生 API,并在最终报告中指出缺少的依赖项。

其他要求均不变:必须先写出论点,再编写任何代码;必须加载各模块;审计报告必须提供证据。无头运行会跳过等待,但绝不会跳过工作。


子技能路径检测

告知代码块此技能所在的位置。 代码块的第一行是 GENJUTSU_SKILL_DIR。 Claude Code 会自动填写。在其他宿主环境中,请在代码块前面、同一次 shell 调用中添加一行,写明你读取此文件的目录: GENJUTSU_SKILL_DIR='/absolute/path/to/paint'。如果 genjutsu 路由器输出了 GENJUTSU_SKILL_DIR= 行,请原样使用该值。

此代码块定义了 shell 状态,而 shell 状态不会在不同的 shell 调用间保留。 $SKILL_BASE、load_skill 和 load_ref 只存在于运行此代码块的那一次 shell 调用中。之后的任何阶段——以及用户验证关卡后的每个阶段——都属于后续阶段, 一切都得从头开始。因此:请在即将运行 load_skill 行的同一次 shell 调用中,重新输出整个代码块。 切勿在未定义 $SKILL_BASE 的调用中运行 cat "$SKILL_BASE/...":路径会解析为 /<name>/...,导致 cat 失败,模块也会丢失。

如果代码块输出 could not find the genjutsu modules,请停止整个流程。 向用户显示该消息(包括安装命令),不要执行其他操作:后续每一步都依赖这些模块,缺少模块继续运行只会产生此技能旨在避免的通用输出。

GENJUTSU_SKILL_DIR="${GENJUTSU_SKILL_DIR:-${CLAUDE_SKILL_DIR}}"
# Resolution order, first hit wins:
#   0. claude.ai, /mnt/skills/plugins (or the older /mnt/skills/user): the
#      genjutsu bundle.
#   1. This skill's own directory: its _jutsu, or the _jutsu next to it.
#   2. ${CLAUDE_PLUGIN_ROOT}/skills/_jutsu, when Claude Code substituted it.
#   3. Bounded probes: .claude/skills and .agents/skills from $PWD upward, then
#      the skills directories installers write to, then /sessions (Cowork).
#   4. The Claude Code plugin cache, newest version. Last on purpose: an old
#      plugin install must never win over a newer bundle.
# Resolved from scratch every time. A cache was tried here and removed: after a
# plugin update the old version directory is still on disk, so a cached path
# passes an "is it a directory" check and serves the previous release.

# No function below names a positional parameter (a dollar sign followed by a
# digit, or the whole argument list). Claude Code replaces those tokens in a
# skill's text with the words typed after the slash command, before the model
# reads this block: after "/genjutsu:cast make the cards feel physical", a path
# built from a function's first parameter would read "the/motion-principles/...",
# the second word typed. Each function reads its arguments with a bare
# `for name; do`, which walks the positional parameters without writing any out.

# A _jutsu directory counts only if it holds motion-principles. The shared
# skills directory of npx serves about 80 agents, so a directory name proves
# nothing. The entry file is named SKILL or GUIDE depending on the artifact
# (the bundle renames it at packaging time), so the name is assembled from
# parts: spelled out in full, the packaging step would rewrite it too.
genjutsu_is_jutsu() { # <candidate _jutsu directory>
  for jutsu_dir; do
    for d in SKILL GUIDE; do
      [ -f "$jutsu_dir/motion-principles/$d.md" ] && return 0
    done
  done
  return 1
}

# Print the first genjutsu _jutsu among the candidate paths read on stdin.
genjutsu_first_jutsu() {
  while read -r jutsu_c; do
    genjutsu_is_jutsu "$jutsu_c" && { printf '%s\n' "$jutsu_c"; return 0; }
  done
  return 1
}

SKILL_BASE=""
# 0. claude.ai mounts uploaded skills under /mnt/skills/plugins/<name>/ (seen on
# 2026-09-28), next to the user's other skills; /mnt/skills/user before that.
# GENJUTSU_CLAUDE_AI_ROOT stands in for /mnt/skills in the test suite only.
# Only a _jutsu holding motion-principles counts: a mount without genjutsu in it
# must never pass for a resolved base.
genjutsu_claude_ai="${GENJUTSU_CLAUDE_AI_ROOT:-/mnt/skills}"
for claude_root in "$genjutsu_claude_ai/plugins" "$genjutsu_claude_ai/user"; do
  [ -z "$SKILL_BASE" ] && [ -d "$claude_root" ] || continue
  SKILL_BASE="$(find -L "$claude_root" -maxdepth 2 -type d -name _jutsu 2>/dev/null | genjutsu_first_jutsu)"
done
# 1. This skill's own directory. Empty means unknown: never probe "/_jutsu".
if [ -z "$SKILL_BASE" ] && [ -n "$GENJUTSU_SKILL_DIR" ]; then
  SKILL_BASE="$(printf '%s\n' "$GENJUTSU_SKILL_DIR/_jutsu" "$GENJUTSU_SKILL_DIR/../_jutsu" | genjutsu_first_jutsu)"
fi
# 2. Claude Code plugin root.
if [ -z "$SKILL_BASE" ] && [ -n "${CLAUDE_PLUGIN_ROOT}" ]; then
  SKILL_BASE="$(printf '%s\n' "${CLAUDE_PLUGIN_ROOT}/skills/_jutsu" | genjutsu_first_jutsu)"
fi
# 3. Bounded probes, following symlinks (npx links .claude/skills/<name> to
# .agents/skills/<name>). The case guard stops the walk at "/", "." or "".
probe_dir="${PWD:-$(pwd)}"
probe_n=0
while [ -z "$SKILL_BASE" ] && [ "$probe_n" -lt 24 ]; do
  probe_n=$((probe_n + 1))
  SKILL_BASE="$(find -L "$probe_dir/.claude/skills" "$probe_dir/.agents/skills" -maxdepth 2 -type d -name _jutsu 2>/dev/null | genjutsu_first_jutsu)"
  case "$probe_dir" in /|.|"") break ;; esac
  probe_dir="$(dirname "$probe_dir")"
done
for probe_root in "$HOME/.agents/skills" "$HOME/.claude/skills" "$HOME/.codex/skills" \
    "$HOME/.cursor/skills" /mnt/.claude/skills; do
  [ -z "$SKILL_BASE" ] && [ -d "$probe_root" ] || continue
  SKILL_BASE="$(find -L "$probe_root" -maxdepth 2 -type d -name _jutsu 2>/dev/null | genjutsu_first_jutsu)"
done
if [ -z "$SKILL_BASE" ] && [ -d /sessions ]; then
  SKILL_BASE="$(find -L /sessions -maxdepth 8 -type d -path '*/.claude/skills/*/_jutsu' 2>/dev/null | genjutsu_first_jutsu)"
fi
# 4. Claude Code plugin cache, newest version first. Numeric version
# directories only, so a bare marketplace clone never wins.
if [ -z "$SKILL_BASE" ] && [ -d "$HOME/.claude/plugins/cache" ]; then
  SKILL_BASE="$(find "$HOME/.claude/plugins/cache" -maxdepth 6 -type d -path '*/genjutsu/[0-9]*/skills/_jutsu' 2>/dev/null | sort -V -r | genjutsu_first_jutsu)"
fi
[ -n "$SKILL_BASE" ] && SKILL_BASE="$(cd "$SKILL_BASE" 2>/dev/null && pwd -P)"

# Stop instead of running the pipeline without its modules. Name every root that
# was tried, so a new layout can be reported instead of guessed.
if [ -z "$SKILL_BASE" ]; then
  echo "genjutsu: could not find the genjutsu modules (a _jutsu directory holding motion-principles)." >&2
  echo "  genjutsu's pipelines do not work without them. Install the full bundle:" >&2
  echo "    any agent    npx skills add https://genjutsu.athevon.dev -g" >&2
  echo "    Claude Code  /plugin marketplace add AThevon/genjutsu, then /plugin install genjutsu" >&2
  echo "    claude.ai    upload genjutsu.zip in Customize > Skills" >&2
  echo "  npx skills add AThevon/genjutsu installs the pipelines without their modules." >&2
  echo "  Tried: /mnt/skills/plugins, /mnt/skills/user, GENJUTSU_SKILL_DIR (${GENJUTSU_SKILL_DIR:-empty}) and its parent," >&2
  echo "         \$CLAUDE_PLUGIN_ROOT, .claude/skills and .agents/skills from \$PWD upward," >&2
  echo "         ~/.agents/skills, ~/.claude/skills, ~/.codex/skills, ~/.cursor/skills," >&2
  echo "         /mnt/.claude/skills, /sessions, ~/.claude/plugins/cache." >&2
  echo "genjutsu: stop the pipeline here and show this message to the user." >&2
  return 1 2>/dev/null || exit 1
fi
echo "genjutsu: modules from $SKILL_BASE" >&2

# Print a module's entry file. A missing module does not stop the pipeline (a
# partial claude.ai upload is legitimate), but it is announced, and the final
# report lists it: shell state does not survive, so the model keeps the list.
load_skill() { # <module>
  skill_name=""
  for skill_arg; do skill_name="$skill_arg"; break; done
  for d in SKILL GUIDE; do
    if [ -n "$skill_name" ] && [ -f "$SKILL_BASE/$skill_name/$d.md" ]; then
      cat "$SKILL_BASE/$skill_name/$d.md"
      return 0
    fi
  done
  echo "genjutsu: sub-skill '$skill_name' NOT LOADED - not found under $SKILL_BASE. Carry on, and list it under 'Modules not loaded' in the final report." >&2
  return 1
}

# Print one reference file of a module: load_ref <module> <path inside it>.
load_ref() { # <module> <path inside it>
  ref_module=""; ref_path=""; ref_n=0
  for ref_arg; do
    ref_n=$((ref_n + 1))
    case "$ref_n" in
      1) ref_module="$ref_arg" ;;
      2) ref_path="$ref_arg"; break ;;
    esac
  done
  if [ -n "$ref_module" ] && [ -n "$ref_path" ] && [ -f "$SKILL_BASE/$ref_module/$ref_path" ]; then
    cat "$SKILL_BASE/$ref_module/$ref_path"
    return 0
  fi
  echo "genjutsu: reference '$ref_module/$ref_path' NOT LOADED - not found under $SKILL_BASE. Carry on, and say so in the final report." >&2
  return 1
}

所有子技能都通过 load_skill <name> 加载(在上文定义),它会输出模块的入口文件;如果模块缺失,则会输出一行 NOT LOADED,并继续执行:请保留列表,最终报告需要用到它。如果代码块完全找不到模块,就会停止,整个流程也随之停止。下方每个需要加载内容的阶段,都必须在同一次 shell 调用中重新输出路径解析代码块:各阶段之间有用户关卡,状态不会延续。


流程

阶段 1 - 头脑风暴(必选,不可跳过)

这是整个流程的基础。匆忙处理会导致后续所有工作出错。目标:充分理解用户的愿景,写出两条让他们毫不犹豫就会认同的论点。

技术栈扫描(头脑风暴前运行)

在询问用户技术栈之前,先扫描项目,检测其中已有的技术:

# 1. Web (existing)
cat package.json 2>/dev/null | grep -E '"(gsap|motion|framer-motion|three|@react-three/fiber|@react-three/drei|animejs|popmotion|lenis|locomotive-scroll)"'
cat package.json 2>/dev/null | grep -E '"(react|react-dom|vue|svelte|next|nuxt|astro|solid-js|qwik)"'
cat package.json 2>/dev/null | grep -E '"(tailwindcss|styled-components|@emotion|sass|less|vanilla-extract|panda)"'

# 2. Android / Compose
ls build.gradle.kts build.gradle settings.gradle.kts settings.gradle 2>/dev/null
grep -rE 'androidx\.compose|implementation\("androidx\.compose' build.gradle* settings.gradle* 2>/dev/null

# 3. Compose Multiplatform / KMP
grep -rE 'org\.jetbrains\.compose|kotlin\("multiplatform"\)|id\("org\.jetbrains\.kotlin\.multiplatform"\)' build.gradle* settings.gradle* 2>/dev/null

# 4. Apple / SwiftUI
ls *.xcodeproj *.xcworkspace Package.swift 2>/dev/null
grep -lE 'import SwiftUI|@main.*App' --include="*.swift" -r . 2>/dev/null | head -1

# 5. Apple platform sub-detection (iOS vs macOS)
grep -E '\.iOS\(|\.macOS\(' Package.swift 2>/dev/null
grep -E 'SDKROOT = (iphoneos|macosx)' *.xcodeproj/project.pbxproj 2>/dev/null

# 6. Mobile web indicators
grep -rE 'viewport.*width=device-width|@media.*pointer:\s*coarse|@media.*max-width' --include='*.html' --include='*.css' --include='*.scss' . 2>/dev/null | head -3
ls public/manifest.json public/sw.js 2>/dev/null

# 7. Legacy bridge indicators (mention in DISCOVER, do not auto-load)
ls -- *.xib *.storyboard 2>/dev/null
find . -path '*/res/layout/*.xml' 2>/dev/null | head -1
grep -rE 'setContentView\(R\.layout' --include='*.kt' --include='*.java' . 2>/dev/null | head -1

整理扫描结果:

  • 动画库:gsap、motion、framer-motion、three/@react-three、anime.js,或无。 motion 和 framer-motion 是同一个库的两个名称。 Framer Motion 更名为 Motion;motion 是当前使用的包,framer-motion 是旧版包,但仍广泛安装并发布。请注明 package.json 中的是哪一个——导入路径有所不同,子技能需要知道。如果两者都存在,则项目正处于迁移过程中:说明这一点,并沿用你正在编辑的文件已导入的那个。
  • 框架:React、Vue、Svelte、Next.js、Nuxt、Astro、vanilla
  • CSS:Tailwind、styled-components、CSS modules、vanilla CSS
  • 未检测到任何内容:从零开始,所有选项均可用
  • 原生 Android:通过 gradle 依赖检测 Compose。
  • 原生 Apple:通过 Package.swift / xcodeproj + swift 文件检测 SwiftUI。根据 Package.swift 平台或 pbxproj SDKROOT 区分 iOS 与 macOS。
  • Compose Multiplatform:kotlin-multiplatform 插件 + jetbrains.compose 插件。
  • 移动端环境:视口、清单文件、仅限移动端的媒体查询,或原生 iOS/Android。
  • 桌面端环境:macOS 目标平台,或 Web 项目中没有移动端迹象。
  • 旧版混合项目:存在 .xib、.storyboard、布局 XML、setContentView(R.layout.*)。仅提及,不自动加载。

如果任务涉及整个网站:请提出使用 bunshin。 genjutsu 还有第三条流程,专为这类规模超出当前任务的工作设计。bunshin 由一组子 Agent 在一位艺术总监的统筹下构建完整网站或 Web 应用;如果已安装 Impeccable,它会负责制定视觉方向;随后反复进行独立评审和修复,直到只剩轻微问题、达到该层级的轮次上限,或某一轮未能修复任何问题为止。它的工作量较大,因此只提出建议,不会自行启动。 仅当以下三项条件全部满足时,才在扫描结束后、提出任何其他问题之前,提议一次:

  • 目标是一个完整的网站或 Web 应用:从零开始构建或全面重新设计,包含多个页面或屏幕;
  • 技术栈是 Web,或者项目尚不存在;
  • 本次会话可以启动子 Agent:其提供的工具中包含子 Agent 工具或多 Agent 工作流工具(在 Claude Code 中为 Agent 或 Workflow);

并且至少满足以下一项:

  • 有真实素材可供收集:社交资料、现有网站、品牌素材或文档;
  • 网站面向两类受众,或支持两种语言;
  • 这是为客户或利益相关方打造的第一个版本;
  • 请求要求做到尽可能好、全面处理或全力以赴。

撰写提议前,检查是否已安装 Impeccable:

for d in .claude/skills/impeccable .agents/skills/impeccable "$HOME/.claude/skills/impeccable" "$HOME/.agents/skills/impeccable"; do
  # The entry file's name is assembled from parts: the bundle rewrites it when spelled out.
  e="$d/SKILL"
  if [ -f "$e.md" ]; then echo "impeccable: $d"; break; fi
done

提议只发一条消息,然后等待:

“这是一个完整的网站<,从零开始 | ,全面重新设计>。genjutsu 有一套专门为此设计的流程:bunshin。它会 收集真实素材,由并行 Agent 进行调研,让 Impeccable 确定设计方向(<已在此处安装 | 未安装:bunshin 无需它也能运行,有它效果更好>),通过并行 Agent 构建页面,并进行审核和完善,直到只剩小问题、达到所选层级的轮次上限,或某一轮不再修复任何问题。 它会询问两次:一次询问是否开始并确认预算,一次让你选择设计方向。已有一次运行记录:一个包含 7 个页面的网站,子 Agent 大约使用了 1,050 万个 token,耗时 6 到 8 小时。各层级根据这次运行估算,约为 600 万到 1,300 万个 token,并会随页面数量变化。 回复 bunshin 即可切换过去,否则我会继续使用当前流程。”

  • 同意:停止当前流程并加载 bunshin。如果宿主列出了 genjutsu:bunshin,则将其作为技能调用;否则,读取与本文件同级的入口文件,路径为 $GENJUTSU_SKILL_DIR/../bunshin/(该文件夹顶层的唯一 Markdown 文件),并从第一步开始遵循其中的说明。
  • 其他任何回复:继续当前流程,本次会话中不再提议它。
  • 前三项条件中有任一项不满足时,绝不提议:无法启动子 Agent 的宿主无法运行 bunshin,而原生技术栈目前尚未纳入其支持范围。
  • 无人回复时:不要提议,也不要等待。用一行说明 bunshin 适合这个需求,继续当前流程,并在最终报告中提及 bunshin。

在第一个问题之前,先说明你的理解。 扫描已经完成,请求也已呈现在你面前。在提出第一个头脑风暴问题之前,用一行说明你目前的理解,让用户能在错误的前提影响后续所有问题之前纠正你:

“我目前的理解是:为 打造 ,方向是 ,技术栈为 。如果有误,请在我提问前纠正。”

<what> 是请求中所述的产品,<whom> 是请求中所述的受众,<direction> 是请求或现有设计所暗示的氛围,<stack> 是扫描发现的技术栈。无法填写的部分应明确说明未知(例如“面向目前无法判断的受众”),绝不能猜测;未知项正是头脑风暴的起点。这句话应单独作为一条消息发送,然后等待回复。用户的更正会替换其指出的部分;用户同意则表示当前理解成立。这不是头脑风暴问题,不计入轻量范围的单个问题额度;它也不是结论:在第 2 阶段之前,其中任何内容都未经验证。如果会话中没有人类用户可供回复,则陈述当前理解,并按原样继续。

如果检测到新旧 UI 混用(XIB / storyboard / layout XML / setContentView(R.layout.*)):

在头脑风暴期间只问一个问题:

“我发现你的项目将 [XML 布局 / XIB / 经典 Activity] 与现代 UI 混合使用。对于此任务,我应该只使用 [Compose/SwiftUI],还是集成到旧版屏幕中?” 如果用户选择旧版集成:编写桥接代码(Compose 使用 AndroidView,SwiftUI 使用 UIViewControllerRepresentable),以便在旧版屏幕中呈现现代代码。绝不生成新的旧版代码(不使用 XML、XIB 或 setContentView)。

如果项目已经有视觉识别(CSS 变量或 Tailwind 主题、Theme.kt / Color.kt、Color+App.swift 或资源目录、徽标、现有品牌):

首先,盘点已有内容。 对于 Web 技术栈,在一次 shell 调用中, 重新输出技能基础块并运行 python3 "$SKILL_BASE/design-audit/scripts/audit.py" . --group tells。 将结果显示为一个简短区块,标题为这个项目已经形成的直觉,按类别计数, 每类附上两到三个 file:line 示例。根据下方问题设定的模式,这些内容都已纳入设计主张: 在保留模式下,默认保留,并将其作为品牌自身特征写入 Allowed patterns: 行;在部分调整模式下,默认在用户指定的范围内保留;在重新设计模式下,默认处处保留。用户可在设计主张确认环节按名称保留或舍弃其中任何一项。

当脑暴过程中旧版问题和视觉识别问题都适用时,紧接着旧版问题,只问这一个模式问题:

"这个项目已经有自己的风格。你想保留品牌并在此基础上构建、调整其中一部分,还是重新设计?"

  • 保留 - 设计主张描述品牌现状,只补足缺少的部分(动效、状态、比例尺上缺失的步骤)。现有设计令牌保持不变。
  • 部分调整 - 用户指定可以更改的内容(“字体和动效,不改颜色”)。设计主张只调整这些范围内的值,保留现有令牌名称,不做其他改动。
  • 重新设计 - 设计主张替换整个视觉层,包括现有设计令牌。

所选模式要作为一个明确条款写入视觉设计主张(“保留现有配色和徽标”),这样第 2 阶段的确认关卡就会与其他内容一并验证。各模式允许设计主张替换哪些内容,请参见下方的“现有项目规范”。轻量范围不询问此问题:那里不涉及视觉识别,因此默认采用保留模式。

需要涵盖的五个领域:

  1. 产品 - 这是什么(应用、落地页、作品集、SaaS、电商、博客、仪表盘……)?它有什么特点,是最接近的竞品或模板无法声称拥有的?
  2. 受众 - 谁会使用它?(开发者、设计师、普通公众、企业、儿童、奢侈品消费者……)
  3. 氛围 - 用 3 到 5 个形容词定义视觉感受
  4. 参考 - 用户提供的网站、截图、情绪板。绝不要主动推荐知名网站:这会让设计主张被别人的风格所锚定。
  5. 技术栈 - 已经使用了什么?还是从零开始?

提问方式: 一次只问一个问题,从最不明显的领域开始。如果已通过扫描 package.json 知道技术栈,就不要问——改为先询问氛围或受众。每个回答都会影响下一个问题的提问方式。

如何处理含糊的回答:

当用户说“现代”或“简洁”,或者“不知道,弄好看点就行”时:

  1. 确认 - “这是个起点。我们把它说得更具体些。”
  2. 回到产品本身 - “和什么相比才算简洁?说一件这个产品确实具备、但最接近的竞品无法自称具备的特质。”回应氛围词时,要用产品事实,而不是另一个品牌的风格:“像 Stripe、像 Linear、像 Apple”会让设计主张被三个知名默认风格所锚定。
  3. 换个角度问 - “什么会让你觉得不对劲?哪些网站让你看了就不舒服?这同样很有帮助。”
  4. 说明影响 - “这个选择会决定整个配色方案和字体排印。花一分钟想清楚很值得。”

绝不要把含糊的回答当作确认。“嗯,差不多吧”意味着要继续深挖——问清楚“差不多”具体是指哪一部分。

当用户要求跳过或加快脑暴时:

不要妥协。改为:

“我们已经梳理了 [covered areas]。我还不清楚 [missing areas],这会直接影响 [concrete consequence]。你想让我再问一个问题,还是希望我先做出假设,再由你事后纠正?”

这样,他们就能在充分了解情况后做出选择。如果他们选择先做假设,请在设计论点中明确列出每一项假设。

绝不要协商剩余问题的数量(“我保证只再问两个”)。你要听到答案后才知道还需要问多少个问题。

**何时停止:**当你能够写出两个设计论点(视觉 + 交互),并且愿意打赌用户会说“oui parfait”时。如果你对任何一个方面还要靠猜,就继续提问。


阶段 2 - 设计论点(明确方向,获取确认)

根据头脑风暴的结果,提出两个设计论点:

视觉设计论点

用一个句子概括整体视觉形象。必须明确涵盖以下四个方面:

  • 色彩方向 - 深色 / 浅色、色彩组合类型、强调色
  • 字体气质 - 说明展示字体的名称及选择理由,或写明 display face: undecided, chosen at the design-system gate;衬线 / 无衬线 / 等宽字体、字重使用、字号对比
  • 间距理念 - 紧凑 / 宽松、基础单位的感觉
  • 组件风格 - 圆角 / 锐角、有边框 / 填充、凸起 / 扁平

示例:“深色新野兽派界面,搭配粗重的等宽字体、荧光黄绿色强调色、宽松留白,以及边缘粗粝、带偏移阴影的组件。”

**自检:**重读你写的设计论点。如果缺少上述四个方面中的任何一个,或表述含糊(如“好看的字体”),请在展示前重写。

**可猜测性测试:**仅凭产品类别,别人能写出这个设计论点吗(金融科技产品用海军蓝和简洁的无衬线字体,设计工作室用纸感背景上的编辑风衬线字体)?如果能,那这只是该类别的默认风格,并非经过选择的设计方向:请根据该产品的真实特点重写。

交互主张

用一个句子概括动效与交互语言。必须明确涵盖以下四点:

  • 时长范围——快速(100-200ms)、中等(200-400ms)或缓慢(400ms+)
  • 悬停行为——悬停时会发生什么
  • 滚动行为——显示内容、视差效果或无变化
  • 禁止模式——本项目不会采用什么

示例:“过渡快速而干脆(100-200ms),悬停时轻微缩放(1.02),滚动触发的内容依次显现;不使用弹跳或弹性效果——所有过渡都采用利落的缓出曲线。”

跨平台主张示例:

  • “此 Compose 主视觉区域将使用 SharedTransitionLayout,并通过 spring(stiffness=Spring.StiffnessMedium, dampingRatio=0.85) 实现流畅的卡片到详情过渡。”
  • “此 SwiftUI 标签页过渡将使用 matchedGeometryEffect 和 .smooth spring(response: 0.5, dampingFraction: 0.85),营造富有触感的空间感。”
  • “此 macOS 仪表板将使用 100ms 透明度悬停状态(悬停时不缩放,保持桌面端的克制感),并通过 Cmd+1-9 键盘快捷键切换面板。”
  • “此 Android 标题栏将使用绑定到 scrollOffset 的 AGSL 着色器,实现动态液态玻璃效果(Android 13+;更低版本使用静态回退效果)。”

**自检:**重新读一遍你的主张。如果无法直接从中推导出 CSS/JS 属性,就说明它过于模糊。请重写。

在两项主张下方,用一行列出允许使用的元素。 写上 Allowed patterns:,后面列出设计有意依赖的所有元素;读者可能会把这些元素当作装饰或习惯性做法:等宽字体标签、极细分隔线、纸张底色、颗粒纹理、辉光、循环动画、代码标签、实时钟表。没有的话,写 Allowed patterns: none。审核会查看这一行:只有列在这里的视觉特征才算获准,因此氛围描述不能放行任何元素;用户也能在确认前看到清单。

**这是第一个视觉关卡。**提供预览菜单(参见上文“展示成果”),然后以选定的模式呈现两项主张。视觉主张尤其适合展示,远胜于文字描述——“荧光黄绿色点缀”在与中性色并排呈现之前,只是猜想。

**在用户明确确认两项主张之前,不要继续。**如果用户提出异议,不要从头开始——询问他们觉得哪里不对,并据此调整。

三个调节项(确认后)

两项主张均获确认后,从中提取三个值。第 3 阶段会将这些值作为 1-10 的调节项传给 search.py:

调节项从何处提取110
--variance视觉主张:布局与组件风格居中、对称、简约大胆、不对称、实验性
--motion交互主张:时长范围、滚动行为、禁止模式微妙复杂、精心编排
--density视觉主张:间距理念宽松紧凑、仪表板风格

每个值都必须根据已确认主张中的具体表述推导,不能依据原始需求,也不能使用默认值。“留白充足”意味着密度偏低;“快速干脆,不出现滚动显现效果”意味着动效程度偏低。若主张无法确定某个调节项,就完全不要在调用中包含它:未发送的调节项不会产生影响;猜测出的值则会让检索偏向无人认可的结果。记录每个值及其对应的表述;进入第 4 阶段前,会将它们与设计系统一并展示。

轻量范围没有第 3 阶段,因此不会设置任何调节项。


第 3 阶段 - 设计系统

对于 Web 技术栈,先加载 tells(轻量范围不会进入此阶段),单独执行一次 shell 调用,然后再查询设计系统。第 2 阶段以用户确认关卡结束,因此在该调用中重新输出解析块:

# (skill-base block re-emitted above this line)
load_skill tells
load_ref tells references/web.md

下面的查询会从一个从未接触过此项目的静态数据集中获取结果。tells 能帮助你识别哪些建议只是惯用做法,并非已确认主张所要求的内容。 加载 ui-ux-pro-max 子技能并运行它。第 2 阶段以用户确认环节结束,因此这是一次新的 shell 调用,$SKILL_BASE 已不再存在。 在本次调用中重新输出上文“子技能路径检测”中的路径解析块,然后:

load_skill ui-ux-pro-max

# Query it with the validated visual thesis, not with the raw user request. The thesis is the
# thing that was approved; the request was not. Product type, industry and the mood adjectives
# from Phase 2, in that order, work best.
# Append only the dials Phase 2 settled (see "The three dials"); drop each flag the theses
# left open, and never send a dial with a made-up value.
python3 "$SKILL_BASE/ui-ux-pro-max/scripts/search.py" \
  "<product type> <industry> <mood adjectives from the visual thesis>" \
  --design-system -f markdown \
  --variance <V> --motion <M> --density <D>

-f markdown 不是可选项。 默认的 ascii 格式会输出原始 ANSI 颜色转义序列,这些序列会穿过管道,并以垃圾内容的形式进入上下文;相同内容的 token 数量约为 3.3 倍。

此路径下各项参数的作用。 使用 --design-system -f markdown 时,--variance 会影响查找选取哪种风格,--motion 会附上强度相匹配的动效片段:两者都会改变提案。--density 则不会。在此路径下,它只会在“Design Dials”下打印其标签:它映射到的间距比例仅由 --persist 写入,颜色也不会持久化。因此,请根据视觉主张所体现的间距理念,手动在 MASTER.md 中推导间距比例,并让密度标签说明该比例应当给人什么感觉。动效片段是 GSAP 代码:若使用其他技术栈,只读取它的时长和缓动方式,切勿因此安装 GSAP。

返回的内容会包含带有角色名和 CSS 变量名的候选调色板、附有现成 Google Fonts URL 的字体搭配、效果说明,以及该风格的反模式列表。请将其视为提案,而非定论:这是对静态数据集的查找,它从未见过这个项目。保留符合已验证视觉主张的内容,舍弃与之冲突的内容,并用一行说明采纳了什么、舍弃了什么。凡是与用户认可的主张矛盾的调色板,都必须让位于该主张。

保留任何内容之前,都要通过两轮筛选:先看视觉主张,再看 tells。在 web 技术栈中,tells 已于本次调用前加载;在 Compose 和 SwiftUI 中则不会加载,只通过视觉主张筛选。数据集按关键词匹配,因此它恰好会提供 tells 所述的惯性选择:

  • 展示字体(衬线或无衬线)只有在视觉主张点名该字体并说明理由时才保留:它为这个产品承载了什么,而其他字体无法做到什么。“创意”“高端”和“编辑风”会引出编辑类衬线字体;“简洁”“现代”和“SaaS”会引出粗重的 grotesque 字体。这两组词都不是理由,用一种惯性选择替换另一种也不算决策。如果视觉主张尚未确定字体,就在这里作出选择:用一行话点名字体并说明理由。设计系统得到验证时,这一行会补充到视觉主张中;第 4 阶段和审查都会依据补充后的句子,而非原先留白的版本。
  • 暖调纸张底色(微暖的灰白底色,搭配近黑色墨色和一个红色点缀)是模型几乎面对任何简报都会选用的调色板。只有当视觉主张说明该产品为何适合以纸张为基础时,才予以保留。
  • 数据集关联到“SaaS”的玻璃拟态,除非视觉主张明确点名要求,否则不予通过。
  • 输出中其他任何被 tells 列为惯性选择的内容,也要接受同样的检验:要么在视觉主张中点名,要么舍弃。说明采纳和舍弃内容的那一行,也要说明这轮筛选淘汰了什么。

本次会话无法渲染的字体,仍然是指定字体。 字体请求被拦截,或沙盒无法联网,只是本次会话的限制,并不代表网站的限制。用户已认可某款 web 字体(点名指定,或表示可以使用 Google Fonts 链接或托管文件),或者项目已经加载了该字体时,绝不能因为这里能渲染另一款字体就换掉它:那只是系统字体默认胜出,视觉主张也因此失去已作出的决定。也绝不能擅自添加用户尚未同意的第三方字体请求:像提出任何新请求一样,询问一次。然后使用该字体:

  • 按照技术栈加载字体(Web 端使用 Google Fonts URL 或 next/font,Compose 和 SwiftUI 使用随应用打包的字体文件);Web 端设置 font-display: swap。为字体配置与其在字宽、x-height 和数字样式上相匹配的后备字体栈,并在 MASTER.md 中命名为 ("Fira Sans", "Segoe UI", Roboto, system-ui, sans-serif);如果后备字体会导致布局变化,则使用 size-adjust 和指标覆盖值。
  • 如果本次会话能够读取论文发布的数据,请检查论文对该字体的要求(等宽数字、某个字重、度数符号等字形):查看其样张页面和 OpenType 特性列表。如果无法读取,请说明哪些特性尚未核验。特性未核验是告知用户的理由,而不是移除该字体的理由。
  • 在用户会看到的地方说明:在设计系统消息、预览和最终报告中写明 Fira Sans: approved, loaded with a matched fallback, not rendered in this session.。审查会交接核验工作:页面在线、字体已加载、论文所依赖的特性均已就位。

paint 从不传递 --persist,因此 design_system.py 的 MASTER.md 模板中硬编码的组件值永远不会进入项目:MASTER.md 会在此处根据经过验证的论文以及上文保留的设计令牌生成。如果 paint 将来会持久化数据,则在保留该文件前,应根据设计令牌重写这些组件值。

如果 python3 不可用或脚本运行失败,请用一行说明,并手动根据论文推导设计系统。流程不会因此停止。

根据技术栈生成设计令牌

MASTER.md 设计系统文件是规范来源,但生成的代码文件会匹配检测到的技术栈:

  • 检测到 Web 技术栈:生成 Tailwind 配置 / CSS 变量(沿用现有格式)。CSS 中的设计令牌使用十六进制颜色值、cubic-bezier(...) 缓动函数和 rem 间距。输出文件扩展名为 tailwind.config.js,或输出 :root { --token: ... } CSS。
  • 检测到 Android Compose 技术栈:生成 Kotlin 设计令牌。输出 MASTER.md 中引用的 Theme.kt、Color.kt、Type.kt、Shapes.kt、Motion.kt。颜色令牌使用 Color(0xFF...),排版使用 TextStyle,形状使用 RoundedCornerShape,动效使用 MotionScheme(范围涉及主视觉或强调效果时采用 M3 Expressive)。间距使用 dp。
  • 检测到 SwiftUI 技术栈(iOS / macOS / 多目标):生成 Swift 扩展。输出 Color+App.swift、Font+App.swift、Animation+App.swift、Shape+App.swift。颜色令牌通过 Color("AssetName") 引用资源目录(如果没有资源目录,则使用 Color(red:green:blue:));排版使用 Font.system(...) 或 .custom(...);动画使用命名预设 .spring(...) / .snappy / .bouncy。间距使用 CGFloat 常量。
  • 检测到 Compose Multiplatform 技术栈:在 commonMain 中生成带有字体和平台专属颜色 expect/actual 声明的 Kotlin 令牌。结构与 Android Compose 相同,并在 MASTER.md 中加入描述各平台差异的章节。
  • 多技术栈项目(例如,Web 管理后台 + 原生移动应用):生成包含各技术栈清晰分节的 MASTER.md,并为每种技术栈生成代码文件。

MASTER.md 文档本身始终是唯一规范来源文件。生成的代码文件(Theme.kt / Color+App.swift / 等)是 MASTER.md 的子文件,并引用它。

根据两篇论文生成完整的设计系统:

  • 调色板 - 主色、次色、强调色、中性色、语义色(成功 / 警告 / 错误 / 信息)。需要时提供浅色和深色模式。
  • 排版 - 字体栈、字号比例(流动或固定)、字重用法、行高规则。
  • 间距 - 基础单位、比例(4px、8px、12px、16px、24px、32px、48px、64px...)。
  • 圆角 - 边框圆角比例(无、小、中、大、全圆)。
  • 阴影 - 阴影层级(0-4),与视觉论文保持一致。
  • 基础组件 - 按钮、输入框、卡片、徽标、链接;样式遵循论文。
  • 动效令牌 - 时长比例(快 / 正常 / 慢)、缓动名称、交错延迟。
MASTER.md

在项目根目录中创建 MASTER.md,其中包含完整的设计系统。此文件是唯一可信来源。每项实现决策都以此为依据。

MCP 工具(如果可用)

检查以下 MCP 是否已连接,并在可用时使用:

  • Stitch - 生成模型图/线框图
  • Nano Banana - 生成视觉素材(插画、图标、背景)
  • 21st.dev Magic - 根据描述生成 UI 组件

如果 MCP 不可用,请从容跳过——设计系统 + 代码实现才是核心路径。

在 Phase 4 之前展示

在会话的预览模式中展示设计系统——用一行说明模式,不要重新打开菜单——并在开始实现之前获取确认。如果 Phase 3 选择了展示字体,也要一并展示选择理由(一句话):确认设计系统也意味着确认对视觉理念的这项修订。调色板和字体比例若只以十六进制代码和像素值列在对话记录中,虽然精确,却完全无法评审;MASTER.md 中的每个设计令牌都即将应用到各处,所以现在是发现错误选择成本最低的时候。

同时展示各项调节参数:传给 search.py 的每个值及其对应的设计理念条款, 以及未纳入的每个参数及其原因。


Phase 4 - 实现

根据技术栈和交互理念加载子技能。

始终加载(使用上文定义的 load_skill <name>:缺失模块会打印 NOT LOADED,流水线会继续运行,因此请在最终报告中保留此列表):

  • load_skill motion-principles - 基础

每个 shell 调用只加载一个模块。 shell 调用的输出超过约 30,000 个字符时,不会以内联形式完整显示:你只会看到 2,000 个字符的预览和一个文件路径,而无法读取的模块就等于没有加载。因此,每次加载调用都会重新输出技能基础内容块并加载一个模块,使其输出低于 25,000 个字符:模块入口文件都不超过该上限,validate-skills.py 会确保这一点。加载时请持续记录:缺失模块会由 load_skill 打印 NOT LOADED,然后继续运行;最终报告会列出这两类模块。请完整加载每个模块:切勿将 load_skill 或 load_ref 的输出通过管道传给 head、tail、sed 或 grep,因为上述分批规则已确保每次调用都低于上限,而被截断的模块就等于没有加载。

上下文层(适用时加载):

检测结果加载
Web 技术栈,且范围为中型或完整load_skill tells(单独调用,并加载 references/web.md,见下文)
移动端情境(移动 Web 或原生 iOS / Android)load_skill mobile-principles
桌面端情境(macOS,或没有移动端指标的桌面 Web)load_skill desktop-principles
明确要求审计,或范围为完整load_skill design-audit
高级 UI/UX 问题load_skill ui-ux-pro-max

tells 必须单独占用一次 shell 调用。 加载其 Web 参考内容后,总计约 20,000 个字符,已接近 shell 调用输出无法以内联形式显示的临界点,不能与其他模块共用一次调用。请在该调用中重新输出技能基础内容块,然后:

# (skill-base block re-emitted above this line)
load_skill tells
load_ref tells references/web.md

它会在理念关卡之后、任何视觉选择定案之前加载:经确认的理念会说明需要哪些模式,而 tells 会指出那些没人要求的惯性做法。绝不在 Compose 或 SwiftUI 技术栈中加载,也不在轻量范围内加载。在 Paint 中,此项已满足:Phase 3 会在设计系统查询之前加载 tells。

特定技术栈(按 SCAN 加载):

检测到的技术栈要加载的子技能
gsapload_skill gsap
motion 或 framer-motion(同一库的两个包名)load_skill framer-motion
纯 CSS / Tailwind / 无库load_skill css-native
three / @react-threeload_skill threejs-r3f
Canvas / 生成式load_skill canvas-generative
Android Composeload_skill compose-motion(始终加载)+ load_skill compose-graphics(如果 scope=full 或论点属于高级内容——见下文)
Compose Multiplatformload_skill compose-motion + load_skill compose-multiplatform(始终加载);如果检测到 iOS 目标且要求 SwiftUI 互操作,则加载 load_skill swiftui-motion;如果属于高级内容,则加载 load_skill compose-graphics
SwiftUI iOS 或 macOSload_skill swiftui-motion(始终加载)+ load_skill swiftui-graphics(如果 scope=full 或论点属于高级内容)

“高级论点”触发条件,适用于 compose-graphics / swiftui-graphics:

如果论点包含以下任一术语,则属于“高级”内容(并触发加载图形子技能):

  • shader、Metal、AGSL、RuntimeShader、MSL
  • liquid-glass、glassEffect、morphing transition
  • M3 Expressive、MotionScheme、expressive motion
  • colorEffect、distortionEffect、layerEffect
  • Canvas(用于生成式 / 粒子 / 流场场景)
  • holographic、CRT、displacement、ripple

否则,只加载基础动效子技能。

实现规则:

  • 按页面或组件逐一处理,绝不要试图一次完成所有工作。
  • 每种颜色、字体、间距、阴影、圆角都必须来自 MASTER.md 令牌。不得使用魔法数字。
  • 每个动画都必须遵循交互论点(时长、缓动、禁止模式)。
  • 对交互元素应用 5 种状态规则:默认、悬停、焦点、激活、禁用。
  • 每完成一个主要页面/章节,都要请用户验证,然后再继续下一项。

第 5 阶段 - AUDIT(绝不可跳过)

加载 design-audit 子技能。**第 4 阶段以用户确认关卡结束,因此 $SKILL_BASE 再次不可用。**在本次调用中重新输出解析块,然后:

load_skill design-audit

运行与检测到的技术栈相匹配的完整审计清单。design-audit 会提供 grep 命令;下方的分类决定你可以声称哪些结果。

此流程的最后一步过去包含 25 个复选框,其中有几项提到 Agent 无法运行的工具。没有打开 Chrome,却勾选“已通过 Chrome DevTools 验证 60fps”,这会把“我没有检查”变成“我检查过了,而且没问题”,这比什么都不说更糟。

因此,检查项分为两组。报告第一组,并附上所用的证据。第二组一律不要勾选——交由他人处理。

在此检查,并提供证据

每一项按 检查项 - 结论 - 证据 的格式报告。证据是你运行的 grep 命令、计算出的数值,或你查看的 file:line。没有附带证据的结论不算检查结果;无法检查的项目应报告为未检查,而不是通过。

  • 对照代码核验设计主张。 首先,以下每项检查都假定代码就是已验证的设计主张。对于已验证的设计主张实际作出的每项承诺(时长、缓动、弹簧效果、配色、字体、布局类型),请给出对应的 file:line,或写“未找到”。如果设计主张没有对配色或字体作出承诺(例如仅涉及交互的设计主张),就不需要提供配色或字体对应的行。承诺没有对应的 file:line,就是发现了问题,而非通过检查。坦诚降级:如果承诺的动效无法正常交付,就交付静态版本,在此说明,并指出放弃了哪项承诺;这算作发现了问题。绝不要为了纸面上履行承诺而交付有缺陷的动效。证据:每项承诺一行,例如 hover 180ms ease-out - src/Card.tsx:42。
  • 遵循减少动态效果设置。 Web:必须有实际降低动画效果的 prefers-reduced-motion 代码块,不能是空代码块。SwiftUI:使用 accessibilityReduceMotion。Compose:使用基于 ValueAnimator.areAnimatorsEnabled() / Settings.Global.ANIMATOR_DURATION_SCALE 的辅助函数。证据:防护代码所在的文件和行,以及它会降级为何种效果。
  • 存在退出动画:所有会卸载内容的地方都应有退出动画。证据:条件渲染及其退出路径,或列出没有退出动画的卸载场景。
  • 不动画布局属性。 不得为 width、height、top、left、margin 或 padding 添加动画;应使用 transform、opacity 或 graphicsLayer。证据:grep 命令及其命中结果,或说明未返回结果。
  • 所有交互元素的焦点状态均可见,且不得在没有替代样式的情况下使用 outline: none。证据:grep 命令。
  • 交互元素具备全部五种状态: 默认、悬停或按下、焦点、激活、禁用。证据:列出每个组件已有的状态,以及缺失的状态。
  • 使用设计令牌,而非随意填写数值。 颜色和间距应来自项目的设计令牌(如果存在 MASTER.md,则以其为准)。证据:列出不合规范的数值及其 file:line。
  • 对比度:正文文本至少为 4.5:1,大号文本和 UI 边界至少为 3:1。必须计算你输出的令牌值之间的对比度;不能凭色块目测。证据:列出颜色组合及计算所得的比率,例如 #831843 on #FDF2F8 = 9.4:1。
  • 语义。 Web:可点击的 div 必须有对应角色,装饰性动效应使用 aria-hidden。Compose:自定义交互组件应使用 Modifier.semantics。SwiftUI:没有文本的控件应使用 .accessibilityLabel。证据:grep 命令。
  • 仅适用于 Web。 条件渲染应使用 AnimatePresence 或框架中的等效方案包裹;谨慎使用 will-change,并在动画结束后移除。证据:grep 命令。
  • 核对设计主张中的明显特征。 仅当已加载 tells 时执行。在一次 shell 调用中,重新输出 skill-base 代码块,并运行 python3 "$SKILL_BASE/design-audit/scripts/audit.py" . --group tells,无论是否已加载 design-audit。每项发现都必须提供证据:要么列出保留的问题及其 file:line,要么引用设计主张 Allowed patterns: 行中对应的条目。保留的明显特征计为发现的问题。获准的特征应标记为“设计主张允许”,并引用对应条目,不计为问题。手动检查 tells 列表(div 中的虚构产品、重复的布局类型、悬浮在角落的段落、文案语体、存放在 JavaScript 数据中的文案)时,也应以相同方式报告。 每项发现只能归入以下三种结果之一,不得有第四种:设计主张允许,并引用其 Allowed patterns: 条目;不属于此类特征,仅当检测结果与该条目自身的判定标记相矛盾时使用(例如循环动画实际表示加载状态、模糊效果是模态框背景、匹配的行不是显示给用户的文本),并提供能够证明这一点的 file:line,绝不能因个人审美判断而使用;或问题。报告前先修复,并且只修复本次运行引入的内容:如果问题出现在本次运行写入的行上(项目有 git diff 时应查看),就移除或改写该行,然后重新运行此检查,最多两轮;仍未通过的项目应连同其 file:line 一并报告。项目中原本就有、且本次运行未写入的行上的明显特征,此处绝不修改:应列出并告知用户。此步骤不得修改现有项目受保护清单中的任何内容(公共令牌名称、URL、导航标签、表单字段名称、徽标、法律声明)。
必须运行的检查——此处未验证

Agent 无法打开性能分析器、连接设备或移动指针。这些检查会作为交接事项列出,包含准确的调用方式,并标记为 UNVERIFIED。不要勾选,不要弱化措辞,也不要因为其余部分看起来没问题就省略本节。

目标运行方式通过条件
WebChrome DevTools > Performance,在交互过程中录制没有超过 16.7ms 的帧
Web在 375 / 768 / 1024 / 1440 宽度下查看页面无水平滚动,无内容被裁切
Web在操作系统开启“减少动态效果”设置时查看页面降级路径确实运行
Web如果本次会话中未渲染网页字体,则在联网时查看页面字体加载成功,而不是使用回退字体;并且字体数值和字重符合论述的要求
ComposeLayout Inspector > Component Tree > View Options > Show Recomposition Counts滚动时计数保持稳定
Compose在中端设备上运行 androidx.benchmark.macro Macrobenchmark60fps 时帧耗时低于 16.67ms,120fps 时低于 8.33ms
SwiftUIInstruments > Animation Hitches转场期间没有卡顿
SwiftUI开启 Reduce Motion,将 Dynamic Type 设为 200%没有内容被裁切,也没有只靠移动来传达的信息
macOS将指针移过每个交互元素;用键盘操作整个视图悬停状态生效,焦点环可见,快捷键已绑定

如果预览或开发服务器已经在运行,且用户同意,那么驱动浏览器收集 Web 项检查结果,比交接给用户更好。切勿仅为此次审核启动预览或开发服务器,也切勿为此安装任何内容。

分别报告两组结果,并列出数量。“已检查 11 项,发现 2 个问题,交接 8 项”才是如实的审核结果。不要只列出一串勾选项。

始终以模块清单结束报告,各占一行:

Modules loaded: motion-principles, framer-motion, design-audit
Modules not loaded: none

Modules not loaded 应列出所有 load_skill 调用报告为 NOT LOADED 的模块,以及加载表要求加载但从未请求的所有模块。只有两者都为空时才写 none。调用之间的 Shell 状态不会保留,因此从首次加载到最后一次加载,都要由你记录这份清单:Shell 不会替你记住。

在已检查的项目组中,按严重性排列发现的问题:Critical > Important > Nice-to-have。交接组不排序、不筛选——应完整列出,因为必须由用户亲自运行这些检查。

现有项目协议

在已有设计/样式的项目中调用时:

  1. 仍然执行完整的 BRAINSTORM(阶段 1)
  2. 在阶段 1 提出模式问题:保留品牌、部分调整或重新设计。模式决定 thesis 可以替换什么:保留模式下不能替换任何已定义内容,部分调整模式下只能替换用户指定的区域,重新设计模式下可以替换整个视觉层
  3. 在阶段 4,只有重新设计模式才会替换现有设计令牌。保留模式下,MASTER.md 保留现有令牌原样不动,并在其旁边添加新令牌。部分调整模式下,保留所有现有令牌名称,只修改用户指定区域中的值,并在其旁边添加新令牌
  4. 保留功能和布局结构——只替换视觉层

在这三种模式下,未经用户明确同意,以下内容均不得更改,并且必须逐项询问:公共令牌名称(本项目以外的代码可能会导入这些名称)、URL 和路由、导航标签、表单字段名称、徽标、法律声明。

编写任何令牌文件之前,先展示现有令牌与新令牌之间的差异(重命名、修改、添加、删除),并等待批准。编写后,使用技术栈自己的命令进行构建:Web 项目使用 tsc --noEmit 或项目的构建脚本(npm run build 或其 pnpm / yarn 等效命令),Android 项目使用 ./gradlew assembleDebug,Apple 项目使用 xcodebuild -scheme <scheme> build。如果无法在此处运行构建,则将其移交并标记为未验证,同时提供确切命令,格式如审计中移交的那组内容。

模式回答的是绘制过程中的“重建还是增强?”:保留模式会增强品牌,而不会替换它。/genjutsu:cast 仍然是实现单个效果或单个组件的入口。


危险信号——你即将违反这项技能

想法实际情况
“用户已经说了‘极简暗色’——足以写 thesis 了”两个词不等于五个领域。继续提问。
“我一次问完全部五个头脑风暴问题”一次一个。对“受众”的回答会影响你如何询问“氛围”。
“用户似乎不耐烦了,我们跳过编码前的步骤吧”使用压力处理协议。糟糕的 thesis 会浪费好几天,而不是几分钟。
“我来选一些感觉合适的颜色”每个令牌都来自 MASTER.md。不要擅自发挥。
“我一次性完成整个网站”按页面逐一完成。按页面逐一验证。
“虽然 thesis 说不要弹跳效果,但这个动画会很酷”thesis 就是规则。想要更改?先与用户重新验证。
“审计可以稍后再做,用户似乎很满意”审计不是可选项。阶段 5 始终要运行——范围较小时可以缩短,但不能跳过。
“审计项目看起来都没问题,我就勾选了”勾选不等于发现。报告 grep 结果、比例、文件:行号——或者注明尚未检查。
“我无法分析性能,所以就省略这一部分”移交说明块是这些项目的交付物。省略它会让人以为已通过检查。
“我把‘嗯,差不多就是这样’理解成了同意”这不算确认。询问用户具体认同哪一部分。
“我用十六进制代码列出调色板,这样很精确”精确,却无法审阅。用本次会话的预览模式展示。
“我再问一次他们想怎么查看设计系统”问过一次后,本次会话中就不再更改。说明模式并继续。
“预览页面看起来不错,我就根据它来构建应用”预览只是一次性的。根据 MASTER.md 构建。
“我无法在这里渲染获批字体,系统字体更稳妥”沙盒不等于网站。使用匹配的后备字体加载获批字体,并说明它未在此处渲染。
“这是整个网站,我要换成 bunshin,显然更好”提议一次,并说明成本;只有用户同意后才切换。这会消耗数百万个令牌。

评论

支持 Markdown