Every non-deck, non-web scene now holds ten templates: cards (spec themes as living samples), infographics, tutorials, comparisons and carousels (nine spec themes with per-set master-board rules), plus two web landings whose 'generate from this' pins their measured style guides — a basis the document swap used to drop before the turn ever saw it. All fifty-eight ship generator-reproduced (sha256- matched), audit-clean, with baked preview cards, and the scene tab row pins Slides second-from-last and Web last. Two prompt-center entries carry the web pair with re-rendered previews and honest render provenance. Claude-Session: https://claude.ai/code/session_01FqKQqNj8exYwopGDpYUU7x
213 lines
9 KiB
Python
213 lines
9 KiB
Python
#!/usr/bin/env python3
|
||
"""version-diff-comparison.op — 新旧版本变化(1080×1080 方版 1:1)
|
||
|
||
对比档里唯一一张**行内**对照:不分左右两栏,每一行自己就是一次「旧 → 新」。
|
||
读者不需要在两栏之间来回扫,只要顺着列表往下滑,每一行都在一处完成对比。
|
||
|
||
### 最近邻论证(为什么它不是已有的哪一张)
|
||
|
||
- **before-after(1600×900)**:那张也是「旧 → 新」,但只有**一组**,主
|
||
体是两张大截图。这张是**五组**,每组只有一对短语——它比的不是画面而是
|
||
条目,所以能塞进一个方版里滑着看。
|
||
- **本批 02 好坏双栏**:那张把「错」和「对」各自聚成一栏,眼睛左右扫;
|
||
这张把「旧」和「新」在**同一行内**并置,眼睛只往下走。同为二元对照,
|
||
版式的骨架完全不同。
|
||
- **本批 07 时间对比**:那张是一年前 / 现在的**整体气质**变化,主体是两
|
||
组并列的状态;这张是**逐条变更**,主体是一份可以照着核对的清单。
|
||
|
||
### 配色推导(采样 → 收敛 → 论证)
|
||
|
||
- **采样**:从「版本记录」这件事采——它属于终端、日志、更新说明那一类
|
||
版面,记忆色是墨绿黑底上的浅字,不是任何品牌色。
|
||
- **收敛**:单色相墨绿(H≈175°)的一条七级明度序列 L 0.09 / 0.21 / 0.38 /
|
||
0.59 / 0.87 / 0.92 / 1.0,chroma 深处 0.03、浅处 0.008,只有一个色相。
|
||
- **论证**:一行里有两个值在争注意力,如果给「新」配一个高饱和色,五行
|
||
下来就是五个亮点,等于没有重点。改用**明度**:旧值是浅底弱字(已经退
|
||
场),新值是深底反白(已经生效)。五行的深块自然连成一条视觉纵线,读
|
||
者第一眼看到的就是「新版是什么样」。
|
||
|
||
### 负约束(本模板明令不做的事)
|
||
|
||
- **不用第二个色相。** 整张图只有墨绿一族 + 中性白。
|
||
- 不用红绿。版本变化没有对错,旧值当时也是对的。
|
||
- **不给旧值加删除线。** 删除线在中文里会压在字身上、糊成一片;「退场」
|
||
由浅底弱字表达就够了。
|
||
- 不写「重磅 / 史诗级 / 全新升级」。每行只写变成了什么。
|
||
- 不用蓝紫渐变、霓虹线条、伪 3D、阴影、emoji 图标。
|
||
- 每个值 ≤8 字:新旧两个 chip 等宽,写长了两边会一高一低,纵线就断了。
|
||
- 不列超过六行。列表一长,读者就会开始跳读,「逐条核对」这个前提就没了。
|
||
|
||
硬契约:
|
||
- 内容距边缘 ≥64px(这里 64)
|
||
- 固定 1:1 画幅:根高写死 1080,靠 space_between 分配三块之间的空隙
|
||
- 新旧两个 chip 必须都是 fill_container,等宽是「同一件事的两个取值」的
|
||
视觉前提
|
||
- 配色全部走 color_vars;单色序列,改一处色相即整张换肤
|
||
- 正文与背景对比度 ≥2.0(最低一对见文件末尾实测表)
|
||
- **CJK 行高**:大标题 1.2,行标题 1.3,正文 1.6
|
||
- **CJK 负字距不超过 -0.02em**;版本号与数字走 Inter
|
||
"""
|
||
|
||
import os
|
||
import sys
|
||
|
||
sys.path.insert(0, os.path.join(
|
||
os.path.dirname(os.path.dirname(os.path.dirname(
|
||
os.path.dirname(os.path.abspath(__file__))))),
|
||
"templates", "step0", "_generators"))
|
||
|
||
from oplib import (Ids, color_vars, frame, icon_font, rect, solid, text,
|
||
write_doc)
|
||
|
||
ids = Ids()
|
||
|
||
VARS = color_vars({
|
||
"c-bg": "#EEF2F1",
|
||
"c-card": "#FFFFFF",
|
||
"c-panel": "#DCE4E2",
|
||
"c-line": "#C7D2CF",
|
||
"c-ink": "#0C1817",
|
||
"c-deep": "#1D3733",
|
||
"c-muted": "#465F5B",
|
||
"c-faint": "#7C918D",
|
||
"c-inv-muted": "#9CB0AC",
|
||
})
|
||
|
||
CJK = "Noto Sans SC"
|
||
NUM = "Inter"
|
||
|
||
W, H = 1080, 1080
|
||
EDGE = 64
|
||
LABEL_W = 168
|
||
|
||
LH_DISPLAY, LH_HEAD, LH_BODY = 1.2, 1.3, 1.6
|
||
|
||
OLD_VER, NEW_VER = "v1.4", "v2.0"
|
||
|
||
# (类别, 旧值, 新值)。每个值 ≤8 字:两个 chip 等宽,长短不一会把纵线撑歪。
|
||
CHANGES = [
|
||
("首页加载", "3.4 秒", "0.9 秒"),
|
||
("导航层级", "三层菜单", "一层直达"),
|
||
("深色模式", "没有", "跟随系统"),
|
||
("导出格式", "只有 PNG", "还能存 PDF"),
|
||
("免费额度", "10 次 / 月", "50 次 / 月"),
|
||
]
|
||
|
||
|
||
def col(name, children, *, gap=16, width="fill_container", align="start",
|
||
**props):
|
||
node = frame(ids, name, width=width, height="fit_content",
|
||
layout="vertical", gap=gap, alignItems=align, fill=[],
|
||
**props)
|
||
node["children"] = children
|
||
return node
|
||
|
||
|
||
def row(name, children, *, gap=16, align="center", width="fill_container",
|
||
**props):
|
||
node = frame(ids, name, width=width, height="fit_content",
|
||
layout="horizontal", gap=gap, alignItems=align, fill=[],
|
||
**props)
|
||
node["children"] = children
|
||
return node
|
||
|
||
|
||
def version_chip(label, current):
|
||
"""版本号徽记。旧版描边、新版实心——形状语言在页头就先声明一次。"""
|
||
node = frame(ids, "版本徽记", width="fit_content", height="fit_content",
|
||
layout="horizontal", padding=[9, 20], cornerRadius=8,
|
||
alignItems="center", justifyContent="center",
|
||
fill=solid("$c-ink") if current else [])
|
||
if not current:
|
||
node["stroke"] = {"thickness": 2, "fill": solid("$c-line")}
|
||
node["children"] = [
|
||
text(ids, "版本号", label, 26, 700,
|
||
"$c-card" if current else "$c-muted", family=NUM,
|
||
width="fit_content", growth="auto", line_height=1.4),
|
||
]
|
||
return node
|
||
|
||
|
||
# --------------------------------------------------------------------- 页头
|
||
def head():
|
||
return col("页头", [
|
||
row("版本行", [
|
||
version_chip(OLD_VER, False),
|
||
icon_font(ids, "版本箭头", "arrow-right", 26, "$c-faint"),
|
||
version_chip(NEW_VER, True),
|
||
], gap=14, align="center", width="fit_content"),
|
||
text(ids, "主标题", "这版改了什么", 72, 700, "$c-ink", family=CJK,
|
||
line_height=LH_DISPLAY, spacing=-1.4),
|
||
text(ids, "副标题", "五处能被立刻用上的变化,深色那一半是现在的样子。",
|
||
26, 400, "$c-muted", family=CJK, line_height=LH_BODY),
|
||
], gap=20)
|
||
|
||
|
||
# ------------------------------------------------------------------- 变更行
|
||
def value_chip(content, current):
|
||
"""一个取值。旧 = 浅底弱字(退场),新 = 深底反白(生效)。"""
|
||
box = frame(ids, "取值", width="fill_container", height=64,
|
||
layout="horizontal", padding=[0, 22], gap=0,
|
||
alignItems="center", justifyContent="start", cornerRadius=8,
|
||
fill=solid("$c-deep" if current else "$c-panel"))
|
||
box["children"] = [
|
||
text(ids, "取值文字", content, 27, 700 if current else 400,
|
||
"$c-card" if current else "$c-muted", family=NUM,
|
||
line_height=1.4),
|
||
]
|
||
return box
|
||
|
||
|
||
def change_row(label, old, new):
|
||
return row("变更行", [
|
||
text(ids, "类别", label, 26, 600, "$c-ink", family=CJK,
|
||
width=LABEL_W, line_height=1.4),
|
||
value_chip(old, False),
|
||
icon_font(ids, "变为", "arrow-right", 24, "$c-faint"),
|
||
value_chip(new, True),
|
||
], gap=14, align="center")
|
||
|
||
|
||
def changes():
|
||
items = [change_row(*change) for change in CHANGES]
|
||
return col("变更列表", items, gap=14)
|
||
|
||
|
||
# --------------------------------------------------------------------- 页脚
|
||
def tail():
|
||
band = col("页脚", [
|
||
text(ids, "结语", "另有六项小修复,都写在更新日志里。", 29, 600,
|
||
"$c-card", family=CJK, line_height=LH_HEAD),
|
||
row("署名行", [
|
||
text(ids, "账号名", "@ 你的账号名", 25, 600, "$c-card",
|
||
family=CJK, width="fit_content", growth="auto",
|
||
line_height=1.4),
|
||
text(ids, "更新说明", "每次发版都出一张", 23, 400, "$c-inv-muted",
|
||
family=CJK, width="fit_content", growth="auto",
|
||
line_height=1.4),
|
||
], gap=14),
|
||
], gap=12, padding=[32, 32])
|
||
band["fill"] = solid("$c-ink")
|
||
return band
|
||
|
||
|
||
def build():
|
||
page = frame(ids, "新旧版本变化", width=W, height=H, layout="vertical",
|
||
padding=[56, EDGE], gap=36, justifyContent="space_between",
|
||
alignItems="start", fill=solid("$c-bg"), clipContent=True)
|
||
page["children"] = [head(), changes(), tail()]
|
||
page["x"], page["y"] = 0, 0
|
||
return [page]
|
||
|
||
|
||
# 对比度(WCAG 相对亮度比,op-design-lint 门槛 2.0;数值由 contrast.py 实测):
|
||
# c-ink on c-bg 16.06 c-muted on c-bg 6.10
|
||
# c-muted on c-panel 5.33 c-card on c-deep 12.75
|
||
# c-card on c-ink 18.12 c-inv-muted on c-ink 7.96
|
||
# c-faint on c-bg 2.96(只画两处箭头与旧版号描边,非正文)
|
||
# 承载正文的最低一对是 5.33 —— 旧值压在浅底 chip 上,过 AA 正文门槛(4.5)
|
||
# 但明显轻于同行的新值(12.75),「退场 / 生效」的落差就是这么来的。
|
||
|
||
if __name__ == "__main__":
|
||
write_doc(sys.argv[1], VARS, build(), "新旧版本变化")
|