对答如牛 App(mobile 端)AI Slop 审查报告

范围:apps/mobile/src(57 个源文件,全部 13 个页面文件与共享 UI 逐行人工复核)· 工具:kill-ai-slop 扫描器(35 条 tell)+ 人工 triage · 基准:甲方《公考App_设计风格与DesignSystem规范.md》(轻陪伴备考设计系统 / Pastel Study Companion)· 日期:2026-08-22

目录
  1. 总体结论
  2. 审美基准:甲方要什么,我们守什么
  3. 确认的 AI 痕迹(12 组,按严重度排序)
  4. 复核后判定为「有意设计」保留的项
  5. 整改方案:token 增量 + 组件收敛 + 全量替换

1. 总体结论

97扫描器命中(13 组线索)
~120人工确认整改点位(含扫描器未覆盖的中文语境项)
12确认的 slop 组
8复核后保留的「疑似项」

这个 App 的骨架是健康的,slop 长在皮肤上。

Token 层(theme/tailwind.css + theme/variables.css)严格对齐了甲方规范:主色 #6C5CE7、六张模块卡浅底色与规范表格逐一对应、圆角 16/20/24 三档、三档轻阴影连数值都和规范一致。文案整体也是规范要求的「学习伙伴」语气而非「系统管理员」语气,没有出现「不只是 X,更是 Y」式 AI 文案腔。

真正的 AI 痕迹集中在一件事上:页面层大量绕过自家 token,直接抓 Tailwind 默认色板——amber-700 出现 32 次、rose-500/600 共 23 次,外加 emerald / sky / purple / gray / red 系散落各处,共约 90 处。这是典型的机器书写指纹:每一行局部看都「对」,合起来是一碗默认彩虹糖——同一个「危险」概念在代码里有 rose-400 / rose-500 / rose-600 / red-500 / danger-soft 五种写法。伴生问题是同一块「刷新失败」提示卡被复制了约 15 份(其中 4 份是在 4 个文件里各自重新定义的同名组件)、同一个控件在不同页面长着两种样子、以及两个从未生效过的失效样式类(写完没人看过渲染结果,这是 taxonomy 里最重的指控:not absence of taste, absence of looking)。

2. 审美基准:甲方要什么,我们守什么

甲方沟通资料(sync-drive)里的有效设计输入是一份完整的《设计风格与 Design System 规范》和一张 Dribbble 风格参考图。规范本身写明参考图「仅作为视觉风格参考,不直接复制」。提炼出的、本次整改据以收敛的基准:

维度甲方规范口径当前代码现状
色彩主色 #6C5CE7 不大面积铺;状态色「明确但不刺眼」;模块卡用指定浅底色;禁全卡同紫、禁多渐变token 层 ✅ 完全一致;页面层 ❌ 约 90 处 Tailwind 默认色板渗漏
圆角16/20/24 三档 + 胶囊;不用 4/6px 小圆角--radius-lg2/xl2/hero 三档,胶囊按钮是规范明确要求
阴影三档轻阴影,禁重黑投影✅ 三档数值与规范逐字一致
字号h1 28 / h2 22 / h3 18 / title 16 / body 14 / note 12.text-h1…text-note 工具类一一对应
文案「学习伙伴」语气;错误文案讲原因和下一步✅ 大体到位(「你好,未来的公务员」「利用思考时间列好答题提纲」),个别句子欠打磨
动效轻动效:按下缩放 0.98,不做过重动效⚠️ Card 已是 0.98,但三处按钮写成 0.95,口径不一
插画10 张场景插画服务于功能(空状态/成功/错误/AI 等)⚠️ 插画系统已建好,但报名成功页没用它,用了一个 text-6xl 的 emerald 大对勾图标

结论:不需要「重新设计」,需要的是把页面层拉回它自己已经建好的规范,并把规范里缺的最后一块——状态文字色(ink 系)token——补上。

3. 确认的 AI 痕迹(12 组)

G1 · 状态色绕过 token:Tailwind 默认色板渗漏 高 · ~90 处

对应 tell:#03 warm cozy palette / #04 default semantic palette / #05 one-hue status box。

