| name | jobs-presentation-style |
| description | 乔布斯发布会 PPT 风格 Skill。从苹果历届 Keynote 逆向提炼的设计原则与 python-pptx 代码模板。 适用于需要高冲击力、极简视觉表达的汇报或答辩场景。 触发词:Jobs 风格 / 乔布斯风格 / 发布会风格 / 极简暗色 / Keynote 风格 /
高对比度深色 PPT / 每页一个概念 / 大字版 PPT。
|
乔布斯发布会 PPT 风格规格
来源:Apple Keynote 1998-2011 苹果年度发布会设计模式归纳
核心原则:一张幻灯片,一个概念,零废话
零、装饰线为什么是错误的(必读)
问题本质
"为什么要加一条横线?" → 因为脑子里有个模式:"用线分隔内容区"。
这是中文 PPT 模板的惯例,不是 Jobs 的逻辑。
Jobs 的视觉层级来源
| 层级工具 | Jobs 的做法 | ❌ 错误替代 |
|---|
| 章节分隔 | 新的一页幻灯片 + 章节名独占整页 + 大量留白 | 同一页加两根线夹住文字 |
| 内容与标题的区分 | 标题 32pt Bold vs 内容 24pt Regular | 标题下加一根线 |
| 封面与副标题的区分 | 副标题缩小到 15pt 且颜色变灰 | 标题与副标题之间加分隔线 |
| 致谢页 | 整页只有一句 "Thank you." 30pt,其余全是空白 | 下方加细线 + 作者姓名 |
Jobs 的任何一张幻灯片上,都没有为了"分隔"而存在的几何线条。
为什么这条规则经常被违反
C_LINE 颜色常量一旦存在,就会被"顺手"使用
- 分隔页模板的 ASCII 图里画了线,生成代码自然跟着画
- "这个地方感觉需要分隔一下" = 留白不够,应该增加字号差,而不是加线
修正规则
- 删除所有
add_hline / _hline 调用
C_LINE 变量从代码中删除(彻底移除,不保留)
- 分隔页:章节名 + 整页留白,无任何几何元素
- 列表页:标题与条目之间用字号差(28pt vs 23pt)区分,无横线
- 封面:只有文字,无任何线条
- 致谢:只有文字,无任何线条
一、五条核心定律
| # | 定律 | 正例 | 反例 |
|---|
| 1 | 一屏一念 | 整页只写"1000 songs in your pocket" | 一页塞 7 条要点 + 子要点 |
| 2 | 数字独立成幕 | 整页只显示"45" + 小标题 | 数字埋在段落里 |
| 3 | 禁止嵌套要点 | 最多 3 项,同级,无缩进 | 一级→二级→三级 bullet |
| 4 | 图像强于文字 | 大图铺满,只覆叠一句话 | 图小文多,图注长于图 |
| 5 | 留白即力量 | 巨字+黑底+大量留白 | 满屏塞满文字图表 |
二、配色系统
深色主题(标准 Jobs 风格)
| 角色 | Hex | 用途 |
|---|
| 背景 | #0D1B2A | 深夜蓝(比纯黑更有层次) |
| 主文字 | #FFFFFF | 白色正文 |
| 关键词/标题 | #5BC0FF | 亮蓝(Apple 蓝的学术化版本) |
| 大数字 | #F5D06E | 金色:统计数字、关键量化 |
| 次要文字 | #8A9BAD | 灰蓝:章节标签、图注、辅助说明 |
⚠️ 无 C_LINE:Jobs 配色系统不存在「分割线颜色」变量。定义了这个名字就会不自觉用它。彻底删除。
C_BG = RGBColor(0x0D, 0x1B, 0x2A)
C_WHITE = RGBColor(0xFF, 0xFF, 0xFF)
C_ACC = RGBColor(0x5B, 0xC0, 0xFF)
C_GOLD = RGBColor(0xF5, 0xD0, 0x6E)
C_MUTED = RGBColor(0x8A, 0x9B, 0xAD)
浅色主题(学术正式场合备选)
| 角色 | Hex |
|---|
| 背景 | #FFFFFF |
| 主文字 | #1A1A2E |
| 关键词 | #0A5FA0 |
| 大数字 | #C67B00 |
| 次要文字 | #666666 |
三、字体系统
| 用途 | 字号 | 粗体 | 颜色 | 对齐 |
|---|
| 封面大标题 | 48 pt | ✅ | C_ACC | CENTER |
| 英雄语句(单页关键句) | 40-52 pt | ✅ | C_WHITE | CENTER |
| 大数字 | 96-120 pt | ✅ | C_GOLD | CENTER |
| 大数字小标签 | 20 pt | ❌ | C_MUTED | CENTER |
| 分隔页章节名 | 32 pt | ✅ | C_ACC | CENTER |
| 列表项 | 24-28 pt | ❌/conditional | C_WHITE | LEFT/CENTER |
| 次要标签 | 11-13 pt | ❌ | C_MUTED | LEFT |
⚠️ 字号最小阈值:内容正文不得低于 22pt。低于 22pt = 演示失败。
四、幻灯片类型目录
4.1 cover(封面)
┌─────────────────────────────────────┐
│ [空白上部] │
│ │
│ 【主标题 48pt 亮蓝 居中】 │
│ ───────────── 细线 │
│ 副标题 1 14pt 灰色 居中 │
│ 副标题 2 13pt 灰色 居中 │
│ │
└─────────────────────────────────────┘
4.2 hero(英雄语句页)
Jobs 最标志性的幻灯片类型。整页只有一句话。
┌─────────────────────────────────────┐
│ [小标签 顶部左对齐 灰色 12pt] │
│ │
│ 关键语句 │
│ 40-52pt 白色 居中 │
│ │
└─────────────────────────────────────┘
代表例:"1000 songs in your pocket." / "窗的通透性,是如何被建构的?"
4.3 number(大数字页)
┌─────────────────────────────────────┐
│ │
│ 45 │
│ 96pt 金色 居中 │
│ 个典型案例 │
│ 20pt 灰色 居中 │
│ │
└─────────────────────────────────────┘
4.4 divider(章节分隔页)
类似 Apple 的 "Introducing..." 幕。
┌─────────────────────────────────────┐
│ │
│ ────── 细分割线 ────── │
│ │
│ 章节名称 32pt 亮蓝 居中 │
│ │
│ ────── 细分割线 ────── │
│ │
└─────────────────────────────────────┘
4.5 list(简洁列表页)
最多 4 项,无子项,同级对齐。 这是 Jobs 对 bullet point 的唯一妥协,且代价极高。
┌─────────────────────────────────────┐
│ 标题 24pt 亮蓝 左对齐 │
│ ──── 细线 ──────────────── │
│ │
│ ① 条目一 24pt 白色 │
│ │
│ ② 条目二 24pt 白色 │
│ │
│ ③ 条目三 24pt 白色 │
│ │
└─────────────────────────────────────┘
4.6 statement(陈述页)
1-2 句完整陈述,字号 26-32pt。
┌─────────────────────────────────────┐
│ [小标签 上部] │
│ │
│ 主陈述语句 28-32pt 白色 │
│ (居中,允许换行) │
│ │
│ 次要补充 18pt 灰色 │
│ │
└─────────────────────────────────────┘
4.7 split(左文右图)
图像优先;文字只放 1-2 句,无列表。
┌─────────────┬───────────────────────┐
│ 文字区 │ 图片区(占 55-60%) │
│ │ │
│ 1-2句 │ 纯图,无边框 │
│ 22-26pt │ 无背景色填充 │
│ 白色 │ │
│ │ 图注 10pt 灰色 │
└─────────────┴───────────────────────┘
4.8 thanks(致谢页)
┌─────────────────────────────────────┐
│ │
│ 感谢各位老师批评指正 │
│ 28-32pt 白色 居中 │
│ │
│ ────── 细线 ────── │
│ 姓名 · 日期 13pt 灰色 │
│ │
└─────────────────────────────────────┘
五、python-pptx 完整代码模板
注意:下方代码模板已移除所有 add_hline 和 C_LINE。
视觉层级完全由字号差(28pt vs 23pt)、字重(Bold vs Regular)和留白承担。
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN
SW = Inches(13.333)
SH = Inches(7.5)
C_BG = RGBColor(0x0D, 0x1B, 0x2A)
C_WHITE = RGBColor(0xFF, 0xFF, 0xFF)
C_ACC = RGBColor(0x5B, 0xC0, 0xFF)
C_GOLD = RGBColor(0xF5, 0xD0, 0x6E)
C_MUTED = RGBColor(0x8A, 0x9B, 0xAD)
F_CH = "微软雅黑"
def new_slide(prs):
"""深色背景空白幻灯片"""
slide = prs.slides.add_slide(prs.slide_layouts[6])
bg = slide.shapes.add_shape(1, 0, 0, SW, SH)
bg.fill.solid()
bg.fill.fore_color.rgb = C_BG
bg.line.fill.background()
return slide
def txb(slide, left, top, width, height):
"""快捷文本框"""
return slide.shapes.add_textbox(left, top, width, height)
def run(para, text, size, bold=False, color=C_WHITE, fname=F_CH):
r = para.add_run()
r.text = text
r.font.size = Pt(size)
r.font.bold = bold
r.font.color.rgb = color
r.font.name = fname
return r
def build_cover(prs, title, sub_lines):
slide = new_slide(prs)
title_top = SH * 0.28
title_w = SW - Inches(3.0)
title_l = (SW - title_w) / 2
t = txb(slide, title_l, title_top, title_w, Inches(1.3))
p = t.text_frame.paragraphs[0]
p.alignment = PP_ALIGN.CENTER
run(p, title, 48, bold=True, color=C_ACC)
for i, line in enumerate(sub_lines):
sb = txb(slide, Inches(2.0), title_top + Inches(1.4) + i * Inches(0.52),
SW - Inches(4.0), Inches(0.50))
p2 = sb.text_frame.paragraphs[0]
p2.alignment = PP_ALIGN.CENTER
run(p2, line, 15 if i == 0 else 13, color=C_MUTED)
def build_hero(prs, statement, label=""):
slide = new_slide(prs)
if label:
lb = txb(slide, Inches(0.7), Inches(0.4), SW - Inches(1.4), Inches(0.35))
p = lb.text_frame.paragraphs[0]
run(p, label, 12, color=C_MUTED)
stmt_top = (SH - Inches(1.5)) / 2
st = txb(slide, Inches(1.0), stmt_top, SW - Inches(2.0), Inches(1.5))
tf = st.text_frame
tf.word_wrap = True
p = tf.paragraphs[0]
p.alignment = PP_ALIGN.CENTER
run(p, statement, 44, bold=True, color=C_WHITE)
def build_number(prs, number, caption):
slide = new_slide(prs)
num_top = SH * 0.22
nt = txb(slide, Inches(1.0), num_top, SW - Inches(2.0), Inches(2.2))
p = nt.text_frame.paragraphs[0]
p.alignment = PP_ALIGN.CENTER
run(p, number, 108, bold=True, color=C_GOLD)
ct = txb(slide, Inches(1.0), num_top + Inches(2.3), SW - Inches(2.0), Inches(0.55))
p2 = ct.text_frame.paragraphs[0]
p2.alignment = PP_ALIGN.CENTER
run(p2, caption, 22, color=C_MUTED)
def build_divider(prs, chapter_name, sub=""):
"""整页留白 + 章节名 = 视觉分隔,无横线"""
slide = new_slide(prs)
mid = SH / 2
name_h = Inches(0.9)
nt = txb(slide, Inches(1.5), mid - name_h / 2, SW - Inches(3.0), name_h)
p = nt.text_frame.paragraphs[0]
p.alignment = PP_ALIGN.CENTER
run(p, chapter_name, 32, bold=True, color=C_ACC)
if sub:
st = txb(slide, Inches(2.0), mid + name_h / 2 + Inches(0.1),
SW - Inches(4.0), Inches(0.5))
p2 = st.text_frame.paragraphs[0]
p2.alignment = PP_ALIGN.CENTER
run(p2, sub, 16, color=C_MUTED)
def build_list(prs, heading, items, label=""):
"""items: list[str],最多 4 项,无子项。字号差即分隔,无需横线。"""
slide = new_slide(prs)
if label:
lb = txb(slide, Inches(0.8), Inches(0.38), SW - Inches(1.6), Inches(0.32))
p = lb.text_frame.paragraphs[0]
run(p, label, 12, color=C_MUTED)
ht = txb(slide, Inches(0.8), Inches(0.78), SW - Inches(1.6), Inches(0.72))
p = ht.text_frame.paragraphs[0]
run(p, heading, 28, bold=True, color=C_ACC)
n = len(items)
avail_h = SH - Inches(1.68) - Inches(0.25)
item_h = avail_h / max(n, 1)
for i, item in enumerate(items):
it = txb(slide,
Inches(1.2),
Inches(1.68) + i * item_h,
SW - Inches(2.4), item_h - Inches(0.1))
tf = it.text_frame
tf.word_wrap = True
p = tf.paragraphs[0]
p.alignment = PP_ALIGN.LEFT
run(p, item, 23, color=C_WHITE)
return slide
def build_statement(prs, main_text, sub_text="", label=""):
slide = new_slide(prs)
if label:
lb = txb(slide, Inches(0.8), Inches(0.4), SW - Inches(1.6), Inches(0.35))
p = lb.text_frame.paragraphs[0]
run(p, label, 12, color=C_MUTED)
mt_h = Inches(1.8)
mt_top = (SH - mt_h) / 2 - (Inches(0.5) if sub_text else 0)
mt = txb(slide, Inches(0.9), mt_top, SW - Inches(1.8), mt_h)
tf = mt.text_frame
tf.word_wrap = True
p = tf.paragraphs[0]
p.alignment = PP_ALIGN.CENTER
run(p, main_text, 30, bold=True, color=C_WHITE)
if sub_text:
st = txb(slide, Inches(0.9), mt_top + mt_h + Inches(0.15),
SW - Inches(1.8), Inches(0.6))
p2 = st.text_frame.paragraphs[0]
p2.alignment = PP_ALIGN.CENTER
run(p2, sub_text, 18, color=C_MUTED)
def build_split(prs, text_lines, img_path=None, img_label="", caption="", label=""):
"""text_lines: list[str],最多 2 行,字号 24-26pt"""
slide = new_slide(prs)
txt_w = Inches(5.0)
img_l = Inches(6.0)
img_w = SW - img_l - Inches(0.4)
img_h = SH - Inches(1.2)
img_t = Inches(0.5)
if label:
lb = txb(slide, Inches(0.6), Inches(0.3), txt_w, Inches(0.35))
run(lb.text_frame.paragraphs[0], label, 12, color=C_MUTED)
txt_top = (SH - len(text_lines) * Inches(0.7)) / 2
for i, line in enumerate(text_lines):
lt = txb(slide, Inches(0.6), txt_top + i * Inches(0.72), txt_w, Inches(0.65))
tf = lt.text_frame
tf.word_wrap = True
p = tf.paragraphs[0]
p.alignment = PP_ALIGN.LEFT
run(p, line, 24, color=C_WHITE)
if img_path and Path(img_path).exists():
slide.shapes.add_picture(str(img_path), img_l, img_t, img_w, img_h)
else:
ph = slide.shapes.add_shape(1, img_l, img_t, img_w, img_h)
ph.fill.solid()
ph.fill.fore_color.rgb = RGBColor(0x1A, 0x2E, 0x44)
ph.line.fill.background()
if img_label:
pt = txb(slide, img_l + Inches(0.2), img_t + img_h / 2 - Inches(0.2),
img_w - Inches(0.4), Inches(0.4))
p = pt.text_frame.paragraphs[0]
p.alignment = PP_ALIGN.CENTER
run(p, img_label, 11, color=C_MUTED)
if caption:
cap = txb(slide, img_l, img_t + img_h + Inches(0.08),
img_w, Inches(0.32))
p = cap.text_frame.paragraphs[0]
p.alignment = PP_ALIGN.CENTER
run(p, caption, 11, color=C_MUTED)
def build_thanks(prs, title):
"""整页只有感谢语 + 大量留白,无横线,无姓名,无日期"""
slide = new_slide(prs)
tt_top = (SH - Inches(1.1)) / 2
tt = txb(slide, Inches(1.5), tt_top, SW - Inches(3.0), Inches(1.1))
p = tt.text_frame.paragraphs[0]
p.alignment = PP_ALIGN.CENTER
run(p, title, 32, bold=True, color=C_WHITE)
六、内容蒸馏规则
将学术内容转化为 Jobs 风格的五步法:
| 操作 | 学术原文示例 | Jobs 版本 |
|---|
| 提炼核心论断 | "本研究从三个维度建立了系统性的分析框架" | "S · I · C" |
| 数字独立成页 | "研究选取了 45 个典型建筑案例" | 整页显示 "45" + 小标签"个典型案例" |
| 消灭中间层 | 三级 bullet → 主张 + 根据 | 主张句单独一页,根据简单列举 |
| 英雄化核心问题 | "本文重点探讨..." | "窗的通透性,是如何被建构的?" |
| 结构化三件事 | 7 条结论 | 归并为 3 条,每条独立成页 |
七、反模式清单(禁止出现的设计)
| 反模式 | 原因 |
|---|
| ⚠️ 任何横线/分割线 | Jobs 从未用几何线条分隔内容——留白与字号差才是分隔 |
⚠️ C_LINE 变量 | 只要定义了这个变量就会被「顺手」使用,彻底不定义 |
| 幻灯片上超过 40 字 | 听众会读幻灯片而不听演讲者 |
| 超过 3 级缩进列表 | 逻辑层次应在演讲中口头呈现,不在幻灯片上 |
| 超过 3 种颜色 | 视觉噪音 |
| 表格塞满数字 | 用"大数字页"代替;只展示最关键的 1 个数 |
| Logo/水印/页码 | 分散视觉焦点 |
| 渐变/阴影/3D 效果 | 1995 年的设计风格 |
| 导航侧栏 | 听进度不如听内容 |
| 封面标题下的细线 | Word/PPT 模板惯例,Jobs 从不这样做 |
| 致谢页加作者姓名和日期 | Jobs 致谢 = 只有感谢的话,留白即是尊重 |
八、典型 Keynote 叙事弧(可复用)
1. 现有世界(反派) → "问题是什么"
2. 揭示代价 → "这有多糟"(或多可惜)
3. 英雄登场 → "今天,我们要解决它"
4. 核心概念一句话 → Hero Slide
5. 拆解(三件事) → Divider × 3 → 每件事展开
6. 关键数字 → Number Slide
7. 收束 → "这意味着什么"
8. 一句话记忆锚点 → Hero Slide(结尾)
9. 致谢 → Thanks