项目明明定义了自己的状态色(warning #FCD34D、danger-soft #FF8FA3、success #34D399、info #22D3EE)和六张 tint 卡,但状态文字没有对应 token,于是每个调用点各自伸手抓 Tailwind 默认值:

原类次数代表位置问题
text-amber-70032ReportPages / PracticePages / RegPages / OrderPages 等全部「提示」卡amber 底(#FEF3C7) + amber 字 = 教科书式 one-hue status box,且 32 处逐一手写
text-rose-500 / text-rose-600 / text-red-50017 + 6 + 1各错误提示;RegPages.tsx:929 混入了一个孤例 text-red-500同一个「错误」概念三种色值并存,rose-500 在白底上对比度仅 ≈3.0:1
text-emerald-500/600/700bg-emerald-50010签约完成提示、微信按钮、审题命中圆点、成功大对勾项目成功色是 #34D399,emerald 系是又一套平行绿色
text-sky-600/7007chip info 调、全款价格、HubPage 图标项目 info 色是 #22D3EE,sky 系是平行蓝色
text-purple-5003HubPage 图标 ×2、试听页教师头像字主色 #6C5CE7 就在手边,却用了默认紫
gray-*8BackendSwitch.tsx(开发工具悬浮球整体)与 ink/line/surface token 平行的一套灰
bg-amber-50bg-amber-500bg-rose-400/5007BasePages.tsx:302、ReportPages 档位药丸、审题未命中圆点tint-notice / warning / danger-soft token 被旁路

为什么算 slop:不是「这些颜色丑」,而是没人选过它们。危险色横跨 rose-400→red-500 五个值、绿色横跨三个 emerald 明度,说明每一处都是生成时就近抓的默认值,而不是从项目色板推导的决定。规范 4.2 明文「禁止将所有状态都用主色表达」的反面正是「每个状态各自发明颜色」。

修复:@theme 增补 4 个状态文字色 token(从 variables.css 已有的 Ionic contrast 对色取值,真源不新增):--color-warning-ink: #78350f / --color-danger-ink: #881337 / --color-success-ink: #065f46 / --color-info-ink: #0369a1,然后全量替换。替换后整个 App 的状态色收敛为「规范六 tint 底 + 四 ink 字」一套口径,对比度全部达标。

G2 · 从未生效的失效样式类 高 · 2 处(1 个真实渲染缺陷)

对应 tell:#22 家族——「每一行局部正确、整体是错的,因为从没人看过渲染结果」。

ReportPages.tsx:184  'rounded-full bg-danger-soft … text-danger'   ← text-danger 未定义,静默失效
ReportPages.tsx:185  'rounded-full bg-surface-2 … text-ink-3'      ← bg-surface-2 未定义,静默失效

主题里只有 --color-danger-soft--color-surface-muted,没有 dangersurface-2。实际渲染效果:报告页「维度评分标准」里命中的扣分项是粉底 + 继承的深灰字(本意显然不是),未命中项干脆没有底色、字浮在卡片上。这是本次审查发现的唯一真实渲染缺陷。

修复:命中项 bg-tint-error text-danger-ink,未命中项 bg-surface-muted text-ink-3

G3 · 同一张提示卡里的色彩矛盾

RegPages.tsx:857-861  签约完成: Card tint="notice"(amber 黄底) + text-emerald-700(绿字)
RegPages.tsx:862-866  无需签约: Card tint="notice"(amber 黄底) + text-ink-2(灰字)

「已签署」是成功态却穿着提醒卡的黄底配绿字——绿字压黄底,两种语义色打架。相邻的「无需签约」又是第三种组合。生成式书写的典型症状:每个分支独立生成,没人横向看过这一组卡。

修复:成功态用 tint="local"(规范的浅绿卡)+ success-ink;中性态用素卡 + ink-2;提醒态保持 notice + warning-ink。状态与颜色一一对应。

G4 · 图标彩虹糖 中 · 10 处

对应 tell:#04「一碗默认彩虹糖」/ #25 icon-in-a-tint 的近亲。

HubPage.tsx  订单 sky-600 · 合同 primary · 住宿 purple-500 · 冲刺 emerald-600 · 权益 amber-500
             练习 primary · 直播 purple-500 · 资料 primary · 安全 sky-600 · 设置 ink-2

「我的」页 10 个列表图标用了 6 种颜色,颜色和功能之间没有任何映射(住宿为什么紫、权益为什么黄?)。这正是 taxonomy 说的「nobody picked these hues」。同族问题:试听页教师头像字用 text-purple-500(RegPages.tsx:1065),登录页微信按钮用 text-emerald-600(BasePages.tsx:191——既不是项目色也不是微信品牌绿)。

修复:让颜色携带真实信息——可用功能图标一律 primary,未开放(planned)功能图标一律 ink-3,扫一眼就能分辨什么能点。教师头像字改 primary;微信按钮改用微信品牌色 #07C160(decoration must mean something:这个绿属于微信,不属于色板抽奖)。

G5 · 评分档位的红绿灯药丸 中 · 6 处

ReportPages.tsx:39-53  优秀 bg-emerald-500 / 良好 bg-primary / 中等 bg-amber-500 / 较差 bg-rose-500 (白字)
ReportPages.tsx:212    审题命中 bg-emerald-500 / 未命中 bg-rose-400 (白字)

对应 tell:#05「red/yellow/green is a traffic light, not a palette」。白字压 amber-500 对比度 ≈2.2:1,压 emerald-500 ≈2.1:1,均不达标;且这四个饱和色块和整页柔和 tint 卡的气质断裂。

修复:药丸改用 App 自己的 chip 语言——tint 底 + ink 字:优秀 bg-tint-local text-success-ink、良好 bg-tint-ai text-primary、中等 bg-tint-notice text-warning-ink、较差 bg-tint-error text-danger-ink。审题命中圆点同理。信息不变,音量调到和整个 App 一致。

G6 · 已完成步骤加删除线 低 · 1 处

PracticePages.tsx:821  评分中步骤条:已完成步骤 text-ink-3 line-through

对应 tell:#09——删除线的语义是「删掉了」,不是「完成了」。步骤左侧已有 ✓ 圆点表达完成,再划掉一次是双重表达且语义错误。修复:去掉 line-through,保留降灰。

G7 · 报名成功页的巨型图标 中 · 1 处

RegPages.tsx:811  <IonIcon icon={checkmarkCircle} className="text-6xl text-emerald-500" />

对应 tell:#18 巨型装饰图标。这个 App 明明有一套规范要求的场景插画系统(错误页都在用 error-gentle、完成页用 success-trophy),偏偏「报名成功」——全流程情绪最高点——放了一个 96px 的默认绿大对勾。这是插画系统建好了却没贯彻的断点。修复:<Illo name="success-trophy" />,与全 App 状态页语言统一。

G8 · 复制粘贴出来的组件 高 · ~30 处

对应 tell:slop 的本质是「无决定的重复」,代码层的表现就是同一块 UI 被生成 N 次而不是抽象一次:

重复物规模细节
「刷新失败,当前展示上次成功加载的…」提示卡4 个同名组件定义 + ~11 处内联复制QueryRefreshWarning 在 PracticePages / HistoryAbilityPages 各定义一次,RefreshWarning 在 ReportPages 定义一次,HomePage/RegPages/OrderPages 再手写 ~11 份;文案尾部有的带句号有的不带,「…的内容」和「…的{scope}。」两种口径
居中 loading 块~14 处<div class="grid justify-items-center py-16"><IonSpinner … 逐页复制,spinner 尺寸 h-6 / h-8 / h-10 三种、留白 py-6/12/16 三种,无规律
会话状态文案表 STATUS_TEXT3 份全等拷贝ai/HomePage、ReportPages、HistoryAbilityPages 各一份,将来改一处漏两处
首页/AI 首页主卡的胶囊按钮2 处同一视觉元素两套内边距:px-5 py-2 vs px-6 py-2.5

修复:ui.tsx 增加三个小组件——Notice(tone: notice/error/success,可带 action 按钮)、RefreshNotice(统一文案)、Loading(统一尺寸留白);STATUS_TEXT 收敛到 data/ai.ts 单一出口。全量替换后删掉 4 个局部定义。

G9 · 同一控件、两张面孔

性别选择器 · ApplyFormPage(报名表单)
bg-[#f9fafb] text-ink-2(无边框浅灰底)
选中:bg-tint-ai text-primary ring-1
性别选择器 · ProfilePage / BasePages(资料页)
border border-line bg-white(白底描边)
选中:bg-primary text-white shadow-card

完全相同的「男/女」二选一控件,两个页面两套视觉,连选中态的表达方式(tint+ring vs 实心主色)都不同。资料页版本还带着一条注释解释了为什么必须有可见边界——报名页版本恰好踩了这条注释警告的坑。同族:active:scale-95(3 处按钮)vs 规范 16.3 与 Card 组件的 0.98。

修复:统一为资料页的带边框版本(有注释论证过的那个);按下缩放统一 0.98。

G10 · 裸 hex 复制 低 · 3 处

bg-[#f9fafb] 在 tailwind.css 的 .field-box 和 ApplyFormPage 内联各写一次。规范 9.8 定义的输入框底色应当是一个 token。修复:--color-field: #f9fafb,两处引用。

G11 · 开发工具悬浮球的平行灰色系 低 · 8 处

BackendSwitch.tsx 整个组件用 gray-100/200/400/500/700 + shadow-lg,与项目的 ink/line/surface/shadow token 平行。虽是 DEV 工具,但它会出现在内测包里,且是最容易顺手收敛的文件。修复:换 token 类。

G12 · 文案微瑕 低 · 2 处

位置现文案问题与建议
ai/HomePage.tsx:141「还没有练习记录,点上方"开始练习"来第一次吧」「来第一次吧」表述别扭 → 「完成第一次练习吧」
PracticePages.tsx:830「稍后查看,先去别处逛逛」「逛逛」偏闲逛语感,与评分等待场景不匹配 → 「稍后查看,先去练别的」(保持伙伴语气、贴合场景)

其余文案整体质量好:错误文案讲原因和下一步(「你的作答已保留,可以重试评分」),未发现「不只是 X 更是 Y」「告别 XX」等 AI 文案腔,也没有装饰性 emoji(唯一一个 ⚠️ 在代码注释里,不进产品)。

4. 复核后判定为「有意设计」保留的项

扫描器命中但读码后确认是被论证过的选择,不改(避免把 de-slop 做成另一种无脑 lint):

命中判定理由
大量 rounded-full 胶囊(tell #19)保留甲方规范 2.4/9.4 明确要求「胶囊按钮、圆形图标按钮」,是设计系统的核心特征
录音按钮 animate-ping 光晕(tell #16)保留背后是真实的「正在录音」状态——taxonomy 的判据是「有没有真实状态」,这里有;录音指示脉动是行业惯例
字体栈含 Inter(tell #33)保留规范 5.1 指定「英文与数字:Inter / SF Pro」,且只是系统栈的末位回退,主体是 PingFang/思源黑体
紫色主色 + 六色 tint 卡(tell #01/04 疑似)保留#6C5CE7 与六张 tint 是甲方规范逐一定义的品牌色板,非默认值
定金/全款双色价格(tell #13 疑似)保留0711 会议甲方拍板「全款与定金同等大小同等突出,双色区分」;仅把第二色从裸 sky-600 收敛为 info-ink token
作答方式 grid-cols-3(tell #28)保留文字/语音/视频是三个真实互斥选项,不是装饰性卡片网格
Card active:scale-[0.98](tell #26 疑似)保留规范 16.3 明确「按钮按下缩放 0.98」;且只 transition transform 单属性,非 transition-all
思考页同心倒计时圆环(tell #21 疑似)保留h-36/h-28 圆形嵌套天然同心,无圆角错位问题

5. 整改方案

三层动作,全部不触碰业务逻辑(仅 className、共享组件抽取、两句文案):

5.1 Token 增量(theme/tailwind.css)

@theme {
  /* 状态文字色:取值 = variables.css 既有的 Ionic contrast 对色,单一真源 */
  --color-warning-ink: #78350f;   /* 压 tint-notice #FEF3C7 */
  --color-danger-ink:  #881337;   /* 压 tint-error  #FFE4E6 */
  --color-success-ink: #065f46;   /* 压 tint-local  #DCFCE7 */
  --color-info-ink:    #0369a1;   /* 压 tint-reg    #DBEAFE,兼任全款价格色 */
  --color-field:       #f9fafb;   /* 输入框底(规范 9.8) */
  --color-wechat:      #07c160;   /* 微信品牌绿,仅登录按钮 */
}

5.2 组件收敛(components/ui.tsx)

<Notice tone="notice|error|success" action={…}>…</Notice>   // 统一提示卡(tint 底 + ink 字 + 可选右侧动作)
<RefreshNotice scope="课程" />                               // 统一「刷新失败」文案,消灭 15 份拷贝
<Loading />                                                  // 统一居中加载块(尺寸/留白一档)
Chip 色调改用 ink token;STATUS_TEXT 收敛到 data/ai.ts

5.3 全量替换清单(按文件)

文件动作
components/ui.tsxchipTones 换 token;新增 Notice / RefreshNotice / Loading
theme/tailwind.css增 6 个 token;field-box 引用 --color-field
pages/ai/ReportPages.tsx修复 2 个失效类;档位药丸/审题圆点改 tint+ink;amber-700×8 → Notice;删本地 RefreshWarning
pages/ai/PracticePages.tsx去 line-through;rose/amber → token/Notice;删本地 QueryRefreshWarning;文案 1 句
pages/ai/HomePage.tsx / HistoryAbilityPages.tsx提示卡 → RefreshNotice;loading → Loading;STATUS_TEXT 引用收敛;主卡按钮统一;文案 1 句
pages/registration/RegPages.tsx成功页换插画;签约状态卡语义配色;性别选择器统一;amber/rose/red/sky/purple 全量 → token;报名协议卡分态
pages/home/HomePage.tsx / pages/my/*HubPage 图标改「可用=primary / 未开放=ink-3」;提示卡与 loading 收敛;rose → danger-ink
pages/base/BasePages.tsxamber-50 盒 → Notice;微信按钮 → 品牌绿;性别选择器保持描边版(作为统一基准)
components/BackendSwitch.tsxgray-* → ink/line/surface token;scale 统一 0.98

验证:整改后重跑 kill-ai-slop 扫描器确认色板类命中归零(保留项加注释说明);pnpm --filter @gk/mobile typecheck + mobile 测试全绿后提 PR